|  |  |  | @ -1,3 +1,4 @@ | 
			
		
	
		
			
				
					|  |  |  |  | class_name copyMultiplayer | 
			
		
	
		
			
				
					|  |  |  |  | extends Mod_Base | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | @export var cache    := "" | 
			
		
	
	
		
			
				
					|  |  |  | @ -5,11 +6,11 @@ extends Mod_Base | 
			
		
	
		
			
				
					|  |  |  |  | @export var address  := "" | 
			
		
	
		
			
				
					|  |  |  |  | @export var port     := 52410 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | var main_controller: ModelController | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | ## Hardcoded list of bone names that will get syncronized. | 
			
		
	
		
			
				
					|  |  |  |  | var tracked_bones: Array[String] | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | var main_controller: ModelController | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | # Temporary positioning system. | 
			
		
	
		
			
				
					|  |  |  |  | # TODO: Add a setting that allows syncing model positions, as an alternative | 
			
		
	
		
			
				
					|  |  |  |  | #       to letting the local player choose where each model is going to appear. | 
			
		
	
	
		
			
				
					|  |  |  | @ -17,9 +18,6 @@ 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) | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | # Allows us to use the "apply_animations" function to apply blendshapes. | 
			
		
	
		
			
				
					|  |  |  |  | var functions_blendshapes: Script = load("res://Mods/MediaPipe/MediaPipeController_BlendShapes.gd") | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 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,7 +38,7 @@ func _ready() -> void: | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 	# FIXME: Hardcoded way to get the main model controller. | 
			
		
	
		
			
				
					|  |  |  |  | 	main_controller = $"/root/SnekStudio_Main/ModelController" | 
			
		
	
		
			
				
					|  |  |  |  | 	main_controller.child_entered_tree.connect(model_changed) | 
			
		
	
		
			
				
					|  |  |  |  | 	main_controller.child_entered_tree.connect(on_model_controller_child_added) | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 	setup_setting_widget("cache"   , "Cache/LineEdit") | 
			
		
	
		
			
				
					|  |  |  |  | 	setup_setting_widget("nickname", "Name/LineEdit" ) | 
			
		
	
	
		
			
				
					|  |  |  | @ -48,11 +46,11 @@ func _ready() -> void: | 
			
		
	
		
			
				
					|  |  |  |  | 	setup_setting_widget("port"    , "Host/Port"     ) | 
			
		
	
		
			
				
					|  |  |  |  | 	setup_button_connections() | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 	multiplayer.peer_connected     .connect(peer_connected) | 
			
		
	
		
			
				
					|  |  |  |  | 	multiplayer.peer_disconnected  .connect(peer_disconnected) | 
			
		
	
		
			
				
					|  |  |  |  | 	multiplayer.connected_to_server.connect(connected_to_server) | 
			
		
	
		
			
				
					|  |  |  |  | 	multiplayer.connection_failed  .connect(connection_failed) | 
			
		
	
		
			
				
					|  |  |  |  | 	multiplayer.server_disconnected.connect(server_disconnected) | 
			
		
	
		
			
				
					|  |  |  |  | 	multiplayer.peer_connected     .connect(on_peer_connected) | 
			
		
	
		
			
				
					|  |  |  |  | 	multiplayer.peer_disconnected  .connect(on_peer_disconnected) | 
			
		
	
		
			
				
					|  |  |  |  | 	multiplayer.connected_to_server.connect(on_connected_to_server) | 
			
		
	
		
			
				
					|  |  |  |  | 	multiplayer.connection_failed  .connect(on_connection_failed) | 
			
		
	
		
			
				
					|  |  |  |  | 	multiplayer.server_disconnected.connect(on_server_disconnected) | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | func _exit_tree() -> void: | 
			
		
	
		
			
				
					|  |  |  |  | 	multiplayer.multiplayer_peer.close() | 
			
		
	
	
		
			
				
					|  |  |  | @ -67,19 +65,19 @@ func setup_setting_widget(setting_name: String, path: NodePath) -> void: | 
			
		
	
		
			
				
					|  |  |  |  | 	_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 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() | 
			
		
	
		
			
				
					|  |  |  |  | 	window.get_node("Buttons/Join").pressed.connect(join_pressed) | 
			
		
	
		
			
				
					|  |  |  |  | 	window.get_node("Buttons/Host").pressed.connect(host_pressed) | 
			
		
	
		
			
				
					|  |  |  |  | 	window.get_node("Buttons/Disconnect").pressed.connect(disconnect_pressed) | 
			
		
	
		
			
				
					|  |  |  |  | 	window.get_node("Buttons/Join").pressed.connect(on_join_pressed) | 
			
		
	
		
			
				
					|  |  |  |  | 	window.get_node("Buttons/Host").pressed.connect(on_host_pressed) | 
			
		
	
		
			
				
					|  |  |  |  | 	window.get_node("Buttons/Disconnect").pressed.connect(on_disconnect_pressed) | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | func join_pressed() -> void: | 
			
		
	
		
			
				
					|  |  |  |  | func on_join_pressed() -> void: | 
			
		
	
		
			
				
					|  |  |  |  | 	var address_widget: LineEdit = get_settings_window().get_node("Host/Address") | 
			
		
	
		
			
				
					|  |  |  |  | 	var default_address: String = address_widget.placeholder_text | 
			
		
	
		
			
				
					|  |  |  |  | 	var actual_address := default_address if address.is_empty() else address | 
			
		
	
	
		
			
				
					|  |  |  | @ -93,7 +91,7 @@ func join_pressed() -> void: | 
			
		
	
		
			
				
					|  |  |  |  | 	else: | 
			
		
	
		
			
				
					|  |  |  |  | 		print_log("Unable to connect!") | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | func host_pressed() -> void: | 
			
		
	
		
			
				
					|  |  |  |  | func on_host_pressed() -> void: | 
			
		
	
		
			
				
					|  |  |  |  | 	var peer := ENetMultiplayerPeer.new() | 
			
		
	
		
			
				
					|  |  |  |  | 	if peer.create_server(port) == OK: | 
			
		
	
		
			
				
					|  |  |  |  | 		multiplayer.multiplayer_peer = peer | 
			
		
	
	
		
			
				
					|  |  |  | @ -103,7 +101,7 @@ func host_pressed() -> void: | 
			
		
	
		
			
				
					|  |  |  |  | 	else: | 
			
		
	
		
			
				
					|  |  |  |  | 		print_log("Unable to open server!") | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | func disconnect_pressed() -> void: | 
			
		
	
		
			
				
					|  |  |  |  | func on_disconnect_pressed() -> void: | 
			
		
	
		
			
				
					|  |  |  |  | 	assert(multiplayer.multiplayer_peer) | 
			
		
	
		
			
				
					|  |  |  |  | 	if multiplayer.is_server(): | 
			
		
	
		
			
				
					|  |  |  |  | 		set_status("") | 
			
		
	
	
		
			
				
					|  |  |  | @ -113,13 +111,16 @@ func disconnect_pressed() -> void: | 
			
		
	
		
			
				
					|  |  |  |  | 	multiplayer.multiplayer_peer.close() | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | func peer_connected(id: int) -> void: | 
			
		
	
		
			
				
					|  |  |  |  | func on_peer_connected(id: int) -> void: | 
			
		
	
		
			
				
					|  |  |  |  | 	update_status() | 
			
		
	
		
			
				
					|  |  |  |  | 	print_log(["Player ", id, " connected"]) | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 	var new_controller := ModelController.new() | 
			
		
	
		
			
				
					|  |  |  |  | 	new_controller.name = str(id) | 
			
		
	
		
			
				
					|  |  |  |  | 	add_child(new_controller) | 
			
		
	
		
			
				
					|  |  |  |  | 	var model_controller := ModelController.new() | 
			
		
	
		
			
				
					|  |  |  |  | 	model_controller.name = str(id) | 
			
		
	
		
			
				
					|  |  |  |  | 	var sync_controller := SyncController.new() | 
			
		
	
		
			
				
					|  |  |  |  | 	sync_controller.name = "SyncController" | 
			
		
	
		
			
				
					|  |  |  |  | 	model_controller.add_child(sync_controller) | 
			
		
	
		
			
				
					|  |  |  |  | 	add_child(model_controller) | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 	player_order.append(id) | 
			
		
	
		
			
				
					|  |  |  |  | 	update_model_transforms() | 
			
		
	
	
		
			
				
					|  |  |  | @ -127,7 +128,7 @@ func peer_connected(id: int) -> void: | 
			
		
	
		
			
				
					|  |  |  |  | 	var filename = main_controller._last_loaded_vrm.get_file() | 
			
		
	
		
			
				
					|  |  |  |  | 	if filename.is_valid_filename(): change_model.rpc_id(id, filename) | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | func peer_disconnected(id: int) -> void: | 
			
		
	
		
			
				
					|  |  |  |  | func on_peer_disconnected(id: int) -> void: | 
			
		
	
		
			
				
					|  |  |  |  | 	update_status() | 
			
		
	
		
			
				
					|  |  |  |  | 	print_log(["Player ", id, " disconnected"]) | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
	
		
			
				
					|  |  |  | @ -138,15 +139,15 @@ func peer_disconnected(id: int) -> void: | 
			
		
	
		
			
				
					|  |  |  |  | 	player_order.remove_at(player_order.find(id)) | 
			
		
	
		
			
				
					|  |  |  |  | 	update_model_transforms() | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | func connected_to_server() -> void: | 
			
		
	
		
			
				
					|  |  |  |  | func on_connected_to_server() -> void: | 
			
		
	
		
			
				
					|  |  |  |  | 	print_log("Connected to server") | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | func connection_failed() -> void: | 
			
		
	
		
			
				
					|  |  |  |  | func on_connection_failed() -> void: | 
			
		
	
		
			
				
					|  |  |  |  | 	set_status("") | 
			
		
	
		
			
				
					|  |  |  |  | 	print_log("Connection failed!") | 
			
		
	
		
			
				
					|  |  |  |  | 	update_enabled_state(false) | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | func server_disconnected() -> void: | 
			
		
	
		
			
				
					|  |  |  |  | func on_server_disconnected() -> void: | 
			
		
	
		
			
				
					|  |  |  |  | 	set_status("") | 
			
		
	
		
			
				
					|  |  |  |  | 	print_log("Disconnected from server") | 
			
		
	
		
			
				
					|  |  |  |  | 	update_enabled_state(false) | 
			
		
	
	
		
			
				
					|  |  |  | @ -162,6 +163,14 @@ func update_enabled_state(is_online: bool) -> void: | 
			
		
	
		
			
				
					|  |  |  |  | 	window.get_node("Buttons/Host"      ).disabled =  is_online | 
			
		
	
		
			
				
					|  |  |  |  | 	window.get_node("Buttons/Disconnect").disabled = !is_online | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 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]) | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | # FIXME: Temporary hardcoded way to assign some offset to other players. | 
			
		
	
		
			
				
					|  |  |  |  | func update_model_transforms() -> void: | 
			
		
	
		
			
				
					|  |  |  |  | 	var offset := starting_offset | 
			
		
	
		
			
				
					|  |  |  |  | 	for id in player_order: | 
			
		
	
	
		
			
				
					|  |  |  | @ -178,12 +187,7 @@ func update_model_rotation(controller: ModelController) -> void: | 
			
		
	
		
			
				
					|  |  |  |  | 	var to_2d   := Vector2(camera.position.x, camera.position.z) | 
			
		
	
		
			
				
					|  |  |  |  | 	controller.rotation.y = from_2d.angle_to_point(to_2d) / 4 # Magic value, probably depends on FOV. | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 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]) | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | ## Removes all networked player models. | 
			
		
	
		
			
				
					|  |  |  |  | func clear_player_models() -> void: | 
			
		
	
		
			
				
					|  |  |  |  | 	for controller in get_children(): | 
			
		
	
		
			
				
					|  |  |  |  | 		if controller is ModelController: | 
			
		
	
	
		
			
				
					|  |  |  | @ -192,28 +196,10 @@ func clear_player_models() -> void: | 
			
		
	
		
			
				
					|  |  |  |  | 	player_order.clear() | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | @rpc("any_peer", "reliable") | 
			
		
	
		
			
				
					|  |  |  |  | func change_model(filename: String) -> void: | 
			
		
	
		
			
				
					|  |  |  |  | 	var player_id := multiplayer.get_remote_sender_id() | 
			
		
	
		
			
				
					|  |  |  |  | 	var controller := get_node_or_null(str(player_id)) as ModelController | 
			
		
	
		
			
				
					|  |  |  |  | 	if not controller: return | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 	if not filename.is_valid_filename(): | 
			
		
	
		
			
				
					|  |  |  |  | 		print_log(["ERROR: '", filename, "' is not a valid file name!"]) | 
			
		
	
		
			
				
					|  |  |  |  | 		return | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 	var full_path := cache.path_join(filename) | 
			
		
	
		
			
				
					|  |  |  |  | 	if not FileAccess.file_exists(full_path): | 
			
		
	
		
			
				
					|  |  |  |  | 		print_log(["Player ", player_id, " wanted to switch to '", filename, "', but it could not be found, skipping"]) | 
			
		
	
		
			
				
					|  |  |  |  | 		return | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 	if controller.load_vrm(full_path): | 
			
		
	
		
			
				
					|  |  |  |  | 		print_log(["Player ", player_id, " switched to '", filename, "'"]) | 
			
		
	
		
			
				
					|  |  |  |  | 	else: | 
			
		
	
		
			
				
					|  |  |  |  | 		print_log(["ERROR: Model '", filename, "' could not be loaded!"]) | 
			
		
	
		
			
				
					|  |  |  |  | func _process(_delta: float) -> void: | 
			
		
	
		
			
				
					|  |  |  |  | 	SyncController.send_model_animation(self) | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | # Called when a node is added to the main ModelController. | 
			
		
	
		
			
				
					|  |  |  |  | func model_changed(child: Node) -> void: | 
			
		
	
		
			
				
					|  |  |  |  | func on_model_controller_child_added(child: Node) -> void: | 
			
		
	
		
			
				
					|  |  |  |  | 	if child.name != "Model": return | 
			
		
	
		
			
				
					|  |  |  |  | 	# Wait for one frame, then "_last_loaded_vrm" is updated. | 
			
		
	
		
			
				
					|  |  |  |  | 	await get_tree().process_frame | 
			
		
	
	
		
			
				
					|  |  |  | @ -221,42 +207,20 @@ func model_changed(child: Node) -> void: | 
			
		
	
		
			
				
					|  |  |  |  | 	if filename.is_valid_filename(): change_model.rpc(filename) | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | # FIXME: This sends way more information than necessary, but works as a proof-of-concept! | 
			
		
	
		
			
				
					|  |  |  |  | @rpc("any_peer", "unreliable_ordered") | 
			
		
	
		
			
				
					|  |  |  |  | func sync_model_animation( | 
			
		
	
		
			
				
					|  |  |  |  | 	model_transform: Transform3D, | 
			
		
	
		
			
				
					|  |  |  |  | 	shape_dict: Dictionary, # Dictionary[String, float] | 
			
		
	
		
			
				
					|  |  |  |  | 	bone_poses: Dictionary, # Dictionary[String, Transform3D] | 
			
		
	
		
			
				
					|  |  |  |  | ) -> void: | 
			
		
	
		
			
				
					|  |  |  |  | 	var player_id := multiplayer.get_remote_sender_id() | 
			
		
	
		
			
				
					|  |  |  |  | 	var controller := get_node_or_null(str(player_id)) | 
			
		
	
		
			
				
					|  |  |  |  | 	if not controller: return | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 	var model := controller.get_node_or_null("Model") as Node3D | 
			
		
	
		
			
				
					|  |  |  |  | 	if model: | 
			
		
	
		
			
				
					|  |  |  |  | 		model.transform = model_transform | 
			
		
	
		
			
				
					|  |  |  |  | 		functions_blendshapes.apply_animations(model, shape_dict) | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 	var skeleton := controller._get_model_skeleton() as Skeleton3D | 
			
		
	
		
			
				
					|  |  |  |  | 	if skeleton: | 
			
		
	
		
			
				
					|  |  |  |  | 		for bone_name in bone_poses: | 
			
		
	
		
			
				
					|  |  |  |  | 			var pose: Transform3D = bone_poses[bone_name] | 
			
		
	
		
			
				
					|  |  |  |  | 			var idx := skeleton.find_bone(bone_name) | 
			
		
	
		
			
				
					|  |  |  |  | 			if idx != -1: skeleton.set_bone_pose(idx, pose) | 
			
		
	
		
			
				
					|  |  |  |  | ## Gets the SyncController for the player with the specified peer id. | 
			
		
	
		
			
				
					|  |  |  |  | func get_sync_controller(peer_id: int) -> SyncController: | 
			
		
	
		
			
				
					|  |  |  |  | 	var model_controller := get_node_or_null(str(peer_id)) as ModelController | 
			
		
	
		
			
				
					|  |  |  |  | 	if not model_controller: return null | 
			
		
	
		
			
				
					|  |  |  |  | 	return model_controller.get_node_or_null("SyncController") as SyncController | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | func _process(_delta: float) -> void: | 
			
		
	
		
			
				
					|  |  |  |  | 	if multiplayer.get_peers().size() == 0: return | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 	var model    := get_model() | 
			
		
	
		
			
				
					|  |  |  |  | 	var skeleton := get_skeleton() | 
			
		
	
		
			
				
					|  |  |  |  | 	var media_pipe_ctrl = $"../MediaPipeController" | 
			
		
	
		
			
				
					|  |  |  |  | 	if (not model) or (not skeleton) or (not media_pipe_ctrl): return | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 	var shape_dict = media_pipe_ctrl.blend_shape_last_values | 
			
		
	
		
			
				
					|  |  |  |  | 	var bone_poses = {} | 
			
		
	
		
			
				
					|  |  |  |  | 	for bone_name in tracked_bones: | 
			
		
	
		
			
				
					|  |  |  |  | 		var bone_idx = skeleton.find_bone(bone_name) | 
			
		
	
		
			
				
					|  |  |  |  | 		if bone_idx == -1: continue | 
			
		
	
		
			
				
					|  |  |  |  | 		var bone_pose = skeleton.get_bone_pose(bone_idx) | 
			
		
	
		
			
				
					|  |  |  |  | 		bone_poses[bone_name] = bone_pose | 
			
		
	
		
			
				
					|  |  |  |  | 	sync_model_animation.rpc(model.transform, shape_dict, bone_poses) | 
			
		
	
		
			
				
					|  |  |  |  | @rpc("any_peer", "reliable") | 
			
		
	
		
			
				
					|  |  |  |  | func change_model(filename: String) -> void: | 
			
		
	
		
			
				
					|  |  |  |  | 	var peer_id := multiplayer.get_remote_sender_id() | 
			
		
	
		
			
				
					|  |  |  |  | 	var controller := get_sync_controller(peer_id) | 
			
		
	
		
			
				
					|  |  |  |  | 	if controller: controller.change_model(filename) | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | @rpc("any_peer", "unreliable_ordered") | 
			
		
	
		
			
				
					|  |  |  |  | func sync_model_animation(model_transform: Transform3D, shape_dict: Dictionary, bone_poses: Dictionary) -> void: | 
			
		
	
		
			
				
					|  |  |  |  | 	var peer_id := multiplayer.get_remote_sender_id() | 
			
		
	
		
			
				
					|  |  |  |  | 	var controller := get_sync_controller(peer_id) | 
			
		
	
		
			
				
					|  |  |  |  | 	if controller: controller.sync_model_animation(model_transform, shape_dict, bone_poses) | 
			
		
	
	
		
			
				
					|  |  |  | 
 |