Ideas for plug-and-play local multiplayer on Godot (like the DS)

:down_arrow::down_arrow::down_arrow::down_arrow:
:raised_hand::neutral_face:First of all, I’d like to clarify that I posted this before but in the wrong part of the forum, my apologies for that (and YES I was too lazy to write the whole article myself :roll_eyes:) these are ideas to contribute to the indieDev community, so please enjoy!

Godot Version

4.2+

Question

:rocket:Is it possible to clone the Nintendo DS's 'Download Play' in Godot? (Multiplayer without installation)

Recreating “DS Download Play” in Godot Engine The idea of replicating the “DS Download Play” mode from the Nintendo DS in a modern environment is fascinating, as it enables local multiplayer without requiring each player to have the game pre-installed or a dedicated game server. Godot Engine, with its web export capabilities and built-in support for WebRTC, is an excellent tool to achieve this.

The main approach will be based on the combination of:

HTML5 export as PWA (Progressive Web App): To make the game accessible directly from a browser and allow it to be “installed” on the device.

WebRTC: To establish direct (peer-to-peer) connections between players’ devices.

Local Discovery Mechanisms (QR Codes, Bluetooth, or a combination): For initial signaling and session discovery.

  1. Game Export as PWA (Progressive Web App)
    To emulate game “downloading” without traditional app installation:

Why PWA? A PWA is a web application that can work offline, be added to the home screen (acting as an app icon), and receive automatic updates. This removes the need to upload your game to app stores and have users download it from there.

:gear: Setup in Godot:

In Godot, you can export your project to HTML5 (Project → Export → Add… → HTML5).

In the HTML5 export options, make sure to enable features that support PWA, such as Export PWA or at least the options that generate the manifest.json and service-worker.js.

The manifest.json defines how your PWA will look (name, icon, colors).

The service-worker.js is crucial for offline functionality and caching game resources. This file intercepts network requests and serves resources from the cache if the user is offline, allowing the game to start even without an internet connection.

Distribution: Simply upload the exported files (HTML, JS, WASM, assets, manifest, service worker) to any web server. Users access your game through a URL. Once opened, the browser may suggest “Add to Home Screen,” installing the PWA.

2. WebRTC for Peer-to-Peer Communication :wireless:
WebRTC is the key technology for direct communication between players’ devices.

WebRTCMultiplayerPeer in Godot:

Godot provides the WebRTCMultiplayerPeer class (available from Godot 3.2+ and improved in Godot 4), which abstracts much of WebRTC’s complexity.

This class allows you to set up a P2P connection between instances of your game.

You can configure your game as a “server” (host, which defaults to ID 1) or a “client.” In your scenario, the host would initiate the connection, and guests would join.

:gear:Data Channels:

WebRTC is not just for video and audio; its “Data Channels” are perfect for sending game data (player positions, firing events, game states, user inputs).

Different channels can be set up for various types of data (reliable/unreliable, ordered/unordered), adapting to the game’s needs. For example, character movements could go through an unreliable and unordered channel (UDP-like), while critical events like a “hit” would go through a reliable and ordered channel (TCP-like).

The Signaling Challenge: :laptop::desktop_computer::mobile_phone:

Although WebRTC is P2P for game data, it requires a “signaling server” for the initial handshake process. This server helps peers find each other and exchange crucial information like SDP (Session Description Protocol) offers/responses and ICE (Interactive Connectivity Establishment) candidates.

For your “serverless” game use case, this signaling server could be:

Public STUN/TURN servers: Godot allows you to configure STUN (Session Traversal Utilities for NAT) servers, which help peers discover their public IP addresses if they are behind NATs or firewalls. For a local game, these are often not strictly necessary if devices are on the same local network, but they can be useful. A TURN (Traversal Using Relays around NAT) server relays traffic if a direct connection is not possible, but it consumes bandwidth and server resources. In your case, you’d want to avoid TURN if possible.

