• 🏆 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!

Make a Map Work for Patch 1.24

Patch 1.24 Compatibility

Table of Contents:


Introduction:

Once patch 1.24 was introduced, many maps have died and no longer work for patch 1.24+. Those maps used a particular bug known as the "return bug", which allowed you to typecast, or change the entity of one data type to another, using a simple method.

Why was it fixed?

Basically, people were toying around with WE a lot. Enough to use I2C and C2I by utilizing the return bug to allow bytecode to be executed in JASS. Then hackers would exploit this "feature" to execute bytecode in JASS to open the command prompt and implement a virus. Luckily though, bytecode shouldn't work anymore since wc3 apparently doesn't let arrays be fired as code anymore.

Typecasting - The Return Bug

Okay, well I talked a lot about this mysterious "Return Bug". What is it? It was a bug that functioned prior to patch 1.24. The key function that made the return bug useful was this:
JASS:
function H2I takes handle h returns integer
    return h
    return 0
endfunction

This famous bug was first exploited by SuperIKI followed by Peppar, and then was used in general in many systems from then on.

What does it do?

Well, it used to typecast variables. It would allow you to essentially transform the entity of the handle input into an integer. This allowed you to get the internal handle ID of that handle you've input, which is generally useful since it is unique for all but a few handles with different allocation methods. (eg: texttags/ubersplats) Now let me break it down to mention the key parts.

JASS:
function H2I takes handle h returns integer

  • H2I - This is the function's name, and refers to "Handle to Integer", following the same naming convention as the other X2Y functions that blizzard has. (I2S, R2I, etc.)
  • handle h - A handle is a data type which many of the types defined in wc3 will extend. Any type (example: unit, location, group) is a handle except for string, real, integer, boolean, and code. By inputting a handle, you may input anything besides the types listed above, for example:
    JASS:
    globals
        integer test
    endglobals
    function H2I takes handle h returns integer
        return h
        return 0
    endfunction
    function Test takes nothing returns nothing
        set test = H2I(GetTriggerUnit()) //This will compile just fine since "unit" is a handle
        set test = H2I("My String")      //Will NOT compile, strings aren't handles.
    endfunction
  • returns integer - Okay, so far we take in handle h, now to complete the H2I cycle, we must return an integer. (Hence, H2I) Basically, once we define a return, the function will be considered a valid input for the returned type. For example:
    JASS:
    function ReturnString takes unit u returns string
        return GetUnitName(u)+"Whee"
    endfunction
    function Test takes nothing returns nothing
        local string s = ReturnString(GetTriggerUnit()) 
        //This will compile just fine, since the "ReturnString" function returns a string-type.
        local unit u   = ReturnString(GetTriggerUnit())
        //This will NOT compile, it returns a string, not a unit-type.
    endfunction

Now:
JASS:
    return h
    return 0

return is the GUI equivalent to Skip Remaining Actions. Once you define a type after the return, that means the function also must return that type defined.
JASS:
function ReturnGood takes nothing returns unit
    return GetTriggerUnit() //Compiles fine, since you state to return a unit and do just that.
endfunction
function ReturnBad takes nothing returns nothing
    return GetTriggerUnit() //error! You declare that you "return nothing" but you return a unit.
endfunction

The thing peculiar about return prior to patch 1.24 though, was that the compiler checked only the last return defined. So as long as you return the correct type at the end, the previous returns will work as well. Example:
JASS:
function CompiledFinePriorToPatch takes nothing returns string
    set bj_lastCreatedUnit = CreateUnit(Player(15),'hfoo',0,0,0)
    if GetUnitUserData(bj_lastCreatedUnit)==50 then
        return bj_lastCreatedUnit //we return a unit instead of a string???!
    endif
    return "This compiles!" //now we return a string.
endfunction

The compiler used to check only return "This compiles!" to see if it was returning the correct data type. Obviously, this is just an example that does absolutely nothing, but it just shows how it could be abused.

Now, let's go back to the return h/return 0. If we were to define only return h, it would show a compile error since it is supposed to return an integer.
JASS:
function H2I takes handle h returns integer
    return h //error! You returned the wrong type!
endfunction

But when we add the final return, it will become "okay" for the compiler, and they'll think it is just fine.
JASS:
function H2I takes handle h returns integer
    return h 
    return 0 //The compiler ignores "return h", and just focuses on this.
//hmm... 0 seems like an integer to me, so it works fine **
//** prior to patch 1.24
endfunction

The cool thing though, is that return 0 is never executed. It skips the remaining actions after the return h, so return 0 is never executed at all.

When doing this, it allowed us to successfully typecast from a handle to an integer. Now, if you were to return an integer of a handle, what would it return? Well, the way normally allocated handles work is that they have specific handle ids. When a new handle is declared, it will be given a new unique handle ID. The way they are assigned is by the showing that it is the nth handle declared. They go from order, from 0x100000 (or 1048576), and then it goes +1 for each new handle declared. So say you have a handle, you can check how many handles were declared before it by using this function:
JASS:
function GetHandleIdPosition takes handle h returns integer
    return H2I(h)-0x100000 //or 1048576
//No longer works as of patch 1.24
//Thus, you'd really use "return GetHandleId(h)-0x100000", the new native.
endfunction

Now, since the handles (besides texttags and ubersplats etc.) had unique ID's, this made it perfect for spells. And thus began the era of:
Local Handle Vars

Local Handle Vars was the most widely used system in JASS of that time, and relied solely on gamecache for timer attachment purposes. It uses H2I, but that wasn't the only part of the return bug it used. Let's first look at the function for attaching a handle:
JASS:
function SetHandleHandle takes handle subject, string name, handle value returns nothing
    if value==null then
        call FlushStoredInteger(LocalVars(),I2S(H2I(subject)),name)
    else
        call StoreInteger(LocalVars(), I2S(H2I(subject)), name, H2I(value))
    endif
endfunction

Ok, this stores an integer into LocalVars() [a defined gamecache]. Gamecache are much like hashtables, where they took a parent string name as a "category" and then the "name". It is analogous to a computer. We store files into a unique folder (represents the category) and store the child file (represents "name"). Then we assign the value. Now, since we couldn't manually assign a handle, we would assign the ID of the handle instead. It was a really neat way to attach objects, but you must think, "How do we get the data back"? Well, we use the same method pretty much:
JASS:
function GetHandleHandle takes handle subject, string name returns handle
    return GetStoredInteger(LocalVars(), I2S(H2I(subject)), name)
    return null
endfunction

Now, it expects us to return a "handle". Since all handles can have a null value, we can return "null" at the end and it will suffice. Now, we return the integer, and it expects a handle to be returned. Well, there is an H2I, why not an I2H? Well, this is essentially the technique for an I2H.
JASS:
function I2H takes integer i returns handle
    return i
    return null
endfunction

That is essentially the return bug wrapped up. So why are maps broken? Well, almost all JASS spells at the time used local handle vars or CSCache, which means that since the return bug no longer works, the functions no longer work. In fact, it will give errors now if you try to use this method, that is the point of this tutorial. To fix it.

Patch 1.24:

Both a killer and a revolutionary patch. In that patch, they removed the return bug, but added a great replacement:
JASS:
native GetHandleId takes handle h returns integer

This is the new H2I function, except it is a native so it is very fast in comparison. We don't have an I2H, but why need that when we have hashtables? Hashtables are the revolutionary feature of patch 1.24, which allows us to store the data types we need without being restricted to integers, strings, reals, and booleans.

However, hashtables also opened a new way to type-cast:
JASS:
globals
    hashtable MyHash = InitHashtable()   
endglobals
function Widget2Unit takes widget w returns unit
    call SaveWidgetHandle(MyHash,0,0,w)
    return LoadUnitHandle(MyHash,0,0)
endfunction

To type cast I2X, you'd use:
JASS:
globals
    hashtable MyHash = InitHashtable()
endglobals
function Integer2Widget takes integer int returns widget
    call SaveFogStateHandle(MyHash,0,0,ConvertFogState(int))
    return LoadWidgetHandle(MyHash,0,0)
endfunction

It uses fogstates and basically exploits hashtables, but this isn't very useful any more now that we have the glorious hashtables to replace the older systems. (Only W2U is useful in specific cases)

