Code Monkey home page Code Monkey logo

cloud-game's Introduction

Anurag's GitHub stats

cloud-game's People

Contributors

0xflotus avatar 88hcsif avatar bbalint105 avatar carousell-bot avatar giongto35 avatar himajpatil avatar matsuyoshi30 avatar sadlil avatar selvan avatar sergystepanov avatar testwill avatar tianrenz2 avatar trichimtrich avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

cloud-game's Issues

External config

Would be great to have some kind of external configuration file to read in runtime.

not work, cannot start game

I1121 21:00:01.783005 50608 room.go:97] Warn: Room 47b72ee81a4a936d___anguna is not in online storage, error Get https://storage.googleapis.com/game-save/47b72ee81a4a936d___anguna: Get http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/token?scopes=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fdevstorage.full_control: dial tcp 169.254.169.254:80: connect: host is down
I1121 21:00:01.783033 50608 room.go:101] Room 47b72ee81a4a936d___anguna started. GamePath: assets/games/anguna.gba, GameName: anguna
I1121 21:00:01.784038 50608 nanoarch.go:240] error loading assets/emulator/libretro/cores/mgba_libretro.so, err 100
make: *** [dev.run] Error 1
I1121 21:00:01.787214 50607 cws.go:156] [!] read: websocket: close 1006 (abnormal closure): unexpected EOF
I1121 21:00:01.787248 50607 handlers.go:327] Unregister server from overlord

It seems that 169.254.169.254:80 is down.

Improve performance

Expectation:
ping 10 in the same country (Singapore - Singapore host) (USWest - Seattle/ Los Angeles)
Ping 50 to the other nearby country (Singapore - Vietnam)

it crash when use libretro

when i used libretro about ps2 core, it crashed. i even doubt it is a core problem, but i tried the same core in retroarch, i workd without crash. the crash program is overworker, it may crash from cgo think.
golang output is at pkg/emulator/libretro/nanoarch/nanoarch.go:367. it says receive a unreconginze signal.

c++ output is the following sentence:
#0 0x00007fffe25bca87 in CGSHandler::WriteRegisterMassively(std::vector<std::pair<unsigned char, unsigned long long>, std::allocator<std::pair<unsigned char, unsigned long long> > >, CGsPacketMetadata const*) () from assets/emulator/libretro/cores/play_libretro.so
#1 0x00007fffe2588b4a in CGIF::ProcessSinglePacket(unsigned char const*, unsigned int, unsigned int, CGsPacketMetadata const&) ()
from assets/emulator/libretro/cores/play_libretro.so
#2 0x00007fffe2588ed8 in CGIF::ProcessMultiplePackets(unsigned char const*, unsigned int, unsigned int, CGsPacketMetadata const&) ()
from assets/emulator/libretro/cores/play_libretro.so
#3 0x00007fffe2588f49 in CGIF::ReceiveDMA(unsigned int, unsigned int, unsigned int, bool) ()
from assets/emulator/libretro/cores/play_libretro.so
#4 0x00007fffe2606d10 in Dmac::CChannel::ExecuteNormal() () from assets/emulator/libretro/cores/play_libretro.so
#5 0x00007fffe2603422 in CDMAC::SetRegister(unsigned int, unsigned int) () from assets/emulator/libretro/cores/play_libretro.so
#6 0x00007fffe257f5d0 in Ee::CSubSystem::IOPortWriteHandler(unsigned int, unsigned int) ()
from assets/emulator/libretro/cores/play_libretro.so
#7 0x00007fffe25e12d5 in CMemoryMap_LSBF::SetWord(unsigned int, unsigned int) () from assets/emulator/libretro/cores/play_libretro.so

Experience with 4k resolution

Try scaling game output to 4k resolution and experience the latency. It's good to push the limit and see how much the infrastructure can provide.

Self-update, Libretro cores downloader / updater

I was wondering whether it'd be useful to have libretro core downloader / updater inside overworkers based on sys.GOOS rather than using manual assets and hard coded dynamic lib names (so, dll, ...):

  1. Remove all the cores from the repo assets
  2. Download static worker binary
  3. Either auto-download needed cores during the first run or exec worker -download-cores
  4. Download cores based on the current system by sys.GOOS -> kinda http://buildbot.libretro.com/nightly/{OS}/x86_64/latest/
  5. Load cores with the extension based on sys.GOOS
    4.5 Possible auto-updater: worker -update-cores
  6. Profit

Improve load balance to consider memory resource

The current implementation is only based on latency from user to each worker only. Each time a user connects to the master server (overlord), overlord will return all the addresses of workers and ask the user to fetch all latency to them. This latency check in the browser is conducted by an HTTP request to worker through /echo/ endpoint.

Overlord logic to return addresses list:
https://github.com/giongto35/cloud-game/blob/master/overlord/handlers.go#L201

Client logic to check all latency
https://github.com/giongto35/cloud-game/blob/master/static/js/ws.js#L88


The current implementation lacks:

  1. Check the if memory resource of worker is still higher than some threshold (or number of GoRoutine doesn't reach some limit), to assign game session to it.
  2. Among all valid servers ( <= 100ms ping and have enough memory resource), we do round robin assignment.

Data race at gameview keypressed

2 GoRoutines spawn in the same time

ListenToInputChannel writes keyPressed
Update reads keyPressed


3 approaches:

  1. Add lock to keyPressed (seems overkill and not natural)
  2. In update loop, select input event. Exetremely slow because of frequently channel polling.
  3. Let it be

Collaborative gameplay

Is it possible for two people to play in the same room? If so, I would like to request, and possibly help implement this feature. I imagine that the inputs from both clients would be sent such that a single game is controlled by both.
Thank you!

[GBA] mGBA Pokemon Gold overworker crash on exit

Pokemon Gold (Pokemon - Gold Version (UE) [C][!].gbc) crashes with segfault on the latests mgba core when you click "quit" button on the site.
Maybe it is worth to research (:/ further.

log
I1008 20:35:15.186391    5468 browser.go:58] Overlord: Received quit request from a browser
I1008 20:35:15.186391    5468 browser.go:59] Overlord: Relay quit request from a browser to worker
I1008 20:35:15.186391   11716 overlord.go:119] Received a quit request from overlord
I1008 20:35:15.187390   11716 overlord.go:121] Find  b55674e8-fedb-412a-b63e-53093c53d9ce &{ 0xc0003961c0 17df6e2970cb3626|Pokemon - Gold Version (UE) [C][!]} true
I1008 20:35:15.187390   11716 handlers.go:96] Detach peerconnection
I1008 20:35:15.187390   11716 room.go:195] Cleaning session:  8ba84b50-23ae-48ea-bc44-a93408eb3e22
I1008 20:35:15.187390   11716 room.go:198] found session:  8ba84b50-23ae-48ea-bc44-a93408eb3e22
I1008 20:35:15.187390   11716 room.go:201] Removed session  8ba84b50-23ae-48ea-bc44-a93408eb3e22  from room:  17df6e2970cb3626|Pokemon - Gold Version (UE) [C][!]
I1008 20:35:15.187390   11716 handlers.go:106] No session in room
I1008 20:35:15.187390   11716 room.go:226] Closing room 17df6e2970cb3626|Pokemon - Gold Version (UE) [C][!]
I1008 20:35:15.187390   11716 room.go:227] Closing director of room  17df6e2970cb3626|Pokemon - Gold Version (UE) [C][!]
I1008 20:35:15.187390   11716 room.go:229] Closing input of room  17df6e2970cb3626|Pokemon - Gold Version (UE) [C][!]
I1008 20:35:15.187390   11716 handlers.go:109] Signal input chan
I1008 20:35:15.187390   11716 room.go:190] Peerconn done
Exception 0xc0000005 0x8 0x0 0x0
PC=0x0

