M2.4: the server decides online (characters, inventory, stats, death, owned graves, per-player saves); death screen with Respawn after 3 s
This commit is contained in:
91
scripts/net/server_player.gd
Normal file
91
scripts/net/server_player.gd
Normal file
@@ -0,0 +1,91 @@
|
||||
extends Node3D
|
||||
## One player's character on a dedicated server (M2.4): what they carry, what
|
||||
## they hold, what they wear, their bed, and their survival stats, which run
|
||||
## here (warmth from the server's fires and buildings, hunger, diet...).
|
||||
##
|
||||
## It has the same fields and functions the Actions rules use on the local
|
||||
## player in single-player (inventory, selected, worn, held_item_id,
|
||||
## survival...), so the server runs exactly the same rules for everyone.
|
||||
## It has no body: where it is comes from the player's moves (net.gd).
|
||||
## Saved as players/<player id>.json in the server's world (player_net.gd).
|
||||
|
||||
signal worn_changed
|
||||
signal selected_changed
|
||||
|
||||
const HOTBAR := 8
|
||||
|
||||
var peer := 0
|
||||
var player_id := ""
|
||||
var display := ""
|
||||
var inventory := Inventory.new(40)
|
||||
var selected := 0
|
||||
var worn := ""
|
||||
var cursor = null ## the stack held on the inventory screen's cursor
|
||||
var survival: Node
|
||||
var hands: Node ## (self: held_heat for a carried torch)
|
||||
var velocity := Vector3.ZERO
|
||||
var water_level := -INF
|
||||
var has_bed_spawn := false
|
||||
var bed_spawn := Vector3.ZERO
|
||||
var anim := 0 ## last movement state reported (avatar.gd Anim)
|
||||
var died_at := 0 ## ticks (ms) when they last died
|
||||
|
||||
func setup(world: Node3D) -> void:
|
||||
hands = self
|
||||
water_level = world.terrain.water_level
|
||||
survival = preload("res://scripts/player/survival.gd").new()
|
||||
survival.name = "Survival"
|
||||
survival.player = self
|
||||
survival.world = world
|
||||
add_child(survival)
|
||||
|
||||
func held_item_id() -> String:
|
||||
var s = inventory.get_slot(selected)
|
||||
return s.id if s != null else ""
|
||||
|
||||
func select(i: int) -> void:
|
||||
selected = wrapi(i, 0, HOTBAR)
|
||||
selected_changed.emit()
|
||||
|
||||
func held_heat() -> float:
|
||||
return float(ItemDB.get_item(held_item_id()).get("light", {}).get("heat", 0.0))
|
||||
|
||||
## Chests and graves call this on interact; on the server there's no screen.
|
||||
func open_inventory(_container: Node) -> void:
|
||||
pass
|
||||
|
||||
func claims_bed(bed: Node3D) -> bool:
|
||||
return has_bed_spawn and bed_spawn.distance_to(bed.global_position) < 0.3
|
||||
|
||||
func claim_bed(bed: Node3D) -> void:
|
||||
has_bed_spawn = true
|
||||
bed_spawn = bed.global_position
|
||||
|
||||
## The bed this player respawns at, if it's still standing.
|
||||
func bed_piece(world: Node3D) -> Node3D:
|
||||
if not has_bed_spawn:
|
||||
return null
|
||||
for p in world.all_pieces():
|
||||
if p.item_id == "bed" and claims_bed(p):
|
||||
return p
|
||||
return null
|
||||
|
||||
func to_data() -> Dictionary:
|
||||
return {"name": display, "pos": [global_position.x, global_position.y, global_position.z], "yaw": rotation.y,
|
||||
"inventory": inventory.to_data(), "selected": selected, "stats": survival.to_data(), "worn": worn,
|
||||
"bed": {"has": has_bed_spawn, "pos": [bed_spawn.x, bed_spawn.y, bed_spawn.z]}}
|
||||
|
||||
func load_data(d: Dictionary) -> void:
|
||||
if d.has("inventory"):
|
||||
inventory.load_data(d.inventory)
|
||||
selected = int(d.get("selected", 0))
|
||||
if d.has("stats"):
|
||||
survival.load_data(d.stats)
|
||||
worn = String(d.get("worn", ""))
|
||||
if not worn.is_empty() and ItemDB.get_item(worn).is_empty():
|
||||
worn = ""
|
||||
var b: Dictionary = d.get("bed", {})
|
||||
has_bed_spawn = bool(b.get("has", false))
|
||||
if b.has("pos"):
|
||||
bed_spawn = Vector3(b.pos[0], b.pos[1], b.pos[2])
|
||||
rotation.y = float(d.get("yaw", 0.0))
|
||||
Reference in New Issue
Block a user