But that isn't the thing. We want to know how to fix maps that used the old systems. But with this knowledge (don't worry, it isn't pointless) you'll be able to do that easily.

Fixing an Old Map:

Hooray, we finally get to fix an old map!

Ok, now as we have discussed, there are a few systems that cause this:
  • CSCache/Caster System
  • Local Handle Vars
  • ABCt - (Resolve: Use TimerTicker or a different timer system)
  • TimerTicker - (Use the compatibility version)
  • Old versions of Table (included with CSCache)
  • Cinematic System v1.5 or lower (Resolve: Use v1.5b)
  • HAIL
  • Graphics System
  • Decal
  • T2Ix
  • Unit Get Local (Deprecated Local Handle Vars)
  • Class System
  • Spell Classes System
  • Handle Lists
  • HSAS
  • Special Events/Alternative Spell Templates System

There are more, but that gets a lot of them. All but the ones with a "Resolve" use H2I and don't have an upgrade. (Except for CSCache/Caster System/Table but those use vJASS, so you might not want to use it)

Fixes:

Before I get into fixing these problems manually, I recommend giving this automated-tool a shot:
Handlevars Return Casting Converter
If that doesn't work, try some of the workarounds mentioned below!

Okay, let's get to one major fix that will probably knock off a lot of them.

Replace:
JASS:
function H2I takes handle h returns integer
    return h
    return 0
endfunction
With:
JASS:
function H2I takes handle h returns integer
    return GetHandleId(h)
endfunction

That will fix most of the systems.

The fix to CSCache and Handle Vars is this:
Faux Handle Vars & CSCache

It uses hashtables to store everything instead of gamecache, and just uses GetHandleId() to replace it, and uses the hashtable API for everything else. It allows you to use the same naming convention as the previous local handle vars & CSCache so you don't have to make changes later.

Let's observe some of the functions:
JASS:
    globals
        hashtable ht
    endglobals
    function SetHandleHandle takes agent subject,  string label, agent value returns nothing
        if(value==null) then
            call RemoveSavedHandle( ht, GetHandleId(subject), StringHash(label))
        else
            call SaveAgentHandle( ht, GetHandleId(subject), StringHash(label), value)
        endif
    endfunction
    function GetHandleUnit takes agent subject, string label returns unit
        return LoadUnitHandle( ht, GetHandleId(subject), StringHash(label))
    endfunction

What this does is it saves an agent handle (the ones we use/create, essentially) and then the 2nd function returns a unit that was saved. Basically, they use GetHandleId() instead of H2I(), and they use StringHash() to convert it an integer input for the hashtable functions.

To implement Faux HandleVars & CSCache, copy the respective library, and paste it into the map's header. (The map header is found by opening the trigger editor, and clicking on the map's name listed in the left window) Then it should say "Custom Script Code" and just replace the old handle vars/CScache with the new ones.

Now it is all fine for CSCache and Handle Vars, but some maps might use things like the Class System, or generally systems that use I2X.

Let's look at some functions of the Class System by AIAndy:
JASS:
function Handle2Int takes handle h returns integer
  return h
  return 0
endfunction

function Int2Group takes integer i returns group
  return i
  return null
endfunction

function Int2Unit takes integer i returns unit
  return i
  return null
endfunction

function Int2Location takes integer i returns location
  return i
  return null
endfunction

function Int2Effect takes integer i returns effect
  return i
  return null
endfunction

function Int2Timer takes integer i returns timer
  return i
  return null
endfunction

function Int2Terraindeformation takes integer i returns terraindeformation
  return i
  return null
endfunction

function Int2Destructable takes integer i returns destructable
  return i
  return null
endfunction

function Int2Trigger takes integer i returns trigger
  return i
  return null
endfunction

How do you think we should fix this? Well, if you look above, you'll realize we can now use fogstates along with hashtables for I2X functions. Thus, we'd end up getting this:
JASS:
globals
    hashtable MyHash = InitHashtable()
endglobals

function Handle2Int takes handle h returns integer
    return GetHandleId(h)
endfunction

function Int2Group takes integer i returns group
    call SaveFogStateHandle(MyHash,0,0,ConvertFogState(i))
    return LoadGroupHandle(MyHash,0,0)
endfunction

