Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

COLORS: Lost Memories: loop when displaying 2nd page in custom textbox #1247

Closed
carstene1ns opened this issue Aug 13, 2017 · 8 comments

Comments

@carstene1ns
Copy link
Member

commented Aug 13, 2017

Name of the game:

COLORS: Lost Memories (download)

Attach files (as a .zip archive or link them)

Savegame: Save01.zip

Describe the issue in detail and how to reproduce it:

Go right, read the sign (or go right, up the tower, collect the red stone and go back). In both cases a Picture containing a message is shown, press the Action key to advance to second page, then get stuck.
There is running an endless loop, you can hear the message box sound repeating.

screenshot_0screenshot_1

This is made of the sign event with 2 pages (39) and 2 common events (8 and 9). So could be a timing problem. Common Event 8 waits for key input and sets some Variables and Switches, Common Event 9 could be ignored, as it only displays and hides the indicator arrow. The Map Event 39 is triggered on Action button, enables the Common Event 8 and on page 2 is set to Autorun on Switch (set by Common Event 8).

@Ghabry

This comment has been minimized.

Copy link
Member

commented Aug 14, 2017

The code after the Key Input "Check, ENTER" in CE 8 is strange because it waits for ENTER but afterwards the code checks if enter was pressed (thats the only possible case) and afterwards if enter was not pressed (impossible)

@Ghabry

This comment has been minimized.

Copy link
Member

commented Aug 14, 2017

Probably the relevant event is Auto-CE 40 which is activated through Switch 20 by Auto-Event 39 Page 2.

CE 40 is responsible for disabling autorun event 39P2 after ENTER is pressed. So my guess is that the common event never gets a chance to run due to a timing problem and E39P2 endlessly repeats instead (as indicated by the endless SE playback)

EDIT:
Check. Can confirm by creating an empty map with one Map Autostart Event and one Common Autostart Event. Output is always "Map". In RPG_RT it switches between both.

EDIT2:
What we already implement: When the 1st Event CE doesn't yield the interpreter a 2nd Auto-Event CE starts (otherwise not). This works because the event already finishes in Game_Event::Update() in this case and no start-check happens until the next frame. This can be also used to launch common events.
In this game the RPG_RT behaviour makes no sense (o'rly) because the map event yields the interpreter (through ShowScreen). Theres no obvious reason why one Autorun CE can run when a yielded Autorun-Map event finished.

@Ghabry

This comment has been minimized.

Copy link
Member

commented Aug 15, 2017

According to my tests the behaviour is (all (common) events are autostart). Adding Parallel processes is left as an exercise ;):

E1: Var X+1
E2: Var X+1
E3: Var X+1

Result: E1, E2 and E3 run in the same frame. (expected and implemented)

E1: Var X+1.
E2: Var X+1. Wait 0.0
E3: Var X+1

Result: E1 and E2 run in the same frame. E3 never. (expected and implemented)

E1: Var X+1. Wait 0.0
E2: ...

Result: E1 runs. (same as the case above) (expected and implemented)

C1: Var X+1.
C2: Var X+1.

Result: C1 runs and the interpreter hangs (massive lag). C2 never runs(!). (not implemented, in EasyRPG C1 and C2 run. easy to fix)

C1: Var X+1. Wait 0.0
C2: Var X+1.

Result: C1 runs. No lag. (expected and implemented)

Now more interesting

E1: Var X+1
E2: Var X+1
C1: Var X+1
C2: Var X+1

Result: E1 runs, C1 runs infinitely (massive lag). (In EasyRPG all events run.)

E1: Var X+1. Wait 0.0
E2: Var X+1
C1: Var X+1
C2: Var X+1

Result: E1 runs, C1 runs infinitely (massive lag). (In EasyRPG only E1 runs.)

E1: Var X+1. Wait 0.0
E2: Var X+1
C1: Var X+1. Wait 0.0
C2: Var X+1

Result: E1 runs, C1 runs. No lag. (In EasyRPG only E1 runs.)

E1: Var X+1
E2: Var X+1
C1: Var X+1. Wait 0.0
C2: Var X+1

Result: E1 runs, C1 runs. No lag. Next frame: repeat. (In EasyRPG E1, E2 and C1 run)

First observation: Only the first auto start common event can ever run, all other CEs never run. (not implemented, easy fix)

Second observation: When you have both Autostart Map and Common Events only the first map event runs and the first common event. A yield (wait) doesn't matter. (not implemented, tricky. This is what the game uses)

So having one autorun Common event messes up the whole execution logic.

Lets ignore the "lag" cases. No game will use this because the lag is really extreme.

Bonus XP and VX Ace:
The execution is more logical.

E1: Var X + 1 [...]
[...]

Only E1 runs.

C1: Var X + 1 [...]
[...]

Only C1 runs.

@CherryDT

This comment has been minimized.

Copy link

commented Aug 15, 2017

I'm slightly confused about the cases with both map and common events. Maybe it's different in different RM versions (2k <1.50, 2k >=1.50, 2k3 <1.05, 2k3 >=1.05)? The information below should be valid for 2k >=1.50.

The thing is, as I understand the engine, it is "supposed" to work like this:

The RM has workers for executing events. A worker is either a generic worker which will try to execute whatever event is waiting for being executed, or a parallel process worker which runs one specific event over and over. (Or a battle worker, but let's leave that aside for now). Every worker can be imagined as a thread which has some program running, a stack, registers (e.g. wait counter) etc. When a worker is asked to run, it will run in a loop until 10000 iterations or execution is suspended (e.g. a command asked to wait a certain amount if frames), whatever is earlier. A generic worker will, as soon as one event is finished, try to find the next waiting event and start it (without leaving the loop). A parallel process worker will quit the loop once the end of the script is reached (this is why the "invisible Wait 0.0" seems to exist at the end of parallel process events).

Every event (map and common) has its own parallel process worker which is used in case the event is set as parallel process (note that such a worker's script may call into another event, which means that a worker associated with an event can't always be assumed to run that exact event - and that's also why turning off a switch of a parallel process event will stop execution even if execution is currently inside another event's script, because the switch suspends the worker and not its original script). Additionally, there is one global worker for "foreground" events which are autostart or activated by action button or touch.

Events have a "waiting" flag which indicates that they would be waiting to get foreground execution. This flag is implemented slightly different for map events and common events: For map events, it's an actual flag which is set and reset, while for common events, it's a property getter which returns true if the event is configured as autostart unless it's empty or the switch condition is not met.

Every frame on the map, the engine will:

  • Reset the "already acted" flag for all events on the map (which is used to prevent events to trigger twice on the same frame in edge cases)
  • Ask all common events' parallel process workers to run
  • Ask all map events' parallel process workers to run
  • Ask all map events (including vehicles and party) to act (unless its "already acted" flag is set): Acting is taking an action like moving, but it will also check if the event is autostart and the start conditions are met, in this case its "waiting" flag is set. The "already acted" flag is also set. (If an event is triggered in another way, for example by action button, it would also get its "waiting" flag set.)
  • Ask the global autostart event worker to run. The global autostart event worker will do a loop of up to 10000 times trying the following:
    • If there is no event script loaded:
      • See if there is any common event waiting. (Remember that this involves a call to a property getter which will be true as soon as a common event is marked as autostart, not empty, and not with unmet switch condition.) Events are checked in numerical order. Once a waiting common event is found, it's loaded.
      • If there was no waiting common event found: See if there is any map event waiting - again in numerical order. (Remember that this checks an actual flag which was previously set while the event was asked to "act".) If yes, reset the "waiting" flag for this event and load it.
      • If there was neither a waiting common nor map event found, abort.
    • Execute a command of the currently loaded script. If the command requires waiting, quit the loop afterwards.
    • If the end of the event script is reached, unload it.

So, in short, once there is any autostart common event available for running, it will run in a tight loop until 10000 commands have been processed. If more than one is available, the one with the lowest ID will always win because once it finished running, the worker will again try to find an event and will again land at the same one unless its start conditions are now unmet (disabled switch). With the logic I wrote above, this should also always make common events win against map events because map events are checked afterwards. (And unlike common events, more than one autostart map event may run in the same frame because for them the "waiting" flag is an actual flag which gets set only once per frame and reset upon execution, so once one is done, it won't be selected again in the next iteration until the next frame at which the "waiting" flags are enabled again.)

Now I wonder if in the version you tested, maybe the order of the event loading tries in the autostart worker is reversed? Because then it would make sense what you observed: Then the common events would still not fairly compete against each other (and cause lags) but the map events should always run...

@Ghabry

This comment has been minimized.

Copy link
Member

commented Aug 15, 2017

Thank for these information! I had no time yet to think about your text but my test cases were created with RPG2k3 1.08 because I analyzed this with the DynRPG Event Tracer plugin.

@fmatthew5876

This comment has been minimized.

Copy link
Contributor

commented Feb 24, 2019

@CherryDT

I had a few clarifying questions about interpreter behavior

  1. In what order does RPG_RT process map events? Currently in player we do player, events, vehicles in that order. Is that right?

  2. Does it check autostart conditions for all events before running the update routes? Or does it do this as part of the update route?

  3. When is the trigger=collision check done? Before or after the event processes a frame of movement?

  4. I'm assuming what we call "processed" flag is the "already acted" flag mentioned by you above.

What happens when event A wants to move but event B is in the way but B.processed = false? Does it go an run B's update routine first and then return? Does this also imply that B's event activation checks would happen at this time too?

@CherryDT

This comment has been minimized.

Copy link

commented Feb 24, 2019

I'm not sure I understood 100% the context of each of the questions (because there are for example several ways of "updating", several ways of "checking collision", and so on - RM has often 2 or 3 functions doing almost the same thing with small differences, called in different places), but I think the answers are as follows:

  1. Normally: Common events, map events, player, vehicles. Initially after changing map or when exiting game menu: Common events, map events.
  2. As part of the update route as described above.
  3. After movement. Note that any event simply attempting to move onto another will trigger a part of the other's processing early, out of order, before the passability calculation returns a result, so that a group of events can move together regardless of their ID order without "bumping into each other".
  4. Yes. (I checked the chunk ID now, it matches.)

Please take all of this with a grain of salt as I'm not sure I really understand the questions (in a technical sense that matches RM's structure so I could properly answer)

@fmatthew5876

This comment has been minimized.

Copy link
Contributor

commented Mar 13, 2019

This is fixed by #1628

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
4 participants
You can’t perform that action at this time.