Staredit Network > Forums > SC1 UMS Theory and Ideas > Topic: [EUD] Virtual Triggers
[EUD] Virtual Triggers
Jan 28 2018, 7:26 pm
By: jjf28  

Jan 28 2018, 7:26 pm jjf28 Post #1

Cartography Artisan

A regular trigger is saved in the TRIG section within the scenario file in a map. A virtual trigger is not saved in the TRIG section/in the scenario file, but rather is written in at some place in StarCrafts memory using EUDs and added to the trigger list using EUDs.

Because a virtual trigger is created and can be modified with EUDs, the contents of a virtual trigger are dynamic - conditions and actions can be added, removed, and modified at will as can previous and next trigger pointers - as opposed to regular triggers, which by virtue of being allocated dynamically cannot be altered with EUDs (since you would not know what address they will be located at and cannot derive the player you would use in your EUD actions).

Virtual triggers, in theory, enables operations such as traversing the unit list (or any pointer-based list), jumping from place to place in the trigger list (emulating loops and function calls), ending trigger execution before the list is finished, and making adjustments to regular triggers (e.g. changing the player you're setting deaths for or changing the display text string).


The structure of a trigger as it appears in StarCraft's memory is as follows:
Trigger List Element (2408 bytes)


Creating a virtual trigger is just a matter of writing the trigger to some place in memory, then adding it to the trigger list, to do so you first need to scope out where you're going to write it to memory, as triggers are 2408 bytes in memory your options for Blizzard write-supported memory locations are actually very limited.
Candidate Memory Locations


Once you've selected a memory location to serve as "VirtualTriggerStart" you need to write in the trigger using EUDs, which is a simple, albeit tedious operation (until tools are developed/enhanced).
Values to Write In

Condition Values

Action Values


Once your trigger is written you need to add the trigger to the trigger list. While it's theoretically possible to add a virtual trigger to the middle or end of the trigger list or to add it to multiple players, it is simplest to add the virtual trigger to the start of a single players trigger list. This is done in three easy steps:

1.) Set the value of the virtual triggers PrevTriggerPointer to that players TriggerListHeader
2.) Move the value currently in a players FirstTriggerPointer into the virtual triggers NextTriggerPointer (use binary countoffs to do a destructive copy from one address to the other, that is, if source is greater than a binary countoff value, subtract from the source and add to the destination)
3.) Set the value of the players FirstTriggerPointer to the VirtualTriggerStart








Now that the theory is all established I'm going to walk through an example of creating a virtual trigger, specifically this trigger:

