theRAPTLab / gsgo

GEM-STEP Foundation repo migrated from GitLab June 2023
1 stars 1 forks source link

Phantom onEvent handlers #421

Closed benloh closed 1 year ago

benloh commented 2 years ago

Symptoms

Repeatedly editing and running a blueprint over time seems to result in duplicate onEvent handlers. E.g. if a Fish has a onEvent Start handler that does a dbgOut "Burp", you'll see it run once, then after an edit, you'll see it run twice, three times after another edit, etc.

If you reload the problem goes away.

The Problem

The onEvent compiler blindly adds new onEvent consequent blocks to the script event handler. So every time you edit the blueprint and it is recompiled, you add another script event handler.

Reloading fixes the problem because ac-blueprints.m_ResetAndCompileBlueprints calls dc-sim-data.DeleteAllScriptEvents() to clear all script events before compiling.

Exploring Solutions

Ideally we would remove an existing script event method before adding the revised version. However, there is no dc-sim-data.DeleteScriptEvent(bpName) command, nor is it easy to add one since the script events are saved by event name and all subscribers are simply added to a generic array.

Another approach would be to DeleteAllScriptEvents() before compiling and adding new ones. The problem is that when we edit blueprints, we are only working on individual blueprints. If we deleted ALL script events, we would need to recompile ALL blueprints. This might be the easy (if inefficient and slightly messy) solution.

Proposed Solution

However, if we assume that during edits and after a blueprint edit, the user will always Reset Stage before running the simulation, then running DeleteAllScriptEvents during the RESET STAGE ought to work.

If the sim is NOT running...

don't want to do it on load, since you might move agents around after load. don't want to do it on Start Round for the same reason.

benloh commented 2 years ago

In GitLab by @daveseah on Jul 17, 2022, 13:14

I was just looking at the onEvent, when, and every keywords (these are all of our "event-ish" types) and seeing how different each one is. The onEvent and when keywords were developed quickly to get "something working for the researchers to play with" which predates Ben's run controller, and they do something bad: they set data structures directly during compile-time.

While Ben's solution would work for this specific case, I think the proper things to do would be to have these keywords emit data structure commands as a program instead of setting them directly. This should be doable just by moving the code into the returned program function (TSMCProgram) array that is returned by every keyword.

I'll post examples for each keyword.


IRRELEVANT SIDE NOTE: Historically, each of these keywords was developed months apart without strict peer oversight because they depended on very different kinds of event detection within the scope of the simulation engine. Interesting to look at them as a group now.

benloh commented 2 years ago

In GitLab by @daveseah on Jul 17, 2022, 19:50

PROCESS NOTES 1745

I'm looking for the code that reads PROGRAM EVENT so I can make the appropriate changes, and am going down the rabbit hole of the sim controller that is built on top of the runtime.

Q. How does the simulation get controlled? A. mod-sim-control wraps api-sim which has sim-rounds tacking-on round logic. The control interface is actually implemented as URSYS net messages that are "raised", which fires on the local machine as well. The messages are sent directly from the various button handlers of various components that are hidden/shown by layers of nested logic. The critical method is SimPlaces() which calls a bunch of other URSYS message handlers that are defined somewhere else in the system as a service API.

Q. What is the critical order in SimPlaces?

This is really spaghetti-like messaging code. The instance creation code is handled in sim-agetns in the SCRIPT_T_INSTANCE SyncMapper where there is a shadow copy of TRANSPILER.MakeAgent() that initializes each instance. But where is the compile happening?

Project Server's Initialize() actually seems do what ACProject.LoadProjectFromAsset() followed by the call to SIMCTRL.SimPlaces(). Is it possible that "loading" projects in ACProject also compiles bundles? That would be unexpected because typically we load assets and then explicitly transform them for operational clarity.

Ah...this method ACBlueprints.setBlueprints() is what is calling m_ResetAndCompileBlueprints() so that's why I couldn't find it in the app lifecycle code.

benloh commented 2 years ago

In GitLab by @daveseah on Jul 17, 2022, 20:04

These are the distinct operations that I expect to see reflected in the code:

I don't think there is any support for the third part in the system other than the initial instance creation. MOST of these are implicitly part of the instancing:

There is no reference to simulation initializtion in api-sim-gameloop; the defined loops are more about runtime control than iniitalization, which means the it's mx-sim-control.SimPlaces() that is the critical control point where ACBlueprints.SetBlueprints() does aeverything, and then right after that we need to hook program event and program condition into the gameloop init for my propsed change to work.

benloh commented 2 years ago

In GitLab by @daveseah on Jul 17, 2022, 20:17

Q. So how does onEvent even work, if program event is never run?
A. A quirk of the current keyword is that it inserts itself into the sim data during compile time so the very act of compiling makes it work, which is bad. For us to fix that, we need to have it emit a proper program that is run during simulation startup.

Q. So how does every even work, if there is no program event or program condition ever run?
A. This keyword is written to be run during an UPDATE cycle, which is automatically handled by the Agent class during agentUPDATE(), which is handled by the runtime engine

Q. So what about the when keyword? If it's not defined inside a program event or program condition how does that even run?
A. When conditions are tricky because they also register their test during compile in the form of a "Pair Interaction Key" that tells the runtime engine that it should check blueprints named A and B against each other using a particular test, and then save the results. This allows multiple blueprints to request the same test, and it is only run once by the enjoy. During runtime, it is the RESULTS that are returned based on the "interaction key" that looks like the string "Bee-touches-Flower-args". The compiled program that's returned from when just looks up the key and iterates through any pairs of agents found. If there are no pairs, then the entire piece of code doesn't do anything and just exits. This can efficiently be run during PROGRAM UPDATE, and on reviewing all the current gemscript projects none of them put when inside the program condition segment, so it's just been working as-is.

THE TAKEAWAY

We can probably drop CONDITION and EVENT types, and just have UPDATE and INIT. Yay!

benloh commented 2 years ago

In GitLab by @jdanish on Jul 18, 2022, 08:50

Love this: We can probably drop CONDITION and EVENT types, and just have UPDATE and INIT. Yay!

benloh commented 2 years ago

In GitLab by @daveseah on Jul 18, 2022, 18:52

I've heard some mixed messages about keeping EVENT, so I've implemented it "for real" and added some checks to report when scripts are doing unexpected things. More in the merge request !255

benloh commented 2 years ago

marked the checklist item ...(reset and compile if sim is not running???)... as completed

benloh commented 2 years ago

Fixed with !255.

benloh commented 2 years ago

mentioned in commit be1b630035875e5d5c75c988af660816e0cd37f4