Skip to content

Commit

Permalink
FINALLY! First working WebRTC multiplayer test.
Browse files Browse the repository at this point in the history
- Implement C++ signalling server integrated with the masterserver. Native server still use UDP for signalling for low overhead, while Web servers keep a WebSocket connection.
- Native and Web clients can play on the same server. Server can multiplex webrtc and native connections by giving webrtc clients imaginary address 127.255.255.25 and differentiating them by port.
- main_thread_queue: can execute tasks synchronously on the main thread for important operations
- fix freezes on tab switches when hosting an integrated server (Web). Use same remaining time truncation as on the client: server_time = std::max(server_time, current_time - advanced_dt * 5);
- fix std::size_t being serialized causing binary incompatibility between 32-bit web and 64 bit clients (causing a bug in net_solvable_stream, among others)
- Pass ICE servers to both server and client from detail/web/stun_server_list.txt.
- Preliminary /game/id and /host implementation
  • Loading branch information
geneotech committed Apr 21, 2024
1 parent 75e59ec commit 188389f
Show file tree
Hide file tree
Showing 57 changed files with 2,119 additions and 700 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/Linux_build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ concurrency:

jobs:
build:
if: "!contains(github.event.head_commit.message, '[ci skip]')"
if: "!contains(github.event.head_commit.message, '[ci skip]') && !contains(github.event.head_commit.message, '[ds only]')"
# The CMake configure and build commands are platform agnostic and should work equally well on Windows or Mac.
# You can convert this to a matrix build if you need cross-platform coverage.
# See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/MacOS_build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ concurrency:

jobs:
build:
if: "!contains(github.event.head_commit.message, '[ci skip]')"
if: "!contains(github.event.head_commit.message, '[ci skip]') && !contains(github.event.head_commit.message, '[ds only]')"
runs-on: macos-latest

steps:
Expand Down
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,6 @@
[submodule "src/3rdparty/libdatachannel"]
path = src/3rdparty/libdatachannel
url = https://github.com/paullouisageneau/libdatachannel.git
[submodule "src/3rdparty/datachannel-wasm"]
path = src/3rdparty/datachannel-wasm
url = https://github.com/paullouisageneau/datachannel-wasm.git
33 changes: 29 additions & 4 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,8 @@ endif()
option(BUILD_OPENAL "Build OpenAL Soft." ${DEFAULT_OPT})
option(BUILD_OPENGL "Build OpenGL-related code." ${DEFAULT_OPT})
option(BUILD_NETWORKING "Build networking." ${DEFAULT_NET_OPT})
option(BUILD_MASTERSERVER "Build the masterserver." ${DEFAULT_NET_OPT})
option(BUILD_NATIVE_SOCKETS "Native socket operations like ping and NAT traversal. Disabled for the web." ${DEFAULT_NET_OPT})
option(BUILD_WEBRTC "Build WebRTC for client and server. This will enable networking on the Web and cross-play between web and native clients." ${DEFAULT_NET_OPT})
option(BUILD_OPENSSL "Build SSL support. Required for secure automatic application updates." ${DEFAULT_NET_OPT})
option(BUILD_FREETYPE "Build FreeType library responsible for loading fonts." ${DEFAULT_OPT})
Expand Down Expand Up @@ -250,14 +252,20 @@ if (NOT BUILD_NETWORKING)
set(BUILD_WEBRTC OFF)
endif()

if (BUILD_FOR_WEB OR HYPERSOMNIA_BARE)
if (BUILD_FOR_WEB)
# Slowly enabling one by one
set(BUILD_FREETYPE ON)
set(BUILD_SOUND_FORMAT_DECODERS ON)

set(BUILD_OPENGL ON)
set(BUILD_OPENAL ON)
set(BUILD_WINDOW_FRAMEWORK ON)

set(BUILD_NETWORKING ON)
set(BUILD_WEBRTC ON)
set(BUILD_MASTERSERVER OFF)
set(BUILD_NATIVE_SOCKETS OFF)
set(BUILD_UNIT_TESTS OFF)
endif()

## Static allocation switches. Possible values are one or zero.
Expand Down Expand Up @@ -521,12 +529,17 @@ if(BUILD_NETWORKING)
"src/application/setups/server/server_setup.cpp"
"src/application/network/network_adapters.cpp"
"src/augs/network/network_types.cpp"
"src/application/masterserver/masterserver.cpp"
"src/application/nat/nat_detection_session.cpp"
"src/application/nat/nat_traversal_session.cpp"
"src/application/setups/server/server_nat_traversal.cpp"
)

if (BUILD_MASTERSERVER)
list(APPEND HYPERSOMNIA_NETWORKING_CPPS
"src/application/masterserver/masterserver.cpp"
)
endif()

if(HAS_HEAD)
list(APPEND HYPERSOMNIA_NETWORKING_CPPS
"src/application/setups/client/client_setup.cpp"
Expand Down Expand Up @@ -1723,6 +1736,14 @@ if(BUILD_NETWORKING)
add_definitions(-DBUILD_NETWORKING=1)
endif()

if(BUILD_MASTERSERVER)
add_definitions(-DBUILD_MASTERSERVER=1)
endif()

if(BUILD_NATIVE_SOCKETS)
add_definitions(-DBUILD_NATIVE_SOCKETS=1)
endif()

if(BUILD_WEBRTC)
add_definitions(-DBUILD_WEBRTC=1)
endif()
Expand Down Expand Up @@ -1984,6 +2005,8 @@ if(BUILD_WEBRTC)

if(BUILD_FOR_WEB)
add_subdirectory("${PROJECT_SOURCE_DIR}/src/3rdparty/datachannel-wasm" EXCLUDE_FROM_ALL)
target_compile_options(datachannel-wasm PRIVATE -Wno-unused-parameter)

list(APPEND HYPERSOMNIA_LIBS datachannel-wasm)
else()
add_subdirectory("${PROJECT_SOURCE_DIR}/src/3rdparty/libdatachannel" EXCLUDE_FROM_ALL)
Expand Down Expand Up @@ -2075,10 +2098,12 @@ endif()
# Add libsodium

if (BUILD_NETWORKING)
if (MSVC)
if (MSVC OR BUILD_FOR_WEB)
file(GLOB_RECURSE SODIUM_SOURCE_FILES "src/3rdparty/yojimbo/sodium/*.c")

set_source_files_properties(${SODIUM_SOURCE_FILES} PROPERTIES COMPILE_FLAGS "-mssse3 -msse4.1")
if (NOT BUILD_FOR_WEB)
set_source_files_properties(${SODIUM_SOURCE_FILES} PROPERTIES COMPILE_FLAGS "-mssse3 -msse4.1")
endif()
add_library(sodium STATIC ${SODIUM_SOURCE_FILES})
list(APPEND HYPERSOMNIA_LIBS sodium)
else()
Expand Down
79 changes: 0 additions & 79 deletions cmake/web/signaling-server.py

This file was deleted.

2 changes: 1 addition & 1 deletion cmake/web_shell.html
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@
</style>
</head>
<body>
<div class="center-container">
<div oncontextmenu="return false;" class="center-container">
<div class="spinner" id="spinner"></div>
<div class="downloading-text" id="downloading-text">Downloading... 0%</div>
<div class="progress-bar" id="progress-bar">
Expand Down
89 changes: 89 additions & 0 deletions docs/pages/todo/brainstorm_now.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,95 @@ permalink: brainstorm_now
summary: That which we are brainstorming at the moment.
---

- fix clipoard on web
- fix tip order in tutorial about fast reloads etc move it to later

- browser adjustments for web servers
- show webrtc instead of id
- dont try to ping and show as active

- setup stun/ice/turn servers

- wtf is that crosshair glitch when hosting via 'srv'
- have to move mouse right away somewhere far

- we could implement a pause mode when we detect no packets from the server for more than 1 sec

- mapping can be done in signalling server easily, only use this map during relay when mapping dest id to peer
- native servers should be able to specify a webrtc "alias" in their heartbeat
- can be honored just on init for simplicity
- will be useful for official servers and other new dedicated ones
- /game/us though could pick the best US server
- /game might just choose the best one always
- we can do aliasing later, for now it's enough to have ad hoc aliases for official servers on startup

- for correct destructions, we may capture not "this" but a shared ptr to callback contexts.
- likely the entire struct should be in shared ptr. then we should lock in all callbacks

- hide the counter on runtime errors

- for better grenade/knife accuracy:
- throw them from the right hand instead of left (the hand holding the weapon)
- zero the recoil

- server list nuance with web clients
- native servers must still use sending udp packets
- we can later easy implement challenge responses via "heartbeat tickets"
- just send back a ticket every time a heartbeat is requested, first one won't pass but that's okay because we'll get a response instantly and we can act on it too
- so still resolve and dont forget to check if its resolved
- use parsed_url
- native servers can't prove their webrtc id
- but that's not much of a problem, if they give a fake one nobody will be able to connect to them
- we're not identifying by it
- web clients will show a different port with every http request
- we'll need a separate server list
- or encode webrtc id as port!!! hell yea
- now they can spoof but only within their own addr
- no.. they might overshadow native servers under their address
- so a separate server list after all
- cant show a reliable ip/port (port doesnt make sense here) on site so just show webrtc id
- if nat is non-public, we may default to using webrtc-only connections on servers too
- opt out of webrtc support on native servers by providing empty signalling server url
- by definition it's only WEB clients that will use the http route.
- and they have to have the constant connection to it.
- So we should just send all server heartbeats through the websocket
- and have the masterserver recognize them by correct webrtc id
- we'll have to send the web client info in server list as 0.0.0.0 because there's no point even pinging them since we dont know the port anyway
- permalinks
- hypersomnia.io/game/gns should join "gns" game or create it if one doesnt exist
- note this means all servers native or not might as well use http
- NO! Note native clients dont need websocket connection!
- They can simply exchange udp packets with the signalling server!!!

- Let's make it as simple as possible
- Native server has no websocket. Data goes through udp to the same port as heartbeats.
- Native client can always use websocket since it's temporary.
- Every server, native or web, has a webrtc id.
- Native server doesnt request an id - no point. Will just be allocated automatically
- Web server requests through wss:// location.
- So no point holding it in the heartbeat.
- Only web client and server use websockets.
- Masterserver:
- Knows that http connection list is strictly separate from udp heartbeat list.
- has to have a map from webrtc id to either udp address or socket.. to know how to send data
- note: native servers dont need a werbtc id. They can be identified through the source ip address, and web clients can send this ip as the "destination id" to the signalling server.
- for native clients wanting to use webrtc anyway (to get around nat) we can still use that ip as webrtc id or use link web://4.4.4.4 etc. The decision happens on the client when they choose to either open websocket to the signal server or go to straight udp connection right away.


- TO SUMMARIZE
- MS keeps a map of native servers as usual; handles them as usual (dont even extract that lambda, no point)
- SS has a separate map of web rtc to web peer; which might be a server or not
- Whenever a heartbeat occurs or a peer is removed it sets an atomic that it needs to be reserialized
- Check for this atomic in the main loop then set a mutex and reserialize
- Or SS can just cal a callback directly to reserialize everything since serialized lists are mutexed already with serialized_list_mutex
- just make sure to properly lock the web peer list then and AVOID RECURSIVE LOCK becuase reserializer might want to lock it too
- Web servers need to know what id they are
- to generate correct links when someone hosts a server via /host (without specified id)
- just a simple message back through websocket, maybe json
- Let MS resolve official addresses

- fix clipboard

- hide settings in web that can crash the app like number of threads
- resolve_address -> address_utils
- consider having short ids for peers so links don't take much space and are easily transferred without copying (e.g. in school)
Expand Down
14 changes: 14 additions & 0 deletions docs/pages/todo/bug_database.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,20 @@ permalink: bug_database
summary: Notable bugs.
---

- server likely shouldn't stop when changing tab
- do workers still run?
- we're out of luck because packets will still not be sent so it will still stall/timeout

- why the f is there nat traversal when connecting to 127.0.0.1?
- it works like this with a local masterserver - 127.0.0.1 is reported as external ip and is subsequently found on the server list as an entry when connecting.
- Fixed by disabling for internal addresses

- test what happens when server list is empty
- works

- fix memory leaks when closing webrtc connections (both client and server)
- done: using shared_ptr/weak_ptr, destructing and all important ops executed in the main thread

- Nasty interpolation glitch when connecting -
- Referential cosmos needs to be snapped as well because we begin from spectator view which shows referential cosmos by default.
- the only "bug" left is when you go spectate during warmup since your player disappears and you go back to spectating at origin (0, 0) pos
Expand Down
28 changes: 28 additions & 0 deletions docs/pages/todo/todo_bugs.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,34 @@ permalink: todo_bugs
summary: Just a hidden scratchpad.
---

- RARE, but there still is some leak in
-
ws.onClosed([wself = std::weak_ptr(self)]() {
if (auto self = wself.lock()) {
self->set_message("WebSocket closed.");
}
});
- even though self must still exist
- Also when reconnecting client:
DOMException: The operation failed for an operation-specific reason
_rtcSendMessage http://localhost:6931/Hypersomnia.js:17228
browserIterationFunc http://localhost:6931/Hypersomnia.js:15583
callUserCallback http://localhost:6931/Hypersomnia.js:6708
runIter http://localhost:6931/Hypersomnia.js:7352
Browser_mainLoop_runner http://localhost:6931/Hypersomnia.js:7251
requestAnimationFrame http://localhost:6931/Hypersomnia.js:7647
Browser_mainLoop_scheduler_rAF http://localhost:6931/Hypersomnia.js:7119

17228 was: dataChannel.send(byteArray);
- also when closing server:
[04:34:57] server stopped Hypersomnia.js:2612:16
<empty string>
Invalid UTF-8 leading byte 0xffffffaa encountered when deserializing a UTF-8 string in wasm memory to a JS string!
[04:34:57] h򘄰ێ7] Hypersomnia.js:2612:16
[04:34:57] All demos are already compressed.
- this suggests that "this" already doesnt exist but onclosed is called only later somehow. - we assign to this->message and LOG it, which results in junk.
- what if theres more than one reference count for this?

