Merge pull request #1 from genxium/manual_collision

Merge the manual collision.
This commit is contained in:
Wing 2022-09-26 11:49:55 +08:00 committed by GitHub
commit ff092a40ed
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 1046 additions and 3840 deletions

11
ConcerningEdgeCases.md Normal file
View File

@ -0,0 +1,11 @@
Under the current "input delay" algorithm, the lag of a single player would cause all the other players to receive outdated commands, e.g. when at a certain moment
- player#1: renderFrameId = 100, significantly lagged due to local CPU overheated
- player#2: renderFrameId = 240
- player#3: renderFrameId = 239
- player#4: renderFrameId = 242
players #2, #3 #4 would receive "outdated(in their subjective feelings) but all-confirmed commands" from then on, thus forced to rollback and chase many frames - the lag due to "large range of frame-chasing" would then further deteriorate the situation - like an avalanche.
In a "no-server & p2p" setup, I couldn't think of a proper way to cope with such edge case. Solely on the frontend we could only mitigate the impact to players #2, #3, #4, e.g. a potential lag due to "large range of frame-chasing" is proactively avoided in `<proj-root>/frontend/assets/scripts/Map.js, function update(dt)`.
However in a "server as authority" setup, the server could force confirming an inputFrame without player#1's upsync, and notify player#1 to apply a "roomDownsyncFrame" as well as drop all its outdated local inputFrames.

View File

@ -1,8 +1,8 @@
package models package models
type RingBuffer struct { type RingBuffer struct {
Ed int32 // write index Ed int32 // write index, open index
St int32 // read index St int32 // read index, closed index
EdFrameId int32 EdFrameId int32
StFrameId int32 StFrameId int32
N int32 N int32

View File

@ -3,7 +3,6 @@ package models
import ( import (
"encoding/xml" "encoding/xml"
"fmt" "fmt"
"strings"
"github.com/ByteArena/box2d" "github.com/ByteArena/box2d"
"github.com/golang/protobuf/proto" "github.com/golang/protobuf/proto"
"github.com/gorilla/websocket" "github.com/gorilla/websocket"
@ -15,6 +14,7 @@ import (
. "server/common" . "server/common"
"server/common/utils" "server/common/utils"
pb "server/pb_output" pb "server/pb_output"
"strings"
"sync" "sync"
"sync/atomic" "sync/atomic"
"time" "time"
@ -31,8 +31,8 @@ const (
) )
const ( const (
MAGIC_REMOVED_AT_FRAME_ID_PERMANENT_REMOVAL_MARGIN = 5 MAGIC_ROOM_DOWNSYNC_FRAME_ID_BATTLE_READY_TO_START = -1
MAGIC_ROOM_DOWNSYNC_FRAME_ID_BATTLE_READY_TO_START = -99 MAGIC_ROOM_DOWNSYNC_FRAME_ID_BATTLE_START = 0
MAGIC_ROOM_DOWNSYNC_FRAME_ID_PLAYER_ADDED_AND_ACKED = -98 MAGIC_ROOM_DOWNSYNC_FRAME_ID_PLAYER_ADDED_AND_ACKED = -98
MAGIC_ROOM_DOWNSYNC_FRAME_ID_PLAYER_READDED_AND_ACKED = -97 MAGIC_ROOM_DOWNSYNC_FRAME_ID_PLAYER_READDED_AND_ACKED = -97
@ -535,7 +535,7 @@ func (pR *Room) ChooseStage() error {
ErrFatal(err) ErrFatal(err)
rand.Seed(time.Now().Unix()) rand.Seed(time.Now().Unix())
stageNameList := []string{/*"pacman" ,*/ "richsoil"} stageNameList := []string{ /*"pacman" ,*/ "richsoil"}
chosenStageIndex := rand.Int() % len(stageNameList) // Hardcoded temporarily. -- YFLu chosenStageIndex := rand.Int() % len(stageNameList) // Hardcoded temporarily. -- YFLu
pR.StageName = stageNameList[chosenStageIndex] pR.StageName = stageNameList[chosenStageIndex]
@ -744,19 +744,15 @@ func (pR *Room) StartBattle() {
}() }()
battleMainLoopStartedNanos := utils.UnixtimeNano() battleMainLoopStartedNanos := utils.UnixtimeNano()
var totalElapsedNanos int64 totalElapsedNanos := int64(0)
totalElapsedNanos = 0
// inputFrameIdDownsyncToleranceFrameCnt := int32(1)
Logger.Info("The `battleMainLoop` is started for:", zap.Any("roomId", pR.Id)) Logger.Info("The `battleMainLoop` is started for:", zap.Any("roomId", pR.Id))
for { for {
pR.Tick++ // It's important to increment "pR.Tick" here such that the "InputFrameDownsync.InputFrameId" is most advanced if 0 == pR.Tick {
if 1 == pR.Tick {
// The legacy frontend code needs this "kickoffFrame" to remove the "ready to start 3-2-1" panel // The legacy frontend code needs this "kickoffFrame" to remove the "ready to start 3-2-1" panel
kickoffFrame := pb.RoomDownsyncFrame{ kickoffFrame := pb.RoomDownsyncFrame{
Id: pR.Tick, Id: pR.Tick,
RefFrameId: 0, // Hardcoded for now. RefFrameId: MAGIC_ROOM_DOWNSYNC_FRAME_ID_BATTLE_START,
Players: toPbPlayers(pR.Players), Players: toPbPlayers(pR.Players),
Treasures: toPbTreasures(pR.Treasures), Treasures: toPbTreasures(pR.Treasures),
Traps: toPbTraps(pR.Traps), Traps: toPbTraps(pR.Traps),
@ -822,7 +818,6 @@ func (pR *Room) StartBattle() {
// [WARNING] EDGE CASE HERE: Upon initialization, all of "lastAllConfirmedInputFrameId", "lastAllConfirmedInputFrameIdWithChange" and "anchorInputFrameId" are "-1", thus "candidateToSendInputFrameId" starts with "0", however "inputFrameId: 0" might not have been all confirmed! // [WARNING] EDGE CASE HERE: Upon initialization, all of "lastAllConfirmedInputFrameId", "lastAllConfirmedInputFrameIdWithChange" and "anchorInputFrameId" are "-1", thus "candidateToSendInputFrameId" starts with "0", however "inputFrameId: 0" might not have been all confirmed!
debugSendingInputFrameId := int32(-1) debugSendingInputFrameId := int32(-1)
// TODO: If a buffered "inputFrame" is inserted but has been non-all-confirmed for a long time, e.g. inserted at "inputFrameId=42" but still non-all-confirmed at "inputFrameId=980", the server should mark that of "inputFrameId=42" as well as send it to the unconfirmed players with an extra "RoomDownsyncFrame" for their FORCE RESET OF REFERENCE STATE
for candidateToSendInputFrameId <= lastAllConfirmedInputFrameIdWithChange { for candidateToSendInputFrameId <= lastAllConfirmedInputFrameIdWithChange {
tmp := pR.AllPlayerInputsBuffer.GetByFrameId(candidateToSendInputFrameId) tmp := pR.AllPlayerInputsBuffer.GetByFrameId(candidateToSendInputFrameId)
if nil == tmp { if nil == tmp {
@ -848,12 +843,7 @@ func (pR *Room) StartBattle() {
} }
} }
/* pR.Tick++
if swapped := atomic.CompareAndSwapInt32(&(pR.LastAllConfirmedInputFrameIdWithChange), lastAllConfirmedInputFrameIdWithChange, -1); !swapped {
// "OnBattleCmdReceived" might be updating "pR.LastAllConfirmedInputFrameIdWithChange" simultaneously, don't update here if the old value is no longer valid
Logger.Warn("pR.LastAllConfirmedInputFrameIdWithChange NOT UPDATED:", zap.Any("roomId", pR.Id), zap.Any("refInputFrameId", refInputFrameId), zap.Any("StFrameId", pR.AllPlayerInputsBuffer.StFrameId), zap.Any("EdFrameId", pR.AllPlayerInputsBuffer.EdFrameId))
}
*/
now := utils.UnixtimeNano() now := utils.UnixtimeNano()
elapsedInCalculation := now - stCalculation elapsedInCalculation := now - stCalculation
totalElapsedNanos = (now - battleMainLoopStartedNanos) totalElapsedNanos = (now - battleMainLoopStartedNanos)
@ -953,7 +943,7 @@ func (pR *Room) OnBattleCmdReceived(pReq *pb.WsReq) {
Logger.Info("Key inputFrame change", zap.Any("roomId", pR.Id), zap.Any("inputFrameId", clientInputFrameId), zap.Any("lastInputFrameId", pR.LastAllConfirmedInputFrameId), zap.Any("StFrameId", pR.AllPlayerInputsBuffer.StFrameId), zap.Any("EdFrameId", pR.AllPlayerInputsBuffer.EdFrameId), zap.Any("newInputList", inputFrameDownsync.InputList), zap.Any("lastInputList", pR.LastAllConfirmedInputList)) Logger.Info("Key inputFrame change", zap.Any("roomId", pR.Id), zap.Any("inputFrameId", clientInputFrameId), zap.Any("lastInputFrameId", pR.LastAllConfirmedInputFrameId), zap.Any("StFrameId", pR.AllPlayerInputsBuffer.StFrameId), zap.Any("EdFrameId", pR.AllPlayerInputsBuffer.EdFrameId), zap.Any("newInputList", inputFrameDownsync.InputList), zap.Any("lastInputList", pR.LastAllConfirmedInputList))
} }
atomic.StoreInt32(&(pR.LastAllConfirmedInputFrameId), clientInputFrameId) // [WARNING] It's IMPORTANT that "pR.LastAllConfirmedInputFrameId" is NOT NECESSARILY CONSECUTIVE, i.e. if one of the players disconnects and reconnects within a considerable amount of frame delays! 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 {
// To avoid potential misuse of pointers // To avoid potential misuse of pointers
pR.LastAllConfirmedInputList[i] = v pR.LastAllConfirmedInputList[i] = v
} }
@ -987,7 +977,7 @@ func (pR *Room) StopBattleForSettlement() {
for playerId, _ := range pR.Players { for playerId, _ := range pR.Players {
assembledFrame := pb.RoomDownsyncFrame{ assembledFrame := pb.RoomDownsyncFrame{
Id: pR.Tick, Id: pR.Tick,
RefFrameId: 0, // Hardcoded for now. RefFrameId: pR.Tick, // Hardcoded for now.
Players: toPbPlayers(pR.Players), Players: toPbPlayers(pR.Players),
SentAt: utils.UnixtimeMilli(), SentAt: utils.UnixtimeMilli(),
CountdownNanos: -1, // TODO: Replace this magic constant! CountdownNanos: -1, // TODO: Replace this magic constant!

View File

@ -1140,9 +1140,8 @@ type RoomDownsyncFrame struct {
Traps map[int32]*Trap `protobuf:"bytes,7,rep,name=traps,proto3" json:"traps,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` Traps map[int32]*Trap `protobuf:"bytes,7,rep,name=traps,proto3" json:"traps,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
Bullets map[int32]*Bullet `protobuf:"bytes,8,rep,name=bullets,proto3" json:"bullets,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` Bullets map[int32]*Bullet `protobuf:"bytes,8,rep,name=bullets,proto3" json:"bullets,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
SpeedShoes map[int32]*SpeedShoe `protobuf:"bytes,9,rep,name=speedShoes,proto3" json:"speedShoes,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` SpeedShoes map[int32]*SpeedShoe `protobuf:"bytes,9,rep,name=speedShoes,proto3" json:"speedShoes,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
Pumpkin map[int32]*Pumpkin `protobuf:"bytes,10,rep,name=pumpkin,proto3" json:"pumpkin,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` GuardTowers map[int32]*GuardTower `protobuf:"bytes,10,rep,name=guardTowers,proto3" json:"guardTowers,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
GuardTowers map[int32]*GuardTower `protobuf:"bytes,11,rep,name=guardTowers,proto3" json:"guardTowers,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` PlayerMetas map[int32]*PlayerMeta `protobuf:"bytes,11,rep,name=playerMetas,proto3" json:"playerMetas,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
PlayerMetas map[int32]*PlayerMeta `protobuf:"bytes,12,rep,name=playerMetas,proto3" json:"playerMetas,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
} }
func (x *RoomDownsyncFrame) Reset() { func (x *RoomDownsyncFrame) Reset() {
@ -1240,13 +1239,6 @@ func (x *RoomDownsyncFrame) GetSpeedShoes() map[int32]*SpeedShoe {
return nil return nil
} }
func (x *RoomDownsyncFrame) GetPumpkin() map[int32]*Pumpkin {
if x != nil {
return x.Pumpkin
}
return nil
}
func (x *RoomDownsyncFrame) GetGuardTowers() map[int32]*GuardTower { func (x *RoomDownsyncFrame) GetGuardTowers() map[int32]*GuardTower {
if x != nil { if x != nil {
return x.GuardTowers return x.GuardTowers
@ -1778,8 +1770,8 @@ var file_room_downsync_frame_proto_rawDesc = []byte{
0x28, 0x05, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x0c, 0x0a, 0x01, 0x78, 0x18, 0x04, 0x20, 0x28, 0x05, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x0c, 0x0a, 0x01, 0x78, 0x18, 0x04, 0x20,
0x01, 0x28, 0x01, 0x52, 0x01, 0x78, 0x12, 0x0c, 0x0a, 0x01, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x01, 0x28, 0x01, 0x52, 0x01, 0x78, 0x12, 0x0c, 0x0a, 0x01, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28,
0x01, 0x52, 0x01, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x18, 0x01, 0x52, 0x01, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x18,
0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x22, 0xbb, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x22, 0x9a,
0x0b, 0x0a, 0x11, 0x52, 0x6f, 0x6f, 0x6d, 0x44, 0x6f, 0x77, 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x46, 0x0a, 0x0a, 0x11, 0x52, 0x6f, 0x6f, 0x6d, 0x44, 0x6f, 0x77, 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x46,
0x72, 0x61, 0x6d, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x72, 0x61, 0x6d, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05,
0x52, 0x02, 0x69, 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x72, 0x65, 0x66, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x72, 0x65, 0x66, 0x46, 0x72, 0x61, 0x6d, 0x65,
0x49, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x72, 0x65, 0x66, 0x46, 0x72, 0x61, 0x49, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x72, 0x65, 0x66, 0x46, 0x72, 0x61,
@ -1811,123 +1803,113 @@ var file_room_downsync_frame_proto_rawDesc = []byte{
0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x52, 0x6f, 0x6f, 0x6d, 0x44, 0x6f, 0x77, 0x6e, 0x73, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x52, 0x6f, 0x6f, 0x6d, 0x44, 0x6f, 0x77, 0x6e, 0x73,
0x79, 0x6e, 0x63, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x2e, 0x53, 0x70, 0x65, 0x65, 0x64, 0x53, 0x68, 0x79, 0x6e, 0x63, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x2e, 0x53, 0x70, 0x65, 0x65, 0x64, 0x53, 0x68,
0x6f, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, 0x73, 0x70, 0x65, 0x65, 0x64, 0x53, 0x6f, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, 0x73, 0x70, 0x65, 0x65, 0x64, 0x53,
0x68, 0x6f, 0x65, 0x73, 0x12, 0x49, 0x0a, 0x07, 0x70, 0x75, 0x6d, 0x70, 0x6b, 0x69, 0x6e, 0x18, 0x68, 0x6f, 0x65, 0x73, 0x12, 0x55, 0x0a, 0x0b, 0x67, 0x75, 0x61, 0x72, 0x64, 0x54, 0x6f, 0x77,
0x0a, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x65, 0x72, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x74, 0x72, 0x65, 0x61,
0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x52, 0x6f, 0x6f, 0x6d, 0x44, 0x6f, 0x77, 0x6e, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x52, 0x6f, 0x6f, 0x6d,
0x73, 0x79, 0x6e, 0x63, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x2e, 0x50, 0x75, 0x6d, 0x70, 0x6b, 0x69, 0x44, 0x6f, 0x77, 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x2e, 0x47, 0x75,
0x6e, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x70, 0x75, 0x6d, 0x70, 0x6b, 0x69, 0x6e, 0x12, 0x61, 0x72, 0x64, 0x54, 0x6f, 0x77, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0b,
0x55, 0x0a, 0x0b, 0x67, 0x75, 0x61, 0x72, 0x64, 0x54, 0x6f, 0x77, 0x65, 0x72, 0x73, 0x18, 0x0b, 0x67, 0x75, 0x61, 0x72, 0x64, 0x54, 0x6f, 0x77, 0x65, 0x72, 0x73, 0x12, 0x55, 0x0a, 0x0b, 0x70,
0x20, 0x03, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x73, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x0b,
0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x52, 0x6f, 0x6f, 0x6d, 0x44, 0x6f, 0x77, 0x6e, 0x73, 0x32, 0x33, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65,
0x79, 0x6e, 0x63, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x2e, 0x47, 0x75, 0x61, 0x72, 0x64, 0x54, 0x6f, 0x72, 0x78, 0x2e, 0x52, 0x6f, 0x6f, 0x6d, 0x44, 0x6f, 0x77, 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x46,
0x77, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0b, 0x67, 0x75, 0x61, 0x72, 0x64, 0x72, 0x61, 0x6d, 0x65, 0x2e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x73,
0x54, 0x6f, 0x77, 0x65, 0x72, 0x73, 0x12, 0x55, 0x0a, 0x0b, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0b, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4d, 0x65, 0x74,
0x4d, 0x65, 0x74, 0x61, 0x73, 0x18, 0x0c, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x74, 0x72, 0x61, 0x73, 0x1a, 0x53, 0x0a, 0x0c, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74,
0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x52, 0x6f, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52,
0x6f, 0x6d, 0x44, 0x6f, 0x77, 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x2e, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2d, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20,
0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75,
0x52, 0x0b, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x73, 0x1a, 0x53, 0x0a, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x52, 0x05, 0x76, 0x61,
0x0c, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x57, 0x0a, 0x0e, 0x54, 0x72, 0x65, 0x61, 0x73,
0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x75, 0x72, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79,
0x2d, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2f, 0x0a, 0x05, 0x76,
0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x74, 0x72, 0x65,
0x2e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x54, 0x72, 0x65,
0x38, 0x01, 0x1a, 0x57, 0x0a, 0x0e, 0x54, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x73, 0x45, 0x61, 0x73, 0x75, 0x72, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01,
0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x1a, 0x4f, 0x0a, 0x0a, 0x54, 0x72, 0x61, 0x70, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10,
0x05, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2f, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x6b, 0x65, 0x79,
0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x12, 0x2b, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32,
0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x54, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x15, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72,
0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x4f, 0x0a, 0x0a, 0x54, 0x78, 0x2e, 0x54, 0x72, 0x61, 0x70, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38,
0x72, 0x61, 0x70, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x01, 0x1a, 0x53, 0x0a, 0x0c, 0x42, 0x75, 0x6c, 0x6c, 0x65, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72,
0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2b, 0x0a, 0x05, 0x76, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03,
0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x72, 0x65, 0x6b, 0x65, 0x79, 0x12, 0x2d, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01,
0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x54, 0x72, 0x61, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e,
0x70, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x53, 0x0a, 0x0c, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x42, 0x75, 0x6c, 0x6c, 0x65, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c,
0x42, 0x75, 0x6c, 0x6c, 0x65, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x59, 0x0a, 0x0f, 0x53, 0x70, 0x65, 0x65, 0x64, 0x53,
0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2d, 0x68, 0x6f, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79,
0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x30, 0x0a, 0x05, 0x76,
0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x74, 0x72, 0x65,
0x42, 0x75, 0x6c, 0x6c, 0x65, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x53, 0x70, 0x65,
0x01, 0x1a, 0x59, 0x0a, 0x0f, 0x53, 0x70, 0x65, 0x65, 0x64, 0x53, 0x68, 0x6f, 0x65, 0x73, 0x45, 0x65, 0x64, 0x53, 0x68, 0x6f, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38,
0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x01, 0x1a, 0x5b, 0x0a, 0x10, 0x47, 0x75, 0x61, 0x72, 0x64, 0x54, 0x6f, 0x77, 0x65, 0x72, 0x73,
0x05, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x30, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01,
0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x28, 0x05, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x31, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65,
0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x53, 0x70, 0x65, 0x65, 0x64, 0x53, 0x68, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72,
0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x54, 0x0a, 0x0c, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x47, 0x75, 0x61, 0x72, 0x64, 0x54, 0x6f,
0x50, 0x75, 0x6d, 0x70, 0x6b, 0x69, 0x6e, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x77, 0x65, 0x72, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x5b,
0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2e, 0x0a, 0x10, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x73, 0x45, 0x6e, 0x74,
0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52,
0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x31, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20,
0x50, 0x75, 0x6d, 0x70, 0x6b, 0x69, 0x6e, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75,
0x38, 0x01, 0x1a, 0x5b, 0x0a, 0x10, 0x47, 0x75, 0x61, 0x72, 0x64, 0x54, 0x6f, 0x77, 0x65, 0x72, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61,
0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x56, 0x0a, 0x10, 0x49,
0x01, 0x28, 0x05, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x31, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x70, 0x73, 0x79, 0x6e, 0x63, 0x12,
0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x22, 0x0a, 0x0c, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x49, 0x64, 0x18,
0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x47, 0x75, 0x61, 0x72, 0x64, 0x54, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d,
0x6f, 0x77, 0x65, 0x72, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x65, 0x49, 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x44, 0x69,
0x5b, 0x0a, 0x10, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x73, 0x45, 0x6e, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x64,
0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x44, 0x69, 0x72, 0x22, 0x7c, 0x0a, 0x12, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d,
0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x31, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x65, 0x44, 0x6f, 0x77, 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x12, 0x22, 0x0a, 0x0c, 0x69, 0x6e, 0x70,
0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52,
0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x0c, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x49, 0x64, 0x12, 0x1c, 0x0a,
0x61, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x56, 0x0a, 0x10, 0x09, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x18, 0x02, 0x20, 0x03, 0x28, 0x04,
0x49, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x70, 0x73, 0x79, 0x6e, 0x63, 0x52, 0x09, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x24, 0x0a, 0x0d, 0x63,
0x12, 0x22, 0x0a, 0x0c, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x49, 0x64, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x65, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x18, 0x03, 0x20, 0x01,
0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x28, 0x04, 0x52, 0x0d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x65, 0x64, 0x4c, 0x69, 0x73,
0x6d, 0x65, 0x49, 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x44, 0x74, 0x22, 0x3b, 0x0a, 0x0f, 0x48, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x55, 0x70,
0x69, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x73, 0x79, 0x6e, 0x63, 0x12, 0x28, 0x0a, 0x0f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x54, 0x69,
0x64, 0x44, 0x69, 0x72, 0x22, 0x7c, 0x0a, 0x12, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0f, 0x63,
0x6d, 0x65, 0x44, 0x6f, 0x77, 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x12, 0x22, 0x0a, 0x0c, 0x69, 0x6e, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0xca,
0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x02, 0x0a, 0x05, 0x57, 0x73, 0x52, 0x65, 0x71, 0x12, 0x14, 0x0a, 0x05, 0x6d, 0x73, 0x67, 0x49,
0x52, 0x0c, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x49, 0x64, 0x12, 0x1c, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x6d, 0x73, 0x67, 0x49, 0x64, 0x12, 0x1a,
0x0a, 0x09, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0a, 0x08, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x49, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05,
0x04, 0x52, 0x09, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x24, 0x0a, 0x0d, 0x52, 0x08, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x49, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x63,
0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x65, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x18, 0x03, 0x20, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x61, 0x63, 0x74, 0x12, 0x1c, 0x0a, 0x09,
0x01, 0x28, 0x04, 0x52, 0x0d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x65, 0x64, 0x4c, 0x69, 0x6a, 0x6f, 0x69, 0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52,
0x73, 0x74, 0x22, 0x3b, 0x0a, 0x0f, 0x48, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x55, 0x09, 0x6a, 0x6f, 0x69, 0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x24, 0x0a, 0x0d, 0x61, 0x63,
0x70, 0x73, 0x79, 0x6e, 0x63, 0x12, 0x28, 0x0a, 0x0f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x54, 0x6b, 0x69, 0x6e, 0x67, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x49, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28,
0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0f, 0x05, 0x52, 0x0d, 0x61, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x49, 0x64,
0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x12, 0x2e, 0x0a, 0x12, 0x61, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x46,
0xca, 0x02, 0x0a, 0x05, 0x57, 0x73, 0x52, 0x65, 0x71, 0x12, 0x14, 0x0a, 0x05, 0x6d, 0x73, 0x67, 0x72, 0x61, 0x6d, 0x65, 0x49, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x12, 0x61, 0x63,
0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x6d, 0x73, 0x67, 0x49, 0x64, 0x12, 0x6b, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x49, 0x64,
0x1a, 0x0a, 0x08, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x49, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x12, 0x57, 0x0a, 0x15, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x70,
0x05, 0x52, 0x08, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x49, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x73, 0x79, 0x6e, 0x63, 0x42, 0x61, 0x74, 0x63, 0x68, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32,
0x63, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x61, 0x63, 0x74, 0x12, 0x1c, 0x0a, 0x21, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72,
0x09, 0x6a, 0x6f, 0x69, 0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x78, 0x2e, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x70, 0x73, 0x79,
0x52, 0x09, 0x6a, 0x6f, 0x69, 0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x24, 0x0a, 0x0d, 0x61, 0x6e, 0x63, 0x52, 0x15, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x70,
0x63, 0x6b, 0x69, 0x6e, 0x67, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x49, 0x64, 0x18, 0x05, 0x20, 0x01, 0x73, 0x79, 0x6e, 0x63, 0x42, 0x61, 0x74, 0x63, 0x68, 0x12, 0x30, 0x0a, 0x02, 0x68, 0x62, 0x18,
0x28, 0x05, 0x52, 0x0d, 0x61, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x49, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65,
0x64, 0x12, 0x2e, 0x0a, 0x12, 0x61, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x48, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61,
0x46, 0x72, 0x61, 0x6d, 0x65, 0x49, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x12, 0x61, 0x74, 0x55, 0x70, 0x73, 0x79, 0x6e, 0x63, 0x52, 0x02, 0x68, 0x62, 0x22, 0xa4, 0x02, 0x0a, 0x06,
0x63, 0x6b, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x49, 0x57, 0x73, 0x52, 0x65, 0x73, 0x70, 0x12, 0x10, 0x0a, 0x03, 0x72, 0x65, 0x74, 0x18, 0x01, 0x20,
0x64, 0x12, 0x57, 0x0a, 0x15, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x01, 0x28, 0x05, 0x52, 0x03, 0x72, 0x65, 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x65, 0x63, 0x68, 0x6f,
0x70, 0x73, 0x79, 0x6e, 0x63, 0x42, 0x61, 0x74, 0x63, 0x68, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x65, 0x64, 0x4d, 0x73, 0x67, 0x49, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x65,
0x32, 0x21, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x63, 0x68, 0x6f, 0x65, 0x64, 0x4d, 0x73, 0x67, 0x49, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x63,
0x72, 0x78, 0x2e, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x70, 0x73, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x61, 0x63, 0x74, 0x12, 0x34, 0x0a, 0x03,
0x79, 0x6e, 0x63, 0x52, 0x15, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x72, 0x64, 0x66, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x74, 0x72, 0x65, 0x61,
0x70, 0x73, 0x79, 0x6e, 0x63, 0x42, 0x61, 0x74, 0x63, 0x68, 0x12, 0x30, 0x0a, 0x02, 0x68, 0x62, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x52, 0x6f, 0x6f, 0x6d,
0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x44, 0x6f, 0x77, 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x52, 0x03, 0x72,
0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x48, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x64, 0x66, 0x12, 0x5d, 0x0a, 0x17, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65,
0x61, 0x74, 0x55, 0x70, 0x73, 0x79, 0x6e, 0x63, 0x52, 0x02, 0x68, 0x62, 0x22, 0xa4, 0x02, 0x0a, 0x44, 0x6f, 0x77, 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x42, 0x61, 0x74, 0x63, 0x68, 0x18, 0x05, 0x20,
0x06, 0x57, 0x73, 0x52, 0x65, 0x73, 0x70, 0x12, 0x10, 0x0a, 0x03, 0x72, 0x65, 0x74, 0x18, 0x01, 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75,
0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x72, 0x65, 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x65, 0x63, 0x68, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65,
0x6f, 0x65, 0x64, 0x4d, 0x73, 0x67, 0x49, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x44, 0x6f, 0x77, 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x52, 0x17, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x46,
0x65, 0x63, 0x68, 0x6f, 0x65, 0x64, 0x4d, 0x73, 0x67, 0x49, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x44, 0x6f, 0x77, 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x42, 0x61, 0x74, 0x63,
0x63, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x61, 0x63, 0x74, 0x12, 0x34, 0x0a, 0x68, 0x12, 0x3f, 0x0a, 0x08, 0x62, 0x63, 0x69, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x18, 0x06, 0x20,
0x03, 0x72, 0x64, 0x66, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x74, 0x72, 0x65, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75,
0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x52, 0x6f, 0x6f, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x43, 0x6f, 0x6c, 0x6c,
0x6d, 0x44, 0x6f, 0x77, 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x52, 0x03, 0x69, 0x64, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x08, 0x62, 0x63, 0x69, 0x46, 0x72, 0x61,
0x72, 0x64, 0x66, 0x12, 0x5d, 0x0a, 0x17, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x6d, 0x65, 0x42, 0x03, 0x5a, 0x01, 0x2e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
0x65, 0x44, 0x6f, 0x77, 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x42, 0x61, 0x74, 0x63, 0x68, 0x18, 0x05,
0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68,
0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d,
0x65, 0x44, 0x6f, 0x77, 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x52, 0x17, 0x69, 0x6e, 0x70, 0x75, 0x74,
0x46, 0x72, 0x61, 0x6d, 0x65, 0x44, 0x6f, 0x77, 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x42, 0x61, 0x74,
0x63, 0x68, 0x12, 0x3f, 0x0a, 0x08, 0x62, 0x63, 0x69, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x18, 0x06,
0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68,
0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x43, 0x6f, 0x6c,
0x6c, 0x69, 0x64, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x08, 0x62, 0x63, 0x69, 0x46, 0x72,
0x61, 0x6d, 0x65, 0x42, 0x03, 0x5a, 0x01, 0x2e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
} }
var ( var (
@ -1942,7 +1924,7 @@ func file_room_downsync_frame_proto_rawDescGZIP() []byte {
return file_room_downsync_frame_proto_rawDescData return file_room_downsync_frame_proto_rawDescData
} }
var file_room_downsync_frame_proto_msgTypes = make([]protoimpl.MessageInfo, 30) var file_room_downsync_frame_proto_msgTypes = make([]protoimpl.MessageInfo, 29)
var file_room_downsync_frame_proto_goTypes = []interface{}{ var file_room_downsync_frame_proto_goTypes = []interface{}{
(*Direction)(nil), // 0: treasurehunterx.Direction (*Direction)(nil), // 0: treasurehunterx.Direction
(*Vec2D)(nil), // 1: treasurehunterx.Vec2D (*Vec2D)(nil), // 1: treasurehunterx.Vec2D
@ -1971,9 +1953,8 @@ var file_room_downsync_frame_proto_goTypes = []interface{}{
nil, // 24: treasurehunterx.RoomDownsyncFrame.TrapsEntry nil, // 24: treasurehunterx.RoomDownsyncFrame.TrapsEntry
nil, // 25: treasurehunterx.RoomDownsyncFrame.BulletsEntry nil, // 25: treasurehunterx.RoomDownsyncFrame.BulletsEntry
nil, // 26: treasurehunterx.RoomDownsyncFrame.SpeedShoesEntry nil, // 26: treasurehunterx.RoomDownsyncFrame.SpeedShoesEntry
nil, // 27: treasurehunterx.RoomDownsyncFrame.PumpkinEntry nil, // 27: treasurehunterx.RoomDownsyncFrame.GuardTowersEntry
nil, // 28: treasurehunterx.RoomDownsyncFrame.GuardTowersEntry nil, // 28: treasurehunterx.RoomDownsyncFrame.PlayerMetasEntry
nil, // 29: treasurehunterx.RoomDownsyncFrame.PlayerMetasEntry
} }
var file_room_downsync_frame_proto_depIdxs = []int32{ var file_room_downsync_frame_proto_depIdxs = []int32{
1, // 0: treasurehunterx.Polygon2D.Anchor:type_name -> treasurehunterx.Vec2D 1, // 0: treasurehunterx.Polygon2D.Anchor:type_name -> treasurehunterx.Vec2D
@ -1990,29 +1971,27 @@ var file_room_downsync_frame_proto_depIdxs = []int32{
24, // 11: treasurehunterx.RoomDownsyncFrame.traps:type_name -> treasurehunterx.RoomDownsyncFrame.TrapsEntry 24, // 11: treasurehunterx.RoomDownsyncFrame.traps:type_name -> treasurehunterx.RoomDownsyncFrame.TrapsEntry
25, // 12: treasurehunterx.RoomDownsyncFrame.bullets:type_name -> treasurehunterx.RoomDownsyncFrame.BulletsEntry 25, // 12: treasurehunterx.RoomDownsyncFrame.bullets:type_name -> treasurehunterx.RoomDownsyncFrame.BulletsEntry
26, // 13: treasurehunterx.RoomDownsyncFrame.speedShoes:type_name -> treasurehunterx.RoomDownsyncFrame.SpeedShoesEntry 26, // 13: treasurehunterx.RoomDownsyncFrame.speedShoes:type_name -> treasurehunterx.RoomDownsyncFrame.SpeedShoesEntry
27, // 14: treasurehunterx.RoomDownsyncFrame.pumpkin:type_name -> treasurehunterx.RoomDownsyncFrame.PumpkinEntry 27, // 14: treasurehunterx.RoomDownsyncFrame.guardTowers:type_name -> treasurehunterx.RoomDownsyncFrame.GuardTowersEntry
28, // 15: treasurehunterx.RoomDownsyncFrame.guardTowers:type_name -> treasurehunterx.RoomDownsyncFrame.GuardTowersEntry 28, // 15: treasurehunterx.RoomDownsyncFrame.playerMetas:type_name -> treasurehunterx.RoomDownsyncFrame.PlayerMetasEntry
29, // 16: treasurehunterx.RoomDownsyncFrame.playerMetas:type_name -> treasurehunterx.RoomDownsyncFrame.PlayerMetasEntry 15, // 16: treasurehunterx.WsReq.inputFrameUpsyncBatch:type_name -> treasurehunterx.InputFrameUpsync
15, // 17: treasurehunterx.WsReq.inputFrameUpsyncBatch:type_name -> treasurehunterx.InputFrameUpsync 17, // 17: treasurehunterx.WsReq.hb:type_name -> treasurehunterx.HeartbeatUpsync
17, // 18: treasurehunterx.WsReq.hb:type_name -> treasurehunterx.HeartbeatUpsync 14, // 18: treasurehunterx.WsResp.rdf:type_name -> treasurehunterx.RoomDownsyncFrame
14, // 19: treasurehunterx.WsResp.rdf:type_name -> treasurehunterx.RoomDownsyncFrame 16, // 19: treasurehunterx.WsResp.inputFrameDownsyncBatch:type_name -> treasurehunterx.InputFrameDownsync
16, // 20: treasurehunterx.WsResp.inputFrameDownsyncBatch:type_name -> treasurehunterx.InputFrameDownsync 5, // 20: treasurehunterx.WsResp.bciFrame:type_name -> treasurehunterx.BattleColliderInfo
5, // 21: treasurehunterx.WsResp.bciFrame:type_name -> treasurehunterx.BattleColliderInfo 3, // 21: treasurehunterx.BattleColliderInfo.StrToVec2DListMapEntry.value:type_name -> treasurehunterx.Vec2DList
3, // 22: treasurehunterx.BattleColliderInfo.StrToVec2DListMapEntry.value:type_name -> treasurehunterx.Vec2DList 4, // 22: treasurehunterx.BattleColliderInfo.StrToPolygon2DListMapEntry.value:type_name -> treasurehunterx.Polygon2DList
4, // 23: treasurehunterx.BattleColliderInfo.StrToPolygon2DListMapEntry.value:type_name -> treasurehunterx.Polygon2DList 6, // 23: treasurehunterx.RoomDownsyncFrame.PlayersEntry.value:type_name -> treasurehunterx.Player
6, // 24: treasurehunterx.RoomDownsyncFrame.PlayersEntry.value:type_name -> treasurehunterx.Player 8, // 24: treasurehunterx.RoomDownsyncFrame.TreasuresEntry.value:type_name -> treasurehunterx.Treasure
8, // 25: treasurehunterx.RoomDownsyncFrame.TreasuresEntry.value:type_name -> treasurehunterx.Treasure 10, // 25: treasurehunterx.RoomDownsyncFrame.TrapsEntry.value:type_name -> treasurehunterx.Trap
10, // 26: treasurehunterx.RoomDownsyncFrame.TrapsEntry.value:type_name -> treasurehunterx.Trap 9, // 26: treasurehunterx.RoomDownsyncFrame.BulletsEntry.value:type_name -> treasurehunterx.Bullet
9, // 27: treasurehunterx.RoomDownsyncFrame.BulletsEntry.value:type_name -> treasurehunterx.Bullet 11, // 27: treasurehunterx.RoomDownsyncFrame.SpeedShoesEntry.value:type_name -> treasurehunterx.SpeedShoe
11, // 28: treasurehunterx.RoomDownsyncFrame.SpeedShoesEntry.value:type_name -> treasurehunterx.SpeedShoe 13, // 28: treasurehunterx.RoomDownsyncFrame.GuardTowersEntry.value:type_name -> treasurehunterx.GuardTower
12, // 29: treasurehunterx.RoomDownsyncFrame.PumpkinEntry.value:type_name -> treasurehunterx.Pumpkin 7, // 29: treasurehunterx.RoomDownsyncFrame.PlayerMetasEntry.value:type_name -> treasurehunterx.PlayerMeta
13, // 30: treasurehunterx.RoomDownsyncFrame.GuardTowersEntry.value:type_name -> treasurehunterx.GuardTower 30, // [30:30] is the sub-list for method output_type
7, // 31: treasurehunterx.RoomDownsyncFrame.PlayerMetasEntry.value:type_name -> treasurehunterx.PlayerMeta 30, // [30:30] is the sub-list for method input_type
32, // [32:32] is the sub-list for method output_type 30, // [30:30] is the sub-list for extension type_name
32, // [32:32] is the sub-list for method input_type 30, // [30:30] is the sub-list for extension extendee
32, // [32:32] is the sub-list for extension type_name 0, // [0:30] is the sub-list for field type_name
32, // [32:32] is the sub-list for extension extendee
0, // [0:32] is the sub-list for field type_name
} }
func init() { file_room_downsync_frame_proto_init() } func init() { file_room_downsync_frame_proto_init() }
@ -2268,7 +2247,7 @@ func file_room_downsync_frame_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(), GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_room_downsync_frame_proto_rawDesc, RawDescriptor: file_room_downsync_frame_proto_rawDesc,
NumEnums: 0, NumEnums: 0,
NumMessages: 30, NumMessages: 29,
NumExtensions: 0, NumExtensions: 0,
NumServices: 0, NumServices: 0,
}, },

View File

@ -126,9 +126,8 @@ message RoomDownsyncFrame {
map<int32, Trap> traps = 7; map<int32, Trap> traps = 7;
map<int32, Bullet> bullets = 8; map<int32, Bullet> bullets = 8;
map<int32, SpeedShoe> speedShoes = 9; map<int32, SpeedShoe> speedShoes = 9;
map<int32, Pumpkin> pumpkin = 10; map<int32, GuardTower> guardTowers = 10;
map<int32, GuardTower> guardTowers = 11; map<int32, PlayerMeta> playerMetas = 11;
map<int32, PlayerMeta> playerMetas = 12;
} }
message InputFrameUpsync { message InputFrameUpsync {

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",
@ -27,7 +28,6 @@
} }
], ],
"_active": true, "_active": true,
"_level": 1,
"_components": [ "_components": [
{ {
"__id__": 11 "__id__": 11
@ -63,17 +63,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": 2,
"_id": "",
"_trs": { "_trs": {
"__type__": "TypedArray", "__type__": "TypedArray",
"ctor": "Float64Array", "ctor": "Float64Array",
@ -89,7 +78,19 @@
1, 1,
1 1
] ]
} },
"_eulerAngles": {
"__type__": "cc.Vec3",
"x": 0,
"y": 0,
"z": 0
},
"_skewX": 0,
"_skewY": 0,
"_is3DNode": false,
"_groupIndex": 2,
"groupIndex": 2,
"_id": ""
}, },
{ {
"__type__": "cc.Node", "__type__": "cc.Node",
@ -100,7 +101,6 @@
}, },
"_children": [], "_children": [],
"_active": false, "_active": false,
"_level": 0,
"_components": [ "_components": [
{ {
"__id__": 3 "__id__": 3
@ -127,17 +127,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",
@ -153,7 +142,19 @@
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",
@ -163,6 +164,7 @@
"__id__": 2 "__id__": 2
}, },
"_enabled": true, "_enabled": true,
"_materials": [],
"_useOriginalSize": false, "_useOriginalSize": false,
"_string": "(0, 0)", "_string": "(0, 0)",
"_N$string": "(0, 0)", "_N$string": "(0, 0)",
@ -200,7 +202,6 @@
}, },
"_children": [], "_children": [],
"_active": false, "_active": false,
"_level": 2,
"_components": [ "_components": [
{ {
"__id__": 6 "__id__": 6
@ -227,17 +228,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",
@ -253,7 +243,19 @@
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.ParticleSystem", "__type__": "cc.ParticleSystem",
@ -263,6 +265,9 @@
"__id__": 5 "__id__": 5
}, },
"_enabled": true, "_enabled": true,
"_materials": [],
"_srcBlendFactor": 770,
"_dstBlendFactor": 1,
"_custom": true, "_custom": true,
"_file": { "_file": {
"__uuid__": "b2687ac4-099e-403c-a192-ff477686f4f5" "__uuid__": "b2687ac4-099e-403c-a192-ff477686f4f5"
@ -271,8 +276,6 @@
"__uuid__": "472df5d3-35e7-4184-9e6c-7f41bee65ee3" "__uuid__": "472df5d3-35e7-4184-9e6c-7f41bee65ee3"
}, },
"_texture": null, "_texture": null,
"_srcBlendFactor": 770,
"_dstBlendFactor": 1,
"_stopped": false, "_stopped": false,
"playOnLoad": true, "playOnLoad": true,
"autoRemoveOnFinish": false, "autoRemoveOnFinish": false,
@ -329,6 +332,7 @@
"x": 7, "x": 7,
"y": 7 "y": 7
}, },
"_positionType": 1,
"positionType": 1, "positionType": 1,
"emitterMode": 0, "emitterMode": 0,
"gravity": { "gravity": {
@ -372,7 +376,6 @@
}, },
"_children": [], "_children": [],
"_active": true, "_active": true,
"_level": 2,
"_components": [ "_components": [
{ {
"__id__": 9 "__id__": 9
@ -399,17 +402,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",
@ -425,7 +417,19 @@
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",
@ -435,6 +439,13 @@
"__id__": 8 "__id__": 8
}, },
"_enabled": true, "_enabled": true,
"_materials": [
{
"__uuid__": "eca5d2f2-8ef6-41c2-bbe6-f9c79d09c432"
}
],
"_srcBlendFactor": 770,
"_dstBlendFactor": 771,
"_spriteFrame": { "_spriteFrame": {
"__uuid__": "a2170e4c-df31-41ef-be73-f4f605e75821" "__uuid__": "a2170e4c-df31-41ef-be73-f4f605e75821"
}, },
@ -449,12 +460,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": ""
}, },
{ {
@ -476,6 +484,9 @@
"__id__": 1 "__id__": 1
}, },
"_enabled": true, "_enabled": true,
"_materials": [],
"_srcBlendFactor": 770,
"_dstBlendFactor": 771,
"_spriteFrame": null, "_spriteFrame": null,
"_type": 0, "_type": 0,
"_sizeMode": 0, "_sizeMode": 0,
@ -488,10 +499,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": ""
}, },
{ {
@ -549,8 +557,8 @@
}, },
"_enabled": true, "_enabled": true,
"animComp": null, "animComp": null,
"baseSpeed": 300, "baseSpeed": 50,
"speed": 200, "speed": 50,
"lastMovedAt": 0, "lastMovedAt": 0,
"eps": 0.1, "eps": 0.1,
"magicLeanLowerBound": 0.414, "magicLeanLowerBound": 0.414,

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",
@ -27,7 +28,6 @@
} }
], ],
"_active": true, "_active": true,
"_level": 1,
"_components": [ "_components": [
{ {
"__id__": 11 "__id__": 11
@ -63,17 +63,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": 2,
"_id": "",
"_trs": { "_trs": {
"__type__": "TypedArray", "__type__": "TypedArray",
"ctor": "Float64Array", "ctor": "Float64Array",
@ -89,7 +78,19 @@
1, 1,
1 1
] ]
} },
"_eulerAngles": {
"__type__": "cc.Vec3",
"x": 0,
"y": 0,
"z": 0
},
"_skewX": 0,
"_skewY": 0,
"_is3DNode": false,
"_groupIndex": 2,
"groupIndex": 2,
"_id": ""
}, },
{ {
"__type__": "cc.Node", "__type__": "cc.Node",
@ -100,7 +101,6 @@
}, },
"_children": [], "_children": [],
"_active": false, "_active": false,
"_level": 0,
"_components": [ "_components": [
{ {
"__id__": 3 "__id__": 3
@ -127,17 +127,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",
@ -153,7 +142,19 @@
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",
@ -163,6 +164,7 @@
"__id__": 2 "__id__": 2
}, },
"_enabled": true, "_enabled": true,
"_materials": [],
"_useOriginalSize": false, "_useOriginalSize": false,
"_string": "(0, 0)", "_string": "(0, 0)",
"_N$string": "(0, 0)", "_N$string": "(0, 0)",
@ -200,7 +202,6 @@
}, },
"_children": [], "_children": [],
"_active": false, "_active": false,
"_level": 2,
"_components": [ "_components": [
{ {
"__id__": 6 "__id__": 6
@ -227,17 +228,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",
@ -253,7 +243,19 @@
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.ParticleSystem", "__type__": "cc.ParticleSystem",
@ -263,6 +265,9 @@
"__id__": 5 "__id__": 5
}, },
"_enabled": true, "_enabled": true,
"_materials": [],
"_srcBlendFactor": 770,
"_dstBlendFactor": 1,
"_custom": true, "_custom": true,
"_file": { "_file": {
"__uuid__": "b2687ac4-099e-403c-a192-ff477686f4f5" "__uuid__": "b2687ac4-099e-403c-a192-ff477686f4f5"
@ -271,8 +276,6 @@
"__uuid__": "472df5d3-35e7-4184-9e6c-7f41bee65ee3" "__uuid__": "472df5d3-35e7-4184-9e6c-7f41bee65ee3"
}, },
"_texture": null, "_texture": null,
"_srcBlendFactor": 770,
"_dstBlendFactor": 1,
"_stopped": false, "_stopped": false,
"playOnLoad": true, "playOnLoad": true,
"autoRemoveOnFinish": false, "autoRemoveOnFinish": false,
@ -329,6 +332,7 @@
"x": 7, "x": 7,
"y": 7 "y": 7
}, },
"_positionType": 1,
"positionType": 1, "positionType": 1,
"emitterMode": 0, "emitterMode": 0,
"gravity": { "gravity": {
@ -372,7 +376,6 @@
}, },
"_children": [], "_children": [],
"_active": true, "_active": true,
"_level": 2,
"_components": [ "_components": [
{ {
"__id__": 9 "__id__": 9
@ -399,17 +402,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",
@ -425,7 +417,19 @@
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",
@ -435,6 +439,13 @@
"__id__": 8 "__id__": 8
}, },
"_enabled": true, "_enabled": true,
"_materials": [
{
"__uuid__": "eca5d2f2-8ef6-41c2-bbe6-f9c79d09c432"
}
],
"_srcBlendFactor": 770,
"_dstBlendFactor": 771,
"_spriteFrame": { "_spriteFrame": {
"__uuid__": "a2170e4c-df31-41ef-be73-f4f605e75821" "__uuid__": "a2170e4c-df31-41ef-be73-f4f605e75821"
}, },
@ -449,12 +460,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": ""
}, },
{ {
@ -476,6 +484,9 @@
"__id__": 1 "__id__": 1
}, },
"_enabled": true, "_enabled": true,
"_materials": [],
"_srcBlendFactor": 770,
"_dstBlendFactor": 771,
"_spriteFrame": null, "_spriteFrame": null,
"_type": 0, "_type": 0,
"_sizeMode": 0, "_sizeMode": 0,
@ -488,10 +499,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": ""
}, },
{ {
@ -549,8 +557,8 @@
}, },
"_enabled": true, "_enabled": true,
"animComp": null, "animComp": null,
"baseSpeed": 300, "baseSpeed": 50,
"speed": 200, "speed": 50,
"lastMovedAt": 0, "lastMovedAt": 0,
"eps": 0.1, "eps": 0.1,
"magicLeanLowerBound": 0.414, "magicLeanLowerBound": 0.414,

View File

@ -440,7 +440,7 @@
"array": [ "array": [
0, 0,
0, 0,
209.7912853806815, 216.05530045313827,
0, 0,
0, 0,
0, 0,
@ -2991,9 +2991,6 @@
"loadingPrefab": { "loadingPrefab": {
"__uuid__": "f2a3cece-30bf-4f62-bc20-34d44a9ddf98" "__uuid__": "f2a3cece-30bf-4f62-bc20-34d44a9ddf98"
}, },
"wechatLoginTips": {
"__id__": 68
},
"_id": "51YNpecnJBea8vzAxGdkv2" "_id": "51YNpecnJBea8vzAxGdkv2"
}, },
{ {

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +0,0 @@
{
"ver": "1.2.5",
"uuid": "475b849b-44b3-4390-982d-bd0d9e695093",
"asyncLoadAssets": false,
"autoReleaseAssets": false,
"subMetas": {}
}

View File

@ -8,11 +8,11 @@ module.export = cc.Class({
}, },
baseSpeed: { baseSpeed: {
type: cc.Float, type: cc.Float,
default: 300, default: 50,
}, },
speed: { speed: {
type: cc.Float, type: cc.Float,
default: 300 default: 50
}, },
lastMovedAt: { lastMovedAt: {
type: cc.Float, type: cc.Float,
@ -35,12 +35,6 @@ module.export = cc.Class({
// LIFE-CYCLE CALLBACKS: // LIFE-CYCLE CALLBACKS:
start() { start() {
const self = this; const self = this;
self.contactedControlledPlayers = [];
self.contactedNPCPlayers = [];
self.coveringShelterZReducers = [];
self.computedNewDifferentPosLocalToParentWithinCurrentFrame = null;
self.actionMangerSingleton = new cc.ActionManager();
self.activeDirection = { self.activeDirection = {
dx: 0, dx: 0,
dy: 0 dy: 0
@ -61,7 +55,6 @@ module.export = cc.Class({
}; };
const canvasNode = self.mapNode.parent; const canvasNode = self.mapNode.parent;
self.mapIns = self.mapNode.getComponent("Map"); self.mapIns = self.mapNode.getComponent("Map");
self.contactedBarriers = [];
const joystickInputControllerScriptIns = canvasNode.getComponent("TouchEventsManager"); const joystickInputControllerScriptIns = canvasNode.getComponent("TouchEventsManager");
self.ctrl = joystickInputControllerScriptIns; self.ctrl = joystickInputControllerScriptIns;
self.animComp = self.node.getComponent(cc.Animation); self.animComp = self.node.getComponent(cc.Animation);
@ -93,330 +86,10 @@ module.export = cc.Class({
} }
}, },
_addCoveringShelterZReducer(comp) {
const self = this;
for (let coveringShelterZReducer of self.coveringShelterZReducers) {
if (coveringShelterZReducer._id == comp._id) {
return false;
}
}
self.coveringShelterZReducers.push(comp);
return true;
},
_removeCoveringShelterZReducer(comp) {
const self = this;
self.coveringShelterZReducers = self.coveringShelterZReducers.filter((coveringShelterZReducer) => {
return coveringShelterZReducer._id != comp._id;
});
return true;
},
_addContactedBarrier(collider) {
const self = this;
if (!self.contactedBarriers) {
cc.log("self.contactedBarriers is null or undefined" + self.contactedBarriers)
}
for (let contactedBarrier of self.contactedBarriers) {
if (contactedBarrier._id == collider._id) {
return false;
}
}
self.contactedBarriers.push(collider);
return true;
},
_removeContactedBarrier(collider) {
const self = this;
self.contactedBarriers = self.contactedBarriers.filter((contactedBarrier) => {
return contactedBarrier._id != collider._id;
});
return true;
},
_addContactedControlledPlayers(comp) {
const self = this;
for (let aComp of self.contactedControlledPlayers) {
if (aComp.uuid == comp.uuid) {
return false;
}
}
self.contactedControlledPlayers.push(comp);
return true;
},
_removeContactedControlledPlayer(comp) {
const self = this;
self.contactedControlledPlayers = self.contactedControlledPlayers.filter((aComp) => {
return aComp.uuid != comp.uuid;
});
return true;
},
_addContactedNPCPlayers(comp) {
const self = this;
for (let aComp of self.contactedNPCPlayers) {
if (aComp.uuid == comp.uuid) {
return false;
}
}
self.contactedNPCPlayers.push(comp);
return true;
},
_removeContactedNPCPlayer(comp) {
const self = this;
self.contactedNPCPlayers = self.contactedNPCPlayers.filter((aComp) => {
return aComp.uuid != comp.uuid;
});
return true;
},
_canMoveBy(vecToMoveBy) {
const self = this;
const computedNewDifferentPosLocalToParentWithinCurrentFrame = self.node.position.add(vecToMoveBy);
self.computedNewDifferentPosLocalToParentWithinCurrentFrame = computedNewDifferentPosLocalToParentWithinCurrentFrame;
if (tileCollisionManager.isOutOfMapNode(self.mapNode, computedNewDifferentPosLocalToParentWithinCurrentFrame)) {
return false;
}
const currentSelfColliderCircle = self.node.getComponent(cc.CircleCollider);
let nextSelfColliderCircle = null;
if (0 < self.contactedBarriers.length) {
/* To avoid unexpected buckling. */
const mutatedVecToMoveBy = vecToMoveBy.mul(5); // To help it escape the engaged `contactedBarriers`.
nextSelfColliderCircle = {
position: self.node.position.add(mutatedVecToMoveBy).add(currentSelfColliderCircle.offset),
radius: currentSelfColliderCircle.radius,
};
} else {
nextSelfColliderCircle = {
position: computedNewDifferentPosLocalToParentWithinCurrentFrame.add(currentSelfColliderCircle.offset),
radius: currentSelfColliderCircle.radius,
};
}
for (let contactedBarrier of self.contactedBarriers) {
let contactedBarrierPolygonLocalToParentWithinCurrentFrame = [];
for (let p of contactedBarrier.points) {
contactedBarrierPolygonLocalToParentWithinCurrentFrame.push(contactedBarrier.node.position.add(p));
}
if (cc.Intersection.pointInPolygon(nextSelfColliderCircle.position, contactedBarrierPolygonLocalToParentWithinCurrentFrame)) {
// Make sure that the player is "leaving" the PolygonCollider.
return false;
}
if (cc.Intersection.polygonCircle(contactedBarrierPolygonLocalToParentWithinCurrentFrame, nextSelfColliderCircle)) {
if (null == self.firstContactedEdge) {
return false;
}
if (null != self.firstContactedEdge && self.firstContactedEdge.associatedBarrier != contactedBarrier) {
const res = self._calculateTangentialMovementAttrs(nextSelfColliderCircle, contactedBarrier);
if (null == res.contactedEdge) {
// Otherwise, the current movement is going to transit smoothly onto the next PolygonCollider.
return false;
}
}
}
}
return true;
/*
* In a subclass, use
*
* _canMoveBy(vecToMoveBy) {
* BasePlayer.prototype._canMoveBy.call(this, vecToMoveBy);
* // Customized codes.
* }
*
* Reference http://www.cocos2d-x.org/docs/creator/manual/en/scripting/reference/class.html#override
*/
},
_calculateTangentialMovementAttrs(currentSelfColliderCircle, contactedBarrier) {
/*
* Theoretically when the `contactedBarrier` is a convex polygon and the `PlayerCollider` is a circle, there can be only 1 `contactedEdge` for each `contactedBarrier`. Except only for around the corner.
*
* We should avoid the possibility of players hitting the "corners of convex polygons" by map design wherever & whenever possible.
*
*/
const self = this;
const sDir = self.activeDirection;
const currentSelfColliderCircleCentrePos = (currentSelfColliderCircle.position ? currentSelfColliderCircle.position : self.node.position.add(currentSelfColliderCircle.offset));
const currentSelfColliderCircleRadius = currentSelfColliderCircle.radius;
let contactedEdgeCandidateList = [];
let skinDepthThreshold = 0.45*currentSelfColliderCircleRadius;
for (let i = 0; i < contactedBarrier.points.length; ++i) {
const stPoint = contactedBarrier.points[i].add(contactedBarrier.offset).add(contactedBarrier.node.position);
const edPoint = (i == contactedBarrier.points.length - 1 ? contactedBarrier.points[0].add(contactedBarrier.offset).add(contactedBarrier.node.position) : contactedBarrier.points[1 + i].add(contactedBarrier.offset).add(contactedBarrier.node.position));
const tmpVSt = stPoint.sub(currentSelfColliderCircleCentrePos);
const tmpVEd = edPoint.sub(currentSelfColliderCircleCentrePos);
const crossProdScalar = tmpVSt.cross(tmpVEd);
if (0 < crossProdScalar) {
// If moving parallel along `st <-> ed`, the trajectory of `currentSelfColliderCircleCentrePos` will cut inside the polygon.
continue;
}
const dis = cc.Intersection.pointLineDistance(currentSelfColliderCircleCentrePos, stPoint, edPoint, true);
if (dis > currentSelfColliderCircleRadius) continue;
if (dis < skinDepthThreshold) continue;
contactedEdgeCandidateList.push({
st: stPoint,
ed: edPoint,
associatedBarrier: contactedBarrier,
});
}
let contactedEdge = null;
let contactedEdgeDir = null;
let largestInnerProdAbs = Number.MIN_VALUE;
if (0 < contactedEdgeCandidateList.length) {
const sDirMag = Math.sqrt(sDir.dx * sDir.dx + sDir.dy * sDir.dy);
for (let contactedEdgeCandidate of contactedEdgeCandidateList) {
const tmp = contactedEdgeCandidate.ed.sub(contactedEdgeCandidate.st);
const contactedEdgeDirCandidate = {
dx: tmp.x,
dy: tmp.y,
};
const contactedEdgeDirCandidateMag = Math.sqrt(contactedEdgeDirCandidate.dx * contactedEdgeDirCandidate.dx + contactedEdgeDirCandidate.dy * contactedEdgeDirCandidate.dy);
const innerDotProd = (sDir.dx * contactedEdgeDirCandidate.dx + sDir.dy * contactedEdgeDirCandidate.dy)/(sDirMag * contactedEdgeDirCandidateMag);
const innerDotProdThresholdMag = 0.7;
if ((0 > innerDotProd && innerDotProd > -innerDotProdThresholdMag) || (0 < innerDotProd && innerDotProd < innerDotProdThresholdMag)) {
// Intentionally left blank, in this case the player is trying to escape from the `contactedEdge`.
continue;
} else if (innerDotProd > 0) {
const abs = Math.abs(innerDotProd);
if (abs > largestInnerProdAbs) {
contactedEdgeDir = contactedEdgeDirCandidate;
contactedEdge = contactedEdgeCandidate;
}
} else {
const abs = Math.abs(innerDotProd);
if (abs > largestInnerProdAbs) {
contactedEdgeDir = {
dx: -contactedEdgeDirCandidate.dx,
dy: -contactedEdgeDirCandidate.dy,
};
contactedEdge = contactedEdgeCandidate;
}
}
}
}
return {
contactedEdgeDir: contactedEdgeDir,
contactedEdge: contactedEdge,
};
},
_calculateVecToMoveByWithChosenDir(elapsedTime, sDir) {
if (0 == sDir.dx && 0 == sDir.dy) {
return cc.v2();
}
const self = this;
const distanceToMove = (self.speed * elapsedTime);
const denominator = Math.sqrt(sDir.dx * sDir.dx + sDir.dy * sDir.dy);
const unitProjDx = (sDir.dx / denominator);
const unitProjDy = (sDir.dy / denominator);
return cc.v2(
distanceToMove * unitProjDx,
distanceToMove * unitProjDy,
);
},
_calculateVecToMoveBy(elapsedTime) {
const self = this;
// Note that `sDir` used in this method MUST BE a copy in RAM.
let sDir = {
dx: self.activeDirection.dx,
dy: self.activeDirection.dy,
};
if (0 == sDir.dx && 0 == sDir.dy) {
return cc.v2();
}
self.firstContactedEdge = null; // Reset everytime (temporary algorithm design, might change later).
if (0 < self.contactedBarriers.length) {
/*
* Hardcoded to take care of only the 1st `contactedEdge` of the 1st `contactedBarrier` for now. Each `contactedBarrier` must be "counterclockwisely convex polygonal", otherwise sliding doesn't work!
*
*/
const contactedBarrier = self.contactedBarriers[0];
const currentSelfColliderCircle = self.node.getComponent(cc.CircleCollider);
const res = self._calculateTangentialMovementAttrs(currentSelfColliderCircle, contactedBarrier);
if (res.contactedEdge) {
self.firstContactedEdge = res.contactedEdge;
sDir = res.contactedEdgeDir;
}
}
return self._calculateVecToMoveByWithChosenDir(elapsedTime, sDir);
},
update(dt) { update(dt) {
const self = this;
const vecToMoveBy = self._calculateVecToMoveBy(self.mapIns.rollbackEstimatedDt); // To be consistent w.r.t. rollback dynamics
// console.log("activeDirection=", self.activeDirection, "vecToMoveBy=", vecToMoveBy, ", computedNewDifferentPosLocalToParentWithinCurrentFrame=", self.computedNewDifferentPosLocalToParentWithinCurrentFrame);
if (self._canMoveBy(vecToMoveBy)) {
self.node.position = self.computedNewDifferentPosLocalToParentWithinCurrentFrame;
}
}, },
lateUpdate(dt) { lateUpdate(dt) {
const self = this;
const now = new Date().getTime();
self.lastMovedAt = now;
},
onCollisionEnter(other, self) {
const playerScriptIns = self.node.getComponent("SelfPlayer");
switch (other.node.name) {
case "NPCPlayer":
if ("NPCPlayer" != self.node.name) {
other.node.getComponent('NPCPlayer').showProfileTrigger();
}
playerScriptIns._addContactedNPCPlayers(other);
break;
case "PolygonBoundaryBarrier":
playerScriptIns._addContactedBarrier(other);
break;
case "PolygonBoundaryShelter":
break;
case "PolygonBoundaryShelterZReducer":
playerScriptIns._addCoveringShelterZReducer(other);
if (1 == playerScriptIns.coveringShelterZReducers.length) {
setLocalZOrder(self.node, 2);
}
break;
default:
break;
}
},
onCollisionStay(other, self) {
// TBD.
},
onCollisionExit(other, self) {
const playerScriptIns = self.getComponent("SelfPlayer");
switch (other.node.name) {
case "NPCPlayer":
other.node.getComponent('NPCPlayer').hideProfileTrigger();
playerScriptIns._removeContactedNPCPlayer(other);
break;
case "PolygonBoundaryBarrier":
playerScriptIns._removeContactedBarrier(other);
break;
case "PolygonBoundaryShelter":
break;
case "PolygonBoundaryShelterZReducer":
playerScriptIns._removeCoveringShelterZReducer(other);
if (0 == playerScriptIns.coveringShelterZReducers.length) {
setLocalZOrder(self.node, 5);
}
break;
default:
break;
}
}, },
_generateRandomDirection() { _generateRandomDirection() {

View File

@ -22,7 +22,7 @@ cc.Class({
if (!self.mapScriptIns) return; if (!self.mapScriptIns) return;
if (!self.mapScriptIns.selfPlayerInfo) return; if (!self.mapScriptIns.selfPlayerInfo) return;
if (!self.mapScriptIns.playerRichInfoDict) return; if (!self.mapScriptIns.playerRichInfoDict) return;
const selfPlayerRichInfo = self.mapScriptIns.playerRichInfoDict[self.mapScriptIns.selfPlayerInfo.id]; const selfPlayerRichInfo = self.mapScriptIns.playerRichInfoDict.get(self.mapScriptIns.selfPlayerInfo.id);
if (!selfPlayerRichInfo) return; if (!selfPlayerRichInfo) return;
const selfPlayerNode = selfPlayerRichInfo.node; const selfPlayerNode = selfPlayerRichInfo.node;
if (!selfPlayerNode) return; if (!selfPlayerNode) return;

View File

@ -26,19 +26,6 @@ cc.Class({
// LIFE-CYCLE CALLBACKS: // LIFE-CYCLE CALLBACKS:
onLoad() { onLoad() {
// WARNING: 不能保证在ws连接成功并且拿到boundRoomId后才运行到此处。
if (cc.sys.platform == cc.sys.WECHAT_GAME) {
const boundRoomId = window.getBoundRoomIdFromPersistentStorage();
const wxToShareMessage = {
title: '夺宝大作战',
imageUrl: 'https://mmocgame.qpic.cn/wechatgame/ibxA6JVNslX02zq6aAWCZiaWTXLYGorrVgUszo3WH1oL1CFDcFU7VKPRXPFiadxagMR/0',
imageUrlId: 'FiLZpa5FT5GgEeEagzGBsA',
query: 'expectedRoomId=' + boundRoomId,
};
console.warn("The boundRoomId for sharing: ", boundRoomId, " wxToShareMessage ", wxToShareMessage);
wx.showShareMenu();
wx.onShareAppMessage(() => (wxToShareMessage));
}
}, },
init() { init() {
@ -73,11 +60,7 @@ cc.Class({
exitBtnOnClick(evt) { exitBtnOnClick(evt) {
window.clearBoundRoomIdInBothVolatileAndPersistentStorage(); window.clearBoundRoomIdInBothVolatileAndPersistentStorage();
window.closeWSConnection(); window.closeWSConnection();
if (cc.sys.platform == cc.sys.WECHAT_GAME) {
cc.director.loadScene('wechatGameLogin');
} else {
cc.director.loadScene('login'); cc.director.loadScene('login');
}
}, },
updatePlayersInfo(playerMetas) { updatePlayersInfo(playerMetas) {
@ -107,23 +90,7 @@ cc.Class({
if (remoteUrl == null || remoteUrl == '') { if (remoteUrl == null || remoteUrl == '') {
cc.log(`No avatar to show for :`); cc.log(`No avatar to show for :`);
cc.log(playerMeta); cc.log(playerMeta);
remoteUrl = 'http://wx.qlogo.cn/mmopen/PiajxSqBRaEJUWib5D85KXWHumaxhU4E9XOn9bUpCNKF3F4ibfOj8JYHCiaoosvoXCkTmOQE1r2AKKs8ObMaz76EdA/0'
} }
cc.loader.load({
url: remoteUrl,
type: 'jpg'
}, function(err, texture) {
if (null != err ) {
console.error(err);
} else {
if (null == texture) {
return;
}
const sf = new cc.SpriteFrame();
sf.setTexture(texture);
playerInfoNode.getChildByName('avatarMask').getChildByName('avatar').getComponent(cc.Sprite).spriteFrame = sf;
}
});
})(); })();
function isEmptyString(str) { function isEmptyString(str) {

View File

@ -1,5 +1,6 @@
const i18n = require('LanguageData'); const i18n = require('LanguageData');
i18n.init(window.language); // languageID should be equal to the one we input in New Language ID input field i18n.init(window.language); // languageID should be equal to the one we input in New Language ID input field
cc.Class({ cc.Class({
extends: cc.Component, extends: cc.Component,
@ -59,11 +60,7 @@ cc.Class({
loadingPrefab: { loadingPrefab: {
default: null, default: null,
type: cc.Prefab type: cc.Prefab
}, }
wechatLoginTips: {
default: null,
type: cc.Label,
},
}, },
// LIFE-CYCLE CALLBACKS: // LIFE-CYCLE CALLBACKS:
@ -88,22 +85,18 @@ cc.Class({
self.getRetCodeList(); self.getRetCodeList();
self.getRegexList(); self.getRegexList();
const isUsingX5BlinkKernelOrWebkitWeChatKernel = window.isUsingX5BlinkKernelOrWebkitWeChatKernel(); self.phoneNumberTips.active = true;
//const isUsingX5BlinkKernelOrWebkitWeChatKernel = true; self.smsLoginCaptchaButton.active = true;
if (!CC_DEBUG) {
self.phoneNumberTips.active = !isUsingX5BlinkKernelOrWebkitWeChatKernel;
self.smsLoginCaptchaButton.active = !isUsingX5BlinkKernelOrWebkitWeChatKernel;
self.captchaTips.active = !isUsingX5BlinkKernelOrWebkitWeChatKernel; self.captchaTips.active = true;
self.phoneCountryCodeInput.active = !isUsingX5BlinkKernelOrWebkitWeChatKernel; self.phoneCountryCodeInput.active = true;
self.phoneNumberInput.active = !isUsingX5BlinkKernelOrWebkitWeChatKernel; self.phoneNumberInput.active = true;
self.smsLoginCaptchaInput.active = !isUsingX5BlinkKernelOrWebkitWeChatKernel; self.smsLoginCaptchaInput.active = true;
self.phoneLabel.active = !isUsingX5BlinkKernelOrWebkitWeChatKernel; self.phoneLabel.active = true;
self.smsLoginCaptchaLabel.active = !isUsingX5BlinkKernelOrWebkitWeChatKernel; self.smsLoginCaptchaLabel.active = true;
self.loginButton.active = !isUsingX5BlinkKernelOrWebkitWeChatKernel; self.loginButton.active = true;
}
self.checkPhoneNumber = self.checkPhoneNumber.bind(self); self.checkPhoneNumber = self.checkPhoneNumber.bind(self);
self.checkIntAuthTokenExpire = self.checkIntAuthTokenExpire.bind(self); self.checkIntAuthTokenExpire = self.checkIntAuthTokenExpire.bind(self);
self.checkCaptcha = self.checkCaptcha.bind(self); self.checkCaptcha = self.checkCaptcha.bind(self);
@ -124,7 +117,6 @@ cc.Class({
cc.error(err.message || err); cc.error(err.message || err);
return; return;
} }
if (false == (cc.sys.platform == cc.sys.WECHAT_GAME)) {
// Otherwise, `window.RoomDownsyncFrame` is already assigned. // Otherwise, `window.RoomDownsyncFrame` is already assigned.
let protoRoot = new protobuf.Root; let protoRoot = new protobuf.Root;
window.protobuf.parse(textAsset.text, protoRoot); window.protobuf.parse(textAsset.text, protoRoot);
@ -132,7 +124,6 @@ cc.Class({
window.BattleColliderInfo = protoRoot.lookupType("treasurehunterx.BattleColliderInfo"); window.BattleColliderInfo = protoRoot.lookupType("treasurehunterx.BattleColliderInfo");
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(
() => { () => {
const intAuthToken = JSON.parse(cc.sys.localStorage.getItem('selfPlayer')).intAuthToken; const intAuthToken = JSON.parse(cc.sys.localStorage.getItem('selfPlayer')).intAuthToken;
@ -140,19 +131,6 @@ cc.Class({
}, },
() => { () => {
window.clearBoundRoomIdInBothVolatileAndPersistentStorage(); window.clearBoundRoomIdInBothVolatileAndPersistentStorage();
if ( (CC_DEBUG || isUsingX5BlinkKernelOrWebkitWeChatKernel) ) {
if (null != qDict && qDict["code"]) {
const code = qDict["code"];
console.log("Got the wx authcode: ", code, "while at full url: " + window.location.href);
self.useWXCodeLogin(code);
} else {
if (isUsingX5BlinkKernelOrWebkitWeChatKernel) {
self.getWechatCode(null);
} else {
// Deliberately left blank.
}
}
}
} }
); );
}); });
@ -355,46 +333,10 @@ cc.Class({
}); });
}, },
onWechatLoggedIn(res) {
const self = this;
if (res.ret === self.retCodeDict.OK) {
self.enableInteractiveControls(false);
const date = Number(res.expiresAt);
const selfPlayer = {
expiresAt: date,
playerId: res.playerId,
intAuthToken: res.intAuthToken,
displayName: res.displayName,
avatar: res.avatar,
}
cc.sys.localStorage.setItem('selfPlayer', JSON.stringify(selfPlayer));
const qDict = window.getQueryParamDict();
const expectedRoomId = qDict["expectedRoomId"];
if (null != expectedRoomId) {
console.log("onWechatLoggedIn using expectedRoomId == " + expectedRoomId);
window.clearBoundRoomIdInBothVolatileAndPersistentStorage();
}
// To remove "code=XXX" in "query string".
window.history.replaceState(qDict, null, window.location.pathname);
self.useTokenLogin(res.intAuthToken);
} else {
cc.sys.localStorage.removeItem("selfPlayer");
window.clearBoundRoomIdInBothVolatileAndPersistentStorage();
self.wechatLoginTips.string = constants.ALERT.TIP_LABEL.WECHAT_LOGIN_FAILS + ", errorCode = " + res.ret;
// To remove "code=XXX" in "query string".
window.history.replaceState({}, null, window.location.pathname);
}
},
onLoggedIn(res) { onLoggedIn(res) {
const self = this; const self = this;
cc.log(`OnLoggedIn ${JSON.stringify(res)}.`) cc.log(`OnLoggedIn ${JSON.stringify(res)}.`)
if (res.ret === self.retCodeDict.OK) { if (res.ret === self.retCodeDict.OK) {
if(window.isUsingX5BlinkKernelOrWebkitWeChatKernel()) {
window.initWxSdk = self.initWxSdk.bind(self);
window.initWxSdk();
}
self.enableInteractiveControls(false); self.enableInteractiveControls(false);
const date = Number(res.expiresAt); const date = Number(res.expiresAt);
const selfPlayer = { const selfPlayer = {
@ -451,105 +393,4 @@ cc.Class({
} }
} }
}, },
useWXCodeLogin(_code) {
const self = this;
NetworkUtils.ajax({
url: backendAddress.PROTOCOL + '://' + backendAddress.HOST + ':' + backendAddress.PORT + constants.ROUTE_PATH.API + constants.ROUTE_PATH.PLAYER + constants.ROUTE_PATH.VERSION + constants.ROUTE_PATH.WECHAT + constants.ROUTE_PATH.LOGIN,
type: "POST",
data: {
code: _code
},
success: function(res) {
self.onWechatLoggedIn(res);
},
error: function(xhr, status, errMsg) {
console.log("Login attempt `useWXCodeLogin` failed, about to execute `clearBoundRoomIdInBothVolatileAndPersistentStorage`.");
cc.sys.localStorage.removeItem("selfPlayer");
window.clearBoundRoomIdInBothVolatileAndPersistentStorage();
self.wechatLoginTips.string = constants.ALERT.TIP_LABEL.WECHAT_LOGIN_FAILS + ", errorMsg =" + errMsg;
window.history.replaceState({}, null, window.location.pathname);
},
});
},
getWechatCode(evt) {
let self = this;
self.wechatLoginTips.string = "";
const wechatServerEndpoint = wechatAddress.PROTOCOL + "://" + wechatAddress.HOST + ((null != wechatAddress.PORT && "" != wechatAddress.PORT.trim()) ? (":" + wechatAddress.PORT) : "");
const url = wechatServerEndpoint + constants.WECHAT.AUTHORIZE_PATH + "?" + wechatAddress.APPID_LITERAL + "&" + constants.WECHAT.REDIRECT_RUI_KEY + NetworkUtils.encode(window.location.href) + "&" + constants.WECHAT.RESPONSE_TYPE + "&" + constants.WECHAT.SCOPE + constants.WECHAT.FIN;
console.log("To visit wechat auth addr: " + url);
window.location.href = url;
},
initWxSdk() {
const selfPlayer = JSON.parse(cc.sys.localStorage.getItem('selfPlayer'));
const origUrl = window.location.protocol + "//" + window.location.host + window.location.pathname;
/*
* The `shareLink` must
* - have its 2nd-order-domain registered as trusted 2nd-order under the targetd `res.jsConfig.app_id`, and
* - extracted from current window.location.href.
*/
const shareLink = origUrl;
const updateAppMsgShareDataObj = {
type: 'link', // 分享类型,music、video或link不填默认为link
dataUrl: '', // 如果type是music或video则要提供数据链接默认为空
title: document.title, // 分享标题
desc: 'Let\'s play together!', // 分享描述
link: shareLink + (cc.sys.localStorage.getItem('boundRoomId') ? "" : ("?expectedRoomId=" + cc.sys.localStorage.getItem('boundRoomId'))),
imgUrl: origUrl + "/favicon.ico", // 分享图标
success: function() {
// 设置成功
}
};
const menuShareTimelineObj = {
title: document.title, // 分享标题
link: shareLink + (cc.sys.localStorage.getItem('boundRoomId') ? "" : ("?expectedRoomId=" + cc.sys.localStorage.getItem('boundRoomId'))),
imgUrl: origUrl + "/favicon.ico", // 分享图标
success: function() {}
};
const wxConfigUrl = (window.isUsingWebkitWechatKernel() ? window.atFirstLocationHref : window.location.href);
//接入微信登录接口
NetworkUtils.ajax({
"url": backendAddress.PROTOCOL + '://' + backendAddress.HOST + ':' + backendAddress.PORT + constants.ROUTE_PATH.API + constants.ROUTE_PATH.PLAYER + constants.ROUTE_PATH.VERSION + constants.ROUTE_PATH.WECHAT + constants.ROUTE_PATH.JSCONFIG,
type: "POST",
data: {
"url": wxConfigUrl,
"intAuthToken": selfPlayer.intAuthToken,
},
success: function(res) {
if (constants.RET_CODE.OK != res.ret) {
console.log("cannot get the wsConfig. retCode == " + res.ret);
return;
}
const jsConfig = res.jsConfig;
console.log(updateAppMsgShareDataObj);
const configData = {
debug: CC_DEBUG, // 开启调试模式,调用的所有api的返回值会在客户端alert出来若要查看传入的参数可以在pc端打开参数信息会通过log打出仅在pc端时才会打印。
appId: jsConfig.app_id, // 必填,公众号的唯一标识
timestamp: jsConfig.timestamp.toString(), // 必填,生成签名的时间戳
nonceStr: jsConfig.nonce_str, // 必填,生成签名的随机串
jsApiList: ['onMenuShareAppMessage', 'onMenuShareTimeline'],
signature: jsConfig.signature, // 必填,签名
};
console.log("config url: " + wxConfigUrl);
console.log("wx.config: ");
console.log(configData);
wx.config(configData);
console.log("Current window.location.href: " + window.location.href);
wx.ready(function() {
console.log("Here is wx.ready.")
wx.onMenuShareAppMessage(updateAppMsgShareDataObj);
wx.onMenuShareTimeline(menuShareTimelineObj);
});
wx.error(function(res) {
console.error("wx config fails and error is " + JSON.stringify(res));
});
},
error: function(xhr, status, errMsg) {
console.log("cannot get the wsConfig. errMsg == " + errMsg);
},
});
},
}); });

View File

@ -1,6 +1,9 @@
const i18n = require('LanguageData'); const i18n = require('LanguageData');
i18n.init(window.language); // languageID should be equal to the one we input in New Language ID input field i18n.init(window.language); // languageID should be equal to the one we input in New Language ID input field
const collisions = require('./modules/Collisions');
const RingBuffer = require('./RingBuffer');
window.ALL_MAP_STATES = { window.ALL_MAP_STATES = {
VISUAL: 0, // For free dragging & zooming. VISUAL: 0, // For free dragging & zooming.
EDITING_BELONGING: 1, EDITING_BELONGING: 1,
@ -15,9 +18,11 @@ window.ALL_BATTLE_STATES = {
}; };
window.MAGIC_ROOM_DOWNSYNC_FRAME_ID = { window.MAGIC_ROOM_DOWNSYNC_FRAME_ID = {
BATTLE_READY_TO_START: -99,
PLAYER_ADDED_AND_ACKED: -98, PLAYER_ADDED_AND_ACKED: -98,
PLAYER_READDED_AND_ACKED: -97, PLAYER_READDED_AND_ACKED: -97,
BATTLE_READY_TO_START: -1,
BATTLE_START: 0,
}; };
cc.Class({ cc.Class({
@ -56,14 +61,6 @@ cc.Class({
type: cc.Prefab, type: cc.Prefab,
default: null, default: null,
}, },
polygonBoundaryShelterPrefab: {
type: cc.Prefab,
default: null,
},
polygonBoundaryShelterZReducerPrefab: {
type: cc.Prefab,
default: null,
},
keyboardInputControllerNode: { keyboardInputControllerNode: {
type: cc.Node, type: cc.Node,
default: null default: null
@ -127,8 +124,9 @@ cc.Class({
type: cc.Float, type: cc.Float,
default: 1.0/60 default: 1.0/60
}, },
rollbackInMainUpdate: { maxChasingRenderFramesPerUpdate: {
default: true type: cc.Integer,
default: 10
}, },
}, },
@ -136,27 +134,46 @@ cc.Class({
return (0 == inputFrameId%10); return (0 == inputFrameId%10);
}, },
_dumpToFullFrameCache: function(fullFrame) { _dumpToRenderCache: function(roomDownsyncFrame) {
const self = this; const self = this;
self.recentFrameCache.set(fullFrame.id, fullFrame); const minToKeepRenderFrameId = self.lastAllConfirmedRenderFrameId;
if (fullFrame.id >= self.recentFrameCacheEd) { while (0 < self.recentRenderCache.cnt && self.recentRenderCache.stFrameId < minToKeepRenderFrameId) {
// Should be consecutive self.recentRenderCache.pop();
++self.recentFrameCacheEd;
} }
while (self.recentFrameCacheEd - self.recentFrameCacheSt >= self.recentFrameCacheMaxCount) { if (self.recentRenderCache.stFrameId < minToKeepRenderFrameId) {
self.recentFrameCache.delete(self.recentFrameCacheSt++); console.warn("Weird dumping of RENDER frame: self.renderFrame=", self.renderFrame, ", self.recentInputCache=", self._stringifyRecentInputCache(false), ", self.recentRenderCache=", self._stringifyRecentRenderCache(false), ", self.lastAllConfirmedRenderFrameId=", self.lastAllConfirmedRenderFrameId, ", self.lastAllConfirmedInputFrameId=", self.lastAllConfirmedInputFrameId);
}
const existing = self.recentRenderCache.getByFrameId(roomDownsyncFrame.id);
if (null != existing) {
existing.players = roomDownsyncFrame.players;
existing.sentAt = roomDownsyncFrame.sentAt;
existing.countdownNanos = roomDownsyncFrame.countdownNanos;
existing.treasures = roomDownsyncFrame.treasures;
existing.bullets = roomDownsyncFrame.bullets;
existing.speedShoes = roomDownsyncFrame.speedShoes;
existing.guardTowers = roomDownsyncFrame.guardTowers;
existing.playerMetas = roomDownsyncFrame.playerMetas;
} else {
self.recentRenderCache.put(roomDownsyncFrame);
} }
}, },
_dumpToInputCache: function(inputFrameDownsync) { _dumpToInputCache: function(inputFrameDownsync) {
const self = this; const self = this;
self.recentInputCache.set(inputFrameDownsync.inputFrameId, inputFrameDownsync); let minToKeepInputFrameId = self._convertToInputFrameId(self.lastAllConfirmedRenderFrameId, self.inputDelayFrames); // [WARNING] This could be different from "self.lastAllConfirmedInputFrameId". We'd like to keep the corresponding inputFrame for "self.lastAllConfirmedRenderFrameId" such that a rollback could place "self.chaserRenderFrameId = self.lastAllConfirmedRenderFrameId" for the worst case incorrect prediction.
if (inputFrameDownsync.inputFrameId >= self.recentInputCacheEd) { if (minToKeepInputFrameId > self.lastAllConfirmedInputFrameId) minToKeepInputFrameId = self.lastAllConfirmedInputFrameId;
// Should be consecutive while (0 < self.recentInputCache.cnt && self.recentInputCache.stFrameId < minToKeepInputFrameId) {
++self.recentInputCacheEd; self.recentInputCache.pop();
} }
while (self.recentInputCacheEd - self.recentInputCacheSt >= self.recentInputCacheMaxCount) { if (self.recentInputCache.stFrameId < minToKeepInputFrameId) {
self.recentInputCache.delete(self.recentInputCacheSt++); console.warn("Weird dumping of INPUT frame: self.renderFrame=", self.renderFrame, ", self.recentInputCache=", self._stringifyRecentInputCache(false), ", self.recentRenderCache=", self._stringifyRecentRenderCache(false), ", self.lastAllConfirmedRenderFrameId=", self.lastAllConfirmedRenderFrameId, ", self.lastAllConfirmedInputFrameId=", self.lastAllConfirmedInputFrameId);
}
const existing = self.recentInputCache.getByFrameId(inputFrameDownsync.inputFrameId);
if (null != existing) {
existing.inputList = inputFrameDownsync.inputList;
existing.confirmedList = inputFrameDownsync.confirmedList;
} else {
self.recentInputCache.put(inputFrameDownsync);
} }
}, },
@ -170,47 +187,40 @@ cc.Class({
}, },
_shouldGenerateInputFrameUpsync(renderFrameId) { _shouldGenerateInputFrameUpsync(renderFrameId) {
return ((renderFrameId & 3) == 0); // 3 is 0x0011 return ((renderFrameId & ((1 << this.inputScaleFrames)-1)) == 0);
},
_allConfirmed(confirmedList) {
return (confirmedList+1) == (1 << this.playerRichInfoDict.size);
}, },
_generateInputFrameUpsync(inputFrameId) { _generateInputFrameUpsync(inputFrameId) {
const instance = this; const self = this;
if ( if (
null == instance.ctrl || null == self.ctrl ||
null == instance.selfPlayerInfo null == self.selfPlayerInfo
) { ) {
return [null, null]; return [null, null];
} }
const discreteDir = instance.ctrl.getDiscretizedDirection(); const joinIndex = self.selfPlayerInfo.joinIndex;
let prefabbedInputList = null; const discreteDir = self.ctrl.getDiscretizedDirection();
let selfPlayerLastInputFrameInput = 0; const previousInputFrameDownsyncWithPrediction = self.getCachedInputFrameDownsyncWithPrediction(inputFrameId);
if (0 == instance.recentInputCacheEd) { const prefabbedInputList = (null == previousInputFrameDownsyncWithPrediction ? new Array(self.playerRichInfoDict.size).fill(0) : previousInputFrameDownsyncWithPrediction.inputList.slice());
prefabbedInputList = new Array(instance.playerRichInfoDict.size).fill(0); prefabbedInputList[(joinIndex-1)] = discreteDir.encodedIdx;
} else {
if (!instance.recentInputCache.has(instance.recentInputCacheEd-1)) {
console.warn("_generateInputFrameUpsync: recentInputCache is NOT having inputFrameId=", instance.recentInputCacheEd-1, "; recentInputCache=", instance._stringifyRecentInputCache(false));
prefabbedInputList = new Array(instance.playerRichInfoDict.size).fill(0);
} else {
prefabbedInputList = Array.from(instance.recentInputCache.get(instance.recentInputCacheEd-1).inputList);
selfPlayerLastInputFrameInput = prefabbedInputList[(instance.selfPlayerInfo.joinIndex-1)]; // it's an integer, thus making a copy here, not impacted by later assignments
}
}
prefabbedInputList[(instance.selfPlayerInfo.joinIndex-1)] = discreteDir.encodedIdx;
const prefabbedInputFrameDownsync = { const prefabbedInputFrameDownsync = {
inputFrameId: inputFrameId, inputFrameId: inputFrameId,
inputList: prefabbedInputList, inputList: prefabbedInputList,
confirmedList: (1 << (instance.selfPlayerInfo.joinIndex-1)) confirmedList: (1 << (self.selfPlayerInfo.joinIndex-1))
}; };
instance._dumpToInputCache(prefabbedInputFrameDownsync); // A prefabbed inputFrame, would certainly be adding a new inputFrame to the cache, because server only downsyncs "all-confirmed inputFrames" self._dumpToInputCache(prefabbedInputFrameDownsync); // A prefabbed inputFrame, would certainly be adding a new inputFrame to the cache, because server only downsyncs "all-confirmed inputFrames"
return [selfPlayerLastInputFrameInput, discreteDir.encodedIdx]; const previousSelfInput = (null == previousInputFrameDownsyncWithPrediction ? null : previousInputFrameDownsyncWithPrediction.inputList[joinIndex-1]);
return [previousSelfInput, discreteDir.encodedIdx];
}, },
_shouldSendInputFrameUpsyncBatch(prevSelfInput, currSelfInput, lastUpsyncInputFrameId, currInputFrameId) { shouldSendInputFrameUpsyncBatch(prevSelfInput, currSelfInput, lastUpsyncInputFrameId, currInputFrameId) {
/* /*
For a 2-player-battle, this "shouldUpsyncForEarlyAllConfirmedOnServer" can be omitted, however for more players in a same battle, to avoid a "long time non-moving player" jamming the downsync of other moving players, we should use this flag. For a 2-player-battle, this "shouldUpsyncForEarlyAllConfirmedOnServer" can be omitted, however for more players in a same battle, to avoid a "long time non-moving player" jamming the downsync of other moving players, we should use this flag.
*/ */
@ -219,33 +229,33 @@ cc.Class({
return shouldUpsyncForEarlyAllConfirmedOnServer || (prevSelfInput != currSelfInput); return shouldUpsyncForEarlyAllConfirmedOnServer || (prevSelfInput != currSelfInput);
}, },
_sendInputFrameUpsyncBatch(inputFrameId) { sendInputFrameUpsyncBatch(inputFrameId) {
// [WARNING] Why not just send the latest input? Because different player would have a different "inputFrameId" of changing its last input, and that could make the server not recognizing any "all-confirmed inputFrame"! // [WARNING] Why not just send the latest input? Because different player would have a different "inputFrameId" of changing its last input, and that could make the server not recognizing any "all-confirmed inputFrame"!
const instance = this; const self = this;
let inputFrameUpsyncBatch = []; let inputFrameUpsyncBatch = [];
for (let i = instance.lastUpsyncInputFrameId+1; i <= inputFrameId; ++i) { for (let i = self.lastUpsyncInputFrameId+1; i <= inputFrameId; ++i) {
const inputFrameDownsync = instance.recentInputCache.get(i); const inputFrameDownsync = self.recentInputCache.getByFrameId(i);
if (null == inputFrameDownsync) { if (null == inputFrameDownsync) {
console.warn("_sendInputFrameUpsyncBatch: recentInputCache is NOT having inputFrameId=", i, "; recentInputCache=", instance._stringifyRecentInputCache(false)); console.warn("sendInputFrameUpsyncBatch: recentInputCache is NOT having inputFrameId=", i, "; recentInputCache=", self._stringifyRecentInputCache(false));
} else { } else {
const inputFrameUpsync = { const inputFrameUpsync = {
inputFrameId: i, inputFrameId: i,
encodedDir: inputFrameDownsync.inputList[instance.selfPlayerInfo.joinIndex-1], encodedDir: inputFrameDownsync.inputList[self.selfPlayerInfo.joinIndex-1],
}; };
inputFrameUpsyncBatch.push(inputFrameUpsync); inputFrameUpsyncBatch.push(inputFrameUpsync);
} }
} }
const reqData = window.WsReq.encode({ const reqData = window.WsReq.encode({
msgId: Date.now(), msgId: Date.now(),
playerId: instance.selfPlayerInfo.id, playerId: self.selfPlayerInfo.id,
act: window.UPSYNC_MSG_ACT_PLAYER_CMD, act: window.UPSYNC_MSG_ACT_PLAYER_CMD,
joinIndex: instance.selfPlayerInfo.joinIndex, joinIndex: self.selfPlayerInfo.joinIndex,
ackingFrameId: instance.lastRoomDownsyncFrameId, ackingFrameId: self.lastAllConfirmedRenderFrameId,
ackingInputFrameId: instance.lastDownsyncInputFrameId, ackingInputFrameId: self.lastAllConfirmedInputFrameId,
inputFrameUpsyncBatch: inputFrameUpsyncBatch, inputFrameUpsyncBatch: inputFrameUpsyncBatch,
}).finish(); }).finish();
window.sendSafely(reqData); window.sendSafely(reqData);
instance.lastUpsyncInputFrameId = inputFrameId; self.lastUpsyncInputFrameId = inputFrameId;
}, },
onEnable() { onEnable() {
@ -274,9 +284,6 @@ cc.Class({
if (null != window.handleClientSessionCloseOrError) { if (null != window.handleClientSessionCloseOrError) {
window.handleClientSessionCloseOrError = null; window.handleClientSessionCloseOrError = null;
} }
if (self.inputControlTimer) {
clearInterval(self.inputControlTimer);
}
}, },
popupSimplePressToGo(labelString, hideYesButton) { popupSimplePressToGo(labelString, hideYesButton) {
@ -323,6 +330,7 @@ cc.Class({
self.countdownNanos = null; self.countdownNanos = null;
// Clearing previous info of all players. [BEGINS] // Clearing previous info of all players. [BEGINS]
self.collisionPlayerIndexPrefix = (1 << 17); // For tracking the movements of players
if (null != self.playerRichInfoDict) { if (null != self.playerRichInfoDict) {
self.playerRichInfoDict.forEach((playerRichInfo, playerId) => { self.playerRichInfoDict.forEach((playerRichInfo, playerId) => {
if (playerRichInfo.node.parent) { if (playerRichInfo.node.parent) {
@ -333,27 +341,27 @@ cc.Class({
self.playerRichInfoDict = new Map(); self.playerRichInfoDict = new Map();
// Clearing previous info of all players. [ENDS] // Clearing previous info of all players. [ENDS]
self.lastRoomDownsyncFrameId = 0;
self.renderFrameId = 0; // After battle started self.renderFrameId = 0; // After battle started
self.lastAllConfirmedRenderFrameId = -1;
self.lastAllConfirmedInputFrameId = -1;
self.chaserRenderFrameId = -1; // at any moment, "lastAllConfirmedRenderFrameId <= chaserRenderFrameId <= renderFrameId", but "chaserRenderFrameId" would fluctuate according to "handleInputFrameDownsyncBatch"
self.inputDelayFrames = 8; self.inputDelayFrames = 8;
self.inputScaleFrames = 2; self.inputScaleFrames = 2;
self.lastDownsyncInputFrameId = -1;
self.lastAllConfirmedInputFrameId = -1;
self.lastUpsyncInputFrameId = -1; self.lastUpsyncInputFrameId = -1;
self.inputFrameUpsyncDelayTolerance = 2; self.inputFrameUpsyncDelayTolerance = 2;
self.recentFrameCache = new Map(); self.recentRenderCache = new RingBuffer(1024);
self.recentFrameCacheSt = 0; // closed index
self.recentFrameCacheEd = 0; // open index
self.recentFrameCacheMaxCount = 1024;
self.selfPlayerInfo = null; // This field is kept for distinguishing "self" and "others". self.selfPlayerInfo = null; // This field is kept for distinguishing "self" and "others".
self.recentInputCache = new Map(); // TODO: Use a ringbuf instead self.recentInputCache = new RingBuffer(1024);
self.recentInputCacheSt = 0; // closed index
self.recentInputCacheEd = 0; // open index self.latestCollisionSys = new collisions.Collisions();
self.recentInputCacheMaxCount = 1024; self.chaserCollisionSys = new collisions.Collisions();
self.toRollbackRenderFrameId1 = null;
self.toRollbackInputFrameId1 = null; self.collisionBarrierIndexPrefix = (1 << 16); // For tracking the movements of barriers, though not yet actually used
self.latestCollisionSysMap = new Map();
self.chaserCollisionSysMap = new Map();
self.transitToState(ALL_MAP_STATES.VISUAL); self.transitToState(ALL_MAP_STATES.VISUAL);
@ -386,8 +394,7 @@ cc.Class({
const mapNode = self.node; const mapNode = self.node;
const canvasNode = mapNode.parent; const canvasNode = mapNode.parent;
cc.director.getCollisionManager().enabled = true; cc.director.getCollisionManager().enabled = false;
cc.director.getCollisionManager().enabledDebugDraw = CC_DEBUG;
// self.musicEffectManagerScriptIns = self.node.getComponent("MusicEffectManager"); // self.musicEffectManagerScriptIns = self.node.getComponent("MusicEffectManager");
self.musicEffectManagerScriptIns = null; self.musicEffectManagerScriptIns = null;
@ -443,8 +450,6 @@ cc.Class({
self.clientUpsyncFps = 60; self.clientUpsyncFps = 60;
window.handleBattleColliderInfo = function(parsedBattleColliderInfo) { window.handleBattleColliderInfo = function(parsedBattleColliderInfo) {
console.log(parsedBattleColliderInfo);
self.battleColliderInfo = parsedBattleColliderInfo; self.battleColliderInfo = parsedBattleColliderInfo;
const tiledMapIns = self.node.getComponent(cc.TiledMap); const tiledMapIns = self.node.getComponent(cc.TiledMap);
@ -459,11 +464,8 @@ cc.Class({
[WARNING] [WARNING]
- The order of the following statements is important, because we should have finished "_resetCurrentMatch" before the first "RoomDownsyncFrame". - The order of the following statements is important, because we should have finished "_resetCurrentMatch" before the first "RoomDownsyncFrame".
- It's important to assign new "tmxAsset" before "extractBoundaryObjects => initMapNodeByTiledBoundaries", to ensure that the correct tilesets are used. - It's important to assign new "tmxAsset" before "extractBoundaryObjects", to ensure that the correct tilesets are used.
- To ensure clearance, put destruction of the "cc.TiledMap" component preceding that of "mapNode.destroyAllChildren()". - To ensure clearance, put destruction of the "cc.TiledMap" component preceding that of "mapNode.destroyAllChildren()".
-- YFLu, 2019-09-07
*/ */
tiledMapIns.tmxAsset = null; tiledMapIns.tmxAsset = null;
@ -485,9 +487,22 @@ cc.Class({
singleImageLayer.node.opacity = 0; singleImageLayer.node.opacity = 0;
} }
let barrierIdCounter = 0;
const boundaryObjs = tileCollisionManager.extractBoundaryObjects(self.node); const boundaryObjs = tileCollisionManager.extractBoundaryObjects(self.node);
tileCollisionManager.initMapNodeByTiledBoundaries(self, mapNode, boundaryObjs); for (let boundaryObj of boundaryObjs.barriers) {
const x0 = boundaryObj[0].x, y0 = boundaryObj[0].y;
let pts = [];
// TODO: Simplify this redundant coordinate conversion within "extractBoundaryObjects", but since this routine is only called once per battle, not urgent.
for (let i = 0; i < boundaryObj.length; ++i) {
pts.push([boundaryObj[i].x-x0, boundaryObj[i].y-y0]);
}
const newBarrierLatest = self.latestCollisionSys.createPolygon(x0, y0, pts);
const newBarrierChaser = self.chaserCollisionSys.createPolygon(x0, y0, pts);
++barrierIdCounter;
const collisionBarrierIndex = (self.collisionBarrierIndexPrefix + barrierIdCounter);
self.latestCollisionSysMap.set(collisionBarrierIndex, newBarrierLatest);
self.chaserCollisionSysMap.set(collisionBarrierIndex, newBarrierChaser);
}
self.selfPlayerInfo = JSON.parse(cc.sys.localStorage.getItem('selfPlayer')); self.selfPlayerInfo = JSON.parse(cc.sys.localStorage.getItem('selfPlayer'));
Object.assign(self.selfPlayerInfo, { Object.assign(self.selfPlayerInfo, {
@ -524,61 +539,40 @@ cc.Class({
self.transitToState(ALL_MAP_STATES.WAITING); self.transitToState(ALL_MAP_STATES.WAITING);
self._inputControlEnabled = false; self._inputControlEnabled = false;
let findingPlayerScriptIns = null; let findingPlayerScriptIns = self.findingPlayerNode.getComponent("FindingPlayer");
window.handleRoomDownsyncFrame = function(rdf) { window.handleRoomDownsyncFrame = function(rdf) {
if (ALL_BATTLE_STATES.WAITING != self.battleState if (ALL_BATTLE_STATES.WAITING != self.battleState
&& ALL_BATTLE_STATES.IN_BATTLE != self.battleState && ALL_BATTLE_STATES.IN_BATTLE != self.battleState
&& ALL_BATTLE_STATES.IN_SETTLEMENT != self.battleState) { && ALL_BATTLE_STATES.IN_SETTLEMENT != self.battleState) {
return; return;
} }
const frameId = rdf.id;
// Right upon establishment of the "PersistentSessionClient", we should receive an initial signal "BattleColliderInfo" earlier than any "RoomDownsyncFrame" containing "PlayerMeta" data. // Right upon establishment of the "PersistentSessionClient", we should receive an initial signal "BattleColliderInfo" earlier than any "RoomDownsyncFrame" containing "PlayerMeta" data.
const refFrameId = rdf.refFrameId; const refFrameId = rdf.refFrameId;
switch (refFrameId) { switch (refFrameId) {
case window.MAGIC_ROOM_DOWNSYNC_FRAME_ID.BATTLE_READY_TO_START:
// 显示倒计时
self.playersMatched(rdf.playerMetas);
// 隐藏返回按钮
findingPlayerScriptIns = self.findingPlayerNode.getComponent("FindingPlayer");
findingPlayerScriptIns.hideExitButton();
return;
case window.MAGIC_ROOM_DOWNSYNC_FRAME_ID.PLAYER_ADDED_AND_ACKED: case window.MAGIC_ROOM_DOWNSYNC_FRAME_ID.PLAYER_ADDED_AND_ACKED:
self._initPlayerRichInfoDict(rdf.players, rdf.playerMetas); // Update the "finding player" GUI and show it if not previously present
// 显示匹配玩家
findingPlayerScriptIns = self.findingPlayerNode.getComponent("FindingPlayer");
if (!self.findingPlayerNode.parent) { if (!self.findingPlayerNode.parent) {
self.showPopupInCanvas(self.findingPlayerNode); self.showPopupInCanvas(self.findingPlayerNode);
} }
findingPlayerScriptIns.updatePlayersInfo(rdf.playerMetas); findingPlayerScriptIns.updatePlayersInfo(rdf.playerMetas);
return; return;
case window.MAGIC_ROOM_DOWNSYNC_FRAME_ID.BATTLE_READY_TO_START:
self.onBattleReadyToStart(rdf.playerMetas, false);
return;
case window.MAGIC_ROOM_DOWNSYNC_FRAME_ID.BATTLE_START:
self.onBattleStarted(rdf);
return;
case window.MAGIC_ROOM_DOWNSYNC_FRAME_ID.PLAYER_READDED_AND_ACKED: case window.MAGIC_ROOM_DOWNSYNC_FRAME_ID.PLAYER_READDED_AND_ACKED:
self._initPlayerRichInfoDict(rdf.players, rdf.playerMetas); // [WARNING] The "frameId" from server could be quite fast-forwarding, don't assign it in other cases.
// In this case, we're definitely in an active battle, thus the "self.findingPlayerNode" should be hidden if being presented. self.renderFrameId = frameId;
if (self.findingPlayerNode && self.findingPlayerNode.parent) { self.lastAllConfirmedRenderFrameId = frameId;
self.findingPlayerNode.parent.removeChild(self.findingPlayerNode); self.onBattleReadyToStart(rdf.playerMetas, true);
self.transitToState(ALL_MAP_STATES.VISUAL); self.onBattleStarted(rdf);
if (self.playersInfoNode) {
for (let playerId in rdf.playerMetas) {
const playerMeta = rdf.playerMetas[playerId];
const playersInfoScriptIns = self.playersInfoNode.getComponent("PlayersInfo");
playersInfoScriptIns.updateData(playerMeta);
}
}
}
return; return;
} }
const frameId = rdf.id;
if (0 == self.lastRoomDownsyncFrameId) {
if (1 == frameId) {
// No need to prompt upon rejoined.
self.popupSimplePressToGo(i18n.t("gameTip.start"));
}
self.onBattleStarted(rdf);
}
self._dumpToFullFrameCache(rdf);
self.lastRoomDownsyncFrameId = frameId;
// TODO: Inject a NetworkDoctor as introduced in https://app.yinxiang.com/shard/s61/nl/13267014/5c575124-01db-419b-9c02-ec81f78c6ddc/. // TODO: Inject a NetworkDoctor as introduced in https://app.yinxiang.com/shard/s61/nl/13267014/5c575124-01db-419b-9c02-ec81f78c6ddc/.
}; };
@ -588,13 +582,13 @@ cc.Class({
return; return;
} }
// console.log("Received inputFrameDownsyncBatch=", batch, ", now correspondingLastLocalInputFrame=", self.recentInputCache.get(batch[batch.length-1].inputFrameId)); // console.log("Received inputFrameDownsyncBatch=", batch, ", now correspondingLastLocalInputFrame=", self.recentInputCache.getByFrameId(batch[batch.length-1].inputFrameId));
let firstPredictedYetIncorrectInputFrameId = null; let firstPredictedYetIncorrectInputFrameId = null;
let firstPredictedYetIncorrectInputFrameJoinIndex = null; let firstPredictedYetIncorrectInputFrameJoinIndex = null;
for (let k in batch) { for (let k in batch) {
const inputFrameDownsync = batch[k]; const inputFrameDownsync = batch[k];
const inputFrameDownsyncId = inputFrameDownsync.inputFrameId; const inputFrameDownsyncId = inputFrameDownsync.inputFrameId;
const localInputFrame = self.recentInputCache.get(inputFrameDownsyncId); const localInputFrame = self.recentInputCache.getByFrameId(inputFrameDownsyncId);
if (null == localInputFrame) { if (null == localInputFrame) {
console.warn("handleInputFrameDownsyncBatch: recentInputCache is NOT having inputFrameDownsyncId=", inputFrameDownsyncId, "; now recentInputCache=", self._stringifyRecentInputCache(false)); console.warn("handleInputFrameDownsyncBatch: recentInputCache is NOT having inputFrameDownsyncId=", inputFrameDownsyncId, "; now recentInputCache=", self._stringifyRecentInputCache(false));
} else { } else {
@ -608,34 +602,36 @@ cc.Class({
} }
} }
} }
self.lastAllConfirmedInputFrameId = inputFrameDownsyncId;
self._dumpToInputCache(inputFrameDownsync); self._dumpToInputCache(inputFrameDownsync);
// [WARNING] Currently "lastDownsyncInputFrameId" and "lastAllConfirmedInputFrameId" are identical, but they (their definitions) are prone to changes in the future
self.lastDownsyncInputFrameId = inputFrameDownsyncId;
self.lastAllConfirmedInputFrameId = inputFrameDownsyncId; // TODO: Should localInputFrameId > self.lastAllConfirmedInputFrameId be corrected for their predictions on the other players?
} }
if (null != firstPredictedYetIncorrectInputFrameId) { if (null != firstPredictedYetIncorrectInputFrameId) {
const renderFrameId2 = self.renderFrameId;
const inputFrameId2 = self._convertToInputFrameId(renderFrameId2, self.inputDelayFrames);
const inputFrameId1 = firstPredictedYetIncorrectInputFrameId; const inputFrameId1 = firstPredictedYetIncorrectInputFrameId;
const renderFrameId1 = self._convertToRenderFrameId(inputFrameId1, self.inputDelayFrames); // a.k.a. "firstRenderFrameIdUsingIncorrectInputFrameId" const renderFrameId1 = self._convertToRenderFrameId(inputFrameId1, self.inputDelayFrames); // a.k.a. "firstRenderFrameIdUsingIncorrectInputFrameId"
if (renderFrameId1 < renderFrameId2) { if (renderFrameId1 < self.renderFrameId) {
// No need to rollback when "renderFrameId1 == renderFrameId2", because the "delayedInputFrame for renderFrameId2" is not yet executed by now, it just went through "++self.renderFrameId" in "update(dt)" and js-runtime is mostly single-threaded in our programmable range. /*
console.warn("Mismatched input detected!: [inputFrameId1:", inputFrameId1, ", inputFrameId2:", inputFrameId2, "), [renderFrameId1:", renderFrameId1, ", renderFrameId2:", renderFrameId2, "). "); A typical case is as follows.
if (true == self.rollbackInMainUpdate) { --------------------------------------------------------
// The actual rollback-and-replay would later be executed in update(dt). [self.lastAllConfirmedRenderFrameId] : 22
if (null == self.toRollbackRenderFrameId1) {
self.toRollbackRenderFrameId1 = renderFrameId1;
self.toRollbackInputFrameId1 = inputFrameId1;
} else {
// Deliberately left blank. This case merely extends the ending indices
}
} else {
self._rollbackAndReplay(inputFrameId1, renderFrameId1, inputFrameId2, renderFrameId2);
}
<renderFrameId1> : 36
<self.chaserRenderFrameId> : 62
[self.renderFrameId] : 64
--------------------------------------------------------
*/
if (renderFrameId1 < self.chaserRenderFrameId) {
// The actual rollback-and-chase would later be executed in update(dt).
console.warn("Mismatched input detected, resetting chaserRenderFrameId: inputFrameId1:", inputFrameId1, ", renderFrameId1:", renderFrameId1, ", chaserRenderFrameId before reset: ", self.chaserRenderFrameId);
self.chaserRenderFrameId = renderFrameId1;
} else { } else {
console.log("Mismatched input yet no rollback needed: [inputFrameId1:", inputFrameId1, ", inputFrameId2:", inputFrameId2, "), [renderFrameId1:", renderFrameId1, ", renderFrameId2:", renderFrameId2, "). "); // Deliberately left blank, chasing is ongoing.
}
} else {
// No need to rollback when "renderFrameId1 == self.renderFrameId", because the "corresponding delayedInputFrame for renderFrameId2" is NOT YET EXECUTED BY NOW, it just went through "++self.renderFrameId" in "update(dt)" and javascript-runtime is mostly single-threaded in our programmable range.
} }
} }
}; };
@ -695,10 +691,19 @@ cc.Class({
onBattleStarted(rdf) { onBattleStarted(rdf) {
// This function is also applicable to "re-joining". // This function is also applicable to "re-joining".
const players = rdf.players;
const playerMetas = rdf.playerMetas;
console.log('On battle started!'); console.log('On battle started!');
const self = window.mapIns; const self = window.mapIns;
const players = rdf.players;
const playerMetas = rdf.playerMetas;
self._initPlayerRichInfoDict(players, playerMetas);
// Show the top status indicators for IN_BATTLE
const playersInfoScriptIns = self.playersInfoNode.getComponent("PlayersInfo");
for (let i in playerMetas) {
const playerMeta = playerMetas[i];
playersInfoScriptIns.updateData(playerMeta);
}
if (null != rdf.countdownNanos) { if (null != rdf.countdownNanos) {
self.countdownNanos = rdf.countdownNanos; self.countdownNanos = rdf.countdownNanos;
} }
@ -712,19 +717,24 @@ cc.Class({
self.countdownToBeginGameNode.parent.removeChild(self.countdownToBeginGameNode); self.countdownToBeginGameNode.parent.removeChild(self.countdownToBeginGameNode);
} }
self.transitToState(ALL_MAP_STATES.VISUAL); self.transitToState(ALL_MAP_STATES.VISUAL);
self.chaserRenderFrameId = rdf.id;
self._applyRoomDownsyncFrameDynamics(rdf); self.applyRoomDownsyncFrameDynamics(rdf);
self._dumpToRenderCache(rdf);
self.battleState = ALL_BATTLE_STATES.IN_BATTLE; // Starts the increment of "self.renderFrameId" in "self.update(dt)" self.battleState = ALL_BATTLE_STATES.IN_BATTLE; // Starts the increment of "self.renderFrameId" in "self.update(dt)"
if (null != window.boundRoomId) {
self.boundRoomIdLabel.string = window.boundRoomId;
}
}, },
logBattleStats() { logBattleStats() {
const self = this; const self = this;
let s = []; let s = [];
s.push("Battle stats: lastUpsyncInputFrameId=" + self.lastUpsyncInputFrameId + ", lastDownsyncInputFrameId=" + self.lastDownsyncInputFrameId + ", lastAllConfirmedInputFrameId=" + self.lastAllConfirmedInputFrameId); s.push("Battle stats: lastUpsyncInputFrameId=" + self.lastUpsyncInputFrameId + ", lastAllConfirmedInputFrameId=" + self.lastAllConfirmedInputFrameId);
self.recentInputCache.forEach((inputFrameDownsync, inputFrameId) => { for (let i = self.recentInputCache.stFrameId; i < self.recentInputCache.edFrameId; ++i) {
const inputFrameDownsync = self.recentInputCache.getByFrameId(i);
s.push(JSON.stringify(inputFrameDownsync)); s.push(JSON.stringify(inputFrameDownsync));
}); }
console.log(s.join('\n')); console.log(s.join('\n'));
}, },
@ -749,12 +759,19 @@ cc.Class({
}, },
spawnPlayerNode(joinIndex, x, y) { spawnPlayerNode(joinIndex, x, y) {
const instance = this; const self = this;
const newPlayerNode = 1 == joinIndex ? cc.instantiate(instance.player1Prefab) : cc.instantiate(instance.player2Prefab); // hardcoded for now, car color determined solely by joinIndex const newPlayerNode = 1 == joinIndex ? cc.instantiate(self.player1Prefab) : cc.instantiate(self.player2Prefab); // hardcoded for now, car color determined solely by joinIndex
newPlayerNode.setPosition(cc.v2(x, y)); newPlayerNode.setPosition(cc.v2(x, y));
newPlayerNode.getComponent("SelfPlayer").mapNode = instance.node; newPlayerNode.getComponent("SelfPlayer").mapNode = self.node;
const currentSelfColliderCircle = newPlayerNode.getComponent(cc.CircleCollider);
safelyAddChild(instance.node, newPlayerNode); const newPlayerColliderLatest = self.latestCollisionSys.createCircle(x, y, currentSelfColliderCircle.radius);
const newPlayerColliderChaser = self.chaserCollisionSys.createCircle(x, y, currentSelfColliderCircle.radius);
const collisionPlayerIndex = self.collisionPlayerIndexPrefix + joinIndex;
self.latestCollisionSysMap.set(collisionPlayerIndex, newPlayerColliderLatest);
self.chaserCollisionSysMap.set(collisionPlayerIndex, newPlayerColliderChaser);
safelyAddChild(self.node, newPlayerNode);
setLocalZOrder(newPlayerNode, 5); setLocalZOrder(newPlayerNode, 5);
newPlayerNode.active = true; newPlayerNode.active = true;
@ -766,8 +783,8 @@ cc.Class({
update(dt) { update(dt) {
const self = this; const self = this;
try {
if (ALL_BATTLE_STATES.IN_BATTLE == self.battleState) { if (ALL_BATTLE_STATES.IN_BATTLE == self.battleState) {
try {
let prevSelfInput = null, currSelfInput = null; let prevSelfInput = null, currSelfInput = null;
const noDelayInputFrameId = self._convertToInputFrameId(self.renderFrameId, 0); // It's important that "inputDelayFrames == 0" here const noDelayInputFrameId = self._convertToInputFrameId(self.renderFrameId, 0); // It's important that "inputDelayFrames == 0" here
if (self._shouldGenerateInputFrameUpsync(self.renderFrameId)) { if (self._shouldGenerateInputFrameUpsync(self.renderFrameId)) {
@ -775,45 +792,36 @@ cc.Class({
prevSelfInput = prevAndCurrInputs[0]; prevSelfInput = prevAndCurrInputs[0];
currSelfInput = prevAndCurrInputs[1]; currSelfInput = prevAndCurrInputs[1];
} }
if (self._shouldSendInputFrameUpsyncBatch(prevSelfInput, currSelfInput, self.lastUpsyncInputFrameId, noDelayInputFrameId)) {
let t0 = performance.now();
if (self.shouldSendInputFrameUpsyncBatch(prevSelfInput, currSelfInput, self.lastUpsyncInputFrameId, noDelayInputFrameId)) {
// TODO: Is the following statement run asynchronously in an implicit manner? Should I explicitly run it asynchronously? // TODO: Is the following statement run asynchronously in an implicit manner? Should I explicitly run it asynchronously?
self._sendInputFrameUpsyncBatch(noDelayInputFrameId); self.sendInputFrameUpsyncBatch(noDelayInputFrameId);
} }
const delayedInputFrameId = self._convertToInputFrameId(self.renderFrameId, self.inputDelayFrames); // The "inputFrameId" to use at current "renderFrameId" let t1 = performance.now();
if (true == self.rollbackInMainUpdate) { // Use "fractional-frame-chasing" to guarantee that "self.update(dt)" is not jammed by a "large range of frame-chasing". See `<proj-root>/ConcerningEdgeCases.md` for the motivation.
if (null != self.toRollbackRenderFrameId1) { const prevChaserRenderFrameId = self.chaserRenderFrameId;
// Rollback-and-replay if necessary, prior to applying the latest dynamics let nextChaserRenderFrameId = (prevChaserRenderFrameId + self.maxChasingRenderFramesPerUpdate);
const anotherJoinIndex = 3-self.selfPlayerInfo.joinIndex; if (nextChaserRenderFrameId > self.renderFrameId) nextChaserRenderFrameId = self.renderFrameId;
self._rollbackAndReplay(self.toRollbackInputFrameId1, self.toRollbackRenderFrameId1, delayedInputFrameId, self.renderFrameId); self.rollbackAndChase(prevChaserRenderFrameId, nextChaserRenderFrameId, self.chaserCollisionSys, self.chaserCollisionSysMap);
self.toRollbackRenderFrameId1 = null; self.chaserRenderFrameId = nextChaserRenderFrameId; // Move the cursor "self.chaserRenderFrameId", keep in mind that "self.chaserRenderFrameId" is not monotonic!
self.toRollbackRenderFrameId2 = null; let t2 = performance.now();
}
}
const delayedInputFrameDownsync = self.assembleInputFrameDownsync(delayedInputFrameId); // Inside "self.rollbackAndChase", the "self.latestCollisionSys" is ALWAYS ROLLED BACK to "self.recentRenderCache.get(self.renderFrameId)" before being applied dynamics from corresponding inputFrameDownsync, REGARDLESS OF whether or not "self.chaserRenderFrameId == self.renderFrameId" now.
if (null == delayedInputFrameDownsync) { const rdf = self.rollbackAndChase(self.renderFrameId, self.renderFrameId+1, self.latestCollisionSys, self.latestCollisionSysMap);
console.warn("update(dt): recentInputCache is NOT having inputFrameId=", delayedInputFrameId, "; recentInputCache=", self._stringifyRecentInputCache(false));
} else { self.applyRoomDownsyncFrameDynamics(rdf);
self._applyInputFrameDownsyncDynamics(delayedInputFrameDownsync, false); let t3 = performance.now();
}
const rdf = self._createRoomDownsyncFrameLocally();
self._dumpToFullFrameCache(rdf);
/* /*
if (null != delayedInputFrameDownsync && null != delayedInputFrameDownsync.inputList && 0 < delayedInputFrameDownsync.inputList[self.selfPlayerInfo.joinIndex-1]) { if (prevChaserRenderFrameId < nextChaserRenderFrameId) {
console.log("My critical status: renderFrame=", JSON.stringify(rdf), ", delayedInputFrameDownsync=", JSON.stringify(delayedInputFrameDownsync)); console.log("Took ", t1-t0, " milliseconds to send upsync cmds, ", t2-t1, " milliseconds to chase renderFrameIds=[", prevChaserRenderFrameId, ", ", nextChaserRenderFrameId, "], @renderFrameId=", self.renderFrameId);
} }
*/ */
++self.renderFrameId; // [WARNING] It's important to increment the renderFrameId AFTER all the operations above!!! } catch (err) {
} console.error("Error during Map.update", err);
const mapNode = self.node; } finally {
const canvasNode = mapNode.parent; // Update countdown
const canvasParentNode = canvasNode.parent;
if (null != window.boundRoomId) {
self.boundRoomIdLabel.string = window.boundRoomId;
}
// update countdown
if (null != self.countdownNanos) { if (null != self.countdownNanos) {
self.countdownNanos -= self.rollbackEstimatedDt*1000000000; self.countdownNanos -= self.rollbackEstimatedDt*1000000000;
if (self.countdownNanos <= 0) { if (self.countdownNanos <= 0) {
@ -827,11 +835,8 @@ cc.Class({
} }
self.countdownLabel.string = countdownSeconds; self.countdownLabel.string = countdownSeconds;
} }
} catch (err) { ++self.renderFrameId; // [WARNING] It's important to increment the renderFrameId AFTER all the operations above!!!
console.error("Error during Map.update", err);
} }
if (null != self.ctrl) {
self.ctrl.justifyMapNodePosAndScale(self.ctrl.linearSpeedBase, self.ctrl.zoomingSpeedBase);
} }
}, },
@ -904,54 +909,84 @@ cc.Class({
setLocalZOrder(toShowNode, 10); setLocalZOrder(toShowNode, 10);
}, },
playersMatched(playerMetas) { onBattleReadyToStart(playerMetas, isSelfRejoining) {
console.log("Calling `playersMatched` with:", playerMetas); console.log("Calling `onBattleReadyToStart` with:", playerMetas);
const self = this; const self = this;
const findingPlayerScriptIns = self.findingPlayerNode.getComponent("FindingPlayer"); const findingPlayerScriptIns = self.findingPlayerNode.getComponent("FindingPlayer");
findingPlayerScriptIns.hideExitButton();
findingPlayerScriptIns.updatePlayersInfo(playerMetas); findingPlayerScriptIns.updatePlayersInfo(playerMetas);
window.setTimeout(() => {
if (null != self.findingPlayerNode.parent) { const hideFindingPlayersGUI = function() {
if (null == self.findingPlayerNode.parent) return;
self.findingPlayerNode.parent.removeChild(self.findingPlayerNode); self.findingPlayerNode.parent.removeChild(self.findingPlayerNode);
self.transitToState(ALL_MAP_STATES.VISUAL); };
const playersInfoScriptIns = self.playersInfoNode.getComponent("PlayersInfo");
for (let i in playerMetas) { if (true == isSelfRejoining) {
const playerMeta = playerMetas[i]; hideFindingPlayersGUI();
playersInfoScriptIns.updateData(playerMeta); } else {
} // Delay to hide the "finding player" GUI, then show a countdown clock
} window.setTimeout(() => {
hideFindingPlayersGUI();
const countDownScriptIns = self.countdownToBeginGameNode.getComponent("CountdownToBeginGame"); const countDownScriptIns = self.countdownToBeginGameNode.getComponent("CountdownToBeginGame");
countDownScriptIns.setData(); countDownScriptIns.setData();
self.showPopupInCanvas(self.countdownToBeginGameNode); self.showPopupInCanvas(self.countdownToBeginGameNode);
return; }, 1500);
}, 2000); }
}, },
_createRoomDownsyncFrameLocally() { _createRoomDownsyncFrameLocally(renderFrameId, collisionSys, collisionSysMap) {
const self = this; const self = this;
const prevRenderFrameId = renderFrameId-1;
const inputFrameForPrevRenderFrame = (
0 > prevRenderFrameId
?
null
:
self.getCachedInputFrameDownsyncWithPrediction(self._convertToInputFrameId(prevRenderFrameId, self.inputDelayFrames))
);
// TODO: Find a better way to assign speeds instead of using "speedRefRenderFrameId".
const speedRefRenderFrameId = prevRenderFrameId;
const speedRefRenderFrame = (
0 > prevRenderFrameId
?
null
:
self.recentRenderCache.getByFrameId(prevRenderFrameId)
);
const rdf = { const rdf = {
id: self.renderFrameId, id: renderFrameId,
refFrameId: self.renderFrameId, refFrameId: renderFrameId,
players: {}, players: {}
countdownNanos: self.countdownNanos
}; };
self.playerRichInfoDict.forEach((playerRichInfo, playerId) => { self.playerRichInfoDict.forEach((playerRichInfo, playerId) => {
const joinIndex = playerRichInfo.joinIndex; const joinIndex = playerRichInfo.joinIndex;
const playerNode = playerRichInfo.node; const collisionPlayerIndex = self.collisionPlayerIndexPrefix + joinIndex;
const playerScriptIns = playerRichInfo.scriptIns; const playerCollider = collisionSysMap.get(collisionPlayerIndex);
rdf.players[playerRichInfo.id] = { rdf.players[playerRichInfo.id] = {
id: playerRichInfo.id, id: playerRichInfo.id,
x: playerNode.position.x, x: playerCollider.x,
y: playerNode.position.y, y: playerCollider.y,
dir: playerScriptIns.activeDirection, dir: self.ctrl.decodeDirection(null == inputFrameForPrevRenderFrame ? 0 : inputFrameForPrevRenderFrame.inputList[joinIndex-1]),
speed: playerScriptIns.speed, speed: (null == speedRefRenderFrame ? playerRichInfo.speed : speedRefRenderFrame.players[playerRichInfo.id].speed),
joinIndex: joinIndex joinIndex: joinIndex
}; };
}); });
if (
null != inputFrameForPrevRenderFrame && self._allConfirmed(inputFrameForPrevRenderFrame.confirmedList)
&&
self.lastAllConfirmedRenderFrameId >= prevRenderFrameId
&&
rdf.id > self.lastAllConfirmedRenderFrameId
) {
self.lastAllConfirmedRenderFrameId = rdf.id;
}
self._dumpToRenderCache(rdf);
return rdf; return rdf;
}, },
_applyRoomDownsyncFrameDynamics(rdf) { applyRoomDownsyncFrameDynamics(rdf) {
const self = this; const self = this;
self.playerRichInfoDict.forEach((playerRichInfo, playerId) => { self.playerRichInfoDict.forEach((playerRichInfo, playerId) => {
@ -962,11 +997,11 @@ cc.Class({
}); });
}, },
assembleInputFrameDownsync(inputFrameId) { getCachedInputFrameDownsyncWithPrediction(inputFrameId) {
const self = this; const self = this;
let inputFrameDownsync = self.recentInputCache.get(inputFrameId); let inputFrameDownsync = self.recentInputCache.getByFrameId(inputFrameId);
if (-1 != self.lastAllConfirmedInputFrameId && inputFrameId > self.lastAllConfirmedInputFrameId) { if (null != inputFrameDownsync && -1 != self.lastAllConfirmedInputFrameId && inputFrameId > self.lastAllConfirmedInputFrameId) {
const lastAllConfirmedInputFrame = self.recentInputCache.get(self.lastAllConfirmedInputFrameId); const lastAllConfirmedInputFrame = self.recentInputCache.getByFrameId(self.lastAllConfirmedInputFrameId);
for (let i = 0; i < inputFrameDownsync.inputList.length; ++i) { for (let i = 0; i < inputFrameDownsync.inputList.length; ++i) {
if (i == self.selfPlayerInfo.joinIndex-1) continue; if (i == self.selfPlayerInfo.joinIndex-1) continue;
inputFrameDownsync.inputList[i] = lastAllConfirmedInputFrame.inputList[i]; inputFrameDownsync.inputList[i] = lastAllConfirmedInputFrame.inputList[i];
@ -976,47 +1011,72 @@ cc.Class({
return inputFrameDownsync; return inputFrameDownsync;
}, },
_applyInputFrameDownsyncDynamics(inputFrameDownsync, invokeUpdateToo) { rollbackAndChase(renderFrameIdSt, renderFrameIdEd, collisionSys, collisionSysMap) {
// This application DOESN'T use a "full physics engine", but only "collider detection" of "box2d", thus when resetting room state, there's no need of resetting "momentums". if (renderFrameSt >= renderFrameIdEd) {
const self = this;
const inputs = inputFrameDownsync.inputList;
// Update controlled player nodes
self.playerRichInfoDict.forEach((playerRichInfo, playerId) => {
const joinIndex = playerRichInfo.joinIndex;
const playerScriptIns = playerRichInfo.scriptIns;
const decodedInput = self.ctrl.decodeDirection(inputs[joinIndex-1]);
playerScriptIns.scheduleNewDirection(decodedInput, true);
if (invokeUpdateToo) {
playerScriptIns.update(self.rollbackEstimatedDt);
}
});
if (invokeUpdateToo) {
// [WARNING] CocosCreator v2.2.1 uses a singleton "CCDirector" to schedule "tree descendent updates" and "collision detections" in different timers, thus the following manual trigger of collision detection might not produce the same outcome for the "selfPlayer" as the other peers. Moreover, the aforementioned use of different timers is an intrinsic source of error!
cc.director._collisionManager.update(self.rollbackEstimatedDt); // Just to avoid unexpected wall penetration, no guarantee on determinism
}
},
_rollbackAndReplay(inputFrameId1, renderFrameId1, inputFrameId2, renderFrameId2) {
const self = this;
const rdf1 = self.recentFrameCache.get(renderFrameId1);
if (null == rdf1) {
console.error("renderFrameId1=", renderFrameId1, "doesn't exist in recentFrameCache ", self._stringifyRecentFrameCache(false), ": COULDN'T ROLLBACK!");
return; return;
} }
let t0 = performance.now(); const self = this;
self._applyRoomDownsyncFrameDynamics(rdf1); const renderFrameSt = self.recentRenderCache.getByFrameId(renderFrameIdSt); // typed "RoomDownsyncFrame"
// DON'T apply inputFrameDownsync dynamics for exactly "renderFrameId2", see the comment around the invocation of "_rollbackAndReplay". if (null == renderFrameSt) {
for (let renderFrameId = renderFrameId1; renderFrameId < renderFrameId2; ++renderFrameId) { console.error("Couldn't find renderFrameId=", renderFrameIdSt, " to rollback, recentRenderCache=", self._stringifyRecentRenderCache(false));
const delayedInputFrameId = self._convertToInputFrameId(renderFrameId, self.inputDelayFrames);
const delayedInputFrameDownsync = self.assembleInputFrameDownsync(delayedInputFrameId);
self._applyInputFrameDownsyncDynamics(delayedInputFrameDownsync, true);
// console.log("_rollbackAndReplay, AFTER:", self._stringifyRollbackResult(renderFrameId, delayedInputFrameDownsync));
} }
let t1 = performance.now(); /*
console.log("Executed rollback-and-replay: [inputFrameId1:", inputFrameId1, ", inputFrameId2:", inputFrameId2, "), [renderFrameId1:", renderFrameId1, ", renderFrameId2:", renderFrameId2, "). It took", t1-t0, "milliseconds"); Reset "position" of players in "collisionSys" according to "renderFrameSt". The easy part is that we don't have path-dependent-integrals to worry about like that of thermal dynamics.
*/
self.playerRichInfoDict.forEach((playerRichInfo, playerId) => {
const joinIndex = playerRichInfo.joinIndex;
const collisionPlayerIndex = self.collisionPlayerIndexPrefix + joinIndex;
const playerCollider = collisionSysMap.get(collisionPlayerIndex);
const player = renderFrameSt.players[playerId];
playerCollider.x = player.x;
playerCollider.y = player.y;
});
/*
This function eventually calculates a "RoomDownsyncFrame" where "RoomDownsyncFrame.id == renderFrameIdEd".
*/
for (let i = renderFrameIdSt; i < renderFrameIdEd; ++i) {
const renderFrame = self.recentRenderCache.getByFrameId(i); // typed "RoomDownsyncFrame"
const j = self._convertToInputFrameId(i, self.inputDelayFrames);
const inputList = self.getCachedInputFrameDownsyncWithPrediction(j).inputList;
self.playerRichInfoDict.forEach((playerRichInfo, playerId) => {
const joinIndex = playerRichInfo.joinIndex;
const collisionPlayerIndex = self.collisionPlayerIndexPrefix + joinIndex;
const playerCollider = collisionSysMap.get(collisionPlayerIndex);
const player = renderFrame.players[playerId];
const encodedInput = inputList[joinIndex-1];
const decodedInput = self.ctrl.decodeDirection(encodedInput);
const baseChange = player.speed*self.rollbackEstimatedDt*decodedInput.speedFactor;
playerCollider.x += baseChange*decodedInput.dx;
playerCollider.y += baseChange*decodedInput.dy;
/*
if (0 < encodedInput) {
console.log("playerId=", playerId, "@renderFrameId=", i, ", delayedInputFrameId=", j, ", baseChange=", baseChange, ": x=", playerCollider.x, ", y=", playerCollider.y);
}
*/
});
collisionSys.update();
const result = collisionSys.createResult(); // Can I reuse a "self.latestCollisionSysResult" object throughout the whole battle?
// [WARNING] Traverse in the order of joinIndices to guarantee determinism.
for (let i in self.playerRichInfoArr) {
const joinIndex = parseInt(i) + 1;
const collisionPlayerIndex = self.collisionPlayerIndexPrefix + joinIndex;
const playerCollider = collisionSysMap.get(collisionPlayerIndex);
const potentials = playerCollider.potentials();
for (const barrier of potentials) {
// Test if the player collides with the wall
if (!playerCollider.collides(barrier, result)) continue;
// Push the player out of the wall
playerCollider.x -= result.overlap * result.overlap_x;
playerCollider.y -= result.overlap * result.overlap_y;
}
}
}
return self._createRoomDownsyncFrameLocally(renderFrameIdEd, collisionSys, collisionSysMap);
}, },
_initPlayerRichInfoDict(players, playerMetas) { _initPlayerRichInfoDict(players, playerMetas) {
@ -1039,61 +1099,36 @@ cc.Class({
nodeAndScriptIns[1].showArrowTipNode(); nodeAndScriptIns[1].showArrowTipNode();
} }
} }
}, self.playerRichInfoArr = new Array(self.playerRichInfoDict.size);
self.playerRichInfoDict.forEach((playerRichInfo, playerId) => {
_stringifyRecentFrameCache(usefullOutput) { self.playerRichInfoArr[playerRichInfo.joinIndex-1] = playerRichInfo;
if (true == usefullOutput) {
let s = [];
self.recentFrameCache.forEach((roomDownsyncFrame, renderFrameId) => {
s.push(JSON.stringify(roomDownsyncFrame));
}); });
return s.join('\n');
}
return "[stRenderFrameId=" + self.recentFrameCacheSt + ", edRenderFrameId=" + self.recentFrameCacheEd + ")";
}, },
_stringifyRecentInputCache(usefullOutput) { _stringifyRecentInputCache(usefullOutput) {
const self = this;
if (true == usefullOutput) { if (true == usefullOutput) {
let s = []; let s = [];
self.recentInputCache.forEach((inputFrameDownsync, inputFrameId) => { for (let i = self.recentInputCache.stFrameId; i < self.recentInputCache.edFrameId; ++i) {
s.push(JSON.stringify(inputFrameDownsync)); s.push(JSON.stringify(self.recentInputCache.getByFrameId(i)));
}); }
return s.join('\n'); return s.join('\n');
} }
return "[stInputFrameId=" + self.recentInputCacheSt + ", edInputFrameId=" + self.recentInputCacheEd + ")"; return "[stInputFrameId=" + self.recentInputCache.stFrameId + ", edInputFrameId=" + self.recentInputCache.edFrameId + ")";
}, },
_stringifyRollbackResult(renderFrameId, delayedInputFrameDownsync) { _stringifyRecentRenderCache(usefullOutput) {
// Slightly different from "_createRoomDownsyncFrameLocally"
const self = this; const self = this;
const s = ( if (true == usefullOutput) {
null == delayedInputFrameDownsync let s = [];
? for (let i = self.recentRenderCache.stFrameId; i < self.recentRenderCache.edFrameId; ++i) {
{ s.push(JSON.stringify(self.recentRenderCache.getByFrameId(i)));
renderFrameId: renderFrameId,
players: {}
} }
:
{
renderFrameId: renderFrameId,
players: {},
delayedInputFrameDownsync: delayedInputFrameDownsync,
}
);
let players = {};
self.playerRichInfoDict.forEach((playerRichInfo, playerId) => {
const joinIndex = playerRichInfo.joinIndex;
const playerNode = playerRichInfo.node;
const playerScriptIns = playerRichInfo.scriptIns;
s.players[playerRichInfo.id] = {
id: playerRichInfo.id,
x: playerNode.position.x,
y: playerNode.position.y,
};
});
return JSON.stringify(s); return s.join('\n');
}
return "[stRenderFrameId=" + self.recentRenderCache.stFrameId + ", edRenderFrameId=" + self.recentRenderCache.edFrameId + ")";
}, },
}); });

View File

@ -0,0 +1,63 @@
var RingBuffer = function(capacity) {
this.ed = 0; // write index, open index
this.st = 0; // read index, closed index
this.edFrameId = 0;
this.stFrameId = 0;
this.n = capacity;
this.cnt = 0; // the count of valid elements in the buffer, used mainly to distinguish what "st == ed" means for "Pop" and "Get" methods
this.eles = new Array(capacity).fill(null);
};
RingBuffer.prototype.put = function(item) {
this.eles[this.ed] = item
this.edFrameId++;
this.cnt++;
this.ed++;
if (this.ed >= this.n) {
this.ed -= this.n; // Deliberately not using "%" operator for performance concern
}
};
RingBuffer.prototype.pop = function() {
if (0 == this.cnt) {
return null;
}
const item = this.eles[this.st];
this.stFrameId++;
this.cnt--;
this.st++;
if (this.st >= this.n) {
this.st -= this.n;
}
return item;
};
RingBuffer.prototype.getByOffset = function(offsetFromSt) {
if (0 == this.cnt) {
return null;
}
let arrIdx = this.st + offsetFromSt;
if (this.st < this.ed) {
// case#1: 0...st...ed...n-1
if (this.st <= arrIdx && arrIdx < this.ed) {
return this.eles[arrIdx];
}
} else {
// if this.st >= this.sd
// case#2: 0...ed...st...n-1
if (arrIdx >= this.n) {
arrIdx -= this.n
}
if (arrIdx >= this.st || arrIdx < this.ed) {
return this.eles[arrIdx];
}
}
return null;
};
RingBuffer.prototype.getByFrameId = function(frameId) {
return this.getByOffset(frameId - this.stFrameId);
};
module.exports = RingBuffer;

View File

@ -1,6 +1,6 @@
{ {
"ver": "1.0.5", "ver": "1.0.5",
"uuid": "8264fb72-e348-45e4-9ab3-5bffb9a561ee", "uuid": "9ec706f0-811c-403b-93a7-b34a7e5f8068",
"isPlugin": false, "isPlugin": false,
"loadPluginInWeb": true, "loadPluginInWeb": true,
"loadPluginInNative": true, "loadPluginInNative": true,

View File

@ -334,10 +334,6 @@ window.battleEntityTypeNameToGlobalGid = {};
TileCollisionManager.prototype.extractBoundaryObjects = function (withTiledMapNode) { TileCollisionManager.prototype.extractBoundaryObjects = function (withTiledMapNode) {
let toRet = { let toRet = {
barriers: [], barriers: [],
shelters: [],
shelterChainTails: [],
shelterChainHeads: [],
sheltersZReducer: [],
frameAnimations: [], frameAnimations: [],
grandBoundaries: [], grandBoundaries: [],
}; };
@ -393,8 +389,6 @@ TileCollisionManager.prototype.extractBoundaryObjects = function (withTiledMapNo
let childrenOfCurrentTile = null; let childrenOfCurrentTile = null;
if (cc.sys.isNative) { if (cc.sys.isNative) {
childrenOfCurrentTile = currentTile.getElementsByTagName("objectgroup"); childrenOfCurrentTile = currentTile.getElementsByTagName("objectgroup");
} else if (cc.sys.platform == cc.sys.WECHAT_GAME) {
childrenOfCurrentTile = currentTile.childNodes;
} else { } else {
childrenOfCurrentTile = currentTile.children; childrenOfCurrentTile = currentTile.children;
} }
@ -404,8 +398,6 @@ TileCollisionManager.prototype.extractBoundaryObjects = function (withTiledMapNo
var currentObjectGroupUnderTile = mapInfo._parseObjectGroup(ch); var currentObjectGroupUnderTile = mapInfo._parseObjectGroup(ch);
gidBoundariesMap[parentGid] = { gidBoundariesMap[parentGid] = {
barriers: [], barriers: [],
shelters: [],
sheltersZReducer: [],
}; };
for (let oidx = 0; oidx < currentObjectGroupUnderTile._objects.length; ++oidx) { for (let oidx = 0; oidx < currentObjectGroupUnderTile._objects.length; ++oidx) {
const oo = currentObjectGroupUnderTile._objects[oidx]; const oo = currentObjectGroupUnderTile._objects[oidx];
@ -429,22 +421,6 @@ TileCollisionManager.prototype.extractBoundaryObjects = function (withTiledMapNo
brToPushTmp.boundaryType = boundaryType; brToPushTmp.boundaryType = boundaryType;
gidBoundariesMap[parentGid].barriers.push(brToPushTmp); gidBoundariesMap[parentGid].barriers.push(brToPushTmp);
break; break;
case "shelter":
let shToPushTmp = [];
for (let shidx = 0; shidx < polylinePoints.length; ++shidx) {
shToPushTmp.push(cc.v2(oo.x, oo.y).add(polylinePoints[shidx]));
}
shToPushTmp.boundaryType = boundaryType;
gidBoundariesMap[parentGid].shelters.push(shToPushTmp);
break;
case "shelter_z_reducer":
let shzrToPushTmp = [];
for (let shzridx = 0; shzridx < polylinePoints.length; ++shzridx) {
shzrToPushTmp.push(cc.v2(oo.x, oo.y).add(polylinePoints[shzridx]));
}
shzrToPushTmp.boundaryType = boundaryType;
gidBoundariesMap[parentGid].sheltersZReducer.push(shzrToPushTmp);
break;
default: default:
break; break;
} }
@ -510,22 +486,6 @@ TileCollisionManager.prototype.extractBoundaryObjects = function (withTiledMapNo
toPushBarriers.boundaryType = boundaryType; toPushBarriers.boundaryType = boundaryType;
toRet.barriers.push(toPushBarriers); toRet.barriers.push(toPushBarriers);
break; break;
case "shelter":
let toPushShelters = [];
for (let kk = 0; kk < polylinePoints.length; ++kk) {
toPushShelters.push(this.continuousObjLayerOffsetToContinuousMapNodePos(withTiledMapNode, object.offset.add(polylinePoints[kk])));
}
toPushShelters.boundaryType = boundaryType;
toRet.shelters.push(toPushShelters);
break;
case "shelter_z_reducer":
let toPushSheltersZReducer = [];
for (let kkk = 0; kkk < polylinePoints.length; ++kkk) {
toPushSheltersZReducer.push(this.continuousObjLayerOffsetToContinuousMapNodePos(withTiledMapNode, object.offset.add(polylinePoints[kkk])));
}
toPushSheltersZReducer.boundaryType = boundaryType;
toRet.sheltersZReducer.push(toPushSheltersZReducer);
break;
default: default:
break; break;
} }
@ -536,7 +496,7 @@ TileCollisionManager.prototype.extractBoundaryObjects = function (withTiledMapNo
let layerDOMTrees = []; let layerDOMTrees = [];
const mapDomTree = mapInfo._parser._parseXML(tiledMapIns.tmxAsset.tmxXmlStr).documentElement; const mapDomTree = mapInfo._parser._parseXML(tiledMapIns.tmxAsset.tmxXmlStr).documentElement;
const mapDOMAllChildren = (cc.sys.platform == cc.sys.WECHAT_GAME ? mapDomTree.childNodes : mapDomTree.children); const mapDOMAllChildren = (mapDomTree.children);
for (let mdtIdx = 0; mdtIdx < mapDOMAllChildren.length; ++mdtIdx) { for (let mdtIdx = 0; mdtIdx < mapDOMAllChildren.length; ++mdtIdx) {
const tmpCh = mapDOMAllChildren[mdtIdx]; const tmpCh = mapDOMAllChildren[mdtIdx];
if (mapInfo._shouldIgnoreNode(tmpCh)) { if (mapInfo._shouldIgnoreNode(tmpCh)) {
@ -585,23 +545,6 @@ TileCollisionManager.prototype.extractBoundaryObjects = function (withTiledMapNo
} }
toRet.barriers.push(brToPushTmp); toRet.barriers.push(brToPushTmp);
} }
for (let shidx = 0; shidx < gidBoundaries.shelters.length; ++shzridx) {
const theShelter = gidBoundaries.shelters[shidx]; // An array of cc.v2 points.
let shToPushTmp = [];
for (let tshidx = 0; tshidx < theShelter.length; ++tshidx) {
shToPushTmp.push(topLeftOfWholeTsxTileInMapNode.add(cc.v2(theShelter[tshidx].x, -theShelter[tshidx].y)));
}
toRet.shelters.push(shToPushTmp);
}
for (let shzridx = 0; shzridx < gidBoundaries.sheltersZReducer.length; ++shzridx) {
const theShelter = gidBoundaries.sheltersZReducer[shzridx]; // An array of cc.v2 points.
let shzrToPushTmp = [];
for (let tshzridx = 0; tshzridx < theShelter.length; ++tshzridx) {
shzrToPushTmp.push(topLeftOfWholeTsxTileInMapNode.add(cc.v2(theShelter[tshzridx].x, -theShelter[tshzridx].y)));
}
toRet.sheltersZReducer.push(shzrToPushTmp);
}
continue; continue;
default: default:
@ -637,6 +580,7 @@ TileCollisionManager.prototype.isOutOfMapNode = function (tiledMapNode, continuo
}; };
TileCollisionManager.prototype.initMapNodeByTiledBoundaries = function(mapScriptIns, mapNode, extractedBoundaryObjs) { TileCollisionManager.prototype.initMapNodeByTiledBoundaries = function(mapScriptIns, mapNode, extractedBoundaryObjs) {
// TODO: TO DEPRECATE!
const tiledMapIns = mapNode.getComponent(cc.TiledMap); const tiledMapIns = mapNode.getComponent(cc.TiledMap);
if (extractedBoundaryObjs.grandBoundaries) { if (extractedBoundaryObjs.grandBoundaries) {
window.grandBoundary = []; window.grandBoundary = [];
@ -683,47 +627,6 @@ TileCollisionManager.prototype.initMapNodeByTiledBoundaries = function(mapScript
frameAnimInType.push(animNode); frameAnimInType.push(animNode);
} }
for (let boundaryObj of extractedBoundaryObjs.shelterChainTails) {
const newShelter = cc.instantiate(mapScriptIns.polygonBoundaryShelterPrefab);
const newBoundaryOffsetInMapNode = cc.v2(boundaryObj[0].x, boundaryObj[0].y);
newShelter.setPosition(newBoundaryOffsetInMapNode);
newShelter.setAnchorPoint(cc.v2(0, 0));
const newShelterColliderIns = newShelter.getComponent(cc.PolygonCollider);
newShelterColliderIns.points = [];
for (let p of boundaryObj) {
newShelterColliderIns.points.push(p.sub(newBoundaryOffsetInMapNode));
}
newShelter.pTiledLayer = boundaryObj.pTiledLayer;
newShelter.tileDiscretePos = boundaryObj.tileDiscretePos;
if (null != boundaryObj.imageObject) {
newShelter.imageObject = boundaryObj.imageObject;
newShelter.tailOrHead = "tail";
window.addToGlobalShelterChainVerticeMap(newShelter.imageObject.imageObjectNode); // Deliberately NOT adding at the "traversal of shelterChainHeads".
}
newShelter.boundaryObj = boundaryObj;
mapScriptIns.node.addChild(newShelter);
}
for (let boundaryObj of extractedBoundaryObjs.shelterChainHeads) {
const newShelter = cc.instantiate(mapScriptIns.polygonBoundaryShelterPrefab);
const newBoundaryOffsetInMapNode = cc.v2(boundaryObj[0].x, boundaryObj[0].y);
newShelter.setPosition(newBoundaryOffsetInMapNode);
newShelter.setAnchorPoint(cc.v2(0, 0));
const newShelterColliderIns = newShelter.getComponent(cc.PolygonCollider);
newShelterColliderIns.points = [];
for (let p of boundaryObj) {
newShelterColliderIns.points.push(p.sub(newBoundaryOffsetInMapNode));
}
newShelter.pTiledLayer = boundaryObj.pTiledLayer;
newShelter.tileDiscretePos = boundaryObj.tileDiscretePos;
if (null != boundaryObj.imageObject) {
newShelter.imageObject = boundaryObj.imageObject;
newShelter.tailOrHead = "head";
}
newShelter.boundaryObj = boundaryObj;
mapScriptIns.node.addChild(newShelter);
}
mapScriptIns.barrierColliders = []; mapScriptIns.barrierColliders = [];
for (let boundaryObj of extractedBoundaryObjs.barriers) { for (let boundaryObj of extractedBoundaryObjs.barriers) {
const newBarrier = cc.instantiate(mapScriptIns.polygonBoundaryBarrierPrefab); const newBarrier = cc.instantiate(mapScriptIns.polygonBoundaryBarrierPrefab);
@ -739,19 +642,6 @@ TileCollisionManager.prototype.initMapNodeByTiledBoundaries = function(mapScript
mapScriptIns.node.addChild(newBarrier); mapScriptIns.node.addChild(newBarrier);
} }
for (let boundaryObj of extractedBoundaryObjs.sheltersZReducer) {
const newShelter = cc.instantiate(mapScriptIns.polygonBoundaryShelterZReducerPrefab);
const newBoundaryOffsetInMapNode = cc.v2(boundaryObj[0].x, boundaryObj[0].y);
newShelter.setPosition(newBoundaryOffsetInMapNode);
newShelter.setAnchorPoint(cc.v2(0, 0));
const newShelterColliderIns = newShelter.getComponent(cc.PolygonCollider);
newShelterColliderIns.points = [];
for (let p of boundaryObj) {
newShelterColliderIns.points.push(p.sub(newBoundaryOffsetInMapNode));
}
mapScriptIns.node.addChild(newShelter);
}
const allLayers = tiledMapIns.getLayers(); const allLayers = tiledMapIns.getLayers();
for (let layer of allLayers) { for (let layer of allLayers) {
const layerType = layer.getProperty("type"); const layerType = layer.getProperty("type");
@ -759,10 +649,6 @@ TileCollisionManager.prototype.initMapNodeByTiledBoundaries = function(mapScript
case "barrier_and_shelter": case "barrier_and_shelter":
setLocalZOrder(layer.node, 3); setLocalZOrder(layer.node, 3);
break; break;
case "shelter_preview":
layer.node.opacity = 100;
setLocalZOrder(layer.node, 500);
break;
default: default:
break; break;
} }

View File

@ -1,19 +1,24 @@
window.DIRECTION_DECODER = [ window.DIRECTION_DECODER = [
[0, 0], [0, 0, null],
[0, +1], [0, +1, null],
[0, -1], [0, -1, null],
[+2, 0], [+2, 0, null],
[-2, 0], [-2, 0, null],
[+2, +1], [+2, +1, null],
[-2, -1], [-2, -1, null],
[+2, -1], [+2, -1, null],
[-2, +1], [-2, +1, null],
[+2, 0], [+2, 0, null],
[-2, 0], [-2, 0, null],
[0, +1], [0, +1, null],
[0, -1], [0, -1, null],
]; ];
for (let k in window.DIRECTION_DECODER) {
const length = Math.sqrt(window.DIRECTION_DECODER[k][0]*window.DIRECTION_DECODER[k][0] + window.DIRECTION_DECODER[k][1]*window.DIRECTION_DECODER[k][1]);
window.DIRECTION_DECODER[k][2] = (0 == length ? 0 : (1.0/length));
}
cc.Class({ cc.Class({
extends: cc.Component, extends: cc.Component,
properties: { properties: {
@ -114,43 +119,6 @@ cc.Class({
this.initialized = true; this.initialized = true;
}, },
justifyMapNodePosAndScale(linearSpeedBase, zoomingSpeedBase) {
const self = this;
if (false == self.mapScriptIns._inputControlEnabled) return;
if (null != self._cachedMapNodePosTarget) {
while (self.maxMovingBufferLength < self._cachedMapNodePosTarget.length) {
self._cachedMapNodePosTarget.shift();
}
if (0 < self._cachedMapNodePosTarget.length && 0 == self.mapNode.getNumberOfRunningActions()) {
const nextMapNodePosTarget = self._cachedMapNodePosTarget.shift();
const linearSpeed = linearSpeedBase;
const finalDiffVec = nextMapNodePosTarget.pos.sub(self.mapNode.position);
const finalDiffVecMag = finalDiffVec.mag();
if (self.linearMovingEps > finalDiffVecMag) {
// Jittering.
// cc.log("Map node moving by finalDiffVecMag == %s is just jittering.", finalDiffVecMag);
return;
}
const durationSeconds = finalDiffVecMag / linearSpeed;
cc.log("Map node moving to %o in %s/%s == %s seconds.", nextMapNodePosTarget.pos, finalDiffVecMag, linearSpeed, durationSeconds);
const bufferedTargetPos = cc.v2(nextMapNodePosTarget.pos.x, nextMapNodePosTarget.pos.y);
self.mapNode.runAction(cc.sequence(
cc.moveTo(durationSeconds, bufferedTargetPos),
cc.callFunc(() => {
if (self._isMapOverMoved(self.mapNode.position)) {
self.mapNode.setPosition(bufferedTargetPos);
}
}, self)
));
}
}
if (null != self._cachedZoomRawTarget && false == self._cachedZoomRawTarget.processed) {
cc.log(`Processing self._cachedZoomRawTarget == ${self._cachedZoomRawTarget}`);
self._cachedZoomRawTarget.processed = true;
self.mapNode.setScale(self._cachedZoomRawTarget.scale);
}
},
_initTouchEvent() { _initTouchEvent() {
const self = this; const self = this;
const translationListenerNode = (self.translationListenerNode ? self.translationListenerNode : self.mapNode); const translationListenerNode = (self.translationListenerNode ? self.translationListenerNode : self.mapNode);
@ -377,7 +345,8 @@ cc.Class({
} }
return { return {
dx: mapped[0], dx: mapped[0],
dy: mapped[1] dy: mapped[1],
speedFactor: mapped[2],
} }
}, },

View File

@ -1,628 +0,0 @@
const i18n = require('LanguageData');
i18n.init(window.language); // languageID should be equal to the one we input in New Language ID input field
const WECHAT_ON_HIDE_TARGET_ACTION = {
SHARE_CHAT_MESSAGE: 8,
CLOSE: 3,
};
const pbStructRoot = require('./modules/room_downsync_frame_proto_bundle.forcemsg.js');
window.RoomDownsyncFrame = pbStructRoot.treasurehunterx.RoomDownsyncFrame;
window.BattleColliderInfo = pbStructRoot.treasurehunterx.BattleColliderInfo;
cc.Class({
extends: cc.Component,
properties: {
cavasNode: {
default: null,
type: cc.Node
},
backgroundNode: {
default: null,
type: cc.Node
},
loadingPrefab: {
default: null,
type: cc.Prefab
},
tipsLabel: {
default: null,
type: cc.Label,
},
downloadProgress: {
default: null,
type: cc.ProgressBar,
},
writtenBytes: {
default: null,
type: cc.Label,
},
expectedToWriteBytes: {
default: null,
type: cc.Label,
},
handlerProgress: {
default: null,
type: cc.ProgressBar,
},
handledUrlsCount: {
default: null,
type: cc.Label,
},
toHandledUrlsCount: {
default: null,
type: cc.Label,
},
},
// LIFE-CYCLE CALLBACKS:
onLoad() {
wx.onShow((res) => {
console.log("+++++ wx onShow(), onShow.res ", res);
window.expectedRoomId = res.query.expectedRoomId;
});
wx.onHide((res) => {
// Reference https://developers.weixin.qq.com/minigame/dev/api/wx.exitMiniProgram.html.
console.log("+++++ wx onHide(), onHide.res: ", res);
if (
WECHAT_ON_HIDE_TARGET_ACTION == res.targetAction
||
"back" == res.mode // After "WeChat v7.0.4 on iOS"
||
"close" == res.mode
) {
window.clearLocalStorageAndBackToLoginScene();
} else {
// Deliberately left blank.
}
});
const self = this;
self.getRetCodeList();
self.getRegexList();
self.showTips(i18n.t("login.tips.AUTO_LOGIN_1"));
self.checkIntAuthTokenExpire().then(
() => {
self.showTips(i18n.t("login.tips.AUTO_LOGIN_2"));
const intAuthToken = JSON.parse(cc.sys.localStorage.getItem('selfPlayer')).intAuthToken;
self.useTokenLogin(intAuthToken);
},
() => {
// 调用wx.login然后请求登录。
wx.authorize({
scope: "scope.userInfo",
success() {
self.showTips(i18n.t("login.tips.WECHAT_AUTHORIZED_AND_INT_AUTH_TOKEN_LOGGING_IN"));
wx.login({
success(res) {
console.log("wx login success, res: ", res);
const code = res.code;
wx.getUserInfo({
success(res) {
const userInfo = res.userInfo;
console.log("Get user info ok: ", userInfo);
self.useWxCodeMiniGameLogin(code, userInfo);
},
fail(err) {
console.error(i18n.t("login.tips.AUTO_LOGIN_FAILED_WILL_USE_MANUAL_LOGIN"), err);
self.showTips(i18n.t("login.tips.AUTO_LOGIN_FAILED_WILL_USE_MANUAL_LOGIN"));
self.createAuthorizeThenLoginButton();
},
})
},
fail(err) {
if (err) {
console.error(i18n.t("login.tips.AUTO_LOGIN_FAILED_WILL_USE_MANUAL_LOGIN"), err);
self.showTips(i18n.t("login.tips.AUTO_LOGIN_FAILED_WILL_USE_MANUAL_LOGIN"));
self.createAuthorizeThenLoginButton();
}
},
});
},
fail(err) {
console.error(i18n.t("login.tips.AUTO_LOGIN_FAILED_WILL_USE_MANUAL_LOGIN"), err);
self.showTips(i18n.t("login.tips.AUTO_LOGIN_FAILED_WILL_USE_MANUAL_LOGIN"));
self.createAuthorizeThenLoginButton();
}
})
}
);
},
createAuthorizeThenLoginButton(tips) {
const self = this;
let sysInfo = wx.getSystemInfoSync();
//获取微信界面大小
let width = sysInfo.screenWidth;
let height = sysInfo.screenHeight;
let button = wx.createUserInfoButton({
type: 'text',
text: '',
style: {
left: 0,
top: 0,
width: width,
height: height,
backgroundColor: '#00000000', //最后两位为透明度
color: '#ffffff',
fontSize: 20,
textAlign: "center",
lineHeight: height,
},
});
button.onTap((res) => {
console.log(res);
if (null != res.userInfo) {
const userInfo = res.userInfo;
self.showTips(i18n.t("login.tips.WECHAT_AUTHORIZED_AND_INT_AUTH_TOKEN_LOGGING_IN"));
wx.login({
success(res) {
console.log('wx.login success, res:', res);
const code = res.code;
self.useWxCodeMiniGameLogin(code, userInfo);
button.destroy();
},
fail(err) {
console.err(i18n.t("login.tips.AUTO_LOGIN_FAILED_WILL_USE_MANUAL_LOGIN"), err);
self.showTips(i18n.t("login.tips.AUTO_LOGIN_FAILED_WILL_USE_MANUAL_LOGIN"));
},
});
} else {
self.showTips(i18n.t("login.tips.PLEASE_AUTHORIZE_WECHAT_LOGIN_FIRST"));
}
})
},
onDestroy() {
console.log("+++++++ WechatGameLogin onDestroy()");
},
showTips(text) {
if (this.tipsLabel != null) {
this.tipsLabel.string = text;
} else {
console.log('Login scene showTips failed')
}
},
getRetCodeList() {
const self = this;
self.retCodeDict = constants.RET_CODE;
},
getRegexList() {
const self = this;
self.regexList = {
EMAIL: /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/,
PHONE: /^\+?[0-9]{8,14}$/,
STREET_META: /^.{5,100}$/,
LNG_LAT_TEXT: /^[0-9]+(\.[0-9]{4,6})$/,
SEO_KEYWORD: /^.{2,50}$/,
PASSWORD: /^.{6,50}$/,
SMS_CAPTCHA_CODE: /^[0-9]{4}$/,
ADMIN_HANDLE: /^.{4,50}$/,
};
},
onSMSCaptchaGetButtonClicked(evt) {
var timerEnable = true;
const self = this;
if (!self.checkPhoneNumber('getCaptcha')) {
return;
}
NetworkUtils.ajax({
url: backendAddress.PROTOCOL + '://' + backendAddress.HOST + ':' + backendAddress.PORT + constants.ROUTE_PATH.API + constants.ROUTE_PATH.PLAYER +
constants.ROUTE_PATH.VERSION + constants.ROUTE_PATH.SMS_CAPTCHA + constants.ROUTE_PATH.GET,
type: 'GET',
data: {
phoneCountryCode: self.phoneCountryCodeInput.getComponent(cc.EditBox).string,
phoneNum: self.phoneNumberInput.getComponent(cc.EditBox).string
},
success: function(res) {
switch (res.ret) {
case constants.RET_CODE.OK:
self.phoneNumberTips.getComponent(cc.Label).string = '';
self.captchaTips.getComponent(cc.Label).string = '';
break;
case constants.RET_CODE.DUPLICATED:
self.captchaTips.getComponent(cc.Label).string = i18n.t("login.tips.DUPLICATED");
break;
case constants.RET_CODE.INCORRECT_PHONE_COUNTRY_CODE:
case constants.RET_CODE.INCORRECT_PHONE_NUMBER:
self.captchaTips.getComponent(cc.Label).string = i18n.t("login.tips.PHONE_ERR");
break;
case constants.RET_CODE.IS_TEST_ACC:
self.smsLoginCaptchaInput.getComponent(cc.EditBox).string = res.smsLoginCaptcha;
self.captchaTips.getComponent(cc.Label).string = i18n.t("login.tips.TEST_USER");
timerEnable = false;
// clearInterval(self.countdownTimer);
break;
case constants.RET_CODE.SMS_CAPTCHA_REQUESTED_TOO_FREQUENTLY:
self.captchaTips.getComponent(cc.Label).string = i18n.t("login.tips.SMS_CAPTCHA_FREEQUENT_REQUIRE");
default:
break;
}
if (timerEnable)
self.countdownTime(self);
}
});
},
countdownTime(self) {
self.smsLoginCaptchaButton.off('click', self.onSMSCaptchaGetButtonClicked);
self.smsLoginCaptchaButton.removeChild(self.smsGetCaptchaNode);
self.smsWaitCountdownNode.parent = self.smsLoginCaptchaButton;
var total = 20; // Magic number
self.countdownTimer = setInterval(function() {
if (total === 0) {
self.smsWaitCountdownNode.parent.removeChild(self.smsWaitCountdownNode);
self.smsGetCaptchaNode.parent = self.smsLoginCaptchaButton;
self.smsWaitCountdownNode.getChildByName('WaitTimeLabel').getComponent(cc.Label).string = 20;
self.smsLoginCaptchaButton.on('click', self.onSMSCaptchaGetButtonClicked);
clearInterval(self.countdownTimer);
} else {
total--;
self.smsWaitCountdownNode.getChildByName('WaitTimeLabel').getComponent(cc.Label).string = total;
}
}, 1000)
},
checkIntAuthTokenExpire() {
return new Promise((resolve, reject) => {
if (!cc.sys.localStorage.getItem("selfPlayer")) {
reject();
return;
}
const selfPlayer = JSON.parse(cc.sys.localStorage.getItem('selfPlayer'));
(selfPlayer.intAuthToken && new Date().getTime() < selfPlayer.expiresAt) ? resolve() : reject();
})
},
checkPhoneNumber(type) {
const self = this;
const phoneNumberRegexp = self.regexList.PHONE;
var phoneNumberString = self.phoneNumberInput.getComponent(cc.EditBox).string;
if (phoneNumberString) {
return true;
if (!phoneNumberRegexp.test(phoneNumberString)) {
self.captchaTips.getComponent(cc.Label).string = i18n.t("login.tips.PHONE_ERR");
return false;
} else {
return true;
}
} else {
if (type === 'getCaptcha' || type === 'login') {
self.captchaTips.getComponent(cc.Label).string = i18n.t("login.tips.PHONE_ERR");
}
return false;
}
},
checkCaptcha(type) {
const self = this;
const captchaRegexp = self.regexList.SMS_CAPTCHA_CODE;
var captchaString = self.smsLoginCaptchaInput.getComponent(cc.EditBox).string;
if (captchaString) {
if (self.smsLoginCaptchaInput.getComponent(cc.EditBox).string.length !== 4 || (!captchaRegexp.test(captchaString))) {
self.captchaTips.getComponent(cc.Label).string = i18n.t("login.tips.CAPTCHA_ERR");
return false;
} else {
return true;
}
} else {
if (type === 'login') {
self.captchaTips.getComponent(cc.Label).string = i18n.t("login.tips.CAPTCHA_ERR");
}
return false;
}
},
useTokenLogin(_intAuthToken) {
var self = this;
NetworkUtils.ajax({
url: backendAddress.PROTOCOL + '://' + backendAddress.HOST + ':' + backendAddress.PORT + constants.ROUTE_PATH.API + constants.ROUTE_PATH.PLAYER + constants.ROUTE_PATH.VERSION + constants.ROUTE_PATH.INT_AUTH_TOKEN + constants.ROUTE_PATH.LOGIN,
type: "POST",
data: {
intAuthToken: _intAuthToken
},
success: function(resp) {
self.onLoggedIn(resp);
},
error: function(xhr, status, errMsg) {
console.log("Login attempt `useTokenLogin` failed, about to execute `clearBoundRoomIdInBothVolatileAndPersistentStorage`.");
window.clearBoundRoomIdInBothVolatileAndPersistentStorage()
self.showTips(i18n.t("login.tips.AUTO_LOGIN_FAILED_WILL_USE_MANUAL_LOGIN"));
self.createAuthorizeThenLoginButton();
},
timeout: function() {
self.enableInteractiveControls(true);
},
});
},
enableInteractiveControls(enabled) {},
onLoginButtonClicked(evt) {
const self = this;
if (!self.checkPhoneNumber('login') || !self.checkCaptcha('login')) {
return;
}
self.loginParams = {
phoneCountryCode: self.phoneCountryCodeInput.getComponent(cc.EditBox).string,
phoneNum: self.phoneNumberInput.getComponent(cc.EditBox).string,
smsLoginCaptcha: self.smsLoginCaptchaInput.getComponent(cc.EditBox).string
};
self.enableInteractiveControls(false);
NetworkUtils.ajax({
url: backendAddress.PROTOCOL + '://' + backendAddress.HOST + ':' + backendAddress.PORT + constants.ROUTE_PATH.API + constants.ROUTE_PATH.PLAYER +
constants.ROUTE_PATH.VERSION + constants.ROUTE_PATH.SMS_CAPTCHA + constants.ROUTE_PATH.LOGIN,
type: "POST",
data: self.loginParams,
success: function(resp) {
self.onLoggedIn(resp);
},
error: function(xhr, status, errMsg) {
console.log("Login attempt `onLoginButtonClicked` failed, about to execute `clearBoundRoomIdInBothVolatileAndPersistentStorage`.");
window.clearBoundRoomIdInBothVolatileAndPersistentStorage()
},
timeout: function() {
self.enableInteractiveControls(true);
}
});
},
onWechatLoggedIn(res) {
const self = this;
if (constants.RET_CODE.OK == res.ret) {
//根据服务器返回信息设置selfPlayer
self.enableInteractiveControls(false);
const date = Number(res.expiresAt);
const selfPlayer = {
expiresAt: date,
playerId: res.playerId,
intAuthToken: res.intAuthToken,
displayName: res.displayName,
avatar: res.avatar,
}
cc.sys.localStorage.setItem('selfPlayer', JSON.stringify(selfPlayer));
self.useTokenLogin(res.intAuthToken);
} else {
cc.sys.localStorage.removeItem("selfPlayer");
window.clearBoundRoomIdInBothVolatileAndPersistentStorage();
self.showTips(i18n.t("login.tips.WECHAT_LOGIN_FAILED_TAP_SCREEN_TO_RETRY") + ", errorCode = " + res.ret);
self.createAuthorizeThenLoginButton();
}
},
onLoggedIn(res) {
const self = this;
console.log("OnLoggedIn: ", res);
if (constants.RET_CODE.OK == res.ret) {
if (window.isUsingX5BlinkKernelOrWebkitWeChatKernel()) {
window.initWxSdk = self.initWxSdk.bind(self);
window.initWxSdk();
}
self.enableInteractiveControls(false);
const date = Number(res.expiresAt);
const selfPlayer = {
expiresAt: date,
playerId: res.playerId,
intAuthToken: res.intAuthToken,
avatar: res.avatar,
displayName: res.displayName,
name: res.name,
}
cc.sys.localStorage.setItem("selfPlayer", JSON.stringify(selfPlayer));
console.log("cc.sys.localStorage.selfPlayer = ", cc.sys.localStorage.getItem("selfPlayer"));
if (self.countdownTimer) {
clearInterval(self.countdownTimer);
}
cc.director.loadScene('default_map');
} else {
console.warn('onLoggedIn failed!')
cc.sys.localStorage.removeItem("selfPlayer");
window.clearBoundRoomIdInBothVolatileAndPersistentStorage();
self.enableInteractiveControls(true);
switch (res.ret) {
case constants.RET_CODE.DUPLICATED:
this.captchaTips.getComponent(cc.Label).string = i18n.t("login.tips.DUPLICATED");
break;
case constants.RET_CODE.TOKEN_EXPIRED:
this.captchaTips.getComponent(cc.Label).string = i18n.t("login.tips.LOGIN_TOKEN_EXPIRED");
break;
case constants.RET_CODE.SMS_CAPTCHA_NOT_MATCH:
self.captchaTips.getComponent(cc.Label).string = i18n.t("login.tips.SMS_CAPTCHA_NOT_MATCH");
break;
case constants.RET_CODE.INCORRECT_CAPTCHA:
self.captchaTips.getComponent(cc.Label).string = i18n.t("login.tips.SMS_CAPTCHA_NOT_MATCH");
break;
case constants.RET_CODE.SMS_CAPTCHA_CODE_NOT_EXISTING:
self.captchaTips.getComponent(cc.Label).string = i18n.t("login.tips.SMS_CAPTCHA_NOT_MATCH");
break;
case constants.RET_CODE.INCORRECT_PHONE_NUMBER:
self.captchaTips.getComponent(cc.Label).string = i18n.t("login.tips.INCORRECT_PHONE_NUMBER");
break;
case constants.RET_CODE.INVALID_REQUEST_PARAM:
self.captchaTips.getComponent(cc.Label).string = i18n.t("login.tips.INCORRECT_PHONE_NUMBER");
break;
case constants.RET_CODE.INCORRECT_PHONE_COUNTRY_CODE:
case constants.RET_CODE.INCORRECT_PHONE_NUMBER:
this.captchaTips.getComponent(cc.Label).string = i18n.t("login.tips.INCORRECT_PHONE_NUMBER");
break;
default:
break;
}
self.showTips(i18n.t("login.tips.AUTO_LOGIN_FAILED_WILL_USE_MANUAL_LOGIN"));
self.createAuthorizeThenLoginButton();
}
},
useWXCodeLogin(_code) {
const self = this;
NetworkUtils.ajax({
url: backendAddress.PROTOCOL + '://' + backendAddress.HOST + ':' + backendAddress.PORT + constants.ROUTE_PATH.API + constants.ROUTE_PATH.PLAYER + constants.ROUTE_PATH.VERSION + constants.ROUTE_PATH.WECHAT + constants.ROUTE_PATH.LOGIN,
type: "POST",
data: {
code: _code
},
success: function(res) {
self.onWechatLoggedIn(res);
},
error: function(xhr, status, errMsg) {
console.log("Login attempt `onLoginButtonClicked` failed, about to execute `clearBoundRoomIdInBothVolatileAndPersistentStorage`.");
cc.sys.localStorage.removeItem("selfPlayer");
window.clearBoundRoomIdInBothVolatileAndPersistentStorage();
self.showTips(i18n.t("login.tips.WECHAT_LOGIN_FAILED_TAP_SCREEN_TO_RETRY") + ", errorMsg =" + errMsg);
},
timeout: function() {
console.log("Login attempt `onLoginButtonClicked` timed out, about to execute `clearBoundRoomIdInBothVolatileAndPersistentStorage`.");
cc.sys.localStorage.removeItem("selfPlayer");
window.clearBoundRoomIdInBothVolatileAndPersistentStorage();
self.showTips(i18n.t("login.tips.WECHAT_LOGIN_FAILED_TAP_SCREEN_TO_RETRY") + ", errorMsg =" + errMsg);
},
});
},
// 对比useWxCodeLogin函数只是请求了不同url
useWxCodeMiniGameLogin(_code, _userInfo) {
const self = this;
NetworkUtils.ajax({
url: backendAddress.PROTOCOL + '://' + backendAddress.HOST + ':' + backendAddress.PORT + constants.ROUTE_PATH.API + constants.ROUTE_PATH.PLAYER + constants.ROUTE_PATH.VERSION + constants.ROUTE_PATH.WECHATGAME + constants.ROUTE_PATH.LOGIN,
type: "POST",
data: {
code: _code,
avatarUrl: _userInfo.avatarUrl,
nickName: _userInfo.nickName,
},
success: function(res) {
self.onWechatLoggedIn(res);
},
error: function(xhr, status, errMsg) {
console.log("Login attempt `onLoginButtonClicked` failed, about to execute `clearBoundRoomIdInBothVolatileAndPersistentStorage`.");
cc.sys.localStorage.removeItem("selfPlayer");
window.clearBoundRoomIdInBothVolatileAndPersistentStorage();
self.showTips(i18n.t("login.tips.WECHAT_LOGIN_FAILED_TAP_SCREEN_TO_RETRY") + ", errorMsg =" + errMsg);
self.createAuthorizeThenLoginButton();
},
timeout: function() {
console.log("Login attempt `onLoginButtonClicked` failed, about to execute `clearBoundRoomIdInBothVolatileAndPersistentStorage`.");
cc.sys.localStorage.removeItem("selfPlayer");
window.clearBoundRoomIdInBothVolatileAndPersistentStorage();
self.showTips(i18n.t("login.tips.WECHAT_LOGIN_FAILED_TAP_SCREEN_TO_RETRY"));
self.createAuthorizeThenLoginButton();
},
});
},
getWechatCode(evt) {
let self = this;
self.showTips("");
const wechatServerEndpoint = wechatAddress.PROTOCOL + "://" + wechatAddress.HOST + ((null != wechatAddress.PORT && "" != wechatAddress.PORT.trim()) ? (":" + wechatAddress.PORT) : "");
const url = wechatServerEndpoint + constants.WECHAT.AUTHORIZE_PATH + "?" + wechatAddress.APPID_LITERAL + "&" + constants.WECHAT.REDIRECT_RUI_KEY + NetworkUtils.encode(window.location.href) + "&" + constants.WECHAT.RESPONSE_TYPE + "&" + constants.WECHAT.SCOPE + constants.WECHAT.FIN;
console.log("To visit wechat auth addr: ", url);
window.location.href = url;
},
initWxSdk() {
const selfPlayer = JSON.parse(cc.sys.localStorage.getItem('selfPlayer'));
const origUrl = window.location.protocol + "//" + window.location.host + window.location.pathname;
/*
* The `shareLink` must
* - have its 2nd-order-domain registered as trusted 2nd-order under the targetd `res.jsConfig.app_id`, and
* - extracted from current window.location.href.
*/
const shareLink = origUrl;
const updateAppMsgShareDataObj = {
type: 'link', // 分享类型,music、video或link不填默认为link
dataUrl: '', // 如果type是music或video则要提供数据链接默认为空
title: document.title, // 分享标题
desc: 'Let\'s play together!', // 分享描述
link: shareLink + (cc.sys.localStorage.getItem('boundRoomId') ? "" : ("?expectedRoomId=" + cc.sys.localStorage.getItem('boundRoomId'))),
imgUrl: origUrl + "/favicon.ico", // 分享图标
success: function() {
// 设置成功
}
};
const menuShareTimelineObj = {
title: document.title, // 分享标题
link: shareLink + (cc.sys.localStorage.getItem('boundRoomId') ? "" : ("?expectedRoomId=" + cc.sys.localStorage.getItem('boundRoomId'))),
imgUrl: origUrl + "/favicon.ico", // 分享图标
success: function() {}
};
const wxConfigUrl = (window.isUsingWebkitWechatKernel() ? window.atFirstLocationHref : window.location.href);
//接入微信登录接口
NetworkUtils.ajax({
"url": backendAddress.PROTOCOL + '://' + backendAddress.HOST + ':' + backendAddress.PORT + constants.ROUTE_PATH.API + constants.ROUTE_PATH.PLAYER + constants.ROUTE_PATH.VERSION + constants.ROUTE_PATH.WECHAT + constants.ROUTE_PATH.JSCONFIG,
type: "POST",
data: {
"url": wxConfigUrl,
"intAuthToken": selfPlayer.intAuthToken,
},
success: function(res) {
if (constants.RET_CODE.OK != res.ret) {
console.warn("Failed to get `wsConfig`: ", res);
return;
}
const jsConfig = res.jsConfig;
const configData = {
debug: CC_DEBUG, // 开启调试模式,调用的所有api的返回值会在客户端alert出来若要查看传入的参数可以在pc端打开参数信息会通过log打出仅在pc端时才会打印。
appId: jsConfig.app_id, // 必填,公众号的唯一标识
timestamp: jsConfig.timestamp.toString(), // 必填,生成签名的时间戳
nonceStr: jsConfig.nonce_str, // 必填,生成签名的随机串
jsApiList: ['onMenuShareAppMessage', 'onMenuShareTimeline'],
signature: jsConfig.signature, // 必填,签名
};
console.log("config url: ", wxConfigUrl);
console.log("wx.config: ", configData);
wx.config(configData);
console.log("Current window.location.href: ", window.location.href);
wx.ready(function() {
console.log("Here is wx.ready.")
wx.onMenuShareAppMessage(updateAppMsgShareDataObj);
wx.onMenuShareTimeline(menuShareTimelineObj);
});
wx.error(function(res) {
console.error("wx config fails and error is ", JSON.stringify(res));
});
},
error: function(xhr, status, errMsg) {
console.error("Failed to get `wsConfig`: ", errMsg);
},
});
},
update(dt) {
const self = this;
if (null != wxDownloader && 0 < wxDownloader.totalBytesExpectedToWriteForAllTasks) {
self.writtenBytes.string = wxDownloader.totalBytesWrittenForAllTasks;
self.expectedToWriteBytes.string = wxDownloader.totalBytesExpectedToWriteForAllTasks;
self.downloadProgress.progress = 1.0*wxDownloader.totalBytesWrittenForAllTasks/wxDownloader.totalBytesExpectedToWriteForAllTasks;
}
const totalUrlsToHandle = (wxDownloader.immediateHandleItemCount + wxDownloader.immediateReadFromLocalCount + wxDownloader.immediatePackDownloaderCount);
const totalUrlsHandled = (wxDownloader.immediateHandleItemCompleteCount + wxDownloader.immediateReadFromLocalCompleteCount + wxDownloader.immediatePackDownloaderCompleteCount);
if (null != wxDownloader && 0 < totalUrlsToHandle) {
self.handledUrlsCount.string = totalUrlsHandled;
self.toHandledUrlsCount.string = totalUrlsToHandle;
self.handlerProgress.progress = 1.0*totalUrlsHandled/totalUrlsToHandle;
}
}
});

View File

@ -74,8 +74,6 @@ function _base64ToUint8Array(base64) {
origBytes[i] = origBinaryStr.charCodeAt(i); origBytes[i] = origBinaryStr.charCodeAt(i);
} }
return origBytes; return origBytes;
} else if (cc.sys.platform == cc.sys.WECHAT_GAME) {
return Buffer.from(base64, 'base64');
} else { } else {
return null; return null;
} }
@ -86,9 +84,6 @@ function _base64ToArrayBuffer(base64) {
} }
window.getExpectedRoomIdSync = function() { window.getExpectedRoomIdSync = function() {
if (cc.sys.platform == cc.sys.WECHAT_GAME) {
return window.expectedRoomId;
} else {
const qDict = window.getQueryParamDict(); const qDict = window.getQueryParamDict();
if (qDict) { if (qDict) {
return qDict["expectedRoomId"]; return qDict["expectedRoomId"];
@ -97,7 +92,6 @@ window.getExpectedRoomIdSync = function() {
return window.history.state.expectedRoomId; return window.history.state.expectedRoomId;
} }
} }
}
return null; return null;
}; };
@ -129,10 +123,6 @@ window.initPersistentSessionClient = function(onopenCb, expectedRoomId) {
if (null != expectedRoomId) { if (null != expectedRoomId) {
console.log("initPersistentSessionClient with expectedRoomId == " + expectedRoomId); console.log("initPersistentSessionClient with expectedRoomId == " + expectedRoomId);
urlToConnect = urlToConnect + "&expectedRoomId=" + expectedRoomId; urlToConnect = urlToConnect + "&expectedRoomId=" + expectedRoomId;
if (cc.sys.platform == cc.sys.WECHAT_GAME) {
// This is a dirty hack. -- YFLu
window.expectedRoomId = null;
}
} else { } else {
window.boundRoomId = getBoundRoomIdFromPersistentStorage(); window.boundRoomId = getBoundRoomIdFromPersistentStorage();
if (null != window.boundRoomId) { if (null != window.boundRoomId) {
@ -143,10 +133,6 @@ window.initPersistentSessionClient = function(onopenCb, expectedRoomId) {
const currentHistoryState = window.history && window.history.state ? window.history.state : {}; const currentHistoryState = window.history && window.history.state ? window.history.state : {};
if (cc.sys.platform != cc.sys.WECHAT_GAME) {
window.history.replaceState(currentHistoryState, document.title, window.location.pathname);
}
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"
@ -254,10 +240,6 @@ window.clearLocalStorageAndBackToLoginScene = function(shouldRetainBoundRoomIdIn
if (true != shouldRetainBoundRoomIdInBothVolatileAndPersistentStorage) { if (true != shouldRetainBoundRoomIdInBothVolatileAndPersistentStorage) {
window.clearBoundRoomIdInBothVolatileAndPersistentStorage(); window.clearBoundRoomIdInBothVolatileAndPersistentStorage();
} }
if (cc.sys.platform == cc.sys.WECHAT_GAME) {
cc.director.loadScene('wechatGameLogin');
} else {
cc.director.loadScene('login'); cc.director.loadScene('login');
}
}; };

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,9 @@
{
"ver": "1.0.5",
"uuid": "da0a517f-5c74-4fc0-ba89-dbcee184b13e",
"isPlugin": false,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": false,
"subMetas": {}
}

View File

@ -4343,7 +4343,6 @@ $root.treasurehunterx = (function() {
* @property {Object.<string,treasurehunterx.Trap>|null} [traps] RoomDownsyncFrame traps * @property {Object.<string,treasurehunterx.Trap>|null} [traps] RoomDownsyncFrame traps
* @property {Object.<string,treasurehunterx.Bullet>|null} [bullets] RoomDownsyncFrame bullets * @property {Object.<string,treasurehunterx.Bullet>|null} [bullets] RoomDownsyncFrame bullets
* @property {Object.<string,treasurehunterx.SpeedShoe>|null} [speedShoes] RoomDownsyncFrame speedShoes * @property {Object.<string,treasurehunterx.SpeedShoe>|null} [speedShoes] RoomDownsyncFrame speedShoes
* @property {Object.<string,treasurehunterx.Pumpkin>|null} [pumpkin] RoomDownsyncFrame pumpkin
* @property {Object.<string,treasurehunterx.GuardTower>|null} [guardTowers] RoomDownsyncFrame guardTowers * @property {Object.<string,treasurehunterx.GuardTower>|null} [guardTowers] RoomDownsyncFrame guardTowers
* @property {Object.<string,treasurehunterx.PlayerMeta>|null} [playerMetas] RoomDownsyncFrame playerMetas * @property {Object.<string,treasurehunterx.PlayerMeta>|null} [playerMetas] RoomDownsyncFrame playerMetas
*/ */
@ -4362,7 +4361,6 @@ $root.treasurehunterx = (function() {
this.traps = {}; this.traps = {};
this.bullets = {}; this.bullets = {};
this.speedShoes = {}; this.speedShoes = {};
this.pumpkin = {};
this.guardTowers = {}; this.guardTowers = {};
this.playerMetas = {}; this.playerMetas = {};
if (properties) if (properties)
@ -4443,14 +4441,6 @@ $root.treasurehunterx = (function() {
*/ */
RoomDownsyncFrame.prototype.speedShoes = $util.emptyObject; RoomDownsyncFrame.prototype.speedShoes = $util.emptyObject;
/**
* RoomDownsyncFrame pumpkin.
* @member {Object.<string,treasurehunterx.Pumpkin>} pumpkin
* @memberof treasurehunterx.RoomDownsyncFrame
* @instance
*/
RoomDownsyncFrame.prototype.pumpkin = $util.emptyObject;
/** /**
* RoomDownsyncFrame guardTowers. * RoomDownsyncFrame guardTowers.
* @member {Object.<string,treasurehunterx.GuardTower>} guardTowers * @member {Object.<string,treasurehunterx.GuardTower>} guardTowers
@ -4524,19 +4514,14 @@ $root.treasurehunterx = (function() {
writer.uint32(/* id 9, wireType 2 =*/74).fork().uint32(/* id 1, wireType 0 =*/8).int32(keys[i]); writer.uint32(/* id 9, wireType 2 =*/74).fork().uint32(/* id 1, wireType 0 =*/8).int32(keys[i]);
$root.treasurehunterx.SpeedShoe.encode(message.speedShoes[keys[i]], writer.uint32(/* id 2, wireType 2 =*/18).fork()).ldelim().ldelim(); $root.treasurehunterx.SpeedShoe.encode(message.speedShoes[keys[i]], writer.uint32(/* id 2, wireType 2 =*/18).fork()).ldelim().ldelim();
} }
if (message.pumpkin != null && Object.hasOwnProperty.call(message, "pumpkin"))
for (var keys = Object.keys(message.pumpkin), i = 0; i < keys.length; ++i) {
writer.uint32(/* id 10, wireType 2 =*/82).fork().uint32(/* id 1, wireType 0 =*/8).int32(keys[i]);
$root.treasurehunterx.Pumpkin.encode(message.pumpkin[keys[i]], writer.uint32(/* id 2, wireType 2 =*/18).fork()).ldelim().ldelim();
}
if (message.guardTowers != null && Object.hasOwnProperty.call(message, "guardTowers")) if (message.guardTowers != null && Object.hasOwnProperty.call(message, "guardTowers"))
for (var keys = Object.keys(message.guardTowers), i = 0; i < keys.length; ++i) { for (var keys = Object.keys(message.guardTowers), i = 0; i < keys.length; ++i) {
writer.uint32(/* id 11, wireType 2 =*/90).fork().uint32(/* id 1, wireType 0 =*/8).int32(keys[i]); writer.uint32(/* id 10, wireType 2 =*/82).fork().uint32(/* id 1, wireType 0 =*/8).int32(keys[i]);
$root.treasurehunterx.GuardTower.encode(message.guardTowers[keys[i]], writer.uint32(/* id 2, wireType 2 =*/18).fork()).ldelim().ldelim(); $root.treasurehunterx.GuardTower.encode(message.guardTowers[keys[i]], writer.uint32(/* id 2, wireType 2 =*/18).fork()).ldelim().ldelim();
} }
if (message.playerMetas != null && Object.hasOwnProperty.call(message, "playerMetas")) if (message.playerMetas != null && Object.hasOwnProperty.call(message, "playerMetas"))
for (var keys = Object.keys(message.playerMetas), i = 0; i < keys.length; ++i) { for (var keys = Object.keys(message.playerMetas), i = 0; i < keys.length; ++i) {
writer.uint32(/* id 12, wireType 2 =*/98).fork().uint32(/* id 1, wireType 0 =*/8).int32(keys[i]); writer.uint32(/* id 11, wireType 2 =*/90).fork().uint32(/* id 1, wireType 0 =*/8).int32(keys[i]);
$root.treasurehunterx.PlayerMeta.encode(message.playerMetas[keys[i]], writer.uint32(/* id 2, wireType 2 =*/18).fork()).ldelim().ldelim(); $root.treasurehunterx.PlayerMeta.encode(message.playerMetas[keys[i]], writer.uint32(/* id 2, wireType 2 =*/18).fork()).ldelim().ldelim();
} }
return writer; return writer;
@ -4705,29 +4690,6 @@ $root.treasurehunterx = (function() {
break; break;
} }
case 10: { case 10: {
if (message.pumpkin === $util.emptyObject)
message.pumpkin = {};
var end2 = reader.uint32() + reader.pos;
key = 0;
value = null;
while (reader.pos < end2) {
var tag2 = reader.uint32();
switch (tag2 >>> 3) {
case 1:
key = reader.int32();
break;
case 2:
value = $root.treasurehunterx.Pumpkin.decode(reader, reader.uint32());
break;
default:
reader.skipType(tag2 & 7);
break;
}
}
message.pumpkin[key] = value;
break;
}
case 11: {
if (message.guardTowers === $util.emptyObject) if (message.guardTowers === $util.emptyObject)
message.guardTowers = {}; message.guardTowers = {};
var end2 = reader.uint32() + reader.pos; var end2 = reader.uint32() + reader.pos;
@ -4750,7 +4712,7 @@ $root.treasurehunterx = (function() {
message.guardTowers[key] = value; message.guardTowers[key] = value;
break; break;
} }
case 12: { case 11: {
if (message.playerMetas === $util.emptyObject) if (message.playerMetas === $util.emptyObject)
message.playerMetas = {}; message.playerMetas = {};
var end2 = reader.uint32() + reader.pos; var end2 = reader.uint32() + reader.pos;
@ -4890,20 +4852,6 @@ $root.treasurehunterx = (function() {
} }
} }
} }
if (message.pumpkin != null && message.hasOwnProperty("pumpkin")) {
if (!$util.isObject(message.pumpkin))
return "pumpkin: object expected";
var key = Object.keys(message.pumpkin);
for (var i = 0; i < key.length; ++i) {
if (!$util.key32Re.test(key[i]))
return "pumpkin: integer key{k:int32} expected";
{
var error = $root.treasurehunterx.Pumpkin.verify(message.pumpkin[key[i]]);
if (error)
return "pumpkin." + error;
}
}
}
if (message.guardTowers != null && message.hasOwnProperty("guardTowers")) { if (message.guardTowers != null && message.hasOwnProperty("guardTowers")) {
if (!$util.isObject(message.guardTowers)) if (!$util.isObject(message.guardTowers))
return "guardTowers: object expected"; return "guardTowers: object expected";
@ -5019,16 +4967,6 @@ $root.treasurehunterx = (function() {
message.speedShoes[keys[i]] = $root.treasurehunterx.SpeedShoe.fromObject(object.speedShoes[keys[i]]); message.speedShoes[keys[i]] = $root.treasurehunterx.SpeedShoe.fromObject(object.speedShoes[keys[i]]);
} }
} }
if (object.pumpkin) {
if (typeof object.pumpkin !== "object")
throw TypeError(".treasurehunterx.RoomDownsyncFrame.pumpkin: object expected");
message.pumpkin = {};
for (var keys = Object.keys(object.pumpkin), i = 0; i < keys.length; ++i) {
if (typeof object.pumpkin[keys[i]] !== "object")
throw TypeError(".treasurehunterx.RoomDownsyncFrame.pumpkin: object expected");
message.pumpkin[keys[i]] = $root.treasurehunterx.Pumpkin.fromObject(object.pumpkin[keys[i]]);
}
}
if (object.guardTowers) { if (object.guardTowers) {
if (typeof object.guardTowers !== "object") if (typeof object.guardTowers !== "object")
throw TypeError(".treasurehunterx.RoomDownsyncFrame.guardTowers: object expected"); throw TypeError(".treasurehunterx.RoomDownsyncFrame.guardTowers: object expected");
@ -5071,7 +5009,6 @@ $root.treasurehunterx = (function() {
object.traps = {}; object.traps = {};
object.bullets = {}; object.bullets = {};
object.speedShoes = {}; object.speedShoes = {};
object.pumpkin = {};
object.guardTowers = {}; object.guardTowers = {};
object.playerMetas = {}; object.playerMetas = {};
} }
@ -5129,11 +5066,6 @@ $root.treasurehunterx = (function() {
for (var j = 0; j < keys2.length; ++j) for (var j = 0; j < keys2.length; ++j)
object.speedShoes[keys2[j]] = $root.treasurehunterx.SpeedShoe.toObject(message.speedShoes[keys2[j]], options); object.speedShoes[keys2[j]] = $root.treasurehunterx.SpeedShoe.toObject(message.speedShoes[keys2[j]], options);
} }
if (message.pumpkin && (keys2 = Object.keys(message.pumpkin)).length) {
object.pumpkin = {};
for (var j = 0; j < keys2.length; ++j)
object.pumpkin[keys2[j]] = $root.treasurehunterx.Pumpkin.toObject(message.pumpkin[keys2[j]], options);
}
if (message.guardTowers && (keys2 = Object.keys(message.guardTowers)).length) { if (message.guardTowers && (keys2 = Object.keys(message.guardTowers)).length) {
object.guardTowers = {}; object.guardTowers = {};
for (var j = 0; j < keys2.length; ++j) for (var j = 0; j < keys2.length; ++j)

View File

@ -33,20 +33,21 @@
"design-resolution-height": 640, "design-resolution-height": 640,
"design-resolution-width": 960, "design-resolution-width": 960,
"excluded-modules": [ "excluded-modules": [
"Spine Skeleton", "Collider",
"DragonBones", "DragonBones",
"RichText", "Geom Utils",
"Mesh",
"MotionStreak", "MotionStreak",
"Physics",
"PageView", "PageView",
"PageViewIndicator", "PageViewIndicator",
"RichText",
"Slider", "Slider",
"Spine Skeleton",
"StudioComponent",
"VideoPlayer", "VideoPlayer",
"WebView", "WebView",
"Physics",
"StudioComponent",
"3D", "3D",
"Mesh",
"Geom Utils",
"3D Primitive" "3D Primitive"
], ],
"facebook": { "facebook": {
@ -74,7 +75,7 @@
"height": 640, "height": 640,
"width": 960 "width": 960
}, },
"start-scene": "current", "use-customize-simulator": true,
"use-customize-simulator": false, "use-project-simulator-setting": false,
"use-project-simulator-setting": false "start-scene": "current"
} }