mirror of
https://github.com/giongto35/cloud-game.git
synced 2026-01-23 02:34:42 +00:00
Update cloud data storage functionality (#349)
Add Oracle Data Storage support for cloud saves.
This commit is contained in:
parent
b916b0bf19
commit
69ff8ae896
22 changed files with 470 additions and 263 deletions
|
|
@ -1,3 +1,5 @@
|
|||
CLOUD_GAME_STORAGE_KEY=https://objectstorage.eu-frankfurt-1.oraclecloud.com/p/VVlJPTNcP28wlnBXtA0ezjD5fTut2T01qz5TVwdNejQc6OR1DF5VYYjKTTP2HIlL/n/frmb1qagq0wf/b/cloud-retro-st-001/o/
|
||||
CLOUD_GAME_STORAGE_PROVIDER=oracle
|
||||
CLOUD_GAME_WORKER_NETWORK_COORDINATORADDRESS=cloudretro.io
|
||||
CLOUD_GAME_WORKER_NETWORK_PUBLICADDRESS=cloudretro.io
|
||||
CLOUD_GAME_WORKER_NETWORK_SECURE=true
|
||||
|
|
|
|||
11
configs/config.yaml
vendored
11
configs/config.yaml
vendored
|
|
@ -203,6 +203,17 @@ encoder:
|
|||
# (experimental)
|
||||
withoutGame: false
|
||||
|
||||
storage:
|
||||
# cloud storage provider:
|
||||
# - empty (No op storage stub)
|
||||
# - google [Google Cloud Storage](https://cloud.google.com/storage)
|
||||
# - oracle [Oracle Object Storage](https://www.oracle.com/cloud/storage/object-storage.html)
|
||||
provider:
|
||||
# this value contains arbitrary key attribute:
|
||||
# - google: not used (see: https://cloud.google.com/docs/authentication/production#create_service_account)
|
||||
# - oracle: pre-authenticated URL (see: https://docs.oracle.com/en-us/iaas/Content/Object/Tasks/usingpreauthenticatedrequests.htm)
|
||||
key:
|
||||
|
||||
webrtc:
|
||||
# turn off default Pion interceptors for performance reasons
|
||||
# (experimental)
|
||||
|
|
|
|||
33
go.mod
vendored
33
go.mod
vendored
|
|
@ -3,35 +3,36 @@ module github.com/giongto35/cloud-game/v2
|
|||
go 1.13
|
||||
|
||||
require (
|
||||
cloud.google.com/go v0.91.1 // indirect
|
||||
cloud.google.com/go/storage v1.16.0
|
||||
cloud.google.com/go/storage v1.16.1
|
||||
github.com/cavaliercoder/grab v1.0.1-0.20201108051000-98a5bfe305ec
|
||||
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
||||
github.com/faiface/mainthread v0.0.0-20171120011319-8b78f0a41ae3
|
||||
github.com/fsnotify/fsnotify v1.4.9
|
||||
github.com/go-gl/gl v0.0.0-20210813123233-e4099ee2221f
|
||||
github.com/fsnotify/fsnotify v1.5.1
|
||||
github.com/go-gl/gl v0.0.0-20210905235341-f7a045908259
|
||||
github.com/gofrs/flock v0.8.1
|
||||
github.com/gofrs/uuid v4.0.0+incompatible
|
||||
github.com/golang/glog v0.0.0-20210429001901-424d2337a529
|
||||
github.com/golang/glog v1.0.0
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/gorilla/websocket v1.4.2
|
||||
github.com/kkyr/fig v0.3.0
|
||||
github.com/mitchellh/mapstructure v1.4.2 // indirect
|
||||
github.com/pelletier/go-toml v1.9.4 // indirect
|
||||
github.com/pion/ice/v2 v2.1.12 // indirect
|
||||
github.com/pion/interceptor v0.0.15
|
||||
github.com/pion/rtp v1.7.1
|
||||
github.com/pion/interceptor v0.0.19
|
||||
github.com/pion/rtcp v1.2.8 // indirect
|
||||
github.com/pion/rtp v1.7.2
|
||||
github.com/pion/srtp/v2 v2.0.5 // indirect
|
||||
github.com/pion/webrtc/v3 v3.0.32
|
||||
github.com/prometheus/client_golang v1.11.0
|
||||
github.com/prometheus/common v0.30.0 // indirect
|
||||
github.com/prometheus/procfs v0.7.2 // indirect
|
||||
github.com/prometheus/procfs v0.7.3 // indirect
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/veandco/go-sdl2 v0.4.8
|
||||
golang.org/x/crypto v0.0.0-20210813211128-0a44fdfbc16e
|
||||
github.com/veandco/go-sdl2 v0.4.10
|
||||
golang.org/x/crypto v0.0.0-20210915214749-c084706c2272
|
||||
golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d
|
||||
golang.org/x/mod v0.5.0 // indirect
|
||||
golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d // indirect
|
||||
golang.org/x/oauth2 v0.0.0-20210810183815-faf39c7919d5 // indirect
|
||||
golang.org/x/sys v0.0.0-20210816074244-15123e1e1f71 // indirect
|
||||
golang.org/x/net v0.0.0-20210916014120-12bc252f5db8 // indirect
|
||||
golang.org/x/sys v0.0.0-20210915083310-ed5796bab164 // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
google.golang.org/api v0.54.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20210816143620-e15ff196659d // indirect
|
||||
google.golang.org/api v0.57.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20210916144049-3192f974c780 // indirect
|
||||
)
|
||||
|
|
|
|||
100
go.sum
vendored
100
go.sum
vendored
|
|
@ -21,10 +21,10 @@ cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E
|
|||
cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY=
|
||||
cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM=
|
||||
cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY=
|
||||
cloud.google.com/go v0.88.0/go.mod h1:dnKwfYbP9hQhefiUvpbcAyoGSHUrOxR20JVElLiUvEY=
|
||||
cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ=
|
||||
cloud.google.com/go v0.91.1 h1:w+u8ttN/QtYrpvgXNUd2G6kwqrqCIQbkINlXQjHP1ek=
|
||||
cloud.google.com/go v0.91.1/go.mod h1:V358WZfbFQkmC3gv5XCxzZq2e3h7OGvQR0IXtj77ylI=
|
||||
cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI=
|
||||
cloud.google.com/go v0.94.1 h1:DwuSvDZ1pTYGbXo8yOJevCTr3BoBlE+OVkHAKiYQUXc=
|
||||
cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4=
|
||||
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=
|
||||
|
|
@ -42,8 +42,8 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo
|
|||
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=
|
||||
cloud.google.com/go/storage v1.16.0 h1:1UwAux2OZP4310YXg5ohqBEpV16Y93uZG4+qOX7K2Kg=
|
||||
cloud.google.com/go/storage v1.16.0/go.mod h1:ieKBmUyzcftN5tbxwnXClMKH00CfcQ+xL6NN0r5QfmE=
|
||||
cloud.google.com/go/storage v1.16.1 h1:sMEIc4wxvoY3NXG7Rn9iP7jb/2buJgWR1vNXCR/UPfs=
|
||||
cloud.google.com/go/storage v1.16.1/go.mod h1:LaNorbty3ehnU3rEjXSNV/NRgQA0O8Y+uh6bPe5UOk4=
|
||||
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=
|
||||
|
|
@ -63,8 +63,9 @@ github.com/cavaliercoder/grab v1.0.1-0.20201108051000-98a5bfe305ec/go.mod h1:NbX
|
|||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
|
||||
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
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=
|
||||
|
|
@ -87,11 +88,12 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7
|
|||
github.com/faiface/mainthread v0.0.0-20171120011319-8b78f0a41ae3 h1:baVdMKlASEHrj19iqjARrPbaRisD7EuZEVJj6ZMLl1Q=
|
||||
github.com/faiface/mainthread v0.0.0-20171120011319-8b78f0a41ae3/go.mod h1:VEPNJUlxl5KdWjDvz6Q1l+rJlxF2i6xqDeGuGAxa87M=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
||||
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/gl v0.0.0-20210813123233-e4099ee2221f h1:s0O46d8fPwk9kU4k1jj76wBquMVETx7uveQD9MCIQoU=
|
||||
github.com/go-gl/gl v0.0.0-20210813123233-e4099ee2221f/go.mod h1:wjpnOv6ONl2SuJSxqCPVaPZibGFdSci9HFocT9qtVYM=
|
||||
github.com/go-gl/gl v0.0.0-20210905235341-f7a045908259 h1:8q7+xl2D2qHPLTII1t4vSMNP2VKwDcn+Avf2WXvdB1A=
|
||||
github.com/go-gl/gl v0.0.0-20210905235341-f7a045908259/go.mod h1:wjpnOv6ONl2SuJSxqCPVaPZibGFdSci9HFocT9qtVYM=
|
||||
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=
|
||||
|
|
@ -109,8 +111,8 @@ github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPh
|
|||
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/glog v0.0.0-20210429001901-424d2337a529 h1:2voWjNECnrZRbfwXxHB1/j8wa6xdKn85B5NzgVL/pTU=
|
||||
github.com/golang/glog v0.0.0-20210429001901-424d2337a529/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/glog v1.0.0 h1:nfP3RFugxnNRyKgeWd4oI1nYvXpxrx8ck8ZrcizshdQ=
|
||||
github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4=
|
||||
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=
|
||||
|
|
@ -180,17 +182,16 @@ github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLe
|
|||
github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20210715191844-86eeefc3e471/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20210804190019-f964ff605595/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/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
github.com/google/uuid v1.3.0/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 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/googleapis/gax-go/v2 v2.1.0 h1:6DWmvNpomjL1+3liNSZbVns3zsYzzCjm6pRBO1tLeso=
|
||||
github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0=
|
||||
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
||||
|
|
@ -204,7 +205,6 @@ github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCV
|
|||
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
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 h1:6QPYqodiu3GuPL+7mfx+NwDdp2eTkp9IfEUpgAwUN0o=
|
||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
||||
|
|
@ -221,8 +221,9 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
|||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag=
|
||||
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mitchellh/mapstructure v1.4.2 h1:6h7AQ0yhTcIsmFmnAwQls75jp2Gzs4iB8W7pjMO+rqo=
|
||||
github.com/mitchellh/mapstructure v1.4.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
|
|
@ -237,8 +238,9 @@ github.com/onsi/ginkgo v1.16.1/go.mod h1:CObGmKUOKaSC0RjmoAK7tKyn4Azo5P2IWuoMnvw
|
|||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||
github.com/onsi/gomega v1.11.0/go.mod h1:azGKhqFUon9Vuj0YmTfLSmx0FUwqXYSTl5re8lQLTUg=
|
||||
github.com/pelletier/go-toml v1.9.3 h1:zeC5b1GviRUyKYd6OJPvBU/mcVDVoL1OhT17FCt5dSQ=
|
||||
github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
||||
github.com/pelletier/go-toml v1.9.4 h1:tjENF6MfZAg8e4ZmZTeWaWiT2vXtsoO6+iuOjFhECwM=
|
||||
github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
||||
github.com/pion/datachannel v1.4.21 h1:3ZvhNyfmxsAqltQrApLPQMhSFNA+aT87RqyCq4OXmf0=
|
||||
github.com/pion/datachannel v1.4.21/go.mod h1:oiNyP4gHx2DIwRzX/MFyH0Rz/Gz05OgBlayAI2hAWjg=
|
||||
github.com/pion/dtls/v2 v2.0.9 h1:7Ow+V++YSZQMYzggI0P9vLJz/hUFcffsfGMfT/Qy+u8=
|
||||
|
|
@ -247,21 +249,23 @@ github.com/pion/ice/v2 v2.1.10/go.mod h1:kV4EODVD5ux2z8XncbLHIOtcXKtYXVgLVCeVqnp
|
|||
github.com/pion/ice/v2 v2.1.12 h1:ZDBuZz+fEI7iDifZCYFVzI4p0Foy0YhdSSZ87ZtRcRE=
|
||||
github.com/pion/ice/v2 v2.1.12/go.mod h1:ovgYHUmwYLlRvcCLI67PnQ5YGe+upXZbGgllBDG/ktU=
|
||||
github.com/pion/interceptor v0.0.13/go.mod h1:svsW2QoLHLoGLUr4pDoSopGBEWk8FZwlfxId/OKRKzo=
|
||||
github.com/pion/interceptor v0.0.15 h1:pQFkBUL8akUHiGoFr+pM94Q/15x7sLFh0K3Nj+DCC6s=
|
||||
github.com/pion/interceptor v0.0.15/go.mod h1:pg3J253eGi5bqyKzA74+ej5Y19ez2jkWANVnF+Z9Dfk=
|
||||
github.com/pion/interceptor v0.0.19 h1:NkxrKHVH7ulrkVHTcZRJubgsF1oJeLQUvMsX1Kqm8to=
|
||||
github.com/pion/interceptor v0.0.19/go.mod h1:mv0Q0oPHxjRY8xz5v85G6aIqb1Tb0G0mxrZOaewHiVo=
|
||||
github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY=
|
||||
github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms=
|
||||
github.com/pion/mdns v0.0.5 h1:Q2oj/JB3NqfzY9xGZ1fPzZzK7sDSD8rZPOvcIQ10BCw=
|
||||
github.com/pion/mdns v0.0.5/go.mod h1:UgssrvdD3mxpi8tMxAXbsppL3vJ4Jipw1mTCW+al01g=
|
||||
github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA=
|
||||
github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8=
|
||||
github.com/pion/rtcp v1.2.6 h1:1zvwBbyd0TeEuuWftrd/4d++m+/kZSeiguxU61LFWpo=
|
||||
github.com/pion/rtcp v1.2.6/go.mod h1:52rMNPWFsjr39z9B9MhnkqhPLoeHTv1aN63o/42bWE0=
|
||||
github.com/pion/rtcp v1.2.7/go.mod h1:qVPhiCzAm4D/rxb6XzKeyZiQK69yJpbUDJSF7TgrqNo=
|
||||
github.com/pion/rtcp v1.2.8 h1:Cys8X6r0xxU65ESTmXkqr8eU1Q1Wx+lNkoZCUH4zD7E=
|
||||
github.com/pion/rtcp v1.2.8/go.mod h1:qVPhiCzAm4D/rxb6XzKeyZiQK69yJpbUDJSF7TgrqNo=
|
||||
github.com/pion/rtp v1.6.2/go.mod h1:bDb5n+BFZxXx0Ea7E5qe+klMuqiBrP+w8XSjiWtCUko=
|
||||
github.com/pion/rtp v1.6.5/go.mod h1:bDb5n+BFZxXx0Ea7E5qe+klMuqiBrP+w8XSjiWtCUko=
|
||||
github.com/pion/rtp v1.7.0/go.mod h1:bDb5n+BFZxXx0Ea7E5qe+klMuqiBrP+w8XSjiWtCUko=
|
||||
github.com/pion/rtp v1.7.1 h1:hCaxfVgPGt13eF/Tu9RhVn04c+dAcRZmhdDWqUE13oY=
|
||||
github.com/pion/rtp v1.7.1/go.mod h1:bDb5n+BFZxXx0Ea7E5qe+klMuqiBrP+w8XSjiWtCUko=
|
||||
github.com/pion/rtp v1.7.2 h1:HCDKDCixh7PVjkQTsqHAbk1lg+bx059EHxcnyl42dYs=
|
||||
github.com/pion/rtp v1.7.2/go.mod h1:bDb5n+BFZxXx0Ea7E5qe+klMuqiBrP+w8XSjiWtCUko=
|
||||
github.com/pion/sctp v1.7.10/go.mod h1:EhpTUQu1/lcK3xI+eriS6/96fWetHGCvBi9MSsnaBN0=
|
||||
github.com/pion/sctp v1.7.12 h1:GsatLufywVruXbZZT1CKg+Jr8ZTkwiPnmUC/oO9+uuY=
|
||||
github.com/pion/sctp v1.7.12/go.mod h1:xFe9cLMZ5Vj6eOzpyiKjT9SwGM4KpK/8Jbw5//jc+0s=
|
||||
|
|
@ -307,8 +311,8 @@ github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R
|
|||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
||||
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||
github.com/prometheus/procfs v0.7.2 h1:zE6zJjRS9S916ptrZ326OU0++1XRwHgxkvCFflxx6Fo=
|
||||
github.com/prometheus/procfs v0.7.2/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||
github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU=
|
||||
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||
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/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw=
|
||||
|
|
@ -327,8 +331,8 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5
|
|||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/veandco/go-sdl2 v0.4.8 h1:A26KeX6R1CGt/BQGEov6oxYmVGMMEWDVqTvK1tXvahE=
|
||||
github.com/veandco/go-sdl2 v0.4.8/go.mod h1:OROqMhHD43nT4/i9crJukyVecjPNYYuCofep6SNiAjY=
|
||||
github.com/veandco/go-sdl2 v0.4.10 h1:8QoD2bhWl7SbQDflIAUYWfl9Vq+mT8/boJFAUzAScgY=
|
||||
github.com/veandco/go-sdl2 v0.4.10/go.mod h1:OROqMhHD43nT4/i9crJukyVecjPNYYuCofep6SNiAjY=
|
||||
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=
|
||||
|
|
@ -350,8 +354,8 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U
|
|||
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-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
golang.org/x/crypto v0.0.0-20210813211128-0a44fdfbc16e h1:VvfwVmMH40bpMeizC9/K7ipM5Qjucuu16RWfneFPyhQ=
|
||||
golang.org/x/crypto v0.0.0-20210813211128-0a44fdfbc16e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20210915214749-c084706c2272 h1:3erb+vDS8lU1sxfDHF4/hhWyaXnhIaO+7RgL4fDZORA=
|
||||
golang.org/x/crypto v0.0.0-20210915214749-c084706c2272/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
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=
|
||||
|
|
@ -377,7 +381,6 @@ golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRu
|
|||
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 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug=
|
||||
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-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||
|
|
@ -390,8 +393,6 @@ 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.5.0 h1:UG21uOlmZabA4fW5i7ZX6bjw1xELEGg/ZLgZq9auk/Q=
|
||||
golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
||||
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-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
|
|
@ -437,8 +438,8 @@ golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qx
|
|||
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d h1:LO7XpTYMwTqxjLcGWPijK3vRXg1aWdlNOVOHRq45d7c=
|
||||
golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210916014120-12bc252f5db8 h1:/6y1LfuqNuQdHAm0jjtPtgRcxIxjVZgm5OTu8/QhZvk=
|
||||
golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
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=
|
||||
|
|
@ -451,11 +452,10 @@ golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ
|
|||
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-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210615190721-d04028783cf1/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210810183815-faf39c7919d5 h1:Ati8dO7+U7mxpkPSxBZQEvzHVUYB/MqCklCN8ig5w/o=
|
||||
golang.org/x/oauth2 v0.0.0-20210810183815-faf39c7919d5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f h1:Qmd2pbz05z7z6lm0DrgQVVPuBm92jqujBKMHMOlOQEw=
|
||||
golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/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=
|
||||
|
|
@ -524,8 +524,10 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/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-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210816074244-15123e1e1f71 h1:ikCpsnYR+Ew0vu99XlDp55lGgDJdIMx3f4a18jfse/s=
|
||||
golang.org/x/sys v0.0.0-20210816074244-15123e1e1f71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210915083310-ed5796bab164 h1:7ZDGnxgHAMw7thfC5bEos0RDAccZKxioiWBhfIe+tvw=
|
||||
golang.org/x/sys v0.0.0-20210915083310-ed5796bab164/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
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=
|
||||
|
|
@ -591,7 +593,6 @@ golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
|||
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.5 h1:ouewzE6p+/VEB31YYnTbEJdi8pFqKp4P4n85vwo3DHA=
|
||||
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
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=
|
||||
|
|
@ -621,12 +622,12 @@ google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBz
|
|||
google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94=
|
||||
google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo=
|
||||
google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4=
|
||||
google.golang.org/api v0.49.0/go.mod h1:BECiH72wsfwUvOVn3+btPD5WHi0LzavZReBndi42L18=
|
||||
google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw=
|
||||
google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU=
|
||||
google.golang.org/api v0.52.0/go.mod h1:Him/adpjt0sxtkWViy0b6xyKW/SD71CwdJ7HqJo7SrU=
|
||||
google.golang.org/api v0.54.0 h1:ECJUVngj71QI6XEm7b1sAf8BljU5inEhMbKPR8Lxhhk=
|
||||
google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k=
|
||||
google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE=
|
||||
google.golang.org/api v0.57.0 h1:4t9zuDlHLcIx0ZEhmXEeFVCRsiOgpgn2QOH9N0MNjPI=
|
||||
google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI=
|
||||
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=
|
||||
|
|
@ -679,18 +680,19 @@ google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQ
|
|||
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
|
||||
google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
|
||||
google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
|
||||
google.golang.org/genproto v0.0.0-20210617175327-b9e0b3197ced/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24=
|
||||
google.golang.org/genproto v0.0.0-20210624174822-c5cf32407d0a/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24=
|
||||
google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24=
|
||||
google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k=
|
||||
google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k=
|
||||
google.golang.org/genproto v0.0.0-20210721163202-f1cecdd8b78a/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
|
||||
google.golang.org/genproto v0.0.0-20210722135532-667f2b7c528f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
|
||||
google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
|
||||
google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
|
||||
google.golang.org/genproto v0.0.0-20210811021853-ddbe55d93216/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w=
|
||||
google.golang.org/genproto v0.0.0-20210816143620-e15ff196659d h1:fPtHPeysWvGVJwQFKu3B7H2DB2sOEsW7UTayKkWESKw=
|
||||
google.golang.org/genproto v0.0.0-20210816143620-e15ff196659d/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
|
||||
google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w=
|
||||
google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
|
||||
google.golang.org/genproto v0.0.0-20210825212027-de86158e7fda/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
|
||||
google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
|
||||
google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
|
||||
google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
|
||||
google.golang.org/genproto v0.0.0-20210916144049-3192f974c780 h1:RE6jTVCXBKZ7U9atSg8N3bsjRvvUujhEPspbEhdyy8s=
|
||||
google.golang.org/genproto v0.0.0-20210916144049-3192f974c780/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
|
||||
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=
|
||||
|
|
|
|||
6
pkg/config/storage/config.go
Normal file
6
pkg/config/storage/config.go
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
package storage
|
||||
|
||||
type Storage struct {
|
||||
Provider string
|
||||
Key string
|
||||
}
|
||||
|
|
@ -11,6 +11,7 @@ import (
|
|||
"github.com/giongto35/cloud-game/v2/pkg/config/encoder"
|
||||
"github.com/giongto35/cloud-game/v2/pkg/config/monitoring"
|
||||
"github.com/giongto35/cloud-game/v2/pkg/config/shared"
|
||||
"github.com/giongto35/cloud-game/v2/pkg/config/storage"
|
||||
webrtcConfig "github.com/giongto35/cloud-game/v2/pkg/config/webrtc"
|
||||
"github.com/giongto35/cloud-game/v2/pkg/environment"
|
||||
flag "github.com/spf13/pflag"
|
||||
|
|
@ -20,6 +21,7 @@ type Config struct {
|
|||
Encoder encoder.Encoder
|
||||
Emulator emulator.Emulator
|
||||
Environment shared.Environment
|
||||
Storage storage.Storage
|
||||
Worker Worker
|
||||
Webrtc webrtcConfig.Webrtc
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ package emulator
|
|||
|
||||
import "github.com/giongto35/cloud-game/v2/pkg/emulator/image"
|
||||
|
||||
// CloudEmulator is the interface of cloud emulator. Currently NES emulator and RetroArch implements this in codebase
|
||||
// CloudEmulator is the interface of cloud emulator.
|
||||
type CloudEmulator interface {
|
||||
// LoadMeta returns meta data of emulator. Refer below
|
||||
LoadMeta(path string) Metadata
|
||||
|
|
@ -12,8 +12,8 @@ type CloudEmulator interface {
|
|||
SetViewport(width int, height int)
|
||||
// GetViewport debug encoder image
|
||||
GetViewport() interface{}
|
||||
// SaveGame save game state, saveExtraFunc is callback to do extra step. Ex: save to google cloud
|
||||
SaveGame(saveExtraFunc func() error) error
|
||||
// SaveGame save game state
|
||||
SaveGame() error
|
||||
// LoadGame load game state
|
||||
LoadGame() error
|
||||
// GetHashPath returns the path emulator will save state to
|
||||
|
|
|
|||
|
|
@ -50,7 +50,6 @@ void coreLog_cgo(enum retro_log_level level, const char *msg);
|
|||
*/
|
||||
import "C"
|
||||
|
||||
// naEmulator implements CloudEmulator
|
||||
type naEmulator struct {
|
||||
sync.Mutex
|
||||
|
||||
|
|
@ -71,16 +70,6 @@ type naEmulator struct {
|
|||
done chan struct{}
|
||||
}
|
||||
|
||||
type Storage struct {
|
||||
// save path without the dir slash in the end
|
||||
Path string
|
||||
// contains the name of the main save file
|
||||
// e.g. abc<...>293.dat
|
||||
// needed for Google Cloud save/restore which
|
||||
// doesn't support multiple files
|
||||
MainSave string
|
||||
}
|
||||
|
||||
// VideoExporter produces image frame to unix socket
|
||||
type VideoExporter struct {
|
||||
sock net.Conn
|
||||
|
|
@ -190,7 +179,11 @@ func (na *naEmulator) SetViewport(width int, height int) {
|
|||
}
|
||||
|
||||
func (na *naEmulator) Start() {
|
||||
na.playGame()
|
||||
err := na.LoadGame()
|
||||
if err != nil {
|
||||
log.Printf("error: couldn't load a save, %v", err)
|
||||
}
|
||||
|
||||
ticker := time.NewTicker(time.Second / time.Duration(na.meta.Fps))
|
||||
|
||||
for range ticker.C {
|
||||
|
|
@ -211,23 +204,10 @@ func (na *naEmulator) Start() {
|
|||
}
|
||||
}
|
||||
|
||||
func (na *naEmulator) playGame() {
|
||||
// When start game, we also try loading if there was a saved state
|
||||
na.LoadGame()
|
||||
}
|
||||
|
||||
func (na *naEmulator) SaveGame(saveExtraFunc func() error) error {
|
||||
func (na *naEmulator) SaveGame() error {
|
||||
if na.roomID != "" {
|
||||
err := na.Save()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = saveExtraFunc()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return na.Save()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -235,11 +215,9 @@ func (na *naEmulator) LoadGame() error {
|
|||
if na.roomID != "" {
|
||||
err := na.Load()
|
||||
if err != nil {
|
||||
log.Println("Error: Cannot load", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -247,13 +225,12 @@ func (na *naEmulator) ToggleMultitap() error {
|
|||
if na.roomID != "" {
|
||||
toggleMultitap()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (na *naEmulator) GetHashPath() string { return na.storage.Path + "/" + na.storage.MainSave }
|
||||
func (na *naEmulator) GetHashPath() string { return na.storage.GetSavePath() }
|
||||
|
||||
func (na *naEmulator) GetSRAMPath() string { return na.storage.Path + "/" + na.roomID + ".srm" }
|
||||
func (na *naEmulator) GetSRAMPath() string { return na.storage.GetSRAMPath() }
|
||||
|
||||
func (*naEmulator) GetViewport() interface{} {
|
||||
return outputImg
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ func GetEmulatorMock(room string, system string) *EmulatorMock {
|
|||
|
||||
store := Storage{
|
||||
Path: os.TempDir(),
|
||||
MainSave: room + ".dat",
|
||||
MainSave: room,
|
||||
}
|
||||
|
||||
// an emu
|
||||
|
|
@ -134,6 +134,11 @@ func (emu *EmulatorMock) loadRom(game string) {
|
|||
fmt.Printf("%v %v\n", emu.paths.cores, emu.core)
|
||||
coreLoad(emulator.Metadata{LibPath: emu.paths.cores + emu.core})
|
||||
coreLoadGame(emu.paths.games + game)
|
||||
|
||||
if emu.canvas.Rect.Dx() == 0 || emu.canvas.Rect.Dy() == 0 {
|
||||
emu.canvas = image.NewRGBA(image.Rect(0, 0, emu.meta.BaseWidth, emu.meta.BaseHeight))
|
||||
outputImg = emu.canvas
|
||||
}
|
||||
}
|
||||
|
||||
// shutdownEmulator closes the emulator and cleans its resources.
|
||||
|
|
|
|||
16
pkg/emulator/libretro/nanoarch/storage.go
Normal file
16
pkg/emulator/libretro/nanoarch/storage.go
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
package nanoarch
|
||||
|
||||
import "path/filepath"
|
||||
|
||||
type Storage struct {
|
||||
// save path without the dir slash in the end
|
||||
Path string
|
||||
// contains the name of the main save file
|
||||
// e.g. abc<...>293.dat
|
||||
// needed for Google Cloud save/restore which
|
||||
// doesn't support multiple files
|
||||
MainSave string
|
||||
}
|
||||
|
||||
func (s *Storage) GetSavePath() string { return filepath.Join(s.Path, s.MainSave+".dat") }
|
||||
func (s *Storage) GetSRAMPath() string { return filepath.Join(s.Path, s.MainSave+".srm") }
|
||||
74
pkg/storage/google.go
Normal file
74
pkg/storage/google.go
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
package storage
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"cloud.google.com/go/storage"
|
||||
)
|
||||
|
||||
type GoogleCloudClient struct {
|
||||
bucket *storage.BucketHandle
|
||||
ctx context.Context
|
||||
}
|
||||
|
||||
// NewGoogleCloudClient returns a Google Cloud Storage client or nil if the client is not initialized.
|
||||
func NewGoogleCloudClient() (*GoogleCloudClient, error) {
|
||||
ctx := context.Background()
|
||||
client, err := storage.NewClient(ctx)
|
||||
if err != nil {
|
||||
log.Printf("warn: failed to create Google Cloud Storage client: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
bucket := client.Bucket("game-save")
|
||||
return &GoogleCloudClient{bucket: bucket}, nil
|
||||
}
|
||||
|
||||
// Save saves a file to GCS.
|
||||
func (c *GoogleCloudClient) Save(name string, srcFile string) (err error) {
|
||||
// Bypass if client is nil
|
||||
if c == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
reader, err := os.Open(srcFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
wc := c.bucket.Object(name).NewWriter(c.ctx)
|
||||
if _, err = io.Copy(wc, reader); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := wc.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Load loads file from GCS.
|
||||
func (c *GoogleCloudClient) Load(name string) (data []byte, err error) {
|
||||
// Bypass if client is nil
|
||||
if c == nil {
|
||||
return nil, errors.New("cloud storage was not initialized")
|
||||
}
|
||||
|
||||
rc, err := c.bucket.Object(name).NewReader(c.ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
_ = rc.Close()
|
||||
}()
|
||||
|
||||
data, err = ioutil.ReadAll(rc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
|
@ -8,7 +8,7 @@ import (
|
|||
)
|
||||
|
||||
func TestSaveGame(t *testing.T) {
|
||||
client := NewInitClient()
|
||||
client, _ := NewGoogleCloudClient()
|
||||
if client == nil {
|
||||
t.Skip("Cloud storage is not initialized")
|
||||
}
|
||||
|
|
@ -18,17 +18,19 @@ func TestSaveGame(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Errorf("Temp dir is not accessable %v", err)
|
||||
}
|
||||
defer os.Remove(file.Name())
|
||||
defer func(name string) {
|
||||
err = os.Remove(name)
|
||||
}(file.Name())
|
||||
|
||||
if err = ioutil.WriteFile(file.Name(), data, 0644); err != nil {
|
||||
t.Errorf("File is not writable %v", err)
|
||||
}
|
||||
|
||||
err = client.SaveFile("Test", file.Name())
|
||||
err = client.Save("Test", file.Name())
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
loadData, err := client.LoadFile("Test")
|
||||
loadData, err := client.Load("Test")
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
19
pkg/storage/noop.go
Normal file
19
pkg/storage/noop.go
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
package storage
|
||||
|
||||
import "errors"
|
||||
|
||||
type NoopCloudStorage struct{}
|
||||
|
||||
var noopErr = errors.New("an empty storage stub")
|
||||
|
||||
func NewNoopCloudStorage() (*NoopCloudStorage, error) {
|
||||
return nil, noopErr
|
||||
}
|
||||
|
||||
func (n *NoopCloudStorage) Save(_ string, _ string) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *NoopCloudStorage) Load(_ string) (data []byte, err error) {
|
||||
return nil, noopErr
|
||||
}
|
||||
107
pkg/storage/oracle.go
Normal file
107
pkg/storage/oracle.go
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
package storage
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/md5"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
type OracleDataStorageClient struct {
|
||||
accessURL string
|
||||
client *http.Client
|
||||
}
|
||||
|
||||
// NewOracleDataStorageClient returns either a new Oracle Data Storage
|
||||
// client or some error in case of failure.
|
||||
// Oracle infrastructure access is based on pre-authenticated requests,
|
||||
// see: https://docs.oracle.com/en-us/iaas/Content/Object/Tasks/usingpreauthenticatedrequests.htm
|
||||
//
|
||||
// It follows broken Google Cloud Storage client design.
|
||||
func NewOracleDataStorageClient(accessURL string) (*OracleDataStorageClient, error) {
|
||||
if accessURL == "" {
|
||||
return nil, errors.New("pre-authenticated request was not specified")
|
||||
}
|
||||
return &OracleDataStorageClient{
|
||||
accessURL: accessURL,
|
||||
client: &http.Client{
|
||||
Timeout: 10 * time.Second,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *OracleDataStorageClient) Save(name string, localPath string) (err error) {
|
||||
if s == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
dat, err := ioutil.ReadFile(localPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("PUT", s.accessURL+name, bytes.NewBuffer(dat))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
resp, err := s.client.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
_ = resp.Body.Close()
|
||||
}()
|
||||
if resp.StatusCode != 200 {
|
||||
return errors.New(resp.Status)
|
||||
}
|
||||
|
||||
dstMD5 := resp.Header.Get("Opc-Content-Md5")
|
||||
srcMD5 := base64.StdEncoding.EncodeToString(md5Hash(dat))
|
||||
if dstMD5 != srcMD5 {
|
||||
return fmt.Errorf("MD5 mismatch %v != %v", srcMD5, dstMD5)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *OracleDataStorageClient) Load(name string) (data []byte, err error) {
|
||||
if s == nil {
|
||||
return nil, errors.New("cloud storage was not initialized")
|
||||
}
|
||||
|
||||
res, err := s.client.Get(s.accessURL + name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
_ = res.Body.Close()
|
||||
}()
|
||||
|
||||
if res.StatusCode != 200 {
|
||||
return nil, errors.New(res.Status)
|
||||
}
|
||||
|
||||
dat, err := ioutil.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dstMD5 := res.Header.Get("Content-Md5")
|
||||
srcMD5 := base64.StdEncoding.EncodeToString(md5Hash(dat))
|
||||
if dstMD5 != srcMD5 {
|
||||
return nil, fmt.Errorf("MD5 mismatch %v != %v", srcMD5, dstMD5)
|
||||
}
|
||||
|
||||
return dat, nil
|
||||
}
|
||||
|
||||
func md5Hash(data []byte) []byte {
|
||||
hash := md5.New()
|
||||
hash.Write(data)
|
||||
return hash.Sum(nil)
|
||||
}
|
||||
54
pkg/storage/oracle_test.go
Normal file
54
pkg/storage/oracle_test.go
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
package storage
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type rtFunc func(req *http.Request) *http.Response
|
||||
|
||||
func (f rtFunc) RoundTrip(req *http.Request) (*http.Response, error) { return f(req), nil }
|
||||
|
||||
func newTestClient(fn rtFunc) *http.Client {
|
||||
return &http.Client{
|
||||
Transport: fn,
|
||||
}
|
||||
}
|
||||
|
||||
func TestOracleSave(t *testing.T) {
|
||||
client, err := NewOracleDataStorageClient("test-url/")
|
||||
client.client = newTestClient(func(req *http.Request) *http.Response {
|
||||
return &http.Response{
|
||||
StatusCode: 200,
|
||||
Body: ioutil.NopCloser(strings.NewReader("")),
|
||||
Header: map[string][]string{
|
||||
"Opc-Content-Md5": {"CY9rzUYh03PK3k6DJie09g=="},
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
tempFile, err := ioutil.TempFile("", "oracle_test.file")
|
||||
if err != nil {
|
||||
t.Errorf("%v", err)
|
||||
}
|
||||
defer func() {
|
||||
_ = tempFile.Close()
|
||||
err := os.Remove(tempFile.Name())
|
||||
if err != nil {
|
||||
t.Errorf("%v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
_, err = tempFile.WriteString("test")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = client.Save("oracle_test.file", tempFile.Name())
|
||||
if err != nil {
|
||||
t.Errorf("can't save, err: %v", err)
|
||||
}
|
||||
}
|
||||
6
pkg/storage/storage.go
Normal file
6
pkg/storage/storage.go
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
package storage
|
||||
|
||||
type CloudStorage interface {
|
||||
Save(name string, localPath string) (err error)
|
||||
Load(name string) (data []byte, err error)
|
||||
}
|
||||
|
|
@ -3,11 +3,11 @@ package webrtc
|
|||
import (
|
||||
conf "github.com/giongto35/cloud-game/v2/pkg/config/webrtc"
|
||||
"github.com/pion/interceptor"
|
||||
. "github.com/pion/webrtc/v3"
|
||||
pion "github.com/pion/webrtc/v3"
|
||||
)
|
||||
|
||||
func NewInterceptedPeerConnection(conf conf.Webrtc, ics []interceptor.Interceptor, vCodec string) (*PeerConnection, error) {
|
||||
m := &MediaEngine{}
|
||||
func NewInterceptedPeerConnection(conf conf.Webrtc, ics []interceptor.Interceptor, vCodec string) (*pion.PeerConnection, error) {
|
||||
m := &pion.MediaEngine{}
|
||||
//if err := m.RegisterDefaultCodecs(); err != nil {
|
||||
// return nil, err
|
||||
//}
|
||||
|
|
@ -18,7 +18,7 @@ func NewInterceptedPeerConnection(conf conf.Webrtc, ics []interceptor.Intercepto
|
|||
|
||||
i := &interceptor.Registry{}
|
||||
if !conf.DisableDefaultInterceptors {
|
||||
if err := RegisterDefaultInterceptors(m, i); err != nil {
|
||||
if err := pion.RegisterDefaultInterceptors(m, i); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
|
@ -26,79 +26,83 @@ func NewInterceptedPeerConnection(conf conf.Webrtc, ics []interceptor.Intercepto
|
|||
i.Add(itc)
|
||||
}
|
||||
|
||||
settingEngine := SettingEngine{}
|
||||
settingEngine := pion.SettingEngine{}
|
||||
if conf.IcePorts.Min > 0 && conf.IcePorts.Max > 0 {
|
||||
if err := settingEngine.SetEphemeralUDPPortRange(conf.IcePorts.Min, conf.IcePorts.Max); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if conf.IceIpMap != "" {
|
||||
settingEngine.SetNAT1To1IPs([]string{conf.IceIpMap}, ICECandidateTypeHost)
|
||||
settingEngine.SetNAT1To1IPs([]string{conf.IceIpMap}, pion.ICECandidateTypeHost)
|
||||
}
|
||||
|
||||
peerConf := Configuration{ICEServers: []ICEServer{}}
|
||||
peerConf := pion.Configuration{ICEServers: []pion.ICEServer{}}
|
||||
for _, server := range conf.IceServers {
|
||||
peerConf.ICEServers = append(peerConf.ICEServers, ICEServer{
|
||||
peerConf.ICEServers = append(peerConf.ICEServers, pion.ICEServer{
|
||||
URLs: []string{server.Url},
|
||||
Username: server.Username,
|
||||
Credential: server.Credential,
|
||||
})
|
||||
}
|
||||
|
||||
api := NewAPI(WithMediaEngine(m), WithInterceptorRegistry(i), WithSettingEngine(settingEngine))
|
||||
api := pion.NewAPI(
|
||||
pion.WithMediaEngine(m),
|
||||
pion.WithInterceptorRegistry(i),
|
||||
pion.WithSettingEngine(settingEngine),
|
||||
)
|
||||
return api.NewPeerConnection(peerConf)
|
||||
}
|
||||
|
||||
// RegisterCodecs registers the default codecs supported by WebRTC.
|
||||
func RegisterCodecs(m *MediaEngine, vCodec string) error {
|
||||
audioRTPCodecParameters := []RTPCodecParameters{
|
||||
func RegisterCodecs(m *pion.MediaEngine, vCodec string) error {
|
||||
audioRTPCodecParameters := []pion.RTPCodecParameters{
|
||||
{
|
||||
RTPCodecCapability: RTPCodecCapability{MimeType: MimeTypeOpus, ClockRate: 48000, Channels: 2},
|
||||
RTPCodecCapability: pion.RTPCodecCapability{MimeType: pion.MimeTypeOpus, ClockRate: 48000, Channels: 2},
|
||||
PayloadType: 111,
|
||||
},
|
||||
}
|
||||
for _, codec := range audioRTPCodecParameters {
|
||||
if err := m.RegisterCodec(codec, RTPCodecTypeAudio); err != nil {
|
||||
if err := m.RegisterCodec(codec, pion.RTPCodecTypeAudio); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
videoRTCPFeedback := []RTCPFeedback{
|
||||
videoRTCPFeedback := []pion.RTCPFeedback{
|
||||
{"goog-remb", ""},
|
||||
{"ccm", "fir"},
|
||||
{"nack", ""},
|
||||
{"nack", "pli"},
|
||||
}
|
||||
video := RTPCodecCapability{MimeType: vCodec, ClockRate: 90000, RTCPFeedback: videoRTCPFeedback}
|
||||
var videoRTPCodecParameters []RTPCodecParameters
|
||||
if vCodec == MimeTypeH264 {
|
||||
videoRTPCodecParameters = []RTPCodecParameters{
|
||||
{RTPCodecCapability: RTPCodecCapability{
|
||||
video := pion.RTPCodecCapability{MimeType: vCodec, ClockRate: 90000, RTCPFeedback: videoRTCPFeedback}
|
||||
var videoRTPCodecParameters []pion.RTPCodecParameters
|
||||
if vCodec == pion.MimeTypeH264 {
|
||||
videoRTPCodecParameters = []pion.RTPCodecParameters{
|
||||
{RTPCodecCapability: pion.RTPCodecCapability{
|
||||
MimeType: video.MimeType, ClockRate: video.ClockRate, RTCPFeedback: video.RTCPFeedback,
|
||||
//SDPFmtpLine: "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f",
|
||||
}, PayloadType: 102},
|
||||
{RTPCodecCapability: RTPCodecCapability{
|
||||
{RTPCodecCapability: pion.RTPCodecCapability{
|
||||
MimeType: video.MimeType, ClockRate: video.ClockRate, RTCPFeedback: video.RTCPFeedback,
|
||||
SDPFmtpLine: "level-asymmetry-allowed=1;profile-level-id=42e01f",
|
||||
}, PayloadType: 108},
|
||||
{RTPCodecCapability: video, PayloadType: 123},
|
||||
{RTPCodecCapability: RTPCodecCapability{
|
||||
{RTPCodecCapability: pion.RTPCodecCapability{
|
||||
MimeType: video.MimeType, ClockRate: video.ClockRate, RTCPFeedback: video.RTCPFeedback,
|
||||
SDPFmtpLine: "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f",
|
||||
}, PayloadType: 125},
|
||||
{RTPCodecCapability: RTPCodecCapability{
|
||||
{RTPCodecCapability: pion.RTPCodecCapability{
|
||||
MimeType: video.MimeType, ClockRate: video.ClockRate, RTCPFeedback: video.RTCPFeedback,
|
||||
SDPFmtpLine: "level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42001f",
|
||||
}, PayloadType: 127},
|
||||
}
|
||||
} else {
|
||||
videoRTPCodecParameters = []RTPCodecParameters{
|
||||
videoRTPCodecParameters = []pion.RTPCodecParameters{
|
||||
{RTPCodecCapability: video, PayloadType: 96},
|
||||
}
|
||||
}
|
||||
|
||||
for _, codec := range videoRTPCodecParameters {
|
||||
if err := m.RegisterCodec(codec, RTPCodecTypeVideo); err != nil {
|
||||
if err := m.RegisterCodec(codec, pion.RTPCodecTypeVideo); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,97 +0,0 @@
|
|||
package storage
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"cloud.google.com/go/storage"
|
||||
)
|
||||
|
||||
// TODO: Add interface, abstract out Gstorage
|
||||
type Client struct {
|
||||
bucket *storage.BucketHandle
|
||||
gclient *storage.Client
|
||||
}
|
||||
|
||||
// NewInitClient returns nil of client is not initialized
|
||||
func NewInitClient() *Client {
|
||||
bucketName := "game-save"
|
||||
|
||||
client, err := NewClient(bucketName)
|
||||
if err != nil {
|
||||
log.Printf("Warn: Failed to create client: %v", err)
|
||||
} else {
|
||||
log.Println("Online storage is initialized")
|
||||
}
|
||||
|
||||
return client
|
||||
}
|
||||
|
||||
// NewClient inits a new Client accessing to GCP
|
||||
func NewClient(bucketName string) (*Client, error) {
|
||||
ctx := context.Background()
|
||||
|
||||
// Sets your Google Cloud Platform project ID.
|
||||
|
||||
// Creates a client.
|
||||
gclient, err := storage.NewClient(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Creates a Bucket instance.
|
||||
bucket := gclient.Bucket(bucketName)
|
||||
|
||||
return &Client{
|
||||
bucket: bucket,
|
||||
gclient: gclient,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Savefile save srcFile to GCP
|
||||
func (c *Client) SaveFile(name string, srcFile string) (err error) {
|
||||
// Bypass if client is nil
|
||||
if c == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
reader, err := os.Open(srcFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Copy source file to GCP
|
||||
wc := c.bucket.Object(name).NewWriter(context.Background())
|
||||
if _, err = io.Copy(wc, reader); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := wc.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Loadfile loads file from GCP
|
||||
func (c *Client) LoadFile(name string) (data []byte, err error) {
|
||||
// Bypass if client is nil
|
||||
if c == nil {
|
||||
return nil, errors.New("cloud storage was not initialized")
|
||||
}
|
||||
|
||||
rc, err := c.bucket.Object(name).NewReader(context.Background())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rc.Close()
|
||||
|
||||
data, err = ioutil.ReadAll(rc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
|
@ -15,8 +15,8 @@ import (
|
|||
"github.com/giongto35/cloud-game/v2/pkg/games"
|
||||
"github.com/giongto35/cloud-game/v2/pkg/network/websocket"
|
||||
"github.com/giongto35/cloud-game/v2/pkg/service"
|
||||
"github.com/giongto35/cloud-game/v2/pkg/storage"
|
||||
"github.com/giongto35/cloud-game/v2/pkg/webrtc"
|
||||
storage "github.com/giongto35/cloud-game/v2/pkg/worker/cloud-storage"
|
||||
"github.com/giongto35/cloud-game/v2/pkg/worker/room"
|
||||
)
|
||||
|
||||
|
|
@ -32,17 +32,14 @@ type Handler struct {
|
|||
// global ID of the current server
|
||||
serverID string
|
||||
// onlineStorage is client accessing to online storage (GCP)
|
||||
onlineStorage *storage.Client
|
||||
onlineStorage storage.CloudStorage
|
||||
// sessions handles all sessions server is handler (key is sessionID)
|
||||
sessions map[string]*Session
|
||||
}
|
||||
|
||||
// NewHandler returns a new server
|
||||
func NewHandler(conf worker.Config, address string) *Handler {
|
||||
// Create offline storage folder
|
||||
createOfflineStorage(conf.Emulator.Storage)
|
||||
// Init online storage
|
||||
onlineStorage := storage.NewInitClient()
|
||||
onlineStorage := initCloudStorage(conf)
|
||||
return &Handler{
|
||||
address: address,
|
||||
cfg: conf,
|
||||
|
|
@ -92,6 +89,25 @@ func (h *Handler) Prepare() {
|
|||
}
|
||||
}
|
||||
|
||||
func initCloudStorage(conf worker.Config) storage.CloudStorage {
|
||||
var st storage.CloudStorage
|
||||
var err error
|
||||
switch conf.Storage.Provider {
|
||||
case "google":
|
||||
st, err = storage.NewGoogleCloudClient()
|
||||
case "oracle":
|
||||
st, err = storage.NewOracleDataStorageClient(conf.Storage.Key)
|
||||
case "coordinator":
|
||||
default:
|
||||
st, _ = storage.NewNoopCloudStorage()
|
||||
}
|
||||
if err != nil {
|
||||
log.Printf("Switching to noop cloud save")
|
||||
st, _ = storage.NewNoopCloudStorage()
|
||||
}
|
||||
return st
|
||||
}
|
||||
|
||||
func newCoordinatorConnection(host string, conf worker.Worker, addr string) (*CoordinatorClient, error) {
|
||||
scheme := "ws"
|
||||
if conf.Network.Secure {
|
||||
|
|
|
|||
|
|
@ -160,7 +160,7 @@ func (h *Handler) handleGameSave() cws.PacketHandler {
|
|||
}
|
||||
err := room.SaveGame()
|
||||
if err != nil {
|
||||
log.Println("[!] Cannot save game state: ", err)
|
||||
log.Printf("error, cannot save game: %v", err)
|
||||
req.Data = "error"
|
||||
}
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package room
|
|||
import (
|
||||
"bytes"
|
||||
"encoding/gob"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
|
|
@ -19,8 +20,8 @@ import (
|
|||
"github.com/giongto35/cloud-game/v2/pkg/encoder"
|
||||
"github.com/giongto35/cloud-game/v2/pkg/games"
|
||||
"github.com/giongto35/cloud-game/v2/pkg/session"
|
||||
"github.com/giongto35/cloud-game/v2/pkg/storage"
|
||||
"github.com/giongto35/cloud-game/v2/pkg/webrtc"
|
||||
storage "github.com/giongto35/cloud-game/v2/pkg/worker/cloud-storage"
|
||||
)
|
||||
|
||||
// Room is a game session. multi webRTC sessions can connect to a same game.
|
||||
|
|
@ -51,7 +52,7 @@ type Room struct {
|
|||
// Director is emulator
|
||||
director emulator.CloudEmulator
|
||||
// Cloud storage to store room state online
|
||||
onlineStorage *storage.Client
|
||||
onlineStorage storage.CloudStorage
|
||||
|
||||
vPipe *encoder.VideoPipe
|
||||
}
|
||||
|
|
@ -119,7 +120,7 @@ func NewVideoImporter(roomID string) chan nanoarch.GameFrame {
|
|||
}
|
||||
|
||||
// NewRoom creates a new room
|
||||
func NewRoom(roomID string, game games.GameMetadata, onlineStorage *storage.Client, cfg worker.Config) *Room {
|
||||
func NewRoom(roomID string, game games.GameMetadata, onlineStorage storage.CloudStorage, cfg worker.Config) *Room {
|
||||
if roomID == "" {
|
||||
roomID = session.GenerateRoomID(game.Name)
|
||||
}
|
||||
|
|
@ -146,13 +147,13 @@ func NewRoom(roomID string, game games.GameMetadata, onlineStorage *storage.Clie
|
|||
go func(game games.GameMetadata, roomID string) {
|
||||
store := nanoarch.Storage{
|
||||
Path: cfg.Emulator.Storage,
|
||||
MainSave: roomID + ".dat",
|
||||
MainSave: roomID,
|
||||
}
|
||||
|
||||
// Check room is on local or fetch from server
|
||||
log.Printf("Check %s on online storage: %v", roomID, isGameOnLocal(store.MainSave))
|
||||
if err := room.saveOnlineRoomToLocal(roomID, store.MainSave); err != nil {
|
||||
log.Printf("Warn: Room %s is not in online storage, error %s", roomID, err)
|
||||
log.Printf("Check for %s in the online storage", roomID)
|
||||
if err := room.saveOnlineRoomToLocal(roomID, store.GetSavePath()); err != nil {
|
||||
log.Printf("warn: room %s is not in the online storage, error %s", roomID, err)
|
||||
}
|
||||
|
||||
// If not then load room or create room from local.
|
||||
|
|
@ -227,8 +228,13 @@ func resizeToAspect(ratio float64, sw int, sh int) (dw int, dh int) {
|
|||
}
|
||||
|
||||
func isGameOnLocal(path string) bool {
|
||||
_, err := os.Open(path)
|
||||
return err == nil
|
||||
file, err := os.Open(path)
|
||||
if err == nil {
|
||||
defer func() {
|
||||
_ = file.Close()
|
||||
}()
|
||||
}
|
||||
return !errors.Is(err, os.ErrNotExist)
|
||||
}
|
||||
|
||||
func (r *Room) AddConnectionToRoom(peerconnection *webrtc.WebRTC) {
|
||||
|
|
@ -353,45 +359,38 @@ func (r *Room) Close() {
|
|||
|
||||
func (r *Room) isRoomExisted() bool {
|
||||
// Check if room is in online storage
|
||||
_, err := r.onlineStorage.LoadFile(r.ID)
|
||||
_, err := r.onlineStorage.Load(r.ID)
|
||||
if err == nil {
|
||||
return true
|
||||
}
|
||||
return isGameOnLocal(r.director.GetHashPath())
|
||||
}
|
||||
|
||||
// SaveGame will save game to local and trigger a callback to store game on onlineStorage, so the game can be accessed later
|
||||
// SaveGame writes save state on the disk as well as
|
||||
// uploads it to a cloud storage.
|
||||
func (r *Room) SaveGame() error {
|
||||
onlineSaveFunc := func() error {
|
||||
// Try to save the game to gCloud
|
||||
if err := r.onlineStorage.SaveFile(r.ID, r.director.GetHashPath()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// TODO: Move to game view
|
||||
if err := r.director.SaveGame(onlineSaveFunc); err != nil {
|
||||
if err := r.director.SaveGame(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := r.onlineStorage.Save(r.ID, r.director.GetHashPath()); err != nil {
|
||||
return err
|
||||
}
|
||||
log.Printf("success, cloud save")
|
||||
return nil
|
||||
}
|
||||
|
||||
// saveOnlineRoomToLocal save online room to local.
|
||||
// !Supports only one file of main save state.
|
||||
func (r *Room) saveOnlineRoomToLocal(roomID string, savePath string) error {
|
||||
log.Println("Check if game is on cloud storage")
|
||||
// If the game is not on local server
|
||||
// Try to load from gcloud
|
||||
data, err := r.onlineStorage.LoadFile(roomID)
|
||||
data, err := r.onlineStorage.Load(roomID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Save the data fetched from gcloud to local server
|
||||
// Save the data fetched from a cloud provider to the local server
|
||||
if data != nil {
|
||||
_ = ioutil.WriteFile(savePath, data, 0644)
|
||||
err = ioutil.WriteFile(savePath, data, 0644)
|
||||
log.Printf("successfully downloaded cloud save")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,8 +23,8 @@ import (
|
|||
"github.com/giongto35/cloud-game/v2/pkg/emulator/libretro/manager/remotehttp"
|
||||
"github.com/giongto35/cloud-game/v2/pkg/encoder"
|
||||
"github.com/giongto35/cloud-game/v2/pkg/games"
|
||||
"github.com/giongto35/cloud-game/v2/pkg/storage"
|
||||
"github.com/giongto35/cloud-game/v2/pkg/thread"
|
||||
storage "github.com/giongto35/cloud-game/v2/pkg/worker/cloud-storage"
|
||||
"golang.org/x/image/font"
|
||||
"golang.org/x/image/font/basicfont"
|
||||
"golang.org/x/image/math/fixed"
|
||||
|
|
@ -238,7 +238,8 @@ func getRoomMock(cfg roomMockConfig) roomMock {
|
|||
}
|
||||
conf.Encoder.Video.Codec = string(cfg.vCodec)
|
||||
|
||||
room := NewRoom(cfg.roomName, cfg.game, storage.NewInitClient(), conf)
|
||||
cloudStore, _ := storage.NewNoopCloudStorage()
|
||||
room := NewRoom(cfg.roomName, cfg.game, cloudStore, conf)
|
||||
|
||||
// loop-wait the room initialization
|
||||
var init sync.WaitGroup
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue