Jump to content
tanu1215

Infinite Loop Crash

Recommended Posts

Hey there,
So as my first callout slowly progresses, I have finally made it to the near end.
Unfortunately, I have run into a graphics glitch.
Basically, the screen of GTA 5 bugs out and flashes, and a huge diamond forms around a newly created blip on my minimap.

I know this is because my infinite loop is repeatedly executing, and causing the blip to regenerate.

I have placed a GameFiber.Yield(), but it still seems to not work. I have placed the .Yield() in a couple of different spots, and none of them cause the plugin to work correctly, and either crashes LSPDFR, or the game.

Help appreciated,
Thanks.

Code

Spoiler

 while (true)
            {
                if (gunman1.IsDead && gunman2.IsDead && gunman3.IsDead)
                {
                    
                    Game.DisplayNotification("~b~Dispatch~w~: The prisoner escaped! Find him before he gets away!");
                    Game.DisplaySubtitle("Find the ~r~prisioner~w~", 6500);
                    if (blip.Exists()) blip.Delete();
                    pblip = prisoner.AttachBlip();
                    pblip.EnableRoute(Color.Red);
                    GameFiber.Yield();
                   
                    if (!prisoner.Exists())
                    {
                        Game.DisplayNotification("~b~Dispatch~w~: Disregard, FIB have captured suspect");
                        Game.DisplayNotification("~b~Dispatch~w~: Code four, resume patrol");
                        this.End();
                    }

                    if (Game.LocalPlayer.Character.Position.DistanceTo(pspawn) <= 15)
                    {
                        Game.DisplayNotification("~y~You~w~: Perp spotted, requesting assitance!");
                        //Dismiss the prisoner from our plugin and add it to the LSPDFR API
                        prisoner.Dismiss();
                        Functions.AddPedToPursuit(this.pursuit, prisoner);
                        state = EConvoyState.endlogic;
                        //Request backup
                        Functions.RequestBackup(Game.LocalPlayer.Character.Position, LSPD_First_Response.EBackupResponseType.Pursuit, LSPD_First_Response.EBackupUnitType.LocalUnit);
                        Functions.RequestBackup(Game.LocalPlayer.Character.Position, LSPD_First_Response.EBackupResponseType.Pursuit, LSPD_First_Response.EBackupUnitType.LocalUnit);
                        Functions.RequestBackup(Game.LocalPlayer.Character.Position, LSPD_First_Response.EBackupResponseType.Pursuit, LSPD_First_Response.EBackupUnitType.LocalUnit);
                        Functions.RequestBackup(Game.LocalPlayer.Character.Position, LSPD_First_Response.EBackupResponseType.Pursuit, LSPD_First_Response.EBackupUnitType.StateUnit);
                        Functions.RequestBackup(Game.LocalPlayer.Character.Position, LSPD_First_Response.EBackupResponseType.Pursuit, LSPD_First_Response.EBackupUnitType.SwatTeam);
                    }
                }
            }
        }

 

 

Edited by tanu1215

Share this post


Link to post
Share on other sites

Yield does not stop execution for ever, it just returns control of the current thread to the caller, so other things can be processed. It's basically what Non-preemptive multitasking (cooperating threads) are to operating systems. The OS (in this case RAGE Plugin Hook) expects the callee to hand back control every now and then so it can process other waiting tasks. If this does not happen, the whole system will come to a halt (since RPH is only pseudo-multithreading this will cause the game to freeze). So what happens when you call Yield, is that execution in your plugin is paused, all other waiting tasks (other script threads) are executed, and then control is handed back to you and your code is resumed. So you effectively only pause your code for one game tick before it executes again.

To prevent things from being executed every tick, use variables indicating whether something has been done already, such as introducing a Boolean hasBlipBeenCreated or checking for the existence of the blip and only creating it, if it doesn't exist.

Share this post


Link to post
Share on other sites

You can also use switch..case and enum of states to control which chunks of code you want to run in a specific moment, eg.

 

enum EState
{
    Initialize,
    CheckConditions,
    Finalize,
};
EState currentState = SState.Initialize;

switch(currentState)
{
    case Initialize:
      //create blips and stuff
      currentState = EState.CheckConditions;
      break;
    case CheckConditions:
      if(Ped.IsDead) currentState = EState.Finalize;
      break;
    case Finalize:
      //delete blips etc.
      break;
}

 

Share this post


Link to post
Share on other sites

To add to what @LtFlash said, if you require certain actions to be executed most of the time and still want to use a state enum, you can use bit flags. Basically that allows you to have one state variable, which can contain several states at once. So maybe you want the CheckConditions part executed most of the time as well, so you can specify that along with the "real" state.

LSPDFR exposes such functionality through RegisterStateCallback for callouts.

Share this post


Link to post
Share on other sites
4 hours ago, LMS said:

To add to what @LtFlash said, if you require certain actions to be executed most of the time and still want to use a state enum, you can use bit flags. Basically that allows you to have one state variable, which can contain several states at once. So maybe you want the CheckConditions part executed most of the time as well, so you can specify that along with the "real" state.

LSPDFR exposes such functionality through RegisterStateCallback for callouts.

Okay, so I am still somewhat at a loss. I tried doing what @LtFlash said, and incorporated a switch statement with enums so I could  take control over the process.

 

  ECheck check = ECheck.Initialize;

            switch (check)
            {
                case ECheck.Initialize:
                    Game.DisplaySubtitle("~w~Kill all ~r~hostilies", 6500);
                    check = ECheck.Checkconditions;
                    break;

                case ECheck.Checkconditions:

                    check = ECheck.Wait;

                    if (gunman1.IsDead && gunman2.IsDead && gunman3.IsDead && check == ECheck.Wait)
                    {
                        Game.DisplayNotification("~b~Dispatch~w~: The prisoner escaped! Find him before he gets away!");
                        Game.DisplaySubtitle("Find the ~r~prisioner~w~", 6500);
                        check = ECheck.Finalize;
                    }

                    break;

                case ECheck.Finalize:
                    if (blip.Exists()) blip.Delete();
                    pblip = prisoner.AttachBlip();
                    pblip.EnableRoute(Color.Red);
                    if (!prisoner.Exists() || !pblip.Exists())
                    {
                        Game.DisplayNotification("~b~Dispatch~w~: Disregard, FIB have captured suspect");
                        Game.DisplayNotification("~b~Dispatch~w~: Code four, resume patrol");
                        this.End();
                    }

                    if (Game.LocalPlayer.Character.Position.DistanceTo(pspawn) <= 15)
                    {
                        Game.DisplayNotification("~y~You~w~: Perp spotted, requesting assitance!");
                        //Dismiss the prisoner from our plugin and add it to the LSPDFR API
                        prisoner.Dismiss();
                        Functions.AddPedToPursuit(this.pursuit, prisoner);
                        state = EConvoyState.endlogic;
                        //Request backup
                        Functions.RequestBackup(Game.LocalPlayer.Character.Position, LSPD_First_Response.EBackupResponseType.Pursuit, LSPD_First_Response.EBackupUnitType.LocalUnit);
                        Functions.RequestBackup(Game.LocalPlayer.Character.Position, LSPD_First_Response.EBackupResponseType.Pursuit, LSPD_First_Response.EBackupUnitType.LocalUnit);
                        Functions.RequestBackup(Game.LocalPlayer.Character.Position, LSPD_First_Response.EBackupResponseType.Pursuit, LSPD_First_Response.EBackupUnitType.LocalUnit);
                        Functions.RequestBackup(Game.LocalPlayer.Character.Position, LSPD_First_Response.EBackupResponseType.Pursuit, LSPD_First_Response.EBackupUnitType.StateUnit);
                        Functions.RequestBackup(Game.LocalPlayer.Character.Position, LSPD_First_Response.EBackupResponseType.Pursuit, LSPD_First_Response.EBackupUnitType.SwatTeam);

                    }

                    break;
            }

So, the  if (gunman1.IsDead && gunman2.IsDead && gunman3.IsDead && check == ECheck.Wait) still doesn't get called because the gunmen aren't dead when the player gets on scene. 

If I'm right (probably not), shouldn't it still execute because of the ECheck.Wait enum?
You said something about executing the "Checkconditions" part repeatedly, which would probably wait until all 3 gunmen are dead until moving on, and I could use the "RegisterStateCallback" for that?

Would you mind explaining that again in a more "noobish" way, I don't really understand.
Thanks again,

Tanu

Share this post


Link to post
Share on other sites

The ECheck.Wait enum is whats stopping it being checked. The next pass, your switch wont execute any code as there is no case for ECheck.Wait. You could try adding a Case ECheck.Wait identical to your Case ECheck.Checkconditions, or just leave the enum at .Checkconditions till it finalizes.

Share this post


Link to post
Share on other sites

@tanu1215

This variable needs to be global in the callout class:

ECheck check = ECheck.Initialize;

I assume it is, let's go further:

case ECheck.Checkconditions:

                    check = ECheck.Wait;

                    if (gunman1.IsDead && gunman2.IsDead && gunman3.IsDead && check == ECheck.Wait)
                    {
                        Game.DisplayNotification("~b~Dispatch~w~: The prisoner escaped! Find him before he gets away!");
                        Game.DisplaySubtitle("Find the ~r~prisioner~w~", 6500);
                        check = ECheck.Finalize;
                    }

                    break;

 

I assume the switch...case is run in an infinite loop. With that in mind look what happened here:

1st loop:

1. You immediately changed the current state to Wait.

2. You checked if "AreGunmenDead" (unknown result) && state == Wait (true). If yes -> state = Finalize.

2nd loop:

3. In the next loop your "AreGunmenDead" && state won't be called at all. Why? Because the state is now Wait which is empty as I suppose.

 

Try to change this case to:

case ECheck.Checkconditions:

	if (gunman1.IsDead && gunman2.IsDead && gunman3.IsDead)
    {
    	Game.DisplayNotification("~b~Dispatch~w~: The prisoner escaped! Find him before he gets away!");
		Game.DisplaySubtitle("Find the ~r~prisioner~w~", 6500);
      
        check = ECheck.Finalize;
    }
    break;

+ define the check variable as a global one.

+ simplify the callout, firstly try to make one where a player is supposed to kill all 3 suspects. The part of code in Finalize which is responsible of handling a pursuit won't work.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×