Skip to content

Commit

Permalink
Merge pull request #14888 from Faless/websocket
Browse files Browse the repository at this point in the history
Websocket module
  • Loading branch information
akien-mga authored Feb 7, 2018
2 parents 3cb1b22 + f3abd4a commit b0a7307
Show file tree
Hide file tree
Showing 72 changed files with 38,618 additions and 3 deletions.
7 changes: 4 additions & 3 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ env:
global:
- SCONS_CACHE=$HOME/.scons_cache
- SCONS_CACHE_LIMIT=1024
- OPTIONS="verbose=yes progress=no openmp=no gdnative_wrapper=yes"

cache:
directories:
Expand All @@ -18,10 +19,10 @@ matrix:
- env: STATIC_CHECKS=yes
os: linux
compiler: clang
- env: GODOT_TARGET=x11 TOOLS=yes CACHE_NAME=${GODOT_TARGET}-gcc-tools
- env: GODOT_TARGET=x11 TOOLS=yes CACHE_NAME=${GODOT_TARGET}-gcc-tools OPTIONS="${OPTIONS} builtin_openssl=yes"
os: linux
compiler: gcc
- env: GODOT_TARGET=x11 TOOLS=no CACHE_NAME=${GODOT_TARGET}-clang
- env: GODOT_TARGET=x11 TOOLS=no CACHE_NAME=${GODOT_TARGET}-clang OPTIONS="${OPTIONS} builtin_openssl=yes"
os: linux
compiler: clang
#- env: GODOT_TARGET=windows TOOLS=yes CACHE_NAME=${GODOT_TARGET}-gcc-tools
Expand Down Expand Up @@ -90,5 +91,5 @@ script:
- if [ "$STATIC_CHECKS" = "yes" ]; then
sh ./misc/travis/clang-format.sh;
else
scons -j2 CC=$CC CXX=$CXX platform=$GODOT_TARGET TOOLS=$TOOLS verbose=yes progress=no openmp=no gdnative_wrapper=yes;
scons -j2 CC=$CC CXX=$CXX platform=$GODOT_TARGET TOOLS=$TOOLS $OPTIONS;
fi
70 changes: 70 additions & 0 deletions modules/websocket/SCsub
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
#!/usr/bin/env python

Import('env')
Import('env_modules')

# Thirdparty source files

env_lws = env_modules.Clone()

thirdparty_dir = "#thirdparty/lws/"
helper_dir = "win32helpers/"
openssl_dir = "#thirdparty/openssl/"
thirdparty_sources = [
"client/client.c",
"client/client-handshake.c",
"client/client-parser.c",
"client/ssl-client.c",

"ext/extension.c",
"ext/extension-permessage-deflate.c",

"server/fops-zip.c",
"server/lejp-conf.c",
"server/parsers.c",
"server/ranges.c",
"server/server.c",
"server/server-handshake.c",
"server/ssl-server.c",

"misc/base64-decode.c",
"misc/lejp.c",
"misc/sha-1.c",

"alloc.c",
"context.c",
"handshake.c",
"header.c",
"libwebsockets.c",
"minilex.c",
"output.c",
"pollfd.c",
"service.c",
"ssl.c",

]

if env_lws["platform"] == "android": # Builtin getifaddrs
thirdparty_sources += ["misc/getifaddrs.c"]

if env_lws["platform"] == "windows": # Winsock
thirdparty_sources += ["plat/lws-plat-win.c", helper_dir + "getopt.c", helper_dir + "getopt_long.c", helper_dir + "gettimeofday.c"]
else: # Unix socket
thirdparty_sources += ["plat/lws-plat-unix.c"]


thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]

if env_lws["platform"] == "javascript": # No need to add third party libraries at all
pass
else:
env_lws.add_source_files(env.modules_sources, thirdparty_sources)
env_lws.Append(CPPPATH=[thirdparty_dir])

if env['builtin_openssl']:
env_lws.Append(CPPPATH=[openssl_dir])

if env_lws["platform"] == "windows":
env_lws.Append(CPPPATH=[thirdparty_dir + helper_dir])

env_lws.add_source_files(env.modules_sources, "*.cpp")
7 changes: 7 additions & 0 deletions modules/websocket/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@

def can_build(platform):
return True


