HomeUser Control Panel (unavailable in archive)ForumsTutorialsArt GalleryResourcesMaps

Game Cache Hates Me

07-16-2006, 12:03 PM#1
Rising_Dusk
Ok..
So reading the "Known Bugs in JASS" topic, I read over that 'setting handles to null' bug thing. Well I've been doing that a long time and not really encountered any problems... Until this spell.

It's basically just an autocast ability that does a pretty simple multi-directional shot. Nothing spectacular.

What I do is add the units to a group and then attach the group to a timer (Sound familiar, hunh?).
Well now, every 40th time the spell is cast, the arrow-looking units go completely haywire during motion. Notably, the spell works 100% the other 39 times before they go nuts.

And when I say they go nuts, I mean it.
They start flying in completely random directions, they will simply STOP moving just stay there the rest of the game, one time an arrow did a circle..
I guess what I'm getting at though is this.
Even if I don't null the timer, it still goes haywire every 40th or so cast.

The code is below.
I always get yelled at if I omit parts of the code because they're completely irrelevant, but I'm really averse to just handing out code for AotZ by public means.
So I hope you can excuse me when I omit the aspects of the spell that deal with the autocasting and just post the parts that make use of the handles/group/timer.
Also, just trust me when I say the functions like ProjectX(...) and stuff work. I've used them hundreds of times before, no need to worry if they cause the problem or not. They all take real return real and have no cache in them at all.

Collapse JASS:
function Diverging_Shot_Cast_Group_Update takes nothing returns nothing
    local unit u = udg_u_Lunera
    local unit e = GetEnumUnit()
    local unit s 
    local integer lvl = GetUnitAbilityLevel(u, 'A017')
    local integer agi = GetHeroAgi(u, true)
    local real perc = .70+(.10*lvl)
    local real ang = GetReal(e, "ang")
    local real dis = GetReal(e, "dis")
    local group g = GetGroup(e, "g")
    local group g2 = CreateGroup()
    local boolexpr b = Condition(function Diverging_Shot_Cast_Group_Check)
    local real x = GetUnitX(e)
    local real y = GetUnitY(e)
    
    set x = ProjectX(x, 33, ang)
    set y = ProjectY(y, 33, ang)
    call SetUnitX(e, x)
    call SetUnitY(e, y)
    call GroupEnumUnitsInRange(g2, x, y, 30, b)
    set s = FirstOfGroup(g2)
    if s != null then
        call DestroyEffect(AddSpecialEffect("Abilities\\Spells\\Other\\Stampede\\StampedeMissileDeath.mdl", GetUnitX(s), GetUnitY(s)))
        call UnitDamageTarget(u, s, perc*agi, false, false, ATTACK_TYPE_HERO, DAMAGE_TYPE_NORMAL, null)
        call FlushLocals(e)
        call GroupRemoveUnit(g, e)
        call RemoveUnit(e)
    else
        call SetReal(e, "dis", dis+33)
        if dis > 650 then
            call FlushLocals(e)
            call GroupRemoveUnit(g, e)
            call RemoveUnit(e)
        endif
    endif
    
    call GroupClear(g2)
    call DestroyGroup(g2)
    call DestroyBoolExpr(b)
    set g2 = null
    set u = null
    set e = null
    set s = null
    set g = null
    set b = null
endfunction

function Diverging_Shot_Cast_Group takes nothing returns nothing
    local timer tm = GetExpiredTimer()
    local group g = GetGroup(tm, "g")
    local unit s = FirstOfGroup(g)
    
    call ForGroup(g, function Diverging_Shot_Cast_Group_Update)
    if s == null then
        call FlushLocals(tm)
        call PauseTimer(tm)
        call DestroyTimer(tm)
    endif
    
    set tm = null
    set s = null
    set g = null
endfunction

function Diverging_Shot_Cast_Actions takes nothing returns nothing
    local unit u = udg_u_Lunera
    local unit t = GetSpellTargetUnit()
    local unit s
    local integer index = 0
    local integer lvl = GetUnitAbilityLevel(u, 'A017')
    local real x = GetUnitX(u)
    local real y = GetUnitY(u)
    local real fac = AngleBetweenPointsXY(x, y, GetUnitX(t), GetUnitY(t))
    local real ang = fac - (3.75+(3.75*lvl))
    local group g = CreateGroup()
    local timer tm = CreateTimer()
    
    loop
        exitwhen index == lvl+2
        set s = CreateUnit(GetOwningPlayer(u), 'h01G', x, y, ang)
        call SetUnitX(s, x)
        call SetUnitY(s, y)
        call GroupAddUnit(g, s)
        call SetHandle(s, "g", g)
        call SetReal(s, "ang", ang)
        call SetReal(s, "dis", 0)
        set index = index + 1
        set ang = ang + 7.5
    endloop
    call SetHandle(tm, "g", g)
    call TimerStart(tm, .033, true, function Diverging_Shot_Cast_Group)
    
    set tm = null
    set u = null
    set t = null
    set s = null
    set g = null
endfunction

Looking back, I can't figure this out for the life of me.
Not to mention I refuse to release AotZ knowing there's a bug in it that could screw up that badly.
So yeah, this is a kind of bad place for me to be in, and any and all help would be appreciated.
Thanks in advance.

Zoom (requires log in)

I added a screenshot to show two types of problems that occur.
The first one, in the arrow row on the bottom, you'll notice one moved far to the right and changed angles. The other issue is them just stopping, which can be seen at the top of the picture.
Attached Images
File type: jpgSigh.JPG (40.4 KB)
07-16-2006, 01:28 PM#2
Vexorian
Try removing the set tm=null in the last function.
07-16-2006, 01:31 PM#3
Rising_Dusk
Quote:
Originally Posted by My last post
Even if I don't null the timer, it still goes haywire every 40th or so cast.

I've tried removing the set timer to null in both the second and third functions and it still goes weird.
I don't know if there's anything else that would cause it?
07-16-2006, 01:35 PM#4
Vexorian
If that isn't the problem then it is surelly a logic flaw in the code, I'll have to browse it...
07-16-2006, 01:37 PM#5
Rising_Dusk
That's what I had assumed too.

However, I don't understand why it would work anywhere from 35-50 times and then suddenly fail once or twice in a row due to a flaw in my logic, then repeat the cycle all over again.
It just doesn't seem to make any sense to me, and I couldn't find any problems in the code itself.

EDIT:
If it's really necessary I can post the entire code. I really don't think it'll help at all, but if anyone really wants it I can.
I really just want this resolved, it's bothering the shiza out of me.
07-16-2006, 02:43 PM#6
Sharingan
Quote:
Even if I don't null the timer, it still goes haywire every 40th or so cast.
Is it exactly 40 times?
07-16-2006, 02:46 PM#7
Rising_Dusk
First test it was 34, second it was 38, third it was 47 or 46, I lost count.
But no, there is no rhyme or reason to it.

Also, sometimes it fails twice in a rows, othertimes it fails only once at a time.
Then you've got to wait say 40~ more times for it to happen again.
07-16-2006, 02:55 PM#8
Sharingan
.................................
This does not make sense ._. ...
Do you have a timer that like, expires every (insert the time you cast that ca. 40 times in a row) which might be connected with the handles you are using in those functions?
07-16-2006, 03:11 PM#9
Rising_Dusk
Well, it seems more COMMON with other spells of her's that utilize game cache.
But I don't think there's is enough evidence for a connection between the failures and the usages of the other spells.
I've done enough testing to know that.

Other than that, the handles aren't even on the same things across spells, so it wouldn't make sense that they were conflicting.
07-16-2006, 04:03 PM#10
Vexorian
I dunno maybe post those non-standard gamecache functions? Also the project ones?
07-16-2006, 04:23 PM#11
Naakaloh
Quote:
Originally Posted by PipeDream
- Please use at least 4 characters for the prefix. Because of the birthday paradox with only 2 there'd be collisions at 40 spells or so. Find and replace all is your friend.

I believe that the birthday paradox is a probability phenomenon that the Warcraft3 gamecache hash table uses to attempt to avoid collisions with values that have the same key, so as PipeDream said, you'll probably want more than just two characters for your keys.
07-16-2006, 04:25 PM#12
Vexorian
I don't think there's a big birthday paradox probability in wc3's gamecache, otherwise I would have ran with those problems like 1000 thousand times , and yes, that's one million
07-16-2006, 04:35 PM#13
Rising_Dusk
Lol, well you asked for it Vex.
These functions really are not exciting at all, and I know they aren't the problem.

Collapse JASS:
//***************************************************************************************************************
//*Returns a target X value projected from a given X value, a distance, and an angle.
//*
function ProjectX takes real x, real dist, real angle returns real
    return x + dist * Cos(angle * 0.01745)
endfunction
 
//***************************************************************************************************************
//*Returns a target Y value projected from a given Y value, a distance, and an angle.
//*
function ProjectY takes real y, real dist, real angle returns real
    return y + dist * Sin(angle * 0.01745)
endfunction

And the non-standard game cache functions are the exact same as the standard ones I just removed the word 'Handle' from all of them because it was annoying to have to type it for every single time I used them.
Plus, do note that I've used them in multiple other things too, this just is the only spell that causes problems at all.
07-16-2006, 05:03 PM#14
Vexorian
I am not sure I am not good reading other people's code, Try adding debug messages like crazy like display each unit's direction and stuff to proof that the error is really on the gamecache side
07-16-2006, 05:07 PM#15
Naakaloh
Quote:
Originally Posted by Vexorian
I don't think there's a big birthday paradox probability in wc3's gamecache, otherwise I would have ran with those problems like 1000 thousand times , and yes, that's one million

Well, I've never experienced the problem either, but it still may be worth a try.