M2.6: server rules (PvP, raiding, locks and friends, death penalty, difficulty, wolf grace), chat, admin commands, server console

This commit is contained in:
Paul Bailey
2026-11-04 22:54:40 +00:00
parent 47fc116839
commit 2ab79b68c6
26 changed files with 1195 additions and 57 deletions

74
scripts/net/rules.gd Normal file
View File

@@ -0,0 +1,74 @@
extends RefCounted
## A dedicated server's rules (docs/MULTIPLAYER_PLAN.md §4): who may use and
## take down whose things, whether players can hurt each other, what death
## costs, and how hard the island is. All checked on the server; single
## player has no rules (everything is yours).
##
## Pieces remember who placed them (meta "owner" = player id, "owner_name").
## Pieces with no owner (placed before M2.6, or by the world) are anyone's.
const DIFFICULTY := {
# hunger and diet drain, animal and wolf damage, extra wolves in the pack
"easy": {"drain": 0.75, "damage": 0.7, "pack": -1},
"normal": {"drain": 1.0, "damage": 1.0, "pack": 0},
"hard": {"drain": 1.3, "damage": 1.4, "pack": 1},
}
static func _cfg() -> RefCounted:
var net: Node = (Engine.get_main_loop() as SceneTree).root.get_node_or_null("Net")
return net.config if net != null and net.is_server() else null
static func v(k: String) -> Variant:
var c := _cfg()
return c.v(k) if c != null else null
static func owner_of(piece: Node) -> String:
return String(piece.get_meta("owner", "")) if piece != null else ""
static func owner_name(piece: Node) -> String:
return String(piece.get_meta("owner_name", "someone"))
## Whether player `id` may open, take from or use `piece` (chests, doors,
## beds, graves), going by `locks`.
static func may_use(id: String, piece: Node) -> bool:
var c := _cfg()
var owner := owner_of(piece)
if c == null or owner.is_empty() or owner == id:
return true
match String(c.v("locks")):
"off":
return true
"owner_and_friends":
return c.is_friend(owner, id)
return false # "owner"
## Why `id` can't use `piece` ("" = they can).
static func use_problem(id: String, piece: Node) -> String:
if may_use(id, piece):
return ""
var what: String = piece.display_name().to_lower() if piece.has_method("display_name") else "that"
if piece.item_id == "grave":
return "That's %s's grave" % owner_name(piece)
return "That %s is %s's (locked)" % [what, owner_name(piece)]
## Taking a piece down: your own, anything you're allowed to use (friends,
## or locks off), or anyone's if raiding is on.
static func dismantle_problem(id: String, piece: Node) -> String:
if may_use(id, piece) or bool(v("raiding")):
return ""
return "That's %s's, and raiding is off on this server" % owner_name(piece)
static func pvp() -> bool:
return bool(v("pvp"))
static func death_penalty() -> String:
var d: Variant = v("death_penalty")
return String(d) if d != null else "grave"
static func difficulty(k: String) -> float:
var d: Variant = v("difficulty")
return float(DIFFICULTY.get(String(d) if d != null else "normal", DIFFICULTY.normal)[k])
static func grace_days() -> int:
var g: Variant = v("wolf_grace_days")
return clampi(int(g), 0, 10) if g != null else 3