• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

Accessing memory from the script - it's time of the revolution

Status
Not open for further replies.
Level 19
Joined
Dec 12, 2010
Messages
2,069
you can basically read whereever you want, whatever index you want to, but you should be aware of trash in extra-bytes. read offset 3 will give you part of 1st 4-bytes and 3/4 of the next 4 bytes. That may be useful in case if your data stands somewhere in non-byte formatting, which isn't the case for object's data anyway
 

LeP

LeP

Level 13
Joined
Feb 13, 2008
Messages
539
the hashing algorithm used to look up the object data is the same as the one for looking up files in mpq :)
 
Level 31
Joined
Jul 10, 2007
Messages
6,306
Do you think something like function pointers from c++ would be possible? : )


A way to call the function directly without going through ForForce or triggers ; O



If so, would it be faster than just doing a TriggerEvaluate/ForForce? ^_^


I sense the possibility of polymorphism, and it excites me.


Please give us writes too, so we can do away with things like BonusMod : D
 
Level 19
Joined
Dec 12, 2010
Messages
2,069
Do you think something like function pointers from c++ would be possible? : )


A way to call the function directly without going through ForForce or triggers ; O



If so, would it be faster than just doing a TriggerEvaluate/ForForce? ^_^


I sense the possibility of polymorphism, and it excites me.


Please give us writes too, so we can do away with things like BonusMod : D
some re-calculatable values tends to alter few other stats of unit in case if new value doesn't fit. i'd say this bonus mode requires much more time to check
 

Zwiebelchen

Hosted Project GR
Level 35
Joined
Sep 17, 2009
Messages
7,236
Something completely new I was thinking about; and I wonder how difficult it would be with this:

Is it possible writing dynamic tooltips for items as per-instance, or can these only be manipulated per-raw?
This could open the door for entirely (or partially) randomized items and/or allow for neat item upgrade systems.
 
Level 19
Joined
Dec 12, 2010
Messages
2,069
Something completely new I was thinking about; and I wonder how difficult it would be with this:

Is it possible writing dynamic tooltips for items as per-instance, or can these only be manipulated per-raw?
This could open the door for entirely (or partially) randomized items and/or allow for neat item upgrade systems.

imagine you have full access to the source code. you can do whatever you want. the only issue is - you working on ASM level, patching bytes & running instructions from the bottom. So you'll spend much time detecting addresses you need and how to improve them.

So far we did most tedious work within our custom DLL which is being extractd and attached to the game on map loading. Memory patching used mostly for short changes, like unit's or ability properties set/check. This allow us to save much time, but everything can be done without DLLs, imitating it's actions.

Example:
Vi5j.png

hUmp.png

everything - is everything. no limits.

I believe you can as well populate map with custom objects which are being created in the runtime. Tho I can't guarantee that.
 

Zwiebelchen

Hosted Project GR
Level 35
Joined
Sep 17, 2009
Messages
7,236
I believe you can as well populate map with custom objects which are being created in the runtime. Tho I can't guarantee that.
I'd say actually adding stuff to the memory (unless it's an unpopulated array you write to) is probably harder than just overwriting an existing value, since you don't have to worry about possible cross-talk or references/counts.

But the thing is: with the power to overwrite variables, why would you need that?
You can just create a simple dummy item and basicly just edit the tooltip (and icon) however you like to create an infinite number of items with the same rawcode, but completely different appearance.


I'd probably kill for a full item API with a read and write function for tooltips and icon path.
 
Level 9
Joined
Jul 30, 2012
Messages
156
How would a function look that only gets the "white" armor not including green or red bonuses?

This information is not stored anywhere, it's calculated from a unit's abilities whenever needed.

Basically whenever the UI is rendered the game will enumerate the abilities of a unit and ask to each of them: "How many armor are you providing?". If an ability says "I'm providing 3 armor", then the game will display +3 as the green number.

After doing that with all abilities, the game will read the total armor of the unit, so if the unit has 10 armor, it will appear as "7+3".

Do you think something like function pointers from c++ would be possible? : )

A way to call the function directly without going through ForForce or triggers ; O

Unlike the natives that take code params, standard Jass calls (opcode 0x16 of the VM) use the function id, not the function address, to perform the call.

To call a code variable directly you need to obtain the id of that function (you can get it with Memory[C2I(func)/4-1]), then write this id to the call instruction, and execute it.

Of course, you can only use direct calls to execute valid functions, you can't call the middle of a function or execute bytecode from an array with this method, that is only possible with things like ForForce.

But the thing is: with the power to overwrite variables, why would you need that?
You can just create a simple dummy item and basicly just edit the tooltip (and icon) however you like to create an infinite number of items with the same rawcode, but completely different appearance.

Things don't work like that. Most objects don't store any data, they just store the rawcode, and when some data is needed, it's taken directly from the data table. This means that if you change, say, the tooltip of an item, you're going to change the tooltip for all items with that rawcode, not just one.

However it's perfectly possible to generate new objects at runtime. The data table is organized in linked lists, so there is no risk of overwriting something else. You can make a data struct in a standard Jass array, then obtain the address of that array, and append it to the linked list.
 
Level 29
Joined
Jul 29, 2007
Messages
5,174
Things don't work like that. Most objects don't store any data, they just store the rawcode, and when some data is needed, it's taken directly from the data table. This means that if you change, say, the tooltip of an item, you're going to change the tooltip for all items with that rawcode, not just one.

However it's perfectly possible to generate new objects at runtime. The data table is organized in linked lists, so there is no risk of overwriting something else. You can make a data struct in a standard Jass array, then obtain the address of that array, and append it to the linked list.

You can actually have different values per object, as can be seen by SharpCraft demos.
 
Level 5
Joined
Sep 6, 2010
Messages
91
You can actually have different values per object, as can be seen by SharpCraft demos.
Yep, Sharpcraft really allows different values on each object in the editor, but I do not know exactly if Sharpcraft and Memory can be used at the same time because if so would the best tool so far. (This would allow, rename two units with equal rawcodes within a game, same for the skills and cooldowns, etc, "there are no limits xD") Even the map of Dota 1 could reach mechanisms Dota 2 easily, lol. :gg:
Greetings ... (would be a matter of switching codes, with the work of MindWorX and leandrotp) :con:
_________
:Blizzard please take this into account and look what can be achieved within the game WC3:
 
Last edited:

Zwiebelchen

Hosted Project GR
Level 35
Joined
Sep 17, 2009
Messages
7,236
Things don't work like that. Most objects don't store any data, they just store the rawcode, and when some data is needed, it's taken directly from the data table. This means that if you change, say, the tooltip of an item, you're going to change the tooltip for all items with that rawcode, not just one.
That wouldn't matter if you can change what is rendered on screen.

All the things I need are purely cosmetical. I can easily apply the green number to armor via abilities, so I don't need to change the armor value directly. What I want is changing how armor is displayed, so that the green number goes into the white number.
As far as I understood now, the white number is just the result of the calculus "total armor minus green". So in this case, wouldn't I just need to overwrite the result of the ability enumeration and set it to 0?

Same goes with unit level and item tooltips. I don't actually need to edit items directly, as long as I can change what is displayed when the tooltip is rendered.
 
Level 19
Joined
Dec 12, 2010
Messages
2,069
Irregular implementation of regular things

1. Movespeed isn't saved as is. Instead it stores ms/32 value everytime when any move-related order issued, including "go there and cast this sht". That leads to maximum (normally) speed limit of 16.3125 per 1/32 sec. Increasing this value allows to move faster BUT unit still missing control points very often and turning around to grab them, wasting time and whole purpose of breaking limit. This value being re-calculated on every given situation.

2. Movement is a vector. Game stores an angle unit move in, current timestamp and speed. Modifying timestamp allows to push unit along vector with distance = (currentTime - timestamp) * speed. Again, strange decision, as game have to track every unit's X/Y modification, recount angle and re-write timestamps literally every game tick.

3. There's no raw "collision size". Moveover, it's not really collision, but rather detection and action size. Unit with 48 collision size will have 1. stored inside his structure. All other sizes stored as multiplier of 48 as well, with pretty high precision. 32 = 0.75, 8 = 0.25, etc. This multiplier affects the distance just like normal collision size, but doesn't affect pathings whatsoever, at least for my short testings.

4. HP/MP regeneration. Everytime unit's hp/mp and related regeneration modified anyhow (jass, attack, spell, anything), unit's structure stores current timestamp, current hp/mp (after the modification) and newest regeneration. Basically game doesn't care about unit's current hp/mp, instead it set them in the way of (currentTime - timestamp) * regeneration. That means changing one of parameters will lead to reduce or increasing current HP/MP as well. Why they did it as a vector too?

5. There's no cooldown animation as it is. Basically every square on the panel and item's panel has it's own cooldown overlays, which are hidden by default. In case if ability tells "im on cd", overlay being attached. So far I didn't went even close to simulate overlay when I need it. So damn strange decisions all around.
 
Level 29
Joined
Jul 29, 2007
Messages
5,174
The cooldown effect is implemented by the model ui/feedback/cooldown/ui-cooldown-indicator.mdx.
I am assuming there's a hardcoded array of instances of this model somewhere in memory, and the game plays their walk animation when needed (with the animation's speed scaled for different cooldown durations?)

I am not sure why the stepping mechanism sounds weird. That's how every game loop works - in discreet steps of time, using differences in time to scale time-related things, which movement, and regeneration, are.
This is how you create code that doesn't run at different speeds depending on the FPS you get in the game (although the game probably runs at a constant FPS, since otherwise FPS jumps can cause all sorts of physics simulation explosions, and probably breaks sync between multiple clients when playing online, but this is still done by using timestamps, and by using a structure that can work with any arbitrary FPS you get on your computer).
If your speed is 2 units per second, and 1 second passed between frames, you move 2 units. If half a second passed, you can now either move 2 units, if your code is dependent on the FPS, or you can scale it by 0.5 (the time passed in seconds), and move 1 unit, which is the definition of 2 units per second.
 
Level 9
Joined
Aug 26, 2010
Messages
573
The cooldown effect is implemented by the model ui/feedback/cooldown/ui-cooldown-indicator.mdx.
I am assuming there's a hardcoded array of instances of this model somewhere in memory, and the game plays their walk animation when needed (with the animation's speed scaled for different cooldown durations?)

I am not sure why the stepping mechanism sounds weird. That's how every game loop works - in discreet steps of time, using differences in time to scale time-related things, which movement, and regeneration, are.
This is how you create code that doesn't run at different speeds depending on the FPS you get in the game (although the game probably runs at a constant FPS, since otherwise FPS jumps can cause all sorts of physics simulation explosions, and probably breaks sync between multiple clients when playing online, but this is still done by using timestamps, and by using a structure that can work with any arbitrary FPS you get on your computer).
If your speed is 2 units per second, and 1 second passed between frames, you move 2 units. If half a second passed, you can now either move 2 units, if your code is dependent on the FPS, or you can scale it by 0.5 (the time passed in seconds), and move 1 unit, which is the definition of 2 units per second.

But you don't have to save timestamp in everything. You can just calculate delta between frames and use it in every calculation. It will be less precise in each frame but in average precision will be same.
 
Level 13
Joined
Oct 18, 2013
Messages
690
I get a little rush everytime someone finds something new to do on war3. ;D


Is there anyway to modify the green bonus to stats? That would be neat.

Really though, give us something to use to write data. I want dynamic ability descriptions.

Can someone verify or deny whether the Bitwise operations are faster or not?
 
Last edited:

Zwiebelchen

Hosted Project GR
Level 35
Joined
Sep 17, 2009
Messages
7,236
I get a little rush everytime someone finds something new to do on war3. ;D


Is there anyway to modify the green bonus to stats? That would be neat.
He already explained how it works for armor and damage. Long story short: it's possible, but you need to apply some workarounds to it, since only the total amount of armor/damage is stored, not the green and white numbers individually.

And if you are talking about STR/AGI/INT, then there are much easier ways to change these (tomes).
 
Level 13
Joined
Oct 18, 2013
Messages
690
He already explained how it works for armor and damage. Long story short: it's possible, but you need to apply some workarounds to it, since only the total amount of armor/damage is stored, not the green and white numbers individually.

And if you are talking about STR/AGI/INT, then there are much easier ways to change these (tomes).

Well, I imagine being able to apply green bonuses to be a lot more efficient with a SetAgiBonus(blahblah) than spamming tomes on a unit, and tomes add to the white bonus anyways.

