J3b: chat, the player list (with each player's land) and admin commands across lands; sleep needs everyone in every land, with a 'waiting for everyone else' message

This commit is contained in:
Paul Bailey
2026-11-06 23:42:50 +00:00
parent 85bbed249b
commit 86ebe07899
9 changed files with 404 additions and 27 deletions

View File

@@ -92,6 +92,7 @@ func server_join(peer: int, id: String, display: String) -> Vector3:
print("[server] %s's character loaded" % display)
if at != Vector3.INF:
p.global_position = at
beds_changed.call_deferred() # (one more awake: everyone's count changes)
return at
func place_at(peer: int, pos: Vector3) -> void:
@@ -212,6 +213,7 @@ func server_moved(peer: int, from: Vector3, to: Vector3, yaw: float, anim: int,
if p.in_bed and to.distance_to(p.bed_at) > 1.5:
p.in_bed = false
tell(p.peer, "You got out of bed")
beds_changed()
func _process(delta: float) -> void:
if not Net.is_server() or proxies.is_empty():
@@ -607,28 +609,66 @@ func go_to_bed(p: Node3D) -> void:
p.in_bed = true
p.bed_at = p.global_position
if not _maybe_pass_night():
var n: int = _alive().size()
var asleep: int = _alive().filter(func(q): return q.in_bed).size()
tell(p.peer, "You're in bed (%d of %d asleep). The night passes when %s." % [asleep, n,
"everyone's in bed" if _beds_needed(n) >= n else "%d are" % _beds_needed(n)])
for q in _alive():
if q != p:
tell(q.peer, "%s is in bed (%d of %d asleep)" % [p.display, asleep, n])
# (Counting everyone, in every land: on another land's server, the
# hub's last count, with this bed in it.)
var counts := sleep_counts()
if not Lands.land.is_empty():
counts = [_alive().size() + _hub_beds[0], _alive().filter(func(q): return q.in_bed).size() + _hub_beds[1]]
var n: int = counts[0]
var asleep: int = counts[1]
var need: int = _beds_needed(n)
tell(p.peer, waiting_text(asleep, n, need))
# Everyone else, in every land, hears it in chat (so whoever's still
# up knows they're being waited for).
Chat._all("%s is in bed (%d of %d asleep%s)" % [p.display, asleep, n,
"" if need >= n else ", %d needed" % need], "server")
## What a player who's just got into bed is told while the others are up.
static func waiting_text(asleep: int, n: int, need: int) -> String:
var left := need - asleep
var who := "everyone else" if need >= n else ("%d more" % left)
return "You're in bed: waiting for %s to go to bed (%d of %d asleep)" % [who, asleep, n]
func _alive() -> Array:
return proxies.values().filter(func(q): return not q.survival.dead)
## Everyone in bed (by the rule)? Then until 06:00 for all: the clock, fires,
## regrowth and weather move on; sleepers wake rested.
## One clock for every land (J3b): the night passes when enough of everyone,
## in every land, is in bed. Another land's server reports its beds to the
## hub, which decides, and tells every land to pass the night too.
func _maybe_pass_night() -> bool:
var w: Node3D = WorldNet.world
var live := _alive()
if w == null or live.is_empty():
if w == null:
return false
var asleep := live.filter(func(q): return q.in_bed)
if asleep.size() < _beds_needed(live.size()):
if not Lands.land.is_empty():
_report_beds()
return false
Lands._push_bed_totals()
var counts := sleep_counts()
if counts[0] == 0 or counts[1] < _beds_needed(counts[0]):
return false
var secs: float = fposmod(6.0 - w.day_night.hours(), 24.0) / 24.0 * w.day_night.day_length
Lands.tell_lands({"t": "night", "secs": secs})
pass_night_here(secs)
return true
## [alive, asleep] on this server and (the hub) every other land.
func sleep_counts() -> Array:
var live := _alive()
var counts := [live.size(), live.filter(func(q): return q.in_bed).size()]
if Lands.hub:
var e: Array = Lands.beds_elsewhere()
counts = [counts[0] + int(e[0]), counts[1] + int(e[1])]
return counts
## The night passes here: until 06:00 for everyone on this server.
func pass_night_here(secs: float) -> void:
var w: Node3D = WorldNet.world
if w == null:
return
var live := _alive()
var asleep := live.filter(func(q): return q.in_bed)
w.skip_time(secs)
for q in live:
if q.in_bed:
@@ -637,7 +677,20 @@ func _maybe_pass_night() -> bool:
_s2c_slept.rpc_id(q.peer, asleep.has(q))
Net.push_clock()
WorldNet.save()
return true
if not Lands.land.is_empty():
_report_beds()
## Someone got into or out of bed, or came or went: (a land server) the hub
## hears the new count; (the hub) the night may pass.
func beds_changed() -> void:
if not Lands.land.is_empty():
_report_beds()
elif Net.is_server():
_maybe_pass_night()
func _report_beds() -> void:
var live := _alive()
Lands.send_hub({"t": "beds", "alive": live.size(), "asleep": live.filter(func(q): return q.in_bed).size()})
## The chest or grave `nid`, if this player may use it right now.
func _container(p: Node3D, nid: int) -> Node3D:
@@ -648,6 +701,10 @@ func _container(p: Node3D, nid: int) -> Node3D:
const Rules = preload("res://scripts/net/rules.gd")
## (Land server) the hub's latest [alive, asleep] in every OTHER land, for
## the "N of M asleep" message and the all-abed check here.
var _hub_beds := [0, 0]
## Tells the player why not, if there's a reason.
func _allowed(why: String) -> bool:
if why.is_empty():