Local Discovery Mechanism (see next section): This is where we “bypass” the need for a persistent signaling server for local interaction.

  1. Local Discovery and Signaling Mechanism
    This is the key to recreating the magic of “DS Download Play,” where the host announces the session, offering several alternatives.

:mobile_phone_with_arrow: General Connection Flow:

Host (Host Player): The host player launches the game (PWA) and selects “Create Local Game.” It generates a WebRTC SDP offer.

Guests (Client Players): The guest players open the same game URL (PWA) on their devices and select “Join Local Game.” They need to obtain the host’s SDP offer and then generate their own SDP response.

Connection Establishment: Once the SDP offer and response have been exchanged (with STUN assistance if needed), a direct WebRTC connection between peers is established.

Options for Discovery and Signaling Exchange:

Option A: QR Codes (Purely Offline)

Host generates QR: The host’s screen displays a large QR code containing the generated SDP offer.

Guests scan QR: The guest’s game scans the host’s QR code, extracting the SDP offer.

Guests generate Response and new QR: With that offer, the guest generates an SDP response, encodes it into a new QR code, and displays it on their screen.

Host scans second QR: The host scans this second QR code to obtain the guest’s SDP response.

Advantages: Completely offline once the game has loaded as a PWA. No external servers are required. Disadvantages: Requires users to perform two QR scans and physically move their devices.

Option B: :blue_circle: Bluetooth for Discovery (WebRTC Connection over Wi-Fi)
Role of Bluetooth: Bluetooth Low Energy (BLE) would be used for proximity detection and session announcement. It would not transfer heavy game data but serve as the initial signaling phase or simple session discovery.

Host Announcement via BLE: The host could use the Web Bluetooth API (which requires permissions and must run in a supported PWA environment like Chrome on Android) to broadcast a small BLE advertisement (like a “beacon”) containing a session ID and/or part of the WebRTC SDP offer.

Guests Scan BLE: Guests, also using Web Bluetooth, would scan nearby BLE advertisements. Upon detecting the host’s announcement, their game could display the available session in a list.

Signaling Exchange (WebRTC): Once a guest selects a discovered session via Bluetooth, the full exchange of WebRTC SDP offers/responses and ICE candidates would still take place, ideally over a local Wi-Fi network (direct or LAN) for efficiency. If Wi-Fi is unavailable, the bidirectional QR code method would serve as a fallback.

Advantages: Provides a nearly automatic session discovery with minimal friction, similar to the original DS experience. The initial connection phase is smoother. Disadvantages: Web Bluetooth support in PWAs is still limited on some browsers/platforms (especially iOS).

Option C: Hybrid Approach (Recommended for Best UX) :shuffle_tracks_button:
Discovery via Bluetooth: The host broadcasts a BLE advertisement containing a session ID. Guests detect it and see the available game session.

Confirmation/Signaling via QR: Once the guest selects the game session, the host displays a QR code containing their WebRTC SDP offer. The guest scans it, then generates their own SDP response and presents a QR code for the host to scan.

Game Communication via WebRTC: Gameplay data transmission occurs through WebRTC Data Channels, optimized for local/Wi-Fi networks.

Advantages: Combines Bluetooth’s seamless discovery with the reliability and offline nature of QR codes for crucial signaling while maintaining WebRTC’s performance for gameplay. Disadvantages: Still faces Web Bluetooth compatibility limitations and requires QR-based steps.

  1. Game Logic and Data Synchronization
    Once the WebRTC connection is established:

RPC (Remote Procedure Calls): Godot simplifies peer-to-peer data transfer using rpc() and rpc_id(). You can tag functions in your nodes to be remotely called on other peers.

Synchronization: Design your game to sync only necessary data. For fast-paced action games, sending player inputs and letting each client simulate gameplay (“client-side prediction” and “rollback”) can be smoother than constantly transmitting the full game state. For turn-based or slower games, sending the full state or critical events may be enough.

on_peer_connected/on_peer_disconnected: Monitor MultiplayerPeer signals to detect when players join or leave a session, updating the UI and game logic accordingly.

