RaiseEvent seems to not execute properly.

Hi guys,

Sorry to post again, but I hit another wall that is baffling me and I am not sure why. So basically I have a ScriptableObject PoolManager script. I am using PhotonNetwork.RaiseEvent to instantiate a couple of hundred objects using Instantiate to avoid using PhotonViews on them. But since this is a pool manager as you guessed it should activate and deactivate objects as needed. Currently, it half works and I am not sure why -- some events get called and other events get skipped on clients.

First here is the code for registering/unregistering:

public void SubscribeEvent()
    {
        // Since we can't use the adding or removing events from OnDisable or OnEnable, we unsubscribe from the event calls just when we add it again. Since SOs keep state after the game ends. 
        PhotonNetwork.OnEventCall -= OnEvent;
        PhotonNetwork.OnEventCall += OnEvent;
    }
Then we have the OnEvent method:

PhotonEventCode is an enum to help me keep track of the events and easily send/receive their "code" as you can see.

private void OnEvent(byte _eventCode, object _content, int _senderID)
    {
        switch (_eventCode)
        {
            case (byte)PhotonEventsCode.INITIALIZE_POOL:
                InitializePool();
                break;
            case (byte)PhotonEventsCode.ACTIVATE_PLAYER_PROJECTILE:
                Vector3[] positions = _content as Vector3[];
                ActivatePlayerProjectile(positions[0], positions[1]);
                break;
            case (byte)PhotonEventsCode.ACTIVATE_ENEMY_PROJECTILE:
                Vector3[] positionsArray = _content as Vector3[];
                ActivateEnemyProjectile(positionsArray[0], positionsArray[1], (byte)positionsArray[2].x);
                break;
            case (byte)PhotonEventsCode.ACTIVATE_RESOURCES:
                ActivateResourcesEvent((Vector3)_content);
                break;
            case (byte)PhotonEventsCode.DISABLE_ENEMY_PROJECTILE:
                byte index = System.Convert.ToByte(_content);
                DeactivateGameObjectEvent((byte)PhotonEventsCode.DISABLE_ENEMY_PROJECTILE, index);
                break;
            case (byte)PhotonEventsCode.DISABLE_PLAYER_PROJECTILE:
                byte index1 = System.Convert.ToByte(_content);
                DeactivateGameObjectEvent((byte)PhotonEventsCode.DISABLE_PLAYER_PROJECTILE, index1);
                break;
            case (byte)PhotonEventsCode.DISABLE_RESOURCES:
                byte index2 = System.Convert.ToByte(_content);
                DeactivateGameObjectEvent((byte)PhotonEventsCode.DISABLE_RESOURCES, index2);
                break;
        }
    }
Finally I have the RaiseEvent call:

public void InitialResourcesSpawn()
    {
        for (int i = 0; i < maxResourcesCount; i++)
        {

            Vector3 position = Vector3.one;

            while (Vector3.Distance(lastResourcePosition, position) < minDistanceBetweenResources)
            {
                position = Vector3.zero + (Random.insideUnitSphere * maxResourcesCount * 5);
                position.y = 1;
            }

            PhotonNetwork.RaiseEvent((byte)PhotonEventsCode.ACTIVATE_RESOURCES, position, true, new RaiseEventOptions { Receivers = ReceiverGroup.All });

            lastResourcePosition = position;
        }
    }
The InitialResourcesSpawn call gets called on SceneLoaded after the player is spawned and the GameManager is activated. The InitialResourcesSpawn is called from the Master Client only and using a .isMasterClient check to verify.

For some reason I am getting this error:

Warning: Unhandled event Event 0.. Set PhotonNetwork.OnEventCall.
UnityEngine.Debug:LogWarning(Object)
NetworkingPeer:OnEvent(EventData) (at Assets/Photon Unity Networking/Plugins/PhotonNetwork/NetworkingPeer.cs:2735)
ExitGames.Client.Photon.PeerBase:DeserializeMessageAndCallback(Byte[])
ExitGames.Client.Photon.EnetPeer:DispatchIncomingCommands()
ExitGames.Client.Photon.PhotonPeer:DispatchIncomingCommands()
PhotonHandler:Update() (at Assets/Photon Unity Networking/Plugins/PhotonNetwork/PhotonHandler.cs:158)


