Skip to content

Experimental/singleton removal merge#495

Closed
mattwalsh-unity wants to merge 10 commits intodevelopfrom
experimental/singleton-removal-merge
Closed

Experimental/singleton removal merge#495
mattwalsh-unity wants to merge 10 commits intodevelopfrom
experimental/singleton-removal-merge

Conversation

@mattwalsh-unity
Copy link
Copy Markdown
Contributor

This is a draft PR so we can have a place to discuss the singleton removal

@unity-cla-assistant
Copy link
Copy Markdown

unity-cla-assistant commented Feb 24, 2021

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

@mattwalsh-unity mattwalsh-unity requested review from 0xFA11, JesseOlmer and TwoTenPvP and removed request for TwoTenPvP February 24, 2021 18:46
@mattwalsh-unity mattwalsh-unity marked this pull request as draft February 24, 2021 18:48
Comment on lines +7 to +8
// TODO: put this back!!!
/*namespace MLAPI.EditorTests
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is definitely not acceptable :)
I did this because I can no longer use var inReader = PooledBitReader.Get(inStream) API

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've fixed this is the new equivalent file (NetworkSerializerTests), although my solution requires a NetworkManager to be present in the test scene.

Comment on lines 643 to 645
NetworkConfig.NetManager = this;

rpcQueueContainer = new RpcQueueContainer(this, false, LoopbackEnabled);
rpcQueueContainer = new RpcQueueContainer(this, false);
//Note: Since frame history is not being used, this is set to 0
//To test frame history, increase the number to (n) where n > 0
rpcQueueContainer.Initialize(0);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this function and OnSingletonReady event should be gone, probably.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, removed it.

List<INetworkedVar> networkedVarList, Stream stream, ulong clientId, NetworkedBehaviour logInstance)
{
using (PooledBitReader reader = PooledBitReader.Get(stream))
using (var reader = networkingManager.PooledBitReaders.GetReader(stream))
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I get the singleton-side change. But, out of curiosity, what's the rationale for using var instead of the type ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's kind of like the 'auto' specifier in C++.

/// </summary>
public static NetworkingManager GetAnyNetworkingManager()
{
return FindObjectOfType<NetworkingManager>();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While I get this is just editor code FindObjectOfType is one of the worst functions ever! Also assume we have more than one manager in a scene that has different settings does that matter ?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We had similar discussions over at the RFC-side in case you'd like to contribute :)

if(NetworkingManager.Singleton != null && NetworkingManager.Singleton.IsServer)

//FIXME: Singleton Conversion, get all of them and be sure to get the server!
NetworkingManager manager = NetworkingManagerEditor.GetNetworkingManager(true);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I think generally in the non-singleton case I would expect the editors to show only useful data if the actual object being selected is a NetworkManager... I just don't think that picking the first thing you see if actually useful.

Also FindObjectOfType doesn't guarantee order so you could call it more than once and get different objects which doesn't seem like the desired behavior either.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is probably the #1 unresolved issue in this addition

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I called out something similar on the RFC-side, inlining my suggestions here too:

Rather than finding NetworkManager with GameObject.FindWithTag, it might be useful to have a static NetworkManager[] array globally where we could quickly iterate over and find the one we're looking for.

Similar to what I said above, we could store NetworkManager instances in a static NetworkManager[] container to avoid Object.FindObjectOfType call.

(response to Matt's Dictionary idea)
oh, static array or hashmap, the main idea being avoiding gameobject hierarchy traversal with Object.FindObjectOfType because seems like it could be unnecessary with a faster centralized static container lookup.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I think one way or another we'll want a central table of NMs

/// The NetworkingManager that owns this Object.
/// </summary>
public NetworkingManager NetworkManager => NetworkingManager.Singleton;
public NetworkingManager NetManager { get; internal set; }
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did we change the name to NetManager ?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, we shouldn't change that like this — we're going to have a renaming RFC soon where we will specifically discuss naming types.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these are all normalized to NetworkManager now.

return;
}

NetworkingManager networkingManager = networkedObjects[0].NetManager;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it guaranteed that every NetworkObject will have the same NetManager?

Copy link
Copy Markdown
Contributor Author

@mattwalsh-unity mattwalsh-unity Feb 26, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@TwoTenPvP this (and the non-static NetworkShow) are not in use. Are these worth keeping around?

If so, we could make this an instance method off of NetworkedManager and if you pass in a NetworkedObject that doesn't belong to this NM, just skip it or throw an exception.

Or, it could be a static method off of NM. It really doesn't seem to have much to to with NetworkedObject. @wackoisgod not every NetworkObject has to have the same NM; but as written it would just show the pile of objects passed in regardless of NM.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was very difficult for me to envision a scenario where you would hand this method a heterogenous list of NetworkObjects, belonging to multiple different NetworkManagers. It seemed like something that could be handled by specifying that as a method precondition in the method's comments, which I've added in my latest changes.

That said, I was anticipating these methods would go away in favor of matt's new visibility work.

@wackoisgod
Copy link
Copy Markdown
Contributor

wackoisgod commented Feb 25, 2021

So I am concerned about that var name change from NetworkManager to NetManager on things as that is a breaking change

container.ticks[i] = ProfilerTick.FromStream(stream);
}
{
container.ticks[i] = ProfilerTick.FromStream(manager, stream);
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same story here, in the FromStream lambda, you can first read the NM id, then de-ref and not need to pass this in.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also was able to break this dep same was as above

using (BitStream stream = new BitStream(binary))
{
using (PooledBitReader reader = PooledBitReader.Get(stream))
using (PooledBitReader reader = NetManager.PooledBitReaders.GetReader(stream))
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How come this has to hang off of NetManager?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed to be a static again

public static void NetworkHide(List<NetworkedObject> networkedObjects, ulong clientId)
{
if (!NetworkingManager.Singleton.IsServer)
if( networkedObjects.Count == 0 )
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same story as NetworkShow, above

if( simulatedObjects.Count > 0 )
{
throw new NotServerException("Only the server can perform lag compensation");
if( !simulatedObjects[0].NetObject.NetManager.IsServer )
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh boy, I guess you could however unlikely, have a collection of simulatedObjects were some are from one NM that is set up as a server and some are as a client. I haven't worked with LagCompensationManager though.

Is this telling us that this should be an instance method? There should be an LCM per NM?

public static void Simulate(NetworkingManager networkingManager, ulong clientId, Action action)
{
if (!NetworkingManager.Singleton.IsServer)
if (!networkingManager.IsServer)
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, again, maybe this is telling us this should be an instance method.

namespace MLAPI.Messaging
{
internal static class InternalMessageSender
internal class InternalMessageSender
Copy link
Copy Markdown
Contributor Author

@mattwalsh-unity mattwalsh-unity Feb 26, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could leave this class static (and stateless) and just pass in NM in each send. What does the room think? That's more like what the UnnamedMessageSender does

/// <summary>
/// The singleton instance of the NetworkingManager
/// </summary>
public static NetworkingManager Singleton { get; private set; }
Copy link
Copy Markdown
Contributor

@0xFA11 0xFA11 Feb 26, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should get rid of this singleton boss but this PR looks like a mess to me.
(I know this is not a very constructive feedback, sorry).

There are lots of things going on here:

  • NetManager/NetworkManager, NetObject and other naming inconsistencies
  • Assumptions like:
  • Controversial changes like pinning PooledBitReader, InternalMessageSender etc to NetworkManager
    • using (PooledBitReader reader = NetManager.PooledBitReaders.GetReader(stream))
  • other fundamental behavioral changes which could be counted as high risks

Overall, I believe we need to redo/rework singleton removal again and think about design changes more carefully.

However, if I should consider this PR only as a draft that's never going to be merged into develop, then I'm fine :)

This is a draft PR so we can have a place to discuss the singleton removal

I wanted to double-check the statement above.

foreach (KeyValuePair<ulong, Queue<BufferedMessage>> pair in bufferQueues)
{
while (pair.Value.Count > 0 && Time.realtimeSinceStartup - pair.Value.Peek().bufferTime >= NetworkingManager.Singleton.NetworkConfig.MessageBufferTimeout)
while (pair.Value.Count > 0 && Time.realtimeSinceStartup - pair.Value.Peek().bufferTime >= messageBufferTimeout)
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we think we want to have per-NM messageBufferTimeouts or could we simplify and have one shared static one?

public static event UnnamedMessageDelegate OnUnnamedMessage;

internal static void InvokeUnnamedMessage(ulong clientId, Stream stream)
internal static void InvokeUnnamedMessage(NetworkingManager networkingManager, ulong clientId, Stream stream)
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When I look at this function I really wonder what if we mad this an instance method off of NetworkingManager? It's weird to have a static method that takes an instance and then does most of its work with that instance.

// Batcher object used to manage the RPC batching on the send side
private readonly RpcBatcher m_RpcBatcher = new RpcBatcher();
private const int k_BatchThreshold = 512;
private NetworkingManager m_NetManager;
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So it appears we have an NM instance in this class so that:

  • we can get an NM-specific RPC Queue Container, which I have some doubts on is the right abstraction
  • we can call NM:InvokeRPC (and, in turn, InvokeRPC is only in NM because it needs to find the right spawn manager, and maybe there's a cleaner way to do that)
  • we can call NM:MessageSender.Send()

/// <param name="stream">The stream containing the ProfilerTick data</param>
/// <returns>The ProfilerTick with data read from the stream</returns>
public static ProfilerTick FromStream(Stream stream)
public static ProfilerTick FromStream(NetworkingManager manager, Stream stream)
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NM is being passed in so that ProfilerTickData can get an NM so that it can get a PooledBitReader, which - not to belabor the point - should be detached from NM

/// </summary>
public event OnClientLoadedSceneDelegate OnClientLoadedScene;

private NetworkingManager networkingManager;
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

inconsistent, is netManager everywhere else


NetworkEventType eventType = relayTransport.ReceiveFromHost(hostId, out int connectionId, out int channelId, messageBuffer, messageBuffer.Length, out int receivedSize, out byte error);

if(eventType != NetworkEventType.Nothing )
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you mean to leave this in?

Copy link
Copy Markdown
Contributor Author

@mattwalsh-unity mattwalsh-unity left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mostly want to have a discussion on where we can break the NetworkManager dependencies

ulong networkId = ReadUInt64Packed();

if (networkingManager.SpawnManager.SpawnedObjects.ContainsKey(networkId))
var netManager = SpawnManager.SpawnedObjectsByNetworkingManager[networkId];
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After talking to @TwoTenPvP, I've come to see this is probably not correct code. Instead, I intend to remove this table and instead pass the NM into ReadObjectPacked.

{
foreach (var manager in FindObjectsOfType<NetworkingManager>())
{
if (manager.IsServer == isServer)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no, that's for the NetworkedObjectEditor.cs in the Editor, not at Runtime I believe, no?

@dwoodruffsf
Copy link
Copy Markdown

Just a heads up that I have updated this branch to fix all merge conflicts with develop. The branch is mergeable (at least as of this comment), but for some reasons, neither my latest changes, nor the merge status, is showing properly in this PR (maybe because this is Matt's PR, and it still represents the merge at the last commit that Matt made it?).

@dwoodruffsf
Copy link
Copy Markdown

Follow on note: I have finished an integration test against the BossRoom sample (which turned up a ton of problems!) I have fixed all of them, and I now think this PR could probably be taken (or rather a new one, from the latest commit on this branch). A couple of interesting things did change:

  1. Added ActiveScene, FindObjectsOfTypeInScene, and FindGameObjectsWithTagInScene methods to NetworkManager. These are worth looking at; they encapsulate the weirdness of what it means to say a NetworkManager is "associated" with a scene, and have usability considerations, particularly for devs that are setting DontDestroyOnLoad on their NetworkManagers.

  2. Made NetworkWriterPool symmetric with NetworkReaderPool; now they both live on NetworkManager, even though NetworkWriterPool doesn't really need NetworkManager for anything except logging. This is basically the opposite direction that Matt was going where he was trying to break the dependency between NetworkManager and the serialization layer. What would you guys think about just removing the Read/Write(GameObject) magic? This seems like a low-value feature to me, and it's causing lots of problems with this refactor--without it we could easily make the NetworkReader/Writer pools static, without hitting the concerns I expressed to Matt in a side thread.

@TwoTenPvP
Copy link
Copy Markdown
Contributor

@mattwalsh-unity Can this be closed?

@JesseOlmer
Copy link
Copy Markdown
Contributor

@mattwalsh-unity can we close this?

@0xFA11 0xFA11 deleted the experimental/singleton-removal-merge branch May 19, 2021 17:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants