diff --git a/ConcerningEdgeCases.md b/ConcerningEdgeCases.md new file mode 100644 index 0000000..f828f41 --- /dev/null +++ b/ConcerningEdgeCases.md @@ -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 `/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. diff --git a/battle_srv/models/ringbuf.go b/battle_srv/models/ringbuf.go index 5d2d82d..572e4d1 100644 --- a/battle_srv/models/ringbuf.go +++ b/battle_srv/models/ringbuf.go @@ -1,8 +1,8 @@ package models type RingBuffer struct { - Ed int32 // write index - St int32 // read index + Ed int32 // write index, open index + St int32 // read index, closed index EdFrameId int32 StFrameId int32 N int32 diff --git a/battle_srv/models/room.go b/battle_srv/models/room.go index ebf3055..cb74cc6 100644 --- a/battle_srv/models/room.go +++ b/battle_srv/models/room.go @@ -3,7 +3,6 @@ package models import ( "encoding/xml" "fmt" - "strings" "github.com/ByteArena/box2d" "github.com/golang/protobuf/proto" "github.com/gorilla/websocket" @@ -15,6 +14,7 @@ import ( . "server/common" "server/common/utils" pb "server/pb_output" + "strings" "sync" "sync/atomic" "time" @@ -31,8 +31,8 @@ const ( ) const ( - MAGIC_REMOVED_AT_FRAME_ID_PERMANENT_REMOVAL_MARGIN = 5 - MAGIC_ROOM_DOWNSYNC_FRAME_ID_BATTLE_READY_TO_START = -99 + MAGIC_ROOM_DOWNSYNC_FRAME_ID_BATTLE_READY_TO_START = -1 + MAGIC_ROOM_DOWNSYNC_FRAME_ID_BATTLE_START = 0 MAGIC_ROOM_DOWNSYNC_FRAME_ID_PLAYER_ADDED_AND_ACKED = -98 MAGIC_ROOM_DOWNSYNC_FRAME_ID_PLAYER_READDED_AND_ACKED = -97 @@ -535,7 +535,7 @@ func (pR *Room) ChooseStage() error { ErrFatal(err) rand.Seed(time.Now().Unix()) - stageNameList := []string{/*"pacman" ,*/ "richsoil"} + stageNameList := []string{ /*"pacman" ,*/ "richsoil"} chosenStageIndex := rand.Int() % len(stageNameList) // Hardcoded temporarily. -- YFLu pR.StageName = stageNameList[chosenStageIndex] @@ -701,10 +701,10 @@ func (pR *Room) CanPopSt(refLowerInputFrameId int32) bool { func (pR *Room) AllPlayerInputsBufferString() string { s := make([]string, 0) - s = append(s, fmt.Sprintf("{lastAllConfirmedInputFrameId: %v, lastAllConfirmedInputFrameIdWithChange: %v}", pR.LastAllConfirmedInputFrameId, pR.LastAllConfirmedInputFrameIdWithChange)) + s = append(s, fmt.Sprintf("{lastAllConfirmedInputFrameId: %v, lastAllConfirmedInputFrameIdWithChange: %v}", pR.LastAllConfirmedInputFrameId, pR.LastAllConfirmedInputFrameIdWithChange)) for playerId, player := range pR.Players { - s = append(s, fmt.Sprintf("{playerId: %v, ackingFrameId: %v, ackingInputFrameId: %v, lastSentInputFrameId: %v}", playerId, player.AckingFrameId, player.AckingInputFrameId, player.LastSentInputFrameId)) - } + s = append(s, fmt.Sprintf("{playerId: %v, ackingFrameId: %v, ackingInputFrameId: %v, lastSentInputFrameId: %v}", playerId, player.AckingFrameId, player.AckingInputFrameId, player.LastSentInputFrameId)) + } for i := pR.AllPlayerInputsBuffer.StFrameId; i < pR.AllPlayerInputsBuffer.EdFrameId; i++ { tmp := pR.AllPlayerInputsBuffer.GetByFrameId(i) if nil == tmp { @@ -736,27 +736,23 @@ func (pR *Room) StartBattle() { */ battleMainLoop := func() { defer func() { - if r := recover(); r != nil { - Logger.Error("battleMainLoop, recovery spot#1, recovered from: ", zap.Any("roomId", pR.Id), zap.Any("panic", r)) - } + if r := recover(); r != nil { + Logger.Error("battleMainLoop, recovery spot#1, recovered from: ", zap.Any("roomId", pR.Id), zap.Any("panic", r)) + } Logger.Info("The `battleMainLoop` is stopped for:", zap.Any("roomId", pR.Id)) pR.onBattleStoppedForSettlement() }() battleMainLoopStartedNanos := utils.UnixtimeNano() - var totalElapsedNanos int64 - totalElapsedNanos = 0 - - // inputFrameIdDownsyncToleranceFrameCnt := int32(1) + totalElapsedNanos := int64(0) Logger.Info("The `battleMainLoop` is started for:", zap.Any("roomId", pR.Id)) for { - pR.Tick++ // It's important to increment "pR.Tick" here such that the "InputFrameDownsync.InputFrameId" is most advanced - if 1 == pR.Tick { + if 0 == pR.Tick { // The legacy frontend code needs this "kickoffFrame" to remove the "ready to start 3-2-1" panel kickoffFrame := pb.RoomDownsyncFrame{ Id: pR.Tick, - RefFrameId: 0, // Hardcoded for now. + RefFrameId: MAGIC_ROOM_DOWNSYNC_FRAME_ID_BATTLE_START, Players: toPbPlayers(pR.Players), Treasures: toPbTreasures(pR.Treasures), 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! 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 { tmp := pR.AllPlayerInputsBuffer.GetByFrameId(candidateToSendInputFrameId) if nil == tmp { @@ -848,12 +843,7 @@ func (pR *Room) StartBattle() { } } - /* - 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)) - } - */ + pR.Tick++ now := utils.UnixtimeNano() elapsedInCalculation := now - stCalculation totalElapsedNanos = (now - battleMainLoopStartedNanos) @@ -953,10 +943,10 @@ 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)) } atomic.StoreInt32(&(pR.LastAllConfirmedInputFrameId), clientInputFrameId) // [WARNING] It's IMPORTANT that "pR.LastAllConfirmedInputFrameId" is NOT NECESSARILY CONSECUTIVE, i.e. if one of the players disconnects and reconnects within a considerable amount of frame delays! - for i,v := range inputFrameDownsync.InputList { - // To avoid potential misuse of pointers - pR.LastAllConfirmedInputList[i] = v - } + for i, v := range inputFrameDownsync.InputList { + // To avoid potential misuse of pointers + pR.LastAllConfirmedInputList[i] = v + } if pR.inputFrameIdDebuggable(clientInputFrameId) { Logger.Info("inputFrame lifecycle#2[allconfirmed]", zap.Any("roomId", pR.Id), zap.Any("playerId", playerId), zap.Any("inputFrameId", clientInputFrameId), zap.Any("StFrameId", pR.AllPlayerInputsBuffer.StFrameId), zap.Any("EdFrameId", pR.AllPlayerInputsBuffer.EdFrameId)) } @@ -987,7 +977,7 @@ func (pR *Room) StopBattleForSettlement() { for playerId, _ := range pR.Players { assembledFrame := pb.RoomDownsyncFrame{ Id: pR.Tick, - RefFrameId: 0, // Hardcoded for now. + RefFrameId: pR.Tick, // Hardcoded for now. Players: toPbPlayers(pR.Players), SentAt: utils.UnixtimeMilli(), CountdownNanos: -1, // TODO: Replace this magic constant! @@ -1027,12 +1017,12 @@ func (pR *Room) onBattlePrepare(cb BattleStartCbType) { } battleReadyToStartFrame := pb.RoomDownsyncFrame{ - Id: pR.Tick, - Players: toPbPlayers(pR.Players), - SentAt: utils.UnixtimeMilli(), - RefFrameId: MAGIC_ROOM_DOWNSYNC_FRAME_ID_BATTLE_READY_TO_START, - PlayerMetas: playerMetas, - CountdownNanos: pR.BattleDurationNanos, + Id: pR.Tick, + Players: toPbPlayers(pR.Players), + SentAt: utils.UnixtimeMilli(), + RefFrameId: MAGIC_ROOM_DOWNSYNC_FRAME_ID_BATTLE_READY_TO_START, + PlayerMetas: playerMetas, + CountdownNanos: pR.BattleDurationNanos, } Logger.Info("Sending out frame for RoomBattleState.PREPARE ", zap.Any("battleReadyToStartFrame", battleReadyToStartFrame)) diff --git a/battle_srv/models/room_heap_manager.go b/battle_srv/models/room_heap_manager.go index e9f4a12..70b58b7 100644 --- a/battle_srv/models/room_heap_manager.go +++ b/battle_srv/models/room_heap_manager.go @@ -109,23 +109,23 @@ func InitRoomHeapManager() { Tick: 0, EffectivePlayerCount: 0, //BattleDurationNanos: int64(5 * 1000 * 1000 * 1000), - BattleDurationNanos: int64(30 * 1000 * 1000 * 1000), - ServerFPS: 60, - Treasures: make(map[int32]*Treasure), - Traps: make(map[int32]*Trap), - GuardTowers: make(map[int32]*GuardTower), - Bullets: make(map[int32]*Bullet), - SpeedShoes: make(map[int32]*SpeedShoe), - Barriers: make(map[int32]*Barrier), - Pumpkins: make(map[int32]*Pumpkin), - AccumulatedLocalIdForBullets: 0, - AllPlayerInputsBuffer: NewRingBuffer(1024), - LastAllConfirmedInputFrameId: -1, - LastAllConfirmedInputFrameIdWithChange: -1, - LastAllConfirmedInputList: make([]uint64, roomCapacity), - InputDelayFrames: 4, - InputScaleFrames: 2, - JoinIndexBooleanArr: joinIndexBooleanArr, + BattleDurationNanos: int64(30 * 1000 * 1000 * 1000), + ServerFPS: 60, + Treasures: make(map[int32]*Treasure), + Traps: make(map[int32]*Trap), + GuardTowers: make(map[int32]*GuardTower), + Bullets: make(map[int32]*Bullet), + SpeedShoes: make(map[int32]*SpeedShoe), + Barriers: make(map[int32]*Barrier), + Pumpkins: make(map[int32]*Pumpkin), + AccumulatedLocalIdForBullets: 0, + AllPlayerInputsBuffer: NewRingBuffer(1024), + LastAllConfirmedInputFrameId: -1, + LastAllConfirmedInputFrameIdWithChange: -1, + LastAllConfirmedInputList: make([]uint64, roomCapacity), + InputDelayFrames: 4, + InputScaleFrames: 2, + JoinIndexBooleanArr: joinIndexBooleanArr, } roomMap[pq[i].Id] = pq[i] pq[i].ChooseStage() diff --git a/battle_srv/pb_output/room_downsync_frame.pb.go b/battle_srv/pb_output/room_downsync_frame.pb.go index 86432fc..7f19732 100644 --- a/battle_srv/pb_output/room_downsync_frame.pb.go +++ b/battle_srv/pb_output/room_downsync_frame.pb.go @@ -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"` 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"` - 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,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,12,rep,name=playerMetas,proto3" json:"playerMetas,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"` + 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"` } func (x *RoomDownsyncFrame) Reset() { @@ -1240,13 +1239,6 @@ func (x *RoomDownsyncFrame) GetSpeedShoes() map[int32]*SpeedShoe { return nil } -func (x *RoomDownsyncFrame) GetPumpkin() map[int32]*Pumpkin { - if x != nil { - return x.Pumpkin - } - return nil -} - func (x *RoomDownsyncFrame) GetGuardTowers() map[int32]*GuardTower { if x != nil { 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, 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, - 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x22, 0xbb, - 0x0b, 0x0a, 0x11, 0x52, 0x6f, 0x6f, 0x6d, 0x44, 0x6f, 0x77, 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x46, + 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x22, 0x9a, + 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, 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, @@ -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, 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, - 0x68, 0x6f, 0x65, 0x73, 0x12, 0x49, 0x0a, 0x07, 0x70, 0x75, 0x6d, 0x70, 0x6b, 0x69, 0x6e, 0x18, - 0x0a, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, - 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x52, 0x6f, 0x6f, 0x6d, 0x44, 0x6f, 0x77, 0x6e, - 0x73, 0x79, 0x6e, 0x63, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x2e, 0x50, 0x75, 0x6d, 0x70, 0x6b, 0x69, - 0x6e, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x70, 0x75, 0x6d, 0x70, 0x6b, 0x69, 0x6e, 0x12, - 0x55, 0x0a, 0x0b, 0x67, 0x75, 0x61, 0x72, 0x64, 0x54, 0x6f, 0x77, 0x65, 0x72, 0x73, 0x18, 0x0b, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, - 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x52, 0x6f, 0x6f, 0x6d, 0x44, 0x6f, 0x77, 0x6e, 0x73, - 0x79, 0x6e, 0x63, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x2e, 0x47, 0x75, 0x61, 0x72, 0x64, 0x54, 0x6f, - 0x77, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0b, 0x67, 0x75, 0x61, 0x72, 0x64, - 0x54, 0x6f, 0x77, 0x65, 0x72, 0x73, 0x12, 0x55, 0x0a, 0x0b, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, - 0x4d, 0x65, 0x74, 0x61, 0x73, 0x18, 0x0c, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x74, 0x72, - 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x52, 0x6f, - 0x6f, 0x6d, 0x44, 0x6f, 0x77, 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x2e, - 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, - 0x52, 0x0b, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x73, 0x1a, 0x53, 0x0a, - 0x0c, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, - 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, - 0x2d, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, - 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, - 0x2e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, - 0x38, 0x01, 0x1a, 0x57, 0x0a, 0x0e, 0x54, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x73, 0x45, - 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x05, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2f, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, - 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x54, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, - 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x4f, 0x0a, 0x0a, 0x54, - 0x72, 0x61, 0x70, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2b, 0x0a, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x72, 0x65, - 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x54, 0x72, 0x61, - 0x70, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x53, 0x0a, 0x0c, - 0x42, 0x75, 0x6c, 0x6c, 0x65, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, - 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2d, - 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, - 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, - 0x42, 0x75, 0x6c, 0x6c, 0x65, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, - 0x01, 0x1a, 0x59, 0x0a, 0x0f, 0x53, 0x70, 0x65, 0x65, 0x64, 0x53, 0x68, 0x6f, 0x65, 0x73, 0x45, - 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x05, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x30, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, - 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x53, 0x70, 0x65, 0x65, 0x64, 0x53, 0x68, 0x6f, - 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x54, 0x0a, 0x0c, - 0x50, 0x75, 0x6d, 0x70, 0x6b, 0x69, 0x6e, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, - 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2e, - 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, - 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, - 0x50, 0x75, 0x6d, 0x70, 0x6b, 0x69, 0x6e, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, - 0x38, 0x01, 0x1a, 0x5b, 0x0a, 0x10, 0x47, 0x75, 0x61, 0x72, 0x64, 0x54, 0x6f, 0x77, 0x65, 0x72, - 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x05, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x31, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, - 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x47, 0x75, 0x61, 0x72, 0x64, 0x54, - 0x6f, 0x77, 0x65, 0x72, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, - 0x5b, 0x0a, 0x10, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x73, 0x45, 0x6e, - 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, - 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x31, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, - 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4d, 0x65, 0x74, - 0x61, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x56, 0x0a, 0x10, - 0x49, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x70, 0x73, 0x79, 0x6e, 0x63, - 0x12, 0x22, 0x0a, 0x0c, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x49, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, - 0x6d, 0x65, 0x49, 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x44, - 0x69, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, - 0x64, 0x44, 0x69, 0x72, 0x22, 0x7c, 0x0a, 0x12, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, - 0x6d, 0x65, 0x44, 0x6f, 0x77, 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x12, 0x22, 0x0a, 0x0c, 0x69, 0x6e, - 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, - 0x52, 0x0c, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x49, 0x64, 0x12, 0x1c, - 0x0a, 0x09, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x18, 0x02, 0x20, 0x03, 0x28, - 0x04, 0x52, 0x09, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x24, 0x0a, 0x0d, - 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x65, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x04, 0x52, 0x0d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x65, 0x64, 0x4c, 0x69, - 0x73, 0x74, 0x22, 0x3b, 0x0a, 0x0f, 0x48, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x55, - 0x70, 0x73, 0x79, 0x6e, 0x63, 0x12, 0x28, 0x0a, 0x0f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x54, - 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0f, - 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, - 0xca, 0x02, 0x0a, 0x05, 0x57, 0x73, 0x52, 0x65, 0x71, 0x12, 0x14, 0x0a, 0x05, 0x6d, 0x73, 0x67, - 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x6d, 0x73, 0x67, 0x49, 0x64, 0x12, - 0x1a, 0x0a, 0x08, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x49, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x05, 0x52, 0x08, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x49, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x61, - 0x63, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x61, 0x63, 0x74, 0x12, 0x1c, 0x0a, - 0x09, 0x6a, 0x6f, 0x69, 0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, - 0x52, 0x09, 0x6a, 0x6f, 0x69, 0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x24, 0x0a, 0x0d, 0x61, - 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x49, 0x64, 0x18, 0x05, 0x20, 0x01, - 0x28, 0x05, 0x52, 0x0d, 0x61, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x49, - 0x64, 0x12, 0x2e, 0x0a, 0x12, 0x61, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x70, 0x75, 0x74, - 0x46, 0x72, 0x61, 0x6d, 0x65, 0x49, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x12, 0x61, - 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x49, - 0x64, 0x12, 0x57, 0x0a, 0x15, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x55, - 0x70, 0x73, 0x79, 0x6e, 0x63, 0x42, 0x61, 0x74, 0x63, 0x68, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x21, 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, 0x55, 0x70, 0x73, - 0x79, 0x6e, 0x63, 0x52, 0x15, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x55, - 0x70, 0x73, 0x79, 0x6e, 0x63, 0x42, 0x61, 0x74, 0x63, 0x68, 0x12, 0x30, 0x0a, 0x02, 0x68, 0x62, - 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, - 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x48, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, - 0x61, 0x74, 0x55, 0x70, 0x73, 0x79, 0x6e, 0x63, 0x52, 0x02, 0x68, 0x62, 0x22, 0xa4, 0x02, 0x0a, - 0x06, 0x57, 0x73, 0x52, 0x65, 0x73, 0x70, 0x12, 0x10, 0x0a, 0x03, 0x72, 0x65, 0x74, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x72, 0x65, 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x65, 0x63, 0x68, - 0x6f, 0x65, 0x64, 0x4d, 0x73, 0x67, 0x49, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, - 0x65, 0x63, 0x68, 0x6f, 0x65, 0x64, 0x4d, 0x73, 0x67, 0x49, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x61, - 0x63, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x61, 0x63, 0x74, 0x12, 0x34, 0x0a, - 0x03, 0x72, 0x64, 0x66, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x74, 0x72, 0x65, - 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x52, 0x6f, 0x6f, - 0x6d, 0x44, 0x6f, 0x77, 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x52, 0x03, - 0x72, 0x64, 0x66, 0x12, 0x5d, 0x0a, 0x17, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, - 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, + 0x68, 0x6f, 0x65, 0x73, 0x12, 0x55, 0x0a, 0x0b, 0x67, 0x75, 0x61, 0x72, 0x64, 0x54, 0x6f, 0x77, + 0x65, 0x72, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x74, 0x72, 0x65, 0x61, + 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x52, 0x6f, 0x6f, 0x6d, + 0x44, 0x6f, 0x77, 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x2e, 0x47, 0x75, + 0x61, 0x72, 0x64, 0x54, 0x6f, 0x77, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0b, + 0x67, 0x75, 0x61, 0x72, 0x64, 0x54, 0x6f, 0x77, 0x65, 0x72, 0x73, 0x12, 0x55, 0x0a, 0x0b, 0x70, + 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x73, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x33, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, + 0x72, 0x78, 0x2e, 0x52, 0x6f, 0x6f, 0x6d, 0x44, 0x6f, 0x77, 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x46, + 0x72, 0x61, 0x6d, 0x65, 0x2e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x73, + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0b, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4d, 0x65, 0x74, + 0x61, 0x73, 0x1a, 0x53, 0x0a, 0x0c, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, + 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, + 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2d, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, + 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x52, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x57, 0x0a, 0x0e, 0x54, 0x72, 0x65, 0x61, 0x73, + 0x75, 0x72, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2f, 0x0a, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x74, 0x72, 0x65, + 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x54, 0x72, 0x65, + 0x61, 0x73, 0x75, 0x72, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, + 0x1a, 0x4f, 0x0a, 0x0a, 0x54, 0x72, 0x61, 0x70, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, + 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x6b, 0x65, 0x79, + 0x12, 0x2b, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x15, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, + 0x78, 0x2e, 0x54, 0x72, 0x61, 0x70, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, + 0x01, 0x1a, 0x53, 0x0a, 0x0c, 0x42, 0x75, 0x6c, 0x6c, 0x65, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, + 0x6b, 0x65, 0x79, 0x12, 0x2d, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, + 0x74, 0x65, 0x72, 0x78, 0x2e, 0x42, 0x75, 0x6c, 0x6c, 0x65, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x59, 0x0a, 0x0f, 0x53, 0x70, 0x65, 0x65, 0x64, 0x53, + 0x68, 0x6f, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x30, 0x0a, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x74, 0x72, 0x65, + 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x53, 0x70, 0x65, + 0x65, 0x64, 0x53, 0x68, 0x6f, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, + 0x01, 0x1a, 0x5b, 0x0a, 0x10, 0x47, 0x75, 0x61, 0x72, 0x64, 0x54, 0x6f, 0x77, 0x65, 0x72, 0x73, + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x05, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x31, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, + 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x47, 0x75, 0x61, 0x72, 0x64, 0x54, 0x6f, + 0x77, 0x65, 0x72, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x5b, + 0x0a, 0x10, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x73, 0x45, 0x6e, 0x74, + 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, + 0x03, 0x6b, 0x65, 0x79, 0x12, 0x31, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, + 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, + 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x56, 0x0a, 0x10, 0x49, + 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x70, 0x73, 0x79, 0x6e, 0x63, 0x12, + 0x22, 0x0a, 0x0c, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x49, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, + 0x65, 0x49, 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x44, 0x69, + 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x64, + 0x44, 0x69, 0x72, 0x22, 0x7c, 0x0a, 0x12, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, + 0x65, 0x44, 0x6f, 0x77, 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x12, 0x22, 0x0a, 0x0c, 0x69, 0x6e, 0x70, + 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, + 0x0c, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x49, 0x64, 0x12, 0x1c, 0x0a, + 0x09, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x18, 0x02, 0x20, 0x03, 0x28, 0x04, + 0x52, 0x09, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x24, 0x0a, 0x0d, 0x63, + 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x65, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x0d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x65, 0x64, 0x4c, 0x69, 0x73, + 0x74, 0x22, 0x3b, 0x0a, 0x0f, 0x48, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x55, 0x70, + 0x73, 0x79, 0x6e, 0x63, 0x12, 0x28, 0x0a, 0x0f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x54, 0x69, + 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0f, 0x63, + 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0xca, + 0x02, 0x0a, 0x05, 0x57, 0x73, 0x52, 0x65, 0x71, 0x12, 0x14, 0x0a, 0x05, 0x6d, 0x73, 0x67, 0x49, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x6d, 0x73, 0x67, 0x49, 0x64, 0x12, 0x1a, + 0x0a, 0x08, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x49, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, + 0x52, 0x08, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x49, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x63, + 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x61, 0x63, 0x74, 0x12, 0x1c, 0x0a, 0x09, + 0x6a, 0x6f, 0x69, 0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, + 0x09, 0x6a, 0x6f, 0x69, 0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x24, 0x0a, 0x0d, 0x61, 0x63, + 0x6b, 0x69, 0x6e, 0x67, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x49, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x05, 0x52, 0x0d, 0x61, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x49, 0x64, + 0x12, 0x2e, 0x0a, 0x12, 0x61, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x46, + 0x72, 0x61, 0x6d, 0x65, 0x49, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x12, 0x61, 0x63, + 0x6b, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x49, 0x64, + 0x12, 0x57, 0x0a, 0x15, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x70, + 0x73, 0x79, 0x6e, 0x63, 0x42, 0x61, 0x74, 0x63, 0x68, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x21, 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, 0x55, 0x70, 0x73, 0x79, + 0x6e, 0x63, 0x52, 0x15, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x70, + 0x73, 0x79, 0x6e, 0x63, 0x42, 0x61, 0x74, 0x63, 0x68, 0x12, 0x30, 0x0a, 0x02, 0x68, 0x62, 0x18, + 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, + 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x48, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, + 0x74, 0x55, 0x70, 0x73, 0x79, 0x6e, 0x63, 0x52, 0x02, 0x68, 0x62, 0x22, 0xa4, 0x02, 0x0a, 0x06, + 0x57, 0x73, 0x52, 0x65, 0x73, 0x70, 0x12, 0x10, 0x0a, 0x03, 0x72, 0x65, 0x74, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x05, 0x52, 0x03, 0x72, 0x65, 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x65, 0x63, 0x68, 0x6f, + 0x65, 0x64, 0x4d, 0x73, 0x67, 0x49, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x65, + 0x63, 0x68, 0x6f, 0x65, 0x64, 0x4d, 0x73, 0x67, 0x49, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x63, + 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x61, 0x63, 0x74, 0x12, 0x34, 0x0a, 0x03, + 0x72, 0x64, 0x66, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x74, 0x72, 0x65, 0x61, + 0x73, 0x75, 0x72, 0x65, 0x68, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x78, 0x2e, 0x52, 0x6f, 0x6f, 0x6d, + 0x44, 0x6f, 0x77, 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x52, 0x03, 0x72, + 0x64, 0x66, 0x12, 0x5d, 0x0a, 0x17, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 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 ( @@ -1942,7 +1924,7 @@ func file_room_downsync_frame_proto_rawDescGZIP() []byte { 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{}{ (*Direction)(nil), // 0: treasurehunterx.Direction (*Vec2D)(nil), // 1: treasurehunterx.Vec2D @@ -1971,9 +1953,8 @@ var file_room_downsync_frame_proto_goTypes = []interface{}{ nil, // 24: treasurehunterx.RoomDownsyncFrame.TrapsEntry nil, // 25: treasurehunterx.RoomDownsyncFrame.BulletsEntry nil, // 26: treasurehunterx.RoomDownsyncFrame.SpeedShoesEntry - nil, // 27: treasurehunterx.RoomDownsyncFrame.PumpkinEntry - nil, // 28: treasurehunterx.RoomDownsyncFrame.GuardTowersEntry - nil, // 29: treasurehunterx.RoomDownsyncFrame.PlayerMetasEntry + nil, // 27: treasurehunterx.RoomDownsyncFrame.GuardTowersEntry + nil, // 28: treasurehunterx.RoomDownsyncFrame.PlayerMetasEntry } var file_room_downsync_frame_proto_depIdxs = []int32{ 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 25, // 12: treasurehunterx.RoomDownsyncFrame.bullets:type_name -> treasurehunterx.RoomDownsyncFrame.BulletsEntry 26, // 13: treasurehunterx.RoomDownsyncFrame.speedShoes:type_name -> treasurehunterx.RoomDownsyncFrame.SpeedShoesEntry - 27, // 14: treasurehunterx.RoomDownsyncFrame.pumpkin:type_name -> treasurehunterx.RoomDownsyncFrame.PumpkinEntry - 28, // 15: treasurehunterx.RoomDownsyncFrame.guardTowers:type_name -> treasurehunterx.RoomDownsyncFrame.GuardTowersEntry - 29, // 16: treasurehunterx.RoomDownsyncFrame.playerMetas:type_name -> treasurehunterx.RoomDownsyncFrame.PlayerMetasEntry - 15, // 17: treasurehunterx.WsReq.inputFrameUpsyncBatch:type_name -> treasurehunterx.InputFrameUpsync - 17, // 18: treasurehunterx.WsReq.hb:type_name -> treasurehunterx.HeartbeatUpsync - 14, // 19: treasurehunterx.WsResp.rdf:type_name -> treasurehunterx.RoomDownsyncFrame - 16, // 20: treasurehunterx.WsResp.inputFrameDownsyncBatch:type_name -> treasurehunterx.InputFrameDownsync - 5, // 21: treasurehunterx.WsResp.bciFrame:type_name -> treasurehunterx.BattleColliderInfo - 3, // 22: treasurehunterx.BattleColliderInfo.StrToVec2DListMapEntry.value:type_name -> treasurehunterx.Vec2DList - 4, // 23: treasurehunterx.BattleColliderInfo.StrToPolygon2DListMapEntry.value:type_name -> treasurehunterx.Polygon2DList - 6, // 24: treasurehunterx.RoomDownsyncFrame.PlayersEntry.value:type_name -> treasurehunterx.Player - 8, // 25: treasurehunterx.RoomDownsyncFrame.TreasuresEntry.value:type_name -> treasurehunterx.Treasure - 10, // 26: treasurehunterx.RoomDownsyncFrame.TrapsEntry.value:type_name -> treasurehunterx.Trap - 9, // 27: treasurehunterx.RoomDownsyncFrame.BulletsEntry.value:type_name -> treasurehunterx.Bullet - 11, // 28: treasurehunterx.RoomDownsyncFrame.SpeedShoesEntry.value:type_name -> treasurehunterx.SpeedShoe - 12, // 29: treasurehunterx.RoomDownsyncFrame.PumpkinEntry.value:type_name -> treasurehunterx.Pumpkin - 13, // 30: treasurehunterx.RoomDownsyncFrame.GuardTowersEntry.value:type_name -> treasurehunterx.GuardTower - 7, // 31: treasurehunterx.RoomDownsyncFrame.PlayerMetasEntry.value:type_name -> treasurehunterx.PlayerMeta - 32, // [32:32] is the sub-list for method output_type - 32, // [32:32] is the sub-list for method input_type - 32, // [32:32] is the sub-list for extension type_name - 32, // [32:32] is the sub-list for extension extendee - 0, // [0:32] is the sub-list for field type_name + 27, // 14: treasurehunterx.RoomDownsyncFrame.guardTowers:type_name -> treasurehunterx.RoomDownsyncFrame.GuardTowersEntry + 28, // 15: treasurehunterx.RoomDownsyncFrame.playerMetas:type_name -> treasurehunterx.RoomDownsyncFrame.PlayerMetasEntry + 15, // 16: treasurehunterx.WsReq.inputFrameUpsyncBatch:type_name -> treasurehunterx.InputFrameUpsync + 17, // 17: treasurehunterx.WsReq.hb:type_name -> treasurehunterx.HeartbeatUpsync + 14, // 18: treasurehunterx.WsResp.rdf:type_name -> treasurehunterx.RoomDownsyncFrame + 16, // 19: treasurehunterx.WsResp.inputFrameDownsyncBatch:type_name -> treasurehunterx.InputFrameDownsync + 5, // 20: treasurehunterx.WsResp.bciFrame:type_name -> treasurehunterx.BattleColliderInfo + 3, // 21: treasurehunterx.BattleColliderInfo.StrToVec2DListMapEntry.value:type_name -> treasurehunterx.Vec2DList + 4, // 22: treasurehunterx.BattleColliderInfo.StrToPolygon2DListMapEntry.value:type_name -> treasurehunterx.Polygon2DList + 6, // 23: treasurehunterx.RoomDownsyncFrame.PlayersEntry.value:type_name -> treasurehunterx.Player + 8, // 24: treasurehunterx.RoomDownsyncFrame.TreasuresEntry.value:type_name -> treasurehunterx.Treasure + 10, // 25: treasurehunterx.RoomDownsyncFrame.TrapsEntry.value:type_name -> treasurehunterx.Trap + 9, // 26: treasurehunterx.RoomDownsyncFrame.BulletsEntry.value:type_name -> treasurehunterx.Bullet + 11, // 27: treasurehunterx.RoomDownsyncFrame.SpeedShoesEntry.value:type_name -> treasurehunterx.SpeedShoe + 13, // 28: treasurehunterx.RoomDownsyncFrame.GuardTowersEntry.value:type_name -> treasurehunterx.GuardTower + 7, // 29: treasurehunterx.RoomDownsyncFrame.PlayerMetasEntry.value:type_name -> treasurehunterx.PlayerMeta + 30, // [30:30] is the sub-list for method output_type + 30, // [30:30] is the sub-list for method input_type + 30, // [30:30] is the sub-list for extension type_name + 30, // [30:30] is the sub-list for extension extendee + 0, // [0:30] is the sub-list for field type_name } func init() { file_room_downsync_frame_proto_init() } @@ -2268,7 +2247,7 @@ func file_room_downsync_frame_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_room_downsync_frame_proto_rawDesc, NumEnums: 0, - NumMessages: 30, + NumMessages: 29, NumExtensions: 0, NumServices: 0, }, diff --git a/frontend/assets/resources/pbfiles/room_downsync_frame.proto b/frontend/assets/resources/pbfiles/room_downsync_frame.proto index ed1e4b4..9f414cd 100644 --- a/frontend/assets/resources/pbfiles/room_downsync_frame.proto +++ b/frontend/assets/resources/pbfiles/room_downsync_frame.proto @@ -126,9 +126,8 @@ message RoomDownsyncFrame { map traps = 7; map bullets = 8; map speedShoes = 9; - map pumpkin = 10; - map guardTowers = 11; - map playerMetas = 12; + map guardTowers = 10; + map playerMetas = 11; } message InputFrameUpsync { diff --git a/frontend/assets/resources/prefabs/Pacman1.prefab b/frontend/assets/resources/prefabs/Pacman1.prefab index bfc71fe..7d5723c 100644 --- a/frontend/assets/resources/prefabs/Pacman1.prefab +++ b/frontend/assets/resources/prefabs/Pacman1.prefab @@ -8,7 +8,8 @@ "__id__": 1 }, "optimizationPolicy": 0, - "asyncLoadAssets": false + "asyncLoadAssets": false, + "readonly": false }, { "__type__": "cc.Node", @@ -27,7 +28,6 @@ } ], "_active": true, - "_level": 1, "_components": [ { "__id__": 11 @@ -63,17 +63,6 @@ "x": 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": { "__type__": "TypedArray", "ctor": "Float64Array", @@ -89,7 +78,19 @@ 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", @@ -100,7 +101,6 @@ }, "_children": [], "_active": false, - "_level": 0, "_components": [ { "__id__": 3 @@ -127,17 +127,6 @@ "x": 0.5, "y": 0.5 }, - "_quat": { - "__type__": "cc.Quat", - "x": 0, - "y": 0, - "z": 0, - "w": 1 - }, - "_skewX": 0, - "_skewY": 0, - "groupIndex": 0, - "_id": "", "_trs": { "__type__": "TypedArray", "ctor": "Float64Array", @@ -153,7 +142,19 @@ 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", @@ -163,6 +164,7 @@ "__id__": 2 }, "_enabled": true, + "_materials": [], "_useOriginalSize": false, "_string": "(0, 0)", "_N$string": "(0, 0)", @@ -200,7 +202,6 @@ }, "_children": [], "_active": false, - "_level": 2, "_components": [ { "__id__": 6 @@ -227,17 +228,6 @@ "x": 0.5, "y": 0.5 }, - "_quat": { - "__type__": "cc.Quat", - "x": 0, - "y": 0, - "z": 0, - "w": 1 - }, - "_skewX": 0, - "_skewY": 0, - "groupIndex": 0, - "_id": "", "_trs": { "__type__": "TypedArray", "ctor": "Float64Array", @@ -253,7 +243,19 @@ 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", @@ -263,6 +265,9 @@ "__id__": 5 }, "_enabled": true, + "_materials": [], + "_srcBlendFactor": 770, + "_dstBlendFactor": 1, "_custom": true, "_file": { "__uuid__": "b2687ac4-099e-403c-a192-ff477686f4f5" @@ -271,8 +276,6 @@ "__uuid__": "472df5d3-35e7-4184-9e6c-7f41bee65ee3" }, "_texture": null, - "_srcBlendFactor": 770, - "_dstBlendFactor": 1, "_stopped": false, "playOnLoad": true, "autoRemoveOnFinish": false, @@ -329,6 +332,7 @@ "x": 7, "y": 7 }, + "_positionType": 1, "positionType": 1, "emitterMode": 0, "gravity": { @@ -372,7 +376,6 @@ }, "_children": [], "_active": true, - "_level": 2, "_components": [ { "__id__": 9 @@ -399,17 +402,6 @@ "x": 0.5, "y": 0.5 }, - "_quat": { - "__type__": "cc.Quat", - "x": 0, - "y": 0, - "z": 0, - "w": 1 - }, - "_skewX": 0, - "_skewY": 0, - "groupIndex": 0, - "_id": "", "_trs": { "__type__": "TypedArray", "ctor": "Float64Array", @@ -425,7 +417,19 @@ 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", @@ -435,6 +439,13 @@ "__id__": 8 }, "_enabled": true, + "_materials": [ + { + "__uuid__": "eca5d2f2-8ef6-41c2-bbe6-f9c79d09c432" + } + ], + "_srcBlendFactor": 770, + "_dstBlendFactor": 771, "_spriteFrame": { "__uuid__": "a2170e4c-df31-41ef-be73-f4f605e75821" }, @@ -449,12 +460,9 @@ "_fillStart": 0, "_fillRange": 0, "_isTrimmedMode": true, - "_state": 0, "_atlas": { "__uuid__": "030d9286-e8a2-40cf-98f8-baf713f0b8c4" }, - "_srcBlendFactor": 770, - "_dstBlendFactor": 771, "_id": "" }, { @@ -476,6 +484,9 @@ "__id__": 1 }, "_enabled": true, + "_materials": [], + "_srcBlendFactor": 770, + "_dstBlendFactor": 771, "_spriteFrame": null, "_type": 0, "_sizeMode": 0, @@ -488,10 +499,7 @@ "_fillStart": 0, "_fillRange": 0, "_isTrimmedMode": true, - "_state": 0, "_atlas": null, - "_srcBlendFactor": 770, - "_dstBlendFactor": 771, "_id": "" }, { @@ -549,8 +557,8 @@ }, "_enabled": true, "animComp": null, - "baseSpeed": 300, - "speed": 200, + "baseSpeed": 50, + "speed": 50, "lastMovedAt": 0, "eps": 0.1, "magicLeanLowerBound": 0.414, diff --git a/frontend/assets/resources/prefabs/Pacman2.prefab b/frontend/assets/resources/prefabs/Pacman2.prefab index 84058c2..cc5b6c8 100644 --- a/frontend/assets/resources/prefabs/Pacman2.prefab +++ b/frontend/assets/resources/prefabs/Pacman2.prefab @@ -8,7 +8,8 @@ "__id__": 1 }, "optimizationPolicy": 0, - "asyncLoadAssets": false + "asyncLoadAssets": false, + "readonly": false }, { "__type__": "cc.Node", @@ -27,7 +28,6 @@ } ], "_active": true, - "_level": 1, "_components": [ { "__id__": 11 @@ -63,17 +63,6 @@ "x": 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": { "__type__": "TypedArray", "ctor": "Float64Array", @@ -89,7 +78,19 @@ 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", @@ -100,7 +101,6 @@ }, "_children": [], "_active": false, - "_level": 0, "_components": [ { "__id__": 3 @@ -127,17 +127,6 @@ "x": 0.5, "y": 0.5 }, - "_quat": { - "__type__": "cc.Quat", - "x": 0, - "y": 0, - "z": 0, - "w": 1 - }, - "_skewX": 0, - "_skewY": 0, - "groupIndex": 0, - "_id": "", "_trs": { "__type__": "TypedArray", "ctor": "Float64Array", @@ -153,7 +142,19 @@ 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", @@ -163,6 +164,7 @@ "__id__": 2 }, "_enabled": true, + "_materials": [], "_useOriginalSize": false, "_string": "(0, 0)", "_N$string": "(0, 0)", @@ -200,7 +202,6 @@ }, "_children": [], "_active": false, - "_level": 2, "_components": [ { "__id__": 6 @@ -227,17 +228,6 @@ "x": 0.5, "y": 0.5 }, - "_quat": { - "__type__": "cc.Quat", - "x": 0, - "y": 0, - "z": 0, - "w": 1 - }, - "_skewX": 0, - "_skewY": 0, - "groupIndex": 0, - "_id": "", "_trs": { "__type__": "TypedArray", "ctor": "Float64Array", @@ -253,7 +243,19 @@ 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", @@ -263,6 +265,9 @@ "__id__": 5 }, "_enabled": true, + "_materials": [], + "_srcBlendFactor": 770, + "_dstBlendFactor": 1, "_custom": true, "_file": { "__uuid__": "b2687ac4-099e-403c-a192-ff477686f4f5" @@ -271,8 +276,6 @@ "__uuid__": "472df5d3-35e7-4184-9e6c-7f41bee65ee3" }, "_texture": null, - "_srcBlendFactor": 770, - "_dstBlendFactor": 1, "_stopped": false, "playOnLoad": true, "autoRemoveOnFinish": false, @@ -329,6 +332,7 @@ "x": 7, "y": 7 }, + "_positionType": 1, "positionType": 1, "emitterMode": 0, "gravity": { @@ -372,7 +376,6 @@ }, "_children": [], "_active": true, - "_level": 2, "_components": [ { "__id__": 9 @@ -399,17 +402,6 @@ "x": 0.5, "y": 0.5 }, - "_quat": { - "__type__": "cc.Quat", - "x": 0, - "y": 0, - "z": 0, - "w": 1 - }, - "_skewX": 0, - "_skewY": 0, - "groupIndex": 0, - "_id": "", "_trs": { "__type__": "TypedArray", "ctor": "Float64Array", @@ -425,7 +417,19 @@ 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", @@ -435,6 +439,13 @@ "__id__": 8 }, "_enabled": true, + "_materials": [ + { + "__uuid__": "eca5d2f2-8ef6-41c2-bbe6-f9c79d09c432" + } + ], + "_srcBlendFactor": 770, + "_dstBlendFactor": 771, "_spriteFrame": { "__uuid__": "a2170e4c-df31-41ef-be73-f4f605e75821" }, @@ -449,12 +460,9 @@ "_fillStart": 0, "_fillRange": 0, "_isTrimmedMode": true, - "_state": 0, "_atlas": { "__uuid__": "030d9286-e8a2-40cf-98f8-baf713f0b8c4" }, - "_srcBlendFactor": 770, - "_dstBlendFactor": 771, "_id": "" }, { @@ -476,6 +484,9 @@ "__id__": 1 }, "_enabled": true, + "_materials": [], + "_srcBlendFactor": 770, + "_dstBlendFactor": 771, "_spriteFrame": null, "_type": 0, "_sizeMode": 0, @@ -488,10 +499,7 @@ "_fillStart": 0, "_fillRange": 0, "_isTrimmedMode": true, - "_state": 0, "_atlas": null, - "_srcBlendFactor": 770, - "_dstBlendFactor": 771, "_id": "" }, { @@ -549,8 +557,8 @@ }, "_enabled": true, "animComp": null, - "baseSpeed": 300, - "speed": 200, + "baseSpeed": 50, + "speed": 50, "lastMovedAt": 0, "eps": 0.1, "magicLeanLowerBound": 0.414, diff --git a/frontend/assets/scenes/login.fire b/frontend/assets/scenes/login.fire index 34a6ab6..020e9a8 100644 --- a/frontend/assets/scenes/login.fire +++ b/frontend/assets/scenes/login.fire @@ -440,7 +440,7 @@ "array": [ 0, 0, - 209.7912853806815, + 216.05530045313827, 0, 0, 0, @@ -2991,9 +2991,6 @@ "loadingPrefab": { "__uuid__": "f2a3cece-30bf-4f62-bc20-34d44a9ddf98" }, - "wechatLoginTips": { - "__id__": 68 - }, "_id": "51YNpecnJBea8vzAxGdkv2" }, { diff --git a/frontend/assets/scenes/wechatGameLogin.fire b/frontend/assets/scenes/wechatGameLogin.fire deleted file mode 100644 index 141f368..0000000 --- a/frontend/assets/scenes/wechatGameLogin.fire +++ /dev/null @@ -1,1706 +0,0 @@ -[ - { - "__type__": "cc.SceneAsset", - "_name": "", - "_objFlags": 0, - "_native": "", - "scene": { - "__id__": 1 - } - }, - { - "__type__": "cc.Scene", - "_objFlags": 0, - "_parent": null, - "_children": [ - { - "__id__": 2 - } - ], - "_active": false, - "_level": 0, - "_components": [], - "_prefab": null, - "_opacity": 255, - "_color": { - "__type__": "cc.Color", - "r": 255, - "g": 255, - "b": 255, - "a": 255 - }, - "_contentSize": { - "__type__": "cc.Size", - "width": 0, - "height": 0 - }, - "_anchorPoint": { - "__type__": "cc.Vec2", - "x": 0, - "y": 0 - }, - "_is3DNode": true, - "groupIndex": 0, - "autoReleaseAssets": false, - "_id": "475b849b-44b3-4390-982d-bd0d9e695093", - "_trs": { - "__type__": "TypedArray", - "ctor": "Float64Array", - "array": [ - 0, - 0, - 0, - 0, - 0, - 0, - 1, - 1, - 1, - 1 - ] - } - }, - { - "__type__": "cc.Node", - "_name": "Canvas", - "_objFlags": 0, - "_parent": { - "__id__": 1 - }, - "_children": [ - { - "__id__": 3 - }, - { - "__id__": 5 - }, - { - "__id__": 7 - }, - { - "__id__": 12 - }, - { - "__id__": 23 - } - ], - "_active": true, - "_level": 1, - "_components": [ - { - "__id__": 34 - }, - { - "__id__": 35 - }, - { - "__id__": 36 - }, - { - "__id__": 37 - } - ], - "_prefab": null, - "_opacity": 255, - "_color": { - "__type__": "cc.Color", - "r": 255, - "g": 255, - "b": 255, - "a": 255 - }, - "_contentSize": { - "__type__": "cc.Size", - "width": 640, - "height": 960 - }, - "_anchorPoint": { - "__type__": "cc.Vec2", - "x": 0.5, - "y": 0.5 - }, - "_eulerAngles": { - "__type__": "cc.Vec3", - "x": 0, - "y": 0, - "z": 0 - }, - "_skewX": 0, - "_skewY": 0, - "_is3DNode": false, - "groupIndex": 0, - "_id": "88kscZWXFCIZtNFekdSP/o", - "_trs": { - "__type__": "TypedArray", - "ctor": "Float64Array", - "array": [ - 320, - 480, - 0, - 0, - 0, - 0, - 1, - 1, - 1, - 1 - ] - } - }, - { - "__type__": "cc.Node", - "_name": "Main Camera", - "_objFlags": 0, - "_parent": { - "__id__": 2 - }, - "_children": [], - "_active": true, - "_level": 0, - "_components": [ - { - "__id__": 4 - } - ], - "_prefab": null, - "_opacity": 255, - "_color": { - "__type__": "cc.Color", - "r": 255, - "g": 255, - "b": 255, - "a": 255 - }, - "_contentSize": { - "__type__": "cc.Size", - "width": 0, - "height": 0 - }, - "_anchorPoint": { - "__type__": "cc.Vec2", - "x": 0.5, - "y": 0.5 - }, - "_eulerAngles": { - "__type__": "cc.Vec3", - "x": 0, - "y": 0, - "z": 0 - }, - "_skewX": 0, - "_skewY": 0, - "_is3DNode": false, - "groupIndex": 0, - "_id": "f1+7GE/nBJIrtiDgZie30q", - "_trs": { - "__type__": "TypedArray", - "ctor": "Float64Array", - "array": [ - 0, - 0, - 202.36995509211857, - 0, - 0, - 0, - 1, - 1, - 1, - 1 - ] - } - }, - { - "__type__": "cc.Camera", - "_name": "", - "_objFlags": 0, - "node": { - "__id__": 3 - }, - "_enabled": true, - "_cullingMask": 4294967295, - "_clearFlags": 0, - "_backgroundColor": { - "__type__": "cc.Color", - "r": 0, - "g": 0, - "b": 0, - "a": 255 - }, - "_depth": 0, - "_zoomRatio": 1, - "_targetTexture": null, - "_fov": 60, - "_orthoSize": 10, - "_nearClip": 1, - "_farClip": 4096, - "_ortho": true, - "_rect": { - "__type__": "cc.Rect", - "x": 0, - "y": 0, - "width": 1, - "height": 1 - }, - "_renderStages": 1, - "_id": "7eS1XfnvVCVJh7J5AVbnd8" - }, - { - "__type__": "cc.Node", - "_name": "Tips", - "_objFlags": 0, - "_parent": { - "__id__": 2 - }, - "_children": [], - "_active": true, - "_level": 2, - "_components": [ - { - "__id__": 6 - } - ], - "_prefab": null, - "_opacity": 255, - "_color": { - "__type__": "cc.Color", - "r": 0, - "g": 0, - "b": 0, - "a": 255 - }, - "_contentSize": { - "__type__": "cc.Size", - "width": 600, - "height": 50.4 - }, - "_anchorPoint": { - "__type__": "cc.Vec2", - "x": 0.5, - "y": 0.5 - }, - "_eulerAngles": { - "__type__": "cc.Vec3", - "x": 0, - "y": 0, - "z": 0 - }, - "_skewX": 0, - "_skewY": 0, - "_is3DNode": false, - "groupIndex": 0, - "_id": "a282mCIUBOmqmf3o22ZSKE", - "_trs": { - "__type__": "TypedArray", - "ctor": "Float64Array", - "array": [ - 0, - 125, - 0, - 0, - 0, - 0, - 1, - 1, - 1, - 1 - ] - } - }, - { - "__type__": "cc.Label", - "_name": "", - "_objFlags": 0, - "node": { - "__id__": 5 - }, - "_enabled": true, - "_materials": [ - { - "__uuid__": "eca5d2f2-8ef6-41c2-bbe6-f9c79d09c432" - } - ], - "_useOriginalSize": false, - "_string": "", - "_N$string": "", - "_fontSize": 32, - "_lineHeight": 40, - "_enableWrapText": true, - "_N$file": null, - "_isSystemFontUsed": true, - "_spacingX": 0, - "_batchAsBitmap": false, - "_N$horizontalAlign": 1, - "_N$verticalAlign": 1, - "_N$fontFamily": "Arial", - "_N$overflow": 3, - "_N$cacheMode": 0, - "_id": "3ejZjl6HVA1rSY1hSxd8H8" - }, - { - "__type__": "cc.Node", - "_name": "Decorations", - "_objFlags": 0, - "_parent": { - "__id__": 2 - }, - "_children": [ - { - "__id__": 8 - }, - { - "__id__": 10 - } - ], - "_active": true, - "_level": 2, - "_components": [], - "_prefab": null, - "_opacity": 255, - "_color": { - "__type__": "cc.Color", - "r": 255, - "g": 255, - "b": 255, - "a": 255 - }, - "_contentSize": { - "__type__": "cc.Size", - "width": 0, - "height": 0 - }, - "_anchorPoint": { - "__type__": "cc.Vec2", - "x": 0.5, - "y": 0.5 - }, - "_eulerAngles": { - "__type__": "cc.Vec3", - "x": 0, - "y": 0, - "z": 0 - }, - "_skewX": 0, - "_skewY": 0, - "_is3DNode": false, - "groupIndex": 0, - "_id": "c3AZmU3YpOTYhMw/8haSH8", - "_trs": { - "__type__": "TypedArray", - "ctor": "Float64Array", - "array": [ - 0, - 0, - 0, - 0, - 0, - 0, - 1, - 1, - 1, - 1 - ] - } - }, - { - "__type__": "cc.Node", - "_name": "Logo", - "_objFlags": 0, - "_parent": { - "__id__": 7 - }, - "_children": [], - "_active": true, - "_level": 3, - "_components": [ - { - "__id__": 9 - } - ], - "_prefab": null, - "_opacity": 255, - "_color": { - "__type__": "cc.Color", - "r": 255, - "g": 255, - "b": 255, - "a": 255 - }, - "_contentSize": { - "__type__": "cc.Size", - "width": 424, - "height": 168 - }, - "_anchorPoint": { - "__type__": "cc.Vec2", - "x": 0.5, - "y": 0.5 - }, - "_eulerAngles": { - "__type__": "cc.Vec3", - "x": 0, - "y": 0, - "z": 0 - }, - "_skewX": 0, - "_skewY": 0, - "_is3DNode": false, - "groupIndex": 0, - "_id": "9bq54961lFtqOPk3DEyZTr", - "_trs": { - "__type__": "TypedArray", - "ctor": "Float64Array", - "array": [ - 0, - 260, - 0, - 0, - 0, - 0, - 1, - 1, - 1, - 1 - ] - } - }, - { - "__type__": "cc.Sprite", - "_name": "", - "_objFlags": 0, - "node": { - "__id__": 8 - }, - "_enabled": true, - "_materials": [ - { - "__uuid__": "eca5d2f2-8ef6-41c2-bbe6-f9c79d09c432" - } - ], - "_srcBlendFactor": 770, - "_dstBlendFactor": 771, - "_spriteFrame": { - "__uuid__": "9e564509-0a36-4f01-a833-79ff8f2e8328" - }, - "_type": 0, - "_sizeMode": 1, - "_fillType": 0, - "_fillCenter": { - "__type__": "cc.Vec2", - "x": 0, - "y": 0 - }, - "_fillStart": 0, - "_fillRange": 0, - "_isTrimmedMode": true, - "_atlas": { - "__uuid__": "030d9286-e8a2-40cf-98f8-baf713f0b8c4" - }, - "_id": "25w02YAVpPTKm8JUfP1Iyn" - }, - { - "__type__": "cc.Node", - "_name": "Bolldozer", - "_objFlags": 0, - "_parent": { - "__id__": 7 - }, - "_children": [], - "_active": true, - "_level": 3, - "_components": [ - { - "__id__": 11 - } - ], - "_prefab": null, - "_opacity": 255, - "_color": { - "__type__": "cc.Color", - "r": 255, - "g": 255, - "b": 255, - "a": 255 - }, - "_contentSize": { - "__type__": "cc.Size", - "width": 248, - "height": 513 - }, - "_anchorPoint": { - "__type__": "cc.Vec2", - "x": 0.5, - "y": 0.5 - }, - "_eulerAngles": { - "__type__": "cc.Vec3", - "x": 0, - "y": 0, - "z": 0 - }, - "_skewX": 0, - "_skewY": 0, - "_is3DNode": false, - "groupIndex": 0, - "_id": "94YmH2GARPAoCvyPWeCbBo", - "_trs": { - "__type__": "TypedArray", - "ctor": "Float64Array", - "array": [ - 0, - -180, - 0, - 0, - 0, - 0, - 1, - 1, - 1, - 1 - ] - } - }, - { - "__type__": "cc.Sprite", - "_name": "", - "_objFlags": 0, - "node": { - "__id__": 10 - }, - "_enabled": true, - "_materials": [ - { - "__uuid__": "eca5d2f2-8ef6-41c2-bbe6-f9c79d09c432" - } - ], - "_srcBlendFactor": 770, - "_dstBlendFactor": 771, - "_spriteFrame": { - "__uuid__": "2158ddd8-02de-47c2-ade8-f701288c22e5" - }, - "_type": 0, - "_sizeMode": 1, - "_fillType": 0, - "_fillCenter": { - "__type__": "cc.Vec2", - "x": 0, - "y": 0 - }, - "_fillStart": 0, - "_fillRange": 0, - "_isTrimmedMode": true, - "_atlas": { - "__uuid__": "030d9286-e8a2-40cf-98f8-baf713f0b8c4" - }, - "_id": "b4I8o4N61CJYbad3GUjAvQ" - }, - { - "__type__": "cc.Node", - "_name": "DownloadProgress", - "_objFlags": 0, - "_parent": { - "__id__": 2 - }, - "_children": [ - { - "__id__": 13 - }, - { - "__id__": 15 - }, - { - "__id__": 17 - }, - { - "__id__": 19 - } - ], - "_active": true, - "_level": 2, - "_components": [ - { - "__id__": 21 - }, - { - "__id__": 22 - } - ], - "_prefab": null, - "_opacity": 255, - "_color": { - "__type__": "cc.Color", - "r": 255, - "g": 255, - "b": 255, - "a": 255 - }, - "_contentSize": { - "__type__": "cc.Size", - "width": 300, - "height": 32 - }, - "_anchorPoint": { - "__type__": "cc.Vec2", - "x": 0.5, - "y": 0.5 - }, - "_eulerAngles": { - "__type__": "cc.Vec3", - "x": 0, - "y": 0, - "z": 0 - }, - "_skewX": 0, - "_skewY": 0, - "_is3DNode": false, - "groupIndex": 0, - "_id": "abQHOjM2dO5rBkz6fGiJKP", - "_trs": { - "__type__": "TypedArray", - "ctor": "Float64Array", - "array": [ - 0, - -435, - 0, - 0, - 0, - 0, - 1, - 1, - 1, - 1 - ] - } - }, - { - "__type__": "cc.Node", - "_name": "bar", - "_objFlags": 0, - "_parent": { - "__id__": 12 - }, - "_children": [], - "_active": true, - "_level": 0, - "_components": [ - { - "__id__": 14 - } - ], - "_prefab": null, - "_opacity": 255, - "_color": { - "__type__": "cc.Color", - "r": 19, - "g": 148, - "b": 35, - "a": 255 - }, - "_contentSize": { - "__type__": "cc.Size", - "width": 0, - "height": 32 - }, - "_anchorPoint": { - "__type__": "cc.Vec2", - "x": 0, - "y": 0.5 - }, - "_eulerAngles": { - "__type__": "cc.Vec3", - "x": 0, - "y": 0, - "z": 0 - }, - "_skewX": 0, - "_skewY": 0, - "_is3DNode": false, - "groupIndex": 0, - "_id": "35HX0AAMpMCYACcb2fdXjq", - "_trs": { - "__type__": "TypedArray", - "ctor": "Float64Array", - "array": [ - -150, - 0, - 0, - 0, - 0, - 0, - 1, - 1, - 1, - 1 - ] - } - }, - { - "__type__": "cc.Sprite", - "_name": "", - "_objFlags": 0, - "node": { - "__id__": 13 - }, - "_enabled": true, - "_materials": [ - { - "__uuid__": "eca5d2f2-8ef6-41c2-bbe6-f9c79d09c432" - } - ], - "_srcBlendFactor": 770, - "_dstBlendFactor": 771, - "_spriteFrame": { - "__uuid__": "67e68bc9-dad5-4ad9-a2d8-7e03d458e32f" - }, - "_type": 1, - "_sizeMode": 0, - "_fillType": 0, - "_fillCenter": { - "__type__": "cc.Vec2", - "x": 0, - "y": 0 - }, - "_fillStart": 0, - "_fillRange": 0, - "_isTrimmedMode": true, - "_atlas": null, - "_id": "65DRQh2TdB24L0axkKhYyy" - }, - { - "__type__": "cc.Node", - "_name": "Written", - "_objFlags": 0, - "_parent": { - "__id__": 12 - }, - "_children": [], - "_active": true, - "_level": 3, - "_components": [ - { - "__id__": 16 - } - ], - "_prefab": null, - "_opacity": 255, - "_color": { - "__type__": "cc.Color", - "r": 255, - "g": 255, - "b": 255, - "a": 255 - }, - "_contentSize": { - "__type__": "cc.Size", - "width": 10.66, - "height": 50.4 - }, - "_anchorPoint": { - "__type__": "cc.Vec2", - "x": 1, - "y": 0.5 - }, - "_eulerAngles": { - "__type__": "cc.Vec3", - "x": 0, - "y": 0, - "z": 0 - }, - "_skewX": 0, - "_skewY": 0, - "_is3DNode": false, - "groupIndex": 0, - "_id": "38DI8Gj5lAEapdfbgcQHRE", - "_trs": { - "__type__": "TypedArray", - "ctor": "Float64Array", - "array": [ - -15, - 0, - 0, - 0, - 0, - 0, - 1, - 1, - 1, - 1 - ] - } - }, - { - "__type__": "cc.Label", - "_name": "", - "_objFlags": 0, - "node": { - "__id__": 15 - }, - "_enabled": true, - "_materials": [ - { - "__uuid__": "eca5d2f2-8ef6-41c2-bbe6-f9c79d09c432" - } - ], - "_useOriginalSize": false, - "_string": "-", - "_N$string": "-", - "_fontSize": 32, - "_lineHeight": 40, - "_enableWrapText": true, - "_N$file": null, - "_isSystemFontUsed": true, - "_spacingX": 0, - "_batchAsBitmap": false, - "_N$horizontalAlign": 1, - "_N$verticalAlign": 1, - "_N$fontFamily": "Arial", - "_N$overflow": 0, - "_N$cacheMode": 0, - "_id": "95L3mPi11Ovojqz7HD7nmk" - }, - { - "__type__": "cc.Node", - "_name": "Separater", - "_objFlags": 0, - "_parent": { - "__id__": 12 - }, - "_children": [], - "_active": true, - "_level": 3, - "_components": [ - { - "__id__": 18 - } - ], - "_prefab": null, - "_opacity": 255, - "_color": { - "__type__": "cc.Color", - "r": 255, - "g": 255, - "b": 255, - "a": 255 - }, - "_contentSize": { - "__type__": "cc.Size", - "width": 8.89, - "height": 50.4 - }, - "_anchorPoint": { - "__type__": "cc.Vec2", - "x": 0.5, - "y": 0.5 - }, - "_eulerAngles": { - "__type__": "cc.Vec3", - "x": 0, - "y": 0, - "z": 0 - }, - "_skewX": 0, - "_skewY": 0, - "_is3DNode": false, - "groupIndex": 0, - "_id": "31gp1c7+hPTacRYMQzCtoI", - "_trs": { - "__type__": "TypedArray", - "ctor": "Float64Array", - "array": [ - 0, - 0, - 0, - 0, - 0, - 0, - 1, - 1, - 1, - 1 - ] - } - }, - { - "__type__": "cc.Label", - "_name": "", - "_objFlags": 0, - "node": { - "__id__": 17 - }, - "_enabled": true, - "_materials": [ - { - "__uuid__": "eca5d2f2-8ef6-41c2-bbe6-f9c79d09c432" - } - ], - "_useOriginalSize": false, - "_string": "/", - "_N$string": "/", - "_fontSize": 32, - "_lineHeight": 40, - "_enableWrapText": true, - "_N$file": null, - "_isSystemFontUsed": true, - "_spacingX": 0, - "_batchAsBitmap": false, - "_N$horizontalAlign": 1, - "_N$verticalAlign": 1, - "_N$fontFamily": "Arial", - "_N$overflow": 0, - "_N$cacheMode": 0, - "_id": "759pNtiHdIB5widXzhhIdV" - }, - { - "__type__": "cc.Node", - "_name": "ExpectedToWrite", - "_objFlags": 0, - "_parent": { - "__id__": 12 - }, - "_children": [], - "_active": true, - "_level": 3, - "_components": [ - { - "__id__": 20 - } - ], - "_prefab": null, - "_opacity": 255, - "_color": { - "__type__": "cc.Color", - "r": 255, - "g": 255, - "b": 255, - "a": 255 - }, - "_contentSize": { - "__type__": "cc.Size", - "width": 10.66, - "height": 50.4 - }, - "_anchorPoint": { - "__type__": "cc.Vec2", - "x": 0, - "y": 0.5 - }, - "_eulerAngles": { - "__type__": "cc.Vec3", - "x": 0, - "y": 0, - "z": 0 - }, - "_skewX": 0, - "_skewY": 0, - "_is3DNode": false, - "groupIndex": 0, - "_id": "20R1sB1uNPf5gsMbmFaHxZ", - "_trs": { - "__type__": "TypedArray", - "ctor": "Float64Array", - "array": [ - 10, - 0, - 0, - 0, - 0, - 0, - 1, - 1, - 1, - 1 - ] - } - }, - { - "__type__": "cc.Label", - "_name": "", - "_objFlags": 0, - "node": { - "__id__": 19 - }, - "_enabled": true, - "_materials": [ - { - "__uuid__": "eca5d2f2-8ef6-41c2-bbe6-f9c79d09c432" - } - ], - "_useOriginalSize": false, - "_string": "-", - "_N$string": "-", - "_fontSize": 32, - "_lineHeight": 40, - "_enableWrapText": true, - "_N$file": null, - "_isSystemFontUsed": true, - "_spacingX": 0, - "_batchAsBitmap": false, - "_N$horizontalAlign": 1, - "_N$verticalAlign": 1, - "_N$fontFamily": "Arial", - "_N$overflow": 0, - "_N$cacheMode": 0, - "_id": "dbFyA15cdL+btd6rNGJcWg" - }, - { - "__type__": "cc.Sprite", - "_name": "", - "_objFlags": 0, - "node": { - "__id__": 12 - }, - "_enabled": true, - "_materials": [ - { - "__uuid__": "eca5d2f2-8ef6-41c2-bbe6-f9c79d09c432" - } - ], - "_srcBlendFactor": 770, - "_dstBlendFactor": 771, - "_spriteFrame": { - "__uuid__": "88e79fd5-96b4-4a77-a1f4-312467171014" - }, - "_type": 1, - "_sizeMode": 0, - "_fillType": 0, - "_fillCenter": { - "__type__": "cc.Vec2", - "x": 0, - "y": 0 - }, - "_fillStart": 0, - "_fillRange": 0, - "_isTrimmedMode": true, - "_atlas": null, - "_id": "593TaHj0FJc7dURtmLMIk/" - }, - { - "__type__": "cc.ProgressBar", - "_name": "", - "_objFlags": 0, - "node": { - "__id__": 12 - }, - "_enabled": true, - "_N$totalLength": 300, - "_N$barSprite": { - "__id__": 14 - }, - "_N$mode": 0, - "_N$progress": 0, - "_N$reverse": false, - "_id": "09vvE4cMRJZ7N0o2D6SeTZ" - }, - { - "__type__": "cc.Node", - "_name": "HandlerProgress", - "_objFlags": 0, - "_parent": { - "__id__": 2 - }, - "_children": [ - { - "__id__": 24 - }, - { - "__id__": 26 - }, - { - "__id__": 28 - }, - { - "__id__": 30 - } - ], - "_active": true, - "_level": 2, - "_components": [ - { - "__id__": 32 - }, - { - "__id__": 33 - } - ], - "_prefab": null, - "_opacity": 255, - "_color": { - "__type__": "cc.Color", - "r": 255, - "g": 255, - "b": 255, - "a": 255 - }, - "_contentSize": { - "__type__": "cc.Size", - "width": 300, - "height": 30 - }, - "_anchorPoint": { - "__type__": "cc.Vec2", - "x": 0.5, - "y": 0.5 - }, - "_eulerAngles": { - "__type__": "cc.Vec3", - "x": 0, - "y": 0, - "z": 0 - }, - "_skewX": 0, - "_skewY": 0, - "_is3DNode": false, - "groupIndex": 0, - "_id": "c0nxsTzCtKa4OX78IBsR0Q", - "_trs": { - "__type__": "TypedArray", - "ctor": "Float64Array", - "array": [ - 0, - -367, - 0, - 0, - 0, - 0, - 1, - 1, - 1, - 1 - ] - } - }, - { - "__type__": "cc.Node", - "_name": "bar", - "_objFlags": 0, - "_parent": { - "__id__": 23 - }, - "_children": [], - "_active": true, - "_level": 0, - "_components": [ - { - "__id__": 25 - } - ], - "_prefab": null, - "_opacity": 255, - "_color": { - "__type__": "cc.Color", - "r": 202, - "g": 115, - "b": 21, - "a": 255 - }, - "_contentSize": { - "__type__": "cc.Size", - "width": 0, - "height": 32 - }, - "_anchorPoint": { - "__type__": "cc.Vec2", - "x": 0, - "y": 0.5 - }, - "_eulerAngles": { - "__type__": "cc.Vec3", - "x": 0, - "y": 0, - "z": 0 - }, - "_skewX": 0, - "_skewY": 0, - "_is3DNode": false, - "groupIndex": 0, - "_id": "24ZKpM0FdNQZYiIRF0KyjW", - "_trs": { - "__type__": "TypedArray", - "ctor": "Float64Array", - "array": [ - -150, - 0, - 0, - 0, - 0, - 0, - 1, - 1, - 1, - 1 - ] - } - }, - { - "__type__": "cc.Sprite", - "_name": "", - "_objFlags": 0, - "node": { - "__id__": 24 - }, - "_enabled": true, - "_materials": [ - { - "__uuid__": "eca5d2f2-8ef6-41c2-bbe6-f9c79d09c432" - } - ], - "_srcBlendFactor": 770, - "_dstBlendFactor": 771, - "_spriteFrame": { - "__uuid__": "67e68bc9-dad5-4ad9-a2d8-7e03d458e32f" - }, - "_type": 1, - "_sizeMode": 0, - "_fillType": 0, - "_fillCenter": { - "__type__": "cc.Vec2", - "x": 0, - "y": 0 - }, - "_fillStart": 0, - "_fillRange": 0, - "_isTrimmedMode": true, - "_atlas": null, - "_id": "15MJsSQ2RBcYNOlHOGBVtf" - }, - { - "__type__": "cc.Node", - "_name": "Handled", - "_objFlags": 0, - "_parent": { - "__id__": 23 - }, - "_children": [], - "_active": true, - "_level": 3, - "_components": [ - { - "__id__": 27 - } - ], - "_prefab": null, - "_opacity": 255, - "_color": { - "__type__": "cc.Color", - "r": 255, - "g": 255, - "b": 255, - "a": 255 - }, - "_contentSize": { - "__type__": "cc.Size", - "width": 10.66, - "height": 50.4 - }, - "_anchorPoint": { - "__type__": "cc.Vec2", - "x": 1, - "y": 0.5 - }, - "_eulerAngles": { - "__type__": "cc.Vec3", - "x": 0, - "y": 0, - "z": 0 - }, - "_skewX": 0, - "_skewY": 0, - "_is3DNode": false, - "groupIndex": 0, - "_id": "8fupG1MHNARIAlsizaxHZ4", - "_trs": { - "__type__": "TypedArray", - "ctor": "Float64Array", - "array": [ - -15, - 0, - 0, - 0, - 0, - 0, - 1, - 1, - 1, - 1 - ] - } - }, - { - "__type__": "cc.Label", - "_name": "", - "_objFlags": 0, - "node": { - "__id__": 26 - }, - "_enabled": true, - "_materials": [ - { - "__uuid__": "eca5d2f2-8ef6-41c2-bbe6-f9c79d09c432" - } - ], - "_useOriginalSize": false, - "_string": "-", - "_N$string": "-", - "_fontSize": 32, - "_lineHeight": 40, - "_enableWrapText": true, - "_N$file": null, - "_isSystemFontUsed": true, - "_spacingX": 0, - "_batchAsBitmap": false, - "_N$horizontalAlign": 1, - "_N$verticalAlign": 1, - "_N$fontFamily": "Arial", - "_N$overflow": 0, - "_N$cacheMode": 0, - "_id": "ddll07J9hAoZQDGbNbZK/r" - }, - { - "__type__": "cc.Node", - "_name": "Separater", - "_objFlags": 0, - "_parent": { - "__id__": 23 - }, - "_children": [], - "_active": true, - "_level": 3, - "_components": [ - { - "__id__": 29 - } - ], - "_prefab": null, - "_opacity": 255, - "_color": { - "__type__": "cc.Color", - "r": 255, - "g": 255, - "b": 255, - "a": 255 - }, - "_contentSize": { - "__type__": "cc.Size", - "width": 8.89, - "height": 50.4 - }, - "_anchorPoint": { - "__type__": "cc.Vec2", - "x": 0.5, - "y": 0.5 - }, - "_eulerAngles": { - "__type__": "cc.Vec3", - "x": 0, - "y": 0, - "z": 0 - }, - "_skewX": 0, - "_skewY": 0, - "_is3DNode": false, - "groupIndex": 0, - "_id": "3c/av+sOZLMbxWRhODcKmG", - "_trs": { - "__type__": "TypedArray", - "ctor": "Float64Array", - "array": [ - 0, - 0, - 0, - 0, - 0, - 0, - 1, - 1, - 1, - 1 - ] - } - }, - { - "__type__": "cc.Label", - "_name": "", - "_objFlags": 0, - "node": { - "__id__": 28 - }, - "_enabled": true, - "_materials": [ - { - "__uuid__": "eca5d2f2-8ef6-41c2-bbe6-f9c79d09c432" - } - ], - "_useOriginalSize": false, - "_string": "/", - "_N$string": "/", - "_fontSize": 32, - "_lineHeight": 40, - "_enableWrapText": true, - "_N$file": null, - "_isSystemFontUsed": true, - "_spacingX": 0, - "_batchAsBitmap": false, - "_N$horizontalAlign": 1, - "_N$verticalAlign": 1, - "_N$fontFamily": "Arial", - "_N$overflow": 0, - "_N$cacheMode": 0, - "_id": "c8z9PIEA1ObK11KdTNPVIP" - }, - { - "__type__": "cc.Node", - "_name": "ToHandle", - "_objFlags": 0, - "_parent": { - "__id__": 23 - }, - "_children": [], - "_active": true, - "_level": 3, - "_components": [ - { - "__id__": 31 - } - ], - "_prefab": null, - "_opacity": 255, - "_color": { - "__type__": "cc.Color", - "r": 255, - "g": 255, - "b": 255, - "a": 255 - }, - "_contentSize": { - "__type__": "cc.Size", - "width": 10.66, - "height": 50.4 - }, - "_anchorPoint": { - "__type__": "cc.Vec2", - "x": 0, - "y": 0.5 - }, - "_eulerAngles": { - "__type__": "cc.Vec3", - "x": 0, - "y": 0, - "z": 0 - }, - "_skewX": 0, - "_skewY": 0, - "_is3DNode": false, - "groupIndex": 0, - "_id": "18hLtXEjBMMbL7WX1voVDu", - "_trs": { - "__type__": "TypedArray", - "ctor": "Float64Array", - "array": [ - 10, - 0, - 0, - 0, - 0, - 0, - 1, - 1, - 1, - 1 - ] - } - }, - { - "__type__": "cc.Label", - "_name": "", - "_objFlags": 0, - "node": { - "__id__": 30 - }, - "_enabled": true, - "_materials": [ - { - "__uuid__": "eca5d2f2-8ef6-41c2-bbe6-f9c79d09c432" - } - ], - "_useOriginalSize": false, - "_string": "-", - "_N$string": "-", - "_fontSize": 32, - "_lineHeight": 40, - "_enableWrapText": true, - "_N$file": null, - "_isSystemFontUsed": true, - "_spacingX": 0, - "_batchAsBitmap": false, - "_N$horizontalAlign": 1, - "_N$verticalAlign": 1, - "_N$fontFamily": "Arial", - "_N$overflow": 0, - "_N$cacheMode": 0, - "_id": "5b+RrNshNILbBAjAAyqvfN" - }, - { - "__type__": "cc.Sprite", - "_name": "", - "_objFlags": 0, - "node": { - "__id__": 23 - }, - "_enabled": true, - "_materials": [ - { - "__uuid__": "eca5d2f2-8ef6-41c2-bbe6-f9c79d09c432" - } - ], - "_srcBlendFactor": 770, - "_dstBlendFactor": 771, - "_spriteFrame": { - "__uuid__": "88e79fd5-96b4-4a77-a1f4-312467171014" - }, - "_type": 1, - "_sizeMode": 0, - "_fillType": 0, - "_fillCenter": { - "__type__": "cc.Vec2", - "x": 0, - "y": 0 - }, - "_fillStart": 0, - "_fillRange": 0, - "_isTrimmedMode": true, - "_atlas": null, - "_id": "b1H93UoY9CpalSJVXUvObw" - }, - { - "__type__": "cc.ProgressBar", - "_name": "", - "_objFlags": 0, - "node": { - "__id__": 23 - }, - "_enabled": true, - "_N$totalLength": 300, - "_N$barSprite": { - "__id__": 25 - }, - "_N$mode": 0, - "_N$progress": 0, - "_N$reverse": false, - "_id": "d44CusjQ1NeL3CU/6/pYju" - }, - { - "__type__": "cc.Canvas", - "_name": "", - "_objFlags": 0, - "node": { - "__id__": 2 - }, - "_enabled": true, - "_designResolution": { - "__type__": "cc.Size", - "width": 640, - "height": 960 - }, - "_fitWidth": true, - "_fitHeight": false, - "_id": "45irrwnU1MAaF3kwQFnJ++" - }, - { - "__type__": "cc.Widget", - "_name": "", - "_objFlags": 0, - "node": { - "__id__": 2 - }, - "_enabled": false, - "alignMode": 0, - "_target": null, - "_alignFlags": 45, - "_left": 0, - "_right": 0, - "_top": 0, - "_bottom": 0, - "_verticalCenter": 0, - "_horizontalCenter": 0, - "_isAbsLeft": true, - "_isAbsRight": true, - "_isAbsTop": true, - "_isAbsBottom": true, - "_isAbsHorizontalCenter": true, - "_isAbsVerticalCenter": true, - "_originalWidth": 640, - "_originalHeight": 960, - "_id": "femrjqTNREkZw7OPQcF2dD" - }, - { - "__type__": "cc.Sprite", - "_name": "", - "_objFlags": 0, - "node": { - "__id__": 2 - }, - "_enabled": true, - "_materials": [ - { - "__uuid__": "eca5d2f2-8ef6-41c2-bbe6-f9c79d09c432" - } - ], - "_srcBlendFactor": 770, - "_dstBlendFactor": 771, - "_spriteFrame": { - "__uuid__": "7838f276-ab48-445a-b858-937dd27d9520" - }, - "_type": 0, - "_sizeMode": 0, - "_fillType": 0, - "_fillCenter": { - "__type__": "cc.Vec2", - "x": 0, - "y": 0 - }, - "_fillStart": 0, - "_fillRange": 0, - "_isTrimmedMode": false, - "_atlas": null, - "_id": "e2yoOcT3ND2YHlCSRJKZ5q" - }, - { - "__type__": "8264fty40hF5JqzW/+5pWHu", - "_name": "", - "_objFlags": 0, - "node": { - "__id__": 2 - }, - "_enabled": true, - "cavasNode": { - "__id__": 2 - }, - "backgroundNode": { - "__id__": 2 - }, - "loadingPrefab": { - "__uuid__": "f2a3cece-30bf-4f62-bc20-34d44a9ddf98" - }, - "tipsLabel": { - "__id__": 6 - }, - "downloadProgress": { - "__id__": 22 - }, - "writtenBytes": { - "__id__": 16 - }, - "expectedToWriteBytes": { - "__id__": 20 - }, - "handlerProgress": { - "__id__": 33 - }, - "handledUrlsCount": { - "__id__": 27 - }, - "toHandledUrlsCount": { - "__id__": 31 - }, - "_id": "c0EVg/B8RMWLA48jmAqxQr" - } -] \ No newline at end of file diff --git a/frontend/assets/scenes/wechatGameLogin.fire.meta b/frontend/assets/scenes/wechatGameLogin.fire.meta deleted file mode 100644 index 2f2638d..0000000 --- a/frontend/assets/scenes/wechatGameLogin.fire.meta +++ /dev/null @@ -1,7 +0,0 @@ -{ - "ver": "1.2.5", - "uuid": "475b849b-44b3-4390-982d-bd0d9e695093", - "asyncLoadAssets": false, - "autoReleaseAssets": false, - "subMetas": {} -} \ No newline at end of file diff --git a/frontend/assets/scripts/BasePlayer.js b/frontend/assets/scripts/BasePlayer.js index e4ce7f0..72d2b53 100644 --- a/frontend/assets/scripts/BasePlayer.js +++ b/frontend/assets/scripts/BasePlayer.js @@ -8,11 +8,11 @@ module.export = cc.Class({ }, baseSpeed: { type: cc.Float, - default: 300, + default: 50, }, speed: { type: cc.Float, - default: 300 + default: 50 }, lastMovedAt: { type: cc.Float, @@ -35,12 +35,6 @@ module.export = cc.Class({ // LIFE-CYCLE CALLBACKS: start() { const self = this; - self.contactedControlledPlayers = []; - self.contactedNPCPlayers = []; - self.coveringShelterZReducers = []; - - self.computedNewDifferentPosLocalToParentWithinCurrentFrame = null; - self.actionMangerSingleton = new cc.ActionManager(); self.activeDirection = { dx: 0, dy: 0 @@ -61,7 +55,6 @@ module.export = cc.Class({ }; const canvasNode = self.mapNode.parent; self.mapIns = self.mapNode.getComponent("Map"); - self.contactedBarriers = []; const joystickInputControllerScriptIns = canvasNode.getComponent("TouchEventsManager"); self.ctrl = joystickInputControllerScriptIns; 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) { - 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) { - 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() { diff --git a/frontend/assets/scripts/CameraTracker.js b/frontend/assets/scripts/CameraTracker.js index 37ab4c9..d822237 100644 --- a/frontend/assets/scripts/CameraTracker.js +++ b/frontend/assets/scripts/CameraTracker.js @@ -22,7 +22,7 @@ cc.Class({ if (!self.mapScriptIns) return; if (!self.mapScriptIns.selfPlayerInfo) 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; const selfPlayerNode = selfPlayerRichInfo.node; if (!selfPlayerNode) return; diff --git a/frontend/assets/scripts/FindingPlayer.js b/frontend/assets/scripts/FindingPlayer.js index e6f145b..628f25a 100644 --- a/frontend/assets/scripts/FindingPlayer.js +++ b/frontend/assets/scripts/FindingPlayer.js @@ -26,19 +26,6 @@ cc.Class({ // LIFE-CYCLE CALLBACKS: 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() { @@ -73,11 +60,7 @@ cc.Class({ exitBtnOnClick(evt) { window.clearBoundRoomIdInBothVolatileAndPersistentStorage(); 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) { @@ -107,23 +90,7 @@ cc.Class({ if (remoteUrl == null || remoteUrl == '') { cc.log(`No avatar to show for :`); 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) { diff --git a/frontend/assets/scripts/Login.js b/frontend/assets/scripts/Login.js index cd46436..a6da459 100644 --- a/frontend/assets/scripts/Login.js +++ b/frontend/assets/scripts/Login.js @@ -1,5 +1,6 @@ const i18n = require('LanguageData'); i18n.init(window.language); // languageID should be equal to the one we input in New Language ID input field + cc.Class({ extends: cc.Component, @@ -59,11 +60,7 @@ cc.Class({ loadingPrefab: { default: null, type: cc.Prefab - }, - wechatLoginTips: { - default: null, - type: cc.Label, - }, + } }, // LIFE-CYCLE CALLBACKS: @@ -88,22 +85,18 @@ cc.Class({ self.getRetCodeList(); self.getRegexList(); - const isUsingX5BlinkKernelOrWebkitWeChatKernel = window.isUsingX5BlinkKernelOrWebkitWeChatKernel(); - //const isUsingX5BlinkKernelOrWebkitWeChatKernel = true; - if (!CC_DEBUG) { - self.phoneNumberTips.active = !isUsingX5BlinkKernelOrWebkitWeChatKernel; - self.smsLoginCaptchaButton.active = !isUsingX5BlinkKernelOrWebkitWeChatKernel; + self.phoneNumberTips.active = true; + self.smsLoginCaptchaButton.active = true; - self.captchaTips.active = !isUsingX5BlinkKernelOrWebkitWeChatKernel; - self.phoneCountryCodeInput.active = !isUsingX5BlinkKernelOrWebkitWeChatKernel; - self.phoneNumberInput.active = !isUsingX5BlinkKernelOrWebkitWeChatKernel; - self.smsLoginCaptchaInput.active = !isUsingX5BlinkKernelOrWebkitWeChatKernel; + self.captchaTips.active = true; + self.phoneCountryCodeInput.active = true; + self.phoneNumberInput.active = true; + self.smsLoginCaptchaInput.active = true; - self.phoneLabel.active = !isUsingX5BlinkKernelOrWebkitWeChatKernel; - self.smsLoginCaptchaLabel.active = !isUsingX5BlinkKernelOrWebkitWeChatKernel; + self.phoneLabel.active = true; + self.smsLoginCaptchaLabel.active = true; - self.loginButton.active = !isUsingX5BlinkKernelOrWebkitWeChatKernel; - } + self.loginButton.active = true; self.checkPhoneNumber = self.checkPhoneNumber.bind(self); self.checkIntAuthTokenExpire = self.checkIntAuthTokenExpire.bind(self); self.checkCaptcha = self.checkCaptcha.bind(self); @@ -124,15 +117,13 @@ cc.Class({ cc.error(err.message || err); return; } - if (false == (cc.sys.platform == cc.sys.WECHAT_GAME)) { - // Otherwise, `window.RoomDownsyncFrame` is already assigned. - let protoRoot = new protobuf.Root; - window.protobuf.parse(textAsset.text, protoRoot); - window.RoomDownsyncFrame = protoRoot.lookupType("treasurehunterx.RoomDownsyncFrame"); - window.BattleColliderInfo = protoRoot.lookupType("treasurehunterx.BattleColliderInfo"); - window.WsReq = protoRoot.lookupType("treasurehunterx.WsReq"); - window.WsResp = protoRoot.lookupType("treasurehunterx.WsResp"); - } + // Otherwise, `window.RoomDownsyncFrame` is already assigned. + let protoRoot = new protobuf.Root; + window.protobuf.parse(textAsset.text, protoRoot); + window.RoomDownsyncFrame = protoRoot.lookupType("treasurehunterx.RoomDownsyncFrame"); + window.BattleColliderInfo = protoRoot.lookupType("treasurehunterx.BattleColliderInfo"); + window.WsReq = protoRoot.lookupType("treasurehunterx.WsReq"); + window.WsResp = protoRoot.lookupType("treasurehunterx.WsResp"); self.checkIntAuthTokenExpire().then( () => { const intAuthToken = JSON.parse(cc.sys.localStorage.getItem('selfPlayer')).intAuthToken; @@ -140,19 +131,6 @@ cc.Class({ }, () => { 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) { const self = this; cc.log(`OnLoggedIn ${JSON.stringify(res)}.`) if (res.ret === self.retCodeDict.OK) { - if(window.isUsingX5BlinkKernelOrWebkitWeChatKernel()) { - window.initWxSdk = self.initWxSdk.bind(self); - window.initWxSdk(); - } self.enableInteractiveControls(false); const date = Number(res.expiresAt); 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); - }, - }); - }, }); diff --git a/frontend/assets/scripts/Map.js b/frontend/assets/scripts/Map.js index 5fb8b60..b92a56f 100644 --- a/frontend/assets/scripts/Map.js +++ b/frontend/assets/scripts/Map.js @@ -1,6 +1,9 @@ const i18n = require('LanguageData'); 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 = { VISUAL: 0, // For free dragging & zooming. EDITING_BELONGING: 1, @@ -15,9 +18,11 @@ window.ALL_BATTLE_STATES = { }; window.MAGIC_ROOM_DOWNSYNC_FRAME_ID = { - BATTLE_READY_TO_START: -99, PLAYER_ADDED_AND_ACKED: -98, PLAYER_READDED_AND_ACKED: -97, + + BATTLE_READY_TO_START: -1, + BATTLE_START: 0, }; cc.Class({ @@ -56,14 +61,6 @@ cc.Class({ type: cc.Prefab, default: null, }, - polygonBoundaryShelterPrefab: { - type: cc.Prefab, - default: null, - }, - polygonBoundaryShelterZReducerPrefab: { - type: cc.Prefab, - default: null, - }, keyboardInputControllerNode: { type: cc.Node, default: null @@ -127,8 +124,9 @@ cc.Class({ type: cc.Float, default: 1.0/60 }, - rollbackInMainUpdate: { - default: true + maxChasingRenderFramesPerUpdate: { + type: cc.Integer, + default: 10 }, }, @@ -136,27 +134,46 @@ cc.Class({ return (0 == inputFrameId%10); }, - _dumpToFullFrameCache: function(fullFrame) { + _dumpToRenderCache: function(roomDownsyncFrame) { const self = this; - self.recentFrameCache.set(fullFrame.id, fullFrame); - if (fullFrame.id >= self.recentFrameCacheEd) { - // Should be consecutive - ++self.recentFrameCacheEd; + const minToKeepRenderFrameId = self.lastAllConfirmedRenderFrameId; + while (0 < self.recentRenderCache.cnt && self.recentRenderCache.stFrameId < minToKeepRenderFrameId) { + self.recentRenderCache.pop(); } - while (self.recentFrameCacheEd - self.recentFrameCacheSt >= self.recentFrameCacheMaxCount) { - self.recentFrameCache.delete(self.recentFrameCacheSt++); + if (self.recentRenderCache.stFrameId < minToKeepRenderFrameId) { + 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) { const self = this; - self.recentInputCache.set(inputFrameDownsync.inputFrameId, inputFrameDownsync); - if (inputFrameDownsync.inputFrameId >= self.recentInputCacheEd) { - // Should be consecutive - ++self.recentInputCacheEd; + 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 (minToKeepInputFrameId > self.lastAllConfirmedInputFrameId) minToKeepInputFrameId = self.lastAllConfirmedInputFrameId; + while (0 < self.recentInputCache.cnt && self.recentInputCache.stFrameId < minToKeepInputFrameId) { + self.recentInputCache.pop(); } - while (self.recentInputCacheEd - self.recentInputCacheSt >= self.recentInputCacheMaxCount) { - self.recentInputCache.delete(self.recentInputCacheSt++); + if (self.recentInputCache.stFrameId < minToKeepInputFrameId) { + 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) { - 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) { - const instance = this; + const self = this; if ( - null == instance.ctrl || - null == instance.selfPlayerInfo + null == self.ctrl || + null == self.selfPlayerInfo ) { return [null, null]; } - const discreteDir = instance.ctrl.getDiscretizedDirection(); - let prefabbedInputList = null; - let selfPlayerLastInputFrameInput = 0; - if (0 == instance.recentInputCacheEd) { - prefabbedInputList = new Array(instance.playerRichInfoDict.size).fill(0); - } 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 = { - inputFrameId: inputFrameId, - inputList: prefabbedInputList, - confirmedList: (1 << (instance.selfPlayerInfo.joinIndex-1)) + const joinIndex = self.selfPlayerInfo.joinIndex; + const discreteDir = self.ctrl.getDiscretizedDirection(); + const previousInputFrameDownsyncWithPrediction = self.getCachedInputFrameDownsyncWithPrediction(inputFrameId); + const prefabbedInputList = (null == previousInputFrameDownsyncWithPrediction ? new Array(self.playerRichInfoDict.size).fill(0) : previousInputFrameDownsyncWithPrediction.inputList.slice()); + prefabbedInputList[(joinIndex-1)] = discreteDir.encodedIdx; + const prefabbedInputFrameDownsync = { + inputFrameId: inputFrameId, + inputList: prefabbedInputList, + 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. */ @@ -219,33 +229,33 @@ cc.Class({ 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"! - const instance = this; + const self = this; let inputFrameUpsyncBatch = []; - for (let i = instance.lastUpsyncInputFrameId+1; i <= inputFrameId; ++i) { - const inputFrameDownsync = instance.recentInputCache.get(i); - if (null == inputFrameDownsync) { - console.warn("_sendInputFrameUpsyncBatch: recentInputCache is NOT having inputFrameId=", i, "; recentInputCache=", instance._stringifyRecentInputCache(false)); - } else { - const inputFrameUpsync = { - inputFrameId: i, - encodedDir: inputFrameDownsync.inputList[instance.selfPlayerInfo.joinIndex-1], - }; - inputFrameUpsyncBatch.push(inputFrameUpsync); - } + for (let i = self.lastUpsyncInputFrameId+1; i <= inputFrameId; ++i) { + const inputFrameDownsync = self.recentInputCache.getByFrameId(i); + if (null == inputFrameDownsync) { + console.warn("sendInputFrameUpsyncBatch: recentInputCache is NOT having inputFrameId=", i, "; recentInputCache=", self._stringifyRecentInputCache(false)); + } else { + const inputFrameUpsync = { + inputFrameId: i, + encodedDir: inputFrameDownsync.inputList[self.selfPlayerInfo.joinIndex-1], + }; + inputFrameUpsyncBatch.push(inputFrameUpsync); + } } const reqData = window.WsReq.encode({ msgId: Date.now(), - playerId: instance.selfPlayerInfo.id, + playerId: self.selfPlayerInfo.id, act: window.UPSYNC_MSG_ACT_PLAYER_CMD, - joinIndex: instance.selfPlayerInfo.joinIndex, - ackingFrameId: instance.lastRoomDownsyncFrameId, - ackingInputFrameId: instance.lastDownsyncInputFrameId, + joinIndex: self.selfPlayerInfo.joinIndex, + ackingFrameId: self.lastAllConfirmedRenderFrameId, + ackingInputFrameId: self.lastAllConfirmedInputFrameId, inputFrameUpsyncBatch: inputFrameUpsyncBatch, }).finish(); window.sendSafely(reqData); - instance.lastUpsyncInputFrameId = inputFrameId; + self.lastUpsyncInputFrameId = inputFrameId; }, onEnable() { @@ -274,9 +284,6 @@ cc.Class({ if (null != window.handleClientSessionCloseOrError) { window.handleClientSessionCloseOrError = null; } - if (self.inputControlTimer) { - clearInterval(self.inputControlTimer); - } }, popupSimplePressToGo(labelString, hideYesButton) { @@ -323,6 +330,7 @@ cc.Class({ self.countdownNanos = null; // Clearing previous info of all players. [BEGINS] + self.collisionPlayerIndexPrefix = (1 << 17); // For tracking the movements of players if (null != self.playerRichInfoDict) { self.playerRichInfoDict.forEach((playerRichInfo, playerId) => { if (playerRichInfo.node.parent) { @@ -333,27 +341,27 @@ cc.Class({ self.playerRichInfoDict = new Map(); // Clearing previous info of all players. [ENDS] - self.lastRoomDownsyncFrameId = 0; 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.inputScaleFrames = 2; - self.lastDownsyncInputFrameId = -1; - self.lastAllConfirmedInputFrameId = -1; self.lastUpsyncInputFrameId = -1; self.inputFrameUpsyncDelayTolerance = 2; - self.recentFrameCache = new Map(); - self.recentFrameCacheSt = 0; // closed index - self.recentFrameCacheEd = 0; // open index - self.recentFrameCacheMaxCount = 1024; + self.recentRenderCache = new RingBuffer(1024); self.selfPlayerInfo = null; // This field is kept for distinguishing "self" and "others". - self.recentInputCache = new Map(); // TODO: Use a ringbuf instead - self.recentInputCacheSt = 0; // closed index - self.recentInputCacheEd = 0; // open index - self.recentInputCacheMaxCount = 1024; - self.toRollbackRenderFrameId1 = null; - self.toRollbackInputFrameId1 = null; + self.recentInputCache = new RingBuffer(1024); + + self.latestCollisionSys = new collisions.Collisions(); + self.chaserCollisionSys = new collisions.Collisions(); + + 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); @@ -386,8 +394,7 @@ cc.Class({ const mapNode = self.node; const canvasNode = mapNode.parent; - cc.director.getCollisionManager().enabled = true; - cc.director.getCollisionManager().enabledDebugDraw = CC_DEBUG; + cc.director.getCollisionManager().enabled = false; // self.musicEffectManagerScriptIns = self.node.getComponent("MusicEffectManager"); self.musicEffectManagerScriptIns = null; @@ -443,8 +450,6 @@ cc.Class({ self.clientUpsyncFps = 60; window.handleBattleColliderInfo = function(parsedBattleColliderInfo) { - console.log(parsedBattleColliderInfo); - self.battleColliderInfo = parsedBattleColliderInfo; const tiledMapIns = self.node.getComponent(cc.TiledMap); @@ -459,11 +464,8 @@ cc.Class({ [WARNING] - 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()". - - -- YFLu, 2019-09-07 - */ tiledMapIns.tmxAsset = null; @@ -485,9 +487,22 @@ cc.Class({ singleImageLayer.node.opacity = 0; } + let barrierIdCounter = 0; 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')); Object.assign(self.selfPlayerInfo, { @@ -524,61 +539,40 @@ cc.Class({ self.transitToState(ALL_MAP_STATES.WAITING); self._inputControlEnabled = false; - let findingPlayerScriptIns = null; + let findingPlayerScriptIns = self.findingPlayerNode.getComponent("FindingPlayer"); window.handleRoomDownsyncFrame = function(rdf) { if (ALL_BATTLE_STATES.WAITING != self.battleState && ALL_BATTLE_STATES.IN_BATTLE != self.battleState && ALL_BATTLE_STATES.IN_SETTLEMENT != self.battleState) { 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. const refFrameId = rdf.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: - self._initPlayerRichInfoDict(rdf.players, rdf.playerMetas); - // 显示匹配玩家 - findingPlayerScriptIns = self.findingPlayerNode.getComponent("FindingPlayer"); + // Update the "finding player" GUI and show it if not previously present if (!self.findingPlayerNode.parent) { self.showPopupInCanvas(self.findingPlayerNode); } findingPlayerScriptIns.updatePlayersInfo(rdf.playerMetas); 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: - self._initPlayerRichInfoDict(rdf.players, rdf.playerMetas); - // In this case, we're definitely in an active battle, thus the "self.findingPlayerNode" should be hidden if being presented. - if (self.findingPlayerNode && self.findingPlayerNode.parent) { - self.findingPlayerNode.parent.removeChild(self.findingPlayerNode); - self.transitToState(ALL_MAP_STATES.VISUAL); - if (self.playersInfoNode) { - for (let playerId in rdf.playerMetas) { - const playerMeta = rdf.playerMetas[playerId]; - const playersInfoScriptIns = self.playersInfoNode.getComponent("PlayersInfo"); - playersInfoScriptIns.updateData(playerMeta); - } - } - } + // [WARNING] The "frameId" from server could be quite fast-forwarding, don't assign it in other cases. + self.renderFrameId = frameId; + self.lastAllConfirmedRenderFrameId = frameId; + self.onBattleReadyToStart(rdf.playerMetas, true); + self.onBattleStarted(rdf); 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/. }; @@ -588,13 +582,13 @@ cc.Class({ 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 firstPredictedYetIncorrectInputFrameJoinIndex = null; for (let k in batch) { const inputFrameDownsync = batch[k]; const inputFrameDownsyncId = inputFrameDownsync.inputFrameId; - const localInputFrame = self.recentInputCache.get(inputFrameDownsyncId); + const localInputFrame = self.recentInputCache.getByFrameId(inputFrameDownsyncId); if (null == localInputFrame) { console.warn("handleInputFrameDownsyncBatch: recentInputCache is NOT having inputFrameDownsyncId=", inputFrameDownsyncId, "; now recentInputCache=", self._stringifyRecentInputCache(false)); } else { @@ -608,34 +602,36 @@ cc.Class({ } } } + self.lastAllConfirmedInputFrameId = inputFrameDownsyncId; 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) { - const renderFrameId2 = self.renderFrameId; - const inputFrameId2 = self._convertToInputFrameId(renderFrameId2, self.inputDelayFrames); const inputFrameId1 = firstPredictedYetIncorrectInputFrameId; const renderFrameId1 = self._convertToRenderFrameId(inputFrameId1, self.inputDelayFrames); // a.k.a. "firstRenderFrameIdUsingIncorrectInputFrameId" - if (renderFrameId1 < renderFrameId2) { - // 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, "). "); - if (true == self.rollbackInMainUpdate) { - // The actual rollback-and-replay would later be executed in update(dt). - 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); - } + if (renderFrameId1 < self.renderFrameId) { + /* + A typical case is as follows. + -------------------------------------------------------- + [self.lastAllConfirmedRenderFrameId] : 22 + : 36 + + + : 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 { + // Deliberately left blank, chasing is ongoing. + } } else { - console.log("Mismatched input yet no rollback needed: [inputFrameId1:", inputFrameId1, ", inputFrameId2:", inputFrameId2, "), [renderFrameId1:", renderFrameId1, ", renderFrameId2:", renderFrameId2, "). "); + // 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) { // This function is also applicable to "re-joining". - const players = rdf.players; - const playerMetas = rdf.playerMetas; console.log('On battle started!'); 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) { self.countdownNanos = rdf.countdownNanos; } @@ -712,19 +717,24 @@ cc.Class({ self.countdownToBeginGameNode.parent.removeChild(self.countdownToBeginGameNode); } self.transitToState(ALL_MAP_STATES.VISUAL); - - self._applyRoomDownsyncFrameDynamics(rdf); + self.chaserRenderFrameId = rdf.id; + self.applyRoomDownsyncFrameDynamics(rdf); + self._dumpToRenderCache(rdf); 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() { const self = this; 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)); - }); + } console.log(s.join('\n')); }, @@ -749,12 +759,19 @@ cc.Class({ }, spawnPlayerNode(joinIndex, x, y) { - const instance = this; - const newPlayerNode = 1 == joinIndex ? cc.instantiate(instance.player1Prefab) : cc.instantiate(instance.player2Prefab); // hardcoded for now, car color determined solely by joinIndex + const self = this; + 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.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); newPlayerNode.active = true; @@ -766,72 +783,60 @@ cc.Class({ update(dt) { const self = this; - try { - if (ALL_BATTLE_STATES.IN_BATTLE == self.battleState) { - let prevSelfInput = null, currSelfInput = null; - const noDelayInputFrameId = self._convertToInputFrameId(self.renderFrameId, 0); // It's important that "inputDelayFrames == 0" here - if (self._shouldGenerateInputFrameUpsync(self.renderFrameId)) { - const prevAndCurrInputs = self._generateInputFrameUpsync(noDelayInputFrameId); - prevSelfInput = prevAndCurrInputs[0]; - currSelfInput = prevAndCurrInputs[1]; - } - 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? - self._sendInputFrameUpsyncBatch(noDelayInputFrameId); - } - - const delayedInputFrameId = self._convertToInputFrameId(self.renderFrameId, self.inputDelayFrames); // The "inputFrameId" to use at current "renderFrameId" - if (true == self.rollbackInMainUpdate) { - if (null != self.toRollbackRenderFrameId1) { - // Rollback-and-replay if necessary, prior to applying the latest dynamics - const anotherJoinIndex = 3-self.selfPlayerInfo.joinIndex; - self._rollbackAndReplay(self.toRollbackInputFrameId1, self.toRollbackRenderFrameId1, delayedInputFrameId, self.renderFrameId); - self.toRollbackRenderFrameId1 = null; - self.toRollbackRenderFrameId2 = null; + if (ALL_BATTLE_STATES.IN_BATTLE == self.battleState) { + try { + let prevSelfInput = null, currSelfInput = null; + const noDelayInputFrameId = self._convertToInputFrameId(self.renderFrameId, 0); // It's important that "inputDelayFrames == 0" here + if (self._shouldGenerateInputFrameUpsync(self.renderFrameId)) { + const prevAndCurrInputs = self._generateInputFrameUpsync(noDelayInputFrameId); + prevSelfInput = prevAndCurrInputs[0]; + currSelfInput = prevAndCurrInputs[1]; } - } - const delayedInputFrameDownsync = self.assembleInputFrameDownsync(delayedInputFrameId); - if (null == delayedInputFrameDownsync) { - console.warn("update(dt): recentInputCache is NOT having inputFrameId=", delayedInputFrameId, "; recentInputCache=", self._stringifyRecentInputCache(false)); - } else { - self._applyInputFrameDownsyncDynamics(delayedInputFrameDownsync, false); - } - const rdf = self._createRoomDownsyncFrameLocally(); - self._dumpToFullFrameCache(rdf); - /* - if (null != delayedInputFrameDownsync && null != delayedInputFrameDownsync.inputList && 0 < delayedInputFrameDownsync.inputList[self.selfPlayerInfo.joinIndex-1]) { - console.log("My critical status: renderFrame=", JSON.stringify(rdf), ", delayedInputFrameDownsync=", JSON.stringify(delayedInputFrameDownsync)); + 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? + self.sendInputFrameUpsyncBatch(noDelayInputFrameId); + } + + let t1 = performance.now(); + // Use "fractional-frame-chasing" to guarantee that "self.update(dt)" is not jammed by a "large range of frame-chasing". See `/ConcerningEdgeCases.md` for the motivation. + const prevChaserRenderFrameId = self.chaserRenderFrameId; + let nextChaserRenderFrameId = (prevChaserRenderFrameId + self.maxChasingRenderFramesPerUpdate); + if (nextChaserRenderFrameId > self.renderFrameId) nextChaserRenderFrameId = self.renderFrameId; + self.rollbackAndChase(prevChaserRenderFrameId, nextChaserRenderFrameId, self.chaserCollisionSys, self.chaserCollisionSysMap); + self.chaserRenderFrameId = nextChaserRenderFrameId; // Move the cursor "self.chaserRenderFrameId", keep in mind that "self.chaserRenderFrameId" is not monotonic! + let t2 = performance.now(); + + // 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. + const rdf = self.rollbackAndChase(self.renderFrameId, self.renderFrameId+1, self.latestCollisionSys, self.latestCollisionSysMap); + + self.applyRoomDownsyncFrameDynamics(rdf); + let t3 = performance.now(); + /* + if (prevChaserRenderFrameId < nextChaserRenderFrameId) { + console.log("Took ", t1-t0, " milliseconds to send upsync cmds, ", t2-t1, " milliseconds to chase renderFrameIds=[", prevChaserRenderFrameId, ", ", nextChaserRenderFrameId, "], @renderFrameId=", self.renderFrameId); + } + */ + } catch (err) { + console.error("Error during Map.update", err); + } finally { + // Update countdown + if (null != self.countdownNanos) { + self.countdownNanos -= self.rollbackEstimatedDt*1000000000; + if (self.countdownNanos <= 0) { + self.onBattleStopped(self.playerRichInfoDict); + return; + } + + const countdownSeconds = parseInt(self.countdownNanos / 1000000000); + if (isNaN(countdownSeconds)) { + console.warn(`countdownSeconds is NaN for countdownNanos == ${self.countdownNanos}.`); + } + self.countdownLabel.string = countdownSeconds; } - */ ++self.renderFrameId; // [WARNING] It's important to increment the renderFrameId AFTER all the operations above!!! } - const mapNode = self.node; - const canvasNode = mapNode.parent; - const canvasParentNode = canvasNode.parent; - if (null != window.boundRoomId) { - self.boundRoomIdLabel.string = window.boundRoomId; - } - - // update countdown - if (null != self.countdownNanos) { - self.countdownNanos -= self.rollbackEstimatedDt*1000000000; - if (self.countdownNanos <= 0) { - self.onBattleStopped(self.playerRichInfoDict); - return; - } - - const countdownSeconds = parseInt(self.countdownNanos / 1000000000); - if (isNaN(countdownSeconds)) { - console.warn(`countdownSeconds is NaN for countdownNanos == ${self.countdownNanos}.`); - } - self.countdownLabel.string = countdownSeconds; - } - } catch (err) { - 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); }, - playersMatched(playerMetas) { - console.log("Calling `playersMatched` with:", playerMetas); - + onBattleReadyToStart(playerMetas, isSelfRejoining) { + console.log("Calling `onBattleReadyToStart` with:", playerMetas); const self = this; const findingPlayerScriptIns = self.findingPlayerNode.getComponent("FindingPlayer"); + findingPlayerScriptIns.hideExitButton(); findingPlayerScriptIns.updatePlayersInfo(playerMetas); - window.setTimeout(() => { - if (null != self.findingPlayerNode.parent) { - self.findingPlayerNode.parent.removeChild(self.findingPlayerNode); - self.transitToState(ALL_MAP_STATES.VISUAL); - const playersInfoScriptIns = self.playersInfoNode.getComponent("PlayersInfo"); - for (let i in playerMetas) { - const playerMeta = playerMetas[i]; - playersInfoScriptIns.updateData(playerMeta); - } - } - const countDownScriptIns = self.countdownToBeginGameNode.getComponent("CountdownToBeginGame"); - countDownScriptIns.setData(); - self.showPopupInCanvas(self.countdownToBeginGameNode); - return; - }, 2000); + + const hideFindingPlayersGUI = function() { + if (null == self.findingPlayerNode.parent) return; + self.findingPlayerNode.parent.removeChild(self.findingPlayerNode); + }; + + if (true == isSelfRejoining) { + hideFindingPlayersGUI(); + } else { + // Delay to hide the "finding player" GUI, then show a countdown clock + window.setTimeout(() => { + hideFindingPlayersGUI(); + const countDownScriptIns = self.countdownToBeginGameNode.getComponent("CountdownToBeginGame"); + countDownScriptIns.setData(); + self.showPopupInCanvas(self.countdownToBeginGameNode); + }, 1500); + } }, - _createRoomDownsyncFrameLocally() { + _createRoomDownsyncFrameLocally(renderFrameId, collisionSys, collisionSysMap) { 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 = { - id: self.renderFrameId, - refFrameId: self.renderFrameId, - players: {}, - countdownNanos: self.countdownNanos + id: renderFrameId, + refFrameId: renderFrameId, + players: {} }; self.playerRichInfoDict.forEach((playerRichInfo, playerId) => { const joinIndex = playerRichInfo.joinIndex; - const playerNode = playerRichInfo.node; - const playerScriptIns = playerRichInfo.scriptIns; + const collisionPlayerIndex = self.collisionPlayerIndexPrefix + joinIndex; + const playerCollider = collisionSysMap.get(collisionPlayerIndex); rdf.players[playerRichInfo.id] = { id: playerRichInfo.id, - x: playerNode.position.x, - y: playerNode.position.y, - dir: playerScriptIns.activeDirection, - speed: playerScriptIns.speed, + x: playerCollider.x, + y: playerCollider.y, + dir: self.ctrl.decodeDirection(null == inputFrameForPrevRenderFrame ? 0 : inputFrameForPrevRenderFrame.inputList[joinIndex-1]), + speed: (null == speedRefRenderFrame ? playerRichInfo.speed : speedRefRenderFrame.players[playerRichInfo.id].speed), 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; }, - _applyRoomDownsyncFrameDynamics(rdf) { + applyRoomDownsyncFrameDynamics(rdf) { const self = this; self.playerRichInfoDict.forEach((playerRichInfo, playerId) => { @@ -962,11 +997,11 @@ cc.Class({ }); }, - assembleInputFrameDownsync(inputFrameId) { + getCachedInputFrameDownsyncWithPrediction(inputFrameId) { const self = this; - let inputFrameDownsync = self.recentInputCache.get(inputFrameId); - if (-1 != self.lastAllConfirmedInputFrameId && inputFrameId > self.lastAllConfirmedInputFrameId) { - const lastAllConfirmedInputFrame = self.recentInputCache.get(self.lastAllConfirmedInputFrameId); + let inputFrameDownsync = self.recentInputCache.getByFrameId(inputFrameId); + if (null != inputFrameDownsync && -1 != self.lastAllConfirmedInputFrameId && inputFrameId > self.lastAllConfirmedInputFrameId) { + const lastAllConfirmedInputFrame = self.recentInputCache.getByFrameId(self.lastAllConfirmedInputFrameId); for (let i = 0; i < inputFrameDownsync.inputList.length; ++i) { if (i == self.selfPlayerInfo.joinIndex-1) continue; inputFrameDownsync.inputList[i] = lastAllConfirmedInputFrame.inputList[i]; @@ -976,49 +1011,74 @@ cc.Class({ return inputFrameDownsync; }, - _applyInputFrameDownsyncDynamics(inputFrameDownsync, invokeUpdateToo) { - // 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". - 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!"); + rollbackAndChase(renderFrameIdSt, renderFrameIdEd, collisionSys, collisionSysMap) { + if (renderFrameSt >= renderFrameIdEd) { return; } - let t0 = performance.now(); - self._applyRoomDownsyncFrameDynamics(rdf1); - // DON'T apply inputFrameDownsync dynamics for exactly "renderFrameId2", see the comment around the invocation of "_rollbackAndReplay". - for (let renderFrameId = renderFrameId1; renderFrameId < renderFrameId2; ++renderFrameId) { - 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"); - }, + const self = this; + const renderFrameSt = self.recentRenderCache.getByFrameId(renderFrameIdSt); // typed "RoomDownsyncFrame" + if (null == renderFrameSt) { + console.error("Couldn't find renderFrameId=", renderFrameIdSt, " to rollback, recentRenderCache=", self._stringifyRecentRenderCache(false)); + } + /* + 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) { const self = this; for (let k in players) { @@ -1039,61 +1099,36 @@ cc.Class({ nodeAndScriptIns[1].showArrowTipNode(); } } - }, - - _stringifyRecentFrameCache(usefullOutput) { - 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 + ")"; + self.playerRichInfoArr = new Array(self.playerRichInfoDict.size); + self.playerRichInfoDict.forEach((playerRichInfo, playerId) => { + self.playerRichInfoArr[playerRichInfo.joinIndex-1] = playerRichInfo; + }); }, _stringifyRecentInputCache(usefullOutput) { + const self = this; if (true == usefullOutput) { let s = []; - self.recentInputCache.forEach((inputFrameDownsync, inputFrameId) => { - s.push(JSON.stringify(inputFrameDownsync)); - }); + for (let i = self.recentInputCache.stFrameId; i < self.recentInputCache.edFrameId; ++i) { + s.push(JSON.stringify(self.recentInputCache.getByFrameId(i))); + } return s.join('\n'); } - return "[stInputFrameId=" + self.recentInputCacheSt + ", edInputFrameId=" + self.recentInputCacheEd + ")"; + return "[stInputFrameId=" + self.recentInputCache.stFrameId + ", edInputFrameId=" + self.recentInputCache.edFrameId + ")"; }, - _stringifyRollbackResult(renderFrameId, delayedInputFrameDownsync) { - // Slightly different from "_createRoomDownsyncFrameLocally" + _stringifyRecentRenderCache(usefullOutput) { const self = this; - const s = ( - null == delayedInputFrameDownsync - ? - { - renderFrameId: renderFrameId, - players: {} + if (true == usefullOutput) { + let s = []; + for (let i = self.recentRenderCache.stFrameId; i < self.recentRenderCache.edFrameId; ++i) { + s.push(JSON.stringify(self.recentRenderCache.getByFrameId(i))); } - : - { - 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 + ")"; }, + }); diff --git a/frontend/assets/scripts/RingBuffer.js b/frontend/assets/scripts/RingBuffer.js new file mode 100644 index 0000000..6262c6d --- /dev/null +++ b/frontend/assets/scripts/RingBuffer.js @@ -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; diff --git a/frontend/assets/scripts/WechatGameLogin.js.meta b/frontend/assets/scripts/RingBuffer.js.meta similarity index 74% rename from frontend/assets/scripts/WechatGameLogin.js.meta rename to frontend/assets/scripts/RingBuffer.js.meta index bf132d7..4c931a3 100644 --- a/frontend/assets/scripts/WechatGameLogin.js.meta +++ b/frontend/assets/scripts/RingBuffer.js.meta @@ -1,6 +1,6 @@ { "ver": "1.0.5", - "uuid": "8264fb72-e348-45e4-9ab3-5bffb9a561ee", + "uuid": "9ec706f0-811c-403b-93a7-b34a7e5f8068", "isPlugin": false, "loadPluginInWeb": true, "loadPluginInNative": true, diff --git a/frontend/assets/scripts/TileCollisionManagerSingleton.js b/frontend/assets/scripts/TileCollisionManagerSingleton.js index 1d710b0..a37cd25 100644 --- a/frontend/assets/scripts/TileCollisionManagerSingleton.js +++ b/frontend/assets/scripts/TileCollisionManagerSingleton.js @@ -334,10 +334,6 @@ window.battleEntityTypeNameToGlobalGid = {}; TileCollisionManager.prototype.extractBoundaryObjects = function (withTiledMapNode) { let toRet = { barriers: [], - shelters: [], - shelterChainTails: [], - shelterChainHeads: [], - sheltersZReducer: [], frameAnimations: [], grandBoundaries: [], }; @@ -393,8 +389,6 @@ TileCollisionManager.prototype.extractBoundaryObjects = function (withTiledMapNo let childrenOfCurrentTile = null; if (cc.sys.isNative) { childrenOfCurrentTile = currentTile.getElementsByTagName("objectgroup"); - } else if (cc.sys.platform == cc.sys.WECHAT_GAME) { - childrenOfCurrentTile = currentTile.childNodes; } else { childrenOfCurrentTile = currentTile.children; } @@ -404,8 +398,6 @@ TileCollisionManager.prototype.extractBoundaryObjects = function (withTiledMapNo var currentObjectGroupUnderTile = mapInfo._parseObjectGroup(ch); gidBoundariesMap[parentGid] = { barriers: [], - shelters: [], - sheltersZReducer: [], }; for (let oidx = 0; oidx < currentObjectGroupUnderTile._objects.length; ++oidx) { const oo = currentObjectGroupUnderTile._objects[oidx]; @@ -429,22 +421,6 @@ TileCollisionManager.prototype.extractBoundaryObjects = function (withTiledMapNo brToPushTmp.boundaryType = boundaryType; gidBoundariesMap[parentGid].barriers.push(brToPushTmp); 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: break; } @@ -510,22 +486,6 @@ TileCollisionManager.prototype.extractBoundaryObjects = function (withTiledMapNo toPushBarriers.boundaryType = boundaryType; toRet.barriers.push(toPushBarriers); 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: break; } @@ -536,7 +496,7 @@ TileCollisionManager.prototype.extractBoundaryObjects = function (withTiledMapNo let layerDOMTrees = []; 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) { const tmpCh = mapDOMAllChildren[mdtIdx]; if (mapInfo._shouldIgnoreNode(tmpCh)) { @@ -585,23 +545,6 @@ TileCollisionManager.prototype.extractBoundaryObjects = function (withTiledMapNo } 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; default: @@ -637,6 +580,7 @@ TileCollisionManager.prototype.isOutOfMapNode = function (tiledMapNode, continuo }; TileCollisionManager.prototype.initMapNodeByTiledBoundaries = function(mapScriptIns, mapNode, extractedBoundaryObjs) { + // TODO: TO DEPRECATE! const tiledMapIns = mapNode.getComponent(cc.TiledMap); if (extractedBoundaryObjs.grandBoundaries) { window.grandBoundary = []; @@ -683,47 +627,6 @@ TileCollisionManager.prototype.initMapNodeByTiledBoundaries = function(mapScript 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 = []; for (let boundaryObj of extractedBoundaryObjs.barriers) { const newBarrier = cc.instantiate(mapScriptIns.polygonBoundaryBarrierPrefab); @@ -739,19 +642,6 @@ TileCollisionManager.prototype.initMapNodeByTiledBoundaries = function(mapScript 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(); for (let layer of allLayers) { const layerType = layer.getProperty("type"); @@ -759,10 +649,6 @@ TileCollisionManager.prototype.initMapNodeByTiledBoundaries = function(mapScript case "barrier_and_shelter": setLocalZOrder(layer.node, 3); break; - case "shelter_preview": - layer.node.opacity = 100; - setLocalZOrder(layer.node, 500); - break; default: break; } diff --git a/frontend/assets/scripts/TouchEventsManager.js b/frontend/assets/scripts/TouchEventsManager.js index 22c1554..4a1e459 100644 --- a/frontend/assets/scripts/TouchEventsManager.js +++ b/frontend/assets/scripts/TouchEventsManager.js @@ -1,19 +1,24 @@ window.DIRECTION_DECODER = [ - [0, 0], - [0, +1], - [0, -1], - [+2, 0], - [-2, 0], - [+2, +1], - [-2, -1], - [+2, -1], - [-2, +1], - [+2, 0], - [-2, 0], - [0, +1], - [0, -1], + [0, 0, null], + [0, +1, null], + [0, -1, null], + [+2, 0, null], + [-2, 0, null], + [+2, +1, null], + [-2, -1, null], + [+2, -1, null], + [-2, +1, null], + [+2, 0, null], + [-2, 0, null], + [0, +1, null], + [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({ extends: cc.Component, properties: { @@ -114,43 +119,6 @@ cc.Class({ 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() { const self = this; const translationListenerNode = (self.translationListenerNode ? self.translationListenerNode : self.mapNode); @@ -377,7 +345,8 @@ cc.Class({ } return { dx: mapped[0], - dy: mapped[1] + dy: mapped[1], + speedFactor: mapped[2], } }, diff --git a/frontend/assets/scripts/WechatGameLogin.js b/frontend/assets/scripts/WechatGameLogin.js deleted file mode 100644 index 8e5f849..0000000 --- a/frontend/assets/scripts/WechatGameLogin.js +++ /dev/null @@ -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; - } - } -}); diff --git a/frontend/assets/scripts/WsSessionMgr.js b/frontend/assets/scripts/WsSessionMgr.js index baa36ee..6d81061 100644 --- a/frontend/assets/scripts/WsSessionMgr.js +++ b/frontend/assets/scripts/WsSessionMgr.js @@ -74,8 +74,6 @@ function _base64ToUint8Array(base64) { origBytes[i] = origBinaryStr.charCodeAt(i); } return origBytes; - } else if (cc.sys.platform == cc.sys.WECHAT_GAME) { - return Buffer.from(base64, 'base64'); } else { return null; } @@ -86,16 +84,12 @@ function _base64ToArrayBuffer(base64) { } window.getExpectedRoomIdSync = function() { - if (cc.sys.platform == cc.sys.WECHAT_GAME) { - return window.expectedRoomId; + const qDict = window.getQueryParamDict(); + if (qDict) { + return qDict["expectedRoomId"]; } else { - const qDict = window.getQueryParamDict(); - if (qDict) { - return qDict["expectedRoomId"]; - } else { - if (window.history && window.history.state) { - return window.history.state.expectedRoomId; - } + if (window.history && window.history.state) { + return window.history.state.expectedRoomId; } } @@ -129,10 +123,6 @@ window.initPersistentSessionClient = function(onopenCb, expectedRoomId) { if (null != expectedRoomId) { console.log("initPersistentSessionClient with expectedRoomId == " + expectedRoomId); urlToConnect = urlToConnect + "&expectedRoomId=" + expectedRoomId; - if (cc.sys.platform == cc.sys.WECHAT_GAME) { - // This is a dirty hack. -- YFLu - window.expectedRoomId = null; - } } else { window.boundRoomId = getBoundRoomIdFromPersistentStorage(); if (null != window.boundRoomId) { @@ -143,10 +133,6 @@ window.initPersistentSessionClient = function(onopenCb, expectedRoomId) { 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); 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) { window.clearBoundRoomIdInBothVolatileAndPersistentStorage(); } - if (cc.sys.platform == cc.sys.WECHAT_GAME) { - cc.director.loadScene('wechatGameLogin'); - } else { - cc.director.loadScene('login'); - } + cc.director.loadScene('login'); }; diff --git a/frontend/assets/scripts/modules/Collisions.js b/frontend/assets/scripts/modules/Collisions.js new file mode 100644 index 0000000..6805e83 --- /dev/null +++ b/frontend/assets/scripts/modules/Collisions.js @@ -0,0 +1,197 @@ +/******/ (function(modules) { // webpackBootstrap +/******/ // The module cache +/******/ var installedModules = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ +/******/ // Check if module is in cache +/******/ if(installedModules[moduleId]) { +/******/ return installedModules[moduleId].exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = installedModules[moduleId] = { +/******/ i: moduleId, +/******/ l: false, +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); +/******/ +/******/ // Flag the module as loaded +/******/ module.l = true; +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/******/ +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = modules; +/******/ +/******/ // expose the module cache +/******/ __webpack_require__.c = installedModules; +/******/ +/******/ // define getter function for harmony exports +/******/ __webpack_require__.d = function(exports, name, getter) { +/******/ if(!__webpack_require__.o(exports, name)) { +/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); +/******/ } +/******/ }; +/******/ +/******/ // define __esModule on exports +/******/ __webpack_require__.r = function(exports) { +/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { +/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); +/******/ } +/******/ Object.defineProperty(exports, '__esModule', { value: true }); +/******/ }; +/******/ +/******/ // create a fake namespace object +/******/ // mode & 1: value is a module id, require it +/******/ // mode & 2: merge all properties of value into the ns +/******/ // mode & 4: return value when already ns object +/******/ // mode & 8|1: behave like require +/******/ __webpack_require__.t = function(value, mode) { +/******/ if(mode & 1) value = __webpack_require__(value); +/******/ if(mode & 8) return value; +/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; +/******/ var ns = Object.create(null); +/******/ __webpack_require__.r(ns); +/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); +/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); +/******/ return ns; +/******/ }; +/******/ +/******/ // getDefaultExport function for compatibility with non-harmony modules +/******/ __webpack_require__.n = function(module) { +/******/ var getter = module && module.__esModule ? +/******/ function getDefault() { return module['default']; } : +/******/ function getModuleExports() { return module; }; +/******/ __webpack_require__.d(getter, 'a', getter); +/******/ return getter; +/******/ }; +/******/ +/******/ // Object.prototype.hasOwnProperty.call +/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; +/******/ +/******/ // __webpack_public_path__ +/******/ __webpack_require__.p = ""; +/******/ +/******/ +/******/ // Load entry module and return exports +/******/ return __webpack_require__(__webpack_require__.s = "./src/Collisions.mjs"); +/******/ }) +/************************************************************************/ +/******/ ({ + +/***/ "./src/Collisions.mjs": +/*!****************************!*\ + !*** ./src/Collisions.mjs ***! + \****************************/ +/*! no exports provided */ +/***/ (function(__webpack_module__, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _modules_BVH_mjs__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./modules/BVH.mjs */ \"./src/modules/BVH.mjs\");\n/* harmony import */ var _modules_Circle_mjs__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./modules/Circle.mjs */ \"./src/modules/Circle.mjs\");\n/* harmony import */ var _modules_Polygon_mjs__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./modules/Polygon.mjs */ \"./src/modules/Polygon.mjs\");\n/* harmony import */ var _modules_Point_mjs__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./modules/Point.mjs */ \"./src/modules/Point.mjs\");\n/* harmony import */ var _modules_Result_mjs__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./modules/Result.mjs */ \"./src/modules/Result.mjs\");\n/* harmony import */ var _modules_SAT_mjs__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./modules/SAT.mjs */ \"./src/modules/SAT.mjs\");\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n/**\r\n * A collision system used to track bodies in order to improve collision detection performance\r\n * @class\r\n */\r\nclass Collisions {\r\n\t/**\r\n\t * @constructor\r\n\t */\r\n\tconstructor() {\r\n\t\t/** @private */\r\n\t\tthis._bvh = new _modules_BVH_mjs__WEBPACK_IMPORTED_MODULE_0__[\"default\"]();\r\n\t}\r\n\r\n\t/**\r\n\t * Creates a {@link Circle} and inserts it into the collision system\r\n\t * @param {Number} [x = 0] The starting X coordinate\r\n\t * @param {Number} [y = 0] The starting Y coordinate\r\n\t * @param {Number} [radius = 0] The radius\r\n\t * @param {Number} [scale = 1] The scale\r\n\t * @param {Number} [padding = 0] The amount to pad the bounding volume when testing for potential collisions\r\n\t * @returns {Circle}\r\n\t */\r\n\tcreateCircle(x = 0, y = 0, radius = 0, scale = 1, padding = 0) {\r\n\t\tconst body = new _modules_Circle_mjs__WEBPACK_IMPORTED_MODULE_1__[\"default\"](x, y, radius, scale, padding);\r\n\r\n\t\tthis._bvh.insert(body);\r\n\r\n\t\treturn body;\r\n\t}\r\n\r\n\t/**\r\n\t * Creates a {@link Polygon} and inserts it into the collision system\r\n\t * @param {Number} [x = 0] The starting X coordinate\r\n\t * @param {Number} [y = 0] The starting Y coordinate\r\n\t * @param {Array} [points = []] An array of coordinate pairs making up the polygon - [[x1, y1], [x2, y2], ...]\r\n\t * @param {Number} [angle = 0] The starting rotation in radians\r\n\t * @param {Number} [scale_x = 1] The starting scale along the X axis\r\n\t * @param {Number} [scale_y = 1] The starting scale long the Y axis\r\n\t * @param {Number} [padding = 0] The amount to pad the bounding volume when testing for potential collisions\r\n\t * @returns {Polygon}\r\n\t */\r\n\tcreatePolygon(x = 0, y = 0, points = [[0, 0]], angle = 0, scale_x = 1, scale_y = 1, padding = 0) {\r\n\t\tconst body = new _modules_Polygon_mjs__WEBPACK_IMPORTED_MODULE_2__[\"default\"](x, y, points, angle, scale_x, scale_y, padding);\r\n\r\n\t\tthis._bvh.insert(body);\r\n\r\n\t\treturn body;\r\n\t}\r\n\r\n\t/**\r\n\t * Creates a {@link Point} and inserts it into the collision system\r\n\t * @param {Number} [x = 0] The starting X coordinate\r\n\t * @param {Number} [y = 0] The starting Y coordinate\r\n\t * @param {Number} [padding = 0] The amount to pad the bounding volume when testing for potential collisions\r\n\t * @returns {Point}\r\n\t */\r\n\tcreatePoint(x = 0, y = 0, padding = 0) {\r\n\t\tconst body = new _modules_Point_mjs__WEBPACK_IMPORTED_MODULE_3__[\"default\"](x, y, padding);\r\n\r\n\t\tthis._bvh.insert(body);\r\n\r\n\t\treturn body;\r\n\t}\r\n\r\n\t/**\r\n\t * Creates a {@link Result} used to collect the detailed results of a collision test\r\n\t */\r\n\tcreateResult() {\r\n\t\treturn new _modules_Result_mjs__WEBPACK_IMPORTED_MODULE_4__[\"default\"]();\r\n\t}\r\n\r\n\t/**\r\n\t * Creates a Result used to collect the detailed results of a collision test\r\n\t */\r\n\tstatic createResult() {\r\n\t\treturn new _modules_Result_mjs__WEBPACK_IMPORTED_MODULE_4__[\"default\"]();\r\n\t}\r\n\r\n\t/**\r\n\t * Inserts bodies into the collision system\r\n\t * @param {...Circle|...Polygon|...Point} bodies\r\n\t */\r\n\tinsert(...bodies) {\r\n\t\tfor(const body of bodies) {\r\n\t\t\tthis._bvh.insert(body, false);\r\n\t\t}\r\n\r\n\t\treturn this;\r\n\t}\r\n\r\n\t/**\r\n\t * Removes bodies from the collision system\r\n\t * @param {...Circle|...Polygon|...Point} bodies\r\n\t */\r\n\tremove(...bodies) {\r\n\t\tfor(const body of bodies) {\r\n\t\t\tthis._bvh.remove(body, false);\r\n\t\t}\r\n\r\n\t\treturn this;\r\n\t}\r\n\r\n\t/**\r\n\t * Updates the collision system. This should be called before any collisions are tested.\r\n\t */\r\n\tupdate() {\r\n\t\tthis._bvh.update();\r\n\r\n\t\treturn this;\r\n\t}\r\n\r\n\t/**\r\n\t * Draws the bodies within the system to a CanvasRenderingContext2D's current path\r\n\t * @param {CanvasRenderingContext2D} context The context to draw to\r\n\t */\r\n\tdraw(context) {\r\n\t\treturn this._bvh.draw(context);\r\n\t}\r\n\r\n\t/**\r\n\t * Draws the system's BVH to a CanvasRenderingContext2D's current path. This is useful for testing out different padding values for bodies.\r\n\t * @param {CanvasRenderingContext2D} context The context to draw to\r\n\t */\r\n\tdrawBVH(context) {\r\n\t\treturn this._bvh.drawBVH(context);\r\n\t}\r\n\r\n\t/**\r\n\t * Returns a list of potential collisions for a body\r\n\t * @param {Circle|Polygon|Point} body The body to test for potential collisions against\r\n\t * @returns {Array}\r\n\t */\r\n\tpotentials(body) {\r\n\t\treturn this._bvh.potentials(body);\r\n\t}\r\n\r\n\t/**\r\n\t * Determines if two bodies are colliding\r\n\t * @param {Circle|Polygon|Point} target The target body to test against\r\n\t * @param {Result} [result = null] A Result object on which to store information about the collision\r\n\t * @param {Boolean} [aabb = true] Set to false to skip the AABB test (useful if you use your own potential collision heuristic)\r\n\t * @returns {Boolean}\r\n\t */\r\n\tcollides(source, target, result = null, aabb = true) {\r\n\t\treturn Object(_modules_SAT_mjs__WEBPACK_IMPORTED_MODULE_5__[\"default\"])(source, target, result, aabb);\r\n\t}\r\n};\r\n\r\nconst toExport = {\r\n\tCollisions,\r\n\tResult: _modules_Result_mjs__WEBPACK_IMPORTED_MODULE_4__[\"default\"],\r\n\tCircle: _modules_Circle_mjs__WEBPACK_IMPORTED_MODULE_1__[\"default\"],\r\n\tPolygon: _modules_Polygon_mjs__WEBPACK_IMPORTED_MODULE_2__[\"default\"],\r\n\tPoint: _modules_Point_mjs__WEBPACK_IMPORTED_MODULE_3__[\"default\"],\r\n};\r\n\r\nmodule.exports = toExport; \r\n\n\n//# sourceURL=webpack:///./src/Collisions.mjs?"); + +/***/ }), + +/***/ "./src/modules/BVH.mjs": +/*!*****************************!*\ + !*** ./src/modules/BVH.mjs ***! + \*****************************/ +/*! exports provided: default */ +/***/ (function(__webpack_module__, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return BVH; });\n/* harmony import */ var _BVHBranch_mjs__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./BVHBranch.mjs */ \"./src/modules/BVHBranch.mjs\");\n\r\n\r\n/**\r\n * A Bounding Volume Hierarchy (BVH) used to find potential collisions quickly\r\n * @class\r\n * @private\r\n */\r\nclass BVH {\r\n\t/**\r\n\t * @constructor\r\n\t */\r\n\tconstructor() {\r\n\t\t/** @private */\r\n\t\tthis._hierarchy = null;\r\n\r\n\t\t/** @private */\r\n\t\tthis._bodies = [];\r\n\r\n\t\t/** @private */\r\n\t\tthis._dirty_branches = [];\r\n\t}\r\n\r\n\t/**\r\n\t * Inserts a body into the BVH\r\n\t * @param {Circle|Polygon|Point} body The body to insert\r\n\t * @param {Boolean} [updating = false] Set to true if the body already exists in the BVH (used internally when updating the body's position)\r\n\t */\r\n\tinsert(body, updating = false) {\r\n\t\tif(!updating) {\r\n\t\t\tconst bvh = body._bvh;\r\n\r\n\t\t\tif(bvh && bvh !== this) {\r\n\t\t\t\tthrow new Error('Body belongs to another collision system');\r\n\t\t\t}\r\n\r\n\t\t\tbody._bvh = this;\r\n\t\t\tthis._bodies.push(body);\r\n\t\t}\r\n\r\n\t\tconst polygon = body._polygon;\r\n\t\tconst body_x = body.x;\r\n\t\tconst body_y = body.y;\r\n\r\n\t\tif(polygon) {\r\n\t\t\tif(\r\n\t\t\t\tbody._dirty_coords ||\r\n\t\t\t\tbody.x !== body._x ||\r\n\t\t\t\tbody.y !== body._y ||\r\n\t\t\t\tbody.angle !== body._angle ||\r\n\t\t\t\tbody.scale_x !== body._scale_x ||\r\n\t\t\t\tbody.scale_y !== body._scale_y\r\n\t\t\t) {\r\n\t\t\t\tbody._calculateCoords();\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tconst padding = body._bvh_padding;\r\n\t\tconst radius = polygon ? 0 : body.radius * body.scale;\r\n\t\tconst body_min_x = (polygon ? body._min_x : body_x - radius) - padding;\r\n\t\tconst body_min_y = (polygon ? body._min_y : body_y - radius) - padding;\r\n\t\tconst body_max_x = (polygon ? body._max_x : body_x + radius) + padding;\r\n\t\tconst body_max_y = (polygon ? body._max_y : body_y + radius) + padding;\r\n\r\n\t\tbody._bvh_min_x = body_min_x;\r\n\t\tbody._bvh_min_y = body_min_y;\r\n\t\tbody._bvh_max_x = body_max_x;\r\n\t\tbody._bvh_max_y = body_max_y;\r\n\r\n\t\tlet current = this._hierarchy;\r\n\t\tlet sort = 0;\r\n\r\n\t\tif(!current) {\r\n\t\t\tthis._hierarchy = body;\r\n\t\t}\r\n\t\telse {\r\n\t\t\twhile(true) {\r\n\t\t\t\t// Branch\r\n\t\t\t\tif(current._bvh_branch) {\r\n\t\t\t\t\tconst left = current._bvh_left;\r\n\t\t\t\t\tconst left_min_y = left._bvh_min_y;\r\n\t\t\t\t\tconst left_max_x = left._bvh_max_x;\r\n\t\t\t\t\tconst left_max_y = left._bvh_max_y;\r\n\t\t\t\t\tconst left_new_min_x = body_min_x < left._bvh_min_x ? body_min_x : left._bvh_min_x;\r\n\t\t\t\t\tconst left_new_min_y = body_min_y < left_min_y ? body_min_y : left_min_y;\r\n\t\t\t\t\tconst left_new_max_x = body_max_x > left_max_x ? body_max_x : left_max_x;\r\n\t\t\t\t\tconst left_new_max_y = body_max_y > left_max_y ? body_max_y : left_max_y;\r\n\t\t\t\t\tconst left_volume = (left_max_x - left._bvh_min_x) * (left_max_y - left_min_y);\r\n\t\t\t\t\tconst left_new_volume = (left_new_max_x - left_new_min_x) * (left_new_max_y - left_new_min_y);\r\n\t\t\t\t\tconst left_difference = left_new_volume - left_volume;\r\n\r\n\t\t\t\t\tconst right = current._bvh_right;\r\n\t\t\t\t\tconst right_min_x = right._bvh_min_x;\r\n\t\t\t\t\tconst right_min_y = right._bvh_min_y;\r\n\t\t\t\t\tconst right_max_x = right._bvh_max_x;\r\n\t\t\t\t\tconst right_max_y = right._bvh_max_y;\r\n\t\t\t\t\tconst right_new_min_x = body_min_x < right_min_x ? body_min_x : right_min_x;\r\n\t\t\t\t\tconst right_new_min_y = body_min_y < right_min_y ? body_min_y : right_min_y;\r\n\t\t\t\t\tconst right_new_max_x = body_max_x > right_max_x ? body_max_x : right_max_x;\r\n\t\t\t\t\tconst right_new_max_y = body_max_y > right_max_y ? body_max_y : right_max_y;\r\n\t\t\t\t\tconst right_volume = (right_max_x - right_min_x) * (right_max_y - right_min_y);\r\n\t\t\t\t\tconst right_new_volume = (right_new_max_x - right_new_min_x) * (right_new_max_y - right_new_min_y);\r\n\t\t\t\t\tconst right_difference = right_new_volume - right_volume;\r\n\r\n\t\t\t\t\tcurrent._bvh_sort = sort++;\r\n\t\t\t\t\tcurrent._bvh_min_x = left_new_min_x < right_new_min_x ? left_new_min_x : right_new_min_x;\r\n\t\t\t\t\tcurrent._bvh_min_y = left_new_min_y < right_new_min_y ? left_new_min_y : right_new_min_y;\r\n\t\t\t\t\tcurrent._bvh_max_x = left_new_max_x > right_new_max_x ? left_new_max_x : right_new_max_x;\r\n\t\t\t\t\tcurrent._bvh_max_y = left_new_max_y > right_new_max_y ? left_new_max_y : right_new_max_y;\r\n\r\n\t\t\t\t\tcurrent = left_difference <= right_difference ? left : right;\r\n\t\t\t\t}\r\n\t\t\t\t// Leaf\r\n\t\t\t\telse {\r\n\t\t\t\t\tconst grandparent = current._bvh_parent;\r\n\t\t\t\t\tconst parent_min_x = current._bvh_min_x;\r\n\t\t\t\t\tconst parent_min_y = current._bvh_min_y;\r\n\t\t\t\t\tconst parent_max_x = current._bvh_max_x;\r\n\t\t\t\t\tconst parent_max_y = current._bvh_max_y;\r\n\t\t\t\t\tconst new_parent = current._bvh_parent = body._bvh_parent = _BVHBranch_mjs__WEBPACK_IMPORTED_MODULE_0__[\"default\"].getBranch();\r\n\r\n\t\t\t\t\tnew_parent._bvh_parent = grandparent;\r\n\t\t\t\t\tnew_parent._bvh_left = current;\r\n\t\t\t\t\tnew_parent._bvh_right = body;\r\n\t\t\t\t\tnew_parent._bvh_sort = sort++;\r\n\t\t\t\t\tnew_parent._bvh_min_x = body_min_x < parent_min_x ? body_min_x : parent_min_x;\r\n\t\t\t\t\tnew_parent._bvh_min_y = body_min_y < parent_min_y ? body_min_y : parent_min_y;\r\n\t\t\t\t\tnew_parent._bvh_max_x = body_max_x > parent_max_x ? body_max_x : parent_max_x;\r\n\t\t\t\t\tnew_parent._bvh_max_y = body_max_y > parent_max_y ? body_max_y : parent_max_y;\r\n\r\n\t\t\t\t\tif(!grandparent) {\r\n\t\t\t\t\t\tthis._hierarchy = new_parent;\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse if(grandparent._bvh_left === current) {\r\n\t\t\t\t\t\tgrandparent._bvh_left = new_parent;\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse {\r\n\t\t\t\t\t\tgrandparent._bvh_right = new_parent;\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\tbreak;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\t/**\r\n\t * Removes a body from the BVH\r\n\t * @param {Circle|Polygon|Point} body The body to remove\r\n\t * @param {Boolean} [updating = false] Set to true if this is a temporary removal (used internally when updating the body's position)\r\n\t */\r\n\tremove(body, updating = false) {\r\n\t\tif(!updating) {\r\n\t\t\tconst bvh = body._bvh;\r\n\r\n\t\t\tif(bvh && bvh !== this) {\r\n\t\t\t\tthrow new Error('Body belongs to another collision system');\r\n\t\t\t}\r\n\r\n\t\t\tbody._bvh = null;\r\n\t\t\tthis._bodies.splice(this._bodies.indexOf(body), 1);\r\n\t\t}\r\n\r\n\t\tif(this._hierarchy === body) {\r\n\t\t\tthis._hierarchy = null;\r\n\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tconst parent = body._bvh_parent;\r\n\t\tconst grandparent = parent._bvh_parent;\r\n\t\tconst parent_left = parent._bvh_left;\r\n\t\tconst sibling = parent_left === body ? parent._bvh_right : parent_left;\r\n\r\n\t\tsibling._bvh_parent = grandparent;\r\n\r\n\t\tif(sibling._bvh_branch) {\r\n\t\t\tsibling._bvh_sort = parent._bvh_sort;\r\n\t\t}\r\n\r\n\t\tif(grandparent) {\r\n\t\t\tif(grandparent._bvh_left === parent) {\r\n\t\t\t\tgrandparent._bvh_left = sibling;\r\n\t\t\t}\r\n\t\t\telse {\r\n\t\t\t\tgrandparent._bvh_right = sibling;\r\n\t\t\t}\r\n\r\n\t\t\tlet branch = grandparent;\r\n\r\n\t\t\twhile(branch) {\r\n\t\t\t\tconst left = branch._bvh_left;\r\n\t\t\t\tconst left_min_x = left._bvh_min_x;\r\n\t\t\t\tconst left_min_y = left._bvh_min_y;\r\n\t\t\t\tconst left_max_x = left._bvh_max_x;\r\n\t\t\t\tconst left_max_y = left._bvh_max_y;\r\n\r\n\t\t\t\tconst right = branch._bvh_right;\r\n\t\t\t\tconst right_min_x = right._bvh_min_x;\r\n\t\t\t\tconst right_min_y = right._bvh_min_y;\r\n\t\t\t\tconst right_max_x = right._bvh_max_x;\r\n\t\t\t\tconst right_max_y = right._bvh_max_y;\r\n\r\n\t\t\t\tbranch._bvh_min_x = left_min_x < right_min_x ? left_min_x : right_min_x;\r\n\t\t\t\tbranch._bvh_min_y = left_min_y < right_min_y ? left_min_y : right_min_y;\r\n\t\t\t\tbranch._bvh_max_x = left_max_x > right_max_x ? left_max_x : right_max_x;\r\n\t\t\t\tbranch._bvh_max_y = left_max_y > right_max_y ? left_max_y : right_max_y;\r\n\r\n\t\t\t\tbranch = branch._bvh_parent;\r\n\t\t\t}\r\n\t\t}\r\n\t\telse {\r\n\t\t\tthis._hierarchy = sibling;\r\n\t\t}\r\n\r\n\t\t_BVHBranch_mjs__WEBPACK_IMPORTED_MODULE_0__[\"default\"].releaseBranch(parent);\r\n\t}\r\n\r\n\t/**\r\n\t * Updates the BVH. Moved bodies are removed/inserted.\r\n\t */\r\n\tupdate() {\r\n\t\tconst bodies = this._bodies;\r\n\t\tconst count = bodies.length;\r\n\r\n\t\tfor(let i = 0; i < count; ++i) {\r\n\t\t\tconst body = bodies[i];\r\n\r\n\t\t\tlet update = false;\r\n\r\n\t\t\tif(!update && body.padding !== body._bvh_padding) {\r\n\t\t\t\tbody._bvh_padding = body.padding;\r\n\t\t\t\tupdate = true;\r\n\t\t\t}\r\n\r\n\t\t\tif(!update) {\r\n\t\t\t\tconst polygon = body._polygon;\r\n\r\n\t\t\t\tif(polygon) {\r\n\t\t\t\t\tif(\r\n\t\t\t\t\t\tbody._dirty_coords ||\r\n\t\t\t\t\t\tbody.x !== body._x ||\r\n\t\t\t\t\t\tbody.y !== body._y ||\r\n\t\t\t\t\t\tbody.angle !== body._angle ||\r\n\t\t\t\t\t\tbody.scale_x !== body._scale_x ||\r\n\t\t\t\t\t\tbody.scale_y !== body._scale_y\r\n\t\t\t\t\t) {\r\n\t\t\t\t\t\tbody._calculateCoords();\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\r\n\t\t\t\tconst x = body.x;\r\n\t\t\t\tconst y = body.y;\r\n\t\t\t\tconst radius = polygon ? 0 : body.radius * body.scale;\r\n\t\t\t\tconst min_x = polygon ? body._min_x : x - radius;\r\n\t\t\t\tconst min_y = polygon ? body._min_y : y - radius;\r\n\t\t\t\tconst max_x = polygon ? body._max_x : x + radius;\r\n\t\t\t\tconst max_y = polygon ? body._max_y : y + radius;\r\n\r\n\t\t\t\tupdate = min_x < body._bvh_min_x || min_y < body._bvh_min_y || max_x > body._bvh_max_x || max_y > body._bvh_max_y;\r\n\t\t\t}\r\n\r\n\t\t\tif(update) {\r\n\t\t\t\tthis.remove(body, true);\r\n\t\t\t\tthis.insert(body, true);\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\t/**\r\n\t * Returns a list of potential collisions for a body\r\n\t * @param {Circle|Polygon|Point} body The body to test\r\n\t * @returns {Array}\r\n\t */\r\n\tpotentials(body) {\r\n\t\tconst results = [];\r\n\t\tconst min_x = body._bvh_min_x;\r\n\t\tconst min_y = body._bvh_min_y;\r\n\t\tconst max_x = body._bvh_max_x;\r\n\t\tconst max_y = body._bvh_max_y;\r\n\r\n\t\tlet current = this._hierarchy;\r\n\t\tlet traverse_left = true;\r\n\r\n\t\tif(!current || !current._bvh_branch) {\r\n\t\t\treturn results;\r\n\t\t}\r\n\r\n\t\twhile(current) {\r\n\t\t\tif(traverse_left) {\r\n\t\t\t\ttraverse_left = false;\r\n\r\n\t\t\t\tlet left = current._bvh_branch ? current._bvh_left : null;\r\n\r\n\t\t\t\twhile(\r\n\t\t\t\t\tleft &&\r\n\t\t\t\t\tleft._bvh_max_x >= min_x &&\r\n\t\t\t\t\tleft._bvh_max_y >= min_y &&\r\n\t\t\t\t\tleft._bvh_min_x <= max_x &&\r\n\t\t\t\t\tleft._bvh_min_y <= max_y\r\n\t\t\t\t) {\r\n\t\t\t\t\tcurrent = left;\r\n\t\t\t\t\tleft = current._bvh_branch ? current._bvh_left : null;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\tconst branch = current._bvh_branch;\r\n\t\t\tconst right = branch ? current._bvh_right : null;\r\n\r\n\t\t\tif(\r\n\t\t\t\tright &&\r\n\t\t\t\tright._bvh_max_x > min_x &&\r\n\t\t\t\tright._bvh_max_y > min_y &&\r\n\t\t\t\tright._bvh_min_x < max_x &&\r\n\t\t\t\tright._bvh_min_y < max_y\r\n\t\t\t) {\r\n\t\t\t\tcurrent = right;\r\n\t\t\t\ttraverse_left = true;\r\n\t\t\t}\r\n\t\t\telse {\r\n\t\t\t\tif(!branch && current !== body) {\r\n\t\t\t\t\tresults.push(current);\r\n\t\t\t\t}\r\n\r\n\t\t\t\tlet parent = current._bvh_parent;\r\n\r\n\t\t\t\tif(parent) {\r\n\t\t\t\t\twhile(parent && parent._bvh_right === current) {\r\n\t\t\t\t\t\tcurrent = parent;\r\n\t\t\t\t\t\tparent = current._bvh_parent;\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\tcurrent = parent;\r\n\t\t\t\t}\r\n\t\t\t\telse {\r\n\t\t\t\t\tbreak;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\treturn results;\r\n\t}\r\n\r\n\t/**\r\n\t * Draws the bodies within the BVH to a CanvasRenderingContext2D's current path\r\n\t * @param {CanvasRenderingContext2D} context The context to draw to\r\n\t */\r\n\tdraw(context) {\r\n\t\tconst bodies = this._bodies;\r\n\t\tconst count = bodies.length;\r\n\r\n\t\tfor(let i = 0; i < count; ++i) {\r\n\t\t\tbodies[i].draw(context);\r\n\t\t}\r\n\t}\r\n\r\n\t/**\r\n\t * Draws the BVH to a CanvasRenderingContext2D's current path. This is useful for testing out different padding values for bodies.\r\n\t * @param {CanvasRenderingContext2D} context The context to draw to\r\n\t */\r\n\tdrawBVH(context) {\r\n\t\tlet current = this._hierarchy;\r\n\t\tlet traverse_left = true;\r\n\r\n\t\twhile(current) {\r\n\t\t\tif(traverse_left) {\r\n\t\t\t\ttraverse_left = false;\r\n\r\n\t\t\t\tlet left = current._bvh_branch ? current._bvh_left : null;\r\n\r\n\t\t\t\twhile(left) {\r\n\t\t\t\t\tcurrent = left;\r\n\t\t\t\t\tleft = current._bvh_branch ? current._bvh_left : null;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\tconst branch = current._bvh_branch;\r\n\t\t\tconst min_x = current._bvh_min_x;\r\n\t\t\tconst min_y = current._bvh_min_y;\r\n\t\t\tconst max_x = current._bvh_max_x;\r\n\t\t\tconst max_y = current._bvh_max_y;\r\n\t\t\tconst right = branch ? current._bvh_right : null;\r\n\r\n\t\t\tcontext.moveTo(min_x, min_y);\r\n\t\t\tcontext.lineTo(max_x, min_y);\r\n\t\t\tcontext.lineTo(max_x, max_y);\r\n\t\t\tcontext.lineTo(min_x, max_y);\r\n\t\t\tcontext.lineTo(min_x, min_y);\r\n\r\n\t\t\tif(right) {\r\n\t\t\t\tcurrent = right;\r\n\t\t\t\ttraverse_left = true;\r\n\t\t\t}\r\n\t\t\telse {\r\n\t\t\t\tlet parent = current._bvh_parent;\r\n\r\n\t\t\t\tif(parent) {\r\n\t\t\t\t\twhile(parent && parent._bvh_right === current) {\r\n\t\t\t\t\t\tcurrent = parent;\r\n\t\t\t\t\t\tparent = current._bvh_parent;\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\tcurrent = parent;\r\n\t\t\t\t}\r\n\t\t\t\telse {\r\n\t\t\t\t\tbreak;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n};\r\n\n\n//# sourceURL=webpack:///./src/modules/BVH.mjs?"); + +/***/ }), + +/***/ "./src/modules/BVHBranch.mjs": +/*!***********************************!*\ + !*** ./src/modules/BVHBranch.mjs ***! + \***********************************/ +/*! exports provided: default */ +/***/ (function(__webpack_module__, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return BVHBranch; });\n/**\r\n * @private\r\n */\r\nconst branch_pool = [];\r\n\r\n/**\r\n * A branch within a BVH\r\n * @class\r\n * @private\r\n */\r\nclass BVHBranch {\r\n\t/**\r\n\t * @constructor\r\n\t */\r\n\tconstructor() {\r\n\t\t/** @private */\r\n\t\tthis._bvh_parent = null;\r\n\r\n\t\t/** @private */\r\n\t\tthis._bvh_branch = true;\r\n\r\n\t\t/** @private */\r\n\t\tthis._bvh_left = null;\r\n\r\n\t\t/** @private */\r\n\t\tthis._bvh_right = null;\r\n\r\n\t\t/** @private */\r\n\t\tthis._bvh_sort = 0;\r\n\r\n\t\t/** @private */\r\n\t\tthis._bvh_min_x = 0;\r\n\r\n\t\t/** @private */\r\n\t\tthis._bvh_min_y = 0;\r\n\r\n\t\t/** @private */\r\n\t\tthis._bvh_max_x = 0;\r\n\r\n\t\t/** @private */\r\n\t\tthis._bvh_max_y = 0;\r\n\t}\r\n\r\n\t/**\r\n\t * Returns a branch from the branch pool or creates a new branch\r\n\t * @returns {BVHBranch}\r\n\t */\r\n\tstatic getBranch() {\r\n\t\tif(branch_pool.length) {\r\n\t\t\treturn branch_pool.pop();\r\n\t\t}\r\n\r\n\t\treturn new BVHBranch();\r\n\t}\r\n\r\n\t/**\r\n\t * Releases a branch back into the branch pool\r\n\t * @param {BVHBranch} branch The branch to release\r\n\t */\r\n\tstatic releaseBranch(branch) {\r\n\t\tbranch_pool.push(branch);\r\n\t}\r\n\r\n\t/**\r\n\t * Sorting callback used to sort branches by deepest first\r\n\t * @param {BVHBranch} a The first branch\r\n\t * @param {BVHBranch} b The second branch\r\n\t * @returns {Number}\r\n\t */\r\n\tstatic sortBranches(a, b) {\r\n\t\treturn a.sort > b.sort ? -1 : 1;\r\n\t}\r\n};\r\n\n\n//# sourceURL=webpack:///./src/modules/BVHBranch.mjs?"); + +/***/ }), + +/***/ "./src/modules/Body.mjs": +/*!******************************!*\ + !*** ./src/modules/Body.mjs ***! + \******************************/ +/*! exports provided: default */ +/***/ (function(__webpack_module__, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return Body; });\n/* harmony import */ var _Result_mjs__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./Result.mjs */ \"./src/modules/Result.mjs\");\n/* harmony import */ var _SAT_mjs__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./SAT.mjs */ \"./src/modules/SAT.mjs\");\n\r\n\r\n\r\n/**\r\n * The base class for bodies used to detect collisions\r\n * @class\r\n * @protected\r\n */\r\nclass Body {\r\n\t/**\r\n\t * @constructor\r\n\t * @param {Number} [x = 0] The starting X coordinate\r\n\t * @param {Number} [y = 0] The starting Y coordinate\r\n\t * @param {Number} [padding = 0] The amount to pad the bounding volume when testing for potential collisions\r\n\t */\r\n\tconstructor(x = 0, y = 0, padding = 0) {\r\n\t\t/**\r\n\t\t * @desc The X coordinate of the body\r\n\t\t * @type {Number}\r\n\t\t */\r\n\t\tthis.x = x;\r\n\r\n\t\t/**\r\n\t\t * @desc The Y coordinate of the body\r\n\t\t * @type {Number}\r\n\t\t */\r\n\t\tthis.y = y;\r\n\r\n\t\t/**\r\n\t\t * @desc The amount to pad the bounding volume when testing for potential collisions\r\n\t\t * @type {Number}\r\n\t\t */\r\n\t\tthis.padding = padding;\r\n\r\n\t\t/** @private */\r\n\t\tthis._circle = false;\r\n\r\n\t\t/** @private */\r\n\t\tthis._polygon = false;\r\n\r\n\t\t/** @private */\r\n\t\tthis._point = false;\r\n\r\n\t\t/** @private */\r\n\t\tthis._bvh = null;\r\n\r\n\t\t/** @private */\r\n\t\tthis._bvh_parent = null;\r\n\r\n\t\t/** @private */\r\n\t\tthis._bvh_branch = false;\r\n\r\n\t\t/** @private */\r\n\t\tthis._bvh_padding = padding;\r\n\r\n\t\t/** @private */\r\n\t\tthis._bvh_min_x = 0;\r\n\r\n\t\t/** @private */\r\n\t\tthis._bvh_min_y = 0;\r\n\r\n\t\t/** @private */\r\n\t\tthis._bvh_max_x = 0;\r\n\r\n\t\t/** @private */\r\n\t\tthis._bvh_max_y = 0;\r\n\t}\r\n\r\n\t/**\r\n\t * Determines if the body is colliding with another body\r\n\t * @param {Circle|Polygon|Point} target The target body to test against\r\n\t * @param {Result} [result = null] A Result object on which to store information about the collision\r\n\t * @param {Boolean} [aabb = true] Set to false to skip the AABB test (useful if you use your own potential collision heuristic)\r\n\t * @returns {Boolean}\r\n\t */\r\n\tcollides(target, result = null, aabb = true) {\r\n\t\treturn Object(_SAT_mjs__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(this, target, result, aabb);\r\n\t}\r\n\r\n\t/**\r\n\t * Returns a list of potential collisions\r\n\t * @returns {Array}\r\n\t */\r\n\tpotentials() {\r\n\t\tconst bvh = this._bvh;\r\n\r\n\t\tif(bvh === null) {\r\n\t\t\tthrow new Error('Body does not belong to a collision system');\r\n\t\t}\r\n\r\n\t\treturn bvh.potentials(this);\r\n\t}\r\n\r\n\t/**\r\n\t * Removes the body from its current collision system\r\n\t */\r\n\tremove() {\r\n\t\tconst bvh = this._bvh;\r\n\r\n\t\tif(bvh) {\r\n\t\t\tbvh.remove(this, false);\r\n\t\t}\r\n\t}\r\n\r\n\t/**\r\n\t * Creates a {@link Result} used to collect the detailed results of a collision test\r\n\t */\r\n\tcreateResult() {\r\n\t\treturn new _Result_mjs__WEBPACK_IMPORTED_MODULE_0__[\"default\"]();\r\n\t}\r\n\r\n\t/**\r\n\t * Creates a Result used to collect the detailed results of a collision test\r\n\t */\r\n\tstatic createResult() {\r\n\t\treturn new _Result_mjs__WEBPACK_IMPORTED_MODULE_0__[\"default\"]();\r\n\t}\r\n};\r\n\n\n//# sourceURL=webpack:///./src/modules/Body.mjs?"); + +/***/ }), + +/***/ "./src/modules/Circle.mjs": +/*!********************************!*\ + !*** ./src/modules/Circle.mjs ***! + \********************************/ +/*! exports provided: default */ +/***/ (function(__webpack_module__, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return Circle; });\n/* harmony import */ var _Body_mjs__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./Body.mjs */ \"./src/modules/Body.mjs\");\n\r\n\r\n/**\r\n * A circle used to detect collisions\r\n * @class\r\n */\r\nclass Circle extends _Body_mjs__WEBPACK_IMPORTED_MODULE_0__[\"default\"] {\r\n\t/**\r\n\t * @constructor\r\n\t * @param {Number} [x = 0] The starting X coordinate\r\n\t * @param {Number} [y = 0] The starting Y coordinate\r\n\t * @param {Number} [radius = 0] The radius\r\n\t * @param {Number} [scale = 1] The scale\r\n\t * @param {Number} [padding = 0] The amount to pad the bounding volume when testing for potential collisions\r\n\t */\r\n\tconstructor(x = 0, y = 0, radius = 0, scale = 1, padding = 0) {\r\n\t\tsuper(x, y, padding);\r\n\r\n\t\t/**\r\n\t\t * @desc\r\n\t\t * @type {Number}\r\n\t\t */\r\n\t\tthis.radius = radius;\r\n\r\n\t\t/**\r\n\t\t * @desc\r\n\t\t * @type {Number}\r\n\t\t */\r\n\t\tthis.scale = scale;\r\n\t}\r\n\r\n\t/**\r\n\t * Draws the circle to a CanvasRenderingContext2D's current path\r\n\t * @param {CanvasRenderingContext2D} context The context to add the arc to\r\n\t */\r\n\tdraw(context) {\r\n\t\tconst x = this.x;\r\n\t\tconst y = this.y;\r\n\t\tconst radius = this.radius * this.scale;\r\n\r\n\t\tcontext.moveTo(x + radius, y);\r\n\t\tcontext.arc(x, y, radius, 0, Math.PI * 2);\r\n\t}\r\n};\r\n\n\n//# sourceURL=webpack:///./src/modules/Circle.mjs?"); + +/***/ }), + +/***/ "./src/modules/Point.mjs": +/*!*******************************!*\ + !*** ./src/modules/Point.mjs ***! + \*******************************/ +/*! exports provided: default */ +/***/ (function(__webpack_module__, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return Point; });\n/* harmony import */ var _Polygon_mjs__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./Polygon.mjs */ \"./src/modules/Polygon.mjs\");\n\r\n\r\n/**\r\n * A point used to detect collisions\r\n * @class\r\n */\r\nclass Point extends _Polygon_mjs__WEBPACK_IMPORTED_MODULE_0__[\"default\"] {\r\n\t/**\r\n\t * @constructor\r\n\t * @param {Number} [x = 0] The starting X coordinate\r\n\t * @param {Number} [y = 0] The starting Y coordinate\r\n\t * @param {Number} [padding = 0] The amount to pad the bounding volume when testing for potential collisions\r\n\t */\r\n\tconstructor(x = 0, y = 0, padding = 0) {\r\n\t\tsuper(x, y, [[0, 0]], 0, 1, 1, padding);\r\n\r\n\t\t/** @private */\r\n\t\tthis._point = true;\r\n\t}\r\n};\r\n\r\nPoint.prototype.setPoints = undefined;\r\n\n\n//# sourceURL=webpack:///./src/modules/Point.mjs?"); + +/***/ }), + +/***/ "./src/modules/Polygon.mjs": +/*!*********************************!*\ + !*** ./src/modules/Polygon.mjs ***! + \*********************************/ +/*! exports provided: default */ +/***/ (function(__webpack_module__, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return Polygon; });\n/* harmony import */ var _Body_mjs__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./Body.mjs */ \"./src/modules/Body.mjs\");\n\r\n\r\n/**\r\n * A polygon used to detect collisions\r\n * @class\r\n */\r\nclass Polygon extends _Body_mjs__WEBPACK_IMPORTED_MODULE_0__[\"default\"] {\r\n\t/**\r\n\t * @constructor\r\n\t * @param {Number} [x = 0] The starting X coordinate\r\n\t * @param {Number} [y = 0] The starting Y coordinate\r\n\t * @param {Array} [points = []] An array of coordinate pairs making up the polygon - [[x1, y1], [x2, y2], ...]\r\n\t * @param {Number} [angle = 0] The starting rotation in radians\r\n\t * @param {Number} [scale_x = 1] The starting scale along the X axis\r\n\t * @param {Number} [scale_y = 1] The starting scale long the Y axis\r\n\t * @param {Number} [padding = 0] The amount to pad the bounding volume when testing for potential collisions\r\n\t */\r\n\tconstructor(x = 0, y = 0, points = [], angle = 0, scale_x = 1, scale_y = 1, padding = 0) {\r\n\t\tsuper(x, y, padding);\r\n\r\n\t\t/**\r\n\t\t * @desc The angle of the body in radians\r\n\t\t * @type {Number}\r\n\t\t */\r\n\t\tthis.angle = angle;\r\n\r\n\t\t/**\r\n\t\t * @desc The scale of the body along the X axis\r\n\t\t * @type {Number}\r\n\t\t */\r\n\t\tthis.scale_x = scale_x;\r\n\r\n\t\t/**\r\n\t\t * @desc The scale of the body along the Y axis\r\n\t\t * @type {Number}\r\n\t\t */\r\n\t\tthis.scale_y = scale_y;\r\n\r\n\r\n\t\t/** @private */\r\n\t\tthis._polygon = true;\r\n\r\n\t\t/** @private */\r\n\t\tthis._x = x;\r\n\r\n\t\t/** @private */\r\n\t\tthis._y = y;\r\n\r\n\t\t/** @private */\r\n\t\tthis._angle = angle;\r\n\r\n\t\t/** @private */\r\n\t\tthis._scale_x = scale_x;\r\n\r\n\t\t/** @private */\r\n\t\tthis._scale_y = scale_y;\r\n\r\n\t\t/** @private */\r\n\t\tthis._min_x = 0;\r\n\r\n\t\t/** @private */\r\n\t\tthis._min_y = 0;\r\n\r\n\t\t/** @private */\r\n\t\tthis._max_x = 0;\r\n\r\n\t\t/** @private */\r\n\t\tthis._max_y = 0;\r\n\r\n\t\t/** @private */\r\n\t\tthis._points = null;\r\n\r\n\t\t/** @private */\r\n\t\tthis._coords = null;\r\n\r\n\t\t/** @private */\r\n\t\tthis._edges = null;\r\n\r\n\t\t/** @private */\r\n\t\tthis._normals = null;\r\n\r\n\t\t/** @private */\r\n\t\tthis._dirty_coords = true;\r\n\r\n\t\t/** @private */\r\n\t\tthis._dirty_normals = true;\r\n\r\n\t\tPolygon.prototype.setPoints.call(this, points);\r\n\t}\r\n\r\n\t/**\r\n\t * Draws the polygon to a CanvasRenderingContext2D's current path\r\n\t * @param {CanvasRenderingContext2D} context The context to add the shape to\r\n\t */\r\n\tdraw(context) {\r\n\t\tif(\r\n\t\t\tthis._dirty_coords ||\r\n\t\t\tthis.x !== this._x ||\r\n\t\t\tthis.y !== this._y ||\r\n\t\t\tthis.angle !== this._angle ||\r\n\t\t\tthis.scale_x !== this._scale_x ||\r\n\t\t\tthis.scale_y !== this._scale_y\r\n\t\t) {\r\n\t\t\tthis._calculateCoords();\r\n\t\t}\r\n\r\n\t\tconst coords = this._coords;\r\n\r\n\t\tif(coords.length === 2) {\r\n\t\t\tcontext.moveTo(coords[0], coords[1]);\r\n\t\t\tcontext.arc(coords[0], coords[1], 1, 0, Math.PI * 2);\r\n\t\t}\r\n\t\telse {\r\n\t\t\tcontext.moveTo(coords[0], coords[1]);\r\n\r\n\t\t\tfor(let i = 2; i < coords.length; i += 2) {\r\n\t\t\t\tcontext.lineTo(coords[i], coords[i + 1]);\r\n\t\t\t}\r\n\r\n\t\t\tif(coords.length > 4) {\r\n\t\t\t\tcontext.lineTo(coords[0], coords[1]);\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\t/**\r\n\t * Sets the points making up the polygon. It's important to use this function when changing the polygon's shape to ensure internal data is also updated.\r\n\t * @param {Array} new_points An array of coordinate pairs making up the polygon - [[x1, y1], [x2, y2], ...]\r\n\t */\r\n\tsetPoints(new_points) {\r\n\t\tconst count = new_points.length;\r\n\r\n\t\tthis._points = new Float64Array(count * 2);\r\n\t\tthis._coords = new Float64Array(count * 2);\r\n\t\tthis._edges = new Float64Array(count * 2);\r\n\t\tthis._normals = new Float64Array(count * 2);\r\n\r\n\t\tconst points = this._points;\r\n\r\n\t\tfor(let i = 0, ix = 0, iy = 1; i < count; ++i, ix += 2, iy += 2) {\r\n\t\t\tconst new_point = new_points[i];\r\n\r\n\t\t\tpoints[ix] = new_point[0];\r\n\t\t\tpoints[iy] = new_point[1];\r\n\t\t}\r\n\r\n\t\tthis._dirty_coords = true;\r\n\t}\r\n\r\n\t/**\r\n\t * Calculates and caches the polygon's world coordinates based on its points, angle, and scale\r\n\t */\r\n\t_calculateCoords() {\r\n\t\tconst x = this.x;\r\n\t\tconst y = this.y;\r\n\t\tconst angle = this.angle;\r\n\t\tconst scale_x = this.scale_x;\r\n\t\tconst scale_y = this.scale_y;\r\n\t\tconst points = this._points;\r\n\t\tconst coords = this._coords;\r\n\t\tconst count = points.length;\r\n\r\n\t\tlet min_x;\r\n\t\tlet max_x;\r\n\t\tlet min_y;\r\n\t\tlet max_y;\r\n\r\n\t\tfor(let ix = 0, iy = 1; ix < count; ix += 2, iy += 2) {\r\n\t\t\tlet coord_x = points[ix] * scale_x;\r\n\t\t\tlet coord_y = points[iy] * scale_y;\r\n\r\n\t\t\tif(angle) {\r\n\t\t\t\tconst cos = Math.cos(angle);\r\n\t\t\t\tconst sin = Math.sin(angle);\r\n\t\t\t\tconst tmp_x = coord_x;\r\n\t\t\t\tconst tmp_y = coord_y;\r\n\r\n\t\t\t\tcoord_x = tmp_x * cos - tmp_y * sin;\r\n\t\t\t\tcoord_y = tmp_x * sin + tmp_y * cos;\r\n\t\t\t}\r\n\r\n\t\t\tcoord_x += x;\r\n\t\t\tcoord_y += y;\r\n\r\n\t\t\tcoords[ix] = coord_x;\r\n\t\t\tcoords[iy] = coord_y;\r\n\r\n\t\t\tif(ix === 0) {\r\n\t\t\t\tmin_x = max_x = coord_x;\r\n\t\t\t\tmin_y = max_y = coord_y;\r\n\t\t\t}\r\n\t\t\telse {\r\n\t\t\t\tif(coord_x < min_x) {\r\n\t\t\t\t\tmin_x = coord_x;\r\n\t\t\t\t}\r\n\t\t\t\telse if(coord_x > max_x) {\r\n\t\t\t\t\tmax_x = coord_x;\r\n\t\t\t\t}\r\n\r\n\t\t\t\tif(coord_y < min_y) {\r\n\t\t\t\t\tmin_y = coord_y;\r\n\t\t\t\t}\r\n\t\t\t\telse if(coord_y > max_y) {\r\n\t\t\t\t\tmax_y = coord_y;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tthis._x = x;\r\n\t\tthis._y = y;\r\n\t\tthis._angle = angle;\r\n\t\tthis._scale_x = scale_x;\r\n\t\tthis._scale_y = scale_y;\r\n\t\tthis._min_x = min_x;\r\n\t\tthis._min_y = min_y;\r\n\t\tthis._max_x = max_x;\r\n\t\tthis._max_y = max_y;\r\n\t\tthis._dirty_coords = false;\r\n\t\tthis._dirty_normals = true;\r\n\t}\r\n\r\n\t/**\r\n\t * Calculates the normals and edges of the polygon's sides\r\n\t */\r\n\t_calculateNormals() {\r\n\t\tconst coords = this._coords;\r\n\t\tconst edges = this._edges;\r\n\t\tconst normals = this._normals;\r\n\t\tconst count = coords.length;\r\n\r\n\t\tfor(let ix = 0, iy = 1; ix < count; ix += 2, iy += 2) {\r\n\t\t\tconst next = ix + 2 < count ? ix + 2 : 0;\r\n\t\t\tconst x = coords[next] - coords[ix];\r\n\t\t\tconst y = coords[next + 1] - coords[iy];\r\n\t\t\tconst length = x || y ? Math.sqrt(x * x + y * y) : 0;\r\n\r\n\t\t\tedges[ix] = x;\r\n\t\t\tedges[iy] = y;\r\n\t\t\tnormals[ix] = length ? y / length : 0;\r\n\t\t\tnormals[iy] = length ? -x / length : 0;\r\n\t\t}\r\n\r\n\t\tthis._dirty_normals = false;\r\n\t}\r\n};\r\n\n\n//# sourceURL=webpack:///./src/modules/Polygon.mjs?"); + +/***/ }), + +/***/ "./src/modules/Result.mjs": +/*!********************************!*\ + !*** ./src/modules/Result.mjs ***! + \********************************/ +/*! exports provided: default */ +/***/ (function(__webpack_module__, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return Result; });\n/**\r\n * An object used to collect the detailed results of a collision test\r\n *\r\n * > **Note:** It is highly recommended you recycle the same Result object if possible in order to avoid wasting memory\r\n * @class\r\n */\r\nclass Result {\r\n\t/**\r\n\t * @constructor\r\n\t */\r\n\tconstructor() {\r\n\t\t/**\r\n\t\t * @desc True if a collision was detected\r\n\t\t * @type {Boolean}\r\n\t\t */\r\n\t\tthis.collision = false;\r\n\r\n\t\t/**\r\n\t\t * @desc The source body tested\r\n\t\t * @type {Circle|Polygon|Point}\r\n\t\t */\r\n\t\tthis.a = null;\r\n\r\n\t\t/**\r\n\t\t * @desc The target body tested against\r\n\t\t * @type {Circle|Polygon|Point}\r\n\t\t */\r\n\t\tthis.b = null;\r\n\r\n\t\t/**\r\n\t\t * @desc True if A is completely contained within B\r\n\t\t * @type {Boolean}\r\n\t\t */\r\n\t\tthis.a_in_b = false;\r\n\r\n\t\t/**\r\n\t\t * @desc True if B is completely contained within A\r\n\t\t * @type {Boolean}\r\n\t\t */\r\n\t\tthis.b_in_a = false;\r\n\r\n\t\t/**\r\n\t\t * @desc The magnitude of the shortest axis of overlap\r\n\t\t * @type {Number}\r\n\t\t */\r\n\t\tthis.overlap = 0;\r\n\r\n\t\t/**\r\n\t\t * @desc The X direction of the shortest axis of overlap\r\n\t\t * @type {Number}\r\n\t\t */\r\n\t\tthis.overlap_x = 0;\r\n\r\n\t\t/**\r\n\t\t * @desc The Y direction of the shortest axis of overlap\r\n\t\t * @type {Number}\r\n\t\t */\r\n\t\tthis.overlap_y = 0;\r\n\t}\r\n};\r\n\n\n//# sourceURL=webpack:///./src/modules/Result.mjs?"); + +/***/ }), + +/***/ "./src/modules/SAT.mjs": +/*!*****************************!*\ + !*** ./src/modules/SAT.mjs ***! + \*****************************/ +/*! exports provided: default */ +/***/ (function(__webpack_module__, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return SAT; });\n/**\r\n * Determines if two bodies are colliding using the Separating Axis Theorem\r\n * @private\r\n * @param {Circle|Polygon|Point} a The source body to test\r\n * @param {Circle|Polygon|Point} b The target body to test against\r\n * @param {Result} [result = null] A Result object on which to store information about the collision\r\n * @param {Boolean} [aabb = true] Set to false to skip the AABB test (useful if you use your own collision heuristic)\r\n * @returns {Boolean}\r\n */\r\nfunction SAT(a, b, result = null, aabb = true) {\r\n\tconst a_polygon = a._polygon;\r\n\tconst b_polygon = b._polygon;\r\n\r\n\tlet collision = false;\r\n\r\n\tif(result) {\r\n\t\tresult.a = a;\r\n\t\tresult.b = b;\r\n\t\tresult.a_in_b = true;\r\n\t\tresult.b_in_a = true;\r\n\t\tresult.overlap = null;\r\n\t\tresult.overlap_x = 0;\r\n\t\tresult.overlap_y = 0;\r\n\t}\r\n\r\n\tif(a_polygon) {\r\n\t\tif(\r\n\t\t\ta._dirty_coords ||\r\n\t\t\ta.x !== a._x ||\r\n\t\t\ta.y !== a._y ||\r\n\t\t\ta.angle !== a._angle ||\r\n\t\t\ta.scale_x !== a._scale_x ||\r\n\t\t\ta.scale_y !== a._scale_y\r\n\t\t) {\r\n\t\t\ta._calculateCoords();\r\n\t\t}\r\n\t}\r\n\r\n\tif(b_polygon) {\r\n\t\tif(\r\n\t\t\tb._dirty_coords ||\r\n\t\t\tb.x !== b._x ||\r\n\t\t\tb.y !== b._y ||\r\n\t\t\tb.angle !== b._angle ||\r\n\t\t\tb.scale_x !== b._scale_x ||\r\n\t\t\tb.scale_y !== b._scale_y\r\n\t\t) {\r\n\t\t\tb._calculateCoords();\r\n\t\t}\r\n\t}\r\n\r\n\tif(!aabb || aabbAABB(a, b)) {\r\n\t\tif(a_polygon && a._dirty_normals) {\r\n\t\t\ta._calculateNormals();\r\n\t\t}\r\n\r\n\t\tif(b_polygon && b._dirty_normals) {\r\n\t\t\tb._calculateNormals();\r\n\t\t}\r\n\r\n\t\tcollision = (\r\n\t\t\ta_polygon && b_polygon ? polygonPolygon(a, b, result) :\r\n\t\t\ta_polygon ? polygonCircle(a, b, result, false) :\r\n\t\t\tb_polygon ? polygonCircle(b, a, result, true) :\r\n\t\t\tcircleCircle(a, b, result)\r\n\t\t);\r\n\t}\r\n\r\n\tif(result) {\r\n\t\tresult.collision = collision;\r\n\t}\r\n\r\n\treturn collision;\r\n};\r\n\r\n/**\r\n * Determines if two bodies' axis aligned bounding boxes are colliding\r\n * @param {Circle|Polygon|Point} a The source body to test\r\n * @param {Circle|Polygon|Point} b The target body to test against\r\n */\r\nfunction aabbAABB(a, b) {\r\n\tconst a_polygon = a._polygon;\r\n\tconst a_x = a_polygon ? 0 : a.x;\r\n\tconst a_y = a_polygon ? 0 : a.y;\r\n\tconst a_radius = a_polygon ? 0 : a.radius * a.scale;\r\n\tconst a_min_x = a_polygon ? a._min_x : a_x - a_radius;\r\n\tconst a_min_y = a_polygon ? a._min_y : a_y - a_radius;\r\n\tconst a_max_x = a_polygon ? a._max_x : a_x + a_radius;\r\n\tconst a_max_y = a_polygon ? a._max_y : a_y + a_radius;\r\n\r\n\tconst b_polygon = b._polygon;\r\n\tconst b_x = b_polygon ? 0 : b.x;\r\n\tconst b_y = b_polygon ? 0 : b.y;\r\n\tconst b_radius = b_polygon ? 0 : b.radius * b.scale;\r\n\tconst b_min_x = b_polygon ? b._min_x : b_x - b_radius;\r\n\tconst b_min_y = b_polygon ? b._min_y : b_y - b_radius;\r\n\tconst b_max_x = b_polygon ? b._max_x : b_x + b_radius;\r\n\tconst b_max_y = b_polygon ? b._max_y : b_y + b_radius;\r\n\r\n\treturn a_min_x < b_max_x && a_min_y < b_max_y && a_max_x > b_min_x && a_max_y > b_min_y;\r\n}\r\n\r\n/**\r\n * Determines if two polygons are colliding\r\n * @param {Polygon} a The source polygon to test\r\n * @param {Polygon} b The target polygon to test against\r\n * @param {Result} [result = null] A Result object on which to store information about the collision\r\n * @returns {Boolean}\r\n */\r\nfunction polygonPolygon(a, b, result = null) {\r\n\tconst a_count = a._coords.length;\r\n\tconst b_count = b._coords.length;\r\n\r\n\t// Handle points specially\r\n\tif(a_count === 2 && b_count === 2) {\r\n\t\tconst a_coords = a._coords;\r\n\t\tconst b_coords = b._coords;\r\n\r\n\t\tif(result) {\r\n\t\t\tresult.overlap = 0;\r\n\t\t}\r\n\r\n\t\treturn a_coords[0] === b_coords[0] && a_coords[1] === b_coords[1];\r\n\t}\r\n\r\n\tconst a_coords = a._coords;\r\n\tconst b_coords = b._coords;\r\n\tconst a_normals = a._normals;\r\n\tconst b_normals = b._normals;\r\n\r\n\tif(a_count > 2) {\r\n\t\tfor(let ix = 0, iy = 1; ix < a_count; ix += 2, iy += 2) {\r\n\t\t\tif(separatingAxis(a_coords, b_coords, a_normals[ix], a_normals[iy], result)) {\r\n\t\t\t\treturn false;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\tif(b_count > 2) {\r\n\t\tfor(let ix = 0, iy = 1; ix < b_count; ix += 2, iy += 2) {\r\n\t\t\tif(separatingAxis(a_coords, b_coords, b_normals[ix], b_normals[iy], result)) {\r\n\t\t\t\treturn false;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\treturn true;\r\n}\r\n\r\n/**\r\n * Determines if a polygon and a circle are colliding\r\n * @param {Polygon} a The source polygon to test\r\n * @param {Circle} b The target circle to test against\r\n * @param {Result} [result = null] A Result object on which to store information about the collision\r\n * @param {Boolean} [reverse = false] Set to true to reverse a and b in the result parameter when testing circle->polygon instead of polygon->circle\r\n * @returns {Boolean}\r\n */\r\nfunction polygonCircle(a, b, result = null, reverse = false) {\r\n\tconst a_coords = a._coords;\r\n\tconst a_edges = a._edges;\r\n\tconst a_normals = a._normals;\r\n\tconst b_x = b.x;\r\n\tconst b_y = b.y;\r\n\tconst b_radius = b.radius * b.scale;\r\n\tconst b_radius2 = b_radius * 2;\r\n\tconst radius_squared = b_radius * b_radius;\r\n\tconst count = a_coords.length;\r\n\r\n\tlet a_in_b = true;\r\n\tlet b_in_a = true;\r\n\tlet overlap = null;\r\n\tlet overlap_x = 0;\r\n\tlet overlap_y = 0;\r\n\r\n\t// Handle points specially\r\n\tif(count === 2) {\r\n\t\tconst coord_x = b_x - a_coords[0];\r\n\t\tconst coord_y = b_y - a_coords[1];\r\n\t\tconst length_squared = coord_x * coord_x + coord_y * coord_y;\r\n\r\n\t\tif(length_squared > radius_squared) {\r\n\t\t\treturn false;\r\n\t\t}\r\n\r\n\t\tif(result) {\r\n\t\t\tconst length = Math.sqrt(length_squared);\r\n\r\n\t\t\toverlap = b_radius - length;\r\n\t\t\toverlap_x = coord_x / length;\r\n\t\t\toverlap_y = coord_y / length;\r\n\t\t\tb_in_a = false;\r\n\t\t}\r\n\t}\r\n\telse {\r\n\t\tfor(let ix = 0, iy = 1; ix < count; ix += 2, iy += 2) {\r\n\t\t\tconst coord_x = b_x - a_coords[ix];\r\n\t\t\tconst coord_y = b_y - a_coords[iy];\r\n\t\t\tconst edge_x = a_edges[ix];\r\n\t\t\tconst edge_y = a_edges[iy];\r\n\t\t\tconst dot = coord_x * edge_x + coord_y * edge_y;\r\n\t\t\tconst region = dot < 0 ? -1 : dot > edge_x * edge_x + edge_y * edge_y ? 1 : 0;\r\n\r\n\t\t\tlet tmp_overlapping = false;\r\n\t\t\tlet tmp_overlap = 0;\r\n\t\t\tlet tmp_overlap_x = 0;\r\n\t\t\tlet tmp_overlap_y = 0;\r\n\r\n\t\t\tif(result && a_in_b && coord_x * coord_x + coord_y * coord_y > radius_squared) {\r\n\t\t\t\ta_in_b = false;\r\n\t\t\t}\r\n\r\n\t\t\tif(region) {\r\n\t\t\t\tconst left = region === -1;\r\n\t\t\t\tconst other_x = left ? (ix === 0 ? count - 2 : ix - 2) : (ix === count - 2 ? 0 : ix + 2);\r\n\t\t\t\tconst other_y = other_x + 1;\r\n\t\t\t\tconst coord2_x = b_x - a_coords[other_x];\r\n\t\t\t\tconst coord2_y = b_y - a_coords[other_y];\r\n\t\t\t\tconst edge2_x = a_edges[other_x];\r\n\t\t\t\tconst edge2_y = a_edges[other_y];\r\n\t\t\t\tconst dot2 = coord2_x * edge2_x + coord2_y * edge2_y;\r\n\t\t\t\tconst region2 = dot2 < 0 ? -1 : dot2 > edge2_x * edge2_x + edge2_y * edge2_y ? 1 : 0;\r\n\r\n\t\t\t\tif(region2 === -region) {\r\n\t\t\t\t\tconst target_x = left ? coord_x : coord2_x;\r\n\t\t\t\t\tconst target_y = left ? coord_y : coord2_y;\r\n\t\t\t\t\tconst length_squared = target_x * target_x + target_y * target_y;\r\n\r\n\t\t\t\t\tif(length_squared > radius_squared) {\r\n\t\t\t\t\t\treturn false;\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\tif(result) {\r\n\t\t\t\t\t\tconst length = Math.sqrt(length_squared);\r\n\r\n\t\t\t\t\t\ttmp_overlapping = true;\r\n\t\t\t\t\t\ttmp_overlap = b_radius - length;\r\n\t\t\t\t\t\ttmp_overlap_x = target_x / length;\r\n\t\t\t\t\t\ttmp_overlap_y = target_y / length;\r\n\t\t\t\t\t\tb_in_a = false;\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\telse {\r\n\t\t\t\tconst normal_x = a_normals[ix];\r\n\t\t\t\tconst normal_y = a_normals[iy];\r\n\t\t\t\tconst length = coord_x * normal_x + coord_y * normal_y;\r\n\t\t\t\tconst absolute_length = length < 0 ? -length : length;\r\n\r\n\t\t\t\tif(length > 0 && absolute_length > b_radius) {\r\n\t\t\t\t\treturn false;\r\n\t\t\t\t}\r\n\r\n\t\t\t\tif(result) {\r\n\t\t\t\t\ttmp_overlapping = true;\r\n\t\t\t\t\ttmp_overlap = b_radius - length;\r\n\t\t\t\t\ttmp_overlap_x = normal_x;\r\n\t\t\t\t\ttmp_overlap_y = normal_y;\r\n\r\n\t\t\t\t\tif(b_in_a && length >= 0 || tmp_overlap < b_radius2) {\r\n\t\t\t\t\t\tb_in_a = false;\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\tif(tmp_overlapping && (overlap === null || overlap > tmp_overlap)) {\r\n\t\t\t\toverlap = tmp_overlap;\r\n\t\t\t\toverlap_x = tmp_overlap_x;\r\n\t\t\t\toverlap_y = tmp_overlap_y;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\tif(result) {\r\n\t\tresult.a_in_b = reverse ? b_in_a : a_in_b;\r\n\t\tresult.b_in_a = reverse ? a_in_b : b_in_a;\r\n\t\tresult.overlap = overlap;\r\n\t\tresult.overlap_x = reverse ? -overlap_x : overlap_x;\r\n\t\tresult.overlap_y = reverse ? -overlap_y : overlap_y;\r\n\t}\r\n\r\n\treturn true;\r\n}\r\n\r\n/**\r\n * Determines if two circles are colliding\r\n * @param {Circle} a The source circle to test\r\n * @param {Circle} b The target circle to test against\r\n * @param {Result} [result = null] A Result object on which to store information about the collision\r\n * @returns {Boolean}\r\n */\r\nfunction circleCircle(a, b, result = null) {\r\n\tconst a_radius = a.radius * a.scale;\r\n\tconst b_radius = b.radius * b.scale;\r\n\tconst difference_x = b.x - a.x;\r\n\tconst difference_y = b.y - a.y;\r\n\tconst radius_sum = a_radius + b_radius;\r\n\tconst length_squared = difference_x * difference_x + difference_y * difference_y;\r\n\r\n\tif(length_squared > radius_sum * radius_sum) {\r\n\t\treturn false;\r\n\t}\r\n\r\n\tif(result) {\r\n\t\tconst length = Math.sqrt(length_squared);\r\n\r\n\t\tresult.a_in_b = a_radius <= b_radius && length <= b_radius - a_radius;\r\n\t\tresult.b_in_a = b_radius <= a_radius && length <= a_radius - b_radius;\r\n\t\tresult.overlap = radius_sum - length;\r\n\t\tresult.overlap_x = difference_x / length;\r\n\t\tresult.overlap_y = difference_y / length;\r\n\t}\r\n\r\n\treturn true;\r\n}\r\n\r\n/**\r\n * Determines if two polygons are separated by an axis\r\n * @param {Array} a_coords The coordinates of the polygon to test\r\n * @param {Array} b_coords The coordinates of the polygon to test against\r\n * @param {Number} x The X direction of the axis\r\n * @param {Number} y The Y direction of the axis\r\n * @param {Result} [result = null] A Result object on which to store information about the collision\r\n * @returns {Boolean}\r\n */\r\nfunction separatingAxis(a_coords, b_coords, x, y, result = null) {\r\n\tconst a_count = a_coords.length;\r\n\tconst b_count = b_coords.length;\r\n\r\n\tif(!a_count || !b_count) {\r\n\t\treturn true;\r\n\t}\r\n\r\n\tlet a_start = null;\r\n\tlet a_end = null;\r\n\tlet b_start = null;\r\n\tlet b_end = null;\r\n\r\n\tfor(let ix = 0, iy = 1; ix < a_count; ix += 2, iy += 2) {\r\n\t\tconst dot = a_coords[ix] * x + a_coords[iy] * y;\r\n\r\n\t\tif(a_start === null || a_start > dot) {\r\n\t\t\ta_start = dot;\r\n\t\t}\r\n\r\n\t\tif(a_end === null || a_end < dot) {\r\n\t\t\ta_end = dot;\r\n\t\t}\r\n\t}\r\n\r\n\tfor(let ix = 0, iy = 1; ix < b_count; ix += 2, iy += 2) {\r\n\t\tconst dot = b_coords[ix] * x + b_coords[iy] * y;\r\n\r\n\t\tif(b_start === null || b_start > dot) {\r\n\t\t\tb_start = dot;\r\n\t\t}\r\n\r\n\t\tif(b_end === null || b_end < dot) {\r\n\t\t\tb_end = dot;\r\n\t\t}\r\n\t}\r\n\r\n\tif(a_start > b_end || a_end < b_start) {\r\n\t\treturn true;\r\n\t}\r\n\r\n\tif(result) {\r\n\t\tlet overlap = 0;\r\n\r\n\t\tif(a_start < b_start) {\r\n\t\t\tresult.a_in_b = false;\r\n\r\n\t\t\tif(a_end < b_end) {\r\n\t\t\t\toverlap = a_end - b_start;\r\n\t\t\t\tresult.b_in_a = false;\r\n\t\t\t}\r\n\t\t\telse {\r\n\t\t\t\tconst option1 = a_end - b_start;\r\n\t\t\t\tconst option2 = b_end - a_start;\r\n\r\n\t\t\t\toverlap = option1 < option2 ? option1 : -option2;\r\n\t\t\t}\r\n\t\t}\r\n\t\telse {\r\n\t\t\tresult.b_in_a = false;\r\n\r\n\t\t\tif(a_end > b_end) {\r\n\t\t\t\toverlap = a_start - b_end;\r\n\t\t\t\tresult.a_in_b = false;\r\n\t\t\t}\r\n\t\t\telse {\r\n\t\t\t\tconst option1 = a_end - b_start;\r\n\t\t\t\tconst option2 = b_end - a_start;\r\n\r\n\t\t\t\toverlap = option1 < option2 ? option1 : -option2;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tconst current_overlap = result.overlap;\r\n\t\tconst absolute_overlap = overlap < 0 ? -overlap : overlap;\r\n\r\n\t\tif(current_overlap === null || current_overlap > absolute_overlap) {\r\n\t\t\tconst sign = overlap < 0 ? -1 : 1;\r\n\r\n\t\t\tresult.overlap = absolute_overlap;\r\n\t\t\tresult.overlap_x = x * sign;\r\n\t\t\tresult.overlap_y = y * sign;\r\n\t\t}\r\n\t}\r\n\r\n\treturn false;\r\n}\r\n\n\n//# sourceURL=webpack:///./src/modules/SAT.mjs?"); + +/***/ }) + +/******/ }); \ No newline at end of file diff --git a/frontend/assets/scripts/modules/Collisions.js.meta b/frontend/assets/scripts/modules/Collisions.js.meta new file mode 100644 index 0000000..657c759 --- /dev/null +++ b/frontend/assets/scripts/modules/Collisions.js.meta @@ -0,0 +1,9 @@ +{ + "ver": "1.0.5", + "uuid": "da0a517f-5c74-4fc0-ba89-dbcee184b13e", + "isPlugin": false, + "loadPluginInWeb": true, + "loadPluginInNative": true, + "loadPluginInEditor": false, + "subMetas": {} +} \ No newline at end of file diff --git a/frontend/assets/scripts/modules/room_downsync_frame_proto_bundle.forcemsg.js b/frontend/assets/scripts/modules/room_downsync_frame_proto_bundle.forcemsg.js index 6a8d59e..bd97e12 100644 --- a/frontend/assets/scripts/modules/room_downsync_frame_proto_bundle.forcemsg.js +++ b/frontend/assets/scripts/modules/room_downsync_frame_proto_bundle.forcemsg.js @@ -4343,7 +4343,6 @@ $root.treasurehunterx = (function() { * @property {Object.|null} [traps] RoomDownsyncFrame traps * @property {Object.|null} [bullets] RoomDownsyncFrame bullets * @property {Object.|null} [speedShoes] RoomDownsyncFrame speedShoes - * @property {Object.|null} [pumpkin] RoomDownsyncFrame pumpkin * @property {Object.|null} [guardTowers] RoomDownsyncFrame guardTowers * @property {Object.|null} [playerMetas] RoomDownsyncFrame playerMetas */ @@ -4362,7 +4361,6 @@ $root.treasurehunterx = (function() { this.traps = {}; this.bullets = {}; this.speedShoes = {}; - this.pumpkin = {}; this.guardTowers = {}; this.playerMetas = {}; if (properties) @@ -4443,14 +4441,6 @@ $root.treasurehunterx = (function() { */ RoomDownsyncFrame.prototype.speedShoes = $util.emptyObject; - /** - * RoomDownsyncFrame pumpkin. - * @member {Object.} pumpkin - * @memberof treasurehunterx.RoomDownsyncFrame - * @instance - */ - RoomDownsyncFrame.prototype.pumpkin = $util.emptyObject; - /** * RoomDownsyncFrame guardTowers. * @member {Object.} 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]); $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")) 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(); } if (message.playerMetas != null && Object.hasOwnProperty.call(message, "playerMetas")) 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(); } return writer; @@ -4705,29 +4690,6 @@ $root.treasurehunterx = (function() { break; } 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) message.guardTowers = {}; var end2 = reader.uint32() + reader.pos; @@ -4750,7 +4712,7 @@ $root.treasurehunterx = (function() { message.guardTowers[key] = value; break; } - case 12: { + case 11: { if (message.playerMetas === $util.emptyObject) message.playerMetas = {}; 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 (!$util.isObject(message.guardTowers)) return "guardTowers: object expected"; @@ -5019,16 +4967,6 @@ $root.treasurehunterx = (function() { 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 (typeof object.guardTowers !== "object") throw TypeError(".treasurehunterx.RoomDownsyncFrame.guardTowers: object expected"); @@ -5071,7 +5009,6 @@ $root.treasurehunterx = (function() { object.traps = {}; object.bullets = {}; object.speedShoes = {}; - object.pumpkin = {}; object.guardTowers = {}; object.playerMetas = {}; } @@ -5129,11 +5066,6 @@ $root.treasurehunterx = (function() { for (var j = 0; j < keys2.length; ++j) 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) { object.guardTowers = {}; for (var j = 0; j < keys2.length; ++j) diff --git a/frontend/settings/project.json b/frontend/settings/project.json index 65839b6..b5a6c89 100644 --- a/frontend/settings/project.json +++ b/frontend/settings/project.json @@ -33,20 +33,21 @@ "design-resolution-height": 640, "design-resolution-width": 960, "excluded-modules": [ - "Spine Skeleton", + "Collider", "DragonBones", - "RichText", + "Geom Utils", + "Mesh", "MotionStreak", + "Physics", "PageView", "PageViewIndicator", + "RichText", "Slider", + "Spine Skeleton", + "StudioComponent", "VideoPlayer", "WebView", - "Physics", - "StudioComponent", "3D", - "Mesh", - "Geom Utils", "3D Primitive" ], "facebook": { @@ -74,7 +75,7 @@ "height": 640, "width": 960 }, - "start-scene": "current", - "use-customize-simulator": false, - "use-project-simulator-setting": false + "use-customize-simulator": true, + "use-project-simulator-setting": false, + "start-scene": "current" }