Though, aren't the green values for all hero stats stored somewhere in war3? I'm assuming API for Green Stats is on the way.
 
Last edited:

Zwiebelchen

Hosted Project GR
Level 35
Joined
Sep 17, 2009
Messages
7,236
Just used experimental pjass to get all that stuff to work.

I get several compiling errors, though:

In "Utilities", there are some small syntax errors
JASS:
// Returns the mana cost of an ability for the specified level. Don't pass a level above the max
function GetAbilityManaCost takes integer a, integer level returns integer
     return Memory[Memory[GetObjectData(pAbilityData, abil)+21]/4-22+level*26] //undeclared variable "abil"
endfunction

// Returns the base cooldown of an ability for a specified level.
function GetAbilityCooldown takes ability abil, integer level returns real
     return indexToReal(Memory[Memory[GetObjectData(pAbilityData, abil)+21]/4-21+level*26]) //can not convert ability to integer
endfunction

In "Version", all instances of I2A get inlined, causing an "Index missing for variable I__A" error in the following functions:

JASS:
function Init26 takes nothing returns nothing
     set GameBase = Memory[A2I()/4]/4-0x254418
     //set GameBase=s__Memory__staticgetindex((l__A) / 4) / 4 - 0x254418 // INLINED!!
     set GameState = GameBase+0x2AD97D
     set pUnitData = GameBase+0x2AD11E
     set pAbilityData = GameBase+0x2ACF99
endfunction

function Init27 takes nothing returns nothing
     set GameBase = Memory[A2I()/4]/4-0x298ECC
     //set GameBase=s__Memory__staticgetindex((l__A) / 4) / 4 - 0x298ECC // INLINED!!
     set GameState = GameBase+0x2F908E
     set pUnitData = GameBase+0x2FB123
     set pAbilityData = GameBase+0x2FB351
endfunction

private function Init takes nothing returns nothing
     local integer i
     call ImLazy()
     set i = Memory[A2I()/4]
     //set i=s__Memory__staticgetindex((l__A) / 4) // INLINED!!
     set i = i - Memory[i/4]
     if i == 2586768 then
         call Init27()
     elseif i == 5205600 then
         call Init26()
     else
         call BJDebugMsg("Unsupported version. All memory access is disabled")
         call DestroyTrigger(MemReader)
         set MemReader = null
     endif
endfunction
 
Level 19
Joined
Dec 12, 2010
Messages
2,069
Just used experimental pjass to get all that stuff to work.

I get several compiling errors, though:

In "Utilities", there are some small syntax errors
JASS:
// Returns the mana cost of an ability for the specified level. Don't pass a level above the max
function GetAbilityManaCost takes integer a, integer level returns integer
     return Memory[Memory[GetObjectData(pAbilityData, abil)+21]/4-22+level*26] //undeclared variable "abil"
endfunction

// Returns the base cooldown of an ability for a specified level.
function GetAbilityCooldown takes ability abil, integer level returns real
     return indexToReal(Memory[Memory[GetObjectData(pAbilityData, abil)+21]/4-21+level*26]) //can not convert ability to integer
endfunction

looks like code messed a bit. I used "a" before as "ability", and later we did that for IDs as well, which allowed to use integers. So it's small issue right there with naming, which actually breaks it
 
Level 9
Joined
Jul 30, 2012
Messages
156
Just used experimental pjass to get all that stuff to work.

I get several compiling errors, though:

In "Utilities", there are some small syntax errors
JASS:
// Returns the mana cost of an ability for the specified level. Don't pass a level above the max
function GetAbilityManaCost takes integer a, integer level returns integer
     return Memory[Memory[GetObjectData(pAbilityData, abil)+21]/4-22+level*26] //undeclared variable "abil"
endfunction

// Returns the base cooldown of an ability for a specified level.
function GetAbilityCooldown takes ability abil, integer level returns real
     return indexToReal(Memory[Memory[GetObjectData(pAbilityData, abil)+21]/4-21+level*26]) //can not convert ability to integer
endfunction

In "Version", all instances of I2A get inlined, causing an "Index missing for variable I__A" error in the following functions:

