I found some time to mess with the code today. I still am so confused about the whole authority thing. I have a body being generated by the spawner, and according to the prints they are being named correctly. All clients (host included) see all bodies but only the host moves (the clients arent moved by the host now though). The camera of the clients moves automatically to the host. I put in an ingame chat to see what is going on with the whole authority thing.
add operator:1
operator ID:1
Operator added with name:1and authority ID:1
1
add operator:750019725
operator ID:750019725
Operator added with name:750019725and authority ID:750019725
1
add operator:1367095416
operator ID:1367095416
Operator added with name:1367095416and authority ID:1367095416
1
add operator:1885263129
operator ID:1885263129
Operator added with name:1885263129and authority ID:1885263129
1
my prints basically are taken multiple points throughout just to confirm that the process is working. What is weird is that the bodies are being set correctly, but the bodies are unable to move?
In fact when I was using the chat box with the ID attached to the chat it was empty (empty text : random text here) comprated to the host (1:random text). The host then would change to 750019725:text, then 1367095416:text, etc. The host seems to take the authority instead of the clients? while the bodies which I thought was getting the authority didn’t.
Got a new fun error which basically tells me Authority isnt being set:
E 0:00:30:0495 on_replication_start: The MultiplayerSynchronizer at path “/root/Origin/GatheringHub/gathering_hub_spawner/97566485/PlayerInputSynchronizer” is unable to process the pending spawn since it has no network ID. This might happen when changing the multiplayer authority during the “_ready” callback. Make sure to only change the authority of multiplayer synchronizers during “_enter_tree” or the “_spawn_custom” callback of their multiplayer spawner.
<C++ Error> Condition “pending_sync_net_ids.is_empty()” is true. Returning: ERR_INVALID_DATA
<C++ Source> modules/multiplayer/scene_replication_interface.cpp:244 @ on_replication_start()
Basically its saying my input synchronizer is wrong? but I cant find it.
I am posting the full code, It is probably barely functioning with how much I deleted, and readded things. So some parts might not be needed/functional. I appreciate your help on this so far, I imagine If I don’t have it working by the end of the week, im just gonna abandon the multiplayer part and move on the the next thing to experiment on.
MultiplayerManager code (fun)
extends Node
##type "netstat -aon" on cmd to find open (listening) ports
##Operator setup
var Operator_name : String
##Multiplayer setup
@export var SERVER_PORT = 2004
@export var SERVER_IP= "127.0.0.1"
var server_peer =ENetMultiplayerPeer.new()
##Gathering Hub
@onready var gathering_hub_spawner = get_node("/root/Origin/GatheringHub/gathering_hub_spawner")
func _ready():
pass
func server_start():
if not multiplayer.is_server():return
server_peer.create_server(SERVER_PORT)
multiplayer.multiplayer_peer=server_peer
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)
for id in multiplayer.get_peers():
GameManager.call_deferred("add_operator", id)
if not OS.has_feature("dedicated_server"):GameManager.call("add_operator",1)
##Server and Client Calls
func peer_connected(operator_id):
GameManager.call("add_operator", operator_id)
func peer_disconnected(operator_id):
GameManager.call("delete_body", operator_id)
##Client Calls
func connected_to_server():
pass
func connection_failed():
pass
##Operator Management
func on_join_button_down():
server_peer.close()
var client_peer=ENetMultiplayerPeer.new()
client_peer.create_client(SERVER_IP,SERVER_PORT)
multiplayer.multiplayer_peer=client_peer
##Cleaning
#delete body
func erase_corruption():
var corruption=get_tree().get_nodes_in_group("current body")
for corrupt in corruption:
corrupt.queue_free()
#disconnect when leaving
func _exit_tree():
if not multiplayer.is_server():
return
multiplayer.peer_connected.disconnect(peer_connected)
multiplayer.peer_disconnected.disconnect(peer_disconnected)
multiplayer.connected_to_server.disconnect(connected_to_server)
multiplayer.connection_failed.disconnect(connection_failed)
GameManagercode (personally idk why I seperated it from multiplayer code?)
extends Node
var operator_ids = []
var local_operator
var SPAWN_RANDOM = 3
##Gathering Hub
@onready var gathering_hub_spawner = get_node("/root/Origin/GatheringHub/gathering_hub_spawner") ##this might break when portal system is setup
func add_operator(operator_id):
print("add operator:"+str(operator_id))
add_body(operator_id)
func add_body(operator_id):
print("operator ID:", operator_id)
operator_ids.append(operator_id)
var operator = preload("res://assets/eye_drone.tscn").instantiate()
operator.operator=operator_id
operator.name=str(operator_id)
var position :=Vector2.from_angle(randf()*2*PI)
operator.position=Vector3(position.x*SPAWN_RANDOM*randf(),0,position.y*SPAWN_RANDOM)
gathering_hub_spawner.add_child(operator,true)
operator.set_multiplayer_authority(operator_id)
print("Operator added with name:", operator.name,"and authority ID:", operator.get_multiplayer_authority())
OperatorManager.operator_name=operator.name
OperatorManager.call_deferred("set_operator_name")
if operator_id == multiplayer.get_unique_id():
local_operator=operator
func delete_body(operator_id):
if !gathering_hub_spawner.has_node(str(operator_id)):
return
gathering_hub_spawner.get_node(str(operator_id)).queue_free()
@rpc
func add_new_operator(new_operator_id):
add_operator(new_operator_id)
@rpc
func add_previous_operator(operator_ids):
for operator_id in operator_ids:
add_operator(operator_id)
Character Body 3D code (its refered to as Operator because im cringe I guess, and bored lol. trying to use words I might be more familiar with to try and understand this better)
extends CharacterBody3D
## 18.51 video tutorial started getting weird as fuck
# Get the gravity from the project settings to be synced with RigidBody nodes.
var gravity = ProjectSettings.get_setting("physics/3d/default_gravity")
##New varialbes (delete extra or unnessassary ones)
var DIRECTION_INTERPOLATE_SPEED=1
var MOTION_INTERPOLATE_SPEED=10
var ROTATION_INTERPOLATE_SPEED=10
var motion=Vector2()
var root_motion=Transform3D()
var orientation=Transform3D()
##Operator
#body
@export var current_body : CharacterBody3D
@export var current_body_model : MeshInstance3D
var forward_direction=Vector3()
#camera
##movement
@export_range(0,100,1) var move_speed:float=50
@export_range(0,100,1) var jump_speed:float=5
##camera
##keyboard
var keyboard_is_rotating_camera:bool=false
## mouse
var mouse_last_position:Vector2=Vector2.ZERO
var mouse_is_rotating_camera:bool=false
##Multiplayer setup
@export var operator :int:
set(operator_id):
operator=operator_id
$PlayerInputSynchronizer.set_multiplayer_authority(operator_id)
@onready var player_input = $PlayerInputSynchronizer
##Delete if fail
func _physics_process(delta):
if !is_multiplayer_authority():
return
movement(delta)
_apply_input(delta)
# Add the gravity
if not is_on_floor():
velocity.y -= gravity * delta
##Inputs
func _apply_input(delta:float):
#movement
motion=motion.lerp(player_input.input_direction, MOTION_INTERPOLATE_SPEED*delta)
set_velocity(velocity)
set_up_direction(Vector3.UP)
move_and_slide()
#rpc("remote_set_position", global_position) #this rpc exists because the ServerSyncrhonizer and player input syncronizer are not... connecting?
#cleanup
#orientation.origin=Vector3()
#orientation=orientation.orthonormalized()
#current_body_model.global_transform.basis=orientation.basis
#
#@rpc("unreliable")
#func remote_set_position(authority_position):
#global_position = authority_position
##Movement
#body input
func movement(delta:float)->void:
var velocity_direction:Vector3=Vector3.ZERO
velocity_direction=(transform.basis*Vector3(player_input.input_direction.x,velocity.y,player_input.input_direction.y)).normalized()
if velocity_direction:
velocity.x=velocity_direction.x*move_speed*delta
velocity.y=velocity_direction.y*move_speed*delta
position+=velocity_direction.normalized()*move_speed*delta #This is the important one!
current_body.rotation.y=player_input.get_camera_rotation()
player input synchronizer code (still says player instead of operater, its old code)
extends MultiplayerSynchronizer
##Body
var input_direction :=Vector2()
##Camera
@export var camera_base : Node3D
@export var camera_rotation : Node3D
@export var camera : Camera3D
##camera dynamics
@export_range(0.1,10,.1) var camera_smoothing:float=10.0
#camera zoom
var camera_zoom_direction:float=0
@export var camera_zoom_start:float = 5
@export_range(0,100,1) var camera_zoom_speed = 5
@export_range(0,100,0.01) var camera_zoom_min = 0
@export_range(0,100,1) var camera_zoom_max = 10
@export_range(0,2,.1) var camera_zoom_damp:float= .92 #this needs to be less then 1.0
#camera rotate
var camera_rotate_direction:int=0
@export_range(0,10,0.1) var camera_rotate_speed:float= 0.20
@export_range(0,20,1) var camera_rotate_y_speed:float=2
@export_range(0,10,1) var camera_rotate_x_min:float= deg_to_rad(-89.9)
@export_range(0,10,1) var camera_rotate_x_max:float= deg_to_rad(89.9)
##Checks
# mouse
var mouse_last_position:Vector2=Vector2.ZERO
var can_camera_mouse_rotate:bool=true
var mouse_is_rotating_camera:bool=false
#keyboard
var keyboard_is_rotating_camera:bool=true
#camera
var can_camera_move:bool=true
var can_camera_target_move:bool=false
var can_camera_zoom:bool=true
var can_camera_rotate:bool=false
var can_camera_rotate_y:bool=true
var can_camera_rotate_x:bool=true
func _enter_tree():
##Checks Authority
if get_multiplayer_authority() == multiplayer.get_unique_id():
camera.make_current()
else:
set_process(false)
set_process_input(false)
func _process(delta):
body_controller()
camera_controller(delta)
is_search_bar_selected()
func body_controller():
if can_camera_move:
input_direction=Input.get_vector("moveleft","moveright","moveforward","movebackward")
##Camera
func camera_controller(delta):
if !can_camera_target_move:
camera_zoom(delta)
camera_rotate(delta)
camera_mouse_rotate(delta)
if is_mouse_over_menu():
can_camera_zoom=false
else:
can_camera_zoom=true
##Inputs (Keyboard and/or Mouse)
#unhandled input
func _unhandled_input(event)->void:
#zoom
if event.is_action("zoomin"):
camera_zoom_direction=-1
if event.is_action("zoomout"):
camera_zoom_direction=1
#rotate from keyboard
if event.is_action_pressed("camerarotateright"):
camera_rotate_direction=-1
keyboard_is_rotating_camera=true
if event.is_action_pressed("camerarotateleft"):
camera_rotate_direction=1
keyboard_is_rotating_camera=true
elif event.is_action_released("camerarotateright") or event.is_action_released("camerarotateleft"):
keyboard_is_rotating_camera=false
#rotate from mouse
if event.is_action_pressed("cameramouserotate"):
mouse_last_position=get_viewport().get_mouse_position()
mouse_is_rotating_camera=true
elif event.is_action_released("cameramouserotate"):
mouse_is_rotating_camera=false
##Camera Zoom
#currently broken
func camera_zoom(delta:float)->void:
if !can_camera_zoom:
return
var new_zoom:float=clamp(camera.position.z+camera_zoom_speed*camera_zoom_direction*delta,camera_zoom_min,camera_zoom_max)
camera.position.z=new_zoom
camera_zoom_direction*=camera_zoom_damp
##Camera Rotation
#camera rotate position (y axis)
func camera_rotate(delta:float)->void:
if !can_camera_rotate or !keyboard_is_rotating_camera:
#print("camera rotate locked") This will spam messages because keyboard_is_rotating_camera unless pressed
return
#to rotate
camera_rotate_y(delta, camera_rotate_direction)
print (camera_rotate_direction)
#camera rotate base (y axis)
func camera_rotate_y(delta:float, direction:float)->void:
if !can_camera_rotate_y:
print("camera rotate y locked")
return
camera_base.rotation.y+=direction*camera_rotate_y_speed*delta #this turned off stops it from left/right
#camera rotate gimble (x axis)
func camera_rotate_x(delta:float, direction:float)->void:
if !can_camera_rotate_x:
print("camera rotate x locked")
return
var new_rotation_x:float=camera_rotation.rotation.x #turn off these 4 lines of code and it stops rotating up/down
new_rotation_x-=direction*camera_rotate_speed*delta
new_rotation_x=clamp(new_rotation_x,camera_rotate_x_min,camera_rotate_x_max)
camera_rotation.rotation.x=new_rotation_x
#camera mouse rotate function
func camera_mouse_rotate(delta:float)->void:
if !can_camera_mouse_rotate or !mouse_is_rotating_camera:
#print("mouse rotated locked") #it will SPAM messages because mouse_is_rotating_camera function is turned OFF unless pressed
return
var mouse_offset:Vector2=get_viewport().get_mouse_position()
mouse_offset=mouse_offset-mouse_last_position
mouse_last_position=get_viewport().get_mouse_position()
camera_rotate_y(delta,mouse_offset.x)
camera_rotate_x(delta,mouse_offset.y)
func get_camera_rotation_basis()->Basis:
return camera_base.global_transform.basis
func get_camera_rotation():
return camera_base.rotation.y
##Menus
func _set_process_input(enable:bool)->void:
if can_camera_move:
can_camera_move=enable
#menu search
func is_search_bar_selected():
var search_bar=get_node_or_null("/root/GatheringHub/main_screen_hud/seed_system_menu_settings/system_menu/Primary Container/primary_path/direction/root_menu_text_display/menu_display_text")
if search_bar!=null:
if search_bar.has_focus():
_set_process_input(false)
else:
_set_process_input(true)
return false
#menu inputs
func is_mouse_over_menu():
var system_menu=get_node_or_null("/root/GatheringHub/main_screen_hud/seed_system_menu_settings/system_menu/Primary Container")
var mouse_position=get_viewport().get_mouse_position()
if system_menu!=null:
var menu_rectangle=system_menu.get_global_rect()
if menu_rectangle.has_point(mouse_position):
return true
var branch_selection_group_nodes=get_tree().get_nodes_in_group("branch_bud_selection")
for node in branch_selection_group_nodes:
var container=node.find_child("PanelContainer")
if container!=null:
var panel_rect=container.get_global_rect()
if panel_rect.has_point(mouse_position):
return true
return false