How can i seperate network logic from game code

I want my original game code to be decoupled from network code for various reasons. But im not quiete sure how can i achieve this.

Lets say i invoke fire event everytime projectile spawned. And network code listens this event but this time network code needs to rewrite instantiate logic with given event data

can you guide me on how to seperate game and network logic.

The command pattern would be one option:

new SpawnProjectileCommand(this, projectileId, position, direction).Execute();

Oh thanks this actually makes super sense! Something came to mind, but im not sure if i understood correctly. I got help from chatgpt to understand, do you recommend something like this?

using System;
using UnityEngine;

namespace Test {
    public sealed class ProjectileSpawnCommand : Command<ProjectileSpawnCommand> {
        public ScriptableObject ProjectileData { get; }
        public Vector3 Position { get; }
        public Vector3 Direction { get; }
        public float Speed { get; }

        public ProjectileSpawnCommand(Vector3 position, Vector3 direction, float speed,
            ScriptableObject projectileData) {
            Position = position;
            Direction = direction;
            Speed = speed;
            ProjectileData = projectileData;
        }
    }

    public abstract class Command<T> where T : Command<T> {
        public static event Action<T> OnCommandExecuted;

        public virtual void Execute() {
            OnCommandExecuted?.Invoke((T)this);
        }
    }

    public class CommandSyncer : MonoBehaviour {
        void OnEnable() {
            ProjectileSpawnCommand.OnCommandExecuted += OnProjectileSpawnCommandExecuted;
        }

        void OnDisable() {
            ProjectileSpawnCommand.OnCommandExecuted -= OnProjectileSpawnCommandExecuted;
        }

        void OnProjectileSpawnCommandExecuted(ProjectileSpawnCommand obj) {
            photonView.RPC("ExecuteProjectileSpawnCommand", RpcTarget.Others, 
                command.Position, command.Direction, command.Speed, command.ProjectileData.name);
        }
        
        [PunRPC]
        void ExecuteProjectileSpawnCommand(Vector3 position, Vector3 direction, float speed, string projectileDataName)
        {
            ScriptableObject projectileData = Resources.Load<ScriptableObject>(projectileDataName); 
            var command = new ProjectileSpawnCommand(position, direction, speed, projectileData);
            command.Execute(); 
        }
    }
}

Yeah, something like that. Key points being that:

  1. The command automatically gets executed on all clients machines.
  2. All the networking solution specific code is encapsulated in one centralized place.

One option would be to place all the networking-specific code in a single base class:

// Networking solution-specific code encapsulated in the NetworkedCommand base class
public sealed class ProjectileSpawnCommand : NetworkedCommand
{
	private readonly ScriptableObject projectileData;
	private readonly Vector3 position;
	private readonly Vector3 direction;
	private readonly float speed;
	
	public Projectile Projectile { get; private set; }

	public ProjectileSpawnCommand
	(
		ScriptableObject projectileData,
		Vector3 position,
		Vector3 direction,
		float speed
	){
		ProjectileData = projectileData;
		Position = position;
		Direction = direction;
		Speed = speed;
	}

	// This method automatically gets executed for all clients when Execute is called.
	protected override void OnExecuted(bool isCommandSender)
	{
		Projectile = Object.Instantiate(projectileData.prefab, position, Quaternion.Euler(direction));
		Projectile.Speed = speed;
	}
}

But ChatGPT’s idea of raising an event, is also an interesting idea.

We started out abstracting our networking but it gets painful fast in a network heavy game like ours so we refactored and instead injected a fake networking socket for the single player elements of our game.

Though our game is a MP game first and a SP game second.

Edit: im not saying above is wrong though, just food for thought

Oh yes this makes more sense, thanks a lot

Thats also interesting idea i will keep in my mind, thanks