runtime: unknown pc 0x6daf9384
stack: frame={sp:0x4fffda0, fp:0x0} stack=[0x0,0x4fffe80)
0000000004fffca0:  0000000002030000  000000c00035c000
0000000004fffcb0:  00000000000000c0  0000000000000008
0000000004fffcc0:  00000000032bd8a0  00007ffe2857fc11
0000000004fffcd0:  0000000002030000  000000c00035c000
0000000004fffce0:  0000000000000006  bf5f5019cb2b5b50
0000000004fffcf0:  00000000032bdb10  00007ffe2857fc11
0000000004fffd00:  000000010000001d  00000000032b0000
0000000004fffd10:  000000c000289d08  0000000000000000
0000000004fffd20:  0000000000000000  0000000000b97848
0000000004fffd30:  000000c000289d48  00007ffe28239cfc
0000000004fffd40:  00000000032a0000  000000c00034e180
0000000004fffd50:  00000000000000a0  0000000000000001
0000000004fffd60:  00000000032a0000  000000006db4d719
0000000004fffd70:  000000c00034e180  000000c000289d48
0000000004fffd80:  000000c000289d08  000000006db45a3b
0000000004fffd90:  0000000000000006  000000006daf9384
0000000004fffda0: <000000c00034e180  000000c000289d48
0000000004fffdb0:  000000c000289d08  00000000032b97e0
0000000004fffdc0:  00000000032b97e0  000000006daf7999
0000000004fffdd0:  0000000004fffe20  000000000043b6c7 <runtime.schedule+391>
0000000004fffde0:  000000c00006a900  0000000000000000
0000000004fffdf0:  000000c000289d48  000000006db48539
0000000004fffe00:  0000000004fffe20  000000c00006a900
0000000004fffe10:  000000c00034e180  000000c00040e000
0000000004fffe20:  0000000004fffe60  0000000000461bd3 <runtime.asmcgocall+115>
0000000004fffe30:  000000c000044f00  000000c00004de00
0000000004fffe40:  0000000000000000  000000c000060027
0000000004fffe50:  0000000000000000  0000000000000328
0000000004fffe60:  000000c00006a900  00000000004602fe <runtime.mcall+94>
0000000004fffe70:  000000c00004de00  00000000032b83f0
runtime: unknown pc 0x6daf9384
stack: frame={sp:0x4fffda0, fp:0x0} stack=[0x0,0x4fffe80)
0000000004fffca0:  0000000002030000  000000c00035c000
0000000004fffcb0:  00000000000000c0  0000000000000008
0000000004fffcc0:  00000000032bd8a0  00007ffe2857fc11
0000000004fffcd0:  0000000002030000  000000c00035c000
0000000004fffce0:  0000000000000006  bf5f5019cb2b5b50
0000000004fffcf0:  00000000032bdb10  00007ffe2857fc11
0000000004fffd00:  000000010000001d  00000000032b0000
0000000004fffd10:  000000c000289d08  0000000000000000
0000000004fffd20:  0000000000000000  0000000000b97848
0000000004fffd30:  000000c000289d48  00007ffe28239cfc
0000000004fffd40:  00000000032a0000  000000c00034e180
0000000004fffd50:  00000000000000a0  0000000000000001
0000000004fffd60:  00000000032a0000  000000006db4d719
0000000004fffd70:  000000c00034e180  000000c000289d48
0000000004fffd80:  000000c000289d08  000000006db45a3b
0000000004fffd90:  0000000000000006  000000006daf9384
0000000004fffda0: <000000c00034e180  000000c000289d48
0000000004fffdb0:  000000c000289d08  00000000032b97e0
0000000004fffdc0:  00000000032b97e0  000000006daf7999
0000000004fffdd0:  0000000004fffe20  000000000043b6c7 <runtime.schedule+391>
0000000004fffde0:  000000c00006a900  0000000000000000
0000000004fffdf0:  000000c000289d48  000000006db48539
0000000004fffe00:  0000000004fffe20  000000c00006a900
0000000004fffe10:  000000c00034e180  000000c00040e000
0000000004fffe20:  0000000004fffe60  0000000000461bd3 <runtime.asmcgocall+115>
0000000004fffe30:  000000c000044f00  000000c00004de00
0000000004fffe40:  0000000000000000  000000c000060027
0000000004fffe50:  0000000000000000  0000000000000328
0000000004fffe60:  000000c00006a900  00000000004602fe <runtime.mcall+94>
0000000004fffe70:  000000c00004de00  00000000032b83f0

goroutine 60 [syscall]:
github.com/giongto35/cloud-game/pkg/emulator/libretro/nanoarch._Cfunc_bridge_retro_unload_game(0x6db48510)
        _cgo_gotypes.go:399 +0x48
github.com/giongto35/cloud-game/pkg/emulator/libretro/nanoarch.nanoarchShutdown.func1()
        D:/Work/elysium/cloud-game/pkg/emulator/libretro/nanoarch/nanoarch.go:513 +0x64
github.com/giongto35/cloud-game/pkg/emulator/libretro/nanoarch.nanoarchShutdown()
        D:/Work/elysium/cloud-game/pkg/emulator/libretro/nanoarch/nanoarch.go:513 +0x27
github.com/giongto35/cloud-game/pkg/emulator/libretro/nanoarch.(*naEmulator).Start(0xc0000d0370)
        D:/Work/elysium/cloud-game/pkg/emulator/libretro/nanoarch/naemulator.go:143 +0x129
github.com/giongto35/cloud-game/pkg/worker/room.NewRoom.func1(0xc0000f22d0, 0xc000328420, 0xc0000d80e0, 0xc000542500, 0xc000328480, 0xc0003bc380, 0xddc866, 0x3, 0xc00013694d, 0x22, ...)
        D:/Work/elysium/cloud-game/pkg/worker/room/room.go:113 +0x4bb
created by github.com/giongto35/cloud-game/pkg/worker/room.NewRoom
        D:/Work/elysium/cloud-game/pkg/worker/room/room.go:90 +0x4a5