JASS:
function Init26 takes nothing returns nothing
     set GameBase = Memory[A2I()/4]/4-0x254418
     //set GameBase=s__Memory__staticgetindex((l__A) / 4) / 4 - 0x254418 // INLINED!!
     set GameState = GameBase+0x2AD97D
     set pUnitData = GameBase+0x2AD11E
     set pAbilityData = GameBase+0x2ACF99
endfunction

function Init27 takes nothing returns nothing
     set GameBase = Memory[A2I()/4]/4-0x298ECC
     //set GameBase=s__Memory__staticgetindex((l__A) / 4) / 4 - 0x298ECC // INLINED!!
     set GameState = GameBase+0x2F908E
     set pUnitData = GameBase+0x2FB123
     set pAbilityData = GameBase+0x2FB351
endfunction

private function Init takes nothing returns nothing
     local integer i
     call ImLazy()
     set i = Memory[A2I()/4]
     //set i=s__Memory__staticgetindex((l__A) / 4) // INLINED!!
     set i = i - Memory[i/4]
     if i == 2586768 then
         call Init27()
     elseif i == 5205600 then
         call Init26()
     else
         call BJDebugMsg("Unsupported version. All memory access is disabled")
         call DestroyTrigger(MemReader)
         set MemReader = null
     endif
endfunction

There was a typo in GetAbilityCooldown, it takes an integer, not an ability handle. I have fixed it in my post. For the inlining problem, I added an extra return in the function, to prevent it from being inlined. It's a temporary fix, I'm going to rework the Memory library soon.
 
Level 17
Joined
Apr 27, 2008
Messages
2,455
Is it possible to run a part of a function ?

For example, could i display "1" , "2" or "3" there ?

JASS:
function Test takes ....

   call BJDebugMsg("1")
   return
   call BJDebugMsg("2")
   return
   call BJDebugMsg("3")
   return
endfunction
That seems a silly example.
But for the custom wait emulation, it would be better than the case, or function approach.
 
Level 9
Joined
Jul 30, 2012
Messages
156
Is it possible to run a part of a function ?

For example, could i display "1" , "2" or "3" there ?

JASS:
function Test takes ....

   call BJDebugMsg("1")
   return
   call BJDebugMsg("2")
   return
   call BJDebugMsg("3")
   return
endfunction
That seems a silly example.
But for the custom wait emulation, it would be better than the case, or function approach.

Absolutely possible. But if that function has local variables, skipping lines of execution from the beginning of the function would cause the creation of local variables to be skipped as well. Then the code may try to access variables that don't exist, and the game will crash. (Unless there's a global variable with the same name as the local, then the global will be accessed when the local doesn't exist)
 
Level 17
Joined
Apr 27, 2008
Messages
2,455
It seems i have not understood how to do it.

I got a fatal warcraft error when i'm trying this :

JASS:
scope Test initializer Init

    function Act takes nothing returns nothing
        call BJDebugMsg(" ")
        call BJDebugMsg("1")
        call BJDebugMsg("2")
        call BJDebugMsg("3")
    endfunction

//===========================================================================
    private function Init takes nothing returns nothing
        call FireCode(I2C(Memory[C2I(function Act)/4-1]))
    endfunction

endscope

JASS:
library FireCode initializer init

    globals
        private force F
    endglobals
    
    function FireCode takes code c returns nothing
        call ForForce(F,c)
    endfunction

    private function init takes nothing returns nothing
        set F=CreateForce()
        call ForceAddPlayer(F,GetLocalPlayer())
    endfunction
    
endlibrary
 
Level 9
Joined
Jul 30, 2012
Messages
156
It seems i have not understood how to do it.

I got a fatal warcraft error when i'm trying this :

You didn't understand my previous post. Forget what I said about "obtaining the id of a function". You're not going to need it here. All you need to do is:

JASS:
scope Test initializer Init

    function Act takes nothing returns nothing
        call BJDebugMsg(" ")
        call BJDebugMsg("1")
        call BJDebugMsg("2")
        call BJDebugMsg("3")
    endfunction

//===========================================================================
    private function Tests takes nothing returns nothing
        call FireCode(function Act) // Displays all 4 messages.
        call FireCode(I2C(32+C2I(function Act))) // Displays only "1", "2" and "3"
        call FireCode(I2C(64+C2I(function Act))) // Displays only "2" and "3"
        call FireCode(I2C(96+C2I(function Act))) // Displays only "3"
    endfunction

