Merge pull request #16 from genxium/gopherjs

Employed Gopherjs into unified game dynamics codegen.
This commit is contained in:
Wing 2022-12-29 15:11:37 +08:00 committed by GitHub
commit b34fa79aeb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
57 changed files with 5275 additions and 2472 deletions

2
.gitignore vendored
View File

@ -132,3 +132,5 @@ gradle/*
*.project
*~
jsexport/jsexport.js*
battle_srv/room_*.txt

View File

@ -27,7 +27,7 @@ _(how rollback-and-chase in this project roughly works)_
## 1.1 Tools to install
### Backend
- [Command Line Tools for Xcode](https://developer.apple.com/download/all/?q=command%20line%20tools) (on OSX) or [TDM-GCC](https://jmeubank.github.io/tdm-gcc/download/) (on Windows) (a `make` executable mandatory)
- [Golang1.19.1](https://golang.org/dl/) (mandatory, in China please try a mirror site like [that of ustc](https://mirrors.ustc.edu.cn/golang/))
- [Golang1.18.6](https://golang.org/dl/) (brought down to 1.18 for GopherJs support, mandatory, in China please try a mirror site like [that of ustc](https://mirrors.ustc.edu.cn/golang/))
- [MySQL 5.7](https://dev.mysql.com/downloads/windows/installer/5.7.html) (mandatory, for OSX not all versions of 5.7 can be found thus 5.7.24 is recommended)
- [Redis 3.0.503 or above](https://redis.io/download/) (mandatory)
- [skeema](https://www.skeema.io/) (optional, only for convenient MySQL schema provisioning)

View File

@ -1,6 +1,6 @@
module battle_srv
go 1.19
go 1.18
require (
github.com/Masterminds/squirrel v0.0.0-20180815162352-8a7e65843414
@ -16,12 +16,13 @@ require (
github.com/jmoiron/sqlx v0.0.0-20180614180643-0dae4fefe7c0
github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e
github.com/robfig/cron v0.0.0-20180505203441-b41be1df6967
github.com/solarlune/resolv v0.5.1
github.com/thoas/go-funk v0.0.0-20180716193722-1060394a7713
go.uber.org/zap v1.9.1
google.golang.org/protobuf v1.28.1
dnmshared v0.0.0
jsexport v0.0.0
resolv v0.0.0
)
require (
@ -48,4 +49,6 @@ require (
replace (
dnmshared => ../dnmshared
jsexport => ../jsexport
resolv => ../resolv_tailored
)

View File

@ -1,9 +0,0 @@
package models
import (
. "dnmshared/sharedprotos"
)
type Barrier struct {
Boundary *Polygon2D
}

View File

@ -1,17 +1,125 @@
package models
import (
. "battle_srv/protos"
pb "battle_srv/protos"
"jsexport/battle"
)
func toPbPlayers(modelInstances map[int32]*Player, withMetaInfo bool) map[int32]*PlayerDownsync {
toRet := make(map[int32]*PlayerDownsync, 0)
func toPbRoomDownsyncFrame(rdf *battle.RoomDownsyncFrame) *pb.RoomDownsyncFrame {
if nil == rdf {
return nil
}
ret := &pb.RoomDownsyncFrame{
Id: rdf.Id,
PlayersArr: make([]*pb.PlayerDownsync, len(rdf.PlayersArr), len(rdf.PlayersArr)),
MeleeBullets: make([]*pb.MeleeBullet, len(rdf.MeleeBullets), len(rdf.MeleeBullets)),
CountdownNanos: rdf.CountdownNanos,
BackendUnconfirmedMask: rdf.BackendUnconfirmedMask,
ShouldForceResync: rdf.ShouldForceResync,
PlayerOpPatternToSkillId: make(map[int32]int32),
}
for i, last := range rdf.PlayersArr {
pbPlayer := &pb.PlayerDownsync{
Id: last.Id,
VirtualGridX: last.VirtualGridX,
VirtualGridY: last.VirtualGridY,
DirX: last.DirX,
DirY: last.DirY,
VelX: last.VelX,
VelY: last.VelY,
Speed: last.Speed,
BattleState: last.BattleState,
CharacterState: last.CharacterState,
InAir: last.InAir,
JoinIndex: last.JoinIndex,
ColliderRadius: last.ColliderRadius,
Score: last.Score,
FramesToRecover: last.FramesToRecover,
Hp: last.Hp,
MaxHp: last.MaxHp,
Removed: last.Removed,
}
ret.PlayersArr[i] = pbPlayer
}
for i, last := range rdf.MeleeBullets {
pbBullet := &pb.MeleeBullet{
BattleLocalId: last.BattleLocalId,
StartupFrames: last.StartupFrames,
ActiveFrames: last.ActiveFrames,
RecoveryFrames: last.RecoveryFrames,
RecoveryFramesOnBlock: last.RecoveryFramesOnBlock,
RecoveryFramesOnHit: last.RecoveryFramesOnHit,
HitboxOffset: last.HitboxOffset,
HitStunFrames: last.HitStunFrames,
BlockStunFrames: last.BlockStunFrames,
Pushback: last.Pushback,
ReleaseTriggerType: last.ReleaseTriggerType,
Damage: last.Damage,
SelfMoveforwardX: last.SelfMoveforwardX,
SelfMoveforwardY: last.SelfMoveforwardY,
HitboxSizeX: last.HitboxSizeX,
HitboxSizeY: last.HitboxSizeY,
OffenderJoinIndex: last.OffenderJoinIndex,
OffenderPlayerId: last.OffenderPlayerId,
}
ret.MeleeBullets[i] = pbBullet
}
for i, last := range rdf.PlayerOpPatternToSkillId {
ret.PlayerOpPatternToSkillId[int32(i)] = int32(last)
}
return ret
}
func toPbPlayers(modelInstances map[int32]*Player, withMetaInfo bool) []*pb.PlayerDownsync {
toRet := make([]*pb.PlayerDownsync, len(modelInstances), len(modelInstances))
if nil == modelInstances {
return toRet
}
for k, last := range modelInstances {
toRet[k] = &PlayerDownsync{
for _, last := range modelInstances {
pbPlayer := &pb.PlayerDownsync{
Id: last.Id,
VirtualGridX: last.VirtualGridX,
VirtualGridY: last.VirtualGridY,
DirX: last.DirX,
DirY: last.DirY,
VelX: last.VelX,
VelY: last.VelY,
Speed: last.Speed,
BattleState: last.BattleState,
CharacterState: last.CharacterState,
InAir: last.InAir,
JoinIndex: last.JoinIndex,
ColliderRadius: last.ColliderRadius,
Score: last.Score,
Removed: last.Removed,
FramesToRecover: last.FramesToRecover,
}
if withMetaInfo {
pbPlayer.Name = last.Name
pbPlayer.DisplayName = last.DisplayName
pbPlayer.Avatar = last.Avatar
}
toRet[last.JoinIndex-1] = pbPlayer
}
return toRet
}
func toJsPlayers(modelInstances map[int32]*Player) []*battle.PlayerDownsync {
toRet := make([]*battle.PlayerDownsync, len(modelInstances), len(modelInstances))
if nil == modelInstances {
return toRet
}
for _, last := range modelInstances {
toRet[last.JoinIndex-1] = &battle.PlayerDownsync{
Id: last.Id,
VirtualGridX: last.VirtualGridX,
VirtualGridY: last.VirtualGridY,
@ -28,11 +136,6 @@ func toPbPlayers(modelInstances map[int32]*Player, withMetaInfo bool) map[int32]
Score: last.Score,
Removed: last.Removed,
}
if withMetaInfo {
toRet[k].Name = last.Name
toRet[k].DisplayName = last.DisplayName
toRet[k].Avatar = last.Avatar
}
}
return toRet

File diff suppressed because it is too large Load Diff

View File

@ -7,7 +7,7 @@
package protos
import (
sharedprotos "dnmshared/sharedprotos"
_ "dnmshared/sharedprotos"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
@ -739,24 +739,26 @@ type MeleeBullet struct {
unknownFields protoimpl.UnknownFields
// for offender
BattleLocalId int32 `protobuf:"varint,1,opt,name=battleLocalId,proto3" json:"battleLocalId,omitempty"`
StartupFrames int32 `protobuf:"varint,2,opt,name=startupFrames,proto3" json:"startupFrames,omitempty"`
ActiveFrames int32 `protobuf:"varint,3,opt,name=activeFrames,proto3" json:"activeFrames,omitempty"`
RecoveryFrames int32 `protobuf:"varint,4,opt,name=recoveryFrames,proto3" json:"recoveryFrames,omitempty"`
RecoveryFramesOnBlock int32 `protobuf:"varint,5,opt,name=recoveryFramesOnBlock,proto3" json:"recoveryFramesOnBlock,omitempty"`
RecoveryFramesOnHit int32 `protobuf:"varint,6,opt,name=recoveryFramesOnHit,proto3" json:"recoveryFramesOnHit,omitempty"`
Moveforward *sharedprotos.Vec2D `protobuf:"bytes,7,opt,name=moveforward,proto3" json:"moveforward,omitempty"`
HitboxOffset float64 `protobuf:"fixed64,8,opt,name=hitboxOffset,proto3" json:"hitboxOffset,omitempty"`
HitboxSize *sharedprotos.Vec2D `protobuf:"bytes,9,opt,name=hitboxSize,proto3" json:"hitboxSize,omitempty"`
OriginatedRenderFrameId int32 `protobuf:"varint,10,opt,name=originatedRenderFrameId,proto3" json:"originatedRenderFrameId,omitempty"`
BattleLocalId int32 `protobuf:"varint,1,opt,name=battleLocalId,proto3" json:"battleLocalId,omitempty"`
StartupFrames int32 `protobuf:"varint,2,opt,name=startupFrames,proto3" json:"startupFrames,omitempty"`
ActiveFrames int32 `protobuf:"varint,3,opt,name=activeFrames,proto3" json:"activeFrames,omitempty"`
RecoveryFrames int32 `protobuf:"varint,4,opt,name=recoveryFrames,proto3" json:"recoveryFrames,omitempty"`
RecoveryFramesOnBlock int32 `protobuf:"varint,5,opt,name=recoveryFramesOnBlock,proto3" json:"recoveryFramesOnBlock,omitempty"`
RecoveryFramesOnHit int32 `protobuf:"varint,6,opt,name=recoveryFramesOnHit,proto3" json:"recoveryFramesOnHit,omitempty"`
HitboxOffset float64 `protobuf:"fixed64,7,opt,name=hitboxOffset,proto3" json:"hitboxOffset,omitempty"`
OriginatedRenderFrameId int32 `protobuf:"varint,8,opt,name=originatedRenderFrameId,proto3" json:"originatedRenderFrameId,omitempty"`
// for defender
HitStunFrames int32 `protobuf:"varint,11,opt,name=hitStunFrames,proto3" json:"hitStunFrames,omitempty"`
BlockStunFrames int32 `protobuf:"varint,12,opt,name=blockStunFrames,proto3" json:"blockStunFrames,omitempty"`
Pushback float64 `protobuf:"fixed64,13,opt,name=pushback,proto3" json:"pushback,omitempty"`
ReleaseTriggerType int32 `protobuf:"varint,14,opt,name=releaseTriggerType,proto3" json:"releaseTriggerType,omitempty"` // 1: rising-edge, 2: falling-edge
Damage int32 `protobuf:"varint,15,opt,name=damage,proto3" json:"damage,omitempty"`
OffenderJoinIndex int32 `protobuf:"varint,16,opt,name=offenderJoinIndex,proto3" json:"offenderJoinIndex,omitempty"`
OffenderPlayerId int32 `protobuf:"varint,17,opt,name=offenderPlayerId,proto3" json:"offenderPlayerId,omitempty"`
HitStunFrames int32 `protobuf:"varint,9,opt,name=hitStunFrames,proto3" json:"hitStunFrames,omitempty"`
BlockStunFrames int32 `protobuf:"varint,10,opt,name=blockStunFrames,proto3" json:"blockStunFrames,omitempty"`
Pushback float64 `protobuf:"fixed64,11,opt,name=pushback,proto3" json:"pushback,omitempty"`
ReleaseTriggerType int32 `protobuf:"varint,12,opt,name=releaseTriggerType,proto3" json:"releaseTriggerType,omitempty"` // 1: rising-edge, 2: falling-edge
Damage int32 `protobuf:"varint,13,opt,name=damage,proto3" json:"damage,omitempty"`
OffenderJoinIndex int32 `protobuf:"varint,14,opt,name=offenderJoinIndex,proto3" json:"offenderJoinIndex,omitempty"`
OffenderPlayerId int32 `protobuf:"varint,15,opt,name=offenderPlayerId,proto3" json:"offenderPlayerId,omitempty"`
HitboxSizeX float64 `protobuf:"fixed64,16,opt,name=hitboxSizeX,proto3" json:"hitboxSizeX,omitempty"`
HitboxSizeY float64 `protobuf:"fixed64,17,opt,name=hitboxSizeY,proto3" json:"hitboxSizeY,omitempty"`
SelfMoveforwardX float64 `protobuf:"fixed64,18,opt,name=selfMoveforwardX,proto3" json:"selfMoveforwardX,omitempty"`
SelfMoveforwardY float64 `protobuf:"fixed64,19,opt,name=selfMoveforwardY,proto3" json:"selfMoveforwardY,omitempty"`
}
func (x *MeleeBullet) Reset() {
@ -833,13 +835,6 @@ func (x *MeleeBullet) GetRecoveryFramesOnHit() int32 {
return 0
}
func (x *MeleeBullet) GetMoveforward() *sharedprotos.Vec2D {
if x != nil {
return x.Moveforward
}
return nil
}
func (x *MeleeBullet) GetHitboxOffset() float64 {
if x != nil {
return x.HitboxOffset
@ -847,13 +842,6 @@ func (x *MeleeBullet) GetHitboxOffset() float64 {
return 0
}
func (x *MeleeBullet) GetHitboxSize() *sharedprotos.Vec2D {
if x != nil {
return x.HitboxSize
}
return nil
}
func (x *MeleeBullet) GetOriginatedRenderFrameId() int32 {
if x != nil {
return x.OriginatedRenderFrameId
@ -910,42 +898,69 @@ func (x *MeleeBullet) GetOffenderPlayerId() int32 {
return 0
}
func (x *MeleeBullet) GetHitboxSizeX() float64 {
if x != nil {
return x.HitboxSizeX
}
return 0
}
func (x *MeleeBullet) GetHitboxSizeY() float64 {
if x != nil {
return x.HitboxSizeY
}
return 0
}
func (x *MeleeBullet) GetSelfMoveforwardX() float64 {
if x != nil {
return x.SelfMoveforwardX
}
return 0
}
func (x *MeleeBullet) GetSelfMoveforwardY() float64 {
if x != nil {
return x.SelfMoveforwardY
}
return 0
}
type BattleColliderInfo struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
StageName string `protobuf:"bytes,1,opt,name=stageName,proto3" json:"stageName,omitempty"`
StrToVec2DListMap map[string]*sharedprotos.Vec2DList `protobuf:"bytes,2,rep,name=strToVec2DListMap,proto3" json:"strToVec2DListMap,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
StrToPolygon2DListMap map[string]*sharedprotos.Polygon2DList `protobuf:"bytes,3,rep,name=strToPolygon2DListMap,proto3" json:"strToPolygon2DListMap,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
StageDiscreteW int32 `protobuf:"varint,4,opt,name=stageDiscreteW,proto3" json:"stageDiscreteW,omitempty"`
StageDiscreteH int32 `protobuf:"varint,5,opt,name=stageDiscreteH,proto3" json:"stageDiscreteH,omitempty"`
StageTileW int32 `protobuf:"varint,6,opt,name=stageTileW,proto3" json:"stageTileW,omitempty"`
StageTileH int32 `protobuf:"varint,7,opt,name=stageTileH,proto3" json:"stageTileH,omitempty"`
IntervalToPing int32 `protobuf:"varint,8,opt,name=intervalToPing,proto3" json:"intervalToPing,omitempty"`
WillKickIfInactiveFor int32 `protobuf:"varint,9,opt,name=willKickIfInactiveFor,proto3" json:"willKickIfInactiveFor,omitempty"`
BoundRoomId int32 `protobuf:"varint,10,opt,name=boundRoomId,proto3" json:"boundRoomId,omitempty"`
BattleDurationFrames int32 `protobuf:"varint,12,opt,name=battleDurationFrames,proto3" json:"battleDurationFrames,omitempty"`
BattleDurationNanos int64 `protobuf:"varint,13,opt,name=battleDurationNanos,proto3" json:"battleDurationNanos,omitempty"`
ServerFps int32 `protobuf:"varint,14,opt,name=serverFps,proto3" json:"serverFps,omitempty"`
InputDelayFrames int32 `protobuf:"varint,15,opt,name=inputDelayFrames,proto3" json:"inputDelayFrames,omitempty"` // in the count of render frames
InputScaleFrames uint32 `protobuf:"varint,16,opt,name=inputScaleFrames,proto3" json:"inputScaleFrames,omitempty"` // inputDelayedAndScaledFrameId = ((originalFrameId - InputDelayFrames) >> InputScaleFrames)
NstDelayFrames int32 `protobuf:"varint,17,opt,name=nstDelayFrames,proto3" json:"nstDelayFrames,omitempty"` // network-single-trip delay in the count of render frames, proposed to be (InputDelayFrames >> 1) because we expect a round-trip delay to be exactly "InputDelayFrames"
InputFrameUpsyncDelayTolerance int32 `protobuf:"varint,18,opt,name=inputFrameUpsyncDelayTolerance,proto3" json:"inputFrameUpsyncDelayTolerance,omitempty"`
MaxChasingRenderFramesPerUpdate int32 `protobuf:"varint,19,opt,name=maxChasingRenderFramesPerUpdate,proto3" json:"maxChasingRenderFramesPerUpdate,omitempty"`
PlayerBattleState int32 `protobuf:"varint,20,opt,name=playerBattleState,proto3" json:"playerBattleState,omitempty"`
RollbackEstimatedDtMillis float64 `protobuf:"fixed64,21,opt,name=rollbackEstimatedDtMillis,proto3" json:"rollbackEstimatedDtMillis,omitempty"`
RollbackEstimatedDtNanos int64 `protobuf:"varint,22,opt,name=rollbackEstimatedDtNanos,proto3" json:"rollbackEstimatedDtNanos,omitempty"`
WorldToVirtualGridRatio float64 `protobuf:"fixed64,23,opt,name=worldToVirtualGridRatio,proto3" json:"worldToVirtualGridRatio,omitempty"`
VirtualGridToWorldRatio float64 `protobuf:"fixed64,24,opt,name=virtualGridToWorldRatio,proto3" json:"virtualGridToWorldRatio,omitempty"`
SpAtkLookupFrames int32 `protobuf:"varint,25,opt,name=spAtkLookupFrames,proto3" json:"spAtkLookupFrames,omitempty"`
RenderCacheSize int32 `protobuf:"varint,26,opt,name=renderCacheSize,proto3" json:"renderCacheSize,omitempty"`
MeleeSkillConfig map[int32]*MeleeBullet `protobuf:"bytes,27,rep,name=meleeSkillConfig,proto3" json:"meleeSkillConfig,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` // skillId -> skill
SnapIntoPlatformOverlap float64 `protobuf:"fixed64,28,opt,name=snapIntoPlatformOverlap,proto3" json:"snapIntoPlatformOverlap,omitempty"`
SnapIntoPlatformThreshold float64 `protobuf:"fixed64,29,opt,name=snapIntoPlatformThreshold,proto3" json:"snapIntoPlatformThreshold,omitempty"`
JumpingInitVelY int32 `protobuf:"varint,30,opt,name=jumpingInitVelY,proto3" json:"jumpingInitVelY,omitempty"`
GravityX int32 `protobuf:"varint,31,opt,name=gravityX,proto3" json:"gravityX,omitempty"`
GravityY int32 `protobuf:"varint,32,opt,name=gravityY,proto3" json:"gravityY,omitempty"`
StageName string `protobuf:"bytes,1,opt,name=stageName,proto3" json:"stageName,omitempty"`
StageDiscreteW int32 `protobuf:"varint,2,opt,name=stageDiscreteW,proto3" json:"stageDiscreteW,omitempty"`
StageDiscreteH int32 `protobuf:"varint,3,opt,name=stageDiscreteH,proto3" json:"stageDiscreteH,omitempty"`
StageTileW int32 `protobuf:"varint,4,opt,name=stageTileW,proto3" json:"stageTileW,omitempty"`
StageTileH int32 `protobuf:"varint,5,opt,name=stageTileH,proto3" json:"stageTileH,omitempty"`
IntervalToPing int32 `protobuf:"varint,6,opt,name=intervalToPing,proto3" json:"intervalToPing,omitempty"`
WillKickIfInactiveFor int32 `protobuf:"varint,7,opt,name=willKickIfInactiveFor,proto3" json:"willKickIfInactiveFor,omitempty"`
BoundRoomId int32 `protobuf:"varint,8,opt,name=boundRoomId,proto3" json:"boundRoomId,omitempty"`
BattleDurationFrames int32 `protobuf:"varint,9,opt,name=battleDurationFrames,proto3" json:"battleDurationFrames,omitempty"`
BattleDurationNanos int64 `protobuf:"varint,10,opt,name=battleDurationNanos,proto3" json:"battleDurationNanos,omitempty"`
ServerFps int32 `protobuf:"varint,11,opt,name=serverFps,proto3" json:"serverFps,omitempty"`
InputDelayFrames int32 `protobuf:"varint,12,opt,name=inputDelayFrames,proto3" json:"inputDelayFrames,omitempty"` // in the count of render frames
InputScaleFrames uint32 `protobuf:"varint,13,opt,name=inputScaleFrames,proto3" json:"inputScaleFrames,omitempty"` // inputDelayedAndScaledFrameId = ((originalFrameId - InputDelayFrames) >> InputScaleFrames)
NstDelayFrames int32 `protobuf:"varint,14,opt,name=nstDelayFrames,proto3" json:"nstDelayFrames,omitempty"` // network-single-trip delay in the count of render frames, proposed to be (InputDelayFrames >> 1) because we expect a round-trip delay to be exactly "InputDelayFrames"
InputFrameUpsyncDelayTolerance int32 `protobuf:"varint,15,opt,name=inputFrameUpsyncDelayTolerance,proto3" json:"inputFrameUpsyncDelayTolerance,omitempty"`
MaxChasingRenderFramesPerUpdate int32 `protobuf:"varint,16,opt,name=maxChasingRenderFramesPerUpdate,proto3" json:"maxChasingRenderFramesPerUpdate,omitempty"`
PlayerBattleState int32 `protobuf:"varint,17,opt,name=playerBattleState,proto3" json:"playerBattleState,omitempty"`
RollbackEstimatedDtMillis float64 `protobuf:"fixed64,18,opt,name=rollbackEstimatedDtMillis,proto3" json:"rollbackEstimatedDtMillis,omitempty"`
RollbackEstimatedDtNanos int64 `protobuf:"varint,19,opt,name=rollbackEstimatedDtNanos,proto3" json:"rollbackEstimatedDtNanos,omitempty"`
WorldToVirtualGridRatio float64 `protobuf:"fixed64,20,opt,name=worldToVirtualGridRatio,proto3" json:"worldToVirtualGridRatio,omitempty"`
VirtualGridToWorldRatio float64 `protobuf:"fixed64,21,opt,name=virtualGridToWorldRatio,proto3" json:"virtualGridToWorldRatio,omitempty"`
SpAtkLookupFrames int32 `protobuf:"varint,22,opt,name=spAtkLookupFrames,proto3" json:"spAtkLookupFrames,omitempty"`
RenderCacheSize int32 `protobuf:"varint,23,opt,name=renderCacheSize,proto3" json:"renderCacheSize,omitempty"`
SnapIntoPlatformOverlap float64 `protobuf:"fixed64,24,opt,name=snapIntoPlatformOverlap,proto3" json:"snapIntoPlatformOverlap,omitempty"`
SnapIntoPlatformThreshold float64 `protobuf:"fixed64,25,opt,name=snapIntoPlatformThreshold,proto3" json:"snapIntoPlatformThreshold,omitempty"`
JumpingInitVelY int32 `protobuf:"varint,26,opt,name=jumpingInitVelY,proto3" json:"jumpingInitVelY,omitempty"`
GravityX int32 `protobuf:"varint,27,opt,name=gravityX,proto3" json:"gravityX,omitempty"`
GravityY int32 `protobuf:"varint,28,opt,name=gravityY,proto3" json:"gravityY,omitempty"`
CollisionMinStep int32 `protobuf:"varint,29,opt,name=collisionMinStep,proto3" json:"collisionMinStep,omitempty"`
FrameDataLoggingEnabled bool `protobuf:"varint,999,opt,name=frameDataLoggingEnabled,proto3" json:"frameDataLoggingEnabled,omitempty"`
}
func (x *BattleColliderInfo) Reset() {
@ -987,20 +1002,6 @@ func (x *BattleColliderInfo) GetStageName() string {
return ""
}
func (x *BattleColliderInfo) GetStrToVec2DListMap() map[string]*sharedprotos.Vec2DList {
if x != nil {
return x.StrToVec2DListMap
}
return nil
}
func (x *BattleColliderInfo) GetStrToPolygon2DListMap() map[string]*sharedprotos.Polygon2DList {
if x != nil {
return x.StrToPolygon2DListMap
}
return nil
}
func (x *BattleColliderInfo) GetStageDiscreteW() int32 {
if x != nil {
return x.StageDiscreteW
@ -1155,13 +1156,6 @@ func (x *BattleColliderInfo) GetRenderCacheSize() int32 {
return 0
}
func (x *BattleColliderInfo) GetMeleeSkillConfig() map[int32]*MeleeBullet {
if x != nil {
return x.MeleeSkillConfig
}
return nil
}
func (x *BattleColliderInfo) GetSnapIntoPlatformOverlap() float64 {
if x != nil {
return x.SnapIntoPlatformOverlap
@ -1197,17 +1191,32 @@ func (x *BattleColliderInfo) GetGravityY() int32 {
return 0
}
func (x *BattleColliderInfo) GetCollisionMinStep() int32 {
if x != nil {
return x.CollisionMinStep
}
return 0
}
func (x *BattleColliderInfo) GetFrameDataLoggingEnabled() bool {
if x != nil {
return x.FrameDataLoggingEnabled
}
return false
}
type RoomDownsyncFrame struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Id int32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
Players map[int32]*PlayerDownsync `protobuf:"bytes,2,rep,name=players,proto3" json:"players,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
CountdownNanos int64 `protobuf:"varint,3,opt,name=countdownNanos,proto3" json:"countdownNanos,omitempty"`
MeleeBullets []*MeleeBullet `protobuf:"bytes,4,rep,name=meleeBullets,proto3" json:"meleeBullets,omitempty"` // I don't know how to mimic inheritance/composition in protobuf by far, thus using an array for each type of bullet as a compromise
BackendUnconfirmedMask uint64 `protobuf:"varint,5,opt,name=backendUnconfirmedMask,proto3" json:"backendUnconfirmedMask,omitempty"` // Indexed by "joinIndex", same compression concern as stated in InputFrameDownsync
ShouldForceResync bool `protobuf:"varint,6,opt,name=shouldForceResync,proto3" json:"shouldForceResync,omitempty"`
Id int32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
PlayersArr []*PlayerDownsync `protobuf:"bytes,2,rep,name=playersArr,proto3" json:"playersArr,omitempty"`
CountdownNanos int64 `protobuf:"varint,3,opt,name=countdownNanos,proto3" json:"countdownNanos,omitempty"`
MeleeBullets []*MeleeBullet `protobuf:"bytes,4,rep,name=meleeBullets,proto3" json:"meleeBullets,omitempty"` // I don't know how to mimic inheritance/composition in protobuf by far, thus using an array for each type of bullet as a compromise
BackendUnconfirmedMask uint64 `protobuf:"varint,5,opt,name=backendUnconfirmedMask,proto3" json:"backendUnconfirmedMask,omitempty"` // Indexed by "joinIndex", same compression concern as stated in InputFrameDownsync
ShouldForceResync bool `protobuf:"varint,6,opt,name=shouldForceResync,proto3" json:"shouldForceResync,omitempty"`
PlayerOpPatternToSkillId map[int32]int32 `protobuf:"bytes,7,rep,name=playerOpPatternToSkillId,proto3" json:"playerOpPatternToSkillId,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"varint,2,opt,name=value,proto3"`
}
func (x *RoomDownsyncFrame) Reset() {
@ -1249,9 +1258,9 @@ func (x *RoomDownsyncFrame) GetId() int32 {
return 0
}
func (x *RoomDownsyncFrame) GetPlayers() map[int32]*PlayerDownsync {
func (x *RoomDownsyncFrame) GetPlayersArr() []*PlayerDownsync {
if x != nil {
return x.Players
return x.PlayersArr
}
return nil
}
@ -1284,6 +1293,13 @@ func (x *RoomDownsyncFrame) GetShouldForceResync() bool {
return false
}
func (x *RoomDownsyncFrame) GetPlayerOpPatternToSkillId() map[int32]int32 {
if x != nil {
return x.PlayerOpPatternToSkillId
}
return nil
}
var File_room_downsync_frame_proto protoreflect.FileDescriptor
var file_room_downsync_frame_proto_rawDesc = []byte{
@ -1405,7 +1421,7 @@ var file_room_downsync_frame_proto_rawDesc = []byte{
0x79, 0x6e, 0x63, 0x73, 0x12, 0x2c, 0x0a, 0x11, 0x73, 0x68, 0x6f, 0x75, 0x6c, 0x64, 0x46, 0x6f,
0x72, 0x63, 0x65, 0x52, 0x65, 0x73, 0x79, 0x6e, 0x63, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52,
0x11, 0x73, 0x68, 0x6f, 0x75, 0x6c, 0x64, 0x46, 0x6f, 0x72, 0x63, 0x65, 0x52, 0x65, 0x73, 0x79,
0x6e, 0x63, 0x22, 0xe5, 0x05, 0x0a, 0x0b, 0x4d, 0x65, 0x6c, 0x65, 0x65, 0x42, 0x75, 0x6c, 0x6c,
0x6e, 0x63, 0x22, 0x95, 0x06, 0x0a, 0x0b, 0x4d, 0x65, 0x6c, 0x65, 0x65, 0x42, 0x75, 0x6c, 0x6c,
0x65, 0x74, 0x12, 0x24, 0x0a, 0x0d, 0x62, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x4c, 0x6f, 0x63, 0x61,
0x6c, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d, 0x62, 0x61, 0x74, 0x74, 0x6c,
0x65, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x49, 0x64, 0x12, 0x24, 0x0a, 0x0d, 0x73, 0x74, 0x61, 0x72,
@ -1422,181 +1438,160 @@ var file_room_downsync_frame_proto_rawDesc = []byte{
0x12, 0x30, 0x0a, 0x13, 0x72, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x46, 0x72, 0x61, 0x6d,
0x65, 0x73, 0x4f, 0x6e, 0x48, 0x69, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x13, 0x72,
0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x73, 0x4f, 0x6e, 0x48,
0x69, 0x74, 0x12, 0x35, 0x0a, 0x0b, 0x6d, 0x6f, 0x76, 0x65, 0x66, 0x6f, 0x72, 0x77, 0x61, 0x72,
0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x73, 0x68, 0x61, 0x72, 0x65, 0x64,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x56, 0x65, 0x63, 0x32, 0x44, 0x52, 0x0b, 0x6d, 0x6f,
0x76, 0x65, 0x66, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x12, 0x22, 0x0a, 0x0c, 0x68, 0x69, 0x74,
0x62, 0x6f, 0x78, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x01, 0x52,
0x0c, 0x68, 0x69, 0x74, 0x62, 0x6f, 0x78, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, 0x33, 0x0a,
0x0a, 0x68, 0x69, 0x74, 0x62, 0x6f, 0x78, 0x53, 0x69, 0x7a, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28,
0x0b, 0x32, 0x13, 0x2e, 0x73, 0x68, 0x61, 0x72, 0x65, 0x64, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73,
0x2e, 0x56, 0x65, 0x63, 0x32, 0x44, 0x52, 0x0a, 0x68, 0x69, 0x74, 0x62, 0x6f, 0x78, 0x53, 0x69,
0x7a, 0x65, 0x12, 0x38, 0x0a, 0x17, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x65, 0x64,
0x52, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x49, 0x64, 0x18, 0x0a, 0x20,
0x01, 0x28, 0x05, 0x52, 0x17, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x65, 0x64, 0x52,
0x65, 0x6e, 0x64, 0x65, 0x72, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x49, 0x64, 0x12, 0x24, 0x0a, 0x0d,
0x68, 0x69, 0x74, 0x53, 0x74, 0x75, 0x6e, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x0b, 0x20,
0x01, 0x28, 0x05, 0x52, 0x0d, 0x68, 0x69, 0x74, 0x53, 0x74, 0x75, 0x6e, 0x46, 0x72, 0x61, 0x6d,
0x65, 0x73, 0x12, 0x28, 0x0a, 0x0f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x53, 0x74, 0x75, 0x6e, 0x46,
0x72, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0f, 0x62, 0x6c, 0x6f,
0x63, 0x6b, 0x53, 0x74, 0x75, 0x6e, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x1a, 0x0a, 0x08,
0x70, 0x75, 0x73, 0x68, 0x62, 0x61, 0x63, 0x6b, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x01, 0x52, 0x08,
0x70, 0x75, 0x73, 0x68, 0x62, 0x61, 0x63, 0x6b, 0x12, 0x2e, 0x0a, 0x12, 0x72, 0x65, 0x6c, 0x65,
0x61, 0x73, 0x65, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x54, 0x79, 0x70, 0x65, 0x18, 0x0e,
0x20, 0x01, 0x28, 0x05, 0x52, 0x12, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x54, 0x72, 0x69,
0x67, 0x67, 0x65, 0x72, 0x54, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x61, 0x6d, 0x61,
0x67, 0x65, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x64, 0x61, 0x6d, 0x61, 0x67, 0x65,
0x12, 0x2c, 0x0a, 0x11, 0x6f, 0x66, 0x66, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x4a, 0x6f, 0x69, 0x6e,
0x49, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x10, 0x20, 0x01, 0x28, 0x05, 0x52, 0x11, 0x6f, 0x66, 0x66,
0x65, 0x6e, 0x64, 0x65, 0x72, 0x4a, 0x6f, 0x69, 0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x2a,
0x0a, 0x10, 0x6f, 0x66, 0x66, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72,
0x49, 0x64, 0x18, 0x11, 0x20, 0x01, 0x28, 0x05, 0x52, 0x10, 0x6f, 0x66, 0x66, 0x65, 0x6e, 0x64,
0x65, 0x72, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x49, 0x64, 0x22, 0xf2, 0x0e, 0x0a, 0x12, 0x42,
0x69, 0x74, 0x12, 0x22, 0x0a, 0x0c, 0x68, 0x69, 0x74, 0x62, 0x6f, 0x78, 0x4f, 0x66, 0x66, 0x73,
0x65, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0c, 0x68, 0x69, 0x74, 0x62, 0x6f, 0x78,
0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, 0x38, 0x0a, 0x17, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e,
0x61, 0x74, 0x65, 0x64, 0x52, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x49,
0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x05, 0x52, 0x17, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61,
0x74, 0x65, 0x64, 0x52, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x49, 0x64,
0x12, 0x24, 0x0a, 0x0d, 0x68, 0x69, 0x74, 0x53, 0x74, 0x75, 0x6e, 0x46, 0x72, 0x61, 0x6d, 0x65,
0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d, 0x68, 0x69, 0x74, 0x53, 0x74, 0x75, 0x6e,
0x46, 0x72, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x28, 0x0a, 0x0f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x53,
0x74, 0x75, 0x6e, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x05, 0x52,
0x0f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x53, 0x74, 0x75, 0x6e, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x73,
0x12, 0x1a, 0x0a, 0x08, 0x70, 0x75, 0x73, 0x68, 0x62, 0x61, 0x63, 0x6b, 0x18, 0x0b, 0x20, 0x01,
0x28, 0x01, 0x52, 0x08, 0x70, 0x75, 0x73, 0x68, 0x62, 0x61, 0x63, 0x6b, 0x12, 0x2e, 0x0a, 0x12,
0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x54, 0x79,
0x70, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x05, 0x52, 0x12, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73,
0x65, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x54, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06,
0x64, 0x61, 0x6d, 0x61, 0x67, 0x65, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x64, 0x61,
0x6d, 0x61, 0x67, 0x65, 0x12, 0x2c, 0x0a, 0x11, 0x6f, 0x66, 0x66, 0x65, 0x6e, 0x64, 0x65, 0x72,
0x4a, 0x6f, 0x69, 0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x05, 0x52,
0x11, 0x6f, 0x66, 0x66, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x4a, 0x6f, 0x69, 0x6e, 0x49, 0x6e, 0x64,
0x65, 0x78, 0x12, 0x2a, 0x0a, 0x10, 0x6f, 0x66, 0x66, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x50, 0x6c,
0x61, 0x79, 0x65, 0x72, 0x49, 0x64, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x05, 0x52, 0x10, 0x6f, 0x66,
0x66, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x49, 0x64, 0x12, 0x20,
0x0a, 0x0b, 0x68, 0x69, 0x74, 0x62, 0x6f, 0x78, 0x53, 0x69, 0x7a, 0x65, 0x58, 0x18, 0x10, 0x20,
0x01, 0x28, 0x01, 0x52, 0x0b, 0x68, 0x69, 0x74, 0x62, 0x6f, 0x78, 0x53, 0x69, 0x7a, 0x65, 0x58,
0x12, 0x20, 0x0a, 0x0b, 0x68, 0x69, 0x74, 0x62, 0x6f, 0x78, 0x53, 0x69, 0x7a, 0x65, 0x59, 0x18,
0x11, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0b, 0x68, 0x69, 0x74, 0x62, 0x6f, 0x78, 0x53, 0x69, 0x7a,
0x65, 0x59, 0x12, 0x2a, 0x0a, 0x10, 0x73, 0x65, 0x6c, 0x66, 0x4d, 0x6f, 0x76, 0x65, 0x66, 0x6f,
0x72, 0x77, 0x61, 0x72, 0x64, 0x58, 0x18, 0x12, 0x20, 0x01, 0x28, 0x01, 0x52, 0x10, 0x73, 0x65,
0x6c, 0x66, 0x4d, 0x6f, 0x76, 0x65, 0x66, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x58, 0x12, 0x2a,
0x0a, 0x10, 0x73, 0x65, 0x6c, 0x66, 0x4d, 0x6f, 0x76, 0x65, 0x66, 0x6f, 0x72, 0x77, 0x61, 0x72,
0x64, 0x59, 0x18, 0x13, 0x20, 0x01, 0x28, 0x01, 0x52, 0x10, 0x73, 0x65, 0x6c, 0x66, 0x4d, 0x6f,
0x76, 0x65, 0x66, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x59, 0x22, 0x8d, 0x0b, 0x0a, 0x12, 0x42,
0x61, 0x74, 0x74, 0x6c, 0x65, 0x43, 0x6f, 0x6c, 0x6c, 0x69, 0x64, 0x65, 0x72, 0x49, 0x6e, 0x66,
0x6f, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x74, 0x61, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01,
0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x74, 0x61, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12,
0x5f, 0x0a, 0x11, 0x73, 0x74, 0x72, 0x54, 0x6f, 0x56, 0x65, 0x63, 0x32, 0x44, 0x4c, 0x69, 0x73,
0x74, 0x4d, 0x61, 0x70, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x73, 0x2e, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x43, 0x6f, 0x6c, 0x6c, 0x69, 0x64,
0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x53, 0x74, 0x72, 0x54, 0x6f, 0x56, 0x65, 0x63, 0x32,
0x44, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x11, 0x73,
0x74, 0x72, 0x54, 0x6f, 0x56, 0x65, 0x63, 0x32, 0x44, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x70,
0x12, 0x6b, 0x0a, 0x15, 0x73, 0x74, 0x72, 0x54, 0x6f, 0x50, 0x6f, 0x6c, 0x79, 0x67, 0x6f, 0x6e,
0x32, 0x44, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x70, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32,
0x35, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x43,
0x6f, 0x6c, 0x6c, 0x69, 0x64, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x53, 0x74, 0x72, 0x54,
0x6f, 0x50, 0x6f, 0x6c, 0x79, 0x67, 0x6f, 0x6e, 0x32, 0x44, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61,
0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x15, 0x73, 0x74, 0x72, 0x54, 0x6f, 0x50, 0x6f, 0x6c,
0x79, 0x67, 0x6f, 0x6e, 0x32, 0x44, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x70, 0x12, 0x26, 0x0a,
0x0e, 0x73, 0x74, 0x61, 0x67, 0x65, 0x44, 0x69, 0x73, 0x63, 0x72, 0x65, 0x74, 0x65, 0x57, 0x18,
0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, 0x73, 0x74, 0x61, 0x67, 0x65, 0x44, 0x69, 0x73, 0x63,
0x72, 0x65, 0x74, 0x65, 0x57, 0x12, 0x26, 0x0a, 0x0e, 0x73, 0x74, 0x61, 0x67, 0x65, 0x44, 0x69,
0x73, 0x63, 0x72, 0x65, 0x74, 0x65, 0x48, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, 0x73,
0x74, 0x61, 0x67, 0x65, 0x44, 0x69, 0x73, 0x63, 0x72, 0x65, 0x74, 0x65, 0x48, 0x12, 0x1e, 0x0a,
0x0a, 0x73, 0x74, 0x61, 0x67, 0x65, 0x54, 0x69, 0x6c, 0x65, 0x57, 0x18, 0x06, 0x20, 0x01, 0x28,
0x05, 0x52, 0x0a, 0x73, 0x74, 0x61, 0x67, 0x65, 0x54, 0x69, 0x6c, 0x65, 0x57, 0x12, 0x1e, 0x0a,
0x0a, 0x73, 0x74, 0x61, 0x67, 0x65, 0x54, 0x69, 0x6c, 0x65, 0x48, 0x18, 0x07, 0x20, 0x01, 0x28,
0x05, 0x52, 0x0a, 0x73, 0x74, 0x61, 0x67, 0x65, 0x54, 0x69, 0x6c, 0x65, 0x48, 0x12, 0x26, 0x0a,
0x0e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x54, 0x6f, 0x50, 0x69, 0x6e, 0x67, 0x18,
0x08, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x54,
0x6f, 0x50, 0x69, 0x6e, 0x67, 0x12, 0x34, 0x0a, 0x15, 0x77, 0x69, 0x6c, 0x6c, 0x4b, 0x69, 0x63,
0x6b, 0x49, 0x66, 0x49, 0x6e, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x46, 0x6f, 0x72, 0x18, 0x09,
0x20, 0x01, 0x28, 0x05, 0x52, 0x15, 0x77, 0x69, 0x6c, 0x6c, 0x4b, 0x69, 0x63, 0x6b, 0x49, 0x66,
0x49, 0x6e, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x46, 0x6f, 0x72, 0x12, 0x20, 0x0a, 0x0b, 0x62,
0x6f, 0x75, 0x6e, 0x64, 0x52, 0x6f, 0x6f, 0x6d, 0x49, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x05,
0x52, 0x0b, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x6f, 0x6f, 0x6d, 0x49, 0x64, 0x12, 0x32, 0x0a,
0x14, 0x62, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46,
0x72, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x05, 0x52, 0x14, 0x62, 0x61, 0x74,
0x74, 0x6c, 0x65, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x72, 0x61, 0x6d, 0x65,
0x73, 0x12, 0x30, 0x0a, 0x13, 0x62, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x44, 0x75, 0x72, 0x61, 0x74,
0x69, 0x6f, 0x6e, 0x4e, 0x61, 0x6e, 0x6f, 0x73, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x03, 0x52, 0x13,
0x62, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x61,
0x6e, 0x6f, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x46, 0x70, 0x73,
0x18, 0x0e, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x46, 0x70,
0x73, 0x12, 0x2a, 0x0a, 0x10, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x44, 0x65, 0x6c, 0x61, 0x79, 0x46,
0x72, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x05, 0x52, 0x10, 0x69, 0x6e, 0x70,
0x75, 0x74, 0x44, 0x65, 0x6c, 0x61, 0x79, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x2a, 0x0a,
0x10, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x53, 0x63, 0x61, 0x6c, 0x65, 0x46, 0x72, 0x61, 0x6d, 0x65,
0x73, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x10, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x53, 0x63,
0x61, 0x6c, 0x65, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x26, 0x0a, 0x0e, 0x6e, 0x73, 0x74,
0x44, 0x65, 0x6c, 0x61, 0x79, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x11, 0x20, 0x01, 0x28,
0x05, 0x52, 0x0e, 0x6e, 0x73, 0x74, 0x44, 0x65, 0x6c, 0x61, 0x79, 0x46, 0x72, 0x61, 0x6d, 0x65,
0x73, 0x12, 0x46, 0x0a, 0x1e, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x55,
0x70, 0x73, 0x79, 0x6e, 0x63, 0x44, 0x65, 0x6c, 0x61, 0x79, 0x54, 0x6f, 0x6c, 0x65, 0x72, 0x61,
0x6e, 0x63, 0x65, 0x18, 0x12, 0x20, 0x01, 0x28, 0x05, 0x52, 0x1e, 0x69, 0x6e, 0x70, 0x75, 0x74,
0x46, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x70, 0x73, 0x79, 0x6e, 0x63, 0x44, 0x65, 0x6c, 0x61, 0x79,
0x54, 0x6f, 0x6c, 0x65, 0x72, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x48, 0x0a, 0x1f, 0x6d, 0x61, 0x78,
0x43, 0x68, 0x61, 0x73, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x46, 0x72, 0x61,
0x6d, 0x65, 0x73, 0x50, 0x65, 0x72, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x18, 0x13, 0x20, 0x01,
0x28, 0x05, 0x52, 0x1f, 0x6d, 0x61, 0x78, 0x43, 0x68, 0x61, 0x73, 0x69, 0x6e, 0x67, 0x52, 0x65,
0x6e, 0x64, 0x65, 0x72, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x73, 0x50, 0x65, 0x72, 0x55, 0x70, 0x64,
0x61, 0x74, 0x65, 0x12, 0x2c, 0x0a, 0x11, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x42, 0x61, 0x74,
0x74, 0x6c, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x18, 0x14, 0x20, 0x01, 0x28, 0x05, 0x52, 0x11,
0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x53, 0x74, 0x61, 0x74,
0x65, 0x12, 0x3c, 0x0a, 0x19, 0x72, 0x6f, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x45, 0x73, 0x74,
0x69, 0x6d, 0x61, 0x74, 0x65, 0x64, 0x44, 0x74, 0x4d, 0x69, 0x6c, 0x6c, 0x69, 0x73, 0x18, 0x15,
0x20, 0x01, 0x28, 0x01, 0x52, 0x19, 0x72, 0x6f, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x45, 0x73,
0x74, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x64, 0x44, 0x74, 0x4d, 0x69, 0x6c, 0x6c, 0x69, 0x73, 0x12,
0x3a, 0x0a, 0x18, 0x72, 0x6f, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x45, 0x73, 0x74, 0x69, 0x6d,
0x61, 0x74, 0x65, 0x64, 0x44, 0x74, 0x4e, 0x61, 0x6e, 0x6f, 0x73, 0x18, 0x16, 0x20, 0x01, 0x28,
0x03, 0x52, 0x18, 0x72, 0x6f, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x45, 0x73, 0x74, 0x69, 0x6d,
0x61, 0x74, 0x65, 0x64, 0x44, 0x74, 0x4e, 0x61, 0x6e, 0x6f, 0x73, 0x12, 0x38, 0x0a, 0x17, 0x77,
0x6f, 0x72, 0x6c, 0x64, 0x54, 0x6f, 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x47, 0x72, 0x69,
0x64, 0x52, 0x61, 0x74, 0x69, 0x6f, 0x18, 0x17, 0x20, 0x01, 0x28, 0x01, 0x52, 0x17, 0x77, 0x6f,
0x72, 0x6c, 0x64, 0x54, 0x6f, 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x47, 0x72, 0x69, 0x64,
0x52, 0x61, 0x74, 0x69, 0x6f, 0x12, 0x38, 0x0a, 0x17, 0x76, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c,
0x47, 0x72, 0x69, 0x64, 0x54, 0x6f, 0x57, 0x6f, 0x72, 0x6c, 0x64, 0x52, 0x61, 0x74, 0x69, 0x6f,
0x18, 0x18, 0x20, 0x01, 0x28, 0x01, 0x52, 0x17, 0x76, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x47,
0x72, 0x69, 0x64, 0x54, 0x6f, 0x57, 0x6f, 0x72, 0x6c, 0x64, 0x52, 0x61, 0x74, 0x69, 0x6f, 0x12,
0x2c, 0x0a, 0x11, 0x73, 0x70, 0x41, 0x74, 0x6b, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x46, 0x72,
0x61, 0x6d, 0x65, 0x73, 0x18, 0x19, 0x20, 0x01, 0x28, 0x05, 0x52, 0x11, 0x73, 0x70, 0x41, 0x74,
0x6b, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x28, 0x0a,
0x0f, 0x72, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x43, 0x61, 0x63, 0x68, 0x65, 0x53, 0x69, 0x7a, 0x65,
0x18, 0x1a, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0f, 0x72, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x43, 0x61,
0x63, 0x68, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x5c, 0x0a, 0x10, 0x6d, 0x65, 0x6c, 0x65, 0x65,
0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x1b, 0x20, 0x03, 0x28,
0x0b, 0x32, 0x30, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x42, 0x61, 0x74, 0x74, 0x6c,
0x65, 0x43, 0x6f, 0x6c, 0x6c, 0x69, 0x64, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x4d, 0x65,
0x6c, 0x65, 0x65, 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x45, 0x6e,
0x74, 0x72, 0x79, 0x52, 0x10, 0x6d, 0x65, 0x6c, 0x65, 0x65, 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x43,
0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x38, 0x0a, 0x17, 0x73, 0x6e, 0x61, 0x70, 0x49, 0x6e, 0x74,
0x6f, 0x50, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x4f, 0x76, 0x65, 0x72, 0x6c, 0x61, 0x70,
0x18, 0x1c, 0x20, 0x01, 0x28, 0x01, 0x52, 0x17, 0x73, 0x6e, 0x61, 0x70, 0x49, 0x6e, 0x74, 0x6f,
0x50, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x4f, 0x76, 0x65, 0x72, 0x6c, 0x61, 0x70, 0x12,
0x3c, 0x0a, 0x19, 0x73, 0x6e, 0x61, 0x70, 0x49, 0x6e, 0x74, 0x6f, 0x50, 0x6c, 0x61, 0x74, 0x66,
0x6f, 0x72, 0x6d, 0x54, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x18, 0x1d, 0x20, 0x01,
0x28, 0x01, 0x52, 0x19, 0x73, 0x6e, 0x61, 0x70, 0x49, 0x6e, 0x74, 0x6f, 0x50, 0x6c, 0x61, 0x74,
0x66, 0x6f, 0x72, 0x6d, 0x54, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x12, 0x28, 0x0a,
0x0f, 0x6a, 0x75, 0x6d, 0x70, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x69, 0x74, 0x56, 0x65, 0x6c, 0x59,
0x18, 0x1e, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0f, 0x6a, 0x75, 0x6d, 0x70, 0x69, 0x6e, 0x67, 0x49,
0x6e, 0x69, 0x74, 0x56, 0x65, 0x6c, 0x59, 0x12, 0x1a, 0x0a, 0x08, 0x67, 0x72, 0x61, 0x76, 0x69,
0x74, 0x79, 0x58, 0x18, 0x1f, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x67, 0x72, 0x61, 0x76, 0x69,
0x74, 0x79, 0x58, 0x12, 0x1a, 0x0a, 0x08, 0x67, 0x72, 0x61, 0x76, 0x69, 0x74, 0x79, 0x59, 0x18,
0x20, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x67, 0x72, 0x61, 0x76, 0x69, 0x74, 0x79, 0x59, 0x1a,
0x5d, 0x0a, 0x16, 0x53, 0x74, 0x72, 0x54, 0x6f, 0x56, 0x65, 0x63, 0x32, 0x44, 0x4c, 0x69, 0x73,
0x74, 0x4d, 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79,
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2d, 0x0a, 0x05, 0x76,
0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x73, 0x68, 0x61,
0x72, 0x65, 0x64, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x56, 0x65, 0x63, 0x32, 0x44, 0x4c,
0x69, 0x73, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x65,
0x0a, 0x1a, 0x53, 0x74, 0x72, 0x54, 0x6f, 0x50, 0x6f, 0x6c, 0x79, 0x67, 0x6f, 0x6e, 0x32, 0x44,
0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03,
0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x31,
0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e,
0x73, 0x68, 0x61, 0x72, 0x65, 0x64, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x50, 0x6f, 0x6c,
0x79, 0x67, 0x6f, 0x6e, 0x32, 0x44, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75,
0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x58, 0x0a, 0x15, 0x4d, 0x65, 0x6c, 0x65, 0x65, 0x53, 0x6b,
0x69, 0x6c, 0x6c, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10,
0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x6b, 0x65, 0x79,
0x12, 0x29, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32,
0x13, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x4d, 0x65, 0x6c, 0x65, 0x65, 0x42, 0x75,
0x6c, 0x6c, 0x65, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22,
0x80, 0x03, 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, 0x40, 0x0a, 0x07, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73,
0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 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, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07,
0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x12, 0x26, 0x0a, 0x0e, 0x63, 0x6f, 0x75, 0x6e, 0x74,
0x64, 0x6f, 0x77, 0x6e, 0x4e, 0x61, 0x6e, 0x6f, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52,
0x0e, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x4e, 0x61, 0x6e, 0x6f, 0x73, 0x12,
0x37, 0x0a, 0x0c, 0x6d, 0x65, 0x6c, 0x65, 0x65, 0x42, 0x75, 0x6c, 0x6c, 0x65, 0x74, 0x73, 0x18,
0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x4d,
0x65, 0x6c, 0x65, 0x65, 0x42, 0x75, 0x6c, 0x6c, 0x65, 0x74, 0x52, 0x0c, 0x6d, 0x65, 0x6c, 0x65,
0x65, 0x42, 0x75, 0x6c, 0x6c, 0x65, 0x74, 0x73, 0x12, 0x36, 0x0a, 0x16, 0x62, 0x61, 0x63, 0x6b,
0x65, 0x6e, 0x64, 0x55, 0x6e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x65, 0x64, 0x4d, 0x61,
0x73, 0x6b, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x16, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e,
0x64, 0x55, 0x6e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x65, 0x64, 0x4d, 0x61, 0x73, 0x6b,
0x12, 0x2c, 0x0a, 0x11, 0x73, 0x68, 0x6f, 0x75, 0x6c, 0x64, 0x46, 0x6f, 0x72, 0x63, 0x65, 0x52,
0x65, 0x73, 0x79, 0x6e, 0x63, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x73, 0x68, 0x6f,
0x75, 0x6c, 0x64, 0x46, 0x6f, 0x72, 0x63, 0x65, 0x52, 0x65, 0x73, 0x79, 0x6e, 0x63, 0x1a, 0x52,
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, 0x2c, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32,
0x16, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x44,
0x6f, 0x77, 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02,
0x38, 0x01, 0x42, 0x13, 0x5a, 0x11, 0x62, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x5f, 0x73, 0x72, 0x76,
0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
0x26, 0x0a, 0x0e, 0x73, 0x74, 0x61, 0x67, 0x65, 0x44, 0x69, 0x73, 0x63, 0x72, 0x65, 0x74, 0x65,
0x57, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, 0x73, 0x74, 0x61, 0x67, 0x65, 0x44, 0x69,
0x73, 0x63, 0x72, 0x65, 0x74, 0x65, 0x57, 0x12, 0x26, 0x0a, 0x0e, 0x73, 0x74, 0x61, 0x67, 0x65,
0x44, 0x69, 0x73, 0x63, 0x72, 0x65, 0x74, 0x65, 0x48, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52,
0x0e, 0x73, 0x74, 0x61, 0x67, 0x65, 0x44, 0x69, 0x73, 0x63, 0x72, 0x65, 0x74, 0x65, 0x48, 0x12,
0x1e, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x67, 0x65, 0x54, 0x69, 0x6c, 0x65, 0x57, 0x18, 0x04, 0x20,
0x01, 0x28, 0x05, 0x52, 0x0a, 0x73, 0x74, 0x61, 0x67, 0x65, 0x54, 0x69, 0x6c, 0x65, 0x57, 0x12,
0x1e, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x67, 0x65, 0x54, 0x69, 0x6c, 0x65, 0x48, 0x18, 0x05, 0x20,
0x01, 0x28, 0x05, 0x52, 0x0a, 0x73, 0x74, 0x61, 0x67, 0x65, 0x54, 0x69, 0x6c, 0x65, 0x48, 0x12,
0x26, 0x0a, 0x0e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x54, 0x6f, 0x50, 0x69, 0x6e,
0x67, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61,
0x6c, 0x54, 0x6f, 0x50, 0x69, 0x6e, 0x67, 0x12, 0x34, 0x0a, 0x15, 0x77, 0x69, 0x6c, 0x6c, 0x4b,
0x69, 0x63, 0x6b, 0x49, 0x66, 0x49, 0x6e, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x46, 0x6f, 0x72,
0x18, 0x07, 0x20, 0x01, 0x28, 0x05, 0x52, 0x15, 0x77, 0x69, 0x6c, 0x6c, 0x4b, 0x69, 0x63, 0x6b,
0x49, 0x66, 0x49, 0x6e, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x46, 0x6f, 0x72, 0x12, 0x20, 0x0a,
0x0b, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x6f, 0x6f, 0x6d, 0x49, 0x64, 0x18, 0x08, 0x20, 0x01,
0x28, 0x05, 0x52, 0x0b, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x6f, 0x6f, 0x6d, 0x49, 0x64, 0x12,
0x32, 0x0a, 0x14, 0x62, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f,
0x6e, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x05, 0x52, 0x14, 0x62,
0x61, 0x74, 0x74, 0x6c, 0x65, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x72, 0x61,
0x6d, 0x65, 0x73, 0x12, 0x30, 0x0a, 0x13, 0x62, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x44, 0x75, 0x72,
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x61, 0x6e, 0x6f, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x03,
0x52, 0x13, 0x62, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e,
0x4e, 0x61, 0x6e, 0x6f, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x46,
0x70, 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72,
0x46, 0x70, 0x73, 0x12, 0x2a, 0x0a, 0x10, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x44, 0x65, 0x6c, 0x61,
0x79, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x05, 0x52, 0x10, 0x69,
0x6e, 0x70, 0x75, 0x74, 0x44, 0x65, 0x6c, 0x61, 0x79, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x73, 0x12,
0x2a, 0x0a, 0x10, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x53, 0x63, 0x61, 0x6c, 0x65, 0x46, 0x72, 0x61,
0x6d, 0x65, 0x73, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x10, 0x69, 0x6e, 0x70, 0x75, 0x74,
0x53, 0x63, 0x61, 0x6c, 0x65, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x26, 0x0a, 0x0e, 0x6e,
0x73, 0x74, 0x44, 0x65, 0x6c, 0x61, 0x79, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x0e, 0x20,
0x01, 0x28, 0x05, 0x52, 0x0e, 0x6e, 0x73, 0x74, 0x44, 0x65, 0x6c, 0x61, 0x79, 0x46, 0x72, 0x61,
0x6d, 0x65, 0x73, 0x12, 0x46, 0x0a, 0x1e, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x72, 0x61, 0x6d,
0x65, 0x55, 0x70, 0x73, 0x79, 0x6e, 0x63, 0x44, 0x65, 0x6c, 0x61, 0x79, 0x54, 0x6f, 0x6c, 0x65,
0x72, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x05, 0x52, 0x1e, 0x69, 0x6e, 0x70,
0x75, 0x74, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x70, 0x73, 0x79, 0x6e, 0x63, 0x44, 0x65, 0x6c,
0x61, 0x79, 0x54, 0x6f, 0x6c, 0x65, 0x72, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x48, 0x0a, 0x1f, 0x6d,
0x61, 0x78, 0x43, 0x68, 0x61, 0x73, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x46,
0x72, 0x61, 0x6d, 0x65, 0x73, 0x50, 0x65, 0x72, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x18, 0x10,
0x20, 0x01, 0x28, 0x05, 0x52, 0x1f, 0x6d, 0x61, 0x78, 0x43, 0x68, 0x61, 0x73, 0x69, 0x6e, 0x67,
0x52, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x73, 0x50, 0x65, 0x72, 0x55,
0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x2c, 0x0a, 0x11, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x42,
0x61, 0x74, 0x74, 0x6c, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x18, 0x11, 0x20, 0x01, 0x28, 0x05,
0x52, 0x11, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x42, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x53, 0x74,
0x61, 0x74, 0x65, 0x12, 0x3c, 0x0a, 0x19, 0x72, 0x6f, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x45,
0x73, 0x74, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x64, 0x44, 0x74, 0x4d, 0x69, 0x6c, 0x6c, 0x69, 0x73,
0x18, 0x12, 0x20, 0x01, 0x28, 0x01, 0x52, 0x19, 0x72, 0x6f, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b,
0x45, 0x73, 0x74, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x64, 0x44, 0x74, 0x4d, 0x69, 0x6c, 0x6c, 0x69,
0x73, 0x12, 0x3a, 0x0a, 0x18, 0x72, 0x6f, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x45, 0x73, 0x74,
0x69, 0x6d, 0x61, 0x74, 0x65, 0x64, 0x44, 0x74, 0x4e, 0x61, 0x6e, 0x6f, 0x73, 0x18, 0x13, 0x20,
0x01, 0x28, 0x03, 0x52, 0x18, 0x72, 0x6f, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x45, 0x73, 0x74,
0x69, 0x6d, 0x61, 0x74, 0x65, 0x64, 0x44, 0x74, 0x4e, 0x61, 0x6e, 0x6f, 0x73, 0x12, 0x38, 0x0a,
0x17, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x54, 0x6f, 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x47,
0x72, 0x69, 0x64, 0x52, 0x61, 0x74, 0x69, 0x6f, 0x18, 0x14, 0x20, 0x01, 0x28, 0x01, 0x52, 0x17,
0x77, 0x6f, 0x72, 0x6c, 0x64, 0x54, 0x6f, 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x47, 0x72,
0x69, 0x64, 0x52, 0x61, 0x74, 0x69, 0x6f, 0x12, 0x38, 0x0a, 0x17, 0x76, 0x69, 0x72, 0x74, 0x75,
0x61, 0x6c, 0x47, 0x72, 0x69, 0x64, 0x54, 0x6f, 0x57, 0x6f, 0x72, 0x6c, 0x64, 0x52, 0x61, 0x74,
0x69, 0x6f, 0x18, 0x15, 0x20, 0x01, 0x28, 0x01, 0x52, 0x17, 0x76, 0x69, 0x72, 0x74, 0x75, 0x61,
0x6c, 0x47, 0x72, 0x69, 0x64, 0x54, 0x6f, 0x57, 0x6f, 0x72, 0x6c, 0x64, 0x52, 0x61, 0x74, 0x69,
0x6f, 0x12, 0x2c, 0x0a, 0x11, 0x73, 0x70, 0x41, 0x74, 0x6b, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70,
0x46, 0x72, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x16, 0x20, 0x01, 0x28, 0x05, 0x52, 0x11, 0x73, 0x70,
0x41, 0x74, 0x6b, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x73, 0x12,
0x28, 0x0a, 0x0f, 0x72, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x43, 0x61, 0x63, 0x68, 0x65, 0x53, 0x69,
0x7a, 0x65, 0x18, 0x17, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0f, 0x72, 0x65, 0x6e, 0x64, 0x65, 0x72,
0x43, 0x61, 0x63, 0x68, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x38, 0x0a, 0x17, 0x73, 0x6e, 0x61,
0x70, 0x49, 0x6e, 0x74, 0x6f, 0x50, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x4f, 0x76, 0x65,
0x72, 0x6c, 0x61, 0x70, 0x18, 0x18, 0x20, 0x01, 0x28, 0x01, 0x52, 0x17, 0x73, 0x6e, 0x61, 0x70,
0x49, 0x6e, 0x74, 0x6f, 0x50, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x4f, 0x76, 0x65, 0x72,
0x6c, 0x61, 0x70, 0x12, 0x3c, 0x0a, 0x19, 0x73, 0x6e, 0x61, 0x70, 0x49, 0x6e, 0x74, 0x6f, 0x50,
0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x54, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64,
0x18, 0x19, 0x20, 0x01, 0x28, 0x01, 0x52, 0x19, 0x73, 0x6e, 0x61, 0x70, 0x49, 0x6e, 0x74, 0x6f,
0x50, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x54, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c,
0x64, 0x12, 0x28, 0x0a, 0x0f, 0x6a, 0x75, 0x6d, 0x70, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x69, 0x74,
0x56, 0x65, 0x6c, 0x59, 0x18, 0x1a, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0f, 0x6a, 0x75, 0x6d, 0x70,
0x69, 0x6e, 0x67, 0x49, 0x6e, 0x69, 0x74, 0x56, 0x65, 0x6c, 0x59, 0x12, 0x1a, 0x0a, 0x08, 0x67,
0x72, 0x61, 0x76, 0x69, 0x74, 0x79, 0x58, 0x18, 0x1b, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x67,
0x72, 0x61, 0x76, 0x69, 0x74, 0x79, 0x58, 0x12, 0x1a, 0x0a, 0x08, 0x67, 0x72, 0x61, 0x76, 0x69,
0x74, 0x79, 0x59, 0x18, 0x1c, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x67, 0x72, 0x61, 0x76, 0x69,
0x74, 0x79, 0x59, 0x12, 0x2a, 0x0a, 0x10, 0x63, 0x6f, 0x6c, 0x6c, 0x69, 0x73, 0x69, 0x6f, 0x6e,
0x4d, 0x69, 0x6e, 0x53, 0x74, 0x65, 0x70, 0x18, 0x1d, 0x20, 0x01, 0x28, 0x05, 0x52, 0x10, 0x63,
0x6f, 0x6c, 0x6c, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x69, 0x6e, 0x53, 0x74, 0x65, 0x70, 0x12,
0x39, 0x0a, 0x17, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x44, 0x61, 0x74, 0x61, 0x4c, 0x6f, 0x67, 0x67,
0x69, 0x6e, 0x67, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0xe7, 0x07, 0x20, 0x01, 0x28,
0x08, 0x52, 0x17, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x44, 0x61, 0x74, 0x61, 0x4c, 0x6f, 0x67, 0x67,
0x69, 0x6e, 0x67, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0xe4, 0x03, 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, 0x36, 0x0a, 0x0a, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x41, 0x72, 0x72, 0x18, 0x02,
0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x50, 0x6c,
0x61, 0x79, 0x65, 0x72, 0x44, 0x6f, 0x77, 0x6e, 0x73, 0x79, 0x6e, 0x63, 0x52, 0x0a, 0x70, 0x6c,
0x61, 0x79, 0x65, 0x72, 0x73, 0x41, 0x72, 0x72, 0x12, 0x26, 0x0a, 0x0e, 0x63, 0x6f, 0x75, 0x6e,
0x74, 0x64, 0x6f, 0x77, 0x6e, 0x4e, 0x61, 0x6e, 0x6f, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03,
0x52, 0x0e, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x4e, 0x61, 0x6e, 0x6f, 0x73,
0x12, 0x37, 0x0a, 0x0c, 0x6d, 0x65, 0x6c, 0x65, 0x65, 0x42, 0x75, 0x6c, 0x6c, 0x65, 0x74, 0x73,
0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e,
0x4d, 0x65, 0x6c, 0x65, 0x65, 0x42, 0x75, 0x6c, 0x6c, 0x65, 0x74, 0x52, 0x0c, 0x6d, 0x65, 0x6c,
0x65, 0x65, 0x42, 0x75, 0x6c, 0x6c, 0x65, 0x74, 0x73, 0x12, 0x36, 0x0a, 0x16, 0x62, 0x61, 0x63,
0x6b, 0x65, 0x6e, 0x64, 0x55, 0x6e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x65, 0x64, 0x4d,
0x61, 0x73, 0x6b, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x16, 0x62, 0x61, 0x63, 0x6b, 0x65,
0x6e, 0x64, 0x55, 0x6e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x65, 0x64, 0x4d, 0x61, 0x73,
0x6b, 0x12, 0x2c, 0x0a, 0x11, 0x73, 0x68, 0x6f, 0x75, 0x6c, 0x64, 0x46, 0x6f, 0x72, 0x63, 0x65,
0x52, 0x65, 0x73, 0x79, 0x6e, 0x63, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x73, 0x68,
0x6f, 0x75, 0x6c, 0x64, 0x46, 0x6f, 0x72, 0x63, 0x65, 0x52, 0x65, 0x73, 0x79, 0x6e, 0x63, 0x12,
0x73, 0x0a, 0x18, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4f, 0x70, 0x50, 0x61, 0x74, 0x74, 0x65,
0x72, 0x6e, 0x54, 0x6f, 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x49, 0x64, 0x18, 0x07, 0x20, 0x03, 0x28,
0x0b, 0x32, 0x37, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 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, 0x4f, 0x70, 0x50, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x54, 0x6f, 0x53, 0x6b,
0x69, 0x6c, 0x6c, 0x49, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x18, 0x70, 0x6c, 0x61, 0x79,
0x65, 0x72, 0x4f, 0x70, 0x50, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x54, 0x6f, 0x53, 0x6b, 0x69,
0x6c, 0x6c, 0x49, 0x64, 0x1a, 0x4b, 0x0a, 0x1d, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4f, 0x70,
0x50, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x54, 0x6f, 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x49, 0x64,
0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01,
0x28, 0x05, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65,
0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38,
0x01, 0x42, 0x13, 0x5a, 0x11, 0x62, 0x61, 0x74, 0x74, 0x6c, 0x65, 0x5f, 0x73, 0x72, 0x76, 0x2f,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
@ -1611,26 +1606,20 @@ 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, 15)
var file_room_downsync_frame_proto_msgTypes = make([]protoimpl.MessageInfo, 12)
var file_room_downsync_frame_proto_goTypes = []interface{}{
(*PlayerDownsync)(nil), // 0: protos.PlayerDownsync
(*InputFrameDecoded)(nil), // 1: protos.InputFrameDecoded
(*InputFrameUpsync)(nil), // 2: protos.InputFrameUpsync
(*InputFrameDownsync)(nil), // 3: protos.InputFrameDownsync
(*HeartbeatUpsync)(nil), // 4: protos.HeartbeatUpsync
(*WsReq)(nil), // 5: protos.WsReq
(*WsResp)(nil), // 6: protos.WsResp
(*InputsBufferSnapshot)(nil), // 7: protos.InputsBufferSnapshot
(*MeleeBullet)(nil), // 8: protos.MeleeBullet
(*BattleColliderInfo)(nil), // 9: protos.BattleColliderInfo
(*RoomDownsyncFrame)(nil), // 10: protos.RoomDownsyncFrame
nil, // 11: protos.BattleColliderInfo.StrToVec2DListMapEntry
nil, // 12: protos.BattleColliderInfo.StrToPolygon2DListMapEntry
nil, // 13: protos.BattleColliderInfo.MeleeSkillConfigEntry
nil, // 14: protos.RoomDownsyncFrame.PlayersEntry
(*sharedprotos.Vec2D)(nil), // 15: sharedprotos.Vec2D
(*sharedprotos.Vec2DList)(nil), // 16: sharedprotos.Vec2DList
(*sharedprotos.Polygon2DList)(nil), // 17: sharedprotos.Polygon2DList
(*PlayerDownsync)(nil), // 0: protos.PlayerDownsync
(*InputFrameDecoded)(nil), // 1: protos.InputFrameDecoded
(*InputFrameUpsync)(nil), // 2: protos.InputFrameUpsync
(*InputFrameDownsync)(nil), // 3: protos.InputFrameDownsync
(*HeartbeatUpsync)(nil), // 4: protos.HeartbeatUpsync
(*WsReq)(nil), // 5: protos.WsReq
(*WsResp)(nil), // 6: protos.WsResp
(*InputsBufferSnapshot)(nil), // 7: protos.InputsBufferSnapshot
(*MeleeBullet)(nil), // 8: protos.MeleeBullet
(*BattleColliderInfo)(nil), // 9: protos.BattleColliderInfo
(*RoomDownsyncFrame)(nil), // 10: protos.RoomDownsyncFrame
nil, // 11: protos.RoomDownsyncFrame.PlayerOpPatternToSkillIdEntry
}
var file_room_downsync_frame_proto_depIdxs = []int32{
2, // 0: protos.WsReq.inputFrameUpsyncBatch:type_name -> protos.InputFrameUpsync
@ -1639,22 +1628,14 @@ var file_room_downsync_frame_proto_depIdxs = []int32{
3, // 3: protos.WsResp.inputFrameDownsyncBatch:type_name -> protos.InputFrameDownsync
9, // 4: protos.WsResp.bciFrame:type_name -> protos.BattleColliderInfo
3, // 5: protos.InputsBufferSnapshot.toSendInputFrameDownsyncs:type_name -> protos.InputFrameDownsync
15, // 6: protos.MeleeBullet.moveforward:type_name -> sharedprotos.Vec2D
15, // 7: protos.MeleeBullet.hitboxSize:type_name -> sharedprotos.Vec2D
11, // 8: protos.BattleColliderInfo.strToVec2DListMap:type_name -> protos.BattleColliderInfo.StrToVec2DListMapEntry
12, // 9: protos.BattleColliderInfo.strToPolygon2DListMap:type_name -> protos.BattleColliderInfo.StrToPolygon2DListMapEntry
13, // 10: protos.BattleColliderInfo.meleeSkillConfig:type_name -> protos.BattleColliderInfo.MeleeSkillConfigEntry
14, // 11: protos.RoomDownsyncFrame.players:type_name -> protos.RoomDownsyncFrame.PlayersEntry
8, // 12: protos.RoomDownsyncFrame.meleeBullets:type_name -> protos.MeleeBullet
16, // 13: protos.BattleColliderInfo.StrToVec2DListMapEntry.value:type_name -> sharedprotos.Vec2DList
17, // 14: protos.BattleColliderInfo.StrToPolygon2DListMapEntry.value:type_name -> sharedprotos.Polygon2DList
8, // 15: protos.BattleColliderInfo.MeleeSkillConfigEntry.value:type_name -> protos.MeleeBullet
0, // 16: protos.RoomDownsyncFrame.PlayersEntry.value:type_name -> protos.PlayerDownsync
17, // [17:17] is the sub-list for method output_type
17, // [17:17] is the sub-list for method input_type
17, // [17:17] is the sub-list for extension type_name
17, // [17:17] is the sub-list for extension extendee
0, // [0:17] is the sub-list for field type_name
0, // 6: protos.RoomDownsyncFrame.playersArr:type_name -> protos.PlayerDownsync
8, // 7: protos.RoomDownsyncFrame.meleeBullets:type_name -> protos.MeleeBullet
11, // 8: protos.RoomDownsyncFrame.playerOpPatternToSkillId:type_name -> protos.RoomDownsyncFrame.PlayerOpPatternToSkillIdEntry
9, // [9:9] is the sub-list for method output_type
9, // [9:9] is the sub-list for method input_type
9, // [9:9] is the sub-list for extension type_name
9, // [9:9] is the sub-list for extension extendee
0, // [0:9] is the sub-list for field type_name
}
func init() { file_room_downsync_frame_proto_init() }
@ -1802,7 +1783,7 @@ func file_room_downsync_frame_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_room_downsync_frame_proto_rawDesc,
NumEnums: 0,
NumMessages: 15,
NumMessages: 12,
NumExtensions: 0,
NumServices: 0,
},

View File

@ -240,14 +240,12 @@ func Serve(c *gin.Context) {
// Construct "battleColliderInfo" to downsync
bciFrame := &pb.BattleColliderInfo{
BoundRoomId: pRoom.Id,
StageName: pRoom.StageName,
StrToVec2DListMap: pRoom.StrToVec2DListMap,
StrToPolygon2DListMap: pRoom.StrToPolygon2DListMap,
StageDiscreteW: pRoom.StageDiscreteW,
StageDiscreteH: pRoom.StageDiscreteH,
StageTileW: pRoom.StageTileW,
StageTileH: pRoom.StageTileH,
BoundRoomId: pRoom.Id,
StageName: pRoom.StageName,
StageDiscreteW: pRoom.StageDiscreteW,
StageDiscreteH: pRoom.StageDiscreteH,
StageTileW: pRoom.StageTileW,
StageTileH: pRoom.StageTileH,
IntervalToPing: int32(Constants.Ws.IntervalToPing),
WillKickIfInactiveFor: int32(Constants.Ws.WillKickIfInactiveFor),
@ -267,12 +265,14 @@ func Serve(c *gin.Context) {
SpAtkLookupFrames: pRoom.SpAtkLookupFrames,
RenderCacheSize: pRoom.RenderCacheSize,
MeleeSkillConfig: pRoom.MeleeSkillConfig,
SnapIntoPlatformOverlap: pRoom.SnapIntoPlatformOverlap,
SnapIntoPlatformThreshold: pRoom.SnapIntoPlatformThreshold,
JumpingInitVelY: pRoom.JumpingInitVelY,
GravityX: pRoom.GravityX,
GravityY: pRoom.GravityY,
CollisionMinStep: pRoom.CollisionMinStep,
FrameDataLoggingEnabled: pRoom.FrameDataLoggingEnabled,
}
resp := &pb.WsResp{

View File

@ -2,8 +2,8 @@ package main
import (
"github.com/hajimehoshi/ebiten/v2"
"github.com/solarlune/resolv"
"image/color"
"resolv"
)
var (

View File

@ -1,30 +1,44 @@
module viscol
go 1.19
go 1.18
require (
jsexport v0.0.0
dnmshared v0.0.0
battle_srv v0.0.0
resolv v0.0.0
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0
github.com/hajimehoshi/ebiten/v2 v2.4.7
github.com/solarlune/resolv v0.5.1
golang.org/x/image v0.0.0-20220902085622-e7cb96979f69
)
require (
github.com/ebitengine/purego v0.0.0-20220905075623-aeed57cda744 // indirect
github.com/fsnotify/fsnotify v1.5.1 // indirect
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20220806181222-55e207c401ad // indirect
github.com/hajimehoshi/file2byteslice v0.0.0-20210813153925-5340248a8f41 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/jezek/xgb v1.0.1 // indirect
github.com/kvartborg/vector v0.0.0-20200419093813-2cba0cabb4f0 // indirect
github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86 // indirect
github.com/neelance/sourcemap v0.0.0-20200213170602-2833bce08e4c // indirect
github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749 // indirect
github.com/sirupsen/logrus v1.8.1 // indirect
github.com/spf13/cobra v1.2.1 // indirect
github.com/spf13/pflag v1.0.5 // indirect
go.uber.org/atomic v1.7.0 // indirect
go.uber.org/multierr v1.6.0 // indirect
go.uber.org/zap v1.23.0 // indirect
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56 // indirect
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 // indirect
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6 // indirect
golang.org/x/mobile v0.0.0-20220722155234-aaac322e2105 // indirect
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 // indirect
golang.org/x/sys v0.0.0-20220818161305-2296e01440c6 // indirect
golang.org/x/term v0.0.0-20220411215600-e5f449aeb171 // indirect
golang.org/x/tools v0.1.12 // indirect
google.golang.org/protobuf v1.28.1 // indirect
)
replace dnmshared => ../dnmshared
replace battle_srv => ../battle_srv
replace (
dnmshared => ../dnmshared
jsexport => ../jsexport
resolv => ../resolv_tailored
)

View File

@ -1,14 +1,153 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg=
cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8=
cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/ebitengine/purego v0.0.0-20220905075623-aeed57cda744 h1:A8UnJ/5OKzki4HBDwoRQz7I6sxKsokpMXcGh+fUxpfc=
github.com/ebitengine/purego v0.0.0-20220905075623-aeed57cda744/go.mod h1:Eh8I3yvknDYZeCuXH9kRNaPuHEwvXDCk378o9xszmHg=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI=
github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20220806181222-55e207c401ad h1:kX51IjbsJPCvzV9jUoVQG9GEUqIq5hjfYzXTqQ52Rh8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20220806181222-55e207c401ad/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g=
github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k=
github.com/gopherjs/gopherjs v1.18.0-beta1 h1:IbykhVEq4SAjwyBRuNHl0aOO6w6IqgL3RUdMhoBo4mY=
github.com/gopherjs/gopherjs v1.18.0-beta1/go.mod h1:6UY8PXRnu51MqjYCCY4toG0S5GeH5uVJ3qDxIsa+kqo=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/hajimehoshi/bitmapfont/v2 v2.2.1 h1:y7zcy02/UgO24IL3COqYtrRZzhRucNBtmCo/SNU648k=
github.com/hajimehoshi/bitmapfont/v2 v2.2.1/go.mod h1:wjrYAy8vKgj9JsFgnYAOK346/uvE22TlmqouzdnYIs0=
github.com/hajimehoshi/ebiten/v2 v2.4.7 h1:XuvB7R0Rbw/O7g6vNU8gqr5b9e7MNhhAONMSsyreLDI=
@ -18,63 +157,292 @@ github.com/hajimehoshi/file2byteslice v0.0.0-20210813153925-5340248a8f41/go.mod
github.com/hajimehoshi/go-mp3 v0.3.3/go.mod h1:qMJj/CSDxx6CGHiZeCgbiq2DSUkbK0UbtXShQcnfyMM=
github.com/hajimehoshi/oto v0.6.1/go.mod h1:0QXGEkbuJRohbJaxr7ZQSxnju7hEhseiPx2hrh6raOI=
github.com/hajimehoshi/oto/v2 v2.3.1/go.mod h1:seWLbgHH7AyUMYKfKYT9pg7PhUu9/SisyJvNTT+ASQo=
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jakecoffman/cp v1.2.1/go.mod h1:JjY/Fp6d8E1CHnu74gWNnU0+b9VzEdUVPoJxg2PsTQg=
github.com/jezek/xgb v1.0.1 h1:YUGhxps0aR7J2Xplbs23OHnV1mWaxFVcOl9b+1RQkt8=
github.com/jezek/xgb v1.0.1/go.mod h1:nrhwO0FX/enq75I7Y7G8iN1ubpSGZEiA3v9e9GyRFlk=
github.com/jfreymuth/oggvorbis v1.0.4/go.mod h1:1U4pqWmghcoVsCJJ4fRBKv9peUJMBHixthRlBeD6uII=
github.com/jfreymuth/vorbis v1.0.2/go.mod h1:DoftRo4AznKnShRl1GxiTFCseHr4zR9BN3TWXyuzrqQ=
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kvartborg/vector v0.0.0-20200419093813-2cba0cabb4f0 h1:v8lWpj5957KtDMKu+xQtlu6G3ZoZR6Tn9bsfZCRG5Xw=
github.com/kvartborg/vector v0.0.0-20200419093813-2cba0cabb4f0/go.mod h1:GAX7tMJqXx9fB1BrsTWPOXy6IBRX+J461BffVPAdpwo=
github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86 h1:D6paGObi5Wud7xg83MaEFyjxQB1W5bz5d0IFppr+ymk=
github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
github.com/neelance/sourcemap v0.0.0-20200213170602-2833bce08e4c h1:bY6ktFuJkt+ZXkX0RChQch2FtHpWQLVS8Qo1YasiIVk=
github.com/neelance/sourcemap v0.0.0-20200213170602-2833bce08e4c/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749 h1:bUGsEnyNbVPw06Bs80sCeARAlK8lhwqGyi6UT8ymuGk=
github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/solarlune/resolv v0.5.1 h1:Ul6PAs/zaxiMUOEYz1Z6VeUj5k3CDcWMvSh+kivybDY=
github.com/solarlune/resolv v0.5.1/go.mod h1:HjM2f/0NoVjVdZsi26GtugX5aFbA62COEFEXkOhveRw=
github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v1.2.1 h1:+KmjbUw1hriSNMF55oPrkZcb27aECyrj8V2ytv7kWDw=
github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk=
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
go.uber.org/zap v1.23.0 h1:OjGQ5KQDEUawVHxNwQgPpiypGHOxo2mNZsOqTak4fFY=
go.uber.org/zap v1.23.0/go.mod h1:D+nX8jyLsMHMYrln8A0rJjFt/T/9/bGgIhAqxv5URuY=
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 h1:kUhD7nTDoI3fVd9G4ORWrbV5NY0liEs/Jg2pv5f+bBA=
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56 h1:estk1glOnSVeJ9tdEZZc5mAMDZk5lNJNyJ6DvrBkTEU=
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4=
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20220722155232-062f8c9fd539/go.mod h1:doUCurBvlfPMKfmIpRIywoHmhN3VyhnoFDbvIEWF4hY=
golang.org/x/image v0.0.0-20220902085622-e7cb96979f69 h1:Lj6HJGCSn5AjxRAH2+r35Mir4icalbqku+CLUtjnvXY=
golang.org/x/image v0.0.0-20220902085622-e7cb96979f69/go.mod h1:doUCurBvlfPMKfmIpRIywoHmhN3VyhnoFDbvIEWF4hY=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20190415191353-3e0bab5405d6/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mobile v0.0.0-20220722155234-aaac322e2105 h1:3vUV5x5+3LfQbgk7paCM6INOaJG9xXQbn79xoNkwfIk=
golang.org/x/mobile v0.0.0-20220722155234-aaac322e2105/go.mod h1:pe2sM7Uk+2Su1y7u/6Z8KJ24D7lepUjFZbhFOrmDfuQ=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190429190828-d89cdac9e872/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220712014510-0a85c31ab51e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@ -83,20 +451,200 @@ golang.org/x/sys v0.0.0-20220818161305-2296e01440c6 h1:Sx/u41w+OwrInGdEckYmEuU5g
golang.org/x/sys v0.0.0-20220818161305-2296e01440c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.0.0-20220411215600-e5f449aeb171 h1:EH1Deb8WZJ0xc0WK//leUHXcX9aLE5SymusoTmMZye8=
golang.org/x/term v0.0.0-20220411215600-e5f449aeb171/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.8-0.20211022200916-316ba0b74098/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU=
google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94=
google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=

View File

@ -11,13 +11,13 @@ import (
"github.com/hajimehoshi/ebiten/v2"
"github.com/hajimehoshi/ebiten/v2/ebitenutil"
"github.com/hajimehoshi/ebiten/v2/text"
"github.com/solarlune/resolv"
"golang.org/x/image/font"
"encoding/xml"
"io/ioutil"
"os"
"path/filepath"
"resolv"
. "dnmshared"
)

View File

@ -1,14 +1,13 @@
package main
import (
. "battle_srv/protos"
. "dnmshared"
. "dnmshared/sharedprotos"
"fmt"
"github.com/hajimehoshi/ebiten/v2"
"github.com/solarlune/resolv"
"go.uber.org/zap"
"image/color"
. "jsexport/battle"
"resolv"
)
type WorldColliderDisplay struct {
@ -39,21 +38,21 @@ func NewWorldColliderDisplay(game *Game, stageDiscreteW, stageDiscreteH, stageTi
playerDefaultSpeed := 1 * worldToVirtualGridRatio
minStep := (int(float64(playerDefaultSpeed)*virtualGridToWorldRatio) << 3)
playerColliderRadius := float64(12)
playerColliders := make([]*resolv.Object, len(playerPosList.Eles))
playerColliders := make([]*resolv.Object, len(playerPosList))
snapIntoPlatformOverlap := float64(0.1)
space := resolv.NewSpace(int(spaceW), int(spaceH), minStep, minStep)
topPadding, bottomPadding, leftPadding, rightPadding := snapIntoPlatformOverlap, snapIntoPlatformOverlap, snapIntoPlatformOverlap, snapIntoPlatformOverlap
for i, playerPos := range playerPosList.Eles {
for i, playerPos := range playerPosList {
colliderWidth, colliderHeight := playerColliderRadius*2, playerColliderRadius*4
playerCollider := GenerateRectCollider(playerPos.X, playerPos.Y, colliderWidth, colliderHeight, topPadding, bottomPadding, leftPadding, rightPadding, spaceOffsetX, spaceOffsetY, "Player") // [WARNING] Deliberately not using a circle because "resolv v0.5.1" doesn't yet align circle center with space cell center, regardless of the "specified within-object offset"
playerCollider := GenerateRectCollider(playerPos.X, playerPos.Y, colliderWidth, colliderHeight, topPadding, bottomPadding, leftPadding, rightPadding, spaceOffsetX, spaceOffsetY, nil, fmt.Sprintf("Player%d", i+1)) // [WARNING] Deliberately not using a circle because "resolv v0.5.1" doesn't yet align circle center with space cell center, regardless of the "specified within-object offset"
Logger.Info(fmt.Sprintf("Player Collider#%d: player world pos=(%.2f, %.2f), shape=%v", i, playerPos.X, playerPos.Y, ConvexPolygonStr(playerCollider.Shape.(*resolv.ConvexPolygon))))
playerColliders[i] = playerCollider
space.Add(playerCollider)
}
barrierLocalId := 0
for _, barrierUnaligned := range barrierList.Eles {
barrierCollider := GenerateConvexPolygonCollider(barrierUnaligned, spaceOffsetX, spaceOffsetY, "Barrier")
for _, barrierUnaligned := range barrierList {
barrierCollider := GenerateConvexPolygonCollider(barrierUnaligned, spaceOffsetX, spaceOffsetY, nil, "Barrier")
Logger.Debug(fmt.Sprintf("Added barrier: shape=%v", ConvexPolygonStr(barrierCollider.Shape.(*resolv.ConvexPolygon))))
space.Add(barrierCollider)
barrierLocalId++
@ -64,16 +63,14 @@ func NewWorldColliderDisplay(game *Game, stageDiscreteW, stageDiscreteH, stageTi
moveToCollide := true
if moveToCollide {
effPushback := Vec2D{X: float64(0), Y: float64(0)}
toTestPlayerCollider := playerColliders[0]
colliderWidth, colliderHeight := playerColliderRadius*2, playerColliderRadius*4
newVx, newVy := int32(-189000), int32(-497000)
toTestPlayerCollider.X, toTestPlayerCollider.Y = VirtualGridToPolygonColliderBLPos(newVx, newVy, colliderWidth, colliderHeight, topPadding, bottomPadding, leftPadding, rightPadding, spaceOffsetX, spaceOffsetY, virtualGridToWorldRatio)
playerColliders[1].X, playerColliders[1].Y = VirtualGridToPolygonColliderBLPos(int32(-165000), int32(-504000), colliderWidth, colliderHeight, topPadding, bottomPadding, leftPadding, rightPadding, spaceOffsetX, spaceOffsetY, virtualGridToWorldRatio)
playerColliders[1].Update()
playerColliders[0].X, playerColliders[0].Y = VirtualGridToPolygonColliderBLPos(int32(-139000), int32(-474500), colliderWidth, colliderHeight, topPadding, bottomPadding, leftPadding, rightPadding, spaceOffsetX, spaceOffsetY, virtualGridToWorldRatio)
playerColliders[0].Update()
Logger.Info(fmt.Sprintf("Checking collision for playerShape=%v", ConvexPolygonStr(toTestPlayerCollider.Shape.(*resolv.ConvexPolygon))))
playerColliders[1].X, playerColliders[1].Y = VirtualGridToPolygonColliderBLPos(int32(-163000), int32(-520000), colliderWidth, colliderHeight, topPadding, bottomPadding, leftPadding, rightPadding, spaceOffsetX, spaceOffsetY, virtualGridToWorldRatio)
playerColliders[1].Update()
toTestPlayerCollider.Update()
toTestPlayerCollider := playerColliders[1]
if collision := toTestPlayerCollider.Check(0, 0); collision != nil {
playerShape := toTestPlayerCollider.Shape.(*resolv.ConvexPolygon)
for _, obj := range collision.Objects {
@ -87,9 +84,9 @@ func NewWorldColliderDisplay(game *Game, stageDiscreteW, stageDiscreteH, stageTi
Logger.Warn(fmt.Sprintf("Collided BUT not overlapped: a=%v, b=%v, overlapResult=%v", ConvexPolygonStr(playerShape), ConvexPolygonStr(bShape), overlapResult))
}
}
toTestPlayerCollider.X -= effPushback.X
toTestPlayerCollider.Y -= effPushback.Y
toTestPlayerCollider.Update()
//toTestPlayerCollider.X -= effPushback.X
//toTestPlayerCollider.Y -= effPushback.Y
//toTestPlayerCollider.Update()
Logger.Info(fmt.Sprintf("effPushback={%v, %v}", effPushback.X, effPushback.Y))
}
}
@ -100,15 +97,11 @@ func NewWorldColliderDisplay(game *Game, stageDiscreteW, stageDiscreteH, stageTi
RecoveryFrames: int32(61),
RecoveryFramesOnBlock: int32(61),
RecoveryFramesOnHit: int32(61),
Moveforward: &Vec2D{
X: 0,
Y: 0,
},
HitboxOffset: float64(24.0),
HitboxSize: &Vec2D{
X: float64(45.0),
Y: float64(32.0),
},
SelfMoveforwardX: 0,
SelfMoveforwardY: 0,
HitboxOffset: float64(24.0),
HitboxSizeX: float64(45.0),
HitboxSizeY: float64(32.0),
// for defender
HitStunFrames: int32(18),
@ -120,9 +113,9 @@ func NewWorldColliderDisplay(game *Game, stageDiscreteW, stageDiscreteH, stageTi
bulletLeftToRight := false
if bulletLeftToRight {
xfac := float64(1.0)
offenderWx, offenderWy := playerPosList.Eles[0].X, playerPosList.Eles[0].Y
offenderWx, offenderWy := playerPosList[0].X, playerPosList[0].Y
bulletWx, bulletWy := offenderWx+xfac*meleeBullet.HitboxOffset, offenderWy
newBulletCollider := GenerateRectCollider(bulletWx, bulletWy, meleeBullet.HitboxSize.X, meleeBullet.HitboxSize.Y, topPadding, bottomPadding, leftPadding, rightPadding, spaceOffsetX, spaceOffsetY, "MeleeBullet")
newBulletCollider := GenerateRectCollider(bulletWx, bulletWy, meleeBullet.HitboxSizeX, meleeBullet.HitboxSizeY, topPadding, bottomPadding, leftPadding, rightPadding, spaceOffsetX, spaceOffsetY, nil, "MeleeBullet")
space.Add(newBulletCollider)
bulletShape := newBulletCollider.Shape.(*resolv.ConvexPolygon)
Logger.Warn(fmt.Sprintf("bullet ->: Added bullet collider to space: a=%v", ConvexPolygonStr(bulletShape)))
@ -142,10 +135,10 @@ func NewWorldColliderDisplay(game *Game, stageDiscreteW, stageDiscreteH, stageTi
bulletRightToLeft := false
if bulletRightToLeft {
xfac := float64(-1.0)
offenderWx, offenderWy := playerPosList.Eles[1].X, playerPosList.Eles[1].Y
offenderWx, offenderWy := playerPosList[1].X, playerPosList[1].Y
bulletWx, bulletWy := offenderWx+xfac*meleeBullet.HitboxOffset, offenderWy
newBulletCollider := GenerateRectCollider(bulletWx, bulletWy, meleeBullet.HitboxSize.X, meleeBullet.HitboxSize.Y, topPadding, bottomPadding, leftPadding, rightPadding, spaceOffsetX, spaceOffsetY, "MeleeBullet")
newBulletCollider := GenerateRectCollider(bulletWx, bulletWy, meleeBullet.HitboxSizeX, meleeBullet.HitboxSizeY, topPadding, bottomPadding, leftPadding, rightPadding, spaceOffsetX, spaceOffsetY, nil, "MeleeBullet")
space.Add(newBulletCollider)
bulletShape := newBulletCollider.Shape.(*resolv.ConvexPolygon)
Logger.Warn(fmt.Sprintf("bullet <-: Added bullet collider to space: a=%v", ConvexPolygonStr(bulletShape)))
@ -172,11 +165,14 @@ func (world *WorldColliderDisplay) Update() {
func (world *WorldColliderDisplay) Draw(screen *ebiten.Image) {
for _, o := range world.Space.Objects() {
if o.HasTags("Player") {
drawColor := color.RGBA{0, 255, 0, 255}
if o.HasTags("Player1") {
drawColor := color.RGBA{255, 0, 0, 255}
DrawPolygon(screen, o.Shape.(*resolv.ConvexPolygon), drawColor)
} else if o.HasTags("Player2") {
drawColor := color.RGBA{0, 0, 255, 255}
DrawPolygon(screen, o.Shape.(*resolv.ConvexPolygon), drawColor)
} else if o.HasTags("MeleeBullet") {
drawColor := color.RGBA{0, 0, 255, 255}
drawColor := color.RGBA{78, 255, 112, 255}
DrawPolygon(screen, o.Shape.(*resolv.ConvexPolygon), drawColor)
} else {
drawColor := color.RGBA{60, 60, 60, 255}

View File

@ -1,50 +0,0 @@
package dnmshared
import (
. "dnmshared/sharedprotos"
"math"
)
func NormVec2D(dx, dy float64) Vec2D {
return Vec2D{X: dy, Y: -dx}
}
func AlignPolygon2DToBoundingBox(input *Polygon2D) *Polygon2D {
// Transform again to put "anchor" at the "bottom-left point (w.r.t. world space)" of the bounding box for "resolv"
boundingBoxBL := &Vec2D{
X: math.MaxFloat64,
Y: math.MaxFloat64,
}
for _, p := range input.Points {
if p.X < boundingBoxBL.X {
boundingBoxBL.X = p.X
}
if p.Y < boundingBoxBL.Y {
boundingBoxBL.Y = p.Y
}
}
// Now "input.Anchor" should move to "input.Anchor+boundingBoxBL", thus "boundingBoxBL" is also the value of the negative diff for all "input.Points"
output := &Polygon2D{
Anchor: &Vec2D{
X: input.Anchor.X + boundingBoxBL.X,
Y: input.Anchor.Y + boundingBoxBL.Y,
},
Points: make([]*Vec2D, len(input.Points)),
}
for i, p := range input.Points {
output.Points[i] = &Vec2D{
X: p.X - boundingBoxBL.X,
Y: p.Y - boundingBoxBL.Y,
}
}
return output
}
func Distance(pt1 *Vec2D, pt2 *Vec2D) float64 {
dx := pt1.X - pt2.X
dy := pt1.Y - pt2.Y
return math.Sqrt(dx*dx + dy*dy)
}

View File

@ -1,3 +1,11 @@
module dnmshared
go 1.19
go 1.18
require (
resolv v0.0.0
)
replace (
resolv => ../resolv_tailored
)

View File

@ -1,14 +1,16 @@
package dnmshared
import (
. "dnmshared/sharedprotos"
"fmt"
"github.com/kvartborg/vector"
"github.com/solarlune/resolv"
"math"
. "jsexport/battle"
"resolv"
"strings"
)
func NormVec2D(dx, dy float64) Vec2D {
return Vec2D{X: dy, Y: -dx}
}
func ConvexPolygonStr(body *resolv.ConvexPolygon) string {
var s []string = make([]string, len(body.Points))
for i, p := range body.Points {
@ -21,249 +23,3 @@ func ConvexPolygonStr(body *resolv.ConvexPolygon) string {
func RectCenterStr(body *resolv.Object, halfBoundingW, halfBoundingH, topPadding, bottomPadding, leftPadding, rightPadding, spaceOffsetX, spaceOffsetY float64) string {
return fmt.Sprintf("{%.2f, %.2f}", body.X+leftPadding+halfBoundingW-spaceOffsetX, body.Y+bottomPadding+halfBoundingH-spaceOffsetY)
}
func GenerateRectCollider(wx, wy, w, h, topPadding, bottomPadding, leftPadding, rightPadding, spaceOffsetX, spaceOffsetY float64, tag string) *resolv.Object {
blX, blY := WorldToPolygonColliderBLPos(wx, wy, w*0.5, h*0.5, topPadding, bottomPadding, leftPadding, rightPadding, spaceOffsetX, spaceOffsetY)
return generateRectColliderInCollisionSpace(blX, blY, leftPadding+w+rightPadding, bottomPadding+h+topPadding, tag)
}
func generateRectColliderInCollisionSpace(blX, blY, w, h float64, tag string) *resolv.Object {
collider := resolv.NewObject(blX, blY, w, h, tag) // Unlike its frontend counter part, the position of a "resolv.Object" must be specified by "bottom-left point" because "w" and "h" must be positive, see "resolv.Object.BoundsToSpace" for details
shape := resolv.NewRectangle(0, 0, w, h)
collider.SetShape(shape)
return collider
}
func GenerateConvexPolygonCollider(unalignedSrc *Polygon2D, spaceOffsetX, spaceOffsetY float64, tag string) *resolv.Object {
aligned := AlignPolygon2DToBoundingBox(unalignedSrc)
var w, h float64 = 0, 0
shape := resolv.NewConvexPolygon()
for i, pi := range aligned.Points {
for j, pj := range aligned.Points {
if i == j {
continue
}
if math.Abs(pj.X-pi.X) > w {
w = math.Abs(pj.X - pi.X)
}
if math.Abs(pj.Y-pi.Y) > h {
h = math.Abs(pj.Y - pi.Y)
}
}
}
for i := 0; i < len(aligned.Points); i++ {
p := aligned.Points[i]
shape.AddPoints(p.X, p.Y)
}
collider := resolv.NewObject(aligned.Anchor.X+spaceOffsetX, aligned.Anchor.Y+spaceOffsetY, w, h, tag)
collider.SetShape(shape)
return collider
}
func CalcPushbacks(oldDx, oldDy float64, playerShape, barrierShape *resolv.ConvexPolygon) (bool, float64, float64, *SatResult) {
origX, origY := playerShape.Position()
defer func() {
playerShape.SetPosition(origX, origY)
}()
playerShape.SetPosition(origX+oldDx, origY+oldDy)
overlapResult := &SatResult{
Overlap: 0,
OverlapX: 0,
OverlapY: 0,
AContainedInB: true,
BContainedInA: true,
Axis: vector.Vector{0, 0},
}
if overlapped := isPolygonPairOverlapped(playerShape, barrierShape, overlapResult); overlapped {
pushbackX, pushbackY := overlapResult.Overlap*overlapResult.OverlapX, overlapResult.Overlap*overlapResult.OverlapY
return true, pushbackX, pushbackY, overlapResult
} else {
return false, 0, 0, overlapResult
}
}
type SatResult struct {
Overlap float64
OverlapX float64
OverlapY float64
AContainedInB bool
BContainedInA bool
Axis vector.Vector
}
func isPolygonPairOverlapped(a, b *resolv.ConvexPolygon, result *SatResult) bool {
aCnt, bCnt := len(a.Points), len(b.Points)
// Single point case
if 1 == aCnt && 1 == bCnt {
if nil != result {
result.Overlap = 0
}
return a.Points[0][0] == b.Points[0][0] && a.Points[0][1] == b.Points[0][1]
}
//Logger.Info(fmt.Sprintf("Checking collision between a=%v, b=%v", ConvexPolygonStr(a), ConvexPolygonStr(b)))
if 1 < aCnt {
for _, axis := range a.SATAxes() {
if isPolygonPairSeparatedByDir(a, b, axis.Unit(), result) {
return false
}
}
}
if 1 < bCnt {
for _, axis := range b.SATAxes() {
if isPolygonPairSeparatedByDir(a, b, axis.Unit(), result) {
return false
}
}
}
//Logger.Info(fmt.Sprintf("a=%v and b=%v are overlapped", ConvexPolygonStr(a), ConvexPolygonStr(b)))
return true
}
func isPolygonPairSeparatedByDir(a, b *resolv.ConvexPolygon, e vector.Vector, result *SatResult) bool {
/*
[WARNING] This function is deliberately made private, it shouldn't be used alone (i.e. not along the norms of a polygon), otherwise the pushbacks calculated would be meaningless.
Consider the following example
a: {
anchor: [1337.19 1696.74]
points: [[0 0] [24 0] [24 24] [0 24]]
},
b: {
anchor: [1277.72 1570.56]
points: [[642.57 319.16] [0 319.16] [5.73 0] [643.75 0.90]]
}
e = (-2.98, 1.49).Unit()
*/
//Logger.Info(fmt.Sprintf("Checking separation between a=%v, b=%v along axis e={%.3f, %.3f}#1", ConvexPolygonStr(a), ConvexPolygonStr(b), e[0], e[1]))
var aStart, aEnd, bStart, bEnd float64 = math.MaxFloat64, -math.MaxFloat64, math.MaxFloat64, -math.MaxFloat64
for _, p := range a.Points {
dot := (p[0]+a.X)*e[0] + (p[1]+a.Y)*e[1]
if aStart > dot {
aStart = dot
}
if aEnd < dot {
aEnd = dot
}
}
for _, p := range b.Points {
dot := (p[0]+b.X)*e[0] + (p[1]+b.Y)*e[1]
if bStart > dot {
bStart = dot
}
if bEnd < dot {
bEnd = dot
}
}
if aStart > bEnd || aEnd < bStart {
// Separated by unit vector "e"
return true
}
if nil != result {
overlap := float64(0)
if aStart < bStart {
result.AContainedInB = false
if aEnd < bEnd {
overlap = aEnd - bStart
result.BContainedInA = false
} else {
option1 := aEnd - bStart
option2 := bEnd - aStart
if option1 < option2 {
overlap = option1
} else {
overlap = -option2
}
}
} else {
result.BContainedInA = false
if aEnd > bEnd {
overlap = aStart - bEnd
result.AContainedInB = false
} else {
option1 := aEnd - bStart
option2 := bEnd - aStart
if option1 < option2 {
overlap = option1
} else {
overlap = -option2
}
}
}
currentOverlap := result.Overlap
absoluteOverlap := overlap
if overlap < 0 {
absoluteOverlap = -overlap
}
if (0 == result.Axis[0] && 0 == result.Axis[1]) || currentOverlap > absoluteOverlap {
var sign float64 = 1
if overlap < 0 {
sign = -1
}
result.Overlap = absoluteOverlap
result.OverlapX = e[0] * sign
result.OverlapY = e[1] * sign
}
result.Axis = e
//Logger.Info(fmt.Sprintf("Checking separation between a=%v, b=%v along axis e={%.3f, %.3f}#2: aStart=%.3f, aEnd=%.3f, bStart=%.3f, bEnd=%.3f, overlap=%.3f, currentOverlap=%.3f, absoluteOverlap=%.3f, result=%v", ConvexPolygonStr(a), ConvexPolygonStr(b), e[0], e[1], aStart, aEnd, bStart, bEnd, overlap, currentOverlap, absoluteOverlap, result))
}
// the specified unit vector "e" doesn't separate "a" and "b", overlap result is generated
return false
}
func WorldToVirtualGridPos(wx, wy, worldToVirtualGridRatio float64) (int32, int32) {
// [WARNING] Introduces loss of precision!
// In JavaScript floating numbers suffer from seemingly non-deterministic arithmetics, and even if certain libs solved this issue by approaches such as fixed-point-number, they might not be used in other libs -- e.g. the "collision libs" we're interested in -- thus couldn't kill all pains.
var virtualGridX int32 = int32(math.Round(wx * worldToVirtualGridRatio))
var virtualGridY int32 = int32(math.Round(wy * worldToVirtualGridRatio))
return virtualGridX, virtualGridY
}
func VirtualGridToWorldPos(vx, vy int32, virtualGridToWorldRatio float64) (float64, float64) {
// No loss of precision
var wx float64 = float64(vx) * virtualGridToWorldRatio
var wy float64 = float64(vy) * virtualGridToWorldRatio
return wx, wy
}
func WorldToPolygonColliderBLPos(wx, wy, halfBoundingW, halfBoundingH, topPadding, bottomPadding, leftPadding, rightPadding, collisionSpaceOffsetX, collisionSpaceOffsetY float64) (float64, float64) {
return wx - halfBoundingW - leftPadding + collisionSpaceOffsetX, wy - halfBoundingH - bottomPadding + collisionSpaceOffsetY
}
func PolygonColliderBLToWorldPos(cx, cy, halfBoundingW, halfBoundingH, topPadding, bottomPadding, leftPadding, rightPadding, collisionSpaceOffsetX, collisionSpaceOffsetY float64) (float64, float64) {
return cx + halfBoundingW + leftPadding - collisionSpaceOffsetX, cy + halfBoundingH + bottomPadding - collisionSpaceOffsetY
}
func PolygonColliderBLToVirtualGridPos(cx, cy, halfBoundingW, halfBoundingH, topPadding, bottomPadding, leftPadding, rightPadding, collisionSpaceOffsetX, collisionSpaceOffsetY float64, worldToVirtualGridRatio float64) (int32, int32) {
wx, wy := PolygonColliderBLToWorldPos(cx, cy, halfBoundingW, halfBoundingH, topPadding, bottomPadding, leftPadding, rightPadding, collisionSpaceOffsetX, collisionSpaceOffsetY)
return WorldToVirtualGridPos(wx, wy, worldToVirtualGridRatio)
}
func VirtualGridToPolygonColliderBLPos(vx, vy int32, halfBoundingW, halfBoundingH, topPadding, bottomPadding, leftPadding, rightPadding, collisionSpaceOffsetX, collisionSpaceOffsetY float64, virtualGridToWorldRatio float64) (float64, float64) {
wx, wy := VirtualGridToWorldPos(vx, vy, virtualGridToWorldRatio)
return WorldToPolygonColliderBLPos(wx, wy, halfBoundingW, halfBoundingH, topPadding, bottomPadding, leftPadding, rightPadding, collisionSpaceOffsetX, collisionSpaceOffsetY)
}