goroutine 1 [chan receive]:
I1008 20:35:15.224389    5468 cws.go:156] [!] read: websocket: close 1001 (going away)
main.main()
        D:/Work/elysium/cloud-game/cmd/overworker/main.go:38 +0x59d

goroutine 6 [syscall]:
os/signal.signal_recv(0x0)
        c:/go/src/runtime/sigqueue.go:147 +0xa3
os/signal.loop()
        c:/go/src/os/signal/signal_unix.go:23 +0x29
created by os/signal.init.0
        c:/go/src/os/signal/signal_unix.go:29 +0x48

goroutine 7 [chan receive]:
github.com/golang/glog.(*loggingT).flushDaemon(0x13ccb80)
        C:/Users/sergy/go/pkg/mod/github.com/golang/[email protected]/glog.go:882 +0x92
created by github.com/golang/glog.init.0
        C:/Users/sergy/go/pkg/mod/github.com/golang/[email protected]/glog.go:410 +0x276

goroutine 26 [chan receive]:
github.com/giongto35/cloud-game/pkg/cws.(*Client).Heartbeat(0xc00010e200)
        D:/Work/elysium/cloud-game/pkg/cws/cws.go:140 +0xc9
created by github.com/giongto35/cloud-game/pkg/worker.(*Handler).Run
        D:/Work/elysium/cloud-game/pkg/worker/handlers.go:66 +0xd1

goroutine 9 [IO wait]:
internal/poll.runtime_pollWait(0x2960dd8, 0x72, 0xedd060)
        c:/go/src/runtime/netpoll.go:184 +0x5c
internal/poll.(*pollDesc).wait(0xc000338448, 0x72, 0xb98400, 0x0, 0x0)
        c:/go/src/internal/poll/fd_poll_runtime.go:87 +0x4c
internal/poll.(*ioSrv).ExecIO(0x13c8160, 0xc000338298, 0xc000340160, 0x1, 0x0, 0x250)
        c:/go/src/internal/poll/fd_windows.go:228 +0x121
internal/poll.(*FD).acceptOne(0xc000338280, 0x250, 0xc0003540f0, 0x2, 0x2, 0xc000338298, 0x1ec200, 0x203000000000000, 0x1effff, 0xc0000bbb78)
        c:/go/src/internal/poll/fd_windows.go:896 +0xa9
internal/poll.(*FD).Accept(0xc000338280, 0xc0000bbc78, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, ...)
        c:/go/src/internal/poll/fd_windows.go:930 +0x17a
net.(*netFD).accept(0xc000338280, 0xc0000bbd10, 0x6fe32b, 0xc0003440a0)
        c:/go/src/net/fd_windows.go:193 +0x7b
net.(*TCPListener).accept(0xc000340020, 0x6fe2d5, 0x0, 0x49683d)
        c:/go/src/net/tcpsock_posix.go:139 +0x39
net.(*TCPListener).Accept(0xc000340020, 0xc0000bbd60, 0x18, 0xc0000f8180, 0x6fd835)
        c:/go/src/net/tcpsock.go:261 +0x4e
net/http.(*Server).Serve(0xc000344000, 0xeeb540, 0xc000340020, 0x0, 0x0)
        c:/go/src/net/http/server.go:2896 +0x28d
net/http.(*Server).ListenAndServe(0xc000344000, 0xc000344000, 0x1)
        c:/go/src/net/http/server.go:2825 +0xbe
net/http.ListenAndServe(...)
        c:/go/src/net/http/server.go:3080
github.com/giongto35/cloud-game/pkg/worker.(*OverWorker).initializeWorker(0xc0000a81e0)
        D:/Work/elysium/cloud-game/pkg/worker/overworker.go:84 +0x2e6
created by github.com/giongto35/cloud-game/pkg/worker.(*OverWorker).Run
        D:/Work/elysium/cloud-game/pkg/worker/overworker.go:32 +0x46

goroutine 37 [select]:
go.opencensus.io/stats/view.(*worker).start(0xc00012c910)
        C:/Users/sergy/go/pkg/mod/[email protected]/stats/view/worker.go:154 +0x107
created by go.opencensus.io/stats/view.init.0
        C:/Users/sergy/go/pkg/mod/[email protected]/stats/view/worker.go:32 +0x5e

goroutine 10 [IO wait]:
internal/poll.runtime_pollWait(0x2960ea8, 0x72, 0xedd060)
        c:/go/src/runtime/netpoll.go:184 +0x5c
internal/poll.(*pollDesc).wait(0xc00009b5c8, 0x72, 0xb98400, 0x0, 0x0)
        c:/go/src/internal/poll/fd_poll_runtime.go:87 +0x4c
internal/poll.(*ioSrv).ExecIO(0x13c8160, 0xc00009b418, 0xc000004c60, 0x1, 0x0, 0x1ec)
        c:/go/src/internal/poll/fd_windows.go:228 +0x121
internal/poll.(*FD).acceptOne(0xc00009b400, 0x1ec, 0xc00033c000, 0x2, 0x2, 0xc00009b418, 0x8ce5d9defb68d101, 0xc0000751e0, 0x8c00000000da0e20, 0xc0002b5a80)
        c:/go/src/internal/poll/fd_windows.go:896 +0xa9
internal/poll.(*FD).Accept(0xc00009b400, 0xc0002b5bd0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, ...)
        c:/go/src/internal/poll/fd_windows.go:930 +0x17a
net.(*netFD).accept(0xc00009b400, 0xc0002b5c38, 0x13cdac0, 0x190008)
        c:/go/src/net/fd_windows.go:193 +0x7b
net.(*TCPListener).accept(0xc000004c40, 0xc0002b5c68, 0x40e94f, 0x30)
        c:/go/src/net/tcpsock_posix.go:139 +0x39
net.(*TCPListener).Accept(0xc000004c40, 0xd5f160, 0xc000089770, 0xcc4e40, 0xbdf060)
        c:/go/src/net/tcpsock.go:261 +0x4e
net/http.(*Server).Serve(0xc0002f6000, 0xeeb540, 0xc000004c40, 0x0, 0x0)
        c:/go/src/net/http/server.go:2896 +0x28d
net/http.(*Server).ListenAndServe(0xc0002f6000, 0xc00002a550, 0xf)
        c:/go/src/net/http/server.go:2825 +0xbe
github.com/giongto35/cloud-game/pkg/monitoring.(*ServerMonitoring).Run(0xc0000c6200, 0x0, 0xc0002b5fb8)
        D:/Work/elysium/cloud-game/pkg/monitoring/monitoring.go:71 +0x240
github.com/giongto35/cloud-game/pkg/worker.(*OverWorker).RunMonitoringServer(0xc0000a81e0)
        D:/Work/elysium/cloud-game/pkg/worker/overworker.go:39 +0x8e