endscope
 
Level 17
Joined
Apr 27, 2008
Messages
2,455
Ok, thank you leantdrop.
Now i need to learn how many bytes jass instructions take.

EDIT : Oh well, i would need to track BJ and other custom function jass functions, so it doesn't worth it for what i want to do.
It's still fun to know that we have goto in jass though :grin:

@DracoL1ch :

Sorry, but i don't get what you're trying to say.
You mean if i edit the code of the function later ?
 
Last edited:
Level 12
Joined
Mar 13, 2012
Messages
1,121
GhostWolf and DracoL1ch are just saying that this is very bad coding style if one would use that instead of the obvious choice of normal "if" conditions to steer function execution.
 
Level 6
Joined
Jul 30, 2013
Messages
282
it should be possible to make like eval() with this (i know its possible now, but i mean one that does not suck balls because of the oplimit and overhead, now u can emit actual bytecode and get jass-native perf!)

could be rly cool, if sb would bother with it.. i wonder if blizz will "fix" it tho, would be sad if they did :(
 
Level 9
Joined
Aug 26, 2010
Messages
573
It will require you to either make a jass assembler in jass (which will be slow as hell I think) or to save your evaling function in bytes form which will require you to make some auto generation for that
Anyway it's useless. Simply use inheritance instead of eval. Eval is evil if you have class system of any kind.

But I'd love to see jass JIT compiler written in JASS. It'll be 100% useless but 100% cool.
 
Level 6
Joined
Jul 30, 2013
Messages
282
ofc, for 1 time use it would be useless.. but like all jit it would become more noticable later, also using inheritance means overhead too tho i agree it is more elegant/robust.

mostly tho i just shudder of expectation of what can be done with this :D.
 
Level 5
Joined
Sep 6, 2010
Messages
91
You didn't understand my previous post. Forget what I said about "obtaining the id of a function". You're not going to need it here. All you need to do is:

JASS:
scope Test initializer Init

    function Act takes nothing returns nothing
        call BJDebugMsg(" ")
        call BJDebugMsg("1")
        call BJDebugMsg("2")
        call BJDebugMsg("3")
    endfunction

//===========================================================================
    private function Tests takes nothing returns nothing
        call FireCode(function Act) // Displays all 4 messages.
        call FireCode(I2C(32+C2I(function Act))) // Displays only "1", "2" and "3"
        call FireCode(I2C(64+C2I(function Act))) // Displays only "2" and "3"
        call FireCode(I2C(96+C2I(function Act))) // Displays only "3"
    endfunction

endscope

Hopefully not the function FIRECODE just speculation, looks very promising (in some way) XD , as: ...Change the UI of the game in real time cof..cof.. (although I exaggerate LoL)
Greetings ... (A doubt someone could already get the attack value of a unit :cq:)
 
Level 5
Joined
Sep 6, 2010
Messages
91
I posted dozen 'get' functions before, there's were many about unit's attack properties
well, changin UI is tricky enough

bpxT.png

Wow nice, I thought it was unlikely do this but seeing the image, I correct myself XD (if this to been done with Memory), since one could also change other [UI] as: [UI] cooldown .mdx ( And combined with this http://www.hiveworkshop.com/forums/lab-715/custom-cooldown-interface-275790/, a new counter of time), [UI] Minimap with some animation, [UI] Day and Night interface, etc.
All I say is just speculation of course, but as you say, this requires a good knowledge to do it ... Uhm :croll:

Greetings... (Oh, and the question I asked about the attack, was for know if someone already found within the Memory, the total attack of a unit, as the GetUnitArmor function leandrotp giving the value directly.
For my part I still do not get the correct data. .-. . But I see that other data are necessary to obtain the true value recently.)
 
Level 19
Joined
Dec 12, 2010
Messages
2,069
everything is possible, question is how long it takes to get into the process. for instance we still have no idea how to draw counters and manipualte them. many stuff hardcoded just for few base IDs and cannot be done for other IDs without modifying whole thing
 
Level 5
Joined
Sep 6, 2010
Messages
91
everything is possible, question is how long it takes to get into the process. for instance we still have no idea how to draw counters and manipualte them. many stuff hardcoded just for few base IDs and cannot be done for other IDs without modifying whole thing

About the method of loads that could be used as an indicator of Cooldown, in this case would only cosmetically change the number. But as you say, a skill data are based on the raw code ID, and modify some data could generate faults. But it could use another method as Firecode to only enter the value shown in the interface .. Uhm.. fence that this is difficult to do.

In itself, I was just looking for a new way, to create a new skin (counter), that Blizzard did not implement for a skill.

Greetings...(I could say that this method would no longer be useful.. :hohum: , for the moment XD)
 

~El

Level 17
Joined
Jun 13, 2016
Messages
556
On a slightly related note, wouldn't it be possible to somehow find the memory location responsible for the ops counter, and being able to read/set it using this? It would allow for a really easy way of circumventing it. Provided, of course, someone found where it is stored.
 

~El

Level 17
Joined
Jun 13, 2016
Messages
556
jass being executed based on tickrate. can't really make anything about it. every procedure has 25 milliseconds to finish it's job, and 100 ms as limit. no idea how to change that - any change in ticks will cause desyncing between realtime and engine time

Is that really so? I mean, is that how threads "time out", based on execution time? Where did you get that information?

I'm sorry for being skeptical, but the tests I performed seem to suggest (at least that is the conclusion I draw) that every JASS instruction seems to increment the ops counter (wherever it is) by a certain number (which don't seem to be related anyhow to real execution time), and when the ops counter goes above a certain limit, the VM terminates the thread.

This is the code I used for testing last time.

JASS:
//! zinc

library Test {
    integer count;
    integer i;
    player targ;
    string teststr1 = "abcdefgh";
    string teststr2 = "aaaaaaaabbbbbbbbbbbbcccccccccccccccddddddddddddddddeeeeeeeeeeeeeeeeeffffffffffffffffgggggggggggggggggggghhhhhhhhhhhhhhhh";
  
    function Dummy() {
    }
  
    function Dummy2(real x) -> real {
        return x;
    }
  
    function EmptyLoad() {
        while (true) {
            count = count + 1;
        }
    }
  
    function Test0() {
        while (true) {
            count = count + 1;
            Dummy();
        }
    }
  
    function Test1() {
        while (true) {
            count = count + 1;
            i = 1;
        }
    }
  
    function Test2() {
        while (true) {
            count = count + 1;
            i = i + 1;
        }
    }

    function Test3() {      
        while (true) {
            count = count + 1;
            SquareRoot(2);
        }
    }
  
    function Test4() {
        while (true) {
            count = count + 1;
            Sin(2);
        }
    }
  
    function Test5() {
        while (true) {
            count = count + 1;
            R2I(2);
        }
    }
  
    function Test6() {      
        while (true) {
            count = count + 1;
            Dummy2(2);
        }
    }
  
    function Test7() {      
        while (true) {
            count = count + 1;
            StringHash(teststr1);
        }
    }
  
    function Test8() {      
        while (true) {
            count = count + 1;
            StringHash(teststr2);
        }
    }
  
    function Test9() {      
        while (true) {
            count = count + 1;
            StringLength(teststr1);
        }
    }
  
    function Test10() {      
        while (true) {
            count = count + 1;
            StringLength(teststr2);
        }
    }

    function onInit() {
        targ = Player(0);
        i = 0;
        count = 0;
        TriggerSleepAction(1);
        BJDebugMsg("Running empty load");
        EmptyLoad.execute();
        BJDebugMsg("loop iterations executed: " + I2S(count));
        count = 0;
        TriggerSleepAction(1);
        BJDebugMsg("Running test0 (empty function call)");
        Test0.execute();
        BJDebugMsg("loop iterations executed: " + I2S(count));
        count = 0;
        TriggerSleepAction(1);
        BJDebugMsg("Running test1 (simple set)");
        Test1.execute();
        BJDebugMsg("loop iterations executed: " + I2S(count));
        count = 0;
        TriggerSleepAction(1);
        BJDebugMsg("Running test2 (incremental set)");
        Test2.execute();
        BJDebugMsg("loop iterations executed: " + I2S(count));
        count = 0;
        TriggerSleepAction(1);
        BJDebugMsg("Running test3 (SquareRoot test)");
        Test3.execute();
        BJDebugMsg("loop iterations executed: " + I2S(count));
        count = 0;
        TriggerSleepAction(1);
        BJDebugMsg("Running test4 (Sin test)");
        Test4.execute();
        BJDebugMsg("loop iterations executed: " + I2S(count));
        count = 0;
        TriggerSleepAction(1);
        BJDebugMsg("Running test5 (R2I test)");
        Test5.execute();
        BJDebugMsg("loop iterations executed: " + I2S(count));
        count = 0;
        TriggerSleepAction(1);
        BJDebugMsg("Running test6 (Dummy returner test)");
        Test6.execute();
        BJDebugMsg("loop iterations executed: " + I2S(count));
        count = 0;
        TriggerSleepAction(1);
        BJDebugMsg("Running test7 (short StringHash test)");
        Test7.execute();
        BJDebugMsg("loop iterations executed: " + I2S(count));
        count = 0;
        TriggerSleepAction(1);
        BJDebugMsg("Running test8 (long StringHash test)");
        Test8.execute();
        BJDebugMsg("loop iterations executed: " + I2S(count));
        count = 0;
        TriggerSleepAction(1);
        BJDebugMsg("Running test9 (short StringLength test)");
        Test9.execute();
        BJDebugMsg("loop iterations executed: " + I2S(count));
        count = 0;
        TriggerSleepAction(1);
        BJDebugMsg("Running test10 (long StringLength test)");
        Test10.execute();
        BJDebugMsg("loop iterations executed: " + I2S(count));
    }
}

//! endzinc

It produces consistent results every time, and not only that, it seems that similiar native calls (i.e. same argument count and types and same return type, e.g. SquareRoot and Sin, StringHash and StringLength) take the same number of ops. If someone else could run this test and share the numbers that they got, it would help greatly. Right now, I have a strong feeling that threads are terminated when their ops count go above a certain limit, rather than being reliant on actual execution time. If someone has evidence to suggest otherwise, I'd love to see it, but until then, I feel very reluctant to accept that it is based on execution time (as opposed to execution count).

Test results:
Empty load - 42857
test0 (empty function call) - 30000
test1 (set i = 1) - 33333
test2 (set i = i + 1) - 23077
test3 (SquareRoot(2)) - 27273
test4 (Sin(2)) - 27273
test5 (R2I(2)) - 27273
test6 (dummy function return) - 18750
test7 (short StringHash) - 30000
test8 (long StringHash) - 30000
test9 (short StringLength) - 30000
test10 (long StringLength) - 30000

P.S. Another argument in refutal of execution time based termination is if you use very expensive calls (such as CreateUnit), then the thread will not terminate even at very high counts, even though it may cause a very major lag spike (e.g. 1000, 2000, 4000 units).
 
Level 19
Joined
Dec 12, 2010
Messages
2,069
well thats how dumped perfomance logs looks like - 25/100 ms steps all over the place. we didn't really investiage opcode limit, so if you bother you can find addresses youself and check if altering them will help. mind to share addresses as well. If they are stored somewhere inside gamedll its easiest thing to change

e:
ye, re-reading my post I found myself bad about it :X
can you drop here clean jass-script (without any stuff like vjas or zinc)? I don't work at editor and can only check raw jass. I'll run tests myself too
 
Last edited:

~El

Level 17
Joined
Jun 13, 2016
Messages
556
well thats how dumped perfomance logs looks like - 25/100 ms steps all over the place. we didn't really investiage opcode limit, so if you bother you can find addresses youself and check if altering them will help. mind to share addresses as well. If they are stored somewhere inside gamedll its easiest thing to change

e:
ye, re-reading my post I found myself bad about it :X
can you drop here clean jass-script (without any stuff like vjas or zinc)? I don't work at editor and can only check raw jass. I'll run tests myself too

Sure. If you don't mind, I'll just dump the war3map.j after it was processed by zinc and vjass.

On a side-related note, unfortunately I don't have much experience with reverse-engineering, but perhaps when I have a boatload of free time I will try. It is definitely something worth looking into.

Edit: Actually, scratch that, I'll just post the map.
 

Attachments

  • opstest.w3m
    17.7 KB · Views: 95
Last edited:
Status
Not open for further replies.
Top