- WebGL glitches with a lot of vertices.
- implement imgui call in terms of glDrawArrays to avoid glitches
- glitchuje przy (cnt*3=11454, sizeof(vertex_triangle) * cnt=229080)
Expand Down
2 changes: 1 addition & 1 deletion hypersomnia/content/menu/tutorial/tutorial.json
Original file line number Diff line number Diff line change
Expand Up @@ -12264,7 +12264,7 @@
"ambience_particles": {
"scale_amounts": 0.30000001192092896
},
"context_tip": "ALL surfaces can bounce bullets - including wood!\nKill 2 enemies covering behind orange boxes."
"context_tip": "ALL surfaces can bounce bullets - including wood!\nKill 2 enemies that hide behind orange boxes."
},
{
"id": "hazard (5)",
Expand Down
9 changes: 5 additions & 4 deletions hypersomnia/default_config.lua
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,13 @@ return {
ip = "0.0.0.0",
ssl_cert_path = "",
ssl_private_key_path = "",
signalling_ssl_cert_path = "",
signalling_ssl_private_key_path = "",
suppress_community_server_webhooks_after_launch_for_secs = 20,
server_entry_timeout_secs = 65,
signalling_peer_timeout_secs = 25,

signalling_server_port = 8000,
first_udp_command_port = 8430,
num_udp_command_ports = 30,

Expand Down Expand Up @@ -876,10 +880,7 @@ treat_as_music_sounds_longer_than_secs = 5,

allow_nat_traversal = true,

notified_server_list = {
address = "masterserver.hypersomnia.xyz",
default_port = 8430
},
notified_server_list = "masterserver.hypersomnia.xyz:8430",

send_heartbeat_to_server_list_once_every_secs = 10,
resolve_server_list_address_once_every_secs = 3600,
Expand Down
2 changes: 2 additions & 0 deletions hypersomnia/detail/web/webrtc_ice_servers.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
stun.l.google.com:19302
stun.1und1.de:3478
1 change: 1 addition & 0 deletions src/3rdparty/datachannel-wasm
Submodule datachannel-wasm added at 1eef8b
Loading

0 comments on commit 188389f

Please sign in to comment.