Organization/Pattern help needed for organizing nodes in "mmo-lite" space game

Godot Version

4.3

Question

I’m reaching a point where my project needs some better organization, I’m starting to get noddly messes, circular references, and “oh no do I need to use sqlite?”

I’m very slowly working on an space mmo-lite (think X4 or elite dangerous) with the intention of always running, so I am trying to leverage the multiplayer spawners and synchronizers as much as possible to make live joining easier.

The current structure is that each star system is in its own subviewport for different physics worlds. Each subviewport has ships spawners. Each ship has its own spawner for Character nodes (this is so PC and NPC characters can man different stations on a ship). Due to issues with multiplayerspawner and reparenting nodes, a characternode is destroyed and recreated when they move to another ship.

Now my problem, I need to reference characters in other game systems and I can’t figure out how to do it cleanly. For example: I am implementing factions, and factions will have a leader and need to reference the character node. MultiplayerSynconizer doesn’t sync any type of Variant. Character nodes are destroyed/created on a decent basis so FactionNode.leader would need to be updated every time. I will eventually implement network visibility so a client might not even know about the character.

I would suggest that you reimagine your “Character” as a Resource instead of a Node3D. You can keep a reference of all characters in a global script. You can implement Factions system that reference these characters, etc. They don’t need to exist in the physical world at all.
When you need to spawn a character physically within the world, you can have a Node3D with a 3D model, colliision, etc., that also additionally holds a reference to the proper “Character” (Resource).
This way when you remove the physical character from the scene, it’s only the Node3D you are removing, but the Resource stays in the memory to be reused, respawned, referenced, etc.

Oh. Oh! I love those moments when you learn about a new feature! Got a couple follow up questions:

  1. New characters will be created dynamically (ex, new player joining) and I’m not sure how to manage this in gdscript, or find a reference to it. The tutorials and documents seem to assume all the custom resource instances are predefined in the editor.

  2. Would making a CharacterManager node or singleton be the best way to manage the resources.

  3. Clients will need to get and update the resources from the server. Because it is dynamic, I assume I’ll need to use rpc to maintain the list?

  1. You can create new instances of Resources by calling Resource.new(). I like to make a custom constructor that allows to create a resource with all the data already included in one method, E.g.
class_name Character
extends Resource

var name: String
var level: int
var experience: int
var money: int
var faction: Faction

static func create(name: String, level: int, experience: int, money: int, faction: Faction) -> Character:
	var new_character: Character = Character.new()
	new_character.name = name
	new_character.level = level
	new_character.experience = experience 
	new_character.faction = faction 
	return new_character

Then in your CharacterManager class you can create new instances of Characters and store them in an array for future reference, e.g. this loop will create 5 new semi-random characters.

class_name CharacterManager
extends Node

var characters: Array[Character]
var factions: Array[Faction]

func _ready() -> void:
	for i in 5:
		var new_character: Character = Character.create("Player " + str(i), i, i, factions.pick_random())
		characters.append(new_character)
  1. Yes, that’s a good idea.
  2. I’m unfortunately not qualified enough about the over-the-network solutions, so I won’t be able to advise you on that.
1 Like