Page 1 of 1

QuestCondition bug report

Posted: Thu Jun 03, 2021 7:47 am
by AArtlone
Hi again, I have created the previous post where I described my problem.

In this post, I want to report the bug.

Steps to reproduce the bug:
  • Using an empty Unity project with only QuestMachine imported
    Locate "Pesky Rabbits" quest
    Set the "rabbits" counter value to 1
    In Autostart, add a CounterQuestCondition
    Set "Required Counter Value" to 1
This bug occurs because the QuestCondition is true at the moment the Quest is initialized by the QuestGiver, thus it sets the condition to true. This causes a StackOverflowException because the code goes into an infinite loop.

Code blocks related to the issue:

Code: Select all

[b]Quest.cs[/b]
public void BecomeUnofferable()
{
	try
        {
        	if (GetState() != QuestState.Disabled) SetState(QuestState.Disabled);
                	SetQuestIndicatorState(questGiverID, QuestIndicatorState.None);
        }
        catch (Exception e) // Don't let exceptions in user-added events break our code.
        {
                if (Debug.isDebugBuild) Debug.LogException(e);
        }
}

Code: Select all

[b]Quest.cs[/b]
public void SetState(QuestState newState, bool informListeners = true)
{
	if (QuestMachine.debug) Debug.Log("Quest Machine: " + GetEditorName() + ".SetState(" + newState + ", informListeners=" + informListeners + ")", this);

	m_state = newState;

	SetStartChecking(newState == QuestState.WaitingToStart);
	SetCounterListeners(newState == QuestState.Active || (newState == QuestState.WaitingToStart && (hasAutostartConditions || hasOfferConditions)));
	....
}

Re: QuestCondition bug report

Posted: Thu Jun 03, 2021 2:27 pm
by Tony Li
Thanks for reporting this bug. I'll investigate and provide a patch here later today.

Re: QuestCondition bug report

Posted: Thu Jun 03, 2021 5:02 pm
by Tony Li

Re: QuestCondition bug report

Posted: Fri Jun 04, 2021 4:37 am
by AArtlone
Great! Thanks a lot.

Re: QuestCondition bug report

Posted: Fri Jun 04, 2021 5:41 am
by AArtlone
Hi Tony!

I think there are more issues in regards to the Autostart.

I will try to explain it below.

So let us consider the following scenario:
  • We have a Quest that has an Autostart QuestCondition
  • The Quest is added to the player's QuestJournal
  • This condition is true at the moment the quest is started up by the QuestJournal
  • The Quest consists of 3 nodes, Start, Condition, and Success
  • The Condition QuetNode has some TestQuestCondition
  • This TestQuestCondition prints a "Subscribe" debug log when the StartChecking() is called
  • This TestQuestCondition prints an "Unsubscribe" debug log when the StopChecking() is called
The problem that occurs is the following:
  • We play the game
  • The Quest checks for the Autostart condition
  • The condition is true, thus the Quest state changes to Active
  • The Console prints out both "Subscribe" and "Unsubscribe" logs
The reason why I think this happens is the following:
  • It all has to do with the SetState() function in the Quest.cs
  • When the game starts, this method is called with newState = WaitingToStart
  • It then calls SetStartChecking(true), since newState == WaitingToStart
  • Consequently, it goes through the Autostart conditions
  • We only have 1 Autostart condition which is true
  • As a result, SetState() is called again with newState = Active. This happens before the previous SetState() method has finished!!!
  • This then calls StartChecking() on the only QuestCondition of our only CondiutionQuestNode, thus printы "Subscribe" to the console
  • Then, when the second SetState() method finishes execution, it goes back to the first SetState() execution!!!
  • Which then does the following check: if (newState == Active) StopNodeListeners();
  • Since this is the first iteration of the SetState() method, newState equals to WaitingToStart, therefore StopNodeListeners() is called.
  • Which consequently gets to our only Condition and prints out "Unsubscribe"

Code: Select all

public void SetState(QuestState newState, bool informListeners = true)
        {
            if (QuestMachine.debug) Debug.Log("Quest Machine: " + GetEditorName() + ".SetState(" + newState + ", informListeners=" + informListeners + ")", this);


            Debug.Log($"NewState: {newState}, MyState: {m_state}");

            m_state = newState;

            SetStartChecking(newState == QuestState.WaitingToStart);
            SetCounterListeners(newState == QuestState.Active || (newState == QuestState.WaitingToStart && (hasAutostartConditions || hasOfferConditions)));
            if (newState != QuestState.Active) StopNodeListeners();
	...
        }
I hope this explanation provides enough information, please let me know if you need any follow-up information from me.

Looking forward to your response.

Re: QuestCondition bug report

Posted: Fri Jun 04, 2021 10:46 am
by Tony Li
Thank you for the breakdown. As you described, the SetState() method wasn't considering that the state may change after calling SetStartChecking(). This patch addresses that:

QM_QuestStatePatch_2021-06-04.unitypackage