1. Updated Resource Submission Rules: All model & skin resource submissions must now include an in-game screenshot. This is to help speed up the moderation process and to show how the model and/or texture looks like from the in-game camera.
    Dismiss Notice
  2. Hey guys, we've posted the Results for the 30th Modeling Contest. Check them out!
    Dismiss Notice
  3. The 15th Mini-Mapping Contest came to an end. The Secrets of Warcraft 3 are soon to be revealed! Come and vote in the public poll for your favorite maps.
    Dismiss Notice
  4. The 12th incarnation of the Music Contest is LIVE! The theme is Synthwave. Knight Rider needs a song to listen to on his journey. You should definitely have some fun with this theme!
    Dismiss Notice
  5. Join other hivers in a friendly concept-art contest. The contestants have to create a genie coming out of its container. We wish you the best of luck!
    Dismiss Notice
  6. Check out the Staff job openings thread.
    Dismiss Notice
Dismiss Notice
60,000 passwords have been reset on July 8, 2019. If you cannot login, read this.

Detect Game Version (GetPatchLevel)

Discussion in 'The Lab' started by TriggerHappy, Aug 29, 2018.

  1. TriggerHappy

    TriggerHappy

    Code Moderator

    Joined:
    Jun 23, 2007
    Messages:
    3,633
    Resources:
    22
    Spells:
    11
    Tutorials:
    2
    JASS:
    9
    Resources:
    22
    Feel free to try and add detection for more patches. For example you could detect 1.28.1 by checking order id's (some got changed on that patch).

    Code (vJASS):
    local integer patch = GetPatchLevel()

    if (patch == PATCH_LEVEL_127) then
        call BJDebugMsg("You are on patch 1.27b or lower")
    elseif (patch == PATCH_LEVEL_128) then
        call BJDebugMsg("You are on patch 1.28")
    elseif (patch == PATCH_LEVEL_129) then
        call BJDebugMsg("You are on patch 1.29")
    elseif (patch == PATCH_LEVEL_130) then
        call BJDebugMsg("You are on patch 1.30.0 or 1.30.1")
    elseif (patch == PATCH_LEVEL_1302) then
        call BJDebugMsg("You are on patch 1.30.2, 1.30.3, 1.30.4, or higher (bye hostbots)")
    elseif (patch == PATCH_LEVEL_131) then
        call BJDebugMsg("You are on patch 1.31.0 or higher.")
    endif
     

    Code (vJASS):
    library GameVersion
       
        globals
            constant integer PATCH_LEVEL_127    = 1
            constant integer PATCH_LEVEL_128    = 2
            constant integer PATCH_LEVEL_129    = 3
            constant integer PATCH_LEVEL_130    = 4
            constant integer PATCH_LEVEL_1302   = 5
            constant integer PATCH_LEVEL_131    = 6
        endglobals
       
        function GetPatchLevel takes nothing returns integer
            local image img
            local string tmp
           
            // This icon wasn't introduced until 1.28a
            set img = CreateImage("ReplaceableTextures\\WorldEditUI\\Editor-Toolbar-MapValidation.blp", 64, 64, 0, 0,0,0,64,64,0, 1)
            if (GetHandleId(img) == -1) then
                return PATCH_LEVEL_127 // 1.27b or lower
            endif
            call DestroyImage(img)

            // The array size limit was increased in 1.29, so if it's the same
            // then we are on 1.28.
            if (JASS_MAX_ARRAY_SIZE <= 8192) then
                return PATCH_LEVEL_128
            endif

            // This string didn't exist until 1.30
            if (GetLocalizedString("DOWNLOADING_MAP") == "DOWNLOADING_MAP") then
                return PATCH_LEVEL_129
            endif
           
            // This string changed in 1.30.2.
            set tmp = GetLocalizedString("ERROR_ID_CDKEY_INUSE")
            if (SubString(tmp, StringLength(tmp)-1, StringLength(tmp)) == ")") then // check the last character to presumably support all locales
                return PATCH_LEVEL_130
            endif
           
            set tmp = GetLocalizedString("WINDOW_MODE_WINDOWED")
           
            if (tmp == "WINDOW_MODE_WINDOWED") then
                return PATCH_LEVEL_1302
            endif
           
            return PATCH_LEVEL_131 // or higher
        endfunction
    endlibrary
     
     
     

    Attached Files:

    Last edited: May 29, 2019
  2. leandrotp

    leandrotp

    Joined:
    Jul 30, 2012
    Messages:
    153
    Resources:
    1
    Tutorials:
    1
    Resources:
    1
    This is really awesome! After memhack was patched, I thought it would be good if I could somehow detect the current patch, to see if the exploit was available or not, but the problem was, how could I try to detect anything without memory access at all? And you have just accomplished that. It's amazing!

    In theory, this could be used to develop a "compatibility layer" for new generation WC3 maps. I mean, maps that are making use of 1.29+ natives, could have some sort of "abstraction layer" the reimplements the new natives using memhack, and then we could use this snippet to detect the game version at runtime, and dynamically choose to use either the natives or their memhack implementation, depending on what is available for the running patch.

    If only I had a bit of time in my life to begin developing this...
     
  3. TriggerHappy

    TriggerHappy

    Code Moderator

    Joined:
    Jun 23, 2007
    Messages:
    3,633
    Resources:
    22
    Spells:
    11
    Tutorials:
    2
    JASS:
    9
    Resources:
    22
    Yeah that should work. Memory hacks can be loaded from a separate .ai file so it won't cause the JASS parser to throw any errors on the latest patch. There might be a problem getting the map to compile on the older patch without the new natives, but there might be a way.
     
  4. fenix140

    fenix140

    Joined:
    Sep 6, 2010
    Messages:
    46
    Resources:
    0
    Resources:
    0
    I thought I would wait for a version that has at least one editor for the UI of Warcraft 3 (by Blizzard), but I do not think the wait is needed anymore, thanks TriggerHappy. + (Up)
     
  5. MyPad

    MyPad

    Spell Reviewer

    Joined:
    May 9, 2014
    Messages:
    1,259
    Resources:
    6
    Models:
    1
    Icons:
    1
    Spells:
    3
    JASS:
    1
    Resources:
    6
    That's going to be tricky to implement, with the relatively small amount of background information on .ai files.
     
  6. TriggerHappy

    TriggerHappy

    Code Moderator

    Joined:
    Jun 23, 2007
    Messages:
    3,633
    Resources:
    22
    Spells:
    11
    Tutorials:
    2
    JASS:
    9
    Resources:
    22
    AI files are simply JASS scripts. They are pretty straight forward. The main function is executed and the script dies when it is finished. I attached a demo map which will load the memory hacks if a lower patch is detected. You will see a message box appear on supported memory hack versions (1.24b, 1.24e, 1.26a, 1.27a, 1.27b, 1.28a, and 1.28c). Otherwise the game will run normally. The tricky part is being able to use memory hack functions from outside of the AI script while still keeping the map compatible on the latest patch. I think it should be doable though.
     

    Attached Files:

  7. Quilnez

    Quilnez

    Joined:
    Oct 12, 2011
    Messages:
    3,235
    Resources:
    37
    Icons:
    2
    Tools:
    1
    Maps:
    7
    Spells:
    21
    Tutorials:
    2
    JASS:
    4
    Resources:
    37
    This is very clever!

    I think the whole thing in
    GetPatchLevel
    should run on map init and save the detected level in a variable. Then GetPatchLevel will return that variable or just make the variable public. It's very minor thou.
     
  8. PurgeandFire

    PurgeandFire

    Code Moderator

    Joined:
    Nov 11, 2006
    Messages:
    7,427
    Resources:
    18
    Icons:
    1
    Spells:
    4
    Tutorials:
    9
    JASS:
    4
    Resources:
    18
    lol this is so clever. Awesome stuff!
     
  9. WaterKnight

    WaterKnight

    Joined:
    Aug 18, 2009
    Messages:
    4,021
    Resources:
    5
    Maps:
    1
    Tutorials:
    4
    Resources:
    5
    Unlikely but all those things can be changed by the map and the JASS_MAX_ARRAY_SIZE adds a dependency on blizzard.j. Is there a more elegant way?
     
  10. TriggerHappy

    TriggerHappy

    Code Moderator

    Joined:
    Jun 23, 2007
    Messages:
    3,633
    Resources:
    22
    Spells:
    11
    Tutorials:
    2
    JASS:
    9
    Resources:
    22
    Might be able to be done with
    GetLocalizedString
    . I will have to check.
     
  11. Almia

    Almia

    Joined:
    Apr 24, 2012
    Messages:
    4,861
    Resources:
    35
    Spells:
    30
    Tutorials:
    4
    JASS:
    1
    Resources:
    35
    May I ask, why not memoize the function? Evaluate on initialization, store the result then return the stored current patch version everytime the function is called.
     
  12. IcemanBo

    IcemanBo

    Joined:
    Sep 6, 2013
    Messages:
    6,112
    Resources:
    22
    Maps:
    3
    Spells:
    11
    Template:
    1
    Tutorials:
    4
    JASS:
    3
    Resources:
    22
    I guess it's support for future live-patching during midgame. But no, it's anyways just a concept to share, which is cool and fine I guess.
     
  13. WaterKnight

    WaterKnight

    Joined:
    Aug 18, 2009
    Messages:
    4,021
    Resources:
    5
    Maps:
    1
    Tutorials:
    4
    Resources:
    5
    Live-patching during game? How is Warcraft III supposed to support that?
     
  14. Pyrogasm

    Pyrogasm

    Joined:
    Feb 27, 2007
    Messages:
    2,550
    Resources:
    0
    Resources:
    0
    Why is a dependency on one of the two core .j files inelegant...? Every map has a Blizzard.j in it.
     
  15. WaterKnight

    WaterKnight

    Joined:
    Aug 18, 2009
    Messages:
    4,021
    Resources:
    5
    Maps:
    1
    Tutorials:
    4
    Resources:
    5
    With my jassAid tool for example that was not the case. All the functions of the blizzard.j were merged to the map script in order to transform it as a whole. Then the Scripts\blizzard.j path was overwritten with an empty file to not clash. Then again, the
    JASS_MAX_ARRAY_SIZE
    is in the Scripts\common.j, my mistake. I call it inelegant because it's not ensured and semantically off. Also, for instance, this assumes that every version before 1.29 had
    JASS_MAX_ARRAY_SIZE <= 8192
    , it's a blacklist approach.
     
  16. Tasyen

    Tasyen

    Joined:
    Jul 18, 2010
    Messages:
    1,187
    Resources:
    16
    Tools:
    2
    Maps:
    2
    Spells:
    7
    Tutorials:
    4
    JASS:
    1
    Resources:
    16
    Why would one do that?
     
  17. WaterKnight

    WaterKnight

    Joined:
    Aug 18, 2009
    Messages:
    4,021
    Resources:
    5
    Maps:
    1
    Tutorials:
    4
    Resources:
    5
    Do what? The jassAid tool injected debugging and safety functionality. The functions were sorted according to the call graph since jass can only call upwards and some of the call sites were automatically converted to TriggerEvaluate or the like. Log messages were inserted, some potentially crashing natives like
    Player
    were shielded. I wanted to detect thread breaks and have stack traces, so there was a need to know what's happening in the blizzard.j as well. You can do all kinds of optimizations like reducing identifiers or obfuscating them for map protection, making sure the local object variables are nullified, kicking unused or useless functions... Basically, any things a compiler would do.

    Of course, you could have kept the blizzard.j and just get rid of the calls but it seemed more clean to me and I did not necessarily have to rename stuff/pay attention to name clashes. Also, I thought maybe the game not having to load the blizzard.j could be even more performant, you save global variables, etc.
     
    Last edited: Nov 5, 2018
  18. Tasyen

    Tasyen

    Joined:
    Jul 18, 2010
    Messages:
    1,187
    Resources:
    16
    Tools:
    2
    Maps:
    2
    Spells:
    7
    Tutorials:
    4
    JASS:
    1
    Resources:
    16
    Replacing the blizzard.j in a map sounds a bit questionable to me. Blizzard.j is a useful file to add custom code to the game for any map played, even campaigns.

    Wana play campaign with consumable stacking, add it to blizzard.j and there you are.
     
  19. WaterKnight

    WaterKnight

    Joined:
    Aug 18, 2009
    Messages:
    4,021
    Resources:
    5
    Maps:
    1
    Tutorials:
    4
    Resources:
    5
    You mean you want to replace it in game files? For local use maybe, since you would desync it from other game instances. Yet this means you would play the game/maps not the way they were designed and this can easily introduce bugs. Also, keep in mind that jass has no concept of scope. When the maps were written, they paid respect to the blizzard.j. Agnostically inserting new global variables/functions might clash with declarations in the map script.
     
  20. TriggerHappy

    TriggerHappy

    Code Moderator

    Joined:
    Jun 23, 2007
    Messages:
    3,633
    Resources:
    22
    Spells:
    11
    Tutorials:
    2
    JASS:
    9
    Resources:
    22
    I updated the main post with detection for 1.30.2. I also am not creating an image to detect 1.29.2 anymore. I haven't tested this method on 1.30.0 though (I don't have it), but I assume it should work correctly there too. If it returns 129 on 1.30.0 then I will revert the old image detection method and add a 131 constant.

    It's not going to be an issue most of the time. In which case would a patch before 1.29 not have constant set to 8192? I suppose someone could overwrite their common.j in their MPQ/CASC and change the value to try and trick the system, but why would someone do that? Chances are it would just lead to a desync in multiplayer. Not to mention if someone is hacking the game on an older patch and increasing the array limit then they can implement their own accurate patch detection.

    If you really care there are a couple ways to avoid using the constant.
    1. Code (vJASS):

      local integer array arr
      set arr[8193] = 1
      if (arr[8193] == 0) then // might crash thread in older patches? idk
           return PATCH_LEVEL_128
      endif
       
    2. Code (vJASS):
      // This string changed slightly in 1.29 (quotes added around "Run as administrator")
      set tmp = GetLocalizedString("USER_DATA_MIGRATION_FAILED_ADMIN_BODY")
      if (SubString(tmp, 19, 20) == "R") then // probably only works for English clients.
          return PATCH_LEVEL_128
      endif
    The only other way I can see a user abusing the system would be be creating an invalid image for "Editor-Toolbar-MapValidation.blp" which would return PATCH_LEVEL_127 regardless of which patch you were on. Again, it would likely just cause a desync depending on what the map maker is doing with the data. I never really planned on anyone using this in their maps anyway. It's kind of just proof of concept and at most I figured to be used for debugging.

    It's in The Lab because I am just dumping the idea here and someone can do what they want with it.