Compare commits

...

2 Commits

  1. 22
      Resources/copy_multiplayer_settings.tscn
  2. 52
      copyMultiplayer.gd
  3. 16
      sync_controller.gd

@ -27,15 +27,17 @@ text = "Cache"
[node name="LineEdit" type="LineEdit" parent="Cache"] [node name="LineEdit" type="LineEdit" parent="Cache"]
layout_mode = 2 layout_mode = 2
size_flags_horizontal = 3 size_flags_horizontal = 3
tooltip_text = "The directory other players' models will be loaded from.
Filenames must match exactly for this to work."
[node name="Button" type="Button" parent="Cache"] [node name="Button" type="Button" parent="Cache"]
layout_mode = 2 layout_mode = 2
tooltip_text = "Open Directory" tooltip_text = "Browse Directory"
text = " ... " text = " ... "
[node name="FileDialog" type="FileDialog" parent="Cache"] [node name="FileDialog" type="FileDialog" parent="Cache"]
title = "Select a Directory" title = "Open a Directory"
ok_button_text = "Select" ok_button_text = "Select Current Folder"
file_mode = 2 file_mode = 2
access = 2 access = 2
@ -50,7 +52,6 @@ text = "Name"
[node name="LineEdit" type="LineEdit" parent="Name"] [node name="LineEdit" type="LineEdit" parent="Name"]
layout_mode = 2 layout_mode = 2
size_flags_horizontal = 3 size_flags_horizontal = 3
placeholder_text = "Anonymous"
[node name="Host" type="HBoxContainer" parent="."] [node name="Host" type="HBoxContainer" parent="."]
layout_mode = 2 layout_mode = 2
@ -63,18 +64,22 @@ text = "Host"
[node name="Address" type="LineEdit" parent="Host"] [node name="Address" type="LineEdit" parent="Host"]
layout_mode = 2 layout_mode = 2
size_flags_horizontal = 3 size_flags_horizontal = 3
tooltip_text = "Address or IP of the host player to connect to.
Not used when you're hosting."
placeholder_text = "127.0.0.1" placeholder_text = "127.0.0.1"
secret = true secret = true
[node name="ShowHide" type="Button" parent="Host"] [node name="ShowHide" type="Button" parent="Host"]
layout_mode = 2 layout_mode = 2
tooltip_text = "Reveal / Hide Address" tooltip_text = "Show / Hide Address"
toggle_mode = true toggle_mode = true
icon = ExtResource("2_1u5pu") icon = ExtResource("2_1u5pu")
flat = true flat = true
[node name="Port" type="SpinBox" parent="Host"] [node name="Port" type="SpinBox" parent="Host"]
layout_mode = 2 layout_mode = 2
tooltip_text = "Port to connect to / listen on.
Host must forward this port."
min_value = 1024.0 min_value = 1024.0
max_value = 65000.0 max_value = 65000.0
value = 52410.0 value = 52410.0
@ -86,19 +91,20 @@ layout_mode = 2
[node name="Join" type="Button" parent="Buttons"] [node name="Join" type="Button" parent="Buttons"]
layout_mode = 2 layout_mode = 2
size_flags_horizontal = 3 size_flags_horizontal = 3
tooltip_text = "Open Directory" tooltip_text = "Join an existing session."
text = "Join" text = "Join"
[node name="Host" type="Button" parent="Buttons"] [node name="Host" type="Button" parent="Buttons"]
layout_mode = 2 layout_mode = 2
size_flags_horizontal = 3 size_flags_horizontal = 3
tooltip_text = "Open Directory" tooltip_text = "Open a new session, allowing
users to connect to your public IP."
text = "Host" text = "Host"
[node name="Disconnect" type="Button" parent="Buttons"] [node name="Disconnect" type="Button" parent="Buttons"]
layout_mode = 2 layout_mode = 2
size_flags_horizontal = 3 size_flags_horizontal = 3
tooltip_text = "Open Directory" tooltip_text = "Disconnect from / close the current session."
disabled = true disabled = true
text = "Disconnect text = "Disconnect
" "

@ -2,7 +2,7 @@ class_name copyMultiplayer
extends Mod_Base extends Mod_Base
@export var cache := "" @export var cache := ""
@export var nickname := "" # FIXME: Not used for anything yet. @export var nickname := ""
@export var address := "" @export var address := ""
@export var port := 52410 @export var port := 52410
@ -18,6 +18,10 @@ var player_order: Array[int] = []
var starting_offset := Vector3(-1.25, 0.0, 0.0) var starting_offset := Vector3(-1.25, 0.0, 0.0)
var accumulative_offset := Vector3( 0.35, 0.0, 0.0) var accumulative_offset := Vector3( 0.35, 0.0, 0.0)
# FIXME: There is an edge case where you can load the settings while connected,
# resulting in out-of-sync information with connected players, but so far
# I don't believe this should be a source of issues.
func _ready() -> void: func _ready() -> void:
# FIXME: This is just thrown together. Dunno if this is an accurate list. # FIXME: This is just thrown together. Dunno if this is an accurate list.
# TODO: Allow specifying additional bones, with the help of a hierachical list of existing bones in the model. # TODO: Allow specifying additional bones, with the help of a hierachical list of existing bones in the model.
@ -40,12 +44,18 @@ func _ready() -> void:
main_controller = $"/root/SnekStudio_Main/ModelController" main_controller = $"/root/SnekStudio_Main/ModelController"
main_controller.child_entered_tree.connect(on_model_controller_child_added) main_controller.child_entered_tree.connect(on_model_controller_child_added)
setup_setting_widget("cache" , "Cache/LineEdit") setup_setting_widget("cache" , "Cache/LineEdit", true )
setup_setting_widget("nickname", "Name/LineEdit" ) setup_setting_widget("nickname", "Name/LineEdit" , false)
setup_setting_widget("address" , "Host/Address" ) setup_setting_widget("address" , "Host/Address" , true )
setup_setting_widget("port" , "Host/Port" ) setup_setting_widget("port" , "Host/Port" , true )
setup_button_connections() setup_button_connections()
# Filter whitespace characters from nickname before saving it to "nickname" field.
var nickname_widget: LineEdit = get_settings_window().get_node("Name/LineEdit")
nickname_widget.text_changed.connect(func(new_text): modify_setting("nickname", new_text.strip_edges()))
nickname_widget.text_submitted.connect(func(_new_text): nickname_widget.text = nickname)
nickname_widget.focus_exited.connect(func(): nickname_widget.text = nickname)
multiplayer.peer_connected .connect(on_peer_connected) multiplayer.peer_connected .connect(on_peer_connected)
multiplayer.peer_disconnected .connect(on_peer_disconnected) multiplayer.peer_disconnected .connect(on_peer_disconnected)
multiplayer.connected_to_server.connect(on_connected_to_server) multiplayer.connected_to_server.connect(on_connected_to_server)
@ -58,13 +68,14 @@ func _exit_tree() -> void:
func _create_settings_window() -> Control: func _create_settings_window() -> Control:
return load("res://Mods/copyMultiplayer/Resources/copy_multiplayer_settings.tscn").instantiate() return load("res://Mods/copyMultiplayer/Resources/copy_multiplayer_settings.tscn").instantiate()
func setup_setting_widget(setting_name: String, path: NodePath) -> void: func setup_setting_widget(setting_name: String, path: NodePath, setup_events: bool) -> void:
var settings = get_settings_window() var settings = get_settings_window()
var widget: Control = settings.get_node(path) var widget: Control = settings.get_node(path)
_settings_properties.append({ name = setting_name, args = { } }) _settings_properties.append({ name = setting_name, args = { } })
_settings_widgets_by_setting_name[setting_name] = widget _settings_widgets_by_setting_name[setting_name] = widget
if setup_events:
if widget is LineEdit: widget.text_changed.connect( if widget is LineEdit: widget.text_changed.connect(
func(text): modify_setting(setting_name, text)) func(text): modify_setting(setting_name, text))
if widget is SpinBox: widget.value_changed.connect( if widget is SpinBox: widget.value_changed.connect(
@ -112,9 +123,6 @@ func on_disconnect_pressed() -> void:
func on_peer_connected(id: int) -> void: func on_peer_connected(id: int) -> void:
update_status()
print_log(["Player ", id, " connected"])
var model_controller := ModelController.new() var model_controller := ModelController.new()
model_controller.name = str(id) model_controller.name = str(id)
var sync_controller := SyncController.new() var sync_controller := SyncController.new()
@ -125,20 +133,28 @@ func on_peer_connected(id: int) -> void:
player_order.append(id) player_order.append(id)
update_model_transforms() update_model_transforms()
# Send information to the newly connected player about ourselves.
# (Technically this doesn't need to be relayed through the server, but oh well.)
if nickname: change_nickname.rpc_id(id, nickname)
var filename = main_controller._last_loaded_vrm.get_file() var filename = main_controller._last_loaded_vrm.get_file()
if filename.is_valid_filename(): change_model.rpc_id(id, filename) if filename.is_valid_filename(): change_model.rpc_id(id, filename)
func on_peer_disconnected(id: int) -> void:
update_status() update_status()
print_log(["Player ", id, " disconnected"]) print_log("%s connected" % sync_controller.get_display_name())
var controller: ModelController = get_node(str(id)) func on_peer_disconnected(id: int) -> void:
remove_child(controller) var controller = get_sync_controller(id)
controller.queue_free() if not controller: return
remove_child(controller.model_controller)
controller.model_controller.queue_free()
player_order.remove_at(player_order.find(id)) player_order.remove_at(player_order.find(id))
update_model_transforms() update_model_transforms()
update_status()
print_log("Player %s disconnected" % id)
func on_connected_to_server() -> void: func on_connected_to_server() -> void:
print_log("Connected to server") print_log("Connected to server")
@ -167,7 +183,7 @@ func update_status() -> void:
var num_players := 1 + multiplayer.get_peers().size() var num_players := 1 + multiplayer.get_peers().size()
var side := "Hosting" if multiplayer.is_server() else "Connected" var side := "Hosting" if multiplayer.is_server() else "Connected"
var s := "s" if num_players != 1 else "" var s := "s" if num_players != 1 else ""
set_status([side, ": ", num_players, " player", s]) set_status("%s: %d player%s" % [side, num_players, s])
# FIXME: Temporary hardcoded way to assign some offset to other players. # FIXME: Temporary hardcoded way to assign some offset to other players.
@ -213,6 +229,12 @@ func get_sync_controller(peer_id: int) -> SyncController:
if not model_controller: return null if not model_controller: return null
return model_controller.get_node_or_null("SyncController") as SyncController return model_controller.get_node_or_null("SyncController") as SyncController
@rpc("any_peer", "reliable")
func change_nickname(new_nickname: String) -> void:
var peer_id := multiplayer.get_remote_sender_id()
var controller := get_sync_controller(peer_id)
if controller: controller.change_nickname(new_nickname)
@rpc("any_peer", "reliable") @rpc("any_peer", "reliable")
func change_model(filename: String) -> void: func change_model(filename: String) -> void:
var peer_id := multiplayer.get_remote_sender_id() var peer_id := multiplayer.get_remote_sender_id()

@ -5,7 +5,7 @@ var module: copyMultiplayer
var model_controller: ModelController var model_controller: ModelController
var peer_id: int var peer_id: int
var nickname: String # FIXME: Not assigned or used. var nickname: String
var model_name: String var model_name: String
var model: Node var model: Node
var skeleton: Skeleton3D var skeleton: Skeleton3D
@ -18,6 +18,16 @@ func _ready() -> void:
model_controller = get_parent() model_controller = get_parent()
peer_id = model_controller.name.to_int() peer_id = model_controller.name.to_int()
func get_display_name() -> String:
if nickname: return "Player '%s' (%d)" % [ nickname, peer_id ]
else: return "Player (%d)" % peer_id
func change_nickname(new_nickname: String) -> void:
new_nickname = new_nickname.strip_edges()
if new_nickname == "": return # Ignore empty nicknames.
module.print_log("%s is now known as '%s'" % [ get_display_name(), new_nickname ])
nickname = new_nickname
## Attempts to change the model of this player. ## Attempts to change the model of this player.
func change_model(filename: String) -> void: func change_model(filename: String) -> void:
if not filename.is_valid_filename(): if not filename.is_valid_filename():
@ -25,12 +35,12 @@ func change_model(filename: String) -> void:
return return
var full_path := module.cache.path_join(filename) var full_path := module.cache.path_join(filename)
if not FileAccess.file_exists(full_path): if not FileAccess.file_exists(full_path):
module.print_log("Player %d wanted to switch to '%s', but it doesn't exist, skipping" % [ peer_id, filename ]) module.print_log("%s wanted to switch to '%s', but it doesn't exist, skipping" % [ get_display_name(), filename ])
return return
if not model_controller.load_vrm(full_path): if not model_controller.load_vrm(full_path):
module.print_log("ERROR: Model '%s' could not be loaded!" % filename) module.print_log("ERROR: Model '%s' could not be loaded!" % filename)
return return
module.print_log("Player %d switched to '%s'" % [ peer_id, filename ]) module.print_log("%s switched to '%s'" % [ get_display_name(), filename ])
model_name = filename model_name = filename
model = model_controller.get_node_or_null("Model") model = model_controller.get_node_or_null("Model")
skeleton = model_controller._get_model_skeleton() skeleton = model_controller._get_model_skeleton()

Loading…
Cancel
Save