Multiplayer code structure with controllers - How to make it work?

I am attempting to create a code structure for a multiplayer game, of which I expect the codebase to become very large over time.
For now I am using blueprints, but for the future I would be open to using C++.

Currently my findings have been that creating dedicated controller blueprints works really well in organizing all code.
For example, a weather controller blueprint could manage time of day and cloud density.
And a traffic controller blueprint could manage the density of traffic, and make sure traffic seems equally spread across the map.
These are very helpful when creating a singleplayer game.
However, when switching to multiplay I find it difficult to have the controllers accessible for all clients and have them replicate their functions.

This is my current structure.
The GameMode will at BeginPlay check if the needed controllers are present in the game with a GetActorOfClass node.
If they can not be found, because maybe the level designer did not have need to change the default values for a controller, the GameMode will spawn them.
Then the GameMode stores these controllers in variables in the GameState, as it was my understanding that the GameState can be accessed by all.

Each controller would of coarse have many variables themselves, for the weathercontroller example that could be CloudDensity, CloudColour, WindDirection etc.
But since the GameState only stores a reference to the controllers it would still be clear and organized.
If however each controller had to have all of its variables also declared in the GameState, then it would end up with hundreds of variables.

It sounded good to me in theory, but I just cant get it to work.
If for instance I open an ingame menu with a widget blueprint on a client, I cant seem to access a function of a needed controller.
For example, a menu which allows me to change the time of day and access a ChangeTime function in the weather controller.
The server however can access the controller.

Taking a wild guess I suspect the variable stored in GameState refers specifically to the controller on the server.
And although it is replicated, the controllers on the clients do not link to this variable.
This is my guess because when in a client, getting the GameState, and then getting the variable with the controller, it turns up empty.
But the server does get a valid variable if it does the same thing.

Is this the correct way to go, or would you have a different approach?
If this is a valid structure, how would I store references to the controllers, and how would a client access functions of a controller through a widget?

It seems the game mode has one set player controller class at any given time which might be why your other controller variables aren’t in scope. You could easily derive all your functions from a central location or player controller to make all the changes you mentioned, but world level changes are probably going to be problematic from a replication stand point. I feel like time of day and or weather-related stuff would be a good candidate for communication from the Game State and Game Mode and less so much of a player controller situation, but that’s just me.

You could use switches or use branches on the player controller to change between the functions if you were running off begin play or just call them from the game state’s player array and cast to the player states on a for each loop and from there call the respective player controller and it’s most likely going to be the player controller type set by the game mode.