created by github.com/giongto35/cloud-game/pkg/worker.(*OverWorker).Run
        D:/Work/elysium/cloud-game/pkg/worker/overworker.go:33 +0x68

goroutine 14 [IO wait]:
internal/poll.runtime_pollWait(0x2960758, 0x72, 0xedd060)
        c:/go/src/runtime/netpoll.go:184 +0x5c
internal/poll.(*pollDesc).wait(0xc0002a35c8, 0x72, 0xb98400, 0x0, 0x0)
        c:/go/src/internal/poll/fd_poll_runtime.go:87 +0x4c
internal/poll.(*ioSrv).ExecIO(0x13c8160, 0xc0002a3418, 0xe17f68, 0xd61540, 0x0, 0x2)
        c:/go/src/internal/poll/fd_windows.go:228 +0x121
internal/poll.(*FD).ReadFrom(0xc0002a3400, 0xc000454000, 0x2000, 0x2000, 0x0, 0x0, 0x0, 0x0, 0x0)
        c:/go/src/internal/poll/fd_windows.go:667 +0x13d
net.(*netFD).readFrom(0xc0002a3400, 0xc000454000, 0x2000, 0x2000, 0xc000314ae0, 0xc0002b7e20, 0x2, 0x0, 0x0)
        c:/go/src/net/fd_windows.go:158 +0x62
net.(*UDPConn).readFrom(0xc000104688, 0xc000454000, 0x2000, 0x2000, 0x40, 0xc000106360, 0x0, 0xc000326780)
        c:/go/src/net/udpsock_posix.go:47 +0x71
net.(*UDPConn).ReadFrom(0xc000104688, 0xc000454000, 0x2000, 0x2000, 0x2000, 0xee3580, 0xc000106360, 0xef6d40, 0xc00010e4c0)
        c:/go/src/net/udpsock.go:121 +0x64
github.com/pion/ice.(*candidateBase).recvLoop(0xc000154360)
        C:/Users/sergy/go/pkg/mod/github.com/pion/[email protected]/candidate_base.go:92 +0x152
created by github.com/pion/ice.(*candidateBase).start
        C:/Users/sergy/go/pkg/mod/github.com/pion/[email protected]/candidate_base.go:81 +0xe7

goroutine 21 [running]:
        goroutine running on other thread; stack unavailable
created by github.com/giongto35/cloud-game/pkg/worker.(*OverWorker).initializeWorker
        D:/Work/elysium/cloud-game/pkg/worker/overworker.go:60 +0xb8

goroutine 30 [IO wait]:
internal/poll.runtime_pollWait(0x29608f8, 0x72, 0xedd060)
        c:/go/src/runtime/netpoll.go:184 +0x5c
internal/poll.(*pollDesc).wait(0xc000326bc8, 0x72, 0xb98400, 0x0, 0x0)
        c:/go/src/internal/poll/fd_poll_runtime.go:87 +0x4c
internal/poll.(*ioSrv).ExecIO(0x13c8160, 0xc000326a18, 0xe17f68, 0x400, 0x29e2300, 0x29fffff)
        c:/go/src/internal/poll/fd_windows.go:228 +0x121
internal/poll.(*FD).ReadFrom(0xc000326a00, 0xc000444000, 0x2000, 0x2000, 0x0, 0x0, 0x0, 0x0, 0x0)
        c:/go/src/internal/poll/fd_windows.go:667 +0x13d
net.(*netFD).readFrom(0xc000326a00, 0xc000444000, 0x2000, 0x2000, 0xc000301ef0, 0x40e0a0, 0x1906b0, 0x65, 0xc000444000)
        c:/go/src/net/fd_windows.go:158 +0x62
net.(*UDPConn).readFrom(0xc0000b8138, 0xc000444000, 0x2000, 0x2000, 0x0, 0x0, 0xc000301f20, 0x448313)
        c:/go/src/net/udpsock_posix.go:47 +0x71
net.(*UDPConn).ReadFrom(0xc0000b8138, 0xc000444000, 0x2000, 0x2000, 0x43c08d, 0xe18688, 0xc000109430, 0xc, 0xf16f)
        c:/go/src/net/udpsock.go:121 +0x64
github.com/pion/ice.(*candidateBase).recvLoop(0xc000362280)
        C:/Users/sergy/go/pkg/mod/github.com/pion/[email protected]/candidate_base.go:92 +0x152
created by github.com/pion/ice.(*candidateBase).start
        C:/Users/sergy/go/pkg/mod/github.com/pion/[email protected]/candidate_base.go:81 +0xe7

goroutine 54 [IO wait]:
internal/poll.runtime_pollWait(0x2960a98, 0x72, 0xedd060)
        c:/go/src/runtime/netpoll.go:184 +0x5c
internal/poll.(*pollDesc).wait(0xc000338948, 0x72, 0xb98400, 0x0, 0x0)
        c:/go/src/internal/poll/fd_poll_runtime.go:87 +0x4c
internal/poll.(*ioSrv).ExecIO(0x13c8160, 0xc000338798, 0xe17f68, 0x400, 0x29e2200, 0x29fffff)
        c:/go/src/internal/poll/fd_windows.go:228 +0x121
internal/poll.(*FD).ReadFrom(0xc000338780, 0xc000442000, 0x2000, 0x2000, 0x0, 0x0, 0x0, 0x0, 0x0)
        c:/go/src/internal/poll/fd_windows.go:667 +0x13d
net.(*netFD).readFrom(0xc000338780, 0xc000442000, 0x2000, 0x2000, 0xc000303ef0, 0x40e0a0, 0x191400, 0x65, 0xc000442000)
        c:/go/src/net/fd_windows.go:158 +0x62
net.(*UDPConn).readFrom(0xc00034c068, 0xc000442000, 0x2000, 0x2000, 0x0, 0x0, 0xc000303f20, 0x448313)
        c:/go/src/net/udpsock_posix.go:47 +0x71
net.(*UDPConn).ReadFrom(0xc00034c068, 0xc000442000, 0x2000, 0x2000, 0x43c08d, 0xe18688, 0xc000336330, 0x9, 0xf16d)
        c:/go/src/net/udpsock.go:121 +0x64
github.com/pion/ice.(*candidateBase).recvLoop(0xc000382140)
        C:/Users/sergy/go/pkg/mod/github.com/pion/[email protected]/candidate_base.go:92 +0x152
created by github.com/pion/ice.(*candidateBase).start
        C:/Users/sergy/go/pkg/mod/github.com/pion/[email protected]/candidate_base.go:81 +0xe7

goroutine 13 [IO wait]:
internal/poll.runtime_pollWait(0x2960828, 0x72, 0xedd060)
        c:/go/src/runtime/netpoll.go:184 +0x5c
internal/poll.(*pollDesc).wait(0xc0002a3348, 0x72, 0xb98400, 0x0, 0x0)
        c:/go/src/internal/poll/fd_poll_runtime.go:87 +0x4c