Virtual Trigger
Players

  • Player 1
  • Conditions

  • (no conditions)
  • Actions

  • Modify death counts for Player 1: Set To 1 for Terran Marine.
  • Preserve Trigger.


  • First I select the memory location at which my virtual trigger will reside: VirtualTriggerStart = 0x0058F44C

    Next I figure out what is needed for set deaths: The group field for Player 1 (+0x010), the unitId field for Terran Marine (+0x018), the numericModifier field for "Set To" (+0x018), and the number field for 1.

    Set Deaths Fields



    Next I calculate the value for each action field

    group = 0; // (player 1, but it's 0-based so 0, because this is 0 I don't need to set anything)
    amount = 1; // (value of 1)
    unitActionIdModType = 120389632; // (terran marine (0), set deaths actionId (45), set to (7))



    Then the offset for each action field I'll be setting within the virtual trigger, the first action starts at VirtualTriggerStart + 0x148 = 0x0058F44C + 0x148 = 0x0058F594.

    0x0058F5A8 is the virtual trigger action amount field that I'll set to 1
    0x0058F5AC is the virtual trigger action unitActionIdModType field that I'll set to 120389632



    To preserve the trigger I don't really need another action, all I need is to set the virtual trigger's execution flags at VirtualTriggerStart + 0x948 to 4

    0x0058FD94 is the virtual trigger's execution flags that I'll set to 4



    Resulting in the following actions to setup the virtual triggers contents:

    Set Memory(0x58F5A8, Set To, 1); // Set the virtual trigger set deaths amount ot 1
    Set Memory(0x58F5AC, Set To, 120389632); // Set the unitId to Terran Marine, modType to Set To, and the actionId to Set Deaths
    Set Memory(0x58FD94, Set To, 4); // Set the execution flags to preserve trigger





    Next I need to add this trigger to the trigger list, first by setting the virtual triggers virtPrevTrigPtr to the trigger list header 0x000 + VirtualTriggerStart = 0x0058F44C

    Set Memory(0x58F44C, Set To, 5350016);



    Then by moving the value of player 1's FirstTriggerPointer (p1FirstTriggerPointer) (0x0051A288) to virtNextTrigPtr (0x58F450) field with binary countoffs, I also don't want these countoffs to continue firing if some of them were not used, so I guard against that using switch 1.

    Text Triggers to copy p1FirstTriggerPointer to virtNextTrigPtr


    And finally I set p1FirstTriggerPointer (0x0051A288) to VirtualTriggerStart (0x0058F44C = 5829708)

    Set Memory(0x51A288, Set To, 5829708);





    Attached is the example map, the virtual trigger setup occurs after elapsed time: 3s as earlier seems to crash (I'm probably missing a pointer or a trigger count or something somewhere that would make it work on the first trigger cycle).

    Attachments:
    Virtual Trigger.scx
    Hits: 0 Size: 48.56kb

    Post has been edited 7 time(s), last time on Jan 28 2018, 9:10 pm by jjf28.



    TheNitesWhoSay - Clan Aura - github

    Reached the top of StarCraft theory crafting 2:12 AM CST, August 2nd, 2014.

    Jan 28 2018, 7:26 pm jjf28 Post #2

    Cartography Artisan

    Un-filtered notes:

    Notes Dump


    Post has been edited 1 time(s), last time on Jan 28 2018, 8:53 pm by jjf28.



    TheNitesWhoSay - Clan Aura - github

    Reached the top of StarCraft theory crafting 2:12 AM CST, August 2nd, 2014.

    Jan 28 2018, 7:26 pm jjf28 Post #3

    Cartography Artisan

    reserved



    TheNitesWhoSay - Clan Aura - github

    Reached the top of StarCraft theory crafting 2:12 AM CST, August 2nd, 2014.

    Jan 28 2018, 7:27 pm jjf28 Post #4

    Cartography Artisan

    reserved



    TheNitesWhoSay - Clan Aura - github

    Reached the top of StarCraft theory crafting 2:12 AM CST, August 2nd, 2014.

    Jan 28 2018, 7:27 pm jjf28 Post #5

    Cartography Artisan

    reserved



    TheNitesWhoSay - Clan Aura - github

    Reached the top of StarCraft theory crafting 2:12 AM CST, August 2nd, 2014.

    Jan 28 2018, 8:23 pm Lanthanide Post #6



    So a "virtual trigger" is better described as "a trigger that's not in the normal player trigger list"?

    Is there anything that can be done with a virtual trigger, that can not be done with a standard trigger? Don't we have access to the trigger array via EUDs, so we could dynamically update conditions and actions etc, the same as with a virtual trigger?

    Edit: jjf has now updated the original post to better answer this question.

    Post has been edited 1 time(s), last time on Jan 28 2018, 9:58 pm by Lanthanide.



    None.

    Jan 28 2018, 8:34 pm jjf28 Post #7

    Cartography Artisan

    Quote from Lanthanide
    So a "virtual trigger" is better described as "a trigger that's not in the normal player trigger list"?

    That is an accurate way of describing a virtual trigger.

    Quote from Lanthanide
    Is there anything that can be done with a virtual trigger, that can not be done with a standard trigger? Don't we have access to the trigger array via EUDs, so we could dynamically update conditions and actions etc, the same as with a virtual trigger?

    Plenty, you cannot update conditions/actions with regular triggers because you don't know where the trigger list is going to be allocated in memory, it could be ~anywhere in 32-bit address space, so you don't know what address you will be changing, and cannot know the player you'll be using in your "Set Deaths" action in advance. With virtual triggers you can adjust which player the set deaths action affects in-game, combining that with reading the various trigger list pointers you can setup triggers to do things like dynamically update conditions and actions.



    TheNitesWhoSay - Clan Aura - github

    Reached the top of StarCraft theory crafting 2:12 AM CST, August 2nd, 2014.

    Jan 28 2018, 11:42 pm jjf28 Post #8

    Cartography Artisan

    I've just been told about the current player trick, which greatly reduces the usefulness of Virtual Triggers (virtual triggers may yet be optimal in larger systems, but the current player trick requires less setup) and greatly simplifies setting virtual triggers up. I'll be making updates and documenting the current player trick more elsewhere.

    The current player trick follows the form:

    targetAddress = Address you wish to make changes to
    currentPlayerVal = (targetAddress - 5808996)/4
    currentPlayerVal = (targetAddress / 4) - 1452249
    operation = Add or Subtract or Set To
    value = Value to check against or to Add to targetAddress or subtract from targetAddress or set targetAddress to
    comparison = At Least, At Most, Exactly

    ... Have current player align to targetAddress ...
    Set Memory(0x6509B0, Set To, currentPlayerVal);

    ... Set targetAddress ...
    Set Deaths("Current Player", "Terran Marine", operation, value);

    ... Read targetAddress ...
    Deaths("Current Player", "Terran Marine", comparison, value);

    Post has been edited 7 time(s), last time on Jan 29 2018, 12:30 am by jjf28.



    TheNitesWhoSay - Clan Aura - github

    Reached the top of StarCraft theory crafting 2:12 AM CST, August 2nd, 2014.

    Jan 29 2018, 2:21 am O)FaRTy1billion[MM] Post #9

    👻 👾 👽 💪

    This still seems useful, since you can directly edit the trigger and not need to store a bunch of pointers in DC's to then copy them around with countoffs.



    TinyMap2 - Latest in map compression! ( 7/09/14 - New build! )
    EUD Action Enabler - Lightweight EUD/EPD support! (ChaosLauncher/MPQDraft support!)
    EUDDB - topic - Help out by adding your EUDs! Or Submit reference files in the References tab!
    MapSketch - New image->map generator!
    EUDTrig - topic - Quickly and easily convert offsets to EUDs! (extended players supported)
    SC2 Map Texture Mask Importer/Exporter - Edit texture placement in an image editor!
    \:farty\: This page has been viewed [img]http://farty1billion.dyndns.org/Clicky.php?img.gif[/img] times!

    Jan 29 2018, 4:03 am jjf28 Post #10

    Cartography Artisan

    Ah yes, I was trying to create a simple for loop by giving the 98th trigger the power to edit its own next trigger pointer, unfortunately triggers are allocated in multiple memory blocks, not one huge memory block, so to traverse all 98 triggers in one trigger cycle without virtual triggers would take 98 sets of countoffs - at least 3136 triggers. A set of virtual triggers on the other hand could tackle this in maybe 100 triggers as you can loop through what you need on the spot; virtual triggers will be immensely useful for initialization of the parts of the trigger list you want to make dynamic.



    TheNitesWhoSay - Clan Aura - github

    Reached the top of StarCraft theory crafting 2:12 AM CST, August 2nd, 2014.

    Jan 29 2018, 4:51 am trgk Post #11



    Triggers should be inside STR section. STR section is uploaded to SC memory as one chunk. Since trigger generator can know relative addresses of all triggers inside it, and you can compute the address of STR section runtime, addresses of each STR triggers can be easily computed and triggers inside STR section can easily get addresses to each other.

    Post has been edited 1 time(s), last time on Jan 29 2018, 5:15 am by trgk.



    EUD

    Jan 29 2018, 5:22 am trgk Post #12



    Fun example of this virtual trigger: variable table. You can copy values using only 2 triggers instead of 64.
    (binary countoffs)

    Basically, the idea is to use entire trigger containing single SetDeaths action as a variable. With proper stacking of unused trigger fields, you can create n variables with 2408 + 72*(n-1) bytes memory.

    Consider this variable A containing value 1234.

    Trigger:
    Actions:
    act1: SetDeaths(0, SetTo, 1234, 0)

    Triggers have nextptr. (NextTrigPtr in jjf's article) It references exactly what trigfer should be executed next to this pointer. You can pull out A's value at runtime.

    If trigger B wants to set address C's value to A's value, B just

    - Set act1's player field to EPD player corresponding to C
    - Set act1's modifier to 'SetTo'
    - Set A's nextptr to whatever the trigger should be executed after variable assignment.
    - Set B's nextptr to A.

    Post has been edited 1 time(s), last time on Jan 29 2018, 5:34 am by trgk.



    EUD

    Jan 29 2018, 9:44 am Lanthanide Post #13



    Farty, Heinermann and jjf were talking about how to do copying of values using fewer triggers, but couldn't come up with a way to do it, although I think we all suspected that we had the tools to do it. No one considered changing the value of the player in the 'set deaths' action itself.



    None.

    Jan 29 2018, 10:10 am Heinermann Post #14

    SDE, BWAPI owner, hacker.

    Quote from trgk
    Fun example of this virtual trigger: variable table. You can copy values using only 2 triggers instead of 64.
    (binary countoffs)

    Basically, the idea is to use entire trigger containing single SetDeaths action as a variable. With proper stacking of unused trigger fields, you can create n variables with 2408 + 72*(n-1) bytes memory.

    Consider this variable A containing value 1234.

    Trigger:
    Actions:
    act1: SetDeaths(0, SetTo, 1234, 0)

    Triggers have nextptr. (NextTrigPtr in jjf's article) It references exactly what trigfer should be executed next to this pointer. You can pull out A's value at runtime.

    If trigger B wants to set address C's value to A's value, B just

    - Set act1's player field to EPD player corresponding to C
    - Set act1's modifier to 'SetTo'
    - Set A's nextptr to whatever the trigger should be executed after variable assignment.
    - Set B's nextptr to A.
    Ah, so utilizing the Set Deaths action itself as the memory space for a variable, instead of deaths. What about copying something from outside of this table (i.e. minerals or some CUnit field)? Those will still need countoffs, but I think only one set of 32 triggers to copy something to this variable space would be necessary.




    Jan 29 2018, 11:52 am trgk Post #15



    Fun fact 2. Conditional jumps.

    You're in trigger A. If A's condition holds, you want to execute trigger B as a next trigger. Else, you want to execute C.

    ----

    Introducing another resetter trigger T.

    A's original nextptr is C. When A's condition is not met, nothing happens' and C gets executed next.

    If A condition does match, A's action sets A,s nextptr to T. So executing T.

    T's nextptr is B and T has no conditions (=Always runs). T just resets A's nextptr to C. After T runs, A's nextptr resets to C and B gets executed next.



    EUD

    Jan 29 2018, 11:55 am trgk Post #16



    Quote from Heinermann
    Quote from trgk
    Fun example of this virtual trigger: variable table. You can copy values using only 2 triggers instead of 64.
    (binary countoffs)

    Basically, the idea is to use entire trigger containing single SetDeaths action as a variable. With proper stacking of unused trigger fields, you can create n variables with 2408 + 72*(n-1) bytes memory.

    Consider this variable A containing value 1234.

    Trigger:
    Actions:
    act1: SetDeaths(0, SetTo, 1234, 0)

    Triggers have nextptr. (NextTrigPtr in jjf's article) It references exactly what trigfer should be executed next to this pointer. You can pull out A's value at runtime.

    If trigger B wants to set address C's value to A's value, B just

    - Set act1's player field to EPD player corresponding to C
    - Set act1's modifier to 'SetTo'
    - Set A's nextptr to whatever the trigger should be executed after variable assignment.
    - Set B's nextptr to A.
    Ah, so utilizing the Set Deaths action itself as the memory space for a variable, instead of deaths. What about copying something from outside of this table (i.e. minerals or some CUnit field)? Those will still need countoffs, but I think only one set of 32 triggers to copy something to this variable space would be necessary.

    I haven't yet addressed that issue. Currently I have functions dedicated to reading specific dword memory with binary countoff, which consists of 35+ triggers. (Not 32 Since this has to deal with parameters.) I doubt if there's a faster way.

    EUDDraft is the tool I've made to write virtual STR triggers easily.My current function or array implementation has several limitations. (One should read array values with binary countoff, no recursive calls). I can explain them if you want.

    Post has been edited 1 time(s), last time on Jan 29 2018, 12:03 pm by trgk.



    EUD

    Jan 30 2018, 7:06 am Lethal_Illusion Post #17



    Quote from trgk
    Quote from Heinermann
    Quote from trgk
    Fun example of this virtual trigger: variable table. You can copy values using only 2 triggers instead of 64.
    (binary countoffs)

    Basically, the idea is to use entire trigger containing single SetDeaths action as a variable. With proper stacking of unused trigger fields, you can create n variables with 2408 + 72*(n-1) bytes memory.

    Consider this variable A containing value 1234.

    Trigger:
    Actions:
    act1: SetDeaths(0, SetTo, 1234, 0)

    Triggers have nextptr. (NextTrigPtr in jjf's article) It references exactly what trigfer should be executed next to this pointer. You can pull out A's value at runtime.

    If trigger B wants to set address C's value to A's value, B just

    - Set act1's player field to EPD player corresponding to C
    - Set act1's modifier to 'SetTo'
    - Set A's nextptr to whatever the trigger should be executed after variable assignment.
    - Set B's nextptr to A.
    Ah, so utilizing the Set Deaths action itself as the memory space for a variable, instead of deaths. What about copying something from outside of this table (i.e. minerals or some CUnit field)? Those will still need countoffs, but I think only one set of 32 triggers to copy something to this variable space would be necessary.

    I haven't yet addressed that issue. Currently I have functions dedicated to reading specific dword memory with binary countoff, which consists of 35+ triggers. (Not 32 Since this has to deal with parameters.) I doubt if there's a faster way.

    EUDDraft is the tool I've made to write virtual STR triggers easily.My current function or array implementation has several limitations. (One should read array values with binary countoff, no recursive calls). I can explain them if you want.

    How many triggers is EUDDraft able to generate? You mentioned needing 35+, but I thought the STR section could fit at most 27 triggers? Do you use other data sections as well?

    When you read the memory, do you use binary countoffs to subtract the value you're reading from (loading them to a new location), or do you use the trigger-variable trick you mentioned? That is, do you alter a condition's "CurrentPlayer suffered at least X deaths of Marine" value until it matches the location being read?


    A more general question: Is it possible in SC:R to modify triggers in the TRIG section (forming conditionals, goto, loop, etc.), or does the EUD emulator not allow any reading from or writing to the TRIG section? If the TRIG section is unavailable, Dynamic Triggers can only be inserted before any given player's triggers execute, right?



    None.

    Jan 30 2018, 7:33 am trgk Post #18



    Quote from Lethal_Illusion
    How many triggers is EUDDraft able to generate? You mentioned needing 35+, but I thought the STR section could fit at most 27 triggers? Do you use other data sections as well?
    As much as you want. 64k is just the limitation of String table format, not STR section itself.

    Quote from Lethal_Illusion
    When you read the memory, do you use binary countoffs to subtract the value you're reading from (loading them to a new location), or do you use the trigger-variable trick you mentioned? That is, do you alter a condition's "CurrentPlayer suffered at least X deaths of Marine" value until it matches the location being read?

    There are two ways of reading memory in euddraft. Both methods use binary countoff. (Since I'm reading memory outside of the variable table)
    dwread_epd_safe function modifies the condition until it matches the memory value. This is useful when reading read-only memory.
    Implementation here.
    dwread_epd uses more traditional method. (Compare and subtract 1<<i from the source memory) This is faster.
    Implementation here.

    These functions are very low-level ones, so they may be hard to comprehend. Users of this tool don't need to write this kind of code themselves.


    Quote from Lethal_Illusion
    A more general question: Is it possible in SC:R to modify triggers in the TRIG section (forming conditionals, goto, loop, etc.), or does the EUD emulator not allow any reading from or writing to the TRIG section? If the TRIG section is unavailable, Dynamic Triggers can only be inserted before any given player's triggers execute, right?

    You can read and write TRIG-section triggers. Helper functions like EUDLoopTrigger helps you traverse through the trigger linked list. freeze protection (one mentioned by 0xeb) decrypts TRIG trigger data online. In practice, however, this method is way harder to implement than just using STR triggers.

    Post has been edited 2 time(s), last time on Jan 30 2018, 7:39 am by trgk.



    EUD

    Aug 29 2019, 9:00 am T-warp Post #19

    Unlimited N-word pass winner

    There's one more method to copy values, using bitmasks. It's non destructive and uses less triggers.




    Options
      Back to forum
    Please log in to reply to this topic or to report it.
    Members in this topic: None.
    [10:11 pm]
    Ultraviolet -- :P
    [10:11 pm]
    Ultraviolet -- How about you all send me your minerals instead of washing them into the gambling void? I'm saving up for a new name color and/or glow
    [11:50 pm]
    O)FaRTy1billion[MM] -- nice, now i have more than enough
    [11:49 pm]
    O)FaRTy1billion[MM] -- if i don't gamble them away first
    [11:49 pm]
    O)FaRTy1billion[MM] -- o, due to a donation i now have enough minerals to send you minerals
    [2024-4-17. : 3:26 am]
    O)FaRTy1billion[MM] -- i have to ask for minerals first tho cuz i don't have enough to send
    [2024-4-17. : 1:53 am]
    Vrael -- bet u'll ask for my minerals first and then just send me some lousy vespene gas instead
    [2024-4-17. : 1:52 am]
    Vrael -- hah do you think I was born yesterday?
    [2024-4-17. : 1:08 am]
    O)FaRTy1billion[MM] -- i'll trade you mineral counts
    [2024-4-16. : 5:05 pm]
    Vrael -- Its simple, just send all minerals to Vrael until you have 0 minerals then your account is gone
    Please log in to shout.


    Members Online: Ultraviolet, Roy, NudeRaider