M2.6: server rules (PvP, raiding, locks and friends, death penalty, difficulty, wolf grace), chat, admin commands, server console
This commit is contained in:
@@ -68,6 +68,7 @@ func server_join(peer: int, id: String, display: String) -> Vector3:
|
||||
p.inventory.changed.connect(func(): _dirty[peer] = true; WorldNet.mark_changed())
|
||||
p.worn_changed.connect(func(): _dirty[peer] = true)
|
||||
p.survival.died.connect(_on_died.bind(peer))
|
||||
p.survival.drain_mult = Rules.difficulty("drain")
|
||||
# A wolf bite or a boar: their screen flashes red, from the right side.
|
||||
p.survival.hurt.connect(func(amount, from):
|
||||
if proxies.has(peer):
|
||||
@@ -210,14 +211,28 @@ func _on_died(peer: int) -> void:
|
||||
for st in p.inventory.slots:
|
||||
if st != null:
|
||||
has_items = true
|
||||
if has_items:
|
||||
# What dying costs (the server's death_penalty rule).
|
||||
var penalty := Rules.death_penalty()
|
||||
if has_items and penalty == "grave":
|
||||
var g: Node3D = w.spawn_piece(ItemDB.get_item("grave"), Transform3D(Basis(Vector3.UP, p.rotation.y), pos), {"owner_id": p.player_id, "owner_name": p.display})
|
||||
g.inventory.load_data(p.inventory.to_data())
|
||||
p.inventory.load_data(Inventory.new(p.inventory.size()).to_data())
|
||||
elif has_items and penalty == "drop_all":
|
||||
# Everything scattered on the ground where you fell, for anyone.
|
||||
var rng := RandomNumberGenerator.new()
|
||||
for st in p.inventory.slots:
|
||||
if st != null:
|
||||
var off := Vector3(rng.randf_range(-1.2, 1.2), 0.0, rng.randf_range(-1.2, 1.2))
|
||||
var at: Vector3 = pos + off
|
||||
at.y = w.terrain.sample_height(at.x, at.z) + 0.05
|
||||
w.drop_item(st.id, int(st.count), Transform3D(Basis(Vector3.UP, rng.randf() * TAU), at))
|
||||
p.inventory.load_data(Inventory.new(p.inventory.size()).to_data())
|
||||
# ("keep": you keep everything.)
|
||||
# They stay down on the death screen for a few seconds, then ask to respawn.
|
||||
p.died_at = Time.get_ticks_msec()
|
||||
_dirty[peer] = true
|
||||
_s2c_died.rpc_id(peer, p.survival.cause if not p.survival.cause.is_empty() else "You died", has_items, p.bed_piece(w) != null)
|
||||
_s2c_died.rpc_id(peer, p.survival.cause if not p.survival.cause.is_empty() else "You died",
|
||||
has_items and penalty != "keep", p.bed_piece(w) != null, penalty)
|
||||
_acting = 0
|
||||
|
||||
const RESPAWN_DELAY := 3.0
|
||||
@@ -301,11 +316,12 @@ func _run(p: Node3D, what: String, a: Array) -> void:
|
||||
Actions.place(p, _i(a, 0), xf.orthonormalized(), w)
|
||||
"dismantle":
|
||||
var piece := WorldNet.piece(_i(a, 0))
|
||||
if piece != null:
|
||||
if piece != null and _allowed(Rules.dismantle_problem(p.player_id, piece)):
|
||||
Actions.dismantle(p, piece)
|
||||
"door":
|
||||
var d := WorldNet.piece(_i(a, 0))
|
||||
if d != null and d.get("open") != null and Actions.toggle_door(p, d):
|
||||
if d != null and d.get("open") != null and _allowed(Rules.use_problem(p.player_id, d)) \
|
||||
and Actions.toggle_door(p, d):
|
||||
WorldNet.piece_state(d)
|
||||
"fuel":
|
||||
var f := WorldNet.piece(_i(a, 0))
|
||||
@@ -313,7 +329,7 @@ func _run(p: Node3D, what: String, a: Array) -> void:
|
||||
WorldNet.piece_state(f)
|
||||
"bed":
|
||||
var b := WorldNet.piece(_i(a, 0))
|
||||
if b != null and b.item_id == "bed":
|
||||
if b != null and b.item_id == "bed" and _allowed(Rules.use_problem(p.player_id, b)):
|
||||
Actions.use_bed(p, b)
|
||||
"inv_click":
|
||||
# Slot `a[1]` of your own inventory (a[0] = 0) or a chest's.
|
||||
@@ -373,6 +389,8 @@ func _run(p: Node3D, what: String, a: Array) -> void:
|
||||
var c: Node3D = CreatureNet.creature(_i(a, 0))
|
||||
if c != null:
|
||||
Actions.melee(p, c)
|
||||
"hit_player":
|
||||
Actions.hit_player(p, _i(a, 0))
|
||||
"butcher":
|
||||
var c: Node3D = CreatureNet.creature(_i(a, 0))
|
||||
if c != null:
|
||||
@@ -455,6 +473,9 @@ func _beds_needed(n: int) -> int:
|
||||
## Actions.use_bed decided `p` may sleep: they get into bed, and the night
|
||||
## passes once enough players are in bed.
|
||||
func go_to_bed(p: Node3D) -> void:
|
||||
if String(Net.config.v("sleep")) == "off":
|
||||
tell(p.peer, "Sleeping is off on this server: the nights have to be lived through")
|
||||
return
|
||||
p.in_bed = true
|
||||
p.bed_at = p.global_position
|
||||
if not _maybe_pass_night():
|
||||
@@ -495,11 +516,16 @@ func _container(p: Node3D, nid: int) -> Node3D:
|
||||
var c := WorldNet.piece(nid)
|
||||
if c == null or c.get("inventory") == null or not Actions._in_reach(p, c, 1.0):
|
||||
return null
|
||||
var why: String = c.use_block_reason(p.player_id) if c.has_method("use_block_reason") else ""
|
||||
if not why.is_empty():
|
||||
Actions.notify.emit(why)
|
||||
return null
|
||||
return c
|
||||
return c if _allowed(Rules.use_problem(p.player_id, c)) else null
|
||||
|
||||
const Rules = preload("res://scripts/net/rules.gd")
|
||||
|
||||
## Tells the player why not, if there's a reason.
|
||||
func _allowed(why: String) -> bool:
|
||||
if why.is_empty():
|
||||
return true
|
||||
Actions.notify.emit(why)
|
||||
return false
|
||||
|
||||
static func _i(a: Array, k: int) -> int:
|
||||
return int(a[k]) if a.size() > k and (a[k] is int or a[k] is float) else -1
|
||||
@@ -567,9 +593,9 @@ func _s2c_stats(stats: Dictionary, statuses: Array, env: Array, bed: Array) -> v
|
||||
world.bed_spawn = bed[1]
|
||||
|
||||
@rpc("authority", "call_remote", "reliable")
|
||||
func _s2c_died(cause: String, has_items: bool, to_bed: bool) -> void:
|
||||
func _s2c_died(cause: String, has_items: bool, to_bed: bool, penalty: String = "grave") -> void:
|
||||
if world != null and is_instance_valid(world):
|
||||
world.died_online(cause, has_items, to_bed)
|
||||
world.died_online(cause, has_items, to_bed, penalty)
|
||||
|
||||
@rpc("authority", "call_remote", "reliable")
|
||||
func _s2c_respawn(at: Vector3, yaw: float, text: String) -> void:
|
||||
|
||||
Reference in New Issue
Block a user