Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save DmitriySalnikov/ce12ff100df4e3352176768f5232abfa to your computer and use it in GitHub Desktop.
Save DmitriySalnikov/ce12ff100df4e3352176768f5232abfa to your computer and use it in GitHub Desktop.
Running a web project exported from Godot Engine with `GDExtension` support on a local network

Running a web project exported from Godot Engine with GDExtension support on a local network

If you want to test the exported project not only on localhost (for example on your phone), then you'll need to run an HTTPS file server with COOP and COEP (Exporting for the Web). Also don't forget to activate extension support:

image

To run HTTPS, you'll need an SSL key and certificate, which can be generated using one of the scripts below (openssl generate ssl key&cert file.*) or you can use an existing certificate that is distributed with this archive.

You need Python 3 installed and its path must be added to the environment variables so that it can be run from anywhere. Python can be pre-installed on your Linux.

  • Click Download ZIP at the top of the page.
  • Unpack the archive somewhere.
  • Now you can run ssl_server.py. Use the -h argument to get a list of available arguments.
ssl_server.py
#or
python ssl_server.py

This is what a command with multiple arguments might look like:

ssl_server.py --host "192.168.88.88" -p 8000 -r "C:/Users/Public" --cert_and_key "ssl.pem"

Generating ssl.pem

On Windows, you'll need to install OpenSSL. You can find the installer here. OpenSSL may be pre-installed on your Linux. OpenSSL should also be added to the environment variables.

Now run the certificate and key generation script. .bat file for Windows and .sh file for Linux and macOS.

After that, ssl.pem will be updated.

Do not use ssl.pem from this archive in your releases. This file is generated only for testing on a local network.

@echo off
for /f "delims=" %%i in ('where openssl') do (
set "OPENSSL_PATH=%%~dpi"
goto :found
)
echo OpenSSL not found
goto :eof
:found
echo Found OpenSSL on this path: %OPENSSL_PATH%
@echo on
cd %OPENSSL_PATH%
openssl req -newkey rsa:4096 -x509 -sha256 -days 3650 -nodes -out %~dp0ssl.pem -keyout %~dp0ssl.pem -subj "/C=RU/ST=Open/L=SSL/O=User/OU=Person/CN=localhost"
openssl req -newkey rsa:4096 -x509 -sha256 -days 3650 -nodes -out ssl.pem -keyout ssl.pem -subj "/C=RU/ST=Open/L=SSL/O=User/OU=Person/CN=localhost"
-----BEGIN PRIVATE KEY-----
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
kCD7BMOeClUYVVEGlbme1sL8qOc6PZU=
-----END PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
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
-----END CERTIFICATE-----
#!/usr/bin/env python3
# Based on platform/web/serve.py from the Godot repository
from http.server import HTTPServer, SimpleHTTPRequestHandler # type: ignore
from pathlib import Path
import os, sys, ipaddress, ssl, argparse, subprocess
any_ip = "0.0.0.0"
class CORSRequestHandler(SimpleHTTPRequestHandler):
def end_headers(self):
self.send_header("Cross-Origin-Opener-Policy", "same-origin")
self.send_header("Cross-Origin-Embedder-Policy", "require-corp")
self.send_header("Access-Control-Allow-Origin", "*")
super().end_headers()
def shell_open(url: str):
if sys.platform == "win32":
os.startfile(url)
else:
opener = "open" if sys.platform == "darwin" else "xdg-open"
subprocess.call([opener, url])
def start_server(root: Path, host: str, port: int, run_browser: bool, cert_and_key: Path):
os.chdir(root)
if not os.path.isfile(cert_and_key):
print(f"The certificate and key file does not exist on this path: {cert_and_key.absolute().as_posix()}")
exit(1)
if not os.path.isdir(root):
print(f"The root directory does not exist: {root.absolute().as_posix()}")
exit(1)
try:
if host != "localhost":
ip_object = ipaddress.ip_address(host)
except ValueError:
print(f"The IP address '{host}' is not valid")
exit(1)
if port <= 0 or port > 2**16:
print(f"The port '{port}' is not valid")
exit(1)
server_address = (host, port)
httpd = HTTPServer(server_address, CORSRequestHandler)
httpd.socket = ssl.wrap_socket(
httpd.socket,
server_side=True,
certfile=cert_and_key,
keyfile=cert_and_key,
ssl_version=ssl.PROTOCOL_TLSv1_2,
)
shell_host = host if host != any_ip else "localhost"
if run_browser:
print("Opening the served URL in the default browser (use `--no-browser` or `-n` to disable this).")
shell_open(f"https://{shell_host}:{port}")
print(f"File Server started at https://{host}:{port}")
if host == any_ip:
print("You can connect to this server from any available IP")
print("Use Ctrl+C or Ctrl+Break to close the server")
httpd.serve_forever()
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("--host", help="host to listen on", default=any_ip, type=str)
parser.add_argument("-p", "--port", help="port to listen on", default=8064, type=int)
parser.add_argument(
"-r",
"--root",
help="path to serve as root",
default="",
type=Path,
)
parser.add_argument(
"--cert_and_key",
help="path to the combined certificate and key file",
default=os.path.dirname(os.path.abspath(__file__)) + "/ssl.pem",
type=Path,
)
browser_parser = parser.add_mutually_exclusive_group(required=False)
browser_parser.add_argument(
"-n", "--no-browser", help="don't open default web browser automatically", dest="browser", action="store_false"
)
parser.set_defaults(browser=True)
args = parser.parse_args()
start_server(args.root, args.host, args.port, args.browser, args.cert_and_key)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment