Race Condition Registering Prefabs on Connect / Scene Load?
  • Vote Up0Vote Down Smooth_PSmooth_P
    Member
    410 Points
    I've noticed that occasionally when my clients connect they fail to initialize properly, seemingly because prefabs are not registered. Here is my setup / process, is there anything I should change?

    Relevant connection manager code:


    public static void LoadLevel(GameModes.GameMode gameMode) {
    if (uLink.Network.isServer) {
    uLink.Network.RemoveAllRPCs();
    Current.networkView.RPC("LoadLevelRpc", uLink.RPCMode.All, gameMode);
    }

    }

    [RPC]
    protected void LoadLevelRpc(GameModes.GameMode gameMode, uLink.NetworkMessageInfo info) {
    if (info.sender.isServer) {
    startOffset = info.timestamp;
    Application.LoadLevel(GameModes.GameModeToSceneName[gameMode]);
    }
    }

    protected void uLink_OnServerInitialized() {
    LoadLevel(BasePreferences.GameMode.Value);
    }

    protected void uLink_OnPlayerApproval(uLink.NetworkPlayerApproval approval) {
    approval.Approve(startOffset, GameModes.SceneNameToGameMode[Application.loadedLevelName]);
    }

    protected void uLink_OnConnectedToServer(System.Net.IPEndPoint server) {
    startOffset = uLink.Network.approvalData.Read();
    Application.LoadLevel(GameModes.GameModeToSceneName[uLink.Network.approvalData.Read()]);
    }



    Scene Setup:

    uLinkRegisterPrefabs in scene
    GameMode subclass (read: main game controller) in scene with manual ID. This class does all the heavy lifting, instantiating of players, sync handling, etc.

    Script Execution Order:

    uLink stuff at default settings (I haven't changed them), and have the highest priority in the project.
    uLinkNetworkView -10002
    uLinkNetworkP2P -10001
    uLinkRegisterPrefabs -10000 // Is this right? It seems like this should have the highest priority...

    GameMode -1

    uLinkInternalHelper 10000

  • 39 Comments sorted by
  • Vote Up2Vote Down Martin SegerstenMartin Segersten
    MuchDifferent Developer
    275 Points
    Accepted Answer
    Hello folks,

    If you are still experiencing this error, please try and set the flag uLink.Network.isMessageQueueRunning to false right before calling Application.LoadLevel(), and then set it back to true in the OnLevelWasLoaded() callback.

    This will prevent uLink from processing any network messages while Unity is loading a level. The order in which they were received will be intact, and no messages will be dropped (as long as they were received by uLink in the first place).

    Please report back your results if this impacts the behaviour and occurrence of the error.
  • I don't think that is the case or else it would never work.

    I did find a workaround and I also think I found why. In my Lobby I am using


    private void uLink_OnPlayerApproval(NetworkPlayerApproval playerApproval)
    {
    if (m_players.Count < uLink.Network.maxConnections)
    {
    playerApproval.Approve("Client_Game");
    }
    else
    {
    playerApproval.Deny(uLink.NetworkConnectionError.TooManyConnectedPlayers);
    }
    }



    then in lobby clientside I use


    void uLink_OnConnectedToServer(System.Net.IPEndPoint server)
    {
    string scene = uLink.Network.approvalData.ReadString();

    Application.LoadLevel(scene);
    }


    and then on the game server I was using


    private void uLink_OnPlayerConnected(uLink.NetworkPlayer player)
    {
    uLink.Network.Instantiate(player, proxyPlayerPrefab, localPlayerPrefab, serverPlayerPrefab, new Vector3(0, playerHeight, 0), Quaternion.identity, NetworkGroup.unassigned);
    }



    so both the loading of the scene and the instantiating were happening on connection. I'm assuming that the scene was not fully loaded yet before the instantiation took place, which made it fail.

    So instead I I changed it to this

    Client

    void Start()
    {
    networkView.RPC("OnPlayerJoinedScene", uLink.RPCMode.Server);
    }


    Server

    [RPC]
    void OnPlayerJoinedScene(uLink.NetworkMessageInfo info)
    {
    uLink.Network.Instantiate(info.sender, proxyPlayerPrefab, localPlayerPrefab, serverPlayerPrefab, new Vector3(0, playerHeight, 0), Quaternion.identity, NetworkGroup.unassigned);
    }


    I hope that helps anybody who's having the same issue, though this does leave me with one other problem. When I join game with someone else in it already I don't always get that player(proxy) instantiated in my scene. If I cant solve that I create a different thread about it.

    Regards
  • Vote Up1Vote Down mk26mk26
    Member
    50 Points
    If uLink is working fine for all platforms, then why my code is not working for iOS, while the same code is working fine for Mac and Windows client. Our server is on Amazon with headless Linux builds.
    See my issue on http://forum.muchdifferent.com/unitypark/index.php?p=/discussion/comment/5434

    Any suggestion on it will be appreciated
  • Vote Up0Vote Down ArcanorArcanor
    Member
    735 Points
    Did you figure this out? I'm noticing the same problem with my supposedly registered prefab (Player object) not instantiating while streaming in an assetbundle containing the level assets (which I then load with LoadLevelAdditive).

    In my limited testing, I found that if I changed the uLinkRegisterPrefabs script priority to be earlier (above uLinkNetworkView) the Player fails to instantiate every time. When I moved uLinkRegisterPrefabs down to just above uLinkInternalHelper it seemed to work correctly. However, I don't know what other effects that might have.
  • Vote Up0Vote Down Smooth_PSmooth_P
    Member
    410 Points
    I never did figure it out and have been hoping from advice from UnityPark as its an extremely annoying and important issue that should have a proper solution, but they haven't been active on the forums lately.

    And, yeah, increasing the priority of uLinkRegisterPrefabs totally breaks everything.
  • Vote Up0Vote Down ArcanorArcanor
    Member
    735 Points
    What about decreasing the priority as I suggested? It seemed to work for me.
  • Vote Up0Vote Down Smooth_PSmooth_P
    Member
    410 Points
    It seems counter-intuitive, but I guess I may as well try it. Especially since the "intuitive" change hoses networking completely. The problem is fairly intermittent for me though so without word from UnityPark "seems to work" is the best we can do. :)

    My code is in the midst of a massive refactoring right now so I won't be able to test it for at least a day, but I'll post again if I get another of these errors.
  • Vote Up0Vote Down ArcanorArcanor
    Member
    735 Points
    My thinking is that since there seems to be a race condition we should try to make sure one or the other always wins the race. Since decreasing the priority of uLinkRegisterPrefabs seems to work it must mean that there is something in the loading of the assetbundles level that must be completed before uLinkRegisterPrefabs can do its magic.

    I don't know why this would be, but maybe MuchDifferent will chime in at some point to explain, or possibly just tell us we're entirely wrong. ;)

    EDIT: you didn't actually mention assetbundles, so what I really meant was LoadLevel.
  • Vote Up0Vote Down ArcanorArcanor
    Member
    735 Points
    After further testing my solution does not work consistently either. There are still definitely times (about 50% of the time for me) when the Player@Owner prefab is not instantiated upon connection to the zone server.

    I'm going to debug this further and report back.
  • Vote Up0Vote Down ArcanorArcanor
    Member
    735 Points
    Apparently my problem is not dependent on uLinkRegisterPrefabs at all. I moved my Player@Owner prefab to Assets/Resources. On the 3rd try of starting the game client (and immediately closing it), the Player didn't Instantiate. Then it worked again on the 4th, 5th, and 6th, but not the 7th, then it worked on the 8th... so it seems totally random.

    I'm not sure if this is the same problem as you're having, but this problem is pretty serious! Instantiate() does not work reliably for me, even when the object is in Assets/Resources. This is a major problem! I would hate to have to verify every Instantiate() with RPC callbacks...

    Is anyone else seeing this kind of issue?
  • Vote Up0Vote Down Staffan EinarssonStaffan Einarsson
    MuchDifferent Developer
    330 Points
    Hi,

    Does anyone of you have a minimal project that can reproduce the problem, that you can send? I know this is a random effect but we need to have something to confirm it with on our side.

    Please send it to support@muchdifferent.com.
  • Vote Up0Vote Down ArcanorArcanor
    Member
    735 Points
    I will create a minimal project and send it to you. Thanks Staffan! :)
  • Vote Up0Vote Down ArcanorArcanor
    Member
    735 Points
    I'm trying to create a "minimal" project but I'm realizing I don't know how I can do that, considering the fact that this is an MMO setup with 7 different moving parts right now.

    Here is a graphic showing my network topography to implement this game:
    image of network topography

    Staffan... do you have any advice as to how I can create a minimal project to send you?

    EDIT: updated network topography image URL
  • Vote Up0Vote Down ArcanorArcanor
    Member
    735 Points
    Okay! I've made progress in isolating the problem I'm seeing. I'm not sure if this is the same as Smooth_P's original reported issue, and if not I apologize for hijacking the thread. ;)

    Anyway, I've isolated my problem down to the following code which I got from the UnityPark Suite demo's zone1@server scene, in the file GameServer.cs. In the function uLink_OnPlayerConnected(), the server is attempting to do this:

    uLink.Network.Instantiate(player, proxyPrefab, ownerPrefab, serverPrefab, Vector3.zero, Quaternion.identity, 0);

    As I mentioned above, this was only actually creating the Player@Owner object on my client about 50% of the time, unpredictably.

    I made a change to the above call, and now it is working 100% of the time! After reviewing the documentation for Network.Instantiate() I was extremely confused as to how the server could use a GameObject as the 2nd-3rd-4th arguments, since those GameObjects are not yet existing on the client. Even more confusing, in the GameServer.cs code that's in the demo, those three arguments (proxyPrefab, ownerPrefab, serverPrefab) aren't even GameObjects, they are Transforms. I simply don't understand how the server can be using a Transform to identify something on the client that doesn't exist yet.

    So, I used an alternative form of Network.Instantiate() that uses strings instead of GameObjects or Transforms. Here is the code that works 100% for me:

    uLink.Network.Instantiate(player, "Player@Proxy", "Player@Owner", "Player@Creator", Vector3.zero, Quaternion.identity, 0);
  • Vote Up0Vote Down ArcanorArcanor
    Member
    735 Points
    Scratch that. Today I'm doing more testing and it seems that the string version of Network.Instantiate() is also being inconsistent. So it must be something different, and I don't know where to begin searching.

    Network.Instantiate() does not work for me, about 50% of the time, inconsistently.
  • Did anybody ever figure out a solution to this? I am experiencing the same issue. It seems to mostly happen on my device and not so much in the editor. I have since moved all my prefabs to under my resources folder so I no longer need to use the register prefabs script, but it is still happening.

    I am getting an exception saying "Owner state with view ID 2001 was dropped because it had no network view"

    to clarify, I am using the code

    uLink.Network.Instantiate(player, proxyPrefab, ownerPrefab, serverPrefab, Vector3.zero, Quaternion.identity);

    and all of the prefabs definitley have a networkView on it. On the device it seems to happen about 50% of the time
  • Vote Up0Vote Down AshkanAshkan
    MuchDifferent Developer
    1445 Points
    I think you guys are not including all prefabs in server's registeration's list which is instantiating.
    You should include all prefabs in the instantiator's list and only the prefabs you want to instantiate in the others (clients probably).

    We've used the feature always and never had a problem like this.
  • Thanks for the response Ashkan, in my case I have every single prefab under the resources folder directly and it still happens. The documentation says that you should not need the register prefabs script if all your prefabs are directly under resources. That is correct right? Right now its just a prototype so I'm not concerned about asset bundles and resources management.

    If I create a new server and join it with the client it seems to be happening about 10% of the time. If I try to join a server with someone with someone else already in it I get the error about 80% of the time.

    again, the error I get is "Owner state with view ID 2001 was dropped because it had no network view". Then all I have is a black screen because I'm assuming the prefab it couldn't create is the one that contains the camera.

    I really need to get this resolved asap so any suggestions are welcome.

    Regards
  • I thought maybe it was trying to instantiate the prefab before it was available somehow so I changed my code this so it would have a delay before instantiating/ However, it only made things worse. It screwed things up pretty bad i would only see some players and players would be left over after they quit. Why would delaying the substantiation do that?


    private void uLink_OnPlayerConnected(uLink.NetworkPlayer player)
    {
    StartCoroutine(SpawnPlayer(player));
    }

    IEnumerator SpawnPlayer(uLink.NetworkPlayer player)
    {
    yield return new WaitForFixedUpdate();

    uLink.Network.Instantiate(player, proxyPlayerPrefab, localPlayerPrefab, serverPlayerPrefab, new Vector3(0, playerHeight, 0), Quaternion.identity, NetworkGroup.unassigned);
    }
  • One thing that might be an issue is that the prefab that is getting instantiated has outlets to other prefabs, which are in the resources folder, which he instantiates instances of when it starts. Those prefabs do not have network views on them because they are only local instances. Could that be a problem?

    Edit: I tried it and it did not help
  • Vote Up0Vote Down AshkanAshkan
    MuchDifferent Developer
    1445 Points
    As much as i know you need to instantiate the prefab in the resources folder using overloads of Instantiate which take string as arguments and not the overload which takes prefab references.

    There is no way to find a prefab in resources folder without having it's reference. Test with the string version and see if it solves the problem or not.
  • Vote Up0Vote Down ArcanorArcanor
    Member
    735 Points
    Accepted Answer
    jingtao said:
    so both the loading of the scene and the instantiating were happening on connection. I'm assuming that the scene was not fully loaded yet before the instantiation took place, which made it fail.

    This sounds correct to me also. In my testing I was also having the (inconsistent) failures happen when there was a race condition between the server instance starting (since my test player was the only player), and the player getting instantiated into that server instance.
  • Vote Up0Vote Down AshkanAshkan
    MuchDifferent Developer
    1445 Points
    You guys are right. You should either delay the instantiations using the uLink_OnPreBufferedRPCs callback or using a client sent RPC to server which says now i'm ready to receive game data.
  • You guys are right. You should either delay the instantiations using the uLink_OnPreBufferedRPCs callback or using a client sent RPC to server which says now i'm ready to receive game data.


    Yeah... no. That doesn't work when you're relying on getting events in the proper order, which is one of the core, fundamental features of the framework. uLink needs to handle queuing up and delaying all RPCs on the client side during scene load in this situation.
  • Vote Up0Vote Down ArcanorArcanor
    Member
    735 Points
    Accepted Answer
    I agree with Smooth_P. Why should we need to do such a workaround? Shouldn't network messages be sent by the framework in the proper order, rather than requiring us to juggle them?
  • Still happening in 1.5.6...
  • Based on anecdotal evidence, it is *highly* likely that 1.5.6 makes this happen much more frequently. Maybe the under the hood improvements are processing the RPCs faster than before and thus winning the race between scene load and instantiation more often?

    I used to see this pretty rarely, and I don't ever remember having to reconnect more than twice to get a proper scene load. Since upgrading I feel lucky when I *don't* have it happen and I've often had to try reconnecting so many times in a row that I lose count of how many attempts it takes...
  • Vote Up0Vote Down aidinabediaidinabedi
    MuchDifferent Developer
    735 Points
    Hello,

    @Smooth_P (or @Arcanor?), could you please send me a project that reproduces the issue at support@muchdifferent.com? I know it might seem like it shouldn't be necessary but trust me it definitely is. Code says a thousand words. So please send me a small project and I'll be able to understand the issue quicker and fix it sooner.

    Sincerely,
  • I actually *think* I managed to fix this by putting the register prefabs script onto the same GameObject as my main game controller class. Which is strange because I'd tried similar things that should have had the same effect in the past that didn't help. But given how infrequently the error used to occur I stopped trying until the recent flareup.

    Race conditions are tricky, and Unity's fundamental design leads directly to race condition hell, but I'm keeping my fingers crossed that this solution holds up.
  • I spoke too soon. Of course after testing with half a dozen clients several times and never getting the error (as opposed to nearly every time with each client after the first before the change), as soon as I pushed the changes live I got a bad scene load. But the new ordering seems to make it happen a lot *less*, so I've got that going for me, which is nice.

    I'd make a project for you guys to check out, but that would require a lot of UI and tedium just to get to the point of establishing client / server connections, while the part pertaining specifically to the error would only take a couple minutes. Do you have a barebones project with all the gruntwork done can be fleshed out with error specific setup / code?
  • I ended up completely rewriting my initialization / scene change code to not actually use scenes. Messing with it some more I realized that scene load instantiation of networked GameObjects was just far too racey non-deterministic / undocumented / doesn't-do-what-it-says-it-does to work reliably.
  • Vote Up0Vote Down ArcanorArcanor
    Member
    735 Points
    I don't have a project I can send, since I'm not yet using my networking code (working on the client side).

    But @aidinabedi, were you able to replicate this problem?
  • Vote Up0Vote Down aidinabediaidinabedi
    MuchDifferent Developer
    735 Points
    Hello @Arcanor!

    Unfortunately, I haven't been able to reproduce it. If you can send support@muchdifferent.com a simple project it would help me tremendously.
  • Vote Up0Vote Down ArcanorArcanor
    Member
    735 Points
    Thanks for your prompt follow-up Aidin! :)

    It will take quite a bit of work for me to create a sample project, unfortunately. I'm not currently even running any zone server, just testing Lobby/Client stuff.

    I'm hoping to start working with a zone server again in the next few weeks, so hopefully I can send you something after that point, once I see the problem again.
  • Vote Up0Vote Down Smooth_PSmooth_P
    Member
    410 Points
    @aidinabedi:

    If you post the code you're using to try and reproduce this, I'm pretty sure I can tweak it to get this to happen.

    Though with a race condition, reproducing it isn't going to help you fix it. You're still going to have to look into the code and see what happens on scene load. Do you have a way to determine the exact moment when Application.LoadLevel() is called, and do you pause / buffer all RPCs until the scene load is complete?

    If not, well, there's a race condition there where RPCs can get in for objects that don't exist yet on the receiver. Or for instantiates for prefabs that aren't registered yet.
  • Vote Up0Vote Down YukichuYukichu
    Member
    1220 Points
    Is it possible to add this functionality into future builds?
  • Vote Up0Vote Down Smooth_PSmooth_P
    Member
    410 Points
    @Martin Segersten

    That sounds like it would work, but I've already refactored my application to not use scene loads and manually keep every object in a parent that gets destroyed when I want to manually change "scenes".

    Maybe I'll undo some of that at some point, as it would be much less maintenance heavy (and a tiny bit faster) to not have so much transform nesting.
  • Vote Up0Vote Down Martin SegerstenMartin Segersten
    MuchDifferent Developer
    275 Points
    Yukichu;

    If you are referring to uLink.Network.isMessageQueueRunning, this is already available in the current uLink release.

    Smooth_P;

    Okay, I see. One possible workaround would be to treat an OnDestroy() message of the "mother"-object in which you set this flag. It would look something like this:

    void OnDestroy()
    {
    uLink.Network.isMessageQueueRunning = false;
    }

    and then in an Start() method of every "mother"-object, a subsequent call that sets it to true once the "scene" has been "loaded";

    void Start()
    {
    uLink.Network.isMessageQueueRunning = true;
    }


    Regards,
    Martin
  • Martin's solution worked for me.

Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!