M2.5: wildlife, wolves, hunting, fishing and sleeping on the server
This commit is contained in:
@@ -109,8 +109,43 @@ func _do(cmd: Dictionary) -> Dictionary:
|
||||
net.leave()
|
||||
await create_timer(0.3).timeout
|
||||
return {"left": true}
|
||||
# M2.5 (tests/test_net_life.gd).
|
||||
"tp":
|
||||
root.get_node("PlayerNet").ask("debug_tp", [_p(cmd.to)])
|
||||
await create_timer(0.6).timeout
|
||||
player.set_physics_process(false)
|
||||
return {"pos": _v(player.global_position)}
|
||||
"ask":
|
||||
var args: Array = []
|
||||
for x in cmd.get("args", []):
|
||||
args.append(_p(x.v) if x is Dictionary and x.has("v") else x)
|
||||
root.get_node("PlayerNet").ask(String(cmd.what), args)
|
||||
await create_timer(0.4).timeout
|
||||
return {"inv": player.inventory.to_data()}
|
||||
"bed":
|
||||
var b: Node3D = WN.pieces.get(int(cmd.nid))
|
||||
if b == null:
|
||||
return {"error": "no bed %d" % int(cmd.nid)}
|
||||
root.get_node("Actions").use_bed(player, b)
|
||||
await create_timer(0.5).timeout
|
||||
return {"asked": true}
|
||||
"life":
|
||||
return life(world, root.get_node("CreatureNet"), root.get_node("PlayerNet"))
|
||||
return {"error": "unknown command"}
|
||||
|
||||
## What this player's game shows of the server's wildlife.
|
||||
static func life(w: Node3D, CN: Node, PN: Node) -> Dictionary:
|
||||
var out := {"puppets": {}, "floats": [], "projectiles": CN.projectiles_seen, "howls": CN.howls_heard,
|
||||
"hurts": PN.hurts, "health": w.player.survival.health, "day": w.day_night.day,
|
||||
"hours": w.day_night.hours(), "statuses": w.player.survival.statuses.duplicate()}
|
||||
for cid in CN.puppets:
|
||||
var c: Node3D = CN.puppets[cid]
|
||||
if is_instance_valid(c):
|
||||
out.puppets[str(cid)] = {"kind": c.kind, "pos": _v(c.global_position), "state": c.state, "hp": c.hp}
|
||||
for peer in CN.floats:
|
||||
out.floats.append(peer)
|
||||
return out
|
||||
|
||||
# Shared with test_net_shared.gd (it loads this script for these).
|
||||
|
||||
## Walks (by setting positions, like the game sending its moves) at 5 m/s.
|
||||
|
||||
@@ -9,7 +9,7 @@ if [ ! -f .godot/global_script_class_cache.cfg ]; then
|
||||
echo "Importing the project first (fresh copy)..."
|
||||
godot --headless --path . --import >/dev/null 2>&1
|
||||
fi
|
||||
for t in test_survival_core test_survival_stats test_save_load test_weather_building test_creatures_combat test_wolves test_den_sites test_fishing_diet test_net_lobby test_net_world test_net_shared test_terrain_edges test_boundary; do
|
||||
for t in test_survival_core test_survival_stats test_save_load test_weather_building test_creatures_combat test_wolves test_den_sites test_fishing_diet test_net_lobby test_net_world test_net_shared test_net_life test_terrain_edges test_boundary; do
|
||||
extra=""
|
||||
[ "$t" = "test_boundary" ] && extra="--fixed-fps 30"
|
||||
# A suite that waits on a world that failed to load would hang forever,
|
||||
|
||||
412
tests/test_net_life.gd
Normal file
412
tests/test_net_life.gd
Normal file
@@ -0,0 +1,412 @@
|
||||
extends SceneTree
|
||||
## M2.5: wildlife on a server. The server runs the animals and the wolves;
|
||||
## both players see the same ones. Hunting (club and bow), butchering,
|
||||
## fishing and sleeping all happen on the server, and a wolf pack hunts two
|
||||
## players at once.
|
||||
##
|
||||
## A real headless server (with --debug-cheats, so the test can set the
|
||||
## clock, give items, spawn an animal and move players), Alice (this test,
|
||||
## with the real world scene) and Bob (tests/net_world_bot.gd, another
|
||||
## process with his own world, following this test's commands).
|
||||
## Run: godot --headless --path . --script res://tests/test_net_life.gd
|
||||
|
||||
const PORT := 24348
|
||||
const DIR := "user://test_net"
|
||||
const SRV := DIR + "/server6"
|
||||
const BOB := DIR + "/bob_life"
|
||||
const SEED := 777
|
||||
const Bot = preload("res://tests/net_world_bot.gd")
|
||||
|
||||
var _fails := 0
|
||||
var _finished := false
|
||||
var _pids: Array[int] = []
|
||||
var Net: Node
|
||||
var WN: Node
|
||||
var PN: Node
|
||||
var CN: Node
|
||||
var A: Node
|
||||
var world: Node3D
|
||||
var player: CharacterBody3D
|
||||
var _cmd := 0
|
||||
var _toasts: Array[String] = []
|
||||
|
||||
func check(cond: bool, what: String) -> void:
|
||||
print((" ok " if cond else " FAIL ") + what)
|
||||
if not cond:
|
||||
_fails += 1
|
||||
|
||||
func _initialize() -> void:
|
||||
_run.call_deferred()
|
||||
|
||||
func _run() -> void:
|
||||
Engine.max_fps = 60
|
||||
Net = root.get_node("Net")
|
||||
WN = root.get_node("WorldNet")
|
||||
PN = root.get_node("PlayerNet")
|
||||
CN = root.get_node("CreatureNet")
|
||||
A = root.get_node("Actions")
|
||||
A.notify.connect(func(t): _toasts.append(t))
|
||||
for d in [SRV, BOB]:
|
||||
_rm_tree(d)
|
||||
DirAccess.make_dir_recursive_absolute(BOB)
|
||||
_spawn(["--server", "--port", str(PORT), "--config", SRV + "/server.json",
|
||||
"--server-name", "Life Test", "--seed", str(SEED), "--debug-cheats"])
|
||||
await _sleep(4.0)
|
||||
await _body()
|
||||
Net.stop()
|
||||
for pid in _pids:
|
||||
if OS.is_process_running(pid):
|
||||
OS.kill(pid)
|
||||
check(_finished, "test ran to the end (no script errors)")
|
||||
_rm_tree(SRV)
|
||||
_rm_tree(BOB)
|
||||
print("RESULT: %s (%d failures)" % ["PASS" if _fails == 0 else "FAIL", _fails])
|
||||
quit(1 if _fails > 0 else 0)
|
||||
|
||||
func _body() -> void:
|
||||
var me = load("res://scripts/net/identity.gd").open(DIR + "/a")
|
||||
me.name = "Alice"
|
||||
Net.identity = me
|
||||
var r := await _join()
|
||||
check(r == "", "Alice joins (%s)" % r)
|
||||
if r != "":
|
||||
return
|
||||
world = load("res://scenes/world.tscn").instantiate()
|
||||
root.add_child(world)
|
||||
for _i in range(8):
|
||||
await physics_frame
|
||||
player = world.player
|
||||
player.set_physics_process(false)
|
||||
await _until(func(): return WN.received >= 1, 10.0)
|
||||
_spawn(["--identity", DIR + "/bob", "--name", "Bob", "--address", "127.0.0.1:%d" % PORT, "--dir", BOB],
|
||||
"res://tests/net_world_bot.gd")
|
||||
var b0 := await _bob_result(0, 30.0)
|
||||
check(bool(b0.get("ok", false)), "Bob joins (%s)" % b0.get("why", "ok"))
|
||||
if not bool(b0.get("ok", false)):
|
||||
return
|
||||
PN.ask("debug_stats", [{"health": 100.0, "hunger": 90.0}])
|
||||
await _wildlife()
|
||||
await _club()
|
||||
await _bow()
|
||||
await _fishing()
|
||||
await _sleeping()
|
||||
await _wolves()
|
||||
world.queue_free()
|
||||
await process_frame
|
||||
Net.leave()
|
||||
_finished = true
|
||||
|
||||
# --- the same animals for everyone ------------------------------------------------
|
||||
|
||||
func _wildlife() -> void:
|
||||
print("the server's wildlife")
|
||||
await _until(func(): return CN.puppets.size() >= 5, 8.0)
|
||||
check(CN.puppets.size() >= 5, "animals appear around Alice: %d, from the server" % CN.puppets.size())
|
||||
check(not world.creatures.enabled and world.creatures.alive().is_empty(), "none of them are her game's own")
|
||||
# (Bob may only just have joined: give his first snapshots a moment.)
|
||||
var life := await _bob({"do": "life"})
|
||||
for _i in range(10):
|
||||
if (life.puppets as Dictionary).size() >= 5:
|
||||
break
|
||||
await _sleep(0.3)
|
||||
life = await _bob({"do": "life"})
|
||||
var same := 0
|
||||
var close := 0
|
||||
for cid in CN.puppets:
|
||||
var mine: Node3D = CN.puppets[cid]
|
||||
var his: Dictionary = life.puppets.get(str(cid), {})
|
||||
if his.is_empty():
|
||||
continue
|
||||
same += 1
|
||||
if _p(his.pos).distance_to(mine.global_position) < 3.0:
|
||||
close += 1
|
||||
check(same >= mini(CN.puppets.size(), life.puppets.size()) - 1 and same >= 3,
|
||||
"Bob sees the same ones (%d in common of %d / %d)" % [same, CN.puppets.size(), life.puppets.size()])
|
||||
check(close >= same - 1, "in the same places (%d of %d within 3 m)" % [close, same])
|
||||
|
||||
## A boar on the server, `dist` metres in front of Alice. Returns her view of it.
|
||||
func _boar(dist: float) -> Node3D:
|
||||
var fwd := -player.global_basis.z
|
||||
fwd.y = 0.0
|
||||
fwd = fwd.normalized()
|
||||
var at := player.global_position + fwd * dist
|
||||
at.y = world.terrain.sample_height(at.x, at.z)
|
||||
var before: Array = CN.puppets.keys()
|
||||
PN.ask("debug_spawn", ["boar", at])
|
||||
var found := {"c": null} # (lambdas capture locals by value: use a dictionary)
|
||||
await _until(func():
|
||||
for cid in CN.puppets:
|
||||
if not cid in before and CN.puppets[cid].kind == "boar":
|
||||
found.c = CN.puppets[cid]
|
||||
return true
|
||||
return false, 3.0)
|
||||
return found.c
|
||||
|
||||
func _club() -> void:
|
||||
print("a club and a boar")
|
||||
var site: Vector3 = world.build.debug_find_site(player.global_position + Vector3(10, 0, 0), 3.0)
|
||||
await _tp(site)
|
||||
await _give("club", 1)
|
||||
player.select(_slot_of("club"))
|
||||
await _sleep(0.3)
|
||||
var boar := await _boar(2.2)
|
||||
check(boar != null, "a boar on the server shows up in front of Alice")
|
||||
if boar == null:
|
||||
return
|
||||
var cid := int(boar.get_meta("cid"))
|
||||
var hurts0: int = PN.hurts
|
||||
var swings := 0
|
||||
var t := 0.0
|
||||
while not boar.is_dead() and t < 15.0:
|
||||
var to: Vector3 = boar.global_position - player.global_position
|
||||
player.rotation.y = atan2(-to.x, -to.z)
|
||||
if to.length() > 2.2:
|
||||
await Bot.walk(player, boar.global_position - to.normalized() * 1.6, world)
|
||||
A.melee(player, boar)
|
||||
swings += 1
|
||||
await _sleep(0.6)
|
||||
t += 0.6
|
||||
check(boar.is_dead(), "Alice clubs it to death on the server (%d swings)" % swings)
|
||||
check(PN.hurts > hurts0, "it fought back: the server's boar gored her (%d hits)" % (PN.hurts - hurts0))
|
||||
var life := await _bob({"do": "life"})
|
||||
var his: Dictionary = life.puppets.get(str(cid), {})
|
||||
check(String(his.get("state", "")) == "dead", "Bob sees the carcass too")
|
||||
var meat0: int = player.inventory.count("raw_meat")
|
||||
A.butcher(player, boar)
|
||||
await _until(func(): return player.inventory.count("raw_meat") > meat0, 2.0)
|
||||
check(player.inventory.count("raw_meat") >= meat0 + 3 and player.inventory.count("hide") >= 1,
|
||||
"she butchers it: %d meat and %d hide from the server" % [player.inventory.count("raw_meat") - meat0, player.inventory.count("hide")])
|
||||
await _until(func(): return not CN.puppets.has(cid), 3.0)
|
||||
check(not CN.puppets.has(cid), "and the carcass is gone")
|
||||
|
||||
func _bow() -> void:
|
||||
print("a bow")
|
||||
PN.ask("debug_stats", [{"health": 100.0, "stamina": 100.0}])
|
||||
await _give("bow", 1)
|
||||
await _give("arrow", 10)
|
||||
player.select(_slot_of("bow"))
|
||||
await _sleep(0.3)
|
||||
var boar := await _boar(9.0)
|
||||
check(boar != null, "another boar, 9 m off")
|
||||
if boar == null:
|
||||
return
|
||||
var cid := int(boar.get_meta("cid"))
|
||||
var seen0: int = CN.projectiles_seen
|
||||
var bob0: Dictionary = await _bob({"do": "life"})
|
||||
var shots := 0
|
||||
while boar.hp >= 60.0 - 0.01 and shots < 4:
|
||||
var aim: Vector3 = (boar.global_position + Vector3(0, 0.55, 0)) - (player.global_position + Vector3(0, 1.6, 0))
|
||||
player.rotation.y = atan2(-aim.x, -aim.z)
|
||||
if A.can_draw(player):
|
||||
A.shoot(player, Vector3.ZERO, aim.normalized(), 1.0, world)
|
||||
shots += 1
|
||||
await _sleep(1.0)
|
||||
check(boar.hp < 60.0, "an arrow hits it (%d shot%s, it's at %.0f HP on Alice's screen)" % [shots, "" if shots == 1 else "s", boar.hp])
|
||||
check(player.inventory.count("arrow") == 10 - shots, "the server took %d arrow%s from her quiver" % [shots, "" if shots == 1 else "s"])
|
||||
check(CN.projectiles_seen > seen0, "she saw her arrow fly")
|
||||
var life := await _bob({"do": "life"})
|
||||
check(int(life.projectiles) > int(bob0.projectiles), "so did Bob")
|
||||
var his: Dictionary = life.puppets.get(str(cid), {})
|
||||
check(float(his.get("hp", 60.0)) < 60.0, "and on his screen it's hurt too (%.0f HP)" % float(his.get("hp", 60.0)))
|
||||
|
||||
func _fishing() -> void:
|
||||
print("fishing")
|
||||
var lake: Vector2 = world.terrain.lake_center
|
||||
var c3 := Vector3(lake.x, 0, lake.y)
|
||||
var dir := (player.global_position - c3)
|
||||
dir.y = 0.0
|
||||
dir = dir.normalized()
|
||||
var shore := c3
|
||||
for i in range(200):
|
||||
shore = c3 + dir * float(i)
|
||||
if world.terrain.sample_height(shore.x, shore.z) > world.terrain.water_level + 0.4:
|
||||
break
|
||||
shore += dir * 1.0
|
||||
shore.y = world.terrain.sample_height(shore.x, shore.z)
|
||||
await _tp(shore)
|
||||
player.rotation.y = atan2(dir.x, dir.z) # facing the water
|
||||
await _give("fishing_rod", 1)
|
||||
player.select(_slot_of("fishing_rod"))
|
||||
await _sleep(0.3)
|
||||
var caught := false
|
||||
var tries := 0
|
||||
while not caught and tries < 3:
|
||||
tries += 1
|
||||
A.cast_line(player, Vector3.ZERO, -dir + Vector3(0, -0.1, 0), world)
|
||||
await _until(func(): return world.bobber != null and world.bobber.state == "floating", 4.0)
|
||||
if tries == 1:
|
||||
check(world.bobber != null and world.bobber.state == "floating", "Alice casts: the server's float lands on the lake, and she sees it")
|
||||
var life := await _bob({"do": "life"})
|
||||
check((life.floats as Array).any(func(f): return int(f) == Net.local_peer), "Bob sees her float too")
|
||||
await _until(func(): return world.bobber != null and world.bobber.state == "biting", 25.0)
|
||||
if world.bobber != null and world.bobber.state == "biting":
|
||||
var fish0: int = player.inventory.count("perch") + player.inventory.count("trout")
|
||||
A.reel(player, world)
|
||||
await _until(func(): return player.inventory.count("perch") + player.inventory.count("trout") > fish0, 2.0)
|
||||
caught = player.inventory.count("perch") + player.inventory.count("trout") > fish0
|
||||
check(caught, "a bite, she reels in, and the server gives her the fish (%d cast%s)" % [tries, "" if tries == 1 else "s"])
|
||||
player.select(0)
|
||||
await _sleep(0.5)
|
||||
check(world.bobber == null, "the line is in")
|
||||
|
||||
func _sleeping() -> void:
|
||||
print("sleeping (the server's rule: everyone online in bed)")
|
||||
PN.ask("debug_time", [1, 21.0])
|
||||
var site: Vector3 = world.build.debug_find_site(player.global_position + Vector3(0, 0, 14), 3.0)
|
||||
var n0: int = WN.pieces.size()
|
||||
PN.ask("debug_hut", [site])
|
||||
await _until(func(): return _beds().size() >= 2, 8.0)
|
||||
var beds := _beds()
|
||||
check(beds.size() >= 2 and WN.pieces.size() > n0 + 10, "a hut with two beds (%d pieces)" % (WN.pieces.size() - n0))
|
||||
if beds.size() < 2:
|
||||
return
|
||||
await _tp(beds[0].global_position + Vector3(0, 0.1, 0.9))
|
||||
await _bob({"do": "tp", "to": _v(beds[1].global_position + Vector3(0, 0.1, 0.9))})
|
||||
check(world.day_night.hours() > 20.5, "it's night (%s)" % world.day_night.clock_text())
|
||||
_toasts.clear()
|
||||
A.use_bed(player, beds[0])
|
||||
await _sleep(0.5)
|
||||
A.use_bed(player, beds[0])
|
||||
await _sleep(1.0)
|
||||
check(_toasts.any(func(t): return t.begins_with("You're in bed (1 of 2")), "Alice gets into bed: 1 of 2 asleep (%s)" % [_toasts])
|
||||
check(world.day_night.hours() > 20.5, "and the night goes on, because Bob's still up")
|
||||
await _bob({"do": "bed", "nid": WN.nid(beds[1])})
|
||||
await _bob({"do": "bed", "nid": WN.nid(beds[1])})
|
||||
await _until(func(): return world.day_night.hours() < 7.0, 3.0)
|
||||
check(world.day_night.hours() >= 5.9 and world.day_night.hours() < 7.0 and world.day_night.day == 2,
|
||||
"Bob gets into bed too, and the night passes for both (%s, day %d)" % [world.day_night.clock_text(), world.day_night.day])
|
||||
await _until(func(): return "Rested" in player.survival.statuses, 2.0)
|
||||
check(_toasts.has("You slept until morning") and "Rested" in player.survival.statuses, "Alice wakes rested")
|
||||
var life := await _bob({"do": "life"})
|
||||
check(float(life.hours) >= 5.9 and float(life.hours) < 7.0, "Bob's clock says morning too (%.1f h)" % float(life.hours))
|
||||
|
||||
func _beds() -> Array:
|
||||
return WN.pieces.values().filter(func(p): return is_instance_valid(p) and p.item_id == "bed")
|
||||
|
||||
func _wolves() -> void:
|
||||
print("a wolf pack hunts both players")
|
||||
PN.ask("debug_time", [5, 12.0]) # by day the pack moves into the den...
|
||||
await _sleep(1.5)
|
||||
PN.ask("debug_time", [5, 22.0]) # ...and at night it hunts
|
||||
PN.ask("debug_stats", [{"health": 100.0, "warmth": 80.0}])
|
||||
await _bob({"do": "ask", "what": "debug_stats", "args": [{"health": 100.0, "warmth": 80.0}]})
|
||||
var den: Node3D = world.den
|
||||
check(den.found, "this island has a den (seed %d)" % SEED)
|
||||
if not den.found:
|
||||
return
|
||||
var out: Vector3 = Basis(Vector3.UP, den.yaw) * Vector3(0, 0, 1)
|
||||
var side := out.cross(Vector3.UP)
|
||||
var a: Vector3 = den.mouth_pos() + out * 16.0
|
||||
var b: Vector3 = den.mouth_pos() + out * 16.0 + side * 10.0
|
||||
a.y = world.terrain.sample_height(a.x, a.z)
|
||||
b.y = world.terrain.sample_height(b.x, b.z)
|
||||
await _tp(a)
|
||||
await _bob({"do": "tp", "to": _v(b)})
|
||||
var a0: int = PN.hurts
|
||||
var life0 := await _bob({"do": "life"})
|
||||
var b0 := int(life0.hurts)
|
||||
var t := 0.0
|
||||
var bob_hurts := b0
|
||||
while t < 45.0 and (PN.hurts == a0 or bob_hurts == b0):
|
||||
await _sleep(1.0)
|
||||
t += 1.0
|
||||
if int(t) % 3 == 0:
|
||||
var l := await _bob({"do": "life"})
|
||||
bob_hurts = int(l.hurts)
|
||||
var wolves: Array = CN.puppets.values().filter(func(c): return c.kind == "wolf")
|
||||
check(wolves.size() >= 2, "Alice sees the pack: %d wolves, from the server" % wolves.size())
|
||||
check(PN.hurts > a0, "the wolves bite Alice (%d)" % (PN.hurts - a0))
|
||||
check(bob_hurts > b0, "and Bob, 10 m away, at the same time (%d), after %.0f s" % [bob_hurts - b0, t])
|
||||
check(player.survival.health < 100.0, "Alice's health is down on her screen (%.0f)" % player.survival.health)
|
||||
|
||||
# --- helpers --------------------------------------------------------------------
|
||||
|
||||
func _tp(at: Vector3) -> void:
|
||||
PN.ask("debug_tp", [at + Vector3(0, 0.05, 0)])
|
||||
await _until(func(): return player.global_position.distance_to(at) < 1.0, 2.0)
|
||||
player.set_physics_process(false)
|
||||
await _sleep(0.3)
|
||||
|
||||
func _give(id: String, n: int) -> int:
|
||||
var before: int = player.inventory.count(id)
|
||||
PN.ask("debug_give", [id, n])
|
||||
await _until(func(): return player.inventory.count(id) >= before + n, 3.0)
|
||||
return _slot_of(id)
|
||||
|
||||
func _slot_of(id: String) -> int:
|
||||
for i in range(player.inventory.size()):
|
||||
var s = player.inventory.get_slot(i)
|
||||
if s != null and s.id == id:
|
||||
return i
|
||||
return -1
|
||||
|
||||
func _until(cond: Callable, limit: float) -> bool:
|
||||
var t := 0.0
|
||||
while not cond.call() and t < limit:
|
||||
await _sleep(0.05)
|
||||
t += 0.05
|
||||
return cond.call()
|
||||
|
||||
static func _v(p: Vector3) -> Array:
|
||||
return [p.x, p.y, p.z]
|
||||
|
||||
static func _p(a: Array) -> Vector3:
|
||||
return Vector3(a[0], a[1], a[2])
|
||||
|
||||
func _bob(cmd: Dictionary) -> Dictionary:
|
||||
_cmd += 1
|
||||
var f := FileAccess.open("%s/cmd_%d.json" % [BOB, _cmd], FileAccess.WRITE)
|
||||
f.store_string(JSON.stringify(cmd))
|
||||
f.close()
|
||||
return await _bob_result(_cmd, 20.0)
|
||||
|
||||
func _bob_result(n: int, limit: float) -> Dictionary:
|
||||
var path := "%s/res_%d.json" % [BOB, n]
|
||||
var t := 0.0
|
||||
while not FileAccess.file_exists(path) and t < limit:
|
||||
await _sleep(0.05)
|
||||
t += 0.05
|
||||
await _sleep(0.05)
|
||||
var d: Variant = JSON.parse_string(FileAccess.get_file_as_string(path)) if FileAccess.file_exists(path) else null
|
||||
return d if d is Dictionary else {"error": "Bob gave no answer to command %d" % n, "puppets": {}, "floats": [],
|
||||
"projectiles": 0, "hurts": 0, "hours": -1.0}
|
||||
|
||||
func _join() -> String:
|
||||
var state := {"done": false, "reason": ""}
|
||||
var ok := func(): state.done = true
|
||||
var bad := func(why): state.done = true; state.reason = why
|
||||
Net.joined.connect(ok)
|
||||
Net.join_failed.connect(bad)
|
||||
Net.join("127.0.0.1:%d" % PORT)
|
||||
var t := 0.0
|
||||
while not state.done and t < 25.0:
|
||||
await _sleep(0.05)
|
||||
t += 0.05
|
||||
Net.joined.disconnect(ok)
|
||||
Net.join_failed.disconnect(bad)
|
||||
return state.reason if state.done else "test timed out"
|
||||
|
||||
func _spawn(user_args: Array, script: String = "") -> int:
|
||||
var args := PackedStringArray(["--headless", "--path", ProjectSettings.globalize_path("res://")])
|
||||
if not script.is_empty():
|
||||
args.append_array(["--script", script])
|
||||
args.append("--")
|
||||
for x in user_args:
|
||||
args.append(str(x))
|
||||
args.append_array(["--exit-with", str(OS.get_process_id())])
|
||||
var pid := OS.create_process(OS.get_executable_path(), args)
|
||||
_pids.append(pid)
|
||||
return pid
|
||||
|
||||
func _sleep(s: float) -> void:
|
||||
await create_timer(s).timeout
|
||||
|
||||
static func _rm_tree(d: String) -> void:
|
||||
var da := DirAccess.open(d)
|
||||
if da == null:
|
||||
return
|
||||
for sub in da.get_directories():
|
||||
_rm_tree(d.path_join(sub))
|
||||
for f in da.get_files():
|
||||
da.remove(f)
|
||||
DirAccess.remove_absolute(d)
|
||||
@@ -68,7 +68,7 @@ func _body() -> void:
|
||||
var sp: Vector3 = Net.world_info.spawn
|
||||
check(Vector2(player.global_position.x - sp.x, player.global_position.z - sp.z).length() < 1.0, "Alice starts where the server put her")
|
||||
check(not world.creatures.enabled and world.creatures.alive().is_empty() and world.wolves == null,
|
||||
"no local-only animals while online (the server will run them in M2.5)")
|
||||
"no animals of her game's own while online (the server runs them: test_net_life.gd)")
|
||||
check(world.day_night.day == int(Net.world_info.day) and absf(world.day_night.time - float(Net.world_info.time)) < 0.01,
|
||||
"the clock follows the server (%s)" % world.day_night.clock_text())
|
||||
|
||||
|
||||
Reference in New Issue
Block a user