def configure(env):
pass
224 changes: 224 additions & 0 deletions modules/websocket/emws_client.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,224 @@
/*************************************************************************/
/* emws_client.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifdef JAVASCRIPT_ENABLED

#include "emws_client.h"
#include "core/io/ip.h"
#include "emscripten.h"

extern "C" {
EMSCRIPTEN_KEEPALIVE void _esws_on_connect(void *obj, char *proto) {
EMWSClient *client = static_cast<EMWSClient *>(obj);
client->_is_connecting = false;
client->_on_connect(String(proto));
}

EMSCRIPTEN_KEEPALIVE void _esws_on_message(void *obj, uint8_t *p_data, int p_data_size, int p_is_string) {
EMWSClient *client = static_cast<EMWSClient *>(obj);

static_cast<EMWSPeer *>(*client->get_peer(1))->read_msg(p_data, p_data_size, p_is_string == 1);
client->_on_peer_packet();
}

EMSCRIPTEN_KEEPALIVE void _esws_on_error(void *obj) {
EMWSClient *client = static_cast<EMWSClient *>(obj);
client->_is_connecting = false;
client->_on_error();
}

EMSCRIPTEN_KEEPALIVE void _esws_on_close(void *obj, int code, char *reason, int was_clean) {
EMWSClient *client = static_cast<EMWSClient *>(obj);
client->_is_connecting = false;
client->_on_disconnect();
}
}

Error EMWSClient::connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, PoolVector<String> p_protocols) {

String str = "ws://";
String proto_string = "";
int i = 0;

if (p_ssl)
str = "wss://";
str += p_host + ":" + itos(p_port) + p_path;
for (int i = 0; i < p_protocols.size(); i++) {
proto_string += p_protocols[i];
proto_string += ",";
}
if (proto_string == "")
proto_string = "binary,";

proto_string = proto_string.substr(0, proto_string.length() - 1);

_is_connecting = true;
/* clang-format off */
int peer_sock = EM_ASM_INT({
var socket = new WebSocket(UTF8ToString($1), UTF8ToString($2).split(","));
var c_ptr = Module.IDHandler.get($0);
socket.binaryType = "arraybuffer";

// Connection opened
socket.addEventListener("open", function (event) {
if (!Module.IDHandler.has($0))
return; // Godot Object is gone!
ccall("_esws_on_connect",
"void",
["number", "string"],
[c_ptr, socket.protocol]
);
});

// Listen for messages
socket.addEventListener("message", function (event) {
if (!Module.IDHandler.has($0))
return; // Godot Object is gone!
var buffer;
var is_string = 0;
if (event.data instanceof ArrayBuffer) {

buffer = new Uint8Array(event.data);

} else if (event.data instanceof Blob) {

alert("Blob type not supported");
return;

} else if (typeof event.data === "string") {

is_string = 1;
var enc = new TextEncoder("utf-8");
buffer = new Uint8Array(enc.encode(event.data));

} else {

alert("Unknown message type");
return;

}
var len = buffer.length*buffer.BYTES_PER_ELEMENT;
var out = Module._malloc(len);
Module.HEAPU8.set(buffer, out);
ccall("_esws_on_message",
"void",
["number", "number", "number", "number"],
[c_ptr, out, len, is_string]
);
Module._free(out);
});

socket.addEventListener("error", function (event) {
if (!Module.IDHandler.has($0))
return; // Godot Object is gone!
ccall("_esws_on_error",
"void",
["number"],
[c_ptr]
);
});

socket.addEventListener("close", function (event) {
if (!Module.IDHandler.has($0))
return; // Godot Object is gone!
var was_clean = 0;
if (event.was_clean)
was_clean = 1;
ccall("_esws_on_close",
"void",
["number", "number", "string", "number"],
[c_ptr, event.code, event.reason, was_clean]
);
});

return Module.IDHandler.add(socket);
}, _js_id, str.utf8().get_data(), proto_string.utf8().get_data());
/* clang-format on */

static_cast<Ref<EMWSPeer> >(_peer)->set_sock(peer_sock);

return OK;
};

void EMWSClient::poll() {
}

Ref<WebSocketPeer> EMWSClient::get_peer(int p_peer_id) const {

return _peer;
}

NetworkedMultiplayerPeer::ConnectionStatus EMWSClient::get_connection_status() const {

if (_peer->is_connected_to_host())
return CONNECTION_CONNECTED;

if (_is_connecting)
return CONNECTION_CONNECTING;

return CONNECTION_DISCONNECTED;
};

void EMWSClient::disconnect_from_host() {

_peer->close();
};

IP_Address EMWSClient::get_connected_host() const {

return IP_Address();
};

uint16_t EMWSClient::get_connected_port() const {

return 1025;
};

EMWSClient::EMWSClient() {
_is_connecting = false;
_peer = Ref<EMWSPeer>(memnew(EMWSPeer));
/* clang-format off */
_js_id = EM_ASM_INT({
return Module.IDHandler.add($0);
}, this);
/* clang-format on */
};

EMWSClient::~EMWSClient() {

disconnect_from_host();
_peer = Ref<EMWSPeer>();
/* clang-format off */
EM_ASM({
Module.IDHandler.remove($0);
}, _js_id);
/* clang-format on */
};

#endif // JAVASCRIPT_ENABLED
62 changes: 62 additions & 0 deletions modules/websocket/emws_client.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*************************************************************************/
/* emws_client.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef EMWSCLIENT_H
#define EMWSCLIENT_H

#ifdef JAVASCRIPT_ENABLED

#include "core/error_list.h"
#include "emws_peer.h"
#include "websocket_client.h"

class EMWSClient : public WebSocketClient {

GDCIIMPL(EMWSClient, WebSocketClient);

private:
int _js_id;

public:
bool _is_connecting;

Error connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, PoolVector<String> p_protocol = PoolVector<String>());
Ref<WebSocketPeer> get_peer(int p_peer_id) const;
void disconnect_from_host();
IP_Address get_connected_host() const;
uint16_t get_connected_port() const;
virtual ConnectionStatus get_connection_status() const;
virtual void poll();
EMWSClient();
~EMWSClient();
};

#endif // JAVASCRIPT_ENABLED

#endif // EMWSCLIENT_H
Loading

0 comments on commit b0a7307

Please sign in to comment.