:thinking: Challenges and Considerations
STUN/TURN Servers: Although SDP exchange can be done via QR or Bluetooth for signaling, STUN servers are often necessary for WebRTC to work across different network setups (NAT traversal). You can use free public STUN servers (e.g., stun:stun.l.google.com:19302) configured in WebRTCMultiplayerPeer.

:selfie: User Experience: The process may be more manual compared to the original DS. A clear UI that guides players step by step is crucial.

:globe_with_meridians: Browser Compatibility: Ensure your target browsers support all WebRTC APIs and PWA features, as well as Web Bluetooth if you plan to use it.

:man_technologist: Handling Disconnections: Implement robust logic for player disconnections to prevent session crashes.

:warning: Security: For a local P2P game, security concerns are lower than for a centralized server-based game, but peer input validation should always be considered.


Godot-Centric Solution (No External Servers Needed)
Objective:

  • The host shares a mini-build of the game (exported from Godot).

  • Guests receive it via Bluetooth/LAN direct transfer (no internet, no STUN servers).

  • Zero installation: Guests use the same Godot executable as a “lightweight client.”

1. Project Structure

/game  
├── host/                # Full build (host)  
├── client/              # Ultra-light build (guests)  
└── shared/              # Common assets (e.g., sprites, sounds)  

Key Code (GDScript)
Host – Embedded Server

# host_server.gd
extends Node

var ble_server = BLEServer.new()
var http_server = TCPServer.new()
var demo_pck = preload("res://client/mini_game.pck")

func _ready():
    # Bluetooth Advertisement
    ble_server.advertise("GameSession", {"ip": get_local_ip()})
    
    # HTTP Server to Share .pck
    http_server.listen(8080)
    print("Scan the QR code or search 'GameSession' via Bluetooth")

func get_local_ip() -> String:
    # Retrieves the local IP for the QR code
    var ip = IP.get_local_addresses()
    return ip[0] if ip.size() > 0 else "127.0.0.1"

func _process(_delta):
    var peer = http_server.take_connection()
    if peer:
        peer.send_response(200, demo_pck)

Guest – Lightweight Client

# client_loader.gd
extends Node

func _on_scan_pressed():
    # Option A: Bluetooth
    var devices = BLEClient.scan()
    for device in devices:
        if device.name == "GameSession":
            download_game(device.data["ip"])

    # Option B: QR with IP
    # (Using the device's camera)

func download_game(host_ip: String):
    var http = HTTPClient.new()
    http.connect_to_host(host_ip, 8080)
    var pck_data = http.get_response_body()
    
    # Saves and loads the temporary package
    var temp_path = "user://temp_game.pck"
    FileAccess.save(temp_path, pck_data)
    ProjectSettings.load_resource_pack(temp_path)
    get_tree().change_scene_to_file("res://client/main.tscn")

Why This Outperforms WebRTC?

WebRTC Problem -----------------------| Godot Solution
Requires STUN servers ----------------| Direct LAN/Bluetooth connection
Complex initial handshake -------------| Local IP via QR/Bluetooth
Limitations on iOS/Android ------------| Native optimized builds

:hammer_and_wrench: Workflow
1. Export two versions in Godot:

  • Host: Full build.

  • Client: Multiplayer-only build (excluding unused assets).

2. Host starts the server:

  • Shares local IP via QR or advertises session via Bluetooth.

3. Guests download:

  • Max size 5MB: Only necessary scenes and assets.

  • Auto-deletes: When the game session ends.


Key Advantages
:white_check_mark: Fully offline: No need for STUN/TURN. :white_check_mark: Cross-platform: Godot exports to PC, Android, iOS. :white_check_mark: Secure: Verified peer-to-peer connections.


Real Example

# On the client, after loading the .pck
func start_mini_game():
    var multiplayer_scene = load("res://client/scenes/racing.tscn")
    get_tree().root.add_child(multiplayer_scene)

This setup keeps everything local, lightweight, and efficient, making it an ideal alternative to WebRTC for peer-to-peer game sessions in Godot. :rocket:

1 Like