View File

@ -3,13 +3,13 @@ package dnmshared
import (
"bytes"
"compress/zlib"
. "dnmshared/sharedprotos"
"encoding/base64"
"encoding/xml"
"errors"
"fmt"
"go.uber.org/zap"
"io/ioutil"
. "jsexport/battle"
"math"
"strconv"
"strings"
@ -175,8 +175,8 @@ func (l *TmxLayer) decodeBase64() ([]uint32, error) {
return gids, nil
}
type StrToVec2DListMap map[string]*Vec2DList
type StrToPolygon2DListMap map[string]*Polygon2DList
type StrToVec2DListMap map[string](*[]*Vec2D)
type StrToPolygon2DListMap map[string](*[]*Polygon2D)
func tmxPolylineToPolygon2D(pTmxMapIns *TmxMap, singleObjInTmxFile *TmxOrTsxObject, targetPolyline *TmxOrTsxPolyline) (*Polygon2D, error) {
if nil == targetPolyline {
@ -321,21 +321,18 @@ func DeserializeTsxToColliderDict(pTmxMapIns *TmxMap, byteArrOfTsxFile []byte, f
theStrToPolygon2DListMap = gidBoundariesMap[globalGid]
}
var pThePolygon2DList *Polygon2DList
if _, ok := theStrToPolygon2DListMap[key]; ok {
pThePolygon2DList = theStrToPolygon2DListMap[key]
} else {
pThePolygon2DList = &Polygon2DList{
Eles: make([]*Polygon2D, 0),
}
theStrToPolygon2DListMap[key] = pThePolygon2DList
var pThePolygon2DList *[]*Polygon2D = nil
if _, ok := theStrToPolygon2DListMap[key]; !ok {
tmp := make([]*Polygon2D, 0)
theStrToPolygon2DListMap[key] = &tmp
}
pThePolygon2DList = theStrToPolygon2DListMap[key]
thePolygon2DFromPolyline, err := tsxPolylineToOffsetsWrtTileCenter(pTmxMapIns, singleObj, singleObj.Polyline, pTsxIns)
if nil != err {
panic(err)
}
pThePolygon2DList.Eles = append(pThePolygon2DList.Eles, thePolygon2DFromPolyline)
*pThePolygon2DList = append(*pThePolygon2DList, thePolygon2DFromPolyline)
}
}
return nil
@ -348,13 +345,10 @@ func ParseTmxLayersAndGroups(pTmxMapIns *TmxMap, gidBoundariesMap map[int]StrToP
for _, objGroup := range pTmxMapIns.ObjectGroups {
switch objGroup.Name {
case "PlayerStartingPos":
var pTheVec2DListToCache *Vec2DList
_, ok := toRetStrToVec2DListMap[objGroup.Name]
if false == ok {
pTheVec2DListToCache = &Vec2DList{
Eles: make([]*Vec2D, 0),
}
toRetStrToVec2DListMap[objGroup.Name] = pTheVec2DListToCache
var pTheVec2DListToCache *[]*Vec2D = nil
if _, ok := toRetStrToVec2DListMap[objGroup.Name]; !ok {
tmp := make([]*Vec2D, 0)
toRetStrToVec2DListMap[objGroup.Name] = &tmp
}
pTheVec2DListToCache = toRetStrToVec2DListMap[objGroup.Name]
for _, singleObjInTmxFile := range objGroup.Objects {
@ -363,18 +357,16 @@ func ParseTmxLayersAndGroups(pTmxMapIns *TmxMap, gidBoundariesMap map[int]StrToP
Y: singleObjInTmxFile.Y,
}
thePosInWorld := pTmxMapIns.continuousObjLayerOffsetToContinuousMapNodePos(theUntransformedPos)
pTheVec2DListToCache.Eles = append(pTheVec2DListToCache.Eles, &thePosInWorld)
*pTheVec2DListToCache = append(*pTheVec2DListToCache, &thePosInWorld)
}
case "Barrier":
// Note that in this case, the "Polygon2D.Anchor" of each "TmxOrTsxObject" is exactly overlapping with "Polygon2D.Points[0]".
var pThePolygon2DListToCache *Polygon2DList
_, ok := toRetStrToPolygon2DListMap[objGroup.Name]
if false == ok {
pThePolygon2DListToCache = &Polygon2DList{
Eles: make([]*Polygon2D, 0),
}
toRetStrToPolygon2DListMap[objGroup.Name] = pThePolygon2DListToCache
var pThePolygon2DListToCache *[]*Polygon2D = nil
if _, ok := toRetStrToPolygon2DListMap[objGroup.Name]; !ok {
tmp := make([]*Polygon2D, 0)
toRetStrToPolygon2DListMap[objGroup.Name] = &tmp
}
pThePolygon2DListToCache = toRetStrToPolygon2DListMap[objGroup.Name]
for _, singleObjInTmxFile := range objGroup.Objects {
if nil == singleObjInTmxFile.Polyline {
@ -408,7 +400,7 @@ func ParseTmxLayersAndGroups(pTmxMapIns *TmxMap, gidBoundariesMap map[int]StrToP
if nil != err {
panic(err)
}
pThePolygon2DListToCache.Eles = append(pThePolygon2DListToCache.Eles, thePolygon2DInWorld)
*pThePolygon2DListToCache = append(*pThePolygon2DListToCache, thePolygon2DInWorld)
}
default:
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,9 @@
{
"ver": "1.0.5",
"uuid": "d41313ca-b2c3-4436-a05f-7e0eb290b1e6",
"isPlugin": true,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": false,
"subMetas": {}
}

View File

@ -9,10 +9,10 @@
</data>
</layer>
<objectgroup id="1" name="PlayerStartingPos">
<object id="135" x="999" y="1608">
<object id="135" x="901" y="1579">
<point/>
</object>
<object id="137" x="875" y="1450">
<object id="137" x="861" y="1540">
<point/>
</object>
</objectgroup>

View File

@ -90,67 +90,71 @@ message MeleeBullet {
int32 recoveryFrames = 4;
int32 recoveryFramesOnBlock = 5;
int32 recoveryFramesOnHit = 6;
sharedprotos.Vec2D moveforward = 7;
double hitboxOffset = 8;
sharedprotos.Vec2D hitboxSize = 9;
int32 originatedRenderFrameId = 10;
double hitboxOffset = 7;
int32 originatedRenderFrameId = 8;
// for defender
int32 hitStunFrames = 11;
int32 blockStunFrames = 12;
double pushback = 13;
int32 hitStunFrames = 9;
int32 blockStunFrames = 10;
double pushback = 11;
int32 releaseTriggerType = 14; // 1: rising-edge, 2: falling-edge
int32 damage = 15;
int32 releaseTriggerType = 12; // 1: rising-edge, 2: falling-edge
int32 damage = 13;
int32 offenderJoinIndex = 16;
int32 offenderPlayerId = 17;
int32 offenderJoinIndex = 14;
int32 offenderPlayerId = 15;
double hitboxSizeX = 16;
double hitboxSizeY = 17;
double selfMoveforwardX = 18;
double selfMoveforwardY = 19;
}
message BattleColliderInfo {
string stageName = 1;
map<string, sharedprotos.Vec2DList> strToVec2DListMap = 2;
map<string, sharedprotos.Polygon2DList> strToPolygon2DListMap = 3;
int32 stageDiscreteW = 4;
int32 stageDiscreteH = 5;
int32 stageTileW = 6;
int32 stageTileH = 7;
int32 stageDiscreteW = 2;
int32 stageDiscreteH = 3;
int32 stageTileW = 4;
int32 stageTileH = 5;
int32 intervalToPing = 8;
int32 willKickIfInactiveFor = 9;
int32 boundRoomId = 10;
int32 battleDurationFrames = 12;
int64 battleDurationNanos = 13;
int32 serverFps = 14;
int32 inputDelayFrames = 15; // in the count of render frames
uint32 inputScaleFrames = 16; // inputDelayedAndScaledFrameId = ((originalFrameId - InputDelayFrames) >> InputScaleFrames)
int32 nstDelayFrames = 17; // network-single-trip delay in the count of render frames, proposed to be (InputDelayFrames >> 1) because we expect a round-trip delay to be exactly "InputDelayFrames"
int32 inputFrameUpsyncDelayTolerance = 18;
int32 maxChasingRenderFramesPerUpdate = 19;
int32 playerBattleState = 20;
double rollbackEstimatedDtMillis = 21;
int64 rollbackEstimatedDtNanos = 22;
int32 intervalToPing = 6;
int32 willKickIfInactiveFor = 7;
int32 boundRoomId = 8;
int32 battleDurationFrames = 9;
int64 battleDurationNanos = 10;
int32 serverFps = 11;
int32 inputDelayFrames = 12; // in the count of render frames
uint32 inputScaleFrames = 13; // inputDelayedAndScaledFrameId = ((originalFrameId - InputDelayFrames) >> InputScaleFrames)
int32 nstDelayFrames = 14; // network-single-trip delay in the count of render frames, proposed to be (InputDelayFrames >> 1) because we expect a round-trip delay to be exactly "InputDelayFrames"
int32 inputFrameUpsyncDelayTolerance = 15;
int32 maxChasingRenderFramesPerUpdate = 16;
int32 playerBattleState = 17;
double rollbackEstimatedDtMillis = 18;
int64 rollbackEstimatedDtNanos = 19;
double worldToVirtualGridRatio = 23;
double virtualGridToWorldRatio = 24;
double worldToVirtualGridRatio = 20;
double virtualGridToWorldRatio = 21;
int32 spAtkLookupFrames = 25;
int32 renderCacheSize = 26;
int32 spAtkLookupFrames = 22;
int32 renderCacheSize = 23;
map<int32, MeleeBullet> meleeSkillConfig = 27; // skillId -> skill
double snapIntoPlatformOverlap = 24;
double snapIntoPlatformThreshold = 25;
int32 jumpingInitVelY = 26;
int32 gravityX = 27;
int32 gravityY = 28;
int32 collisionMinStep = 29;
double snapIntoPlatformOverlap = 28;
double snapIntoPlatformThreshold = 29;
int32 jumpingInitVelY = 30;
int32 gravityX = 31;
int32 gravityY = 32;
bool frameDataLoggingEnabled = 999;
}
message RoomDownsyncFrame {
int32 id = 1;
map<int32, PlayerDownsync> players = 2;
repeated PlayerDownsync playersArr = 2;
int64 countdownNanos = 3;
repeated MeleeBullet meleeBullets = 4; // I don't know how to mimic inheritance/composition in protobuf by far, thus using an array for each type of bullet as a compromise
uint64 backendUnconfirmedMask = 5; // Indexed by "joinIndex", same compression concern as stated in InputFrameDownsync
bool shouldForceResync = 6;
map<int32, int32> playerOpPatternToSkillId = 7;
}

View File

@ -97,7 +97,7 @@
"__id__": 1
},
"_children": [],
"_active": false,
"_active": true,
"_components": [
{
"__id__": 3
@ -129,7 +129,7 @@
"ctor": "Float64Array",
"array": [
0,
0,
45,
0,
0,
0,

View File

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

View File

@ -58,7 +58,7 @@
"_groupIndex": 0,
"groupIndex": 0,
"autoReleaseAssets": false,
"_id": "368b10b6-88fc-423c-9fcd-545d9fc673bd"
"_id": "8491a86c-bec9-4813-968a-128ca01639e0"
},
{
"__type__": "cc.Node",
@ -249,7 +249,7 @@
"_id": "3crA1nz5xPSLAnCSLQIPOq"
},
{
"__type__": "47d7dy4S4lB2pxqJJlGOoai",
"__type__": "b3810kDSWtD14RhiYzulYVI",
"_name": "",
"_objFlags": 0,
"node": {
@ -278,7 +278,8 @@
"renderFrameIdLagTolerance": 4,
"jigglingEps1D": 0.001,
"bulletTriggerEnabled": true,
"_id": "4b+kZ46VhC0LCBixXEK2dk"
"closeOnForcedtoResyncNotSelf": true,
"_id": "e5xQdv12xLoIRr0b36Pie+"
},
{
"__type__": "cc.Node",

View File

@ -1,6 +1,6 @@
{
"ver": "1.2.5",
"uuid": "368b10b6-88fc-423c-9fcd-545d9fc673bd",
"uuid": "8491a86c-bec9-4813-968a-128ca01639e0",
"asyncLoadAssets": false,
"autoReleaseAssets": false,
"subMetas": {}

View File

@ -67,7 +67,6 @@ cc.Class({
this.speciesName = null;
this.hp = 100;
this.maxHp = 100;
this.framesToRecover = 0;
this.inAir = true;
},
@ -86,18 +85,18 @@ cc.Class({
},
updateCharacterAnim(rdfPlayer, prevRdfPlayer, forceAnimSwitch) {
// As this function might be called after many frames of a rollback, it's possible that the playing animation was predicted, different from "prevRdfPlayer.characterState" but same as "newCharacterState". More granular checks are needed to determine whether we should interrupt the playing animation.
// As this function might be called after many frames of a rollback, it's possible that the playing animation was predicted, different from "prevRdfPlayer.CharacterState" but same as "newCharacterState". More granular checks are needed to determine whether we should interrupt the playing animation.
// Update directions
if (this.animComp && this.animComp.node) {
if (0 > rdfPlayer.dirX) {
if (0 > rdfPlayer.DirX) {
this.animComp.node.scaleX = (-1.0);
} else if (0 < rdfPlayer.dirX) {
} else if (0 < rdfPlayer.DirX) {
this.animComp.node.scaleX = (1.0);
}
}
let newCharacterState = rdfPlayer.characterState;
let newCharacterState = rdfPlayer.CharacterState;
let newAnimName = window.ATK_CHARACTER_STATE_ARR[newCharacterState][1];
let playingAnimName = null;
let underlyingAnimationCtrl = null;
@ -110,7 +109,7 @@ cc.Class({
playingAnimName = (!underlyingAnimationCtrl ? null : underlyingAnimationCtrl.name);
}
// It turns out that "prevRdfPlayer.characterState" is not useful in this function :)
// It turns out that "prevRdfPlayer.CharacterState" is not useful in this function :)
if (newAnimName == playingAnimName && window.ATK_CHARACTER_STATE_INTERRUPT_WAIVE_SET.has(newCharacterState)) {
// No need to interrupt
// console.warn(`JoinIndex=${rdfPlayer.joinIndex}, not interrupting ${newAnimName} while the playing anim is also ${playingAnimName}, player rdf changed from: ${null == prevRdfPlayer ? null : JSON.stringify(prevRdfPlayer)}, to: ${JSON.stringify(rdfPlayer)}`);
@ -131,7 +130,7 @@ cc.Class({
underlyingAnimationCtrl.gotoAndPlayByFrame(newAnimName, 0, -1);
} else {
const animationData = underlyingAnimationCtrl._animations[newAnimName];
let fromAnimFrame = (animationData.frameCount - rdfPlayer.framesToRecover);
let fromAnimFrame = (animationData.frameCount - rdfPlayer.FramesToRecover);
if (fromAnimFrame < 0) {
// For Atk1 or Atk2, it's possible that the "meleeBullet.recoveryFrames" is configured to be slightly larger than corresponding animation duration frames
fromAnimFrame = 0;
@ -149,7 +148,7 @@ cc.Class({
}
// The "playTimes" counterpart is managed by each "cc.AnimationClip.wrapMode", already preset in the editor.
const targetClip = this.animComp.getClips()[newCharacterState]; // The clips follow the exact order in ATK_CHARACTER_STATE
let fromTime = (targetClip.duration - rdfPlayer.framesToRecover / targetClip.sample); // TODO: Anyway to avoid using division here?
let fromTime = (targetClip.duration - rdfPlayer.FramesToRecover / targetClip.sample); // TODO: Anyway to avoid using division here?
if (fromTime < 0) {
// For Atk1 or Atk2, it's possible that the "meleeBullet.recoveryFrames" is configured to be slightly larger than corresponding animation duration frames
fromTime = 0;

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -13,7 +13,7 @@ cc.Class({
onLoad() {
const self = this;
window.mapIns = self;
self.showCriticalCoordinateLabels = true;
self.showCriticalCoordinateLabels = false;
const mapNode = self.node;
const canvasNode = mapNode.parent;
@ -31,6 +31,7 @@ cc.Class({
self.inputDelayFrames = 8;
self.inputScaleFrames = 2;
self.inputFrameUpsyncDelayTolerance = 2;
self.collisionMinStep = 8;
self.renderCacheSize = 1024;
self.serverFps = 60;
@ -41,32 +42,11 @@ cc.Class({
self.worldToVirtualGridRatio = 1000;
self.virtualGridToWorldRatio = 1.0 / self.worldToVirtualGridRatio;
self.meleeSkillConfig = {
1: {
// for offender
startupFrames: 10,
activeFrames: 20,
recoveryFrames: 34, // usually but not always "startupFrames+activeFrames", I hereby set it to be 1 frame more than the actual animation to avoid critical transition, i.e. when the animation is 1 frame from ending but "rdfPlayer.framesToRecover" is already counted 0 and the player triggers an other same attack, making an effective bullet trigger but no animation is played due to same animName is still playing
recoveryFramesOnBlock: 34,
recoveryFramesOnHit: 34,
moveforward: {
x: 0,
y: 0,
},
hitboxOffset: 12.0, // should be about the radius of the PlayerCollider
hitboxSize: {
x: 23.0,
y: 32.0,
},
// for defender
hitStunFrames: 18,
blockStunFrames: 9,
pushback: 8.0,
releaseTriggerType: 1, // 1: rising-edge, 2: falling-edge
damage: 5
}
};
const opJoinIndexPrefix1 = (1 << 8);
const opJoinIndexPrefix2 = (2 << 8);
self.playerOpPatternToSkillId = {};
self.playerOpPatternToSkillId[opJoinIndexPrefix1 + 0] = 1;
self.playerOpPatternToSkillId[opJoinIndexPrefix2 + 0] = 1;
/*
[WARNING] As when a character is standing on a barrier, if not carefully curated there MIGHT BE a bouncing sequence of "[(inAir -> dropIntoBarrier ->), (notInAir -> pushedOutOfBarrier ->)], [(inAir -> ..."
@ -76,7 +56,7 @@ cc.Class({
self.snapIntoPlatformOverlap = 0.1;
self.snapIntoPlatformThreshold = 0.5; // a platform must be "horizontal enough" for a character to "stand on"
self.jumpingInitVelY = 7 * self.worldToVirtualGridRatio; // unit: (virtual grid length/renderFrame)
[self.gravityX, self.gravityY] = [0, -0.5*self.worldToVirtualGridRatio]; // unit: (virtual grid length/renderFrame^2)
[self.gravityX, self.gravityY] = [0, -0.5 * self.worldToVirtualGridRatio]; // unit: (virtual grid length/renderFrame^2)
const tiledMapIns = self.node.getComponent(cc.TiledMap);
@ -89,7 +69,6 @@ cc.Class({
tiledMapIns.tmxAsset = null;
mapNode.removeAllChildren();
self._resetCurrentMatch();
if (self.showCriticalCoordinateLabels) {
const drawer = new cc.Node();
@ -101,25 +80,30 @@ cc.Class({
self.g = g;
}
tiledMapIns.tmxAsset = tmxAsset;
const newMapSize = tiledMapIns.getMapSize();
const newTileSize = tiledMapIns.getTileSize();
self.node.setContentSize(newMapSize.width * newTileSize.width, newMapSize.height * newTileSize.height);
self.node.setPosition(cc.v2(0, 0));
self.stageDiscreteW = newMapSize.width;
self.stageDiscreteH = newMapSize.height;
self.stageTileW = newTileSize.width;
self.stageTileH = newTileSize.height;
self._resetCurrentMatch();
let barrierIdCounter = 0;
const boundaryObjs = tileCollisionManager.extractBoundaryObjects(self.node);
for (let boundaryObj of boundaryObjs.barriers) {
const x0 = boundaryObj.anchor.x,
y0 = boundaryObj.anchor.y;
const gopkgsBoundaryAnchor = gopkgs.NewVec2DJs(boundaryObj.anchor.x, boundaryObj.anchor.y);
const gopkgsBoundaryPts = Array.from(boundaryObj, p => {
return gopkgs.NewVec2DJs(p.x, p.y);
});
const gopkgsBoundary = gopkgs.NewPolygon2DJs(gopkgsBoundaryAnchor, gopkgsBoundaryPts);
const gopkgsBarrier = gopkgs.NewBarrierJs(gopkgsBoundary);
const newBarrier = self.collisionSys.createPolygon(x0, y0, Array.from(boundaryObj, p => {
return [p.x, p.y];
}));
newBarrier.data = {
hardPushback: true
};
const newBarrierCollider = gopkgs.GenerateConvexPolygonColliderJs(gopkgsBoundary, self.spaceOffsetX, self.spaceOffsetY, gopkgsBarrier, "Barrier");
self.gopkgsCollisionSys.Add(newBarrierCollider);
if (false && self.showCriticalCoordinateLabels) {
for (let i = 0; i < boundaryObj.length; ++i) {
@ -152,20 +136,20 @@ cc.Class({
}
}
// console.log("Created barrier: ", newBarrier);
// console.log("Created barrier: ", newBarrierCollider);
++barrierIdCounter;
const collisionBarrierIndex = (self.collisionBarrierIndexPrefix + barrierIdCounter);
self.collisionSysMap.set(collisionBarrierIndex, newBarrier);
self.gopkgsCollisionSysMap[collisionBarrierIndex] = newBarrierCollider;
}
const startRdf = window.pb.protos.RoomDownsyncFrame.create({
id: window.MAGIC_ROOM_DOWNSYNC_FRAME_ID.BATTLE_START,
players: {
10: window.pb.protos.PlayerDownsync.create({
playersArr: [
window.pb.protos.PlayerDownsync.create({
id: 10,
joinIndex: 1,
virtualGridX: self.worldToVirtualGridPos(boundaryObjs.playerStartingPositions[0].x, boundaryObjs.playerStartingPositions[0].y)[0],
virtualGridY: self.worldToVirtualGridPos(boundaryObjs.playerStartingPositions[0].x, boundaryObjs.playerStartingPositions[0].y)[1],
virtualGridX: boundaryObjs.playerStartingPositions[0].x * self.worldToVirtualGridRatio,
virtualGridY: boundaryObjs.playerStartingPositions[0].y * self.worldToVirtualGridRatio,
speed: 1 * self.worldToVirtualGridRatio,
colliderRadius: 12,
characterState: window.ATK_CHARACTER_STATE.InAirIdle1[0],
@ -176,11 +160,11 @@ cc.Class({
velY: 0,
inAir: true,
}),
11: window.pb.protos.PlayerDownsync.create({
window.pb.protos.PlayerDownsync.create({
id: 11,
joinIndex: 2,
virtualGridX: self.worldToVirtualGridPos(boundaryObjs.playerStartingPositions[1].x, boundaryObjs.playerStartingPositions[1].y)[0],
virtualGridY: self.worldToVirtualGridPos(boundaryObjs.playerStartingPositions[1].x, boundaryObjs.playerStartingPositions[1].y)[1],
virtualGridX: boundaryObjs.playerStartingPositions[1].x * self.worldToVirtualGridRatio,
virtualGridY: boundaryObjs.playerStartingPositions[1].y * self.worldToVirtualGridRatio,
speed: 1 * self.worldToVirtualGridRatio,
colliderRadius: 12,
characterState: window.ATK_CHARACTER_STATE.InAirIdle1[0],
@ -191,12 +175,13 @@ cc.Class({
velY: 0,
inAir: true,
}),
}
]
});
self.selfPlayerInfo = {
id: 11
Id: 10,
JoinIndex: 1,
};
self._initPlayerRichInfoDict(startRdf.players);
self.onRoomDownsyncFrame(startRdf);
self.battleState = ALL_BATTLE_STATES.IN_BATTLE;
@ -219,12 +204,12 @@ cc.Class({
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);
const prevAndCurrInputs = self.getOrPrefabInputFrameUpsync(noDelayInputFrameId);
prevSelfInput = prevAndCurrInputs[0];
currSelfInput = prevAndCurrInputs[1];
}
const [prevRdf, rdf] = self.rollbackAndChase(self.renderFrameId, self.renderFrameId + 1, self.collisionSys, self.collisionSysMap, false);
const [prevRdf, rdf] = self.rollbackAndChase(self.renderFrameId, self.renderFrameId + 1, self.gopkgsCollisionSys, self.gopkgsCollisionSysMap, false);
self.applyRoomDownsyncFrameDynamics(rdf, prevRdf);
self.showDebugBoundaries(rdf);
++self.renderFrameId;
@ -235,4 +220,5 @@ cc.Class({
}
}
},
});

View File

@ -1,6 +1,6 @@
{
"ver": "1.0.5",
"uuid": "47d7dcb8-4b89-41da-9c6a-2499463a86a2",
"uuid": "b3810903-496b-43d7-8461-898cee958548",
"isPlugin": false,
"loadPluginInWeb": true,
"loadPluginInNative": true,

View File

@ -197,6 +197,9 @@ window.initPersistentSessionClient = function(onopenCb, expectedRoomId) {
break;
case constants.RET_CODE.BATTLE_STOPPED:
// deliberately do nothing
if (mapIns.frameDataLoggingEnabled) {
console.warn(`${mapIns._stringifyRdfIdToActuallyUsedInput()}`);
}
break;
case constants.RET_CODE.PLAYER_NOT_ADDABLE_TO_ROOM:
case constants.RET_CODE.PLAYER_NOT_READDABLE_TO_ROOM:
@ -211,7 +214,9 @@ window.initPersistentSessionClient = function(onopenCb, expectedRoomId) {
case constants.RET_CODE.PLAYER_NOT_FOUND:
case constants.RET_CODE.PLAYER_CHEATING:
case 1006: // Peer(i.e. the backend) gone unexpectedly
console.warn(`${mapIns._stringifyRecentInputAndRenderCacheCorrespondingly()}`);
if (mapIns.frameDataLoggingEnabled) {
console.warn(`${mapIns._stringifyRdfIdToActuallyUsedInput()}`);
}
window.clearLocalStorageAndBackToLoginScene(true);
break;
default:

View File

@ -17,7 +17,7 @@
},
"encryptJs": true,
"excludeScenes": [
"368b10b6-88fc-423c-9fcd-545d9fc673bd"
"8491a86c-bec9-4813-968a-128ca01639e0"
],
"fb-instant-games": {},
"includeSDKBox": false,

View File

@ -68,7 +68,7 @@
"shelter_z_reducer",
"shelter"
],
"last-module-event-record-time": 1671346284377,
"last-module-event-record-time": 1672287736326,
"simulator-orientation": false,
"simulator-resolution": {
"height": 640,

28
jsexport/Makefile Normal file
View File

@ -0,0 +1,28 @@
PROJECTNAME=jsexport
ROOT_DIR=.
all: help
## Available proxies for downloading go modules are listed in "https://github.com/golang/go/wiki/Modules#how-do-i-use-vendoring-with-modules-is-vendoring-going-away".
#GOPROXY=https://mirrors.aliyun.com/goproxy
GOPROXY=https://goproxy.io
serve:
gopherjs clean
gopherjs serve $(PROJECTNAME)
build:
gopherjs build $(PROJECTNAME)
rm ../frontend/assets/plugin_scripts/jsexport.js && mv ./jsexport.js ../frontend/assets/plugin_scripts/jsexport.js
build-min:
gopherjs build -m $(PROJECTNAME)
rm ../frontend/assets/plugin_scripts/jsexport.js && mv ./jsexport.js ../frontend/assets/plugin_scripts/jsexport.js
.PHONY: help
help: Makefile
@echo
@echo " Choose a command run:"
@echo
@sed -n 's/^##//p' $< | column -t -s ':' | sed -e 's/^/ /'
@echo

7
jsexport/README.md Normal file
View File

@ -0,0 +1,7 @@
GopherJs is supposed to be run by `go run`.
If on-the-fly compilation is needed, run `gopherjs serve jsexport` and then visit `http://localhost:8080/jsexport.js` -- if 404 not found is responded, run `gopherjs build` to check syntax errors.
See the `Makefile` for more options.
Kindly note that the sources of the greate opensource projects [resolv](https://github.com/SolarLune/resolv) and [vector](https://github.com/quartercastle/vector) are copied and modified here to reduce the size of generated js codes, i.e. standard libs `fmt`, `error`, `pb`(including standard libs `sync` and `reflect`) are deliberately avoided from scratch.

713
jsexport/battle/battle.go Normal file
View File

@ -0,0 +1,713 @@
package battle
import (
"math"
"resolv"
)
const (
MAX_FLOAT64 = 1.7e+308
COLLISION_PLAYER_INDEX_PREFIX = (1 << 17)
COLLISION_BARRIER_INDEX_PREFIX = (1 << 16)
COLLISION_BULLET_INDEX_PREFIX = (1 << 15)
PATTERN_ID_UNABLE_TO_OP = -2
PATTERN_ID_NO_OP = -1
)
// These directions are chosen such that when speed is changed to "(speedX+delta, speedY+delta)" for any of them, the direction is unchanged.
var DIRECTION_DECODER = [][]int32{
{0, 0},
{0, +2},
{0, -2},
{+2, 0},
{-2, 0},
{+1, +1},
{-1, -1},
{+1, -1},
{-1, +1},
}
var skillIdToBullet = map[int]interface{}{
1: &MeleeBullet{
Bullet: Bullet{
// for offender
StartupFrames: int32(5),
ActiveFrames: int32(10),
RecoveryFrames: int32(34),
RecoveryFramesOnBlock: int32(34),
RecoveryFramesOnHit: int32(34),
HitboxOffset: float64(12.0), // should be about the radius of the PlayerCollider
// for defender
HitStunFrames: int32(18),
BlockStunFrames: int32(9),
Pushback: float64(8.0),
ReleaseTriggerType: int32(1), // 1: rising-edge, 2: falling-edge
Damage: int32(5),
SelfMoveforwardX: 0,
SelfMoveforwardY: 0,
HitboxSizeX: 24.0,
HitboxSizeY: 32.0,
},
},
}
const (
ATK_CHARACTER_STATE_IDLE1 = int32(0)
ATK_CHARACTER_STATE_WALKING = int32(1)
ATK_CHARACTER_STATE_ATK1 = int32(2)
ATK_CHARACTER_STATE_ATKED1 = int32(3)
ATK_CHARACTER_STATE_INAIR_IDLE1 = int32(4)
ATK_CHARACTER_STATE_INAIR_ATK1 = int32(5)
ATK_CHARACTER_STATE_INAIR_ATKED1 = int32(6)
)
func ConvertToInputFrameId(renderFrameId int32, inputDelayFrames int32, inputScaleFrames uint32) int32 {
if renderFrameId < inputDelayFrames {
return 0
}
return ((renderFrameId - inputDelayFrames) >> inputScaleFrames)
}
func decodeInput(encodedInput uint64) *InputFrameDecoded {
encodedDirection := (encodedInput & uint64(15))
btnALevel := int32((encodedInput >> 4) & 1)
btnBLevel := int32((encodedInput >> 5) & 1)
return &InputFrameDecoded{
Dx: DIRECTION_DECODER[encodedDirection][0],
Dy: DIRECTION_DECODER[encodedDirection][1],
BtnALevel: btnALevel,
BtnBLevel: btnBLevel,
}
}
type SatResult struct {
Overlap float64
OverlapX float64
OverlapY float64
AContainedInB bool
BContainedInA bool
Axis resolv.Vector
}
func CalcPushbacks(oldDx, oldDy float64, playerShape, barrierShape *resolv.ConvexPolygon) (bool, float64, float64, *SatResult) {
origX, origY := playerShape.Position()
defer func() {
playerShape.SetPosition(origX, origY)
}()
playerShape.SetPosition(origX+oldDx, origY+oldDy)
overlapResult := &SatResult{
Overlap: 0,
OverlapX: 0,
OverlapY: 0,
AContainedInB: true,
BContainedInA: true,
Axis: resolv.Vector{0, 0},
}
if overlapped := isPolygonPairOverlapped(playerShape, barrierShape, overlapResult); overlapped {
pushbackX, pushbackY := overlapResult.Overlap*overlapResult.OverlapX, overlapResult.Overlap*overlapResult.OverlapY
return true, pushbackX, pushbackY, overlapResult
} else {
return false, 0, 0, overlapResult
}
}
func isPolygonPairOverlapped(a, b *resolv.ConvexPolygon, result *SatResult) bool {
aCnt, bCnt := len(a.Points), len(b.Points)
// Single point case
if 1 == aCnt && 1 == bCnt {
if nil != result {
result.Overlap = 0
}
return a.Points[0][0] == b.Points[0][0] && a.Points[0][1] == b.Points[0][1]
}
if 1 < aCnt {
for _, axis := range a.SATAxes() {
if isPolygonPairSeparatedByDir(a, b, axis.Unit(), result) {
return false
}
}
}
if 1 < bCnt {
for _, axis := range b.SATAxes() {
if isPolygonPairSeparatedByDir(a, b, axis.Unit(), result) {
return false
}
}
}
return true
}
func isPolygonPairSeparatedByDir(a, b *resolv.ConvexPolygon, e resolv.Vector, result *SatResult) bool {
/*
[WARNING] This function is deliberately made private, it shouldn't be used alone (i.e. not along the norms of a polygon), otherwise the pushbacks calculated would be meaningless.
Consider the following example
a: {
anchor: [1337.19 1696.74]
points: [[0 0] [24 0] [24 24] [0 24]]
},
b: {
anchor: [1277.72 1570.56]
points: [[642.57 319.16] [0 319.16] [5.73 0] [643.75 0.90]]
}
e = (-2.98, 1.49).Unit()
*/
var aStart, aEnd, bStart, bEnd float64 = MAX_FLOAT64, -MAX_FLOAT64, MAX_FLOAT64, -MAX_FLOAT64
for _, p := range a.Points {
dot := (p[0]+a.X)*e[0] + (p[1]+a.Y)*e[1]
if aStart > dot {
aStart = dot
}
if aEnd < dot {
aEnd = dot
}
}
for _, p := range b.Points {
dot := (p[0]+b.X)*e[0] + (p[1]+b.Y)*e[1]
if bStart > dot {
bStart = dot
}
if bEnd < dot {
bEnd = dot
}
}
if aStart > bEnd || aEnd < bStart {
// Separated by unit vector "e"
return true
}
if nil != result {
overlap := float64(0)
if aStart < bStart {
result.AContainedInB = false
if aEnd < bEnd {
overlap = aEnd - bStart
result.BContainedInA = false
} else {
option1 := aEnd - bStart
option2 := bEnd - aStart
if option1 < option2 {
overlap = option1
} else {
overlap = -option2
}
}
} else {
result.BContainedInA = false
if aEnd > bEnd {
overlap = aStart - bEnd
result.AContainedInB = false
} else {
option1 := aEnd - bStart
option2 := bEnd - aStart
if option1 < option2 {
overlap = option1
} else {
overlap = -option2
}
}
}
currentOverlap := result.Overlap
absoluteOverlap := overlap
if overlap < 0 {
absoluteOverlap = -overlap
}
if (0 == result.Axis[0] && 0 == result.Axis[1]) || currentOverlap > absoluteOverlap {
var sign float64 = 1
if overlap < 0 {
sign = -1
}
result.Overlap = absoluteOverlap
result.OverlapX = e[0] * sign
result.OverlapY = e[1] * sign
}
result.Axis = e
}
// the specified unit vector "e" doesn't separate "a" and "b", overlap result is generated
return false
}
func WorldToVirtualGridPos(wx, wy, worldToVirtualGridRatio float64) (int32, int32) {
// [WARNING] Introduces loss of precision!
// In JavaScript floating numbers suffer from seemingly non-deterministic arithmetics, and even if certain libs solved this issue by approaches such as fixed-point-number, they might not be used in other libs -- e.g. the "collision libs" we're interested in -- thus couldn't kill all pains.
var virtualGridX int32 = int32(math.Floor(wx * worldToVirtualGridRatio))
var virtualGridY int32 = int32(math.Floor(wy * worldToVirtualGridRatio))
return virtualGridX, virtualGridY
}
func VirtualGridToWorldPos(vx, vy int32, virtualGridToWorldRatio float64) (float64, float64) {
// No loss of precision
var wx float64 = float64(vx) * virtualGridToWorldRatio
var wy float64 = float64(vy) * virtualGridToWorldRatio
return wx, wy
}
func WorldToPolygonColliderBLPos(wx, wy, halfBoundingW, halfBoundingH, topPadding, bottomPadding, leftPadding, rightPadding, collisionSpaceOffsetX, collisionSpaceOffsetY float64) (float64, float64) {
return wx - halfBoundingW - leftPadding + collisionSpaceOffsetX, wy - halfBoundingH - bottomPadding + collisionSpaceOffsetY
}
func PolygonColliderBLToWorldPos(cx, cy, halfBoundingW, halfBoundingH, topPadding, bottomPadding, leftPadding, rightPadding, collisionSpaceOffsetX, collisionSpaceOffsetY float64) (float64, float64) {
return cx + halfBoundingW + leftPadding - collisionSpaceOffsetX, cy + halfBoundingH + bottomPadding - collisionSpaceOffsetY
}
func PolygonColliderBLToVirtualGridPos(cx, cy, halfBoundingW, halfBoundingH, topPadding, bottomPadding, leftPadding, rightPadding, collisionSpaceOffsetX, collisionSpaceOffsetY float64, worldToVirtualGridRatio float64) (int32, int32) {
wx, wy := PolygonColliderBLToWorldPos(cx, cy, halfBoundingW, halfBoundingH, topPadding, bottomPadding, leftPadding, rightPadding, collisionSpaceOffsetX, collisionSpaceOffsetY)
return WorldToVirtualGridPos(wx, wy, worldToVirtualGridRatio)
}
func VirtualGridToPolygonColliderBLPos(vx, vy int32, halfBoundingW, halfBoundingH, topPadding, bottomPadding, leftPadding, rightPadding, collisionSpaceOffsetX, collisionSpaceOffsetY float64, virtualGridToWorldRatio float64) (float64, float64) {
wx, wy := VirtualGridToWorldPos(vx, vy, virtualGridToWorldRatio)
return WorldToPolygonColliderBLPos(wx, wy, halfBoundingW, halfBoundingH, topPadding, bottomPadding, leftPadding, rightPadding, collisionSpaceOffsetX, collisionSpaceOffsetY)
}
func calcHardPushbacksNorms(joinIndex int32, playerCollider *resolv.Object, playerShape *resolv.ConvexPolygon, snapIntoPlatformOverlap float64, pEffPushback *Vec2D) *[]Vec2D {
ret := make([]Vec2D, 0, 10) // no one would simultaneously have more than 5 hardPushbacks
collision := playerCollider.Check(0, 0)
if nil == collision {
return &ret
}
//playerColliderCenterX, playerColliderCenterY := playerCollider.Center()
//fmt.Printf("joinIndex=%d calcHardPushbacksNorms has non-empty collision;playerColliderPos=(%.2f,%.2f)\n", joinIndex, playerColliderCenterX, playerColliderCenterY)
for _, obj := range collision.Objects {
isBarrier := false
switch obj.Data.(type) {
case *PlayerDownsync:
case *MeleeBullet:
default:
// By default it's a regular barrier, even if data is nil, note that Golang syntax of switch-case is kind of confusing, this "default" condition is met only if "!*PlayerDownsync && !*MeleeBullet".
isBarrier = true
}
if !isBarrier {
continue
}
barrierShape := obj.Shape.(*resolv.ConvexPolygon)
overlapped, pushbackX, pushbackY, overlapResult := CalcPushbacks(0, 0, playerShape, barrierShape)
if !overlapped {
continue
}
// ALWAY snap into hardPushbacks!
// [OverlapX, OverlapY] is the unit vector that points into the platform
pushbackX, pushbackY = (overlapResult.Overlap-snapIntoPlatformOverlap)*overlapResult.OverlapX, (overlapResult.Overlap-snapIntoPlatformOverlap)*overlapResult.OverlapY
ret = append(ret, Vec2D{X: overlapResult.OverlapX, Y: overlapResult.OverlapY})
pEffPushback.X += pushbackX
pEffPushback.Y += pushbackY
//fmt.Printf("joinIndex=%d calcHardPushbacksNorms found one hardpushback; immediatePushback=(%.2f,%.2f)\n", joinIndex, pushbackX, pushbackY)
}
return &ret
}
func deriveOpPattern(currPlayerDownsync, thatPlayerInNextFrame *PlayerDownsync, currRenderFrame *RoomDownsyncFrame, inputsBuffer *RingBuffer, inputDelayFrames int32, inputScaleFrames uint32) (int, bool, int32, int32) {
// returns (patternId, jumpedOrNot, effectiveDx, effectiveDy)
delayedInputFrameId := ConvertToInputFrameId(currRenderFrame.Id, inputDelayFrames, inputScaleFrames)
delayedInputFrameIdForPrevRdf := ConvertToInputFrameId(currRenderFrame.Id-1, inputDelayFrames, inputScaleFrames)
if 0 >= delayedInputFrameId {
return PATTERN_ID_UNABLE_TO_OP, false, 0, 0
}
delayedInputList := inputsBuffer.GetByFrameId(delayedInputFrameId).(*InputFrameDownsync).InputList
var delayedInputListForPrevRdf []uint64 = nil
if 0 < delayedInputFrameIdForPrevRdf {
delayedInputListForPrevRdf = inputsBuffer.GetByFrameId(delayedInputFrameIdForPrevRdf).(*InputFrameDownsync).InputList
}
jumpedOrNot := false
joinIndex := currPlayerDownsync.JoinIndex
if 0 < currPlayerDownsync.FramesToRecover {
return PATTERN_ID_UNABLE_TO_OP, false, 0, 0
}
decodedInput := decodeInput(delayedInputList[joinIndex-1])
effDx, effDy := decodedInput.Dx, decodedInput.Dy
prevBtnALevel, prevBtnBLevel := int32(0), int32(0)
if nil != delayedInputListForPrevRdf {
prevDecodedInput := decodeInput(delayedInputListForPrevRdf[joinIndex-1])
prevBtnALevel = prevDecodedInput.BtnALevel
prevBtnBLevel = prevDecodedInput.BtnBLevel
}
if decodedInput.BtnBLevel > prevBtnBLevel {
characStateAlreadyInAir := false
if ATK_CHARACTER_STATE_INAIR_IDLE1 == currPlayerDownsync.CharacterState || ATK_CHARACTER_STATE_INAIR_ATK1 == currPlayerDownsync.CharacterState || ATK_CHARACTER_STATE_INAIR_ATKED1 == currPlayerDownsync.CharacterState {
characStateAlreadyInAir = true
}
characStateIsInterruptWaivable := false
if ATK_CHARACTER_STATE_IDLE1 == currPlayerDownsync.CharacterState || ATK_CHARACTER_STATE_WALKING == currPlayerDownsync.CharacterState || ATK_CHARACTER_STATE_INAIR_IDLE1 == currPlayerDownsync.CharacterState {
characStateIsInterruptWaivable = true
}
if !characStateAlreadyInAir && characStateIsInterruptWaivable {
jumpedOrNot = true
}
}
patternId := PATTERN_ID_NO_OP
if decodedInput.BtnALevel > prevBtnALevel {
patternId = 0
effDx, effDy = 0, 0 // Most patterns/skills should not allow simultaneous movement
}
return patternId, jumpedOrNot, effDx, effDy
}
// [WARNING] The params of this method is carefully tuned such that only "battle.RoomDownsyncFrame" is a necessary custom struct.
func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(inputsBuffer *RingBuffer, currRenderFrame *RoomDownsyncFrame, collisionSys *resolv.Space, collisionSysMap map[int32]*resolv.Object, gravityX, gravityY, jumpingInitVelY, inputDelayFrames int32, inputScaleFrames uint32, collisionSpaceOffsetX, collisionSpaceOffsetY, snapIntoPlatformOverlap, snapIntoPlatformThreshold, worldToVirtualGridRatio, virtualGridToWorldRatio float64, playerOpPatternToSkillId map[int]int) *RoomDownsyncFrame {
// [WARNING] On backend this function MUST BE called while "InputsBufferLock" is locked!
roomCapacity := len(currRenderFrame.PlayersArr)
nextRenderFramePlayers := make([]*PlayerDownsync, roomCapacity)
// Make a copy first
for i, currPlayerDownsync := range currRenderFrame.PlayersArr {
nextRenderFramePlayers[i] = &PlayerDownsync{
Id: currPlayerDownsync.Id,
VirtualGridX: currPlayerDownsync.VirtualGridX,
VirtualGridY: currPlayerDownsync.VirtualGridY,
DirX: currPlayerDownsync.DirX,
DirY: currPlayerDownsync.DirY,
VelX: currPlayerDownsync.VelX,
VelY: currPlayerDownsync.VelY,
CharacterState: currPlayerDownsync.CharacterState,
InAir: true,
Speed: currPlayerDownsync.Speed,
BattleState: currPlayerDownsync.BattleState,
Score: currPlayerDownsync.Score,
Removed: currPlayerDownsync.Removed,
JoinIndex: currPlayerDownsync.JoinIndex,
FramesToRecover: currPlayerDownsync.FramesToRecover - 1,
Hp: currPlayerDownsync.Hp,
MaxHp: currPlayerDownsync.MaxHp,
}
if nextRenderFramePlayers[i].FramesToRecover < 0 {
nextRenderFramePlayers[i].FramesToRecover = 0
}
}
nextRenderFrameMeleeBullets := make([]*MeleeBullet, 0, len(currRenderFrame.MeleeBullets)) // Is there any better way to reduce malloc/free impact, e.g. smart prediction for fixed memory allocation?
effPushbacks := make([]Vec2D, roomCapacity)
hardPushbackNorms := make([]*[]Vec2D, roomCapacity)
// 1. Process player inputs
for i, currPlayerDownsync := range currRenderFrame.PlayersArr {
thatPlayerInNextFrame := nextRenderFramePlayers[i]
patternId, jumpedOrNot, effDx, effDy := deriveOpPattern(currPlayerDownsync, thatPlayerInNextFrame, currRenderFrame, inputsBuffer, inputDelayFrames, inputScaleFrames)
if PATTERN_ID_UNABLE_TO_OP == patternId {
continue
}
if jumpedOrNot {
thatPlayerInNextFrame.VelY = jumpingInitVelY
thatPlayerInNextFrame.VirtualGridY += jumpingInitVelY // Immediately gets out of any snapping
}
joinIndex := currPlayerDownsync.JoinIndex
if PATTERN_ID_NO_OP != patternId {
if skillId, existent := playerOpPatternToSkillId[(int(joinIndex)<<uint(8))+patternId]; existent {
skillConfig := skillIdToBullet[skillId].(*MeleeBullet) // Hardcoded type "MeleeBullet" for now
var newMeleeBullet MeleeBullet = *skillConfig
newMeleeBullet.OffenderJoinIndex = joinIndex
newMeleeBullet.OffenderPlayerId = currPlayerDownsync.Id
newMeleeBullet.OriginatedRenderFrameId = currRenderFrame.Id
nextRenderFrameMeleeBullets = append(nextRenderFrameMeleeBullets, &newMeleeBullet)
thatPlayerInNextFrame.FramesToRecover = newMeleeBullet.RecoveryFrames
thatPlayerInNextFrame.CharacterState = ATK_CHARACTER_STATE_ATK1
if false == currPlayerDownsync.InAir {
thatPlayerInNextFrame.VelX = 0
}
}
continue
}
if 0 != effDx || 0 != effDy {
thatPlayerInNextFrame.DirX, thatPlayerInNextFrame.DirY = effDx, effDy
thatPlayerInNextFrame.VelX = effDx * currPlayerDownsync.Speed
thatPlayerInNextFrame.CharacterState = ATK_CHARACTER_STATE_WALKING
} else {
thatPlayerInNextFrame.CharacterState = ATK_CHARACTER_STATE_IDLE1
thatPlayerInNextFrame.VelX = 0
}
}
// 2. Process player movement
for i, currPlayerDownsync := range currRenderFrame.PlayersArr {
joinIndex := currPlayerDownsync.JoinIndex
effPushbacks[joinIndex-1].X, effPushbacks[joinIndex-1].Y = float64(0), float64(0)
collisionPlayerIndex := COLLISION_PLAYER_INDEX_PREFIX + joinIndex
playerCollider := collisionSysMap[collisionPlayerIndex]
thatPlayerInNextFrame := nextRenderFramePlayers[i]
// Reset playerCollider position from the "virtual grid position"
newVx, newVy := currPlayerDownsync.VirtualGridX+currPlayerDownsync.VelX, currPlayerDownsync.VirtualGridY+currPlayerDownsync.VelY
playerCollider.X, playerCollider.Y = VirtualGridToPolygonColliderBLPos(newVx, newVy, playerCollider.W*0.5, playerCollider.H*0.5, 0, 0, 0, 0, collisionSpaceOffsetX, collisionSpaceOffsetY, virtualGridToWorldRatio)
// Update in the collision system
playerCollider.Update()
if currPlayerDownsync.InAir {
thatPlayerInNextFrame.VelX += gravityX
thatPlayerInNextFrame.VelY += gravityY
}
}
// 3. Add bullet colliders into collision system
bulletColliders := make([]*resolv.Object, 0, len(currRenderFrame.MeleeBullets)) // Will all be removed at the end of this function due to the need for being rollback-compatible
for _, meleeBullet := range currRenderFrame.MeleeBullets {
if (meleeBullet.OriginatedRenderFrameId+meleeBullet.StartupFrames <= currRenderFrame.Id) && (meleeBullet.OriginatedRenderFrameId+meleeBullet.StartupFrames+meleeBullet.ActiveFrames > currRenderFrame.Id) {
offender := currRenderFrame.PlayersArr[meleeBullet.OffenderJoinIndex-1]
xfac := float64(1.0) // By now, straight Punch offset doesn't respect "y-axis"
if 0 > offender.DirX {
xfac = float64(-1.0)
}
offenderWx, offenderWy := VirtualGridToWorldPos(offender.VirtualGridX, offender.VirtualGridY, virtualGridToWorldRatio)
bulletWx, bulletWy := offenderWx+xfac*meleeBullet.HitboxOffset, offenderWy
newBulletCollider := GenerateRectCollider(bulletWx, bulletWy, meleeBullet.HitboxSizeX, meleeBullet.HitboxSizeY, snapIntoPlatformOverlap, snapIntoPlatformOverlap, snapIntoPlatformOverlap, snapIntoPlatformOverlap, collisionSpaceOffsetX, collisionSpaceOffsetY, meleeBullet, "MeleeBullet")
collisionSys.Add(newBulletCollider)
bulletColliders = append(bulletColliders, newBulletCollider)
} else {
nextRenderFrameMeleeBullets = append(nextRenderFrameMeleeBullets, meleeBullet)
}
}
// 4. Calc pushbacks for each player (after its movement) w/o bullets
for i, currPlayerDownsync := range currRenderFrame.PlayersArr {
joinIndex := currPlayerDownsync.JoinIndex
collisionPlayerIndex := COLLISION_PLAYER_INDEX_PREFIX + joinIndex
playerCollider := collisionSysMap[collisionPlayerIndex]
playerShape := playerCollider.Shape.(*resolv.ConvexPolygon)
hardPushbackNorms[joinIndex-1] = calcHardPushbacksNorms(joinIndex, playerCollider, playerShape, snapIntoPlatformOverlap, &(effPushbacks[joinIndex-1]))
thatPlayerInNextFrame := nextRenderFramePlayers[i]
landedOnGravityPushback := false
if collision := playerCollider.Check(0, 0); nil != collision {
for _, obj := range collision.Objects {
isBarrier, isAnotherPlayer, isBullet := false, false, false
switch obj.Data.(type) {
case *PlayerDownsync:
isAnotherPlayer = true
case *MeleeBullet:
isBullet = true
default:
// By default it's a regular barrier, even if data is nil
isBarrier = true
}
if isBullet {
// ignore bullets for this step
continue
}
bShape := obj.Shape.(*resolv.ConvexPolygon)
overlapped, pushbackX, pushbackY, overlapResult := CalcPushbacks(0, 0, playerShape, bShape)
if !overlapped {
continue
}
normAlignmentWithGravity := (overlapResult.OverlapX*float64(0) + overlapResult.OverlapY*float64(-1.0))
if isAnotherPlayer {
// [WARNING] The "zero overlap collision" might be randomly detected/missed on either frontend or backend, to have deterministic result we added paddings to all sides of a playerCollider. As each velocity component of (velX, velY) being a multiple of 0.5 at any renderFrame, each position component of (x, y) can only be a multiple of 0.5 too, thus whenever a 1-dimensional collision happens between players from [player#1: i*0.5, player#2: j*0.5, not collided yet] to [player#1: (i+k)*0.5, player#2: j*0.5, collided], the overlap becomes (i+k-j)*0.5+2*s, and after snapping subtraction the effPushback magnitude for each player is (i+k-j)*0.5, resulting in 0.5-multiples-position for the next renderFrame.
pushbackX, pushbackY = (overlapResult.Overlap-snapIntoPlatformOverlap*2)*overlapResult.OverlapX, (overlapResult.Overlap-snapIntoPlatformOverlap*2)*overlapResult.OverlapY
}
for _, hardPushbackNorm := range *hardPushbackNorms[joinIndex-1] {
projectedMagnitude := pushbackX*hardPushbackNorm.X + pushbackY*hardPushbackNorm.Y
if isBarrier || (isAnotherPlayer && 0 > projectedMagnitude) {
pushbackX -= projectedMagnitude * hardPushbackNorm.X
pushbackY -= projectedMagnitude * hardPushbackNorm.Y
}
}
effPushbacks[joinIndex-1].X += pushbackX
effPushbacks[joinIndex-1].Y += pushbackY
if snapIntoPlatformThreshold < normAlignmentWithGravity {
landedOnGravityPushback = true
//playerColliderCenterX, playerColliderCenterY := playerCollider.Center()
//fmt.Printf("joinIndex=%d landedOnGravityPushback\n{renderFrame.id: %d, isBarrier: %v, isAnotherPlayer: %v}\nhardPushbackNormsOfThisPlayer=%v, playerColliderPos=(%.2f,%.2f), immediatePushback={%.3f, %.3f}, effPushback={%.3f, %.3f}, overlapMag=%.4f\n", joinIndex, currRenderFrame.Id, isBarrier, isAnotherPlayer, *hardPushbackNorms[joinIndex-1], playerColliderCenterX, playerColliderCenterY, pushbackX, pushbackY, effPushbacks[joinIndex-1].X, effPushbacks[joinIndex-1].Y, overlapResult.Overlap)
}
}
}
if landedOnGravityPushback {
thatPlayerInNextFrame.InAir = false
if currPlayerDownsync.InAir && 0 >= currPlayerDownsync.VelY {
// fallStopping
thatPlayerInNextFrame.VelX = 0
thatPlayerInNextFrame.VelY = 0
thatPlayerInNextFrame.CharacterState = ATK_CHARACTER_STATE_IDLE1
thatPlayerInNextFrame.FramesToRecover = 0
}
}
if currPlayerDownsync.InAir {
oldNextCharacterState := thatPlayerInNextFrame.CharacterState
switch oldNextCharacterState {
case ATK_CHARACTER_STATE_IDLE1, ATK_CHARACTER_STATE_WALKING:
thatPlayerInNextFrame.CharacterState = ATK_CHARACTER_STATE_INAIR_IDLE1
case ATK_CHARACTER_STATE_ATK1:
thatPlayerInNextFrame.CharacterState = ATK_CHARACTER_STATE_INAIR_ATK1
case ATK_CHARACTER_STATE_ATKED1:
thatPlayerInNextFrame.CharacterState = ATK_CHARACTER_STATE_INAIR_ATKED1
}
}
}
// 5. Check bullet-anything collisions
for _, bulletCollider := range bulletColliders {
meleeBullet := bulletCollider.Data.(*MeleeBullet)
bulletShape := bulletCollider.Shape.(*resolv.ConvexPolygon)
collision := bulletCollider.Check(0, 0)
bulletCollider.Space.Remove(bulletCollider) // Make sure that the bulletCollider is always removed for each renderFrame
if nil == collision {
nextRenderFrameMeleeBullets = append(nextRenderFrameMeleeBullets, meleeBullet)
continue
}
offender := currRenderFrame.PlayersArr[meleeBullet.OffenderJoinIndex-1]
for _, obj := range collision.Objects {
defenderShape := obj.Shape.(*resolv.ConvexPolygon)
switch t := obj.Data.(type) {
case *PlayerDownsync:
if meleeBullet.OffenderPlayerId == t.Id {
continue
}
overlapped, _, _, _ := CalcPushbacks(0, 0, bulletShape, defenderShape)
if !overlapped {
continue
}
joinIndex := t.JoinIndex
xfac := float64(1.0) // By now, straight Punch offset doesn't respect "y-axis"
if 0 > offender.DirX {
xfac = float64(-1.0)
}
pushbackX, pushbackY := -xfac*meleeBullet.Pushback, float64(0)
for _, hardPushbackNorm := range *hardPushbackNorms[joinIndex-1] {
projectedMagnitude := pushbackX*hardPushbackNorm.X + pushbackY*hardPushbackNorm.Y
if 0 > projectedMagnitude {
//fmt.Printf("defenderPlayerId=%d, joinIndex=%d reducing bullet pushback={%.3f, %.3f} by {%.3f, %.3f} where hardPushbackNorm={%.3f, %.3f}, projectedMagnitude=%.3f at renderFrame.id=%d", t.Id, joinIndex, pushbackX, pushbackY, projectedMagnitude*hardPushbackNorm.X, projectedMagnitude*hardPushbackNorm.Y, hardPushbackNorm.X, hardPushbackNorm.Y, projectedMagnitude, currRenderFrame.Id)
pushbackX -= projectedMagnitude * hardPushbackNorm.X
pushbackY -= projectedMagnitude * hardPushbackNorm.Y
}
}
effPushbacks[joinIndex-1].X += pushbackX
effPushbacks[joinIndex-1].Y += pushbackY
atkedPlayerInCurFrame, atkedPlayerInNextFrame := currRenderFrame.PlayersArr[t.JoinIndex-1], nextRenderFramePlayers[t.JoinIndex-1]
atkedPlayerInNextFrame.CharacterState = ATK_CHARACTER_STATE_ATKED1
if atkedPlayerInCurFrame.InAir {
atkedPlayerInNextFrame.CharacterState = ATK_CHARACTER_STATE_INAIR_ATKED1
}
oldFramesToRecover := nextRenderFramePlayers[t.JoinIndex-1].FramesToRecover
if meleeBullet.HitStunFrames > oldFramesToRecover {
atkedPlayerInNextFrame.FramesToRecover = meleeBullet.HitStunFrames
}
default:
}
}
}
// 6. Get players out of stuck barriers if there's any
for i, currPlayerDownsync := range currRenderFrame.PlayersArr {
joinIndex := currPlayerDownsync.JoinIndex
collisionPlayerIndex := COLLISION_PLAYER_INDEX_PREFIX + joinIndex
playerCollider := collisionSysMap[collisionPlayerIndex]
// Update "virtual grid position"
thatPlayerInNextFrame := nextRenderFramePlayers[i]
thatPlayerInNextFrame.VirtualGridX, thatPlayerInNextFrame.VirtualGridY = PolygonColliderBLToVirtualGridPos(playerCollider.X-effPushbacks[joinIndex-1].X, playerCollider.Y-effPushbacks[joinIndex-1].Y, playerCollider.W*0.5, playerCollider.H*0.5, 0, 0, 0, 0, collisionSpaceOffsetX, collisionSpaceOffsetY, worldToVirtualGridRatio)
}
return &RoomDownsyncFrame{
Id: currRenderFrame.Id + 1,
PlayersArr: nextRenderFramePlayers,
MeleeBullets: nextRenderFrameMeleeBullets,
}
}
func GenerateRectCollider(wx, wy, w, h, topPadding, bottomPadding, leftPadding, rightPadding, spaceOffsetX, spaceOffsetY float64, data interface{}, tag string) *resolv.Object {
blX, blY := WorldToPolygonColliderBLPos(wx, wy, w*0.5, h*0.5, topPadding, bottomPadding, leftPadding, rightPadding, spaceOffsetX, spaceOffsetY)
return generateRectColliderInCollisionSpace(blX, blY, leftPadding+w+rightPadding, bottomPadding+h+topPadding, data, tag)
}
func generateRectColliderInCollisionSpace(blX, blY, w, h float64, data interface{}, tag string) *resolv.Object {
collider := resolv.NewObject(blX, blY, w, h, tag) // Unlike its frontend counter part, the position of a "resolv.Object" must be specified by "bottom-left point" because "w" and "h" must be positive, see "resolv.Object.BoundsToSpace" for details
shape := resolv.NewRectangle(0, 0, w, h)
collider.SetShape(shape)
collider.Data = data
return collider
}
func GenerateConvexPolygonCollider(unalignedSrc *Polygon2D, spaceOffsetX, spaceOffsetY float64, data interface{}, tag string) *resolv.Object {
aligned := AlignPolygon2DToBoundingBox(unalignedSrc)
var w, h float64 = 0, 0
shape := resolv.NewConvexPolygon()
for i, pi := range aligned.Points {
for j, pj := range aligned.Points {
if i == j {
continue
}
if math.Abs(pj.X-pi.X) > w {
w = math.Abs(pj.X - pi.X)
}
if math.Abs(pj.Y-pi.Y) > h {
h = math.Abs(pj.Y - pi.Y)
}
}
}
for i := 0; i < len(aligned.Points); i++ {
p := aligned.Points[i]
shape.AddPoints(p.X, p.Y)
}
collider := resolv.NewObject(aligned.Anchor.X+spaceOffsetX, aligned.Anchor.Y+spaceOffsetY, w, h, tag)
collider.SetShape(shape)
collider.Data = data
return collider
}
func AlignPolygon2DToBoundingBox(input *Polygon2D) *Polygon2D {
// Transform again to put "anchor" at the "bottom-left point (w.r.t. world space)" of the bounding box for "resolv"
boundingBoxBL := &Vec2D{
X: MAX_FLOAT64,
Y: MAX_FLOAT64,
}
for _, p := range input.Points {
if p.X < boundingBoxBL.X {
boundingBoxBL.X = p.X
}
if p.Y < boundingBoxBL.Y {
boundingBoxBL.Y = p.Y
}
}
// Now "input.Anchor" should move to "input.Anchor+boundingBoxBL", thus "boundingBoxBL" is also the value of the negative diff for all "input.Points"
output := &Polygon2D{
Anchor: &Vec2D{
X: input.Anchor.X + boundingBoxBL.X,
Y: input.Anchor.Y + boundingBoxBL.Y,
},
Points: make([]*Vec2D, len(input.Points)),
}
for i, p := range input.Points {
output.Points[i] = &Vec2D{
X: p.X - boundingBoxBL.X,
Y: p.Y - boundingBoxBL.Y,
}
}
return output
}

View File

@ -1,4 +1,10 @@
package models
package battle
const (
RING_BUFF_CONSECUTIVE_SET = int32(0)
RING_BUFF_NON_CONSECUTIVE_SET = int32(1)
RING_BUFF_FAILED_TO_SET = int32(2)
)
type RingBuffer struct {
Ed int32 // write index, open index
@ -48,15 +54,15 @@ func (rb *RingBuffer) Pop() interface{} {
return pItem
}
func (rb *RingBuffer) GetByOffset(offsetFromSt int32) interface{} {
if 0 == rb.Cnt {
return nil
func (rb *RingBuffer) GetArrIdxByOffset(offsetFromSt int32) int32 {
if 0 == rb.Cnt || 0 > offsetFromSt {
return -1
}
arrIdx := rb.St + offsetFromSt
if rb.St < rb.Ed {
// case#1: 0...st...ed...N-1
if rb.St <= arrIdx && arrIdx < rb.Ed {
return rb.Eles[arrIdx]
return arrIdx
}
} else {
// if rb.St >= rb.Ed
@ -65,11 +71,19 @@ func (rb *RingBuffer) GetByOffset(offsetFromSt int32) interface{} {
arrIdx -= rb.N
}
if arrIdx >= rb.St || arrIdx < rb.Ed {
return rb.Eles[arrIdx]
return arrIdx
}
}
return nil
return -1
}
func (rb *RingBuffer) GetByOffset(offsetFromSt int32) interface{} {
arrIdx := rb.GetArrIdxByOffset(offsetFromSt)
if -1 == arrIdx {
return nil
}
return rb.Eles[arrIdx]
}
func (rb *RingBuffer) GetByFrameId(frameId int32) interface{} {
@ -78,3 +92,33 @@ func (rb *RingBuffer) GetByFrameId(frameId int32) interface{} {
}
return rb.GetByOffset(frameId - rb.StFrameId)
}
// [WARNING] During a battle, frontend could receive non-consecutive frames (either renderFrame or inputFrame) due to resync, the buffer should handle these frames properly.
func (rb *RingBuffer) SetByFrameId(pItem interface{}, frameId int32) (int32, int32, int32) {
oldStFrameId, oldEdFrameId := rb.StFrameId, rb.EdFrameId
if frameId < oldStFrameId {
return RING_BUFF_FAILED_TO_SET, oldStFrameId, oldEdFrameId
}
// By now "rb.StFrameId <= frameId"
if oldEdFrameId > frameId {
arrIdx := rb.GetArrIdxByOffset(frameId - rb.StFrameId)
if -1 != arrIdx {
rb.Eles[arrIdx] = pItem
return RING_BUFF_CONSECUTIVE_SET, oldStFrameId, oldEdFrameId
}
}
// By now "rb.EdFrameId <= frameId"
ret := RING_BUFF_CONSECUTIVE_SET
if oldEdFrameId < frameId {
rb.St, rb.Ed = 0, 0
rb.StFrameId, rb.EdFrameId = frameId, frameId
rb.Cnt = 0
ret = RING_BUFF_NON_CONSECUTIVE_SET
}
// By now "rb.EdFrameId == frameId"
rb.Put(pItem)
return ret, oldStFrameId, oldEdFrameId
}

View File

@ -0,0 +1,108 @@
package battle
// TODO: Replace all "int32", "int64", "uint32" and "uint64" with just "int" for better performance in JavaScript! Reference https://github.com/gopherjs/gopherjs#performance-tips
type Vec2D struct {
X float64
Y float64
}
type Polygon2D struct {
Anchor *Vec2D
Points []*Vec2D
}
type PlayerDownsync struct {
Id int32
VirtualGridX int32
VirtualGridY int32
DirX int32
DirY int32
VelX int32
VelY int32
Speed int32
BattleState int32
JoinIndex int32
ColliderRadius float64
Removed bool
Score int32
LastMoveGmtMillis int32
FramesToRecover int32
Hp int32
MaxHp int32
CharacterState int32
InAir bool
}
type InputFrameDecoded struct {
Dx int32
Dy int32
BtnALevel int32
BtnBLevel int32
}
type InputFrameUpsync struct {
InputFrameId int32
Encoded uint64
}
type Barrier struct {
Boundary *Polygon2D
}
type Bullet struct {
// for offender
BattleLocalId int32
StartupFrames int32
ActiveFrames int32
RecoveryFrames int32
RecoveryFramesOnBlock int32
RecoveryFramesOnHit int32
HitboxOffset float64
OriginatedRenderFrameId int32
// for defender
HitStunFrames int32
BlockStunFrames int32
Pushback float64
ReleaseTriggerType int32
Damage int32
OffenderJoinIndex int32
OffenderPlayerId int32
SelfMoveforwardX float64
SelfMoveforwardY float64
HitboxSizeX float64
HitboxSizeY float64
}
type MeleeBullet struct {
Bullet
}
type FireballBullet struct {
VirtualGridX int32
VirtualGridY int32
DirX int32
DirY int32
VelX int32
VelY int32
Speed int32
Bullet
}
type RoomDownsyncFrame struct {
Id int32
PlayersArr []*PlayerDownsync
CountdownNanos int64
MeleeBullets []*MeleeBullet
FireballBullets []*FireballBullet
BackendUnconfirmedMask uint64
ShouldForceResync bool
PlayerOpPatternToSkillId map[int]int
}
type InputFrameDownsync struct {
InputFrameId int32
InputList []uint64
ConfirmedList uint64
}

12
jsexport/go.mod Normal file
View File

@ -0,0 +1,12 @@
module jsexport
go 1.18
require (
github.com/gopherjs/gopherjs v1.18.0-beta1
resolv v0.0.0
)
replace (
resolv => ../resolv_tailored
)

2
jsexport/go.sum Normal file
View File

@ -0,0 +1,2 @@
github.com/gopherjs/gopherjs v1.18.0-beta1 h1:IbykhVEq4SAjwyBRuNHl0aOO6w6IqgL3RUdMhoBo4mY=
github.com/gopherjs/gopherjs v1.18.0-beta1/go.mod h1:6UY8PXRnu51MqjYCCY4toG0S5GeH5uVJ3qDxIsa+kqo=

155
jsexport/main.go Normal file
View File

@ -0,0 +1,155 @@
package main
import (
"github.com/gopherjs/gopherjs/js"
. "jsexport/battle"
"resolv"
)
func NewInputFrameDownsync(inputFrameId int32, inputList []uint64, confirmedList uint64) *js.Object {
return js.MakeFullWrapper(&InputFrameDownsync{
InputFrameId: inputFrameId,
InputList: inputList,
ConfirmedList: confirmedList,
})
}
func NewRingBufferJs(n int32) *js.Object {
return js.MakeFullWrapper(NewRingBuffer(n))
}
func NewCollisionSpaceJs(spaceW, spaceH, minStepW, minStepH int) *js.Object {
return js.MakeWrapper(resolv.NewSpace(spaceW, spaceH, minStepW, minStepH))
}
func NewVec2DJs(x, y float64) *js.Object {
return js.MakeFullWrapper(&Vec2D{
X: x,
Y: y,
})
}
func NewPolygon2DJs(anchor *Vec2D, points []*Vec2D) *js.Object {
return js.MakeFullWrapper(&Polygon2D{
Anchor: anchor,
Points: points,
})
}
func NewBarrierJs(boundary *Polygon2D) *js.Object {
return js.MakeWrapper(&Barrier{
Boundary: boundary,
})
}
func NewPlayerDownsyncJs(id, virtualGridX, virtualGridY, dirX, dirY, velX, velY, framesToRecover, speed, battleState, characterState, joinIndex, hp, maxHp int32, inAir bool, colliderRadius float64) *js.Object {
return js.MakeWrapper(&PlayerDownsync{
Id: id,
VirtualGridX: virtualGridX,
VirtualGridY: virtualGridY,
DirX: dirX,
DirY: dirY,
VelX: velX,
VelY: velY,
FramesToRecover: framesToRecover,
Speed: speed,
BattleState: battleState,
JoinIndex: joinIndex,
ColliderRadius: colliderRadius,
Hp: hp,
MaxHp: maxHp,
CharacterState: characterState,
InAir: inAir,
})
}
func NewMeleeBulletJs(battleLocalId, startupFrames, activeFrames, recoveryFrames, recoveryFramesOnBlock, recoveryFramesOnHit, hitStunFrames, blockStunFrames, releaseTriggerType, damage, offenderJoinIndex, offenderPlayerId int32, pushback, hitboxOffset, selfMoveforwardX, selfMoveforwardY, hitboxSizeX, hitboxSizeY float64) *js.Object {
return js.MakeWrapper(&MeleeBullet{
Bullet: Bullet{
BattleLocalId: battleLocalId,
StartupFrames: startupFrames,
ActiveFrames: activeFrames,
RecoveryFrames: recoveryFrames,
RecoveryFramesOnBlock: recoveryFramesOnBlock,
RecoveryFramesOnHit: recoveryFramesOnHit,
HitboxOffset: hitboxOffset,
HitStunFrames: hitStunFrames,
BlockStunFrames: blockStunFrames,
Pushback: pushback,
ReleaseTriggerType: releaseTriggerType,
Damage: damage,
SelfMoveforwardX: selfMoveforwardX,
SelfMoveforwardY: selfMoveforwardY,
HitboxSizeX: hitboxSizeX,
HitboxSizeY: hitboxSizeY,
OffenderJoinIndex: offenderJoinIndex,
OffenderPlayerId: offenderPlayerId,
},
})
}
func NewRoomDownsyncFrameJs(id int32, playersArr []*PlayerDownsync, meleeBullets []*MeleeBullet) *js.Object {
// [WARNING] Avoid using "pb.RoomDownsyncFrame" here, in practive "MakeFullWrapper" doesn't expose the public fields for a "protobuf struct" as expected and requires helper functions like "GetCollisionSpaceObjsJs".
return js.MakeFullWrapper(&RoomDownsyncFrame{
Id: id,
PlayersArr: playersArr,
MeleeBullets: meleeBullets,
})
}
func GetCollisionSpaceObjsJs(space *resolv.Space) []*js.Object {
// [WARNING] We couldn't just use the existing method "space.Objects()" to access them in JavaScript, there'd a stackoverflow error
objs := space.Objects()
ret := make([]*js.Object, 0, len(objs))
for _, obj := range objs {
ret = append(ret, js.MakeFullWrapper(obj))
}
return ret
}
func GenerateRectColliderJs(wx, wy, w, h, topPadding, bottomPadding, leftPadding, rightPadding, spaceOffsetX, spaceOffsetY float64, data interface{}, tag string) *js.Object {
/*
[WARNING] It's important to note that we don't need "js.MakeFullWrapper" for a call sequence as follows.
```
var space = gopkgs.NewCollisionSpaceJs(2048, 2048, 8, 8);
var a = gopkgs.GenerateRectColliderJs(189, 497, 48, 48, snapIntoPlatformOverlap, snapIntoPlatformOverlap, snapIntoPlatformOverlap, snapIntoPlatformOverlap, spaceOffsetX, spaceOffsetY, "Player");
space.Add(a);
```
The "space" variable doesn't need access to the field of "a" in JavaScript level to run "space.Add(...)" method, which is good.
However, the full wrapper access here is used for updating "collider.X/collider.Y" at JavaScript runtime.
*/
return js.MakeFullWrapper(GenerateRectCollider(wx, wy, w, h, topPadding, bottomPadding, leftPadding, rightPadding, spaceOffsetX, spaceOffsetY, data, tag))
}
func GenerateConvexPolygonColliderJs(unalignedSrc *Polygon2D, spaceOffsetX, spaceOffsetY float64, data interface{}, tag string) *js.Object {
return js.MakeFullWrapper(GenerateConvexPolygonCollider(unalignedSrc, spaceOffsetX, spaceOffsetY, data, tag))
}
func ApplyInputFrameDownsyncDynamicsOnSingleRenderFrameJs(inputsBuffer *RingBuffer, currRenderFrame *RoomDownsyncFrame, collisionSys *resolv.Space, collisionSysMap map[int32]*resolv.Object, gravityX, gravityY, jumpingInitVelY, inputDelayFrames int32, inputScaleFrames uint32, collisionSpaceOffsetX, collisionSpaceOffsetY, snapIntoPlatformOverlap, snapIntoPlatformThreshold, worldToVirtualGridRatio, virtualGridToWorldRatio float64, playerOpPatternToSkillId map[int]int) *js.Object {
// We need access to all fields of RoomDownsyncFrame for displaying in frontend
return js.MakeFullWrapper(ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame(inputsBuffer, currRenderFrame, collisionSys, collisionSysMap, gravityX, gravityY, jumpingInitVelY, inputDelayFrames, inputScaleFrames, collisionSpaceOffsetX, collisionSpaceOffsetY, snapIntoPlatformOverlap, snapIntoPlatformThreshold, worldToVirtualGridRatio, virtualGridToWorldRatio, playerOpPatternToSkillId))
}
func main() {
js.Global.Set("gopkgs", map[string]interface{}{
"NewVec2DJs": NewVec2DJs,
"NewPolygon2DJs": NewPolygon2DJs,
"NewBarrierJs": NewBarrierJs,
"NewPlayerDownsyncJs": NewPlayerDownsyncJs,
"NewMeleeBulletJs": NewMeleeBulletJs,
"NewRoomDownsyncFrameJs": NewRoomDownsyncFrameJs,
"NewCollisionSpaceJs": NewCollisionSpaceJs,
"NewInputFrameDownsync": NewInputFrameDownsync,
"NewRingBufferJs": NewRingBufferJs,
"GenerateRectColliderJs": GenerateRectColliderJs,
"GenerateConvexPolygonColliderJs": GenerateConvexPolygonColliderJs,
"GetCollisionSpaceObjsJs": GetCollisionSpaceObjsJs,
"ApplyInputFrameDownsyncDynamicsOnSingleRenderFrameJs": ApplyInputFrameDownsyncDynamicsOnSingleRenderFrameJs,
"WorldToPolygonColliderBLPos": WorldToPolygonColliderBLPos, // No need to wrap primitive return types
"PolygonColliderBLToWorldPos": PolygonColliderBLToWorldPos,
})
}

View File

@ -6,7 +6,7 @@
golang_basedir_1=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )/dnmshared
protoc -I=$golang_basedir_1/../frontend/assets/resources/pbfiles/ --go_out=. geometry.proto
echo "GOLANG part 1 done"
echo "GOLANG part 1 done" # [WARNING] The output of "part 1" is DEPRECATED, the codes are not using it anymore.
# [WARNING] The following "room_downsync_frame.proto" is generated in another Go package than "geometry.proto", but the generated Go codes are also required to work with imports correctly!
golang_basedir_2=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )/battle_srv

14
resolv_tailored/.gitignore vendored Normal file
View File

@ -0,0 +1,14 @@
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
Game
# Test binary, build with `go test -c`
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
.vscode/launch.json

21
resolv_tailored/LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2018-2021 SolarLune
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,164 @@
// This file contains code from the gonum repository:
// https://github.com/gonum/gonum/blob/master/internal/asm/f64/scalunitaryto_amd64.s
// it is distributed under the 3-Clause BSD license:
//
// Copyright ©2013 The Gonum Authors. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of the Gonum project nor the names of its authors and
// contributors may be used to endorse or promote products derived from this
// software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Some of the loop unrolling code is copied from:
// http://golang.org/src/math/big/arith_amd64.s
// which is distributed under these terms:
//
// Copyright (c) 2012 The Go Authors. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// +build !noasm
#include "textflag.h"
#define X_PTR SI
#define Y_PTR DX
#define DST_PTR DI
#define IDX AX
#define LEN CX
#define TAIL BX
#define ALPHA X0
#define ALPHA_2 X1
// func axpyUnitaryTo(dst []float64, alpha float64, x, y []float64)
TEXT ·axpyUnitaryTo(SB), NOSPLIT, $0
MOVQ dst_base+0(FP), DST_PTR // DST_PTR := &dst
MOVQ x_base+32(FP), X_PTR // X_PTR := &x
MOVQ y_base+56(FP), Y_PTR // Y_PTR := &y
MOVQ x_len+40(FP), LEN // LEN = min( len(x), len(y), len(dst) )
CMPQ y_len+64(FP), LEN
CMOVQLE y_len+64(FP), LEN
CMPQ dst_len+8(FP), LEN
CMOVQLE dst_len+8(FP), LEN
CMPQ LEN, $0
JE end // if LEN == 0 { return }
XORQ IDX, IDX // IDX = 0
MOVSD alpha+24(FP), ALPHA
SHUFPD $0, ALPHA, ALPHA // ALPHA := { alpha, alpha }
MOVQ Y_PTR, TAIL // Check memory alignment
ANDQ $15, TAIL // TAIL = &y % 16
JZ no_trim // if TAIL == 0 { goto no_trim }
// Align on 16-byte boundary
MOVSD (X_PTR), X2 // X2 := x[0]
MULSD ALPHA, X2 // X2 *= a
ADDSD (Y_PTR), X2 // X2 += y[0]
MOVSD X2, (DST_PTR) // y[0] = X2
INCQ IDX // i++
DECQ LEN // LEN--
JZ end // if LEN == 0 { return }
no_trim:
MOVQ LEN, TAIL
ANDQ $7, TAIL // TAIL := n % 8
SHRQ $3, LEN // LEN = floor( n / 8 )
JZ tail_start // if LEN == 0 { goto tail_start }
MOVUPS ALPHA, ALPHA_2 // ALPHA_2 := ALPHA for pipelining
loop: // do {
// y[i] += alpha * x[i] unrolled 8x.
MOVUPS (X_PTR)(IDX*8), X2 // X_i = x[i]
MOVUPS 16(X_PTR)(IDX*8), X3
MOVUPS 32(X_PTR)(IDX*8), X4
MOVUPS 48(X_PTR)(IDX*8), X5
MULPD ALPHA, X2 // X_i *= alpha
MULPD ALPHA_2, X3
MULPD ALPHA, X4
MULPD ALPHA_2, X5
ADDPD (Y_PTR)(IDX*8), X2 // X_i += y[i]
ADDPD 16(Y_PTR)(IDX*8), X3
ADDPD 32(Y_PTR)(IDX*8), X4
ADDPD 48(Y_PTR)(IDX*8), X5
MOVUPS X2, (DST_PTR)(IDX*8) // y[i] = X_i
MOVUPS X3, 16(DST_PTR)(IDX*8)
MOVUPS X4, 32(DST_PTR)(IDX*8)
MOVUPS X5, 48(DST_PTR)(IDX*8)
ADDQ $8, IDX // i += 8
DECQ LEN
JNZ loop // } while --LEN > 0
CMPQ TAIL, $0 // if TAIL == 0 { return }
JE end
tail_start: // Reset loop registers
MOVQ TAIL, LEN // Loop counter: LEN = TAIL
SHRQ $1, LEN // LEN = floor( TAIL / 2 )
JZ tail_one // if LEN == 0 { goto tail }
tail_two: // do {
MOVUPS (X_PTR)(IDX*8), X2 // X2 = x[i]
MULPD ALPHA, X2 // X2 *= alpha
ADDPD (Y_PTR)(IDX*8), X2 // X2 += y[i]
MOVUPS X2, (DST_PTR)(IDX*8) // y[i] = X2
ADDQ $2, IDX // i += 2
DECQ LEN
JNZ tail_two // } while --LEN > 0
ANDQ $1, TAIL
JZ end // if TAIL == 0 { goto end }
tail_one:
MOVSD (X_PTR)(IDX*8), X2 // X2 = x[i]
MULSD ALPHA, X2 // X2 *= a
ADDSD (Y_PTR)(IDX*8), X2 // X2 += y[i]
MOVSD X2, (DST_PTR)(IDX*8) // y[i] = X2
end:
RET

View File

@ -0,0 +1,137 @@
// This file contains code from the gonum repository:
// https://github.com/gonum/gonum/blob/master/internal/asm/f64/axpyunitaryto_amd64.s
// it is distributed under the 3-Clause BSD license:
//
// Copyright ©2013 The Gonum Authors. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of the Gonum project nor the names of its authors and
// contributors may be used to endorse or promote products derived from this
// software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Some of the loop unrolling code is copied from:
// http://golang.org/src/math/big/arith_amd64.s
// which is distributed under these terms:
//
// Copyright (c) 2012 The Go Authors. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// +build !noasm
#include "textflag.h"
#define MOVDDUP_ALPHA LONG $0x44120FF2; WORD $0x2024 // @ MOVDDUP 32(SP), X0 /*XMM0, 32[RSP]*/
#define X_PTR SI
#define DST_PTR DI
#define IDX AX
#define LEN CX
#define TAIL BX
#define ALPHA X0
#define ALPHA_2 X1
// func scalUnitaryTo(dst []float64, alpha float64, x []float64)
// This function assumes len(dst) >= len(x).
TEXT ·scalUnitaryTo(SB), NOSPLIT, $0
MOVQ x_base+32(FP), X_PTR // X_PTR = &x
MOVQ dst_base+0(FP), DST_PTR // DST_PTR = &dst
MOVDDUP_ALPHA // ALPHA = { alpha, alpha }
MOVQ x_len+40(FP), LEN // LEN = len(x)
CMPQ LEN, $0
JE end // if LEN == 0 { return }
XORQ IDX, IDX // IDX = 0
MOVQ LEN, TAIL
ANDQ $7, TAIL // TAIL = LEN % 8
SHRQ $3, LEN // LEN = floor( LEN / 8 )
JZ tail_start // if LEN == 0 { goto tail_start }
MOVUPS ALPHA, ALPHA_2 // ALPHA_2 = ALPHA for pipelining
loop: // do { // dst[i] = alpha * x[i] unrolled 8x.
MOVUPS (X_PTR)(IDX*8), X2 // X_i = x[i]
MOVUPS 16(X_PTR)(IDX*8), X3
MOVUPS 32(X_PTR)(IDX*8), X4
MOVUPS 48(X_PTR)(IDX*8), X5
MULPD ALPHA, X2 // X_i *= ALPHA
MULPD ALPHA_2, X3
MULPD ALPHA, X4
MULPD ALPHA_2, X5
MOVUPS X2, (DST_PTR)(IDX*8) // dst[i] = X_i
MOVUPS X3, 16(DST_PTR)(IDX*8)
MOVUPS X4, 32(DST_PTR)(IDX*8)
MOVUPS X5, 48(DST_PTR)(IDX*8)
ADDQ $8, IDX // i += 8
DECQ LEN
JNZ loop // while --LEN > 0
CMPQ TAIL, $0
JE end // if TAIL == 0 { return }
tail_start: // Reset loop counters
MOVQ TAIL, LEN // Loop counter: LEN = TAIL
SHRQ $1, LEN // LEN = floor( TAIL / 2 )
JZ tail_one // if LEN == 0 { goto tail_one }
tail_two: // do {
MOVUPS (X_PTR)(IDX*8), X2 // X_i = x[i]
MULPD ALPHA, X2 // X_i *= ALPHA
MOVUPS X2, (DST_PTR)(IDX*8) // dst[i] = X_i
ADDQ $2, IDX // i += 2
DECQ LEN
JNZ tail_two // while --LEN > 0
ANDQ $1, TAIL
JZ end // if TAIL == 0 { return }
tail_one:
MOVSD (X_PTR)(IDX*8), X2 // X_i = x[i]
MULSD ALPHA, X2 // X_i *= ALPHA
MOVSD X2, (DST_PTR)(IDX*8) // dst[i] = X_i
end:
RET

View File

@ -0,0 +1,9 @@
//go:build !noasm
// +build !noasm
package resolv
// functions from the gonum package that optimizes arithmetic
// operations on lists of float64 values
func axpyUnitaryTo(dst []float64, alpha float64, x, y []float64)
func scalUnitaryTo(dst []float64, alpha float64, x []float64)

63
resolv_tailored/cell.go Normal file
View File

@ -0,0 +1,63 @@
package resolv
// Cell is used to contain and organize Object information.
type Cell struct {
X, Y int // The X and Y position of the cell in the Space - note that this is in Grid position, not World position.
Objects []*Object // The Objects that a Cell contains.
}
// newCell creates a new cell at the specified X and Y position. Should not be used directly.
func newCell(x, y int) *Cell {
return &Cell{
X: x,
Y: y,
Objects: []*Object{},
}
}
// register registers an object with a Cell. Should not be used directly.
func (cell *Cell) register(obj *Object) {
if !cell.Contains(obj) {
cell.Objects = append(cell.Objects, obj)
}
}
// unregister unregisters an object from a Cell. Should not be used directly.
func (cell *Cell) unregister(obj *Object) {
for i, o := range cell.Objects {
if o == obj {
cell.Objects[i] = cell.Objects[len(cell.Objects)-1]
cell.Objects = cell.Objects[:len(cell.Objects)-1]
break
}
}
}
// Contains returns whether a Cell contains the specified Object at its position.
func (cell *Cell) Contains(obj *Object) bool {
for _, o := range cell.Objects {
if o == obj {
return true
}
}
return false
}
// ContainsTags returns whether a Cell contains an Object that has the specified tag at its position.
func (cell *Cell) ContainsTags(tags ...string) bool {
for _, o := range cell.Objects {
if o.HasTags(tags...) {
return true
}
}
return false
}
// Occupied returns whether a Cell contains any Objects at all.
func (cell *Cell) Occupied() bool {
return len(cell.Objects) > 0
}

View File

@ -0,0 +1,156 @@
package resolv
// Collision contains the results of an Object.Check() call, and represents a collision between an Object and cells that contain other Objects.
// The Objects array indicate the Objects collided with.
type Collision struct {
checkingObject *Object // The checking object
dx, dy float64 // The delta the checking object was moving on that caused this collision
Objects []*Object // Slice of objects that were collided with; sorted according to distance to calling Object.
Cells []*Cell // Slice of cells that were collided with; sorted according to distance to calling Object.
}
func NewCollision() *Collision {
return &Collision{
Objects: []*Object{},
}
}
// HasTags returns whether any objects within the Collision have all of the specified tags. This slice does not contain the Object that called Check().
func (cc *Collision) HasTags(tags ...string) bool {
for _, o := range cc.Objects {
if o == cc.checkingObject {
continue
}
if o.HasTags(tags...) {
return true
}
}
return false
}
// ObjectsByTags returns a slice of Objects from the cells reported by a Collision object by searching for Objects with a specific set of tags.
// This slice does not contain the Object that called Check().
func (cc *Collision) ObjectsByTags(tags ...string) []*Object {
objects := []*Object{}
for _, o := range cc.Objects {
if o == cc.checkingObject {
continue
}
if o.HasTags(tags...) {
objects = append(objects, o)
}
}
return objects
}
// ContactWithObject returns the delta to move to come into contact with the specified Object.
func (cc *Collision) ContactWithObject(object *Object) Vector {
delta := Vector{0, 0}
if cc.dx < 0 {
delta[0] = object.X + object.W - cc.checkingObject.X
} else if cc.dx > 0 {
delta[0] = object.X - cc.checkingObject.W - cc.checkingObject.X
}
if cc.dy < 0 {
delta[1] = object.Y + object.H - cc.checkingObject.Y
} else if cc.dy > 0 {
delta[1] = object.Y - cc.checkingObject.H - cc.checkingObject.Y
}
return delta
}
// ContactWithCell returns the delta to move to come into contact with the specified Cell.
func (cc *Collision) ContactWithCell(cell *Cell) Vector {
delta := Vector{0, 0}
cx := float64(cell.X * cc.checkingObject.Space.CellWidth)
cy := float64(cell.Y * cc.checkingObject.Space.CellHeight)
if cc.dx < 0 {
delta[0] = cx + float64(cc.checkingObject.Space.CellWidth) - cc.checkingObject.X
} else if cc.dx > 0 {
delta[0] = cx - cc.checkingObject.W - cc.checkingObject.X
}
if cc.dy < 0 {
delta[1] = cy + float64(cc.checkingObject.Space.CellHeight) - cc.checkingObject.Y
} else if cc.dy > 0 {
delta[1] = cy - cc.checkingObject.H - cc.checkingObject.Y
}
return delta
}
// SlideAgainstCell returns how much distance the calling Object can slide to avoid a collision with the targetObject. This only works on vertical and horizontal axes (x and y directly),
// primarily for platformers / top-down games. avoidTags is a sequence of tags (as strings) to indicate when sliding is valid (i.e. if a Cell contains an Object that has the tag given in
// the avoidTags slice, then sliding CANNOT happen). If sliding is not able to be done for whatever reason, SlideAgainstCell returns nil.
func (cc *Collision) SlideAgainstCell(cell *Cell, avoidTags ...string) Vector {
sp := cc.checkingObject.Space
collidingCell := cc.Cells[0]
ccX, ccY := sp.SpaceToWorld(collidingCell.X, collidingCell.Y)
hX := float64(sp.CellWidth) / 2.0
hY := float64(sp.CellHeight) / 2.0
ccX += hX
ccY += hY
oX, oY := cc.checkingObject.Center()
diffX := oX - ccX
diffY := oY - ccY
left := sp.Cell(collidingCell.X-1, collidingCell.Y)
right := sp.Cell(collidingCell.X+1, collidingCell.Y)
up := sp.Cell(collidingCell.X, collidingCell.Y-1)
down := sp.Cell(collidingCell.X, collidingCell.Y+1)
slide := Vector{0, 0}
// Moving vertically
if cc.dy != 0 {
if diffX > 0 && (right == nil || !right.ContainsTags(avoidTags...)) {
// Slide right
slide[0] = ccX + hX - cc.checkingObject.X
} else if diffX < 0 && (left == nil || !left.ContainsTags(avoidTags...)) {
// Slide left
slide[0] = ccX - hX - (cc.checkingObject.X + cc.checkingObject.W)
} else {
return nil
}
}
if cc.dx != 0 {
if diffY > 0 && (down == nil || !down.ContainsTags(avoidTags...)) {
// Slide down
slide[1] = ccY + hY - cc.checkingObject.Y
} else if diffY < 0 && (up == nil || !up.ContainsTags(avoidTags...)) {
// Slide up
slide[1] = ccY - hY - (cc.checkingObject.Y + cc.checkingObject.H)
} else {
return nil
}
}
return slide
}

3
resolv_tailored/go.mod Normal file
View File

@ -0,0 +1,3 @@
module resolv
go 1.18

0
resolv_tailored/go.sum Normal file
View File

24
resolv_tailored/gonum.go Normal file
View File

@ -0,0 +1,24 @@
//go:build !amd64 || noasm
// +build !amd64 noasm
package resolv
// This function is from the gonum repository:
// https://github.com/gonum/gonum/blob/c3867503e73e5c3fee7ab93e3c2c562eb2be8178/internal/asm/f64/axpy.go#L23
func axpyUnitaryTo(dst []float64, alpha float64, x, y []float64) {
dim := len(y)
for i, v := range x {
if i == dim {
return
}
dst[i] = alpha*v + y[i]
}
}
// This function is from the gonum repository:
// https://github.com/gonum/gonum/blob/c3867503e73e5c3fee7ab93e3c2c562eb2be8178/internal/asm/f64/scal.go#L23
func scalUnitaryTo(dst []float64, alpha float64, x []float64) {
for i := range x {
dst[i] *= alpha
}
}

328
resolv_tailored/object.go Normal file
View File

@ -0,0 +1,328 @@
package resolv
import (
"math"
//"sort"
)
// Object represents an object that can be spread across one or more Cells in a Space. An Object is essentially an AABB (Axis-Aligned Bounding Box) Rectangle.
type Object struct {
Shape Shape // A shape for more specific collision-checking.
Space *Space // Reference to the Space the Object exists within
X, Y, W, H float64 // Position and size of the Object in the Space
TouchingCells []*Cell // An array of Cells the Object is touching
Data interface{} // A pointer to a user-definable object
ignoreList map[*Object]bool // Set of Objects to ignore when checking for collisions
tags []string // A list of tags the Object has
}
// NewObject returns a new Object of the specified position and size.
func NewObject(x, y, w, h float64, tags ...string) *Object {
o := &Object{
X: x,
Y: y,
W: w,
H: h,
tags: []string{},
ignoreList: map[*Object]bool{},
}
if len(tags) > 0 {
o.AddTags(tags...)
}
return o
}
// Clone clones the Object with its properties into another Object. It also clones the Object's Shape (if it has one).
func (obj *Object) Clone() *Object {
newObj := NewObject(obj.X, obj.Y, obj.W, obj.H, obj.Tags()...)
newObj.Data = obj.Data
if obj.Shape != nil {
newObj.SetShape(obj.Shape.Clone())
}
for k := range obj.ignoreList {
newObj.AddToIgnoreList(k)
}
return newObj
}
// Update updates the object's association to the Cells in the Space. This should be called whenever an Object is moved.
// This is automatically called once when creating the Object, so you don't have to call it for static objects.
func (obj *Object) Update() {
if obj.Space != nil {
// Object.Space.Remove() sets the removed object's Space to nil, indicating it's been removed. Because we're updating
// the Object (which is essentially removing it from its previous Cells / position and re-adding it to the new Cells /
// position), we store the original Space to re-set it.
space := obj.Space
obj.Space.Remove(obj)
obj.Space = space
cx, cy, ex, ey := obj.BoundsToSpace(0, 0)
for y := cy; y <= ey; y++ {
for x := cx; x <= ex; x++ {
c := obj.Space.Cell(x, y)
if c != nil {
c.register(obj)
obj.TouchingCells = append(obj.TouchingCells, c)
}
}
}
}
if obj.Shape != nil {
obj.Shape.SetPosition(obj.X, obj.Y)
}
}
// AddTags adds tags to the Object.
func (obj *Object) AddTags(tags ...string) {
obj.tags = append(obj.tags, tags...)
}
// RemoveTags removes tags from the Object.
func (obj *Object) RemoveTags(tags ...string) {
for _, tag := range tags {
for i, t := range obj.tags {
if t == tag {
obj.tags = append(obj.tags[:i], obj.tags[i+1:]...)
break
}
}
}
}
// HasTags indicates if an Object has any of the tags indicated.
func (obj *Object) HasTags(tags ...string) bool {
for _, tag := range tags {
for _, t := range obj.tags {
if t == tag {
return true
}
}
}
return false
}
// Tags returns the tags an Object has.
func (obj *Object) Tags() []string {
return append([]string{}, obj.tags...)
}
// SetShape sets the Shape on the Object, in case you need to use precise per-Shape intersection detection. SetShape calls Object.Update() as well, so that it's able to
// update the Shape's position to match its Object as necessary. (If you don't use this, the Shape's position might not match the Object's, depending on if you set the Shape
// after you added the Object to a Space and if you don't call Object.Update() yourself afterwards.)
func (obj *Object) SetShape(shape Shape) {
if obj.Shape != shape {
obj.Shape = shape
obj.Update()
}
}
// BoundsToSpace returns the Space coordinates of the shape (x, y, w, and h), given its world position and size, and a supposed movement of dx and dy.
func (obj *Object) BoundsToSpace(dx, dy float64) (int, int, int, int) {
cx, cy := obj.Space.WorldToSpace(obj.X+dx, obj.Y+dy)
ex, ey := obj.Space.WorldToSpace(obj.X+obj.W+dx-1, obj.Y+obj.H+dy-1)
return cx, cy, ex, ey
}
// SharesCells returns whether the Object occupies a cell shared by the specified other Object.
func (obj *Object) SharesCells(other *Object) bool {
for _, cell := range obj.TouchingCells {
if cell.Contains(other) {
return true
}
}
return false
}
// SharesCellsTags returns if the Cells the Object occupies have an object with the specified tags.
func (obj *Object) SharesCellsTags(tags ...string) bool {
for _, cell := range obj.TouchingCells {
if cell.ContainsTags(tags...) {
return true
}
}
return false
}
// Center returns the center position of the Object.
func (obj *Object) Center() (float64, float64) {
return obj.X + (obj.W / 2.0), obj.Y + (obj.H / 2.0)
}
// SetCenter sets the Object such that its center is at the X and Y position given.
func (obj *Object) SetCenter(x, y float64) {
obj.X = x - (obj.W / 2)
obj.Y = y - (obj.H / 2)
}
// CellPosition returns the cellular position of the Object's center in the Space.
func (obj *Object) CellPosition() (int, int) {
return obj.Space.WorldToSpace(obj.Center())
}
// SetRight sets the X position of the Object so the right edge is at the X position given.
func (obj *Object) SetRight(x float64) {
obj.X = x - obj.W
}
// SetBottom sets the Y position of the Object so that the bottom edge is at the Y position given.
func (obj *Object) SetBottom(y float64) {
obj.Y = y - obj.H
}
// Bottom returns the bottom Y coordinate of the Object (i.e. object.Y + object.H).
func (obj *Object) Bottom() float64 {
return obj.Y + obj.H
}
// Right returns the right X coordinate of the Object (i.e. object.X + object.W).
func (obj *Object) Right() float64 {
return obj.X + obj.W
}
func (obj *Object) SetBounds(topLeft, bottomRight Vector) {
obj.X = topLeft[0]
obj.Y = topLeft[1]
obj.W = bottomRight[0] - obj.X
obj.H = bottomRight[1] - obj.Y
}
// Check checks the space around the object using the designated delta movement (dx and dy). This is done by querying the containing Space's Cells
// so that it can see if moving it would coincide with a cell that houses another Object (filtered using the given selection of tag strings). If so,
// Check returns a Collision. If no objects are found or the Object does not exist within a Space, this function returns nil.
func (obj *Object) Check(dx, dy float64, tags ...string) *Collision {
if obj.Space == nil {
return nil
}
cc := NewCollision()
cc.checkingObject = obj
if dx < 0 {
dx = math.Min(dx, -1)
} else if dx > 0 {
dx = math.Max(dx, 1)
}
if dy < 0 {
dy = math.Min(dy, -1)
} else if dy > 0 {
dy = math.Max(dy, 1)
}
cc.dx = dx
cc.dy = dy
cx, cy, ex, ey := obj.BoundsToSpace(dx, dy)
objectsAdded := map[*Object]bool{}
cellsAdded := map[*Cell]bool{}
for y := cy; y <= ey; y++ {
for x := cx; x <= ex; x++ {
if c := obj.Space.Cell(x, y); c != nil {
for _, o := range c.Objects {
// We only want cells that have objects other than the checking object, or that aren't on the ignore list.
if ignored := obj.ignoreList[o]; o == obj || ignored {
continue
}
if _, added := objectsAdded[o]; (len(tags) == 0 || o.HasTags(tags...)) && !added {
cc.Objects = append(cc.Objects, o)
objectsAdded[o] = true
if _, added := cellsAdded[c]; !added {
cc.Cells = append(cc.Cells, c)
cellsAdded[c] = true
}
continue
}
}
}
}
}
if len(cc.Objects) == 0 {
return nil
}
/*
// In my use case, order of objects within a collision instance is not needed, and this also favors both runtime performance & size reduction of `jsexport.js`.
ox, oy := cc.checkingObject.Center()
oc := Vector{ox, oy}
sort.Slice(cc.Objects, func(i, j int) bool {
ix, iy := cc.Objects[i].Center()
jx, jy := cc.Objects[j].Center()
return Vector{ix, iy}.Sub(oc).Magnitude2() < Vector{jx, jy}.Sub(oc).Magnitude2()
})
cw := cc.checkingObject.Space.CellWidth
ch := cc.checkingObject.Space.CellHeight
sort.Slice(cc.Cells, func(i, j int) bool {
return Vector{float64(cc.Cells[i].X*cw + (cw / 2)), float64(cc.Cells[i].Y*ch + (ch / 2))}.Sub(oc).Magnitude2() <
Vector{float64(cc.Cells[j].X*cw + (cw / 2)), float64(cc.Cells[j].Y*ch + (ch / 2))}.Sub(oc).Magnitude2()
})
*/
return cc
}
// Overlaps returns if an Object overlaps another Object.
func (obj *Object) Overlaps(other *Object) bool {
return other.X <= obj.X+obj.W && other.X+other.W >= obj.X && other.Y <= obj.Y+obj.H && other.Y+other.H >= obj.Y
}
// AddToIgnoreList adds the specified Object to the Object's internal collision ignoral list. Cells that contain the specified Object will not be counted when calling Check().
func (obj *Object) AddToIgnoreList(ignoreObj *Object) {
obj.ignoreList[ignoreObj] = true
}
// RemoveFromIgnoreList removes the specified Object from the Object's internal collision ignoral list. Objects removed from this list will once again be counted for Check().
func (obj *Object) RemoveFromIgnoreList(ignoreObj *Object) {
delete(obj.ignoreList, ignoreObj)
}

754
resolv_tailored/shape.go Normal file
View File

@ -0,0 +1,754 @@
package resolv
import (
"math"
)
type Shape interface {
// Intersection tests to see if a Shape intersects with the other given Shape. dx and dy are delta movement variables indicating
// movement to be applied before the intersection check (thereby allowing you to see if a Shape would collide with another if it
// were in a different relative location). If an Intersection is found, a ContactSet will be returned, giving information regarding
// the intersection.
Intersection(dx, dy float64, other Shape) *ContactSet
// Bounds returns the top-left and bottom-right points of the Shape.
Bounds() (Vector, Vector)
// Position returns the X and Y position of the Shape.
Position() (float64, float64)
// SetPosition allows you to place a Shape at another location.
SetPosition(x, y float64)
// Clone duplicates the Shape.
Clone() Shape
}
// A Line is a helper shape used to determine if two ConvexPolygon lines intersect; you can't create a Line to use as a Shape.
// Instead, you can create a ConvexPolygon, specify two points, and set its Closed value to false.
type Line struct {
Start, End Vector
}
func NewLine(x, y, x2, y2 float64) *Line {
return &Line{
Start: Vector{x, y},
End: Vector{x2, y2},
}
}
func (line *Line) Project(axis Vector) Vector {
return line.Vector().Scale(axis.Dot(line.Start.Sub(line.End)))
}
func (line *Line) Normal() Vector {
v := line.Vector()
return Vector{v[1], -v[0]}.Unit()
}
func (line *Line) Vector() Vector {
return line.End.Clone().Sub(line.Start).Unit()
}
// IntersectionPointsLine returns the intersection point of a Line with another Line as a Vector. If no intersection is found, it will return nil.
func (line *Line) IntersectionPointsLine(other *Line) Vector {
det := (line.End[0]-line.Start[0])*(other.End[1]-other.Start[1]) - (other.End[0]-other.Start[0])*(line.End[1]-line.Start[1])
if det != 0 {
// MAGIC MATH; the extra + 1 here makes it so that corner cases (literally, lines going through corners) works.
// lambda := (float32(((line.Y-b.Y)*(b.X2-b.X))-((line.X-b.X)*(b.Y2-b.Y))) + 1) / float32(det)
lambda := (((line.Start[1] - other.Start[1]) * (other.End[0] - other.Start[0])) - ((line.Start[0] - other.Start[0]) * (other.End[1] - other.Start[1])) + 1) / det
// gamma := (float32(((line.Y-b.Y)*(line.X2-line.X))-((line.X-b.X)*(line.Y2-line.Y))) + 1) / float32(det)
gamma := (((line.Start[1] - other.Start[1]) * (line.End[0] - line.Start[0])) - ((line.Start[0] - other.Start[0]) * (line.End[1] - line.Start[1])) + 1) / det
if (0 < lambda && lambda < 1) && (0 < gamma && gamma < 1) {
// Delta
dx := line.End[0] - line.Start[0]
dy := line.End[1] - line.Start[1]
// dx, dy := line.GetDelta()
return Vector{line.Start[0] + (lambda * dx), line.Start[1] + (lambda * dy)}
}
}
return nil
}
// IntersectionPointsCircle returns a slice of Vectors, each indicating the intersection point. If no intersection is found, it will return an empty slice.
func (line *Line) IntersectionPointsCircle(circle *Circle) []Vector {
points := []Vector{}
cp := Vector{circle.X, circle.Y}
lStart := line.Start.Sub(cp)
lEnd := line.End.Sub(cp)
diff := lEnd.Sub(lStart)
a := diff[0]*diff[0] + diff[1]*diff[1]
b := 2 * ((diff[0] * lStart[0]) + (diff[1] * lStart[1]))
c := (lStart[0] * lStart[0]) + (lStart[1] * lStart[1]) - (circle.Radius * circle.Radius)
det := b*b - (4 * a * c)
if det < 0 {
// Do nothing, no intersections
} else if det == 0 {
t := -b / (2 * a)
if t >= 0 && t <= 1 {
points = append(points, Vector{line.Start[0] + t*diff[0], line.Start[1] + t*diff[1]})
}
} else {
t := (-b + math.Sqrt(det)) / (2 * a)
// We have to ensure t is between 0 and 1; otherwise, the collision points are on the circle as though the lines were infinite in length.
if t >= 0 && t <= 1 {
points = append(points, Vector{line.Start[0] + t*diff[0], line.Start[1] + t*diff[1]})
}
t = (-b - math.Sqrt(det)) / (2 * a)
if t >= 0 && t <= 1 {
points = append(points, Vector{line.Start[0] + t*diff[0], line.Start[1] + t*diff[1]})
}
}
return points
}
type ConvexPolygon struct {
Points []Vector
X, Y float64
Closed bool
}
// NewConvexPolygon creates a new convex polygon from the provided set of X and Y positions of 2D points (or vertices). Should generally be ordered clockwise,
// from X and Y of the first, to X and Y of the last. For example: NewConvexPolygon(0, 0, 10, 0, 10, 10, 0, 10) would create a 10x10 convex
// polygon square, with the vertices at {0,0}, {10,0}, {10, 10}, and {0, 10}.
func NewConvexPolygon(points ...float64) *ConvexPolygon {
// if len(points)/2 < 2 {
// return nil
// }
cp := &ConvexPolygon{Points: []Vector{}, Closed: true}
cp.AddPoints(points...)
return cp
}
func (cp *ConvexPolygon) Clone() Shape {
points := []Vector{}
for _, point := range cp.Points {
points = append(points, point.Clone())
}
newPoly := NewConvexPolygon()
newPoly.X = cp.X
newPoly.Y = cp.Y
newPoly.AddPointsVec(points...)
newPoly.Closed = cp.Closed
return newPoly
}
// AddPointsVec allows you to add points to the ConvexPolygon with a slice of Vectors, each indicating a point / vertex.
func (cp *ConvexPolygon) AddPointsVec(points ...Vector) {
cp.Points = append(cp.Points, points...)
}
// AddPoints allows you to add points to the ConvexPolygon with a slice or selection of float64s, with each pair indicating an X or Y value for
// a point / vertex (i.e. AddPoints(0, 1, 2, 3) would add two points - one at {0, 1}, and another at {2, 3}).
func (cp *ConvexPolygon) AddPoints(vertexPositions ...float64) {
for v := 0; v < len(vertexPositions); v += 2 {
cp.Points = append(cp.Points, Vector{vertexPositions[v], vertexPositions[v+1]})
}
}
// Lines returns a slice of transformed Lines composing the ConvexPolygon.
func (cp *ConvexPolygon) Lines() []*Line {
lines := []*Line{}
vertices := cp.Transformed()
for i := 0; i < len(vertices); i++ {
start, end := vertices[i], vertices[0]
if i < len(vertices)-1 {
end = vertices[i+1]
} else if !cp.Closed {
break
}
line := NewLine(start[0], start[1], end[0], end[1])
lines = append(lines, line)
}
return lines
}
// Transformed returns the ConvexPolygon's points / vertices, transformed according to the ConvexPolygon's position.
func (cp *ConvexPolygon) Transformed() []Vector {
transformed := []Vector{}
for _, point := range cp.Points {
transformed = append(transformed, Vector{point[0] + cp.X, point[1] + cp.Y})
}
return transformed
}
// Bounds returns two Vectors, comprising the top-left and bottom-right positions of the bounds of the
// ConvexPolygon, post-transformation.
func (cp *ConvexPolygon) Bounds() (Vector, Vector) {
transformed := cp.Transformed()
topLeft := Vector{transformed[0][0], transformed[0][1]}
bottomRight := topLeft.Clone()
for i := 0; i < len(transformed); i++ {
point := transformed[i]
if point[0] < topLeft[0] {
topLeft[0] = point[0]
} else if point[0] > bottomRight[0] {
bottomRight[0] = point[0]
}
if point[1] < topLeft[1] {
topLeft[1] = point[1]
} else if point[1] > bottomRight[1] {
bottomRight[1] = point[1]
}
}
return topLeft, bottomRight
}
// Position returns the position of the ConvexPolygon.
func (cp *ConvexPolygon) Position() (float64, float64) {
return cp.X, cp.Y
}
// SetPosition sets the position of the ConvexPolygon. The offset of the vertices compared to the X and Y position is relative to however
// you initially defined the polygon and added the vertices.
func (cp *ConvexPolygon) SetPosition(x, y float64) {
cp.X = x
cp.Y = y
}
// SetPositionVec allows you to set the position of the ConvexPolygon using a Vector. The offset of the vertices compared to the X and Y
// position is relative to however you initially defined the polygon and added the vertices.
func (cp *ConvexPolygon) SetPositionVec(vec Vector) {
cp.X = vec.X()
cp.Y = vec.Y()
}
// Move translates the ConvexPolygon by the designated X and Y values.
func (cp *ConvexPolygon) Move(x, y float64) {
cp.X += x
cp.Y += y
}
// MoveVec translates the ConvexPolygon by the designated Vector.
func (cp *ConvexPolygon) MoveVec(vec Vector) {
cp.X += vec.X()
cp.Y += vec.Y()
}
// Center returns the transformed Center of the ConvexPolygon.
func (cp *ConvexPolygon) Center() Vector {
pos := Vector{0, 0}
for _, v := range cp.Transformed() {
pos.Add(v)
}
pos[0] /= float64(len(cp.Transformed()))
pos[1] /= float64(len(cp.Transformed()))
return pos
}
// Project projects (i.e. flattens) the ConvexPolygon onto the provided axis.
func (cp *ConvexPolygon) Project(axis Vector) Projection {
axis = axis.Unit()
vertices := cp.Transformed()
min := axis.Dot(Vector{vertices[0][0], vertices[0][1]})
max := min
for i := 1; i < len(vertices); i++ {
p := axis.Dot(Vector{vertices[i][0], vertices[i][1]})
if p < min {
min = p
} else if p > max {
max = p
}
}
return Projection{min, max}
}
// SATAxes returns the axes of the ConvexPolygon for SAT intersection testing.
func (cp *ConvexPolygon) SATAxes() []Vector {
axes := []Vector{}
for _, line := range cp.Lines() {
axes = append(axes, line.Normal())
}
return axes
}
// PointInside returns if a Point (a Vector) is inside the ConvexPolygon.
func (polygon *ConvexPolygon) PointInside(point Vector) bool {
pointLine := NewLine(point[0], point[1], point[0]+999999999999, point[1])
contactCount := 0
for _, line := range polygon.Lines() {
if line.IntersectionPointsLine(pointLine) != nil {
contactCount++
}
}
return contactCount == 1
}
type ContactSet struct {
Points []Vector // Slice of Points indicating contact between the two Shapes.
MTV Vector // Minimum Translation Vector; this is the vector to move a Shape on to move it outside of its contacting Shape.
Center Vector // Center of the Contact set; this is the average of all Points contained within the Contact Set.
}
func NewContactSet() *ContactSet {
return &ContactSet{
Points: []Vector{},
MTV: Vector{0, 0},
Center: Vector{0, 0},
}
}
// LeftmostPoint returns the left-most point out of the ContactSet's Points slice. If the Points slice is empty somehow, this returns nil.
func (cs *ContactSet) LeftmostPoint() Vector {
var left Vector
for _, point := range cs.Points {
if left == nil || point[0] < left[0] {
left = point
}
}
return left
}
// RightmostPoint returns the right-most point out of the ContactSet's Points slice. If the Points slice is empty somehow, this returns nil.
func (cs *ContactSet) RightmostPoint() Vector {
var right Vector
for _, point := range cs.Points {
if right == nil || point[0] > right[0] {
right = point
}
}
return right
}
// TopmostPoint returns the top-most point out of the ContactSet's Points slice. If the Points slice is empty somehow, this returns nil.
func (cs *ContactSet) TopmostPoint() Vector {
var top Vector
for _, point := range cs.Points {
if top == nil || point[1] < top[1] {
top = point
}
}
return top
}
// BottommostPoint returns the bottom-most point out of the ContactSet's Points slice. If the Points slice is empty somehow, this returns nil.
func (cs *ContactSet) BottommostPoint() Vector {
var bottom Vector
for _, point := range cs.Points {
if bottom == nil || point[1] > bottom[1] {
bottom = point
}
}
return bottom
}
// Intersection tests to see if a ConvexPolygon intersects with the other given Shape. dx and dy are delta movement variables indicating
// movement to be applied before the intersection check (thereby allowing you to see if a Shape would collide with another if it
// were in a different relative location). If an Intersection is found, a ContactSet will be returned, giving information regarding
// the intersection.
func (cp *ConvexPolygon) Intersection(dx, dy float64, other Shape) *ContactSet {
contactSet := NewContactSet()
ogX := cp.X
ogY := cp.Y
cp.X += dx
cp.Y += dy
if circle, isCircle := other.(*Circle); isCircle {
for _, line := range cp.Lines() {
contactSet.Points = append(contactSet.Points, line.IntersectionPointsCircle(circle)...)
}
} else if poly, isPoly := other.(*ConvexPolygon); isPoly {
for _, line := range cp.Lines() {
for _, otherLine := range poly.Lines() {
if point := line.IntersectionPointsLine(otherLine); point != nil {
contactSet.Points = append(contactSet.Points, point)
}
}
}
}
if len(contactSet.Points) > 0 {
for _, point := range contactSet.Points {
contactSet.Center = contactSet.Center.Add(point)
}
contactSet.Center[0] /= float64(len(contactSet.Points))
contactSet.Center[1] /= float64(len(contactSet.Points))
if mtv := cp.calculateMTV(contactSet, other); mtv != nil {
contactSet.MTV = mtv
}
} else {
contactSet = nil
}
// If dx or dy aren't 0, then the MTV will be greater to compensate; this adjusts the vector back.
if contactSet != nil && (dx != 0 || dy != 0) {
deltaMagnitude := Vector{dx, dy}.Magnitude()
ogMagnitude := contactSet.MTV.Magnitude()
contactSet.MTV = contactSet.MTV.Unit().Scale(ogMagnitude - deltaMagnitude)
}
cp.X = ogX
cp.Y = ogY
return contactSet
}
// calculateMTV returns the MTV, if possible, and a bool indicating whether it was possible or not.
func (cp *ConvexPolygon) calculateMTV(contactSet *ContactSet, otherShape Shape) Vector {
delta := Vector{0, 0}
smallest := Vector{math.MaxFloat64, 0}
switch other := otherShape.(type) {
case *ConvexPolygon:
for _, axis := range cp.SATAxes() {
if !cp.Project(axis).Overlapping(other.Project(axis)) {
return nil
}
overlap := cp.Project(axis).Overlap(other.Project(axis))
if smallest.Magnitude() > overlap {
smallest = axis.Scale(overlap)
}
}
for _, axis := range other.SATAxes() {
if !cp.Project(axis).Overlapping(other.Project(axis)) {
return nil
}
overlap := cp.Project(axis).Overlap(other.Project(axis))
if smallest.Magnitude() > overlap {
smallest = axis.Scale(overlap)
}
}
// Removed support of "Circle" to remove dependency of "sort" module
}
delta[0] = smallest[0]
delta[1] = smallest[1]
return delta
}
// ContainedBy returns if the ConvexPolygon is wholly contained by the other shape provided.
func (cp *ConvexPolygon) ContainedBy(otherShape Shape) bool {
switch other := otherShape.(type) {
case *ConvexPolygon:
for _, axis := range cp.SATAxes() {
if !cp.Project(axis).IsInside(other.Project(axis)) {
return false
}
}
for _, axis := range other.SATAxes() {
if !cp.Project(axis).IsInside(other.Project(axis)) {
return false
}
}
}
return true
}
// FlipH flips the ConvexPolygon's vertices horizontally according to their initial offset when adding the points.
func (cp *ConvexPolygon) FlipH() {
for _, v := range cp.Points {
v[0] = -v[0]
}
// We have to reverse vertex order after flipping the vertices to ensure the winding order is consistent between Objects (so that the normals are consistently outside or inside, which is important
// when doing Intersection tests). If we assume that the normal of a line, going from vertex A to vertex B, is one direction, then the normal would be inverted if the vertices were flipped in position,
// but not in order. This would make Intersection tests drive objects into each other, instead of giving the delta to move away.
cp.ReverseVertexOrder()
}
// FlipV flips the ConvexPolygon's vertices vertically according to their initial offset when adding the points.
func (cp *ConvexPolygon) FlipV() {
for _, v := range cp.Points {
v[1] = -v[1]
}
cp.ReverseVertexOrder()
}
// ReverseVertexOrder reverses the vertex ordering of the ConvexPolygon.
func (cp *ConvexPolygon) ReverseVertexOrder() {
verts := []Vector{cp.Points[0]}
for i := len(cp.Points) - 1; i >= 1; i-- {
verts = append(verts, cp.Points[i])
}
cp.Points = verts
}
// NewRectangle returns a rectangular ConvexPolygon with the vertices in clockwise order. In actuality, an AABBRectangle should be its own
// "thing" with its own optimized Intersection code check.
func NewRectangle(x, y, w, h float64) *ConvexPolygon {
return NewConvexPolygon(
x, y,
x+w, y,
x+w, y+h,
x, y+h,
)
}
type Circle struct {
X, Y, Radius float64
}
// NewCircle returns a new Circle, with its center at the X and Y position given, and with the defined radius.
func NewCircle(x, y, radius float64) *Circle {
circle := &Circle{
X: x,
Y: y,
Radius: radius,
}
return circle
}
func (circle *Circle) Clone() Shape {
return NewCircle(circle.X, circle.Y, circle.Radius)
}
// Bounds returns the top-left and bottom-right corners of the Circle.
func (circle *Circle) Bounds() (Vector, Vector) {
return Vector{circle.X - circle.Radius, circle.Y - circle.Radius}, Vector{circle.X + circle.Radius, circle.Y + circle.Radius}
}
// Intersection tests to see if a Circle intersects with the other given Shape. dx and dy are delta movement variables indicating
// movement to be applied before the intersection check (thereby allowing you to see if a Shape would collide with another if it
// were in a different relative location). If an Intersection is found, a ContactSet will be returned, giving information regarding
// the intersection.
func (circle *Circle) Intersection(dx, dy float64, other Shape) *ContactSet {
var contactSet *ContactSet
ox := circle.X
oy := circle.Y
circle.X += dx
circle.Y += dy
// here
switch shape := other.(type) {
case *ConvexPolygon:
// Maybe this would work?
contactSet = shape.Intersection(-dx, -dy, circle)
if contactSet != nil {
contactSet.MTV = contactSet.MTV.Scale(-1)
}
case *Circle:
contactSet = NewContactSet()
contactSet.Points = circle.IntersectionPointsCircle(shape)
if len(contactSet.Points) == 0 {
return nil
}
contactSet.MTV = Vector{circle.X - shape.X, circle.Y - shape.Y}
dist := contactSet.MTV.Magnitude()
contactSet.MTV = contactSet.MTV.Unit().Scale(circle.Radius + shape.Radius - dist)
for _, point := range contactSet.Points {
contactSet.Center = contactSet.Center.Add(point)
}
contactSet.Center[0] /= float64(len(contactSet.Points))
contactSet.Center[1] /= float64(len(contactSet.Points))
// if contactSet != nil {
// contactSet.MTV[0] -= dx
// contactSet.MTV[1] -= dy
// }
// contactSet.MTV = Vector{circle.X - shape.X, circle.Y - shape.Y}
}
circle.X = ox
circle.Y = oy
return contactSet
}
// Move translates the Circle by the designated X and Y values.
func (circle *Circle) Move(x, y float64) {
circle.X += x
circle.Y += y
}
// MoveVec translates the Circle by the designated Vector.
func (circle *Circle) MoveVec(vec Vector) {
circle.X += vec.X()
circle.Y += vec.Y()
}
// SetPosition sets the center position of the Circle using the X and Y values given.
func (circle *Circle) SetPosition(x, y float64) {
circle.X = x
circle.Y = y
}
// SetPosition sets the center position of the Circle using the Vector given.
func (circle *Circle) SetPositionVec(vec Vector) {
circle.X = vec.X()
circle.Y = vec.Y()
}
// Position() returns the X and Y position of the Circle.
func (circle *Circle) Position() (float64, float64) {
return circle.X, circle.Y
}
// PointInside returns if the given Vector is inside of the circle.
func (circle *Circle) PointInside(point Vector) bool {
return point.Sub(Vector{circle.X, circle.Y}).Magnitude() <= circle.Radius
}
// IntersectionPointsCircle returns the intersection points of the two circles provided.
func (circle *Circle) IntersectionPointsCircle(other *Circle) []Vector {
d := math.Sqrt(math.Pow(other.X-circle.X, 2) + math.Pow(other.Y-circle.Y, 2))
if d > circle.Radius+other.Radius || d < math.Abs(circle.Radius-other.Radius) || d == 0 && circle.Radius == other.Radius {
return nil
}
a := (math.Pow(circle.Radius, 2) - math.Pow(other.Radius, 2) + math.Pow(d, 2)) / (2 * d)
h := math.Sqrt(math.Pow(circle.Radius, 2) - math.Pow(a, 2))
x2 := circle.X + a*(other.X-circle.X)/d
y2 := circle.Y + a*(other.Y-circle.Y)/d
return []Vector{
{x2 + h*(other.Y-circle.Y)/d, y2 - h*(other.X-circle.X)/d},
{x2 - h*(other.Y-circle.Y)/d, y2 + h*(other.X-circle.X)/d},
}
}
type Projection struct {
Min, Max float64
}
// Overlapping returns whether a Projection is overlapping with the other, provided Projection. Credit to https://www.sevenson.com.au/programming/sat/
func (projection Projection) Overlapping(other Projection) bool {
return projection.Overlap(other) > 0
}
// Overlap returns the amount that a Projection is overlapping with the other, provided Projection. Credit to https://dyn4j.org/2010/01/sat/#sat-nointer
func (projection Projection) Overlap(other Projection) float64 {
return math.Min(projection.Max, other.Max) - math.Max(projection.Min, other.Min)
}
// IsInside returns whether the Projection is wholly inside of the other, provided Projection.
func (projection Projection) IsInside(other Projection) bool {
return projection.Min >= other.Min && projection.Max <= other.Max
}

263
resolv_tailored/space.go Normal file
View File

@ -0,0 +1,263 @@
package resolv
import (
"math"
)
// Space represents a collision space. Internally, each Space contains a 2D array of Cells, with each Cell being the same size. Cells contain information on which
// Objects occupy those spaces.
type Space struct {
Cells [][]*Cell
CellWidth, CellHeight int // Width and Height of each Cell in "world-space" / pixels / whatever
}
// NewSpace creates a new Space. spaceWidth and spaceHeight is the width and height of the Space (usually in pixels), which is then populated with cells of size
// cellWidth by cellHeight. Generally, you want cells to be the size of the smallest collide-able objects in your game, and you want to move Objects at a maximum
// speed of one cell size per collision check to avoid missing any possible collisions.
func NewSpace(spaceWidth, spaceHeight, cellWidth, cellHeight int) *Space {
sp := &Space{
CellWidth: cellWidth,
CellHeight: cellHeight,
}
sp.Resize(spaceWidth/cellWidth, spaceHeight/cellHeight)
// sp.Resize(int(math.Ceil(float64(spaceWidth)/float64(cellWidth))),
// int(math.Ceil(float64(spaceHeight)/float64(cellHeight))))
return sp
}
// Add adds the specified Objects to the Space, updating the Space's cells to refer to the Object.
func (sp *Space) Add(objects ...*Object) {
if sp == nil {
panic("ERROR: space is nil")
}
for _, obj := range objects {
obj.Space = sp
// We call Update() once to make sure the object gets its cells added.
obj.Update()
}
}
// Remove removes the specified Objects from being associated with the Space. This should be done whenever an Object is removed from the
// game.
func (sp *Space) Remove(objects ...*Object) {
if sp == nil {
panic("ERROR: space is nil")
}
for _, obj := range objects {
for _, cell := range obj.TouchingCells {
cell.unregister(obj)
}
obj.TouchingCells = []*Cell{}
obj.Space = nil
}
}
// Objects loops through all Cells in the Space (from top to bottom, and from left to right) to return all Objects
// that exist in the Space. Of course, each Object is counted only once.
func (sp *Space) Objects() []*Object {
objectsAdded := map[*Object]bool{}
objects := []*Object{}
for cy := range sp.Cells {
for cx := range sp.Cells[cy] {
for _, o := range sp.Cells[cy][cx].Objects {
if _, added := objectsAdded[o]; !added {
objects = append(objects, o)
objectsAdded[o] = true
}
}
}
}
return objects
}
// Resize resizes the internal Cells array.
func (sp *Space) Resize(width, height int) {
sp.Cells = [][]*Cell{}
for y := 0; y < height; y++ {
sp.Cells = append(sp.Cells, []*Cell{})
for x := 0; x < width; x++ {
sp.Cells[y] = append(sp.Cells[y], newCell(x, y))
}
}
}
// Cell returns the Cell at the given cellular / spatial (not world) X and Y position in the Space. If the X and Y position are
// out of bounds, Cell() will return nil.
func (sp *Space) Cell(x, y int) *Cell {
if y >= 0 && y < len(sp.Cells) && x >= 0 && x < len(sp.Cells[y]) {
return sp.Cells[y][x]
}
return nil
}
// CheckCells checks a set of cells (from x,y to x + w, y + h in cellular coordinates) and return the first object within those Cells that contains any of the tags given.
// If no tags are given, then CheckCells will return the first Object found in any Cell.
func (sp *Space) CheckCells(x, y, w, h int, tags ...string) *Object {
for ix := x; ix < x+w; ix++ {
for iy := y; iy < y+h; iy++ {
cell := sp.Cell(ix, iy)
if cell != nil {
if len(tags) > 0 {
if cell.ContainsTags(tags...) {
for _, obj := range cell.Objects {
if obj.HasTags(tags...) {
return obj
}
}
}
} else if cell.Occupied() {
return cell.Objects[0]
}
}
}
}
return nil
}
// CheckCellsWorld checks the cells of the Grid with the given world coordinates.
// Internally, this is just syntactic sugar for calling Space.WorldToSpace() on the
// position and size given.
func (sp *Space) CheckCellsWorld(x, y, w, h float64, tags ...string) *Object {
sx, sy := sp.WorldToSpace(x, y)
cw, ch := sp.WorldToSpace(w, h)
return sp.CheckCells(sx, sy, cw, ch, tags...)
}
// UnregisterAllObjects unregisters all Objects registered to Cells in the Space.
func (sp *Space) UnregisterAllObjects() {
for y := 0; y < len(sp.Cells); y++ {
for x := 0; x < len(sp.Cells[y]); x++ {
cell := sp.Cells[y][x]
sp.Remove(cell.Objects...)
}
}
}
// WorldToSpace converts from a world position (x, y) to a position in the Space (a grid-based position).
func (sp *Space) WorldToSpace(x, y float64) (int, int) {
fx := int(math.Floor(x / float64(sp.CellWidth)))
fy := int(math.Floor(y / float64(sp.CellHeight)))
return fx, fy
}
// SpaceToWorld converts from a position in the Space (on a grid) to a world-based position, given the size of the Space when first created.
func (sp *Space) SpaceToWorld(x, y int) (float64, float64) {
fx := float64(x * sp.CellWidth)
fy := float64(y * sp.CellHeight)
return fx, fy
}
// Height returns the height of the Space grid in Cells (so a 320x240 Space with 16x16 cells would have a height of 15).
func (sp *Space) Height() int {
return len(sp.Cells)
}
// Width returns the width of the Space grid in Cells (so a 320x240 Space with 16x16 cells would have a width of 20).
func (sp *Space) Width() int {
if len(sp.Cells) > 0 {
return len(sp.Cells[0])
}
return 0
}
func (sp *Space) CellsInLine(startX, startY, endX, endY int) []*Cell {
cells := []*Cell{}
cell := sp.Cell(startX, startY)
endCell := sp.Cell(endX, endY)
if cell != nil && endCell != nil {
dv := Vector{float64(endX - startX), float64(endY - startY)}.Unit()
dv[0] *= float64(sp.CellWidth / 2)
dv[1] *= float64(sp.CellHeight / 2)
pX, pY := sp.SpaceToWorld(startX, startY)
p := Vector{pX + float64(sp.CellWidth/2), pY + float64(sp.CellHeight/2)}
alternate := false
for cell != nil {
if cell == endCell {
cells = append(cells, cell)
break
}
cells = append(cells, cell)
if alternate {
p[1] += dv[1]
} else {
p[0] += dv[0]
}
cx, cy := sp.WorldToSpace(p[0], p[1])
c := sp.Cell(cx, cy)
if c != cell {
cell = c
}
alternate = !alternate
}
}
return cells
}

281
resolv_tailored/vector.go Normal file
View File

@ -0,0 +1,281 @@
package resolv
import (
"math"
)
// Vector is the definition of a row vector that contains scalars as
// 64 bit floats
type Vector []float64
// Axis is an integer enum type that describes vector axis
type Axis int
const (
// the consts below are used to represent vector axis, they are useful
// to lookup values within the vector.
X Axis = iota
Y
Z
)
// Clone a vector
func Clone(v Vector) Vector {
return v.Clone()
}
// Clone a vector
func (v Vector) Clone() Vector {
clone := make(Vector, len(v))
copy(clone, v)
return clone
}
// Add a vector with a vector or a set of vectors
func Add(v1 Vector, vs ...Vector) Vector {
return v1.Clone().Add(vs...)
}
// Add a vector with a vector or a set of vectors
func (v Vector) Add(vs ...Vector) Vector {
dim := len(v)
for i := range vs {
if len(vs[i]) > dim {
axpyUnitaryTo(v, 1, v, vs[i][:dim])
} else {
axpyUnitaryTo(v, 1, v, vs[i])
}
}
return v
}
// Sub subtracts a vector with another vector or a set of vectors
func Sub(v1 Vector, vs ...Vector) Vector {
return v1.Clone().Sub(vs...)
}
// Sub subtracts a vector with another vector or a set of vectors
func (v Vector) Sub(vs ...Vector) Vector {
dim := len(v)
for i := range vs {
if len(vs[i]) > dim {
axpyUnitaryTo(v, -1, vs[i][:dim], v)
} else {
axpyUnitaryTo(v, -1, vs[i], v)
}
}
return v
}
// Scale vector with a given size
func Scale(v Vector, size float64) Vector {
return v.Clone().Scale(size)
}
// Scale vector with a given size
func (v Vector) Scale(size float64) Vector {
scalUnitaryTo(v, size, v)
return v
}
// Equal compares that two vectors are equal to each other
func Equal(v1, v2 Vector) bool {
return v1.Equal(v2)
}
// Equal compares that two vectors are equal to each other
func (v Vector) Equal(v2 Vector) bool {
if len(v) != len(v2) {
return false
}
for i := range v {
if math.Abs(v[i]-v2[i]) > 1e-8 {
return false
}
}
return true
}
// Magnitude of a vector
func Magnitude(v Vector) float64 {
return v.Magnitude()
}
// Magnitude of a vector
func (v Vector) Magnitude() float64 {
return math.Sqrt(v.Magnitude2())
}
func (v Vector) Magnitude2() float64 {
var result float64
for _, scalar := range v {
result += scalar * scalar
}
return result
}
// Unit returns a direction vector with the length of one.
func Unit(v Vector) Vector {
return v.Clone().Unit()
}
// Unit returns a direction vector with the length of one.
func (v Vector) Unit() Vector {
l := v.Magnitude()
if l < 1e-8 {
return v
}
for i := range v {
v[i] = v[i] / l
}
return v
}
// Dot product of two vectors
func Dot(v1, v2 Vector) float64 {
result, dim1, dim2 := 0., len(v1), len(v2)
if dim1 > dim2 {
v2 = append(v2, make(Vector, dim1-dim2)...)
}
if dim1 < dim2 {
v1 = append(v1, make(Vector, dim2-dim1)...)
}
for i := range v1 {
result += v1[i] * v2[i]
}
return result
}
// Dot product of two vectors
func (v Vector) Dot(v2 Vector) float64 {
return Dot(v, v2)
}
// Cross product of two vectors
func Cross(v1, v2 Vector) Vector {
return v1.Cross(v2)
}
// Cross product of two vectors
func (v Vector) Cross(v2 Vector) Vector {
if len(v) != 3 || len(v2) != 3 {
return nil
}
return Vector{
v[Y]*v2[Z] - v[Z]*v2[Y],
v[Z]*v2[X] - v[X]*v2[Z],
v[X]*v2[Z] - v[Z]*v2[X],
}
}
// Rotate is rotating a vector around a specified axis.
// If no axis are specified, it will default to the Z axis.
//
// If a vector with more than 3-dimensions is rotated, it will cut the extra
// dimensions and return a 3-dimensional vector.
//
// NOTE: the ...Axis is just syntactic sugar that allows the axis to not be
// specified and default to Z, if multiple axis is passed the first will be
// set as the rotation axis
func Rotate(v Vector, angle float64, as ...Axis) Vector {
return v.Clone().Rotate(angle, as...)
}
// Rotate is rotating a vector around a specified axis.
// If no axis are specified, it will default to the Z axis.
//
// If a vector with more than 3-dimensions is rotated, it will cut the extra
// dimensions and return a 3-dimensional vector.
//
// NOTE: the ...Axis is just syntactic sugar that allows the axis to not be
// specified and default to Z, if multiple axis is passed the first will be
// set as the rotation axis
func (v Vector) Rotate(angle float64, as ...Axis) Vector {
axis, dim := Z, len(v)
if dim == 0 {
return v
}
if len(as) > 0 {
axis = as[0]
}
if dim == 1 && axis != Z {
v = append(v, 0, 0)
}
if (dim < 2 && axis == Z) || (dim == 2 && axis != Z) {
v = append(v, 0)
}
x, y := v[X], v[Y]
cos, sin := math.Cos(angle), math.Sin(angle)
switch axis {
case X:
z := v[Z]
v[Y] = y*cos - z*sin
v[Z] = y*sin + z*cos
case Y:
z := v[Z]
v[X] = x*cos + z*sin
v[Z] = -x*sin + z*cos
case Z:
v[X] = x*cos - y*sin
v[Y] = x*sin + y*cos
}
if dim > 3 {
return v[:3]
}
return v
}
// X is corresponding to doing a v[0] lookup, if index 0 does not exist yet, a
// 0 will be returned instead
func (v Vector) X() float64 {
if len(v) < 1 {
return 0.
}
return v[X]
}
// Y is corresponding to doing a v[1] lookup, if index 1 does not exist yet, a
// 0 will be returned instead
func (v Vector) Y() float64 {
if len(v) < 2 {
return 0.
}
return v[Y]
}
// Z is corresponding to doing a v[2] lookup, if index 2 does not exist yet, a
// 0 will be returned instead
func (v Vector) Z() float64 {
if len(v) < 3 {
return 0.
}
return v[Z]
}