M2.2: player bodies, name tags, shared island and checked movement
- Joining loads the server's island (seed, spawn, clock); the server generates the same terrain from its seed and keeps a world clock that pauses when empty - Other players are procedural low-poly characters in their chosen colours with name tags (hidden by walls, fading with distance), walk/run/swim animation and held items - Movement: local and instant, sent at 20 Hz stamped with the sender's clock; the server refuses impossible moves (speed, rising, leaving the island, underground) and sends the player back; flagged respawns allowed - Avatars drawn 100 ms behind and interpolated, with brief extrapolation - Online: no pausing, player list in the Esc menu, Disconnect back to the title (with the reason); animals off and world changes local-only until M2.3-M2.5, shown on the HUD - test_net_world: real server + walking and cheating bots at 100 ms +/- 30 ms simulated latency (27 checks)
This commit is contained in:
@@ -23,6 +23,8 @@ var _main: VBoxContainer
|
||||
var _controls: VBoxContainer
|
||||
var _quit_btn: Button
|
||||
var _quit_armed := false
|
||||
var _online := false ## on a server: no pausing, no saving here
|
||||
var _players_label: Label
|
||||
|
||||
func bind(p: CharacterBody3D) -> void:
|
||||
player = p
|
||||
@@ -79,7 +81,15 @@ func bind(p: CharacterBody3D) -> void:
|
||||
_main.add_child(sens_row)
|
||||
|
||||
var saved: bool = Saves.has_world()
|
||||
if saved:
|
||||
_online = Net.state == "connected"
|
||||
if _online:
|
||||
_players_label = UITheme.label("", 15)
|
||||
_players_label.autowrap_mode = TextServer.AUTOWRAP_WORD
|
||||
_players_label.custom_minimum_size = Vector2(340, 0)
|
||||
_main.add_child(_players_label)
|
||||
_main.add_child(_button("Disconnect", _on_disconnect))
|
||||
_quit_btn = _button("Disconnect & Quit to Desktop", _on_quit)
|
||||
elif saved:
|
||||
_main.add_child(_button("Save & Quit to Title", _on_quit_to_title))
|
||||
_quit_btn = _button("Save & Quit to Desktop", _on_quit)
|
||||
else:
|
||||
@@ -87,6 +97,8 @@ func bind(p: CharacterBody3D) -> void:
|
||||
_main.add_child(_quit_btn)
|
||||
var note_text := "Autosaves every 2 minutes. World: %s" % Saves.read_meta(Saves.current_id).get("name", "?") if saved \
|
||||
else "Not playing a saved world: progress won't be kept."
|
||||
if _online:
|
||||
note_text = "The game keeps running while this menu is open."
|
||||
var note := UITheme.label(note_text, 13, UITheme.TEXT_DIM)
|
||||
note.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
_main.add_child(note)
|
||||
@@ -125,8 +137,14 @@ func open() -> void:
|
||||
_show_controls(false)
|
||||
_disarm_quit()
|
||||
player.ui_open = true
|
||||
get_tree().paused = true
|
||||
get_tree().paused = not _online # a shared world can't be paused by one player
|
||||
Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
|
||||
if _online:
|
||||
var names: Array[String] = []
|
||||
for p in Net.players.values():
|
||||
names.append(String(p.name) + (" (admin)" if p.get("admin", false) else ""))
|
||||
names.sort()
|
||||
_players_label.text = "%s · %d online\n%s" % [Net.server_info.get("server_name", "Server"), names.size(), ", ".join(names)]
|
||||
|
||||
func close() -> void:
|
||||
visible = false
|
||||
@@ -148,7 +166,16 @@ func _show_controls(on: bool) -> void:
|
||||
func _on_fullscreen(on: bool) -> void:
|
||||
DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_FULLSCREEN if on else DisplayServer.WINDOW_MODE_WINDOWED)
|
||||
|
||||
## Leaves the server (world.gd sees Net.left and goes back to the title).
|
||||
func _on_disconnect() -> void:
|
||||
close()
|
||||
Net.leave()
|
||||
|
||||
func _on_quit() -> void:
|
||||
if _online:
|
||||
Net.leave()
|
||||
get_tree().quit()
|
||||
return
|
||||
if Saves.has_world():
|
||||
player.get_parent().save_now()
|
||||
get_tree().quit()
|
||||
@@ -169,4 +196,6 @@ func _on_quit_to_title() -> void:
|
||||
|
||||
func _disarm_quit() -> void:
|
||||
_quit_armed = false
|
||||
if _online:
|
||||
return
|
||||
_quit_btn.text = "Save & Quit to Desktop" if Saves.has_world() else "Quit to Desktop"
|
||||
|
||||
@@ -30,6 +30,7 @@ var _lobby_motd: Label
|
||||
var _lobby_rules: Label
|
||||
var _lobby_count: Label
|
||||
var _lobby_list: VBoxContainer
|
||||
var _lobby_note: Label
|
||||
|
||||
func _ready() -> void:
|
||||
if Net.dedicated:
|
||||
@@ -88,15 +89,31 @@ func _ready() -> void:
|
||||
_pages["lobby"] = _lobby_page()
|
||||
for p in _pages.values():
|
||||
col.add_child(p)
|
||||
Net.joined.connect(func(): _show("lobby"))
|
||||
Net.joined.connect(_enter_server_world)
|
||||
Net.join_failed.connect(_on_join_failed)
|
||||
Net.left.connect(_on_left)
|
||||
Net.players_changed.connect(func():
|
||||
if _pages["lobby"].visible:
|
||||
_fill_lobby())
|
||||
_show("main")
|
||||
# Back from a server (left, kicked, or it went away): say why.
|
||||
if Net.state == "offline" and not Net.last_reason.is_empty():
|
||||
_show("join")
|
||||
_join_msg.text = Net.last_reason
|
||||
Net.last_reason = ""
|
||||
_maybe_capture()
|
||||
|
||||
## The server let us in: show who's there for a moment, then load the island.
|
||||
func _enter_server_world() -> void:
|
||||
_show("lobby")
|
||||
_lobby_note.text = "Loading the island..."
|
||||
for _i in range(3):
|
||||
await get_tree().process_frame
|
||||
if Net.state != "connected":
|
||||
return
|
||||
Saves.current_id = "" # a server's world isn't saved locally
|
||||
get_tree().change_scene_to_file(WORLD_SCENE)
|
||||
|
||||
## Dev hook, same as world.gd: `-- --shot=/abs/prefix` saves the title pages.
|
||||
func _maybe_capture() -> void:
|
||||
var prefix := ""
|
||||
@@ -370,10 +387,10 @@ func _lobby_page() -> Control:
|
||||
_lobby_list = VBoxContainer.new()
|
||||
_lobby_list.add_theme_constant_override("separation", 4)
|
||||
v.add_child(_lobby_list)
|
||||
var note := UITheme.label("Playing together in the world comes next (M2.2-M2.5). For now, this is who's here.", 13, UITheme.TEXT_DIM)
|
||||
note.autowrap_mode = TextServer.AUTOWRAP_WORD
|
||||
note.custom_minimum_size = Vector2(420, 0)
|
||||
v.add_child(note)
|
||||
_lobby_note = UITheme.label("", 13, UITheme.TEXT_DIM)
|
||||
_lobby_note.autowrap_mode = TextServer.AUTOWRAP_WORD
|
||||
_lobby_note.custom_minimum_size = Vector2(420, 0)
|
||||
v.add_child(_lobby_note)
|
||||
v.add_child(_button("Disconnect", Net.leave, 180))
|
||||
return p
|
||||
|
||||
|
||||
Reference in New Issue
Block a user