internal/poll.(*ioSrv).ExecIO(0x13c8160, 0xc0002a3198, 0xe17f68, 0x60, 0x10, 0x20)
        c:/go/src/internal/poll/fd_windows.go:228 +0x121
internal/poll.(*FD).ReadFrom(0xc0002a3180, 0xc00044e000, 0x2000, 0x2000, 0x0, 0x0, 0x0, 0x0, 0x0)
        c:/go/src/internal/poll/fd_windows.go:667 +0x13d
net.(*netFD).readFrom(0xc0002a3180, 0xc00044e000, 0x2000, 0x2000, 0x0, 0x1, 0x50, 0xc00003c2a0, 0xc000142000)
        c:/go/src/net/fd_windows.go:158 +0x62
net.(*UDPConn).readFrom(0xc000104670, 0xc00044e000, 0x2000, 0x2000, 0x46, 0xc00013e720, 0x0, 0xc000326780)
        c:/go/src/net/udpsock_posix.go:47 +0x71
net.(*UDPConn).ReadFrom(0xc000104670, 0xc00044e000, 0x2000, 0x2000, 0x2000, 0xee3580, 0xc00013e720, 0xef6d40, 0xc00010e4c0)
        c:/go/src/net/udpsock.go:121 +0x64
github.com/pion/ice.(*candidateBase).recvLoop(0xc0002dc8c0)
        C:/Users/sergy/go/pkg/mod/github.com/pion/[email protected]/candidate_base.go:92 +0x152
created by github.com/pion/ice.(*candidateBase).start
        C:/Users/sergy/go/pkg/mod/github.com/pion/[email protected]/candidate_base.go:81 +0xe7

goroutine 52 [IO wait]:
internal/poll.runtime_pollWait(0x2960c38, 0x72, 0xedd060)
        c:/go/src/runtime/netpoll.go:184 +0x5c
internal/poll.(*pollDesc).wait(0xc0003386c8, 0x72, 0xb98400, 0x0, 0x0)
        c:/go/src/internal/poll/fd_poll_runtime.go:87 +0x4c
internal/poll.(*ioSrv).ExecIO(0x13c8160, 0xc000338518, 0xe17f60, 0xffffffffffffffff, 0x1, 0x1)
        c:/go/src/internal/poll/fd_windows.go:228 +0x121
internal/poll.(*FD).Read(0xc000338500, 0xc000384000, 0x1000, 0x1000, 0x0, 0x0, 0x0)
        c:/go/src/internal/poll/fd_windows.go:527 +0x31c
net.(*netFD).Read(0xc000338500, 0xc000384000, 0x1000, 0x1000, 0x0, 0xc0002fd7d8, 0xc0002fd830)
        c:/go/src/net/fd_windows.go:152 +0x56
net.(*conn).Read(0xc00034c058, 0xc000384000, 0x1000, 0x1000, 0x0, 0x0, 0x0)
        c:/go/src/net/net.go:184 +0x6f
net/http.(*connReader).Read(0xc00033e3f0, 0xc000384000, 0x1000, 0x1000, 0xc0003385f0, 0x0, 0x5)
        c:/go/src/net/http/server.go:785 +0xfb
bufio.(*Reader).fill(0xc0003280c0)
        c:/go/src/bufio/bufio.go:100 +0x10a
bufio.(*Reader).ReadSlice(0xc0003280c0, 0xc00033860a, 0x0, 0xc0002fd9a8, 0x40e0a0, 0xc000386100, 0x100)
        c:/go/src/bufio/bufio.go:359 +0x44
bufio.(*Reader).ReadLine(0xc0003280c0, 0xc0002fd9b0, 0xc000342380, 0x191400, 0x0, 0x3, 0x4a6599)
        c:/go/src/bufio/bufio.go:388 +0x3b
net/textproto.(*Reader).readLineSlice(0xc00033e420, 0xc000386100, 0xc000338500, 0x0, 0x0, 0x495f24)
        c:/go/src/net/textproto/reader.go:57 +0x73
net/textproto.(*Reader).ReadLine(...)
        c:/go/src/net/textproto/reader.go:38
net/http.readRequest(0xc0003280c0, 0x0, 0xc000386100, 0x0, 0x0)
        c:/go/src/net/http/request.go:1012 +0x99
net/http.(*conn).readRequest(0xc000382000, 0xeed240, 0xc00034a040, 0x0, 0x0, 0x0)
        c:/go/src/net/http/server.go:965 +0x166
net/http.(*conn).serve(0xc000382000, 0xeed240, 0xc00034a040)
        c:/go/src/net/http/server.go:1817 +0x6db
created by net/http.(*Server).Serve
        c:/go/src/net/http/server.go:2927 +0x395

goroutine 55 [IO wait]:
internal/poll.runtime_pollWait(0x29609c8, 0x72, 0xedd060)
        c:/go/src/runtime/netpoll.go:184 +0x5c
internal/poll.(*pollDesc).wait(0xc0002a30c8, 0x72, 0xb98400, 0x0, 0x0)
        c:/go/src/internal/poll/fd_poll_runtime.go:87 +0x4c
internal/poll.(*ioSrv).ExecIO(0x13c8160, 0xc0002a2f18, 0xe17f68, 0x400, 0x29e2100, 0x29fffff)
        c:/go/src/internal/poll/fd_windows.go:228 +0x121
internal/poll.(*FD).ReadFrom(0xc0002a2f00, 0xc000440000, 0x2000, 0x2000, 0x0, 0x0, 0x0, 0x0, 0x0)
        c:/go/src/internal/poll/fd_windows.go:667 +0x13d
net.(*netFD).readFrom(0xc0002a2f00, 0xc000440000, 0x2000, 0x2000, 0xc0002ffef0, 0x40e0a0, 0x191400, 0xc000016065, 0xc000440000)
        c:/go/src/net/fd_windows.go:158 +0x62
net.(*UDPConn).readFrom(0xc000104668, 0xc000440000, 0x2000, 0x2000, 0x4, 0xc0002fff08, 0xc0002fff20, 0x448313)
        c:/go/src/net/udpsock_posix.go:47 +0x71
net.(*UDPConn).ReadFrom(0xc000104668, 0xc000440000, 0x2000, 0x2000, 0xc00034a110, 0x13e9840, 0x29a0001, 0xc0002fffc8, 0x6f2d6a)
        c:/go/src/net/udpsock.go:121 +0x64
github.com/pion/ice.(*candidateBase).recvLoop(0xc0002dc820)
        C:/Users/sergy/go/pkg/mod/github.com/pion/[email protected]/candidate_base.go:92 +0x152
created by github.com/pion/ice.(*candidateBase).start
        C:/Users/sergy/go/pkg/mod/github.com/pion/[email protected]/candidate_base.go:81 +0xe7

goroutine 85 [chan receive]:
github.com/pion/sctp.(*Association).AcceptStream(...)
        C:/Users/sergy/go/pkg/mod/github.com/pion/[email protected]/association.go:1047
github.com/pion/datachannel.Accept(0xc000116000, 0xc0003b3f88, 0x450e62, 0xc0000b65a8, 0xc000064501)
        C:/Users/sergy/go/pkg/mod/github.com/pion/[email protected]/datachannel.go:128 +0x50
github.com/pion/webrtc/v2.(*SCTPTransport).acceptDataChannels(0xc0002d09a0, 0xc000116000)
        C:/Users/sergy/go/pkg/mod/github.com/pion/webrtc/[email protected]/sctptransport.go:132 +0xcc
created by github.com/pion/webrtc/v2.(*SCTPTransport).Start
        C:/Users/sergy/go/pkg/mod/github.com/pion/webrtc/[email protected]/sctptransport.go:98 +0x137

goroutine 29 [select]:
github.com/pion/ice.(*Agent).taskLoop(0xc000326780)
        C:/Users/sergy/go/pkg/mod/github.com/pion/[email protected]/agent.go:696 +0x10e
created by github.com/pion/ice.NewAgent
        C:/Users/sergy/go/pkg/mod/github.com/pion/[email protected]/agent.go:397 +0x810

goroutine 87 [sync.Cond.Wait]:
runtime.goparkunlock(...)
        c:/go/src/runtime/proc.go:310
sync.runtime_notifyListWait(0xc000135490, 0xc000000001)
        c:/go/src/runtime/sema.go:510 +0x106
sync.(*Cond).Wait(0xc000135480)
        c:/go/src/sync/cond.go:56 +0xa4
github.com/pion/sctp.(*Stream).ReadSCTP(0xc00012eb40, 0xc000230000, 0xffff, 0xffff, 0x0, 0x0, 0x0, 0x0)
        C:/Users/sergy/go/pkg/mod/github.com/pion/[email protected]/stream.go:107 +0xbd
github.com/pion/datachannel.(*DataChannel).ReadDataChannel(0xc0001600e0, 0xc000230000, 0xffff, 0xffff, 0xffff, 0xe18600, 0x0, 0x0)
        C:/Users/sergy/go/pkg/mod/github.com/pion/[email protected]/datachannel.go:188 +0x61
github.com/pion/webrtc/v2.(*DataChannel).readLoop(0xc00045c000)
        C:/Users/sergy/go/pkg/mod/github.com/pion/webrtc/[email protected]/datachannel.go:296 +0xac
created by github.com/pion/webrtc/v2.(*DataChannel).handleOpen
        C:/Users/sergy/go/pkg/mod/github.com/pion/webrtc/[email protected]/datachannel.go:271 +0xf5

goroutine 98 [runnable]:
github.com/pion/transport/packetio.(*Buffer).Read(0xc0000b6c80, 0xc00009c000, 0x2000, 0x2000, 0x6000105, 0x0, 0xffffffffffffffff)
        C:/Users/sergy/go/pkg/mod/github.com/pion/[email protected]/packetio/buffer.go:135 +0x41
github.com/pion/ice.(*Conn).Read(0xc000340040, 0xc00009c000, 0x2000, 0x2000, 0x0, 0x0, 0x0)
        C:/Users/sergy/go/pkg/mod/github.com/pion/[email protected]/transport.go:79 +0x9d
github.com/pion/webrtc/v2/internal/mux.(*Mux).readLoop(0xc000072000)
        C:/Users/sergy/go/pkg/mod/github.com/pion/webrtc/[email protected]/internal/mux/mux.go:105 +0xe3
created by github.com/pion/webrtc/v2/internal/mux.NewMux
        C:/Users/sergy/go/pkg/mod/github.com/pion/webrtc/[email protected]/internal/mux/mux.go:43 +0x12e

goroutine 84 [chan receive]:
github.com/giongto35/cloud-game/pkg/webrtc.(*WebRTC).startStreaming.func2(0xc0003961c0, 0xc000154480)
        D:/Work/elysium/cloud-game/pkg/webrtc/webrtc.go:305 +0x9f
created by github.com/giongto35/cloud-game/pkg/webrtc.(*WebRTC).startStreaming
        D:/Work/elysium/cloud-game/pkg/webrtc/webrtc.go:297 +0xc0

goroutine 73 [chan receive]:
github.com/giongto35/cloud-game/pkg/encoder/vpx-encoder.(*VpxEncoder).startLooping(0xc000150100)
        D:/Work/elysium/cloud-game/pkg/encoder/vpx-encoder/encoder.go:140 +0x11b
created by github.com/giongto35/cloud-game/pkg/encoder/vpx-encoder.(*VpxEncoder).init
        D:/Work/elysium/cloud-game/pkg/encoder/vpx-encoder/encoder.go:120 +0x192

goroutine 83 [chan receive]:
github.com/giongto35/cloud-game/pkg/webrtc.(*WebRTC).startStreaming.func1(0xc0003961c0, 0xc0001543f0)
        D:/Work/elysium/cloud-game/pkg/webrtc/webrtc.go:275 +0x9f
created by github.com/giongto35/cloud-game/pkg/webrtc.(*WebRTC).startStreaming
        D:/Work/elysium/cloud-game/pkg/webrtc/webrtc.go:267 +0x94

goroutine 70 [chan receive]:
github.com/pion/srtp.(*SessionSRTP).AcceptStream(0xc000128310, 0xc000128310, 0x0, 0x0, 0x0)
        C:/Users/sergy/go/pkg/mod/github.com/pion/[email protected]/session_srtp.go:81 +0x4c
github.com/pion/webrtc/v2.(*PeerConnection).drainSRTP.func1(0xc0003be000)
        C:/Users/sergy/go/pkg/mod/github.com/pion/webrtc/[email protected]/peerconnection.go:1147 +0xa9
created by github.com/pion/webrtc/v2.(*PeerConnection).drainSRTP
        C:/Users/sergy/go/pkg/mod/github.com/pion/webrtc/[email protected]/peerconnection.go:1139 +0x4a

goroutine 101 [chan receive]:
github.com/pion/transport/packetio.(*Buffer).Read(0xc0000720a0, 0xc00016e000, 0x2000, 0x2000, 0xc80160, 0x897101, 0xc00016e000)
        C:/Users/sergy/go/pkg/mod/github.com/pion/[email protected]/packetio/buffer.go:135 +0x41
github.com/pion/webrtc/v2/internal/mux.(*Endpoint).Read(0xc00049d1a0, 0xc00016e000, 0x2000, 0x2000, 0x0, 0x0, 0xc0001403c0)
        C:/Users/sergy/go/pkg/mod/github.com/pion/webrtc/[email protected]/internal/mux/endpoint.go:36 +0x54
