Replies: 15 (Who?), Viewed: 321 times.
Test Subject
Original Poster
#1 Old 5th Jul 2022 at 6:09 PM
Default (Scripting) Targeting Sims when Listening for Social Events
I'm getting null interaction errors while listening for social events. While using the reflector, I noticed that socialevent doesn't seem to have targeting. So, how do I detect the actor sim and target sim while using it?
Advertisement
Virtual gardener
staff: administrator
#2 Old 6th Jul 2022 at 9:54 AM
Hey Yolksims !

Which Social event? Because it may well just be EA passes it in as a null, but the actor is still actually there.

Because generally speaking, EA tends to do the following:

Code:
EventTracker.SendEvent(new SocialEvent(EventTypeId.kSocialInteraction, base.Actor, base.Target, this));
EventTracker.SendEvent(new SocialEvent(EventTypeId.kSocialInteraction, base.Target, base.Actor, this));


Meaning, both target/actor has been parsed in.

So I'm wondering it it's maybe the way you're trying to get the actor/target, if you're 100% sure the event you're listening to, sends the right information.

here's how I'd tackle it though:

Code:
        public ListenerAction OnSocialization(Event e)
        {
            SocialEvent socialEvent = e as SocialEvent;
            
            if (socialEvent != null)
            {

                // Wouldn't 100% recommend this, since we can't always guarantee that "CreatedSim" won't return null or the Simdescription being able to find it. 
                // Reason for it, is because if a sim isn't in the world (Like a service), then the SimDescription exists, but will return null on CreatedSim.
                Sim target = SimDescription.Find(socialEvent.mTargetSimDescription.SimDescriptionId).CreatedSim;


                // This is EA's weird naming convention (but sort of makes sense). While it says "Target" this is actually the Actor.
                Sim actor = socialEvent.TargetObject as Sim;

                // It's always good practise to check for null references  
                if (actor != null || target != null)
                {
                    // DO THING HERE
                }
                else
                {
                    SimpleMessageDialog.Show("Title", "Actor or target were null! Exiting now...");
                }
            }
            return ListenerAction.Keep;
        }
Test Subject
Original Poster
#3 Old 6th Jul 2022 at 5:08 PM Last edited by YolkSims : 7th Jul 2022 at 3:42 AM.
Hi Lyralei!

Right now, I'm checking for ANY social event, so I've got
Code:
private static void OnWorldLoadFinished(object sender, EventArgs e)
        {
            EventTracker.AddListener(EventTypeId.kSocialInteraction, new ProcessEventDelegate(OnInteraction));
        }

and
Code:
private static ListenerAction OnInteraction(Event ev)
        {
            try
            {
                var e = ev as SocialEvent;

                // Define actor and target sims.
                Sim Actor = e.Actor as Sim;
                Sim Target = e.TargetObject as Sim;

After which I'm running a random for random chance of firing and checking the sims against a bunch of conditions, then choosing a romantic social interaction based on those conditions: kiss/first kiss, go steady, propose marriage, woohoo, try for baby, etc.

I should probably mention that I'm not 100% sure it's the target. The code worked with polling (OnUpdate), but now doesn't work now that I'm using a ListenerEvent. I previously had this same problem in this thread, but what I used to fix that isn't working.

Code:
Target.InteractionQueue.CancelAllInteractions();
Sim.ForceSocial(Target, Actor, "Kiss", InteractionPriorityLevel.High, true, false, false);
if (kDebugMode == true)
{
   Sims3.UI.StyledNotification.Show(new Sims3.UI.StyledNotification.Format("NPCRomance DEBUG: Initiating Kiss", StyledNotification.NotificationStyle.kSimTalking));
}


Edit: I managed to catch the error again.
Forum Resident
#4 Old 6th Jul 2022 at 6:48 PM
after "var e = ..." do a thorough null check.

Code:
if (e != null && e.Actor != null && e.TargetObject != null && !e.WasRecipient)
{

    Sim actor = e.Actor as Sim;
    Sim target = e.TargetObject as Sim;


I also check for !e.WasRecipient to avoid double triggers.

Find my Mods: Here at MTS, over at Simlogical
Test Subject
Original Poster
#5 Old 6th Jul 2022 at 8:50 PM
Alright, it is that either the target or actor is null. I was able to replicate the error and identify it. Now to figure out how to fix it.
Lyralei's attempt didn't work for me, it's saying it does not contain a definition for mTargetSimDescription.
(Please forgive me for my blunt-minded slowness, I'm having a difficult day.)
Test Subject
Original Poster
#6 Old 7th Jul 2022 at 3:41 AM Last edited by YolkSims : 7th Jul 2022 at 5:31 PM.
I'm seriously confused. I've gotten nowhere, and I when I removed all the contents of my script except for a single notification just for shits and giggles, I still got the error.

Code:
private static void OnWorldLoadFinished(object sender, EventArgs e)
        {
            EventTracker.AddListener(EventTypeId.kSocialInteraction, new ProcessEventDelegate(OnInteraction));
        }

private static ListenerAction OnInteraction(Event ev)
        {
            try
            {
                SocialEvent SEvent = ev as SocialEvent;

                if (!SEvent.WasRecipient)
                {
                    // Define actor and target sims.
                    // TargetObject is actually the sim.
                    Sim Actor = SEvent.Actor as Sim;
                    Sim Target = SEvent.TargetObject as Sim;

                    String ActorName = Actor.SimDescription.FullName;
                    String TargetName = Target.SimDescription.FullName;

                    Sims3.UI.StyledNotification.Show(new Sims3.UI.StyledNotification.Format("NPCRomance DEBUG: Social Interaction Detected Between:" + ActorName + TargetName, StyledNotification.NotificationStyle.kSimTalking));
                }
            }
            catch (Exception exception)
            {
                Errorlog.OnError.Exception(exception);
            }

            return ListenerAction.Keep;
        }
Virtual gardener
staff: administrator
#7 Old 7th Jul 2022 at 12:25 PM
Ohh I totally missed that indeed the base Event class does actually make mTargetObject the target Somehow my brain didn't think of that... So ignore my weird SimDescription solution thing! :p



Now, EA did a really odd thing? When passing through the target. It first passes it in as a Sim. Then it passes it in as a minisimdescription and then converts it back to a Sim like.... you made a property for this! What are you doing :p

Anyways, getting to the code....

The main thing to always check is if you're not 100% certain something will always be instantiated. Social Event most likely can be null if EA's code screwed up somewhere, so I'd always do a null check there. I used to absolutely trust my code too (or EA's code rather) to always give me something, but nul references are everywhere. And I mean Everywhere.

So take it from me, null reference check anything you can think of Even if you're mainly working with 80% of your own code.

Technically we actually don't know if it's 100% the target returning null. We want to know as well if the actor isn't null. That's what I mean with that you can never be certain that it will always be the sim we're looking for, as hickups and weirdness happen and creating a null reference.

So as Consort suggested, null reference the ever-living heck out of everything (to put it mildly) :p

So for debug reasons I'd also do:

Code:
        private static ListenerAction OnInteraction(Event ev)
        {
            try
            {
                SocialEvent SEvent = ev as SocialEvent;

                if (SEvent != null && !SEvent.WasRecipient)
                {
                    // Define actor and target sims.
                    // TargetObject is actually the sim.
                    Sim Actor = SEvent.mActor as Sim;
                    Sim Target = SEvent.mTargetObject as Sim;

                    // Uncomment this if you are done debugging 
                    //if (Actor == null) { return ListenerAction.Keep; }
                    //if (Target == null) { return ListenerAction.Keep; }

                    // Comment the Actor, target, boolean (and the if bool true, then return) once you're certain that you've found your null reference.
                    bool OneReturnedNull = false;
                    if (Actor == null)
                    {
                        SimpleMessageDialog.Show("Title Here", "Actor was null!");
                        OneReturnedNull = true;
                    }

                    if (Target == null)
                    {
                        SimpleMessageDialog.Show("Title Here", "Target was null!");
                        OneReturnedNull = true;
                    }

                    // Because we have incomplete data, exit out...
                    if (OneReturnedNull) { return ListenerAction.Keep; }

                    // Technically your old Simdescription code can work too, but name is fine as well! you may get some old Localised names though, especially in bridgeport.
                    string ActorName = Actor.Name;
                    string TargetName = Target.Name;

                    Sims3.UI.StyledNotification.Show(new Sims3.UI.StyledNotification.Format("NPCRomance DEBUG: Social Interaction Detected Between:" + ActorName + TargetName, StyledNotification.NotificationStyle.kSimTalking));
                }
            }
            catch (Exception exception)
            {
                Errorlog.OnError.Exception(exception);
            }

            return ListenerAction.Keep;
        }


That's how I would've debugged it Secondly, make sure your sims are both in the world. Which they probably are but just mentioning it as the way EA passes in the target can return into a null reference if the sim is still in the relationship tab of your sim, but is a service, or homeless (or in egypt/china/france)
Forum Resident
#8 Old 7th Jul 2022 at 3:21 PM
Yea Lyralei's code is looking fine. So whats going on seems to be this... Sometimes the game sends 4 events for a social interaction

Let's say Eve flirts with Adam but Adam doesn't like to be flirted with.

First event:
This is a SocialEvent
SEvent.Actor is Eve
SEvent.TargetObject is Adam
SEvent.WasRecipient is false
SEvent.WasAccepted is false

Second event:
This is a SocialEvent
SEvent.Actor is Adam
SEvent.TargetObject is Eve
SEvent.WasRecipient is true
SEvent.WasAccepted is false

Third event:
This is an Event
ev.Actor is Eve

Fourth event:
This is an Event
ev.Actor is Adam

I don't understand why 3 and 4 trigger only sometimes and what is stored in ev.TargetObject. But we dont' really need to do anything with this except filter it out.

Quote:
Now, EA did a really odd thing? When passing through the target. It first passes it in as a Sim. Then it passes it in as a minisimdescription and then converts it back to a Sim like.... you made a property for this! What are you doing :p


Hmm this almost looks like some contractor tried to wrench this in for WorldAdventures where I believe you can make phonecalls to Sims in other worlds which are probably stored as MiniSims.

Find my Mods: Here at MTS, over at Simlogical
Test Subject
Original Poster
#9 Old 7th Jul 2022 at 5:30 PM
I don't know what your winky face means, but thank you.
Test Subject
Original Poster
#10 Old 7th Jul 2022 at 6:47 PM
Quote:
Originally Posted by Consort
Yea Lyralei's code is looking fine. So whats going on seems to be this... Sometimes the game sends 4 events for a social interaction

Let's say Eve flirts with Adam but Adam doesn't like to be flirted with.

First event:
This is a SocialEvent
SEvent.Actor is Eve
SEvent.TargetObject is Adam
SEvent.WasRecipient is false
SEvent.WasAccepted is false

Second event:
This is a SocialEvent
SEvent.Actor is Adam
SEvent.TargetObject is Eve
SEvent.WasRecipient is true
SEvent.WasAccepted is false

Third event:
This is an Event
ev.Actor is Eve

Fourth event:
This is an Event
ev.Actor is Adam

I don't understand why 3 and 4 trigger only sometimes and what is stored in ev.TargetObject. But we dont' really need to do anything with this except filter it out.



Hmm this almost looks like some contractor tried to wrench this in for WorldAdventures where I believe you can make phonecalls to Sims in other worlds which are probably stored as MiniSims.


Thank you. This explains what I was missing in my understanding.
Test Subject
Original Poster
#11 Old 7th Jul 2022 at 9:57 PM
I'm still having the problem, and I don't know if it's me being dumb or what. I'm starting to feel like a pain, haha.
I wasn't able to put mActor and mTargetObject because those are private to Event, but my error log seems to be pulling them alright, because they mention IActor and IGameObject.
I still have the code in that's checking if the interaction, actor and target are null. But, something is still managing to slip through my null checks.
It seemed to be working for a while, but now...

Code:
simulating object is null
unable to obtain an interaction object

A null value was found where an object instance was required.
#0: 0x00008 ldfld.o in YolkSims.YolkSims.NPCRomance:Listener (Sims3.Gameplay.EventSystem.Event) ([43E14BB8] )
#1: 0x00000 in Sims3.Gameplay.EventSystem.Sims3.Gameplay.EventSystem.ProcessEventDelegate:Invoke (Sims3.Gameplay.EventSystem.Event) (424D27D0 [43E14BB8] )
#2: 0x00007 callvirt in Sims3.Gameplay.EventSystem.Sims3.Gameplay.EventSystem.DelegateListener:ProcessEvent (Sims3.Gameplay.EventSystem.Event) (37CD2B60 [43E14BB8] )
#3: 0x00054 callvirt in Sims3.Gameplay.EventSystem.Sims3.Gameplay.EventSystem.EventTracker:ProcessEvent (Sims3.Gameplay.EventSystem.Event) (35EB3C18 [43E14BB8] )
#4: 0x0000e callvirt in Sims3.Gameplay.EventSystem.Sims3.Gameplay.EventSystem.EventTracker:SendEvent (Sims3.Gameplay.EventSystem.Event) ([43E14BB8] )
#5: 0x0000e call in Sims3.Gameplay.EventSystem.Sims3.Gameplay.EventSystem.EventTracker:SendEvent (Sims3.Gameplay.EventSystem.EventTypeId,Sims3.Gameplay.Interfaces.IActor,Sims3.Gameplay.Interfaces.IGameObject) ([10] [362B4C00] [36776000] )
#6: 0x007e1 call in Sims3.Gameplay.Socializing.Sims3.Gameplay.Socializing.SocialInteractionA:Run () ()
#7: 0x000db stloc.u1 in Sims3.Gameplay.Interactions.Sims3.Gameplay.Interactions.InteractionInstance:RunInteractionWithoutCleanup () ()
#8: 0x00348 stloc.u1 in Sims3.Gameplay.ActorSystems.Sims3.Gameplay.ActorSystems.InteractionQueue:ProcessOneInteraction () ()
#9: 0x0011c stfld.u1 in Sims3.Gameplay.Actors.Sims3.Gameplay.Actors.Sim:DoInteraction () ()
#10: 0x00015 br.s in Sims3.Gameplay.Actors.Sims3.Gameplay.Actors.Sim:Simulate () ()
#11: 0x00022 ldc.i4.0 in ScriptCore.ScriptCore.ScriptProxy:Simulate () ()


Here's the entire code, I've probably messed up again:
Instructor
#12 Old 7th Jul 2022 at 10:20 PM Last edited by Battery : 7th Jul 2022 at 10:35 PM.
Quote:
Originally Posted by YolkSims
I'm still having the problem, and I don't know if it's me being dumb or what. I'm starting to feel like a pain, haha.
I wasn't able to put mActor and mTargetObject because those are private to Event, but my error log seems to be pulling them alright, because they mention IActor and IGameObject.
I still have the code in that's checking if the interaction, actor and target are null. But, something is still managing to slip through my null checks.
It seemed to be working for a while, but now...

Code:
simulating object is null
unable to obtain an interaction object

A null value was found where an object instance was required.
#0: 0x00008 ldfld.o in YolkSims.YolkSims.NPCRomance:Listener (Sims3.Gameplay.EventSystem.Event) ([43E14BB8] )
#1: 0x00000 in Sims3.Gameplay.EventSystem.Sims3.Gameplay.EventSystem.ProcessEventDelegate:Invoke (Sims3.Gameplay.EventSystem.Event) (424D27D0 [43E14BB8] )
#2: 0x00007 callvirt in Sims3.Gameplay.EventSystem.Sims3.Gameplay.EventSystem.DelegateListener:ProcessEvent (Sims3.Gameplay.EventSystem.Event) (37CD2B60 [43E14BB8] )
#3: 0x00054 callvirt in Sims3.Gameplay.EventSystem.Sims3.Gameplay.EventSystem.EventTracker:ProcessEvent (Sims3.Gameplay.EventSystem.Event) (35EB3C18 [43E14BB8] )
#4: 0x0000e callvirt in Sims3.Gameplay.EventSystem.Sims3.Gameplay.EventSystem.EventTracker:SendEvent (Sims3.Gameplay.EventSystem.Event) ([43E14BB8] )
#5: 0x0000e call in Sims3.Gameplay.EventSystem.Sims3.Gameplay.EventSystem.EventTracker:SendEvent (Sims3.Gameplay.EventSystem.EventTypeId,Sims3.Gameplay.Interfaces.IActor,Sims3.Gameplay.Interfaces.IGameObject) ([10] [362B4C00] [36776000] )
#6: 0x007e1 call in Sims3.Gameplay.Socializing.Sims3.Gameplay.Socializing.SocialInteractionA:Run () ()
#7: 0x000db stloc.u1 in Sims3.Gameplay.Interactions.Sims3.Gameplay.Interactions.InteractionInstance:RunInteractionWithoutCleanup () ()
#8: 0x00348 stloc.u1 in Sims3.Gameplay.ActorSystems.Sims3.Gameplay.ActorSystems.InteractionQueue:ProcessOneInteraction () ()
#9: 0x0011c stfld.u1 in Sims3.Gameplay.Actors.Sims3.Gameplay.Actors.Sim:DoInteraction () ()
#10: 0x00015 br.s in Sims3.Gameplay.Actors.Sims3.Gameplay.Actors.Sim:Simulate () ()
#11: 0x00022 ldc.i4.0 in ScriptCore.ScriptCore.ScriptProxy:Simulate () ()


Here's the entire code, I've probably messed up again:



you try to access Interaction.WasRecipient even if it is null
Code:
if ((Interaction == null | Actor == null | Target == null) && !Interaction.WasRecipient)


solution:
Code:
if ((Interaction == null || Actor == null || Target == null) || !Interaction.WasRecipient)


E: just noticed that you were using Interaction even before this code bit, so like gamefreak said check Interaction for null directly after the cast
Field Researcher
#13 Old 7th Jul 2022 at 10:30 PM Last edited by gamefreak130 : 7th Jul 2022 at 10:44 PM.
One thing that can help you debug issues like this is to look at the stack trace to see exactly where things are failing. Here, the "#0: 0x00008 ldfld.o" means that the function hits an exception really early on (presumably the 8th CIL instruction), which suggests that your SocialEvent is null. (Attempting to access members of a null object throws the NullReferenceException you're seeing.)

Moreover, if you look at SocialInteractionA.Run(), you can see the following lines:
Code:
EventTracker.SendEvent(new SocialEvent(EventTypeId.kSocialInteraction, this.Actor, this.Target, this));
EventTracker.SendEvent(new SocialEvent(EventTypeId.kSocialInteraction, this.Target, this.Actor, this));
EventTracker.SendEvent(EventTypeId.kSocialInteraction, this.Actor, this.Actor.LotCurrent);
EventTracker.SendEvent(EventTypeId.kSocialInteraction, this.Target, this.Target.LotCurrent);


The first two lines will send a SocialEvent as expected, but the second two lines will send a generic Event object with the same EventTypeId (this is what @Consort was referring to earlier). In other words, not all events with the EventTypeId "kSocialInteraction" can necessarily be cast to SocialEvents, and thus you need to do a null check like this:

Code:
SocialEvent Interaction = ev as SocialEvent;
 
if (Interaction != null)
{
    Sim Actor = Interaction.Actor as Sim;
    Sim Target = Interaction.TargetObject as Sim;

    // etc.
}


To make sure that the event you're responding to is in fact a SocialEvent before you try and access anything within it.

"The Internet is the first thing that humanity has built that humanity doesn't understand, the largest experiment in anarchy that we have ever had." - Eric Schmidt

If you enjoy the mods I put out, consider supporting me on patreon: www.patreon.com/Gamefreak130
Test Subject
Original Poster
#14 Old 8th Jul 2022 at 12:59 AM
I'm testing now, but can I ask what the difference is between | and ||?
Test Subject
Original Poster
#15 Old 8th Jul 2022 at 1:49 AM
...We have lift off, guys. It's working.
Field Researcher
#16 Old 8th Jul 2022 at 2:46 AM
Quote:
Originally Posted by YolkSims
I'm testing now, but can I ask what the difference is between | and ||?


The single pipe is a bitwise OR, while two pipes is a conditional OR. You can read more about the differences here.

In many situations they are functionally equivalent, but || is preferred because it has the advantage of short-circuiting, meaning that if the expression on the left hand side is true, then the right hand side will be completely ignored (since "true || x" will always be true, regardless of the value of x). This means that you can do things like:

Code:
if (possiblyNullObj == null || possiblyNullObj.mField == 0)
{
    // Do stuff
}


Using a bitwise OR here would throw an exception if possiblyNullObj is in fact null because it would attempt to access mField. With the conditional operator, the right hand side is ignored in this case and the code inside the if block can run.

Note that a similar distinction exists between bitwise AND (&) and logical AND (&&).

"The Internet is the first thing that humanity has built that humanity doesn't understand, the largest experiment in anarchy that we have ever had." - Eric Schmidt

If you enjoy the mods I put out, consider supporting me on patreon: www.patreon.com/Gamefreak130
Back to top