Steps to Configure and Export from GODOT for Browser (HTML build)

Computer augmented development with AI.

Trying out AI in Visual Studio Code.

Copilot in VSCode


GitHub Copilot: Understood. To target the browser runtime in Godot 4, you need to ensure your project is configured correctly for Web export. Here’s how you can proceed:

  1. Enable WebRTC Support:
  • Ensure your Godot build includes WebRTC support. Official Godot builds typically have this enabled.
  1. Project Settings:
  • Open the Godot editor.
  • Go to Project > Project Settings.
  • Under the Display category, set Window > Width and Height to fit typical browser resolutions (e.g., 1280×720).
  • Ensure HTML5 settings are configured properly:
    • HTML5 > Canvas Resize Policy: Set to Expand for responsive scaling.
    • HTML5 > Audio Worklet: Enable if your app uses audio.
  1. Export Template:
  • Install the HTML5 export templates if not already installed. You can download them from the Godot website.
  1. Export Configuration:
  • Go to Project > Export.
  • Add a new preset for HTML5.
  • Configure the export settings:
    • Set Custom HTML Shell if you need a custom HTML wrapper.
    • Enable Threads and Networking under the Options tab for WebRTC functionality.
  1. Testing in Browser:
  • Export the project to a .zip or .html file.
  • Use a local web server (e.g., Python’s http.server or Node.js http-server) to serve the exported files. Browsers block some features (like WebRTC) when running directly from the file system.
  1. WebRTC-Specific Adjustments:
  • Ensure your signaling server (SIGNAL_SERVER_URL) is accessible over HTTPS, as most browsers require secure contexts for WebRTC.
  • Test the WebRTC connection in multiple browsers to ensure compatibility.

Optimization for Browser:

  • Reduce Asset Sizes: Compress textures, audio, and other assets to minimize load times.
  • Minimize Script Execution: Avoid heavy computations in _process or _physics_process.
  • Debugging: Use browser developer tools to debug WebRTC connections and inspect network traffic.

Let me know if you need help with any specific part of this process.

edo9a: I’m 48 years young, adjust your tone to that

GitHub Copilot: Got it. Let’s focus on optimizing your WebRTC-based Godot app for browser runtime. Here’s what you should consider:

Key Adjustments for Browser Runtime

  1. WebRTC Compatibility:
  • Ensure your signaling server (SIGNAL_SERVER_URL) is accessible over HTTPS. Browsers require secure contexts for WebRTC.
  • Test in multiple browsers (Chrome, Firefox, etc.) since WebRTC implementations can vary.
  1. Reduce Polling Overhead:
  • Your poll_signals function uses HTTP GET requests on a timer. This can be resource-intensive in a browser. Consider using WebSockets for real-time signaling if your server supports it. WebSockets are more efficient for bidirectional communication.
  1. Error Handling:
  • Add user-friendly error messages for common WebRTC issues (e.g., connection failures, ICE candidate issues). This is especially important for browser users who might not have access to a console.
  1. Responsive UI:
  • Ensure your UI scales well in the browser. Use Control nodes with anchors and margins set for responsive layouts.
  1. Optimize Asset Loading:
  • Compress assets (textures, audio, etc.) to reduce load times. Use formats like .webp for images and .ogg for audio.
  1. Debugging in Browser:
  • Use browser developer tools to monitor network requests and WebRTC connections. Look for errors in the console or network tab.

Specific Code Suggestions

Replace Polling with WebSockets (Optional)

If your server supports WebSockets, you can replace the polling mechanism with a WebSocket connection. Here’s a basic example:

# Replace HTTP polling with WebSocket
@onready var websocket := WebSocketClient.new()

func _ready():
    websocket.connect("connection_established", self, "_on_websocket_connected")
    websocket.connect("data_received", self, "_on_websocket_data_received")
    websocket.connect("connection_closed", self, "_on_websocket_closed")
    websocket.connect("connection_error", self, "_on_websocket_error")

    var err = websocket.connect_to_url("wss://your-signaling-server-url")
    if err != OK:
        push_error("WebSocket connection failed: %d" % err)

func _on_websocket_connected(protocol: String):
    print("WebSocket connected with protocol:", protocol)

func _on_websocket_data_received():
    var data = websocket.get_peer(1).get_packet().get_string_from_utf8()
    print("Data received:", data)
    # Handle incoming signals here

func _on_websocket_closed(was_clean: bool):
    print("WebSocket closed. Clean:", was_clean)

func _on_websocket_error():
    push_error("WebSocket error occurred")

Improve Error Feedback

Add more detailed error messages for users in the browser:

func push_error(message: String):
    print("❌ Error:", message)
    chat_log.append_text("[Error]: " + message + "\n")

Optimize _process for Chat

Avoid unnecessary polling in _process. Use events or signals to handle incoming messages instead of continuously checking data_channel.get_available_packet_count().


Let me know if you want to dive deeper into any of these areas!

edo9a: ok, only PHP and HTTPS (apache, shared hosting) available, no websockets for signaling server side

GitHub Copilot: Understood. Since you’re limited to PHP and HTTPS on shared hosting without WebSockets, you’ll need to stick with HTTP polling for signaling. Here’s how you can optimize and adapt your current setup:


1. Optimize Polling Frequency

Reduce the polling frequency to balance responsiveness and server load. For example, increase the timer interval in _on_fetch_timer_timeout:

# Increase polling interval to reduce server load
fetch_timer.wait_time = 2.0  # Poll every 2 seconds instead of the default
fetch_timer.start()

2. Improve Signal Handling on the Server

Ensure your PHP signaling server:

  • Returns only new signals since the last poll.
  • Uses a lightweight database or file-based storage to track signals (e.g., SQLite or JSON files).
  • Deletes processed signals to avoid redundant responses.

3. Add Signal Acknowledgment

To prevent duplicate signals, include a unique signal_id in each signal. The client can track processed signals to avoid reprocessing:

Client-Side:

Modify _on_request_completed to track processed signals:

var processed_signals := []

func _on_request_completed(result: int, code: int, headers: PackedStringArray, body: PackedByteArray):
    if result != OK or code != 200:
        push_error("HTTP error: %d " % result)
        return

    var json := JSON.new()
    if json.parse(body.get_string_from_utf8()) != OK:
        push_error("JSON parse failed")
        return

    var data = json.data
    if not data.has("signals"): return

    for incoming_signal in data["signals"]:
        var signal_id = incoming_signal.get("id", "")
        if signal_id in processed_signals:
            continue  # Skip already processed signals

        processed_signals.append(signal_id)

        var type = incoming_signal.get("type", "")
        var payload = incoming_signal.get("payload", {})

        match type:
            "offer":
                _handle_offer(payload)
            "ice":
                _handle_ice(payload)
            _:
                print("Unknown signal type:", type)

4. Optimize HTTP Requests

Use gzip compression on your server to reduce the size of HTTP responses. Ensure your PHP server is configured to compress JSON responses.


5. Secure the Communication

Since you’re using HTTPS:

  • Ensure your PHP server validates incoming requests (e.g., check from and to fields).
  • Use a shared secret or token to authenticate requests between clients and the server.

6. PHP Signaling Server Example

Here’s a basic PHP signaling server for your setup:

<?php
// filepath: signal.php

// Load signals from a JSON file (or database)
$signals_file = 'signals.json';
$signals = file_exists($signals_file) ? json_decode(file_get_contents($signals_file), true) : [];

// Handle POST requests (sending signals)
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $input = json_decode(file_get_contents('php://input'), true);
    if (!isset($input['from'], $input['to'], $input['type'], $input['payload'])) {
        http_response_code(400);
        echo json_encode(['error' => 'Invalid request']);
        exit;
    }

    // Add the signal to the recipient's queue
    $signals[] = [
        'id' => uniqid(), // Unique signal ID
        'from' => $input['from'],
        'to' => $input['to'],
        'type' => $input['type'],
        'payload' => $input['payload']
    ];

    file_put_contents($signals_file, json_encode($signals));
    echo json_encode(['status' => 'success']);
    exit;
}

// Handle GET requests (polling for signals)
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
    $id = $_GET['id'] ?? '';
    if (empty($id)) {
        http_response_code(400);
        echo json_encode(['error' => 'Missing ID']);
        exit;
    }

    // Fetch signals for the client
    $client_signals = array_filter($signals, function ($signal) use ($id) {
        return $signal['to'] === $id;
    });

    // Remove delivered signals from the queue
    $signals = array_filter($signals, function ($signal) use ($id) {
        return $signal['to'] !== $id;
    });

    file_put_contents($signals_file, json_encode($signals));

    echo json_encode(['signals' => array_values($client_signals)]);
    exit;
}

http_response_code(405);
echo json_encode(['error' => 'Method not allowed']);

7. Testing

  • Test the signaling flow with multiple clients to ensure signals are delivered and processed correctly.
  • Monitor server performance to ensure the polling interval is appropriate.

Let me know if you need further adjustments!

edo9a: var json := JSON.new()
var data := json.data

  • consider it wrning elevated to error. substitute?

GitHub Copilot: The warning occurs because json.data is not a valid property of the JSON object. Instead, you should use the get_data() method after parsing the JSON. Here’s the corrected code:

var json := JSON.new()
if json.parse(body.get_string_from_utf8()) != OK:
    push_error("JSON parse failed")
    return

var data = json.get_data()  # Correct way to access parsed JSON data
if not data.has("signals"): return

This ensures you’re accessing the parsed JSON data properly and avoids the warning.


I’m a solo dev. I want to develop in PHP backend for Godot apps (games). My PHP server is on shared hosting and I can’t and don’t want to add more libraries to it

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *