| 
						
						
							
								
							
						
						
					 | 
				
				 | 
				 | 
				
					@ -2,7 +2,7 @@ class_name copyMultiplayer | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				 | 
				
					extends Mod_Base | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				 | 
				
					@export var cache    := "" | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				 | 
				
					@export var nickname := "" # FIXME: Not used for anything yet. | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				 | 
				
					@export var nickname := "" | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				 | 
				
					@export var address  := "" | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				 | 
				
					@export var port     := 52410 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				 | 
				
					
 | 
				
			
			
		
	
	
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
				
				 | 
				 | 
				
					@ -18,6 +18,10 @@ var player_order: Array[int] = [] | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				 | 
				
					var starting_offset     := Vector3(-1.25, 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: | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				 | 
				
						# 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. | 
				
			
			
		
	
	
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
				
				 | 
				 | 
				
					@ -40,12 +44,18 @@ func _ready() -> void: | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				 | 
				
						main_controller = $"/root/SnekStudio_Main/ModelController" | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				 | 
				
						main_controller.child_entered_tree.connect(on_model_controller_child_added) | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				 | 
				
						setup_setting_widget("cache"   , "Cache/LineEdit") | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				 | 
				
						setup_setting_widget("nickname", "Name/LineEdit" ) | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				 | 
				
						setup_setting_widget("address" , "Host/Address"  ) | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				 | 
				
						setup_setting_widget("port"    , "Host/Port"     ) | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				 | 
				
						setup_setting_widget("cache"   , "Cache/LineEdit", true ) | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				 | 
				
						setup_setting_widget("nickname", "Name/LineEdit" , false) | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				 | 
				
						setup_setting_widget("address" , "Host/Address"  , true ) | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				 | 
				
						setup_setting_widget("port"    , "Host/Port"     , true ) | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				 | 
				
						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_disconnected  .connect(on_peer_disconnected) | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				 | 
				
						multiplayer.connected_to_server.connect(on_connected_to_server) | 
				
			
			
		
	
	
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
				
				 | 
				 | 
				
					@ -58,17 +68,18 @@ func _exit_tree() -> void: | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				 | 
				
					func _create_settings_window() -> Control: | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				 | 
				
						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 widget: Control = settings.get_node(path) | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				 | 
				
						_settings_properties.append({ name = setting_name, args = {  } }) | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				 | 
				
						_settings_widgets_by_setting_name[setting_name] = widget | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				 | 
				
						if widget is LineEdit: widget.text_changed.connect( | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				 | 
				
							func(text): modify_setting(setting_name, text)) | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				 | 
				
						if widget is SpinBox: widget.value_changed.connect( | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				 | 
				
							func(number): modify_setting(setting_name, roundi(number))) | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				 | 
				
						if setup_events: | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				 | 
				
							if widget is LineEdit: widget.text_changed.connect( | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				 | 
				
								func(text): modify_setting(setting_name, text)) | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				 | 
				
							if widget is SpinBox: widget.value_changed.connect( | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				 | 
				
								func(number): modify_setting(setting_name, roundi(number))) | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				 | 
				
					func setup_button_connections() -> void: | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				 | 
				
						var window = get_settings_window() | 
				
			
			
		
	
	
		
			
				
					| 
						
							
								
							
						
						
							
								
							
						
						
					 | 
				
				 | 
				 | 
				
					@ -112,9 +123,6 @@ func on_disconnect_pressed() -> void: | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				 | 
				
					func on_peer_connected(id: int) -> void: | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				 | 
				
						update_status() | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				 | 
				
						print_log(["Player ", id, " connected"]) | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				 | 
				
						var model_controller := ModelController.new() | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				 | 
				
						model_controller.name = str(id) | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				 | 
				
						var sync_controller := SyncController.new() | 
				
			
			
		
	
	
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
				
				 | 
				 | 
				
					@ -125,20 +133,28 @@ func on_peer_connected(id: int) -> void: | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				 | 
				
						player_order.append(id) | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				 | 
				
						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() | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				 | 
				
						if filename.is_valid_filename(): change_model.rpc_id(id, filename) | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				 | 
				
					func on_peer_disconnected(id: int) -> void: | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				 | 
				
						update_status() | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				 | 
				
						print_log(["Player ", id, " disconnected"]) | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				 | 
				
						print_log("%s connected" % sync_controller.get_display_name()) | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				 | 
				
					func on_peer_disconnected(id: int) -> void: | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				 | 
				
						var controller = get_sync_controller(id) | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				 | 
				
						if not controller: return | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				 | 
				
						var controller: ModelController = get_node(str(id)) | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				 | 
				
						remove_child(controller) | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				 | 
				
						controller.queue_free() | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				 | 
				
						remove_child(controller.model_controller) | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				 | 
				
						controller.model_controller.queue_free() | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				 | 
				
						player_order.remove_at(player_order.find(id)) | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				 | 
				
						update_model_transforms() | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				 | 
				
						update_status() | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				 | 
				
						print_log("Player %s disconnected" % id) | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				 | 
				
					func on_connected_to_server() -> void: | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				 | 
				
						print_log("Connected to server") | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				 | 
				
					
 | 
				
			
			
		
	
	
		
			
				
					| 
						
							
								
							
						
						
							
								
							
						
						
					 | 
				
				 | 
				 | 
				
					@ -167,7 +183,7 @@ func update_status() -> void: | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				 | 
				
						var num_players := 1 + multiplayer.get_peers().size() | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				 | 
				
						var side := "Hosting" if multiplayer.is_server() else "Connected" | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				 | 
				
						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. | 
				
			
			
		
	
	
		
			
				
					| 
						
							
								
							
						
						
							
								
							
						
						
					 | 
				
				 | 
				 | 
				
					@ -213,6 +229,12 @@ func get_sync_controller(peer_id: int) -> SyncController: | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				 | 
				
						if not model_controller: return null | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				 | 
				
						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") | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				 | 
				
					func change_model(filename: String) -> void: | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				 | 
				
						var peer_id := multiplayer.get_remote_sender_id() | 
				
			
			
		
	
	
		
			
				
					| 
						
							
								
							
						
						
						
					 | 
				
				 | 
				 | 
				
					
  |