• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.

[Snippet] [cJass] Anti-Leak Library

Level 3
Joined
Jan 31, 2012
Messages
42
This library automatically removes the most part of memory leaks, including locs, rects, effects, groups and forces. Works even in GUI-triggers.

All what You need to use this is to copy the source code to Your map.

If You do not need to destroy some object, You can disable the system writing "ALL_Remove = false" before creating "leak" and "ALL_Remove = true" after that.

It catches all (I hope) calls of functions return objects of types that need destroying and removes leaks by timer of 0. Also, the system replaces leaking BJ's (like GetUnitsInRectMatching(...)) with their clear analogs.
JASS:
library ALL {
    include "cj_types_priv.j"
    native int GetPlayerUnitTypeCount(player p, int unitid)
    //Adapt to standard AntiBJ base
    setdef GetPlayerStartLocationLoc(p) = ALL_RemoveLocation(GetStartLocation##Loc(GetPlayerStartLocation(p)))
    setdef GetUnitsInRangeOfLocAll(r, l) = ALL_DestroyGroup(ALL_GetUnitsInRangeOfLocMatching(r, l, null))
    setdef GetUnitsInRectAll(r) = ALL_DestroyGroup(ALL_GetUnitsInRectMatching(r, null))
    setdef GetUnitsOfPlayerAll(p) = ALL_DestroyGroup(ALL_GetUnitsOfPlayerMatching(p, null))
    setdef LoadLocationHandleBJ(childKey, parentKey, table) = ALL_RemoveLocation(LoadLocation##Handle(table, parentKey, childKey))
    setdef LoadRectHandleBJ(childKey, parentKey, table) = ALL_RemoveRect(LoadRect##Handle(table, parentKey, childKey))
    setdef LoadGroupHandleBJ(childKey, parentKey, table) = ALL_DestroyGroup(LoadGroup##Handle(table, parentKey, childKey))
    setdef LoadForceHandleBJ(childKey, parentKey, table) = ALL_DestroyForce(LoadForce##Handle(table, parentKey, childKey))
    setdef <call AddSpecialEffectLocBJ>(l, s) = call ALL_AddSpecialEffectLocBJ(l, s)
    setdef <call AddSpecialEffectTargetUnitBJ>(a, w, s) = call ALL_AddSpecialEffectTargetUnitBJ(a, w, s)
    //Rects created in CreateRegions will not pass through RemoveRect
    setdef Rect(minX, minY, maxX, maxY) = ALL_RemoveRect(Re##ct(minX, minY, maxX, maxY))
    define {
        <InitCustomTriggers()> = { InitCustom##Triggers(); ALL_Remove = true }
        GetPlayerStartLocationLoc(p) = GetStartLocationLoc(GetPlayerStartLocation(p))
        GetUnitsInRangeOfLocAll(r, l) = GetUnitsInRangeOfLocMatching(r, l, null)
        GetUnitsInRectAll(r) = GetUnitsInRectMatching(r, null)
        GetUnitsOfPlayerAll(p) = GetUnitsOfPlayerMatching(p, null)
        LoadLocationHandleBJ(a, b, h) = LoadLocationHandle(h, b, a)
        LoadRectHandleBJ(a, b, h) = LoadRectHandle(h, b, a)
        LoadGroupHandleBJ(a, b, h) = LoadGroupHandle(h, b, a)
        LoadForceHandleBJ(a, b, h) = LoadForceHandle(h, b, a)
        <call AddSpecialEffectLocBJ>(l, s) = bj_lastCreatedEffect = AddSpecialEffectLoc(s, l)
        <call AddSpecialEffectTargetUnitBJ>(a, w, s) = bj_lastCreatedEffect = AddSpecialEffectTarget(a, w, s)

        <GetCameraEyePositionLoc()> = ALL_RemoveLocation(GetCameraEyePosition##Loc())
        <GetCameraTargetPositionLoc()> = ALL_RemoveLocation(GetCameraTargetPosition##Loc())
        GetDestructableLoc(d) = ALL_RemoveLocation(ALL_GetWidgetLoc(d))
        GetItemLoc(i) = ALL_RemoveLocation(ALL_GetWidgetLoc(i))
        <GetOrderPointLoc()> = ALL_RemoveLocation(GetOrderPoint##Loc())
        GetRandomLocInRect(r) = ALL_RemoveLocation(GetRandom##LocInRect(r))
        GetRectCenter(r) = ALL_RemoveLocation(GetRect##Center(r))
        <GetSpellTargetLoc()> = ALL_RemoveLocation(GetSpellTarget##Loc())
        GetStartLocationLoc(i) = ALL_RemoveLocation(GetStartLocation##Loc(i))
        GetUnitLoc(u) = ALL_RemoveLocation(GetUnit##Loc(u))
        Location(x, y) = ALL_RemoveLocation(Loc##ation(x, y))
        MeleeGetLocWithinRect(l, r) = ALL_RemoveLocation(ALL_MeleeGetLocWithinRect(l, r))
        MeleeGetProjectedLoc(l, targ, dist, a) = ALL_RemoveLocation(MeleeGet##ProjectedLoc(l, targ, dist, a))
        OffsetLocation(l, x, y) = ALL_RemoveLocation(OffsetLoc##ation(l, x, y))
        PolarProjectionBJ(l, r, a) = ALL_RemoveLocation(ALL_PolarProjectionBJ(l, r, a))
        WaygateGetDestinationLocBJ(u) = ALL_RemoveLocation(Waygate##GetDestinationLocBJ(u))
        GetRectFromCircleBJ(l, r) = ALL_RemoveRect(ALL_RectFromCenterSizeBJ_GetRectFromCircleBJ(l, r, .0, false))
        OffsetRectBJ(r, x, y) = ALL_RemoveRect(OffsetRect##BJ(r, x, y))
        Rect(minX, minY, maxX, maxY) = Re##ct(minX, minY, maxX, maxY)
        RectFromCenterSizeBJ(l, width, heigth) = ALL_RemoveRect(ALL_RectFromCenterSizeBJ_GetRectFromCircleBJ(l, width, heigth, true))
        RectFromLoc(min, max) = ALL_RemoveRect(RectFrom##Loc(min, max))
        CountLivingPlayerUnitsOfTypeId(id, p) = GetPlayerUnitTypeCount(p, id)
        GetUnitsInRangeOfLocMatching(r, l, b) = ALL_DestroyGroup(ALL_GetUnitsInRangeOfLocMatching(r, l, b))
        GetUnitsInRectMatching(r, b) = ALL_DestroyGroup(ALL_GetUnitsInRectMatching(r, b))
        GetUnitsInRectOfPlayer(r, p) = ALL_DestroyGroup(ALL_GetUnitsInRectOfPlayer(r, p))
        GetUnitsOfPlayerAndTypeId(p, id) = ALL_DestroyGroup(ALL_GetUnitsOfPlayerAndTypeId(p, id))
        GetUnitsOfPlayerMatching(p, b) = ALL_DestroyGroup(ALL_GetUnitsOfPlayerMatching(p, b))
        GetUnitsOfTypeIdAll(id) = ALL_DestroyGroup(ALL_GetUnitsOfTypeIdAll(id))
        GetUnitsSelectedAll(p) = ALL_DestroyGroup(ALL_GetUnitsSelectedAll(p))
        GetForceOfPlayer(p) = ALL_GetForceOfPlayer(p)
        GetPlayersAllies(p) = ALL_DestroyForce(ALL_GetPlayersAllies(p))
        GetPlayersByMapControl(mc) = ALL_DestroyForce(ALL_GetPlayersByMapControl(mc))
        GetPlayersEnemies(p) = ALL_DestroyForce(ALL_GetPlayersEnemies(p))
        GetPlayersMatching(b) = ALL_DestroyForce(ALL_GetPlayersMatching(b))
        <call ALL_AddSpecialEffectLocBJ>(l, s) = {
            if ALL_Remove {
                bj_lastCreatedEffect = null
                DestroyEffect(AddSpecialEffectLoc(s, l))
            } else {
                bj_lastCreatedEffect = AddSpecialEffectLoc(s, l)
            }
        }
        <call ALL_AddSpecialEffectTargetUnitBJ>(a, w, s) = {
            if ALL_Remove {
                bj_lastCreatedEffect = null
                DestroyEffect(AddSpecialEffectTarget(s, w, a))
            } else {
                bj_lastCreatedEffect = AddSpecialEffectTarget(s, w, a)
            }
        }
    }

    enum (ALL_Leak) {
        private LocationLeak
        private RectLeak
        private GroupLeak
        private ForceLeak
    }

    private constant timer Timer = CreateTimer()
    private int MaxVar = -1
    private int array LeakNum
    private location array LocationVar
    private rect array RectVar
    private group array GroupVar
    private force array ForceVar
    private group Group
    private force Force
    public bool Remove = false

    private void RemoveLeak() {
        do {
            if MaxVar:LeakNum < GroupLeak {
                if MaxVar:LeakNum < RectLeak {
                    RemoveLocation(MaxVar:LocationVar)
                } else {
                    RemoveRect(MaxVar:RectVar)
                }
            } elseif MaxVar:LeakNum < ForceLeak {
                DestroyGroup(MaxVar:GroupVar)
            } else {
                DestroyForce(MaxVar:ForceVar)
            }
        } whilenot --MaxVar < 0
    }

    public rect RemoveRect(rect leak) {
        if Remove {
            MaxVar++
            MaxVar:RectVar = leak
            MaxVar:LeakNum = RectLeak
            if MaxVar == 0 { TimerStart(Timer, .0, false, function RemoveLeak) }
        }
        return leak
    }
    public rect RectFromCenterSizeBJ_GetRectFromCircleBJ(location l, real width, real height, bool b) {
        real x = GetLocationX(l), y = GetLocationY(l)
        if b {
            width = width * .5
            height = height * .5
            return Re##ct(x - width, y - height, x + width, y + height)
        }
        return Re##ct(x - width, y - width, x + width, y + width)
    }

    public location RemoveLocation(location leak) {
        if Remove {
            MaxVar++
            MaxVar:LocationVar = leak
            MaxVar:LeakNum = LocationLeak
            if MaxVar == 0 { TimerStart(Timer, .0, false, function RemoveLeak) }
        }
        return leak
    }
    public location GetWidgetLoc(widget w) {
        return Loc##ation(GetWidgetX(w), GetWidgetY(w))
    }
    public location PolarProjection##BJ(location l, real r, real a) {
        a = a * .017
        return Loc##ation(GetLocationX(l) + Cos(a) * r, GetLocationY(l) + Sin(a) * r)
    }
    public location MeleeGetLoc##WithinRect(location l, rect r) {
        real minX = GetRectMinX(r), maxX = GetRectMaxX(r), \
             minY = GetRectMinY(r), maxY = GetRectMaxY(r), \
             x = GetLocationX(l),   y = GetLocationY(l)
        if x < minX {
            x = minX
        } elseif x > maxX {
            x = maxX
        }
        if y < minY {
            return Loc##ation(x, minY)
        } elseif y > maxY {
            return Loc##ation(x, maxY)
        }
        return Loc##ation(x, y)
    }

    public group DestroyGroup(group leak) {
        if Remove {
            MaxVar++
            MaxVar:GroupVar = leak
            MaxVar:LeakNum = GroupLeak
            if MaxVar == 0 { TimerStart(Timer, .0, false, function RemoveLeak) }
        }
        return leak
    }
    public group GetUnitsInRange##OfLocMatching(real r, location l, boolexpr b) {
        Group = CreateGroup()
        GroupEnumUnitsInRangeOfLoc(Group, l, r, b)
        DestroyBoolExpr(b)
        return Group
    }
    public group GetUnitsInRect##Matching(rect r, boolexpr b) {
        Group = CreateGroup()
        GroupEnumUnitsInRect(Group, r, b)
        DestroyBoolExpr(b)
        return Group
    }
    public group GetUnitsInRect##OfPlayer(rect r, player p) {
        Group = CreateGroup()
        bj_groupEnumOwningPlayer = p
        GroupEnumUnitsInRect(Group, r, filterGetUnitsInRectOfPlayer)
        return Group
    }
    public group GetUnitsOfPlayer##Matching(player p, boolexpr b) {
        Group = CreateGroup()
        GroupEnumUnitsOfPlayer(Group, p, b)
        DestroyBoolExpr(b)
        return Group
    }
    public group GetUnitsOfPlayer##AndTypeId(player p, int id) {
        Group = CreateGroup()
        bj_groupEnumTypeId = id
        GroupEnumUnitsOfPlayer(Group, p, filterGetUnitsOfPlayerAndTypeId)
        return Group
    }
    public group GetUnitsOfTypeId##All(int id) {
        group g = CreateGroup()
        int i = 0
        bj_groupAddGroupDest = CreateGroup()
        bj_groupEnumTypeId = id
        do {
            GroupEnumUnitsOfPlayer(g, Player(i), filterGetUnitsOfTypeIdAll)
            ForGroup(g, function GroupAddGroupEnum)
            exitwhen i == 15
            i++
        }
        DestroyGroup(g)
        g = null
        return bj_groupAddGroupDest
    }
    public group GetUnits##SelectedAll(player p) {
        Group = CreateGroup()
        SyncSelections()
        GroupEnumUnitsSelected(Group, p, null)
        return Group
    }

    public force DestroyForce(force leak) {
        if Remove {
            MaxVar++
            MaxVar:ForceVar = leak
            MaxVar:LeakNum = ForceLeak
            if MaxVar == 0 { TimerStart(Timer, .0, false, function RemoveLeak) }
        }
        return leak
    }
    public force GetForce##OfPlayer(player p) {
        Force = CreateForce()
        ForceAddPlayer(Force, p)
        return Force
    }
    public force GetPlay##ersAllies(player p) {
        Force = CreateForce()
        ForceEnumAllies(Force, p, null)
        return Force
    }
    public force GetPlayersByMap##Control(mapcontrol mc) {
        player p = Player(0)
        int i = 0
        Force = CreateForce()
        do {
            if GetPlayerController(p) == mc {
                ForceAddPlayer(Force, p)
            }
            exitwhen i == 15
            p = Player(++i)
        }
        p = null
        return Force
    }
    public force GetPlay##ersEnemies(player p) {
        Force = CreateForce()
        ForceEnumEnemies(Force, p, null)
        return Force
    }
    public force GetPlay##ersMatching(boolexpr b) {
        Force = CreateForce()
        ForceEnumPlayers(Force, b)
        DestroyBoolExpr(b)
        return Force
    }
}
 
Last edited:
It is not working 100%, because it uses cJass and cJass is not fully compatible with vJass (it doesn't compile the NewTable library for example).

cJass is not an acceptable language on TheHiveworkshop, you can have a resource in cJass but in order for it to get accepted it needs to have a vJass/Zinc/Vanilla JASS equivalent.

http://www.hiveworkshop.com/forums/jass-resources-412/system-automatic-memory-leak-destroyer-142235/

Although Garbage Collection is not really practical in the user-side of wc3 because Blizzard does not allow us to detect when a handle is no longer referenced. The idea is good and I approve of it, but the concept is impractical. The user would have to do something like what Automatic Memory Leak Destroyer did, by using a "protect handle" boolean---might as well just get familiar with the custom scripts at that point.
 

LeP

LeP

Level 13
Joined
Feb 13, 2008
Messages
543
It's much more efficient to clear the leaks yourselves.

It is much more cumbersome to clear the memory by yourself.

_______
But yeah, jass isn't really a language where you can do some garbage collection. There were some experiments though.

But, i had this idea for a long time, if someone ever creates a new language.
What about c-style memory management but in reverse: You mark your local variables which are destroyed when they leave there scope. Smth. like
JASS:
struct vector
// whatever
endstruct

function some_vetor_math takes unit u returns nothing
   local !vector pos = vector(GetUnitX(u), GetUnitY(u))
   local !vector speed = u:speed.scaled(FPS)
   pos += speed
   call SetUnitX(u, pos.x)
   call SetUnitY(u, pos.y)
endfunction

which gets translated to
JASS:
struct vector
// whatever
endstruct

function some_vetor_math takes unit u returns nothing
   local vector pos = vector(GetUnitX(u), GetUnitY(u))
   local vector speed = u:speed.scaled(FPS)
   pos += speed
   call SetUnitX(u, pos.x)
   call SetUnitY(u, pos.y)
   call pos.destroy()
   call speed.destroy()
endfunction

Now the cool thing about this is, that we can use special allocate/deallocate methods which would be faster than the normal allocate/deallocate (being a stack, only inc and dec, etc.) ofc then called smth like stack_allocate but you get it. And you could probably expand it so that it not only works for variables but also for every kind of expression.


Heh, i should probably start a thread to write about all the cool ideas i've got regarding new languages for wc3 :alol:.
 
Jass doesn't even need garbage collection :p
Once you know how to use Jass correctly, RemoveLocation and DestroyGroup become the most useless natives ever ;D

Locations can be replaced with coordinates and the only real need to have them is when you need to get the Z of a certain point. But for that, you only need 1 static location ;p

As for groups, all you need is global groups for enumeration and storage.
You never really have to destroy a group.
The reason we use DestroyGroup in GUI is because it creates a dynamic group and never destroys it :p

edit

When talking about speed for allocate/deallocate functions, I say that writing your own allocaters is always more efficient.
It cuts down TriggerEvaluations and function calls and sometimes, you might want to use a stack for allocation and deallocation :p
 

LeP

LeP

Level 13
Joined
Feb 13, 2008
Messages
543
You did not quite get my point.
Jass doesn't even need garbage collection :p

Not really, no. And as i said, it isn't really feasible either. But jass still has some repetitive tasks perfect suited to get automated by a compiler. I'm looking at you, null-setting.

Once you know how to use Jass correctly, RemoveLocation and DestroyGroup become the most useless natives ever ;D

Locations can be replaced with coordinates and the only real need to have them is when you need to get the Z of a certain point. But for that, you only need 1 static location ;p

As for groups, all you need is global groups for enumeration and storage.
You never really have to destroy a group.
The reason we use DestroyGroup in GUI is because it creates a dynamic group and never destroys it :p

My post wasn't about natives, though you could use it for them too. It was about a concept to get rid of that local-create/use/.destroy pattern. Or specially annotating local variables so the compiler can do some cool things.
It may look a bit useless but it would allow us to do some crazy things

For example i think that working with closures would become possible if we knew about the lifespan of variables.

One step nearer to a high level language.

When talking about speed for allocate/deallocate functions, I say that writing your own allocaters is always more efficient.
It cuts down TriggerEvaluations and function calls and sometimes, you might want to use a stack for allocation and deallocation :p

It wasn't about efficiency. It just would be a cool side-effect, which happens to be faster, of that stack-variables.

e: but damn, i really have to think it through. Maybe it is useless after all.
 
It wasn't about efficiency. It just would be a cool side-effect, which happens to be faster, of that stack-variables.

I was agreeing with you in that statement :x

My post wasn't about natives, though you could use it for them too. It was about a concept to get rid of that local-create/use/.destroy pattern. Or specially annotating local variables so the compiler can do some cool things.
It may look a bit useless but it would allow us to do some crazy things

I am aware of that. I was just blabbing. bla bla bla

Not really, no. And as i said, it isn't really feasible either. But jass still has some repetitive tasks perfect suited to get automated by a compiler. I'm looking at you, null-setting.

Ok, you got me there. (null-setting)
But technically speaking, it's not like garbage collection ^.^
It's more like dynamic memory allocation and deallocation (It doesn't have to be like that though, who knows how the Jass VM handles locals)
Still, .. bleh, I don't know what to say at this point.
 
Well, I personally think that this resource would be great if cjass actually worked, but cjass is no longer being worked on. What's his face said that he's done with it, which means that its current bugs will stay ; |.


But w/e. Maybe we can ask cohador to implement definitions ^)^.

edit
btw, if a lib doesn't use this, it will cause syntax errors. A user would have to make all libs that they imported into their map use this, heh.
 
Top