J3c: a land server stopping tells its players and is started afresh when they rejoin; the extra ports to forward on the Host page, the hosting message, the server log and HOSTING.md

This commit is contained in:
Paul Bailey
2026-11-07 00:08:05 +00:00
parent 86ebe07899
commit f9b138b304
9 changed files with 108 additions and 12 deletions

View File

@@ -65,6 +65,8 @@ func server_started(args: Dictionary) -> void:
if err != OK:
push_warning("[lands] couldn't open the private link on %d (error %d): no other lands this run" % [game_port() + LINK_PORT_OFFSET, err])
_link = null
elif not _order().is_empty():
print("[lands] other lands start on demand. Players over the internet need %s forwarded" % ports_text(home(), game_port()))
## Called as a server stops: stops every land server it started.
func server_stopped() -> void:
@@ -109,13 +111,39 @@ func home() -> String:
## Every land after home, in the order the journey reaches them.
func _order() -> Array:
return journey_from(home())
## The lands a server whose home is `home` and whose port is `base` may run,
## after home: [[land, port]] (24243 for the Cove when the server's 24242).
## Friends playing over the internet need these forwarded too.
static func extra_ports(home_land: String, base: int) -> Array:
var out: Array = []
var at := home()
var i := 1
for l in journey_from(home_land):
out.append([l, base + i])
i += 1
return out
## "UDP 24242 and 24243 (the Cove)": what to forward for a server on `base`.
static func ports_text(home_land: String, base: int) -> String:
var extra := extra_ports(home_land, base)
if extra.is_empty():
return "UDP %d" % base
var T: Script = load("res://scripts/world/terrain.gd")
var parts: Array[String] = []
for e in extra:
parts.append("%d (the %s)" % [int(e[1]), T.worldlet_name(String(e[0]))])
return "UDP %d and %s" % [base, ", ".join(parts)]
static func journey_from(home_land: String) -> Array:
var out: Array = []
var at := home_land
for _i in range(16):
var d: Dictionary = load("res://scripts/world/terrain.gd").load_worldlet(at)
var nxt := ""
for c in d.get("next", []):
if c is Dictionary and not String(c.get("to", "")) in out and String(c.get("to", "")) != home():
if c is Dictionary and not String(c.get("to", "")) in out and String(c.get("to", "")) != home_land \
and not load("res://scripts/world/terrain.gd").load_worldlet(String(c.get("to", ""))).is_empty():
nxt = String(c.to)
break
if nxt.is_empty():
@@ -148,6 +176,9 @@ func ensure(l: String, then: Callable) -> void:
if not hub:
then.call("this isn't the hub")
return
if servers.has(l) and int(servers[l].pid) != 0 and not OS.is_process_running(int(servers[l].pid)):
print("[lands] the %s's server has gone: starting it again" % l)
servers.erase(l) # (crashed, and not noticed yet: start afresh)
if servers.has(l) and bool(servers[l].ready):
servers[l].idle = 0.0
then.call("")

View File

@@ -963,6 +963,7 @@ func _on_server_disconnected() -> void:
if was == "connecting":
_fail(reason if not reason.is_empty() else "the server hung up") # (the next land wouldn't have us)
return # (the land we're leaving letting us go)
var land_name := on_land()
stop_hosting()
stop()
if was == "connecting":
@@ -970,8 +971,19 @@ func _on_server_disconnected() -> void:
join_failed.emit(last_reason)
else:
last_reason = reason if not reason.is_empty() else "The server closed the connection"
if reason.is_empty() and not land_name.is_empty():
# (Another land's server went: the rest of the server's still up,
# and joining again starts that land's server afresh.)
last_reason = "Lost the connection to the %s. Join the server again to carry on there." % \
preload("res://scripts/world/terrain.gd").worldlet_name(land_name)
left.emit(last_reason)
## Client: the land we're in when it's not the server's home land (we're on
## one of its other lands' servers), else "".
func on_land() -> String:
var here := String(world_info.get("worldlet", ""))
return here if not here.is_empty() and here != String(server_info.get("worldlet", here)) else ""
func _notification(what: int) -> void:
if what == NOTIFICATION_WM_CLOSE_REQUEST or what == NOTIFICATION_PREDELETE:
if _peer != null:

View File

@@ -426,7 +426,9 @@ func _fill_host() -> void:
_host_seed_label.text = "Seed" if not existing else "Seed (kept)"
_host_seed.text = "" if not existing else str(int(c.data.seed))
_show_host_worldlet(existing, meta_path)
_host_msg.text = "Your hosted island carries on where you left it." if existing else "A new island will be made."
_host_msg.text = ("Your hosted island carries on where you left it." if existing else "A new island will be made.") + \
"\nFriends on your network join at your address. Over the internet, forward %s on your router." % \
Lands.ports_text(picked_worldlet(_host_worldlet), int(c.data.port))
_host_msg.add_theme_color_override("font_color", UITheme.TEXT_DIM)
_host_btn.disabled = false

View File

@@ -1283,6 +1283,9 @@ func _go_online() -> void:
if Net.hosting:
var at := Net.my_addresses()
Actions.notify.emit("You're hosting. Friends join at %s" % (", ".join(PackedStringArray(at)) if not at.is_empty() else "this computer's address"))
var home_land := String(Net.server_info.get("worldlet", "valley"))
if not Lands.extra_ports(home_land, Net._host_port).is_empty():
Actions.notify.emit("Over the internet, forward %s" % Lands.ports_text(home_land, Net._host_port))
func _on_net_left(_reason: String) -> void:
WorldNet.leave_world()