and this one

Warning: Unhandled event Event 3.. Set PhotonNetwork.OnEventCall.
UnityEngine.Debug:LogWarning(Object)
NetworkingPeer:OnEvent(EventData) (at Assets/Photon Unity Networking/Plugins/PhotonNetwork/NetworkingPeer.cs:2735)
ExitGames.Client.Photon.PeerBase:DeserializeMessageAndCallback(Byte[])
ExitGames.Client.Photon.EnetPeer:DispatchIncomingCommands()
ExitGames.Client.Photon.PhotonPeer:DispatchIncomingCommands()
PhotonHandler:Update() (at Assets/Photon Unity Networking/Plugins/PhotonNetwork/PhotonHandler.cs:158)


What is baffling about this whole thing is, despite getting the Event 0 error, the event is actually executed and the Initialization of the pool (which has the Instantiation code) happens on both Master Client and clients. Weirder is Event 3's error. It executes just fine on Master Client and I can see the objects being activated but on clients all objects are deactivated.

Any ideas why is this occurring?

Thanks in advance.

Answers

  • Hi @Vallar,

    those two warnings are logged, when there is no registered OnEvent handler. This means that PhotonNetwork.OnEventCall += OnEvent; hasn't been called when the client receives a custom event from the server. When do you call your SubscribeEvent function? To check if there is some kind of a timing issue (registering the OnEvent handler and receiving custom events), I would recommend you adding some Debug.Log calls to your SubscribeEvent function and check the console (or application log) after running the game: it would be interesting to see, if the above mentioned warnings are occurring before you call the SubscribeEvent function.

    What is baffling about this whole thing is, despite getting the Event 0 error, the event is actually executed and the Initialization of the pool (which has the Instantiation code) happens on both Master Client and clients. Weirder is Event 3's error. It executes just fine on Master Client and I can see the objects being activated but on clients all objects are deactivated.


    Okay, this is what you mean by halfway working: Event 0 is working and event 3 is not working on any other than the MasterClient. Can you check if the event is processed on the other clients as well?

    Hint: after getting this working you maybe should try to find a solution, where you call RaiseEvent just once (in relation to the third code snippet) instead of using it in each iteration of the for-loop. You can for example use a Dictionary (if the position is related to a certain object) or an array where you store all the position data. But as said: this can be done after fixing the main issue.

    Since I didn't ask in the other topic: is it necessary to use a ScriptableObject or could you replace it by using a 'standard' MonoBehaviour? I'm just asking because I don't have any references for checking issues with ScriptableObjects.
  • Hi @Vallar,

    those two warnings are logged, when there is no registered OnEvent handler. This means that PhotonNetwork.OnEventCall += OnEvent; hasn't been called when the client receives a custom event from the server. When do you call your SubscribeEvent function? To check if there is some kind of a timing issue (registering the OnEvent handler and receiving custom events), I would recommend you adding some Debug.Log calls to your SubscribeEvent function and check the console (or application log) after running the game: it would be interesting to see, if the above mentioned warnings are occurring before you call the SubscribeEvent function.
    .

    The event registration happens EXACTLY the call right before the RaiseEvent (so basically I subscribe then call the RaiseEvent):
    
    poolManager.SubscribeEvent();
    PhotonNetwork.RaiseEvent((byte)PhotonEventsCode.INITIALIZE_POOL, null, true, new RaiseEventOptions { Receivers = ReceiverGroup.All });
    
    This is because of the registration and deregistration problem I ran into when using OnEnable and OnDisable with ScriptableObjects. Despite updating to your PUN's 1.9.1 latest update and trying to use OnEnable and OnDisable (since the notes mention it was fixed), the same error occurs. So I just left the code as is.

    I put some Debug.Logs() as you mentioned and surprisingly I don't see the warning about Event0 it isn't a consistent thing. Sometimes I see it, sometimes I don't.

    Either way, the result is surprising; take a look at this screenshot from a client (not master client):

    https://pasteboard.co/HtPklOt.png

    As you can see the Subscription + OnEvent both run in correct order but the warning happens well after they are executed. Moreover, despite the Event0 warning is there, the code is run on both Master Client and normal clients without problems.


    Okay, this is what you mean by halfway working: Event 0 is working and event 3 is not working on any other than the MasterClient. Can you check if the event is processed on the other clients as well?

    Yes, as mentioned and proven above, Event0 runs perfectly fine on all clients. The Editor was a client while the exported .exe was the Master Client.

    On another note, with Event0 (one that activates objects in random positions) the positions are sent in wrong. For some reason on Master Client (the only client that the code runs on) piles them up all in one position despite the code above specifically doing otherwise. Not sure if this is related to Event3's warning, something with PUN or my code.


    Hint: after getting this working you maybe should try to find a solution, where you call RaiseEvent just once (in relation to the third code snippet) instead of using it in each iteration of the for-loop. You can for example use a Dictionary (if the position is related to a certain object) or an array where you store all the position data. But as said: this can be done after fixing the main issue.

    So it is better to send 1 big piece of data (all positions) across the network one time than small pieces over (one position per for loop iteration) a course of whatever time they are executed in? Genuinely asking here, not trying to sound know-it-all or anything (just in case the question comes across as such).


    Since I didn't ask in the other topic: is it necessary to use a ScriptableObject or could you replace it by using a 'standard' MonoBehaviour? I'm just asking because I don't have any references for checking issues with ScriptableObjects.

    Necessary? No. Better? Yes. Basically PoolManager (the Scriptable Object) is just a data container. It has lists of all the objects that are being pooled and then activates and deactivates objects when needed. So it doesn't really need to be a Monobehaviour. But if PUN doesn't like SOs and I must absolutely change it to a Monobehaviour then I have no choice and will do that. But I'd rater keep it like that till I reach the point where it has to be done.
  • Vallar
    Vallar
    edited July 2018

    Hi @Vallar,

    those two warnings are logged, when there is no registered OnEvent handler. This means that PhotonNetwork.OnEventCall += OnEvent; hasn't been called when the client receives a custom event from the server. When do you call your SubscribeEvent function? To check if there is some kind of a timing issue (registering the OnEvent handler and receiving custom events), I would recommend you adding some Debug.Log calls to your SubscribeEvent function and check the console (or application log) after running the game: it would be interesting to see, if the above mentioned warnings are occurring before you call the SubscribeEvent function.
    .

    The event registration happens EXACTLY the call right before the RaiseEvent (so basically I subscribe then call the RaiseEvent):
    poolManager.SubscribeEvent();
            PhotonNetwork.RaiseEvent((byte)PhotonEventsCode.INITIALIZE_POOL, null, true, new RaiseEventOptions { Receivers = ReceiverGroup.All });
    This is to get around the OnEnable and OnDisable error I am getting with ScriptableObjects that we talked about before. I did update my PUN to the latest version where it said this issue is resolved and it wasn't resolved the same error is still occurring so I kept the code as is.

    In any case, I put the Debug.Logs as you mentioned. The warning with Event0 isn't consistent. Sometimes I get it, sometimes I don't. Either way, the surprising part is that the code always runs and the Debug.Log() appear in correct order. However before the log messages appear the warnings appear.

    That said, if Event0 and Event3 aren't running (gleaned from the warning) why are they run (Event0 for example is responsible for spawning objects, with the warning the objects are indeed spawned) and why does Event0 run on both Client and Master Client flawlessly and Event3 runs on Master Client with issues but doesn't even run on Client?


    Okay, this is what you mean by halfway working: Event 0 is working and event 3 is not working on any other than the MasterClient. Can you check if the event is processed on the other clients as well?

    Event 0 always executes on both Master Client and normal Client. Event 3 executes only on Master Client and doesn't execute on Clients.


    Hint: after getting this working you maybe should try to find a solution, where you call RaiseEvent just once (in relation to the third code snippet) instead of using it in each iteration of the for-loop. You can for example use a Dictionary (if the position is related to a certain object) or an array where you store all the position data. But as said: this can be done after fixing the main issue.

    Is it better to send one big piece of data over the network (list of objects) once or multiple smaller pieces? Honestly I want to understand.


    Since I didn't ask in the other topic: is it necessary to use a ScriptableObject or could you replace it by using a 'standard' MonoBehaviour? I'm just asking because I don't have any references for checking issues with ScriptableObjects.

    Necessary, no. Better, yes. Basically PoolManager (the ScriptableObject) is a data container. It just has all the lists of objects that instantiated and will be pooled in the game. Plus some methods to activate and deactivate them. So it doesn't need to be a Monobehaviour. However, if ScriptableObjects don't play nice with PUN and I have to change it, I'll change it (no choice after all).
  • Bump, still looking for a solution to this problem.
  • That said, if Event0 and Event3 aren't running (gleaned from the warning) why are they run (Event0 for example is responsible for spawning objects, with the warning the objects are indeed spawned) and why does Event0 run on both Client and Master Client flawlessly and Event3 runs on Master Client with issues but doesn't even run on Client?


    This is odd.

    I'm currently trying to create a test case similar to your described scenario: I have a ScriptableObject with a method similar to your SubscribeEvent() function, which gets called directly before RaiseEvent. I have two different events (both without content, it's just a simple test case) which I can send without getting the warnings you have described above. I just get them on the second client, who is not raising an event. However he isn't calling SubscribeEvent() either, so this is a wanted behaviour in this certain case. When he sets up the OnEvent handler properly, he doesn't get those warnings anymore and receives both events. So for the non-MasterClients please make sure, that they have set up the OnEvent handler properly - I guess you already have done this.

    As you may have already noticed, I haven't been ably to reproduce the same behaviour (issue) as you have described, so as far as I can say, there aren't problems with using ScriptableObjects - which is good I guess. This however means, that there might be another problem which we have to find. Do you have the chance to create a (simplified) repro case for me where I can take a look at the problem directly?

    Is it better to send one big piece of data over the network (list of objects) once or multiple smaller pieces? Honestly I want to understand.


    I would say so, yes. At least on clients' end you just have to call OnEvent (in this case) once instead of multiple times.
  • So for the non-MasterClients please make sure, that they have set up the OnEvent handler properly - I guess you already have done this.

    What you said made me realize a mistake I did. I forgot I am using Instantiate instead of PhotoNetwork.Instantiate which then can't be propagated over all clients. Moreover I was restricting the Event3 activation of objects to occur only on MasterClient but MasterClient uses Receivers.All to activate the objects. Since Instantiation is local it doesn't activate anything. So when removing the isMasterClient check before raising the event, it worked correctly (all objects now appear in the same locations on all clients).

    However the warning with Event0 and Event3 still persists and still occurs regardless.


    As you may have already noticed, I haven't been ably to reproduce the same behaviour (issue) as you have described, so as far as I can say, there aren't problems with using ScriptableObjects - which is good I guess. This however means, that there might be another problem which we have to find. Do you have the chance to create a (simplified) repro case for me where I can take a look at the problem directly?

    If the warnings are an issue and you still want to investigate this further, I don't mind trying to put together a Unitypackage and sending it.


    I would say so, yes. At least on clients' end you just have to call OnEvent (in this case) once instead of multiple times.



    Alright, may I ask (for clarification) why is OnEvent being called multiple times on the client is bad? Is this related to generally having code executed multiple times? Or is it related to PUN/Networking?
  • Alright, may I ask (for clarification) why is OnEvent being called multiple times on the client is bad? Is this related to generally having code executed multiple times? Or is it related to PUN/Networking?


    Originally I thought about another reason (Method Call Depth), but this doesn't apply here I guess.

    The other thing which came into my mind is the RaiseEvent call itself. Maybe this is also just a personal liking, but in my opinion it is more 'readable' to create one Array with all the data and send this one instead of sending each single position. But as said this might be a personal liking.

    Technically I currently can't give you any advise about which approach is better or if there is a better approach at all.
  • Programming is opinions half the time, I took your advice (or your opinion if you will :) ) and in the process of changing the code to be sent only once with an array rather than being called multiple times.

    If anything I am hoping it could eliminate the warning. :)
  • hello @Vallar I know it was quite a long time ago but did you manage to get rid of the skipped event on clients? I am having the same issue when the event is raised quite a lot (5 to 6 times/sec).