extends SceneTree
## Hello World — GDScript side of the Hyperswarm interop test.
##
## Joins the live public DHT on a shared topic, discovers peers,
## and exchanges messages via protomux "godot-rpc" channel (type 0).
##
## Usage:
## godot4 --headless --path project --script res://hello.gd -- --topic hello-hyperswarm
##
## The --topic arg defaults to "hello-hyperswarm" if not provided.
const POLL_HZ := 0.05
const SEND_INTERVAL := 5.0
var _peer: Object
var _dht_kp: Dictionary
var _my_pk_hex := ""
var _topic_str := "hello-hyperswarm"
var _seq := 0
var _connected_peers: Dictionary = {}
func _init() -> void:
var args := OS.get_cmdline_user_args()
for i in args.size():
if args[i] == "--topic" and i + 1 < args.size():
_topic_str = args[i + 1]
_start.call_deferred()
func _start() -> void:
print("[hello-godot] topic: \"%s\"" % _topic_str)
print("[hello-godot] pid: %d" % OS.get_process_id())
# Create peer
_peer = ClassDB.instantiate(&"HyperswarmMultiplayerPeer")
# Generate DHT keypair
_dht_kp = _peer.generate_dht_keypair()
var pk: PackedByteArray = _dht_kp["public_key"]
_my_pk_hex = pk.hex_encode()
print("[hello-godot] pk: %s" % _my_pk_hex.left(12))
# Connect signals
_peer.peer_connected.connect(_on_peer_connected)
_peer.peer_disconnected.connect(_on_peer_disconnected)
# Join DHT — empty bootstrap = use hardcoded live DHT defaults
var empty_bootstrap := PackedStringArray()
var err: int = _peer.join_dht(pk, _dht_kp["secret_key"], empty_bootstrap)
if err != OK:
print("[hello-godot] FAIL: join_dht error %d" % err)
quit()
return
# Wait for DHT to become active
print("[hello-godot] joining live DHT...")
var elapsed := 0.0
while elapsed < 30.0:
_peer.poll()
if _peer.get_dht_status().get("active", false):
break
await create_timer(POLL_HZ).timeout
elapsed += POLL_HZ
if not _peer.get_dht_status().get("active", false):
print("[hello-godot] FAIL: DHT not active after 30s")
_peer.close()
quit()
return
print("[hello-godot] DHT active")
# Join swarm topic
var topic_hash: PackedByteArray = _topic_str.sha256_buffer()
print("[hello-godot] topic hash: %s..." % topic_hash.hex_encode().left(16))
err = _peer.swarm_join(topic_hash, true, true)
if err != OK:
print("[hello-godot] FAIL: swarm_join error %d" % err)
_peer.close()
quit()
return
print("[hello-godot] topic joined, waiting for peers...\n")
# Main poll loop — runs until Ctrl+C
var send_timer := 0.0
while true:
_peer.poll()
# Receive packets
while _peer.get_available_packet_count() > 0:
var pkt: PackedByteArray = _peer.get_packet()
var text: String = pkt.get_string_from_utf8()
print("[recv] %s" % text)
# Send every SEND_INTERVAL seconds
send_timer += POLL_HZ
if send_timer >= SEND_INTERVAL:
send_timer = 0.0
if _connected_peers.size() > 0:
_seq += 1
var payload: String = JSON.stringify({
"from": "godot",
"msg": "Hello World",
"seq": _seq,
})
_peer.set_target_peer(0)
_peer.set_transfer_mode(MultiplayerPeer.TRANSFER_MODE_RELIABLE)
_peer.put_packet(payload.to_utf8_buffer())
print("[send] %s" % payload)
await create_timer(POLL_HZ).timeout
func _on_peer_connected(id: int) -> void:
_connected_peers[id] = true
print("[connected] peer_id=%d (total: %d)" % [id, _connected_peers.size()])
func _on_peer_disconnected(id: int) -> void:
_connected_peers.erase(id)
print("[disconnected] peer_id=%d (total: %d)" % [id, _connected_peers.size()])