Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multiplayer for web client. Closes GH-12 #36

Merged
merged 11 commits into from
Apr 4, 2017
Merged

Multiplayer for web client. Closes GH-12 #36

merged 11 commits into from
Apr 4, 2017

Conversation

satoshinm
Copy link
Owner

GH-12 Multiplayer support (connecting to servers) in web version

@satoshinm
Copy link
Owner Author

satoshinm commented Apr 3, 2017

/online localhost:

[Error] connect: Operation in progress
	printErr (craft.html:1251)
	put_char (craft.js:3706)
	write (craft.js:3626)
	write (craft.js:5656)
	doWritev (craft.js:6461)
	___syscall146 (craft.js:7309)
	___stdio_write (craft.js:381166)
	___overflow (craft.js:388784)
	_fputc (craft.js:391276)
	_perror (craft.js:391416)
	_client_connect (craft.js:10224)
	_main_init (craft.js:20473)
	_one_iter (craft.js:20865)
	dynCall_v (craft.js:396973)
	browserIterationFunc (craft.js:2115)
	runIter (craft.js:2232)
	Browser_mainLoop_runner (craft.js:2170)
[Error] exit(1) called, but noExitRuntime, so halting execution but not exiting the runtime or preventing further async execution (you can use emscripten_force_exit, if you want to force a true shutdown)
	printErr (craft.html:1251)
	exit (craft.js:397858)
	__exit (craft.js:6984)
	_exit (craft.js:6986)
	_client_connect (craft.js:10225)
	_main_init (craft.js:20473)
	_one_iter (craft.js:20865)
	dynCall_v (craft.js:396973)
	browserIterationFunc (craft.js:2115)
	runIter (craft.js:2232)
	Browser_mainLoop_runner (craft.js:2170)

Berkeley sockets connect() is sync, but the web is async. Emscripten APIs: http://kripken.github.io/emscripten-site/docs/api_reference/emscripten.h.html#socket-event-registration . library_sockfs connect: https://github.com/kripken/emscripten/blob/master/src/library_sockfs.js#L445 - bridges to websocket, always returns EINPROGRESS since its non-blocking. Asyncify? Change client to use nb sockets?

http://beej.us/guide/bgnet/output/html/multipage/faq.html

The gist of it is that you make a socket descriptor with socket(), set it to non-blocking, call connect(), and if all goes well connect() will return -1 immediately and errno will be set to EINPROGRESS. Then you call select() with whatever timeout you want, passing the socket descriptor in both the read and write sets. If it doesn't timeout, it means the connect() call completed. At this point, you'll have to use getsockopt() with the SO_ERROR option to get the return value from the connect() call, which should be zero if there was no error.

Finally, you'll probably want to set the socket back to be blocking again before you start transferring data over it.

Notice that this has the added benefit of allowing your program to do something else while it's connecting, too. You could, for example, set the timeout to something low, like 500 ms, and update an indicator onscreen each timeout, then call select() again. When you've called select() and timed-out, say, 20 times, you'll know it's time to give up on the connection.

Emscripten's socket tests/demos: https://github.com/kripken/emscripten/tree/master/tests/sockets

…rotocol)

This also eases proxying from websockets to the native server, for
example with https://github.com/novnc/websockify:

./websockify.py 4081 localhost:4080
@satoshinm
Copy link
Owner Author

satoshinm commented Apr 4, 2017

Progress, able to connect to a websockified server.py (using port 4081 to 4080). emscripten has some clever tricks up its sleeve, 172.19.x.x IP addresses as placeholders from gethostbyname() so the BSD socket APIs can be used to pass hostnames ("localhost") to the websocket APIs. This works, the major change vs native BSD sockets is the addition of emscripten socket callbacks.

connect() returns immediately with errno=EINPROGRESS, and calls the callback set by emscripten_set_socket_open_callback() when it is ready. Instead of a worker thread to blocking recv() in a loop, using emscripten_set_socket_message_callback() to recv then parse_buffer(). The receiving code is actually simpler, no need for a queue and buffering since websockets can deliver framed messages. Some work to do handling other callbacks but it is able to connect to a server, screenshot:

screen shot 2017-04-03 at 9 26 03 pm


Looping, client_message callback reads bytes 1468, 78, 7, then logs "Looks like you are rendering without using requestAnimationFrame for the main loop. You should use 0 for the frame rate in emscripten_set_main_loop in order to use requestAnimationFrame, as that can greatly improve your frame rates!" and client_message callback reads -1 bytes six times in a row, then client_closed callback is called.. and then back to client_opened, Logging in anonymously, and "[post-exception status] Exception thrown, see JavaScript console" but can't see it, "SimulateInfiniteLoop", then repeats client_message callbacks reading data. Infinite recursion?

Stack overflow! Attempted to allocate 49152 bytes on the stack, but stack has only 31040 bytes available!


The problem seems to be setting the main loop (emscripten_set_main_loop()) in the socket callback (emscripten_set_socket_open_callback()), main loop with simulate infinite loop raises exception SimulateInfiniteLoop, so the caller doesn't let code execute after it, simulating while(1);. Maybe not the right way to do this. There are other utility functions https://kripken.github.io/emscripten-site/docs/api_reference/emscripten.h.html to control the main loop: pause, resume, or push blockers. Can't pause the main loop before setting it, but setting runs it infinitely. emscripten_push_main_loop_blocker()?

@satoshinm
Copy link
Owner Author

satoshinm commented Apr 4, 2017

Changed to emscripten_push_main_loop_blocker(main_init, NULL);, seems to be how this API is supposed to be used. Once main_init() returns then the main loop can run. Fixes the infinite looping/recursion when connecting to online servers, offline still works, but now online mode doesn't continue rendering the game after connecting:

screen shot 2017-04-03 at 10 25 37 pm

however, the text console shows network messages are received and processed (player join, chat, etc). Something amiss in the main game loop after going online?

@satoshinm
Copy link
Owner Author

satoshinm commented Apr 4, 2017

Fixed looping from g_inner_break, set to 0 (note: can also test reinitializing with /offline), works! Web and native chatting together:

screen shot 2017-04-03 at 11 22 59 pm

And web and native looking at each other, nice:

screen shot 2017-04-03 at 11 28 37 pm

@satoshinm satoshinm changed the title [WIP] Multiplayer for web client Multiplayer for web client Apr 4, 2017
@satoshinm satoshinm changed the title Multiplayer for web client Multiplayer for web client. Closes GH-12 Apr 4, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant