Help migration C# code from 3.5 to 4.0

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By ondesic

I have 3.5 project that i would like to port to 4 beta.
Upon opening the project, I have 269 errors. I don’t understand the syntax changes.
I see that _process now takes a double and _notifications take a long
However, i have a lot of “partial” errors for functions. In simple terms how do I fix these and why are they happening?
I have a ton of Remote and RemoteSync errors. How do I fix this in Godot 4?
Also, is there a guide for all the code changes i need to make?

Thanks so much for any help.

:bust_in_silhouette: Reply From: dezboyle

I have no experience with multiplayer in 3.5, but I’m making a multiplayer game in 4.0 and the high level multiplayer API has been completely overhauled.

This blog post summarizes all of the changes they made. There are 4 articles linked near the top of the page.

Things work a bit differently, so it’s going to be a bigger task than just fixing some syntax. Also, the c# docs on multiplayer are incorrect or practically nonexistent, so I had to figure a lot of it out through trial and error. Wishing you the best of luck

I read these articles but they are for GDScript. I’m hoping someone can help me with the C# equivalent syntax.

ondesic | 2022-12-19 14:59

Yeah, even though its in GDScript, there is no C# documentation on it, so you just gotta work with what you got and figure out the C# equivalent yourself. It’s a pain though- I hope it gets documented soon.

Here’s one of my network scripts if you need an example of syntax.

public static GameState instance; //maybe bad practice.  I only put this here to use with PlayerDebug
public string ConnectionType { get; private set; }
public Godot.Collections.Array<long> PeerIDs { get; private set; }

const int port = 3460;
const string address = "localhost";

[Export] private NodePath nameText;

private ENetMultiplayerPeer peer;
private Player localPlayer;
private string playerName;
private LineEdit nameLineEdit;
private Dictionary<long, Player> players;


public override void _EnterTree()
{
    nameLineEdit = GetNode<LineEdit>(nameText);
	nameLineEdit.TextChanged += UpdateName;
}

public override void _Ready()
{
	if(instance != null)
	{
		GD.PrintErr("TWO GAMESTATES EXIST");
		Free();
		return;
	}
	instance = this;

	PeerIDs = new Godot.Collections.Array<long>();
	players = new Dictionary<long, Player>();
	UpdateName(nameLineEdit.Text);
}

public void HostGameButton()
{
	if(peer == null)
    { HostGame(); }
}

public void JoinGameButton()
{
	if(peer == null)
    { JoinGame(); }
}

public void UpdateName(string newName)
{
	playerName = newName;
}

void HostGame()
{
	ConnectionType = "Mr Host Man";
	peer = new ENetMultiplayerPeer();
	peer.CreateServer(port, 8);
	GetTree().GetMultiplayer().MultiplayerPeer = peer;
	LoadLevel();
    AddPlayer(1);
	peer.PeerConnected += PlayerConnected;
	
	GD.Print("Hosting Game");
}

void JoinGame()
{
	ConnectionType = "Mrs McClient";
	peer = new ENetMultiplayerPeer();
	peer.CreateClient(address, port);
	GetTree().GetMultiplayer().MultiplayerPeer = peer;
	LoadLevel();
	GD.Print("Joining Game");
}

async void PlayerConnected(long id)
{
	GD.Print("Player Connected " + id);
	//HOST
	await ToSignal(GetTree().CreateTimer(1), "timeout"); //BUG in godot Alpha.  Takes a sec until rpcs can be recieved
	Rpc(nameof(AddNewlyConnectedPlayer), id);
	if(PeerIDs != null && PeerIDs.Count > 0)
    	{ RpcId(id, nameof(AddPreviouslyConnectedPlayers), PeerIDs.Duplicate()); }
	AddPlayer(id);
}

[RPC(TransferMode = MultiplayerPeer.TransferModeEnum.Reliable)]
private void RemoteSyncPeerIDs(Godot.Collections.Array<long> newPeerIDs)
{
	if(!Multiplayer.IsServer())
    { PeerIDs = newPeerIDs; }
}

[RPC(TransferMode = MultiplayerPeer.TransferModeEnum.Reliable)]
private void AddNewlyConnectedPlayer(long id)
{
	GD.Print("Adding newly connected players");
	AddPlayer(id);
}

[RPC(TransferMode = MultiplayerPeer.TransferModeEnum.Reliable)]
private void AddPreviouslyConnectedPlayers(Godot.Collections.Array<long> ids)
{
	GD.Print("Adding previously connected players");
	for(int i = 0; i < ids.Count; i++)
	{
		AddPlayer(ids[i]);
	}
}

public void PlayerConnectedFully()
{
	RpcId(1, nameof(RemotePlayerConnectedFully));
}

[RPC(MultiplayerAPI.RPCMode.AnyPeer, CallLocal = true)]
private void RemotePlayerConnectedFully()
{
	GD.Print("RemotePlayerConnectedFully");
	//SERVER callback from player that they loaded in
	for(int i = 0; i < PeerIDs.Count; i++)
	{
		GD.Print("I should refresh " + PeerIDs[i]);
		players[PeerIDs[i]].PlayerState.RpcId(PeerIDs[i], nameof(localPlayer.PlayerState.RefreshState));
	}
}

public void RPCPeer(Node node, bool callLocal, StringName method, params Variant[] args)
{
	if(PeerIDs.Count <= 0 || peer == null)
    { return; }
	for(int i = 0; i < PeerIDs.Count; i++)
	{
		if(callLocal || PeerIDs[i] != peer.GetUniqueId())
        {
			node.RpcId(PeerIDs[i], method, args);
		}
	}
}

private void AddPlayer(long id)
{
	Player player = GameManager.instance.SpawnLocalPlayer();
    player.Name = id.ToString();
    player.SetMultiplayerAuthority((int)id);
	if(id == peer.GetUniqueId()) //its our player
    {
		player.PlayerState.PlayerInfo.Add(nameof(PlayerState.PlayerNameDictionaryKey), playerName);
		localPlayer = player;
	}
    GetTree().Root.AddChild(player);

	if(Multiplayer.IsServer())
    {
		PeerIDs.Add(id);
        Rpc(new StringName(nameof(RemoteSyncPeerIDs)), PeerIDs.Duplicate());
		GD.PrintErr("Adding ID " + id);
		players.Add(id, player);
	}
}

private void LoadLevel()
{
	GameManager.instance.LoadLevel();
	UI.instance.TitleScreen.Visible = false;
}

private void EndGame()
{
    GameManager.instance.UnLoadLevel();
    GetTree().GetMultiplayer().MultiplayerPeer = null;
    UI.instance.TitleScreen.Visible = true;
}

dezboyle | 2022-12-19 15:27