function Int2Unit takes integer i returns unit
    call SaveFogStateHandle(MyHash,0,0,ConvertFogState(i))
    return LoadUnitHandle(MyHash,0,0)
endfunction

function Int2Location takes integer i returns location
    call SaveFogStateHandle(MyHash,0,0,ConvertFogState(i))
    return LoadLocationHandle(MyHash,0,0)
endfunction

function Int2Effect takes integer i returns effect
    call SaveFogStateHandle(MyHash,0,0,ConvertFogState(i))
    return LoadEffectHandle(MyHash,0,0)
endfunction

function Int2Timer takes integer i returns timer
    call SaveFogStateHandle(MyHash,0,0,ConvertFogState(i))
    return LoadTimerHandle(MyHash,0,0)
endfunction

//function Int2Terraindeformation takes integer i returns terraindeformation
//    call SaveFogStateHandle(MyHash,0,0,ConvertFogState(i))
//    return LoadGroupHandle(MyHash,0,0)
//endfunction
//Lol there is no save/load for terrain deformations, so we are stuck.
//There is another way to typecast, known as the "Return Nothing" bug [discovered by Deaod]
//However, it no longer works.

function Int2Destructable takes integer i returns destructable
    call SaveFogStateHandle(MyHash,0,0,ConvertFogState(i))
    return LoadDestructableHandle(MyHash,0,0)
endfunction

function Int2Trigger takes integer i returns trigger
    call SaveFogStateHandle(MyHash,0,0,ConvertFogState(i))
    return LoadTriggerHandle(MyHash,0,0)
endfunction

Basically, we've succesfully completed it for all but the terrain deformation. Sadly, that one no longer works. There are a handful of types we can't save, but we don't use it that much anyway. The only real reason to save a terraindeformation is to stop it anyway, so you'd need to manually fix that or just generally remove the functions. The only types that support typecasting at the moment are:
JASS:
real
string
integer
boolean
player
widget
destructable
item
unit
ability
timer
trigger
triggercondition
triggeraction
event
force
group
location
rect
boolexpr
sound
effect
unitpool
itempool
quest
questitem
defeatcondition
timerdialog
leaderboard
multiboard
multiboarditem
trackable
dialog
button
texttag
lightning
image
ubersplat
region
fogstate
fogmodifier
agent
hashtable

The rest can't since there are no hashtable functions for them. However, this system can help fix that problem, for most of them (except weathereffect and terraindeformation):
http://www.thehelper.net/forums/showthread.php/152826-Handle

After you have fixed the functions, and your map saves, you may still experience some problems. Perhaps your map will throw an immediate error upon testing the map or something along those lines. Now that the map can be saved, you will need to troubleshoot the problem by disabling triggers until your map loads. Once your map loads, hopefully you will be able to narrow down which trigger caused the problem. In general, it may be harder to fix that trigger opposed to just remaking it, so I recommend that you remake whatever does not function properly.

Conclusion:

Hopefully you may now fix your map. Generally, Faux Handle Vars & CSCache will usually fix a map with ease, but sometimes it isn't enough if they use special systems. Generally, it is just good to know how to fix them, since it will help you to refrain from using the old systems as well.

Credits:
  • SuperIKI and Peppar and others for H2I.
  • Vexorian for Faux Handle Vars and CSCache
  • Jesus4Lyf for some of the malicious bytecode info
  • AIAndy for making a system that can no longer work in 1.24. =P (since the typecasting methods don't work for terraindeformations)
  • kingkingyyk3 for a quick reference to the X2Y and I2X functions.
 
Last edited:
Level 9
Joined
Nov 4, 2007
Messages
931
Very good tutorial, and thorough in its explanation and fixes, I expect if any tutorial is worth the time and effort of an early review by the moderator, this would definitely be it, though it would have been of great use had it been around earlier. A shame some things are impossible to fix now that the return bug is gone, but hopefully this tutorial will allow people to salvage what they can from maps that had died, sadly one of my own maps used the return bug to takes a real and return a location, was devastating to have to abandon that map after the patch, but oh well moving on, great tutorial, good work.
 
Level 2
Joined
Oct 18, 2004
Messages
14
How to Fix the Return Bug!¿

Hello All there, well this is my problem... Like we Know, (About the return bug), Dont leave host Old maps , so i have this hash in one map (and just need the exacly config) to fix it.! mayb some here know about it


JASS:
function GetTrigger takes gamecache cache ,string string1,string string2 returns trigger
return GetStoredInteger(cache,string1,string2)
return null
endfunction

function GetUnit takes gamecache cache ,string string1,string string2 returns unit
return GetStoredInteger(cache,string1,string2)
return null
endfunction


function H2I takes handle h returns integer
return h
return 0
endfunction

function H2S takes handle h returns string
return I2S(H2I(h))
endfunction

function H2T takes handle h returns timer
return h
endfunction

function I2H takes integer i returns handle
return i
return null
endfunction

function I2G takes integer i returns group
return i
return null
endfunction

function ErrorMessage takes player whichplayer,string text returns nothing
local sound s=CreateSoundFromLabel("InterfaceError",false,false,false,10,10)
if(GetLocalPlayer()==whichplayer)then
if(text!="")and(text!=null)then
call ClearTextMessages()
call DisplayTimedTextToPlayer(whichplayer,0.52,-1.00,2.00,"|cffffcc00"+text+"|r")
endif
call StartSound(s)
endif
call KillSoundWhenDone(s)
set s=null
endfunction

and here the Photo http://img837.imageshack.us/img837/772/jassp.jpg

Thanks so much!

( Another question.. this map have that jass Text. but Wehn i open anothers maps Cant Found this chain Jass text So the question is, Where need Search it?---

( mean this Chain function H2I takes handle h returns integer
return h
return 0
endfunction ) Thanks so much since now!
 
Last edited:
Level 2
Joined
Feb 10, 2014
Messages
9
Fixing an Old Map

Can't do it myself since I'm on a Mac running some software that disables harddrive partitioning so no bandcamp or anything.....So could someone do me a huge favor and fix this map for me?

Also, I believe I tried some of the methods listen in this post on the map before, and they were ineffective. :(
 
Can't do it myself since I'm on a Mac running some software that disables harddrive partitioning so no bandcamp or anything.....So could someone do me a huge favor and fix this map for me?

Also, I believe I tried some of the methods listen in this post on the map before, and they were ineffective. :(

Which map? You should attach it to your post, or put it on pastebin, or post a URL link to it.

Also, the conversion isn't perfectly 1-1. There are some functions that aren't able to be created anymore (e.g. SetHandleHandle); this will mostly help you get rid of syntax errors so the map can be opened in wc3 (and usually to recode it). Some cases are worse than others (generally, the mostly-GUI ones are easier to fix because they rarely used the bug, but the JASS maps take a little more work). It varies.
 
Level 2
Joined
Feb 10, 2014
Messages
9
Sorry

Guess when I tried to attach the map it didn't go through. I apologize.

Thanks for responding so quickly by the way. :D I'll try to reattach it in this post.
 

Attachments

  • WheelOfTime.w3x
    2.7 MB · Views: 3,289
I can't edit it at the moment because I too am on a Mac. I do see the issue though. The user used WEU functions, which in turn used its own set of typecasting and a similar form of local handle vars. There are about 23 instances of it and a couple of functions. It shouldn't take that long to fix. Hopefully it'll still work.

There might be a few other oddities (sometimes functions are designed poorly and might not return anything), I'll try to figure it out this weekend when I get back to my PC. :) If you could send me a visitor message or PM as a reminder on Friday, that'd be very helpful (I have a tendency to forget).
 
Level 2
Joined
Feb 10, 2014
Messages
9
Thanks!

Yeah I can do that no problem. Thanks for your help!

By the way, I think there should be a section for recruiting help for certain things. Like fixing old maps, testing new ones, other things..... Makes sense to me especially when you already have a rep system in place enticing people TO help.....
 
Level 1
Joined
Jul 4, 2016
Messages
2
I'm not a english speaker, for me is too hard fix my map. Can anyone with a good soul fix it to me? I love this map, but I can not play it.

Please. Please. Please.

Thank you. Thank you. Thank you.

We have to use worldedit to fix?
 

Attachments

  • Open VampBR 0.8b.w3x
    2 MB · Views: 2,797
Last edited:
Top