Jump to content

Fix crash on empty when enter/leave during quest state change.


Recommended Posts

  • Honorable Member

Hello, it's been a while since I shared something. I saw someone asking for support about this today, so here's the fix.

This is a long-standing pitfall (even in some official quests). If a `when enter` or `when leave` handler exists but is empty, the server crashes on state change (`set_state()`), because the game tries to execute zero-length script code.

What you'll see in the backtrace

Expression: vector subscript out of range

Backtrace points to `AStateScriptType::GetCode()` with `GetSize() == 0.`

When `set_state()` runs the `when enter`/`when leave` blocks and one of them is empty, the game tries to index code at position 0 (`GetCode()[0]`), causing an out-of-bounds crash.

Reproduction (minimal)

Spoiler
quest simul begin
	state start begin
		when xxx begin
			set_state("state0") -- triggers `when leave` of start and `when enter` of `state0`
		end

		when leave begin
			-- empty -> crash on set_state()
		end
	end

	state state0 begin
		when enter begin
			-- empty -> crash on set_state()
		end
	end
end

 

Quests known to contain this issue

Found via regex scan (see below):

  • subquest_16.quest
  • subquest_30.quest
  • subquest_46.quest
  • main_quest_flame_lv99.quest
  • main_quest_flame_lv100.quest
  • main_quest_flame_lv101.quest
  • main_quest_flame_lv102.quest
  • main_quest_flame_lv103.quest
  • main_quest_flame_lv104.quest
  • main_quest_flame_lv105.quest
     

Solution

Skip execution when the state script size is zero.

Spoiler
/// 1. game/src/questnpc.cpp
// Search @ bool NPC::ExecuteEventScript
		sys_log(0, "ExecuteEventScript ei %d qi %u is %d", EventIndex, dwQuestIndex, iState);
		CQuestManager::instance().SetCurrentEventIndex(EventIndex);

// Add above
		// 20250322 <Owsap> : Prevent crash on empty enter/leave handlers.
		// Some quests register `when enter`/`when leave` blocks with no code.
		// Those have size == 0; executing them would index into an empty buffer.
		if (itState->second.GetSize() == 0)
		{
			sys_log(0, "ExecuteEventScript ei %d qi %u is %d - EMPTY STATE SCRIPT (skipping)", EventIndex, dwQuestIndex, iState);
			return false;
		}

 

This works because empty handlers have `GetSize() == 0`. Early returning makes them a safe no-operation instead of indexing into an empty buffer.

Recommendation

In general, avoid leaving `when enter` / `when leave` empty. If you must keep the block, add a harmless no-operation line (e.g., `local _ = 0`).

Edited by Owsap
  • Metin2 Dev 27
  • kekw 1
  • Scream 1
  • Lmao 1
  • Good 2
  • muscle 2
  • Love 11

Don't use any images from : imgur, turkmmop, freakgamers, inforge, hizliresim... Or your content will be deleted without notice...
Use : https://metin2.download/media/add/

Please use https://metin2.download/ when uploading files smaller than 100MB, otherwise the approval will take longer due to manual upload.

Please sign in to comment

You will be able to leave a comment after signing in



Sign In Now
×
×
  • Create New...

Important Information

Terms of Use / Privacy Policy / Guidelines / We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.