github.com/pion/srtp.(*session).start.func1(0xc000128310, 0xc00049d8b0, 0xee9e00, 0xc000128310)
        C:/Users/sergy/go/pkg/mod/github.com/pion/[email protected]/session.go:124 +0xd7
created by github.com/pion/srtp.(*session).start
        C:/Users/sergy/go/pkg/mod/github.com/pion/[email protected]/session.go:111 +0x1e8

goroutine 102 [chan receive]:
github.com/pion/transport/packetio.(*Buffer).Read(0xc0000720f0, 0xc000172000, 0x2000, 0x2000, 0xc00022b17c, 0xee9d40, 0xc0002bfc50)
        C:/Users/sergy/go/pkg/mod/github.com/pion/[email protected]/packetio/buffer.go:135 +0x41
github.com/pion/webrtc/v2/internal/mux.(*Endpoint).Read(0xc00049d1b0, 0xc000172000, 0x2000, 0x2000, 0x0, 0x0, 0x0)
        C:/Users/sergy/go/pkg/mod/github.com/pion/webrtc/[email protected]/internal/mux/endpoint.go:36 +0x54
github.com/pion/srtp.(*session).start.func1(0xc000128380, 0xc00049d910, 0xee9dc0, 0xc000128380)
        C:/Users/sergy/go/pkg/mod/github.com/pion/[email protected]/session.go:124 +0xd7
created by github.com/pion/srtp.(*session).start
        C:/Users/sergy/go/pkg/mod/github.com/pion/[email protected]/session.go:111 +0x1e8

goroutine 103 [chan receive]:
github.com/pion/srtp.(*SessionSRTCP).AcceptStream(0xc000128380, 0xc000128380, 0x0, 0x0, 0x0)
        C:/Users/sergy/go/pkg/mod/github.com/pion/[email protected]/session_srtcp.go:74 +0x4c
github.com/pion/webrtc/v2.(*PeerConnection).drainSRTP(0xc0003be000)
        C:/Users/sergy/go/pkg/mod/github.com/pion/webrtc/[email protected]/peerconnection.go:1164 +0xcb
created by github.com/pion/webrtc/v2.(*PeerConnection).startTransports
        C:/Users/sergy/go/pkg/mod/github.com/pion/webrtc/[email protected]/peerconnection.go:1885 +0x498

goroutine 104 [running]:
        goroutine running on other thread; stack unavailable
created by github.com/pion/sctp.(*Association).init
        C:/Users/sergy/go/pkg/mod/github.com/pion/[email protected]/association.go:281 +0xb1

goroutine 105 [runnable]:
github.com/pion/sctp.(*Association).writeLoop(0xc000116000)
        C:/Users/sergy/go/pkg/mod/github.com/pion/[email protected]/association.go:437 +0x192
created by github.com/pion/sctp.(*Association).init
        C:/Users/sergy/go/pkg/mod/github.com/pion/[email protected]/association.go:I1008 20:35:15.247388   11716 webrtc.go:165] Data channel closed
I1008 20:35:15.247388   11716 webrtc.go:166] Closed webrtc
282 +0xd6

goroutine 108 [chan receive]:
github.com/giongto35/cloud-game/pkg/worker/room.(*Room).startVideo(0xc0000f22d0, 0xa0, 0x90, 0xddc866, 0x3)
        D:/Work/elysium/cloud-game/pkg/worker/room/media.go:188 +0x23b
created by github.com/giongto35/cloud-game/pkg/worker/room.NewRoom.func1
        D:/Work/elysium/cloud-game/pkg/worker/room/room.go:111 +0x46f

goroutine 109 [chan receive]:
github.com/giongto35/cloud-game/pkg/worker/room.(*Room).startAudio(0xc0000f22d0, 0x8000)
        D:/Work/elysium/cloud-game/pkg/worker/room/media.go:108 +0x2d4
created by github.com/giongto35/cloud-game/pkg/worker/room.NewRoom.func1
        D:/Work/elysium/cloud-game/pkg/worker/room/room.go:112 +0x4a1

goroutine 74 [chan receive]:
github.com/giongto35/cloud-game/pkg/worker/room.(*Room).startVideo.func2(0xc000064420, 0xc0000f22d0)
        D:/Work/elysium/cloud-game/pkg/worker/room/media.go:175 +0x10c
created by github.com/giongto35/cloud-game/pkg/worker/room.(*Room).startVideo
        D:/Work/elysium/cloud-game/pkg/worker/room/media.go:167 +0x1fb
rax     0x6db45ee0
rbx     0x32a0000
rcx     0x6db99480
rdi     0xc000289d48
rsi     0xc00034e180
rbp     0xc000289d08
rsp     0x4fffd98
r8      0x5d86801
r9      0x1
r10     0x3
r11     0x4fffcd0
r12     0xbf5f5019cb2b5b50
r13     0x6
r14     0xc00035c000
r15     0x2030000
rip     0x0
rflags  0x10202
cs      0x33
fs      0x53
gs      0x2b
I1008 20:35:15.253389    5468 cws.go:156] [!] read: read tcp [::1]:8000->[::1]:58755: wsarecv: An existing connection was forcibly closed by the remote host.
I1008 20:35:15.253389    5468 handlers.go:311] Unregister server from overlord

Pokemon Gold Emulates with a odd screen

When trying to load Pokemon Gold ROM its graphics load oddly in a broken manner. Not sure if this is a ROM or emulator issue or something else, but worth looking into to make sure it can handle different ROMs.

Move away from WebRTC DataChannel, using socket for game input.

WebRTC Datachannel has some bug (when the connection disconnect abnormally)
ortc ERROR: 2019/10/23 19:24:00 Failed to accept data channel: unexpected packet type: WebRTC #Binary
In addition, WebRTC Datachannel seems not be currently supported by some browser.
Because game input payload is small, better use WebSocket for input.

Proposal: Split binaries for overlordhost and overlord into separate binary

Currently, cloud-game generates only one binary for both the program included in a single binary. While running we have to explicitly set flags to indicate which mode we intend it to run.
I am suggesting to split the two binaries into two different executables. They might share some of the same library packages but should live in different executables. I think there are some benefits - like -

1. we could include runtime flags specific to one program,
2. We could do different versioning for overlordhost and overlord,
3. one executable doesn't include the life cycle of the other one.

Here is some package restructure proposal that I think would suit nicely

cloud-game
|-- assets     // includes all static .so or game file
|-- assests/games
|-- assests/lib

|-- cmd         // will include all the executables
|-- cmd/overlord  // will include the main.go for overlord. can use spf13/cobra
|-- cmd/overlordhost  // main.go for overlordhost

|-- docs      // includes all the static docs
|-- docs/design
|-- docs/userguide
|-- docs/site    // includes static site pages for projects

|-- hack       // Includes various scripts, build files
|-- hack/docker // includes docker build files
|-- hack/config  // includes various config files like prometheus.conf
|-- hack/k8s    // includes k8s runtime files
|-- hac/scripts  // various scripts to build env or others

|-- pkg  // includes all go/ cgo librarie codes. ie-
|-- pkg/api
|-- pkg/util
|-- pkg/config
|-- pkg/webrtc

| -- static   // include project-level static resources like the logo
| -- static/logo
| -- static

|-- web      // all the files for the web frontend
|-- web/

|-- vendor // go vendor directory

|-- Makefiles
|-- go.mod
|-- go.sum
|-- README.md

@giongto35 Let me know what you think about this? I can go forward with a PR if it seems okay. Thanks for this awesome project. ๐Ÿ˜„

hello Giongto

hello giongto, it makes me happy to see and see that your project and the ideas I had and you told me a few months ago, come true ... ok now seeing the structure ... some questions arise.
regardless of the server where you are staying .. example a powerful one can I run demanding games say (ps2, xbox, xbox 360, game cube,? and another question can I add a frontend "launchbox" to the environment?

Stream game to twitch

Integrate with Twitch API to stream game to Twitch, hence can gain more exposure.

Audio streaming

I was trying to make audio streaming work (again) and it is kinda working now. It's synced with the video but overly compressed for some reason (see example on the video down below).

So, here what I fixed so far:

  • it seems that non-blocking select-default goroutine in the emulator audio samples pusher drops a fair amount of samples here. Tried it without select and it pushes all samples now. For the testing I wrote a simple function, which writes raw PCM samples into a file and then played it in the Audacity (Import -> Raw).
  • I excluded resampler and used nestopia core because it runs in 48kHz audio rate to not to worry about resampling for Opus (which needs to be checked)
  • I'm not quite sure why there is int16 to float32 sample conversion, Opus encoder by default operates with int16. When I tried to play saved float32 samples they were overly clipped or loud distorted, maybe I just saved it wrong, but I switched GO channel into int16 and it works fine.
  • next I checked that Opus encoded frames before push into WebRTC are legit and they are, checked by decoding, saving to pcm, and playing.
  • the last bit in the webrtc.go where RTP audio packets are made. It seems the length of the payload should be equal (see the doc) to the length of the encoded audio frame (for 2.5ms/48kHz it's 120 and so on) and not to the length of raw Opus encoded bytes as it is right now.

After these fixes I was able to run the app with sound, but it's distorted. I think there is something with RTP opus packets or something on the client-side. No idea what to do next.
Maybe you have some (:?

Raw video with example (it's loud)

Can't start any game

I got some error when starting the server and can't start any game on localhost:8000.

2019/09/10 10:30:56 Listening at port: localhost:8000
2019/09/10 10:30:56 Running as worker
2019/09/10 10:30:56 Warn: Failed to create client: dialing: google: could not find default credentials. See https://developers.google.com/accounts/docs/application-default-credentials for more information.
2019/09/10 10:30:56 Listening at port: localhost: 9000
.....
.....
2019/09/10 10:31:04 === StartClient ===
2019/09/10 10:31:04 Start peerconnection 1b9e405b-1c5f-4e67-84a8-26d76d84d7de
2019/09/10 10:31:04 Error: Cannot create new webrtc session mDNS: failed to join multicast group
2019/09/10 10:31:04 Overlord: Received sdp request from a worker
2019/09/10 10:31:04 Overlord: Sending back sdp to browser
Connected

URI encode / decode JS bug for the latest room changes

When escape function in the controller.js:

copyToClipboard(window.location.href.split('?')[0] + `?id=${escape(roomID)}`)

escapes roomId___gameName then result link contains escaped characters for ;,/?:@&=+$, as example:

Legend of Zelda, The - A Link To The Past Four Swords (U) [!] -> 
Legend%20of%20Zelda%2C%20The%20-%20A%20Link%20To%20The%20Past%20Four%20Swords%20(U)%20%5B!%5D

but corresponding parser uses decodeURI function, which leaves described above characters escaped, so on the server side it won't find the game by its name and a worker just crashes:

I1022 17:06:32.121056   11960 browser.go:88] Received room response from browser:  5e3024468942a6fd___Legend of Zelda%2C The - A Link To The Past Four Swords (U) [!]
I1022 17:06:32.122056   11812 room.go:101] Room 5e3024468942a6fd___Legend of Zelda%2C The - A Link To The Past Four Swords (U) [!] started. GamePath: , GameName:
I1022 17:06:32.122056   11812 nanoarch.go:237] error loading , err 34

It can be fixed by using decodeURIComponent function instead.
I've already fixed it in the new frontend, but not sure when I'll be ready to PR, so wanna let you know about this bug.

Add aspect ratio support

Added proper nearest neighbour up/downscaling support with aspect ratio calculation.
Now it sets the initial viewport (output image) resolution to the base resolution from the loaded core upscaled by 2 to remove resampling artefacts (as on pictures below). So the manual resolution config for each core now removed completely.
A Sony BIOS intro now is being shown properly.
Need some testing.

Before
image

After
image

Local host can be connected over internet

There are host A, local host B and overlord(Master handles all session)

A person Y host a game on local host B with room_ID K
A person X connecting to host A. Person X enter room_ID K.
Host A inform person X to create new offer. Offer is sent to overlord -> local host B (by websocket event)
Localhost creates new webRTC session based on the offer, attach to the room with roomID K. SDP is sent back from localhost to overlord and from overlord to Host A.
Browser create new peerconnection based on the SDP.


Host A maintains 1 single websocket connection with X. When X change room, the WS connection is still there, only the peer connection is updated.
It means: A WS session from host A to X can handle peerconnection to different host Y!! .

WebRTC ice candidate errors

If you add ice candidates error handler, i.e.:

    pc.onicecandidateerror = (error) => {
        console.error(error);
    }

a bunch of messages will be shown in the console later
image

Does anyone have same errors with this handler or is it just an issue with my dev environment?

controller map is not same among platforms

Current supported

  • [Windows] Chrome
  • [Windows] Firefox
  • [Windows] Edge
  • [Mac] Chromium
  • [Mac] Firefox
  • [Mac] Safari
  • [Android] Firefox
  • [Android] UC Browser
  • [Android] Chromium : Chrome, Opera, Samsung Browser, Brave, ...

Not tested yet

  • Linux: ...
  • iOS: ...
  • Other platform: ...

Audio support

It seems there is no audio when you run games on the cloudretro.io or locally.
Maybe I missed that point in the docs, but does it support audio decode/playback right now?
Tried the latests versions:
W10 / Chrome (Stable, Dev, Canary),
Ubuntu 18 / Chrome (Stable),
Android 8 / Chrome (Stable).

Add Integration test

Test 1: On Single server

  1. Connect to server
  2. Create a room
  3. Send input

Test 2: On collaborative hosting

  1. Spawn Coordinator
  2. Spawn 2 server
  3. One server create a room. Get the room ID
  4. The other server try to connect with the roomID

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.