HomeUser Control Panel (unavailable in archive)ForumsTutorialsArt GalleryResourcesMaps

Moyack Memory System (Under development)

12-30-2006, 11:12 PM#1
moyack
Hi:

As you can see in the title of this thread, I'm working with a memory system, like Game cache, but without using GameCache. I've been doing that for two weeks and now I'm stuck, so I will need some help

This system was inspired by the weaaddar's tutorial about recursive thought, where he use this "style" of programming (a good way to do efficient algorithms IMO).

First let me explain a little bit how it works. This system requires a memory pool which is made with an array of locations. Why locations?? because this structure let me save a data pair (x,y) where I use the x value to save the data (using the return bug) and the linked submemory (the location memory using return bug too). Let's see the picture below.

Moyack Memory System:
Zoom (requires log in)

We can set a subspace for allocationg a permanent memory (for storing data during all the game) and a variable subspace which will be used temporally by spells or other triggers in the game. By default the permanent space = 0, and it can be changeable at run time.

Other feature is that each Slot can have other linked memories, very useful for carrying several variables which can be obtained calling the index of the slot.

How this system manage the space in the non permanent subspace?? well, when you call the function to create a new memory, the system check in the first slots if one of them is free, if so, it assigns that slot to the trigger/spell, and this slot will be identified with a handle index. If in an strange case, you reach the slot 8192, then the system will take the first slot, it will erase it and then it will uses it. Obviously this system has functions to purge the memory, so if you do that, you won't have any problem and the slot system will always use the first free slot in the array.

The usage of this system is like gamecache, but easier. In order to see how it works, I'm going to show a demo spell:

Demo Spell


Collapse JASS:
//******************************************************************************
//*             Moyack's Memory System Test spell. Movement Frenzy.            *
//******************************************************************************
constant function Movement_Frenzy_SpellID takes nothing returns integer
    return 'A000'
endfunction

constant function Movement_Frenzy_Max_Affected_Units takes nothing returns integer
    return 5
endfunction

constant function Movement_Frenzy_AOE takes nothing returns real
    return 500.0
endfunction

constant function Movement_Frenzy_SpellDur takes nothing returns real
    return 20.0
endfunction



function Movement_Frenzy_Get_Allied_Units takes nothing returns boolean
    local unit u = GetFilterUnit()
    local player p = GetOwningPlayer(GetSpellTargetUnit())
    local boolean b = IsUnitAlly(u, p)
    set u = null
    set p = null
    return b
endfunction

function Movement_Frenzy_CopyGroup takes group g returns group
    set bj_groupAddGroupDest = CreateGroup()
    call ForGroup(g, function GroupAddGroupEnum)
    return bj_groupAddGroupDest
endfunction

function Movement_Frenzy_Tmp1 takes nothing returns nothing
    // Controls the lightings
    local timer t = GetExpiredTimer()
    local group g = H2G(MMSys_GetHandleData(t, 1))
    local group g1 = Movement_Frenzy_CopyGroup(g)
    local lightning array l
    local unit u1
    local unit u2
    local integer i = 0
    loop
        set u1 = FirstOfGroup(g)
        exitwhen CountUnitsInGroup(g1) == 1
        call GroupRemoveUnit(g1, u1)
        set u2 = FirstOfGroup(g1)
        set l[i] = H2Ltn(MMSys_GetHandleData(t, i+2))
        set i = i + 1
        call MoveLightning(l[i], true, GetUnitX(u1), GetUnitY(u1), GetUnitX(u2), GetUnitY(u2))      
    endloop
    call DestroyGroup(g1)
    set g1 = null
    set u1 = null
    set u2 = null
endfunction

function Movement_Frenzy_Tmp2 takes nothing returns nothing
    // Controls the units movement
    local timer t =  GetExpiredTimer()
    local group g = H2G(MMSys_GetHandleData(t, 1))
    local group g1 = Movement_Frenzy_CopyGroup(g)
    local unit u
    local real x
    local real y
    local real a
    loop
        set u = FirstOfGroup(g1)
        exitwhen u == null
        set a = GetRandomReal(0, 2 * bj_PI)
        set x = GetUnitX(u) + Movement_Frenzy_AOE() * Cos(a)
        set y = GetUnitY(u) + Movement_Frenzy_AOE() * Sin(a)
        call IssuePointOrder(u, "move", x, y)
        call GroupRemoveUnit(g1, u)    
    endloop
    call DestroyGroup(g1)
    set g1 = null
endfunction

function Movement_Frenzy_Tmp3 takes nothing returns nothing
    // Kill the spells and variables
    local timer t = GetExpiredTimer()
    local group g = H2G(MMSys_GetHandleData(t, 1))
    local timer t1 = H2T(MMSys_GetHandleData(t, 2))
    local timer t2 = H2T(MMSys_GetHandleData(t, 3))
    local lightning array l
    local integer i = 0
    loop
        exitwhen i == Movement_Frenzy_Max_Affected_Units()-1
        set l[i] = H2Ltn(MMSys_GetHandleData(t, i+4))
        call DestroyLightning(l[i])
        set l[i] = null
        set i = i + 1   
    endloop
    call MMSys_ClearMemorySlot(t) // Free the memory slot
    call MMSys_ClearMemorySlot(t1) // Free the memory slot
    call MMSys_ClearMemorySlot(t2) // Free the memory slot
    call DestroyTimer(t)
    call DestroyGroup(g)
    call DestroyTimer(t1)
    call DestroyTimer(t2)
    set t = null
    set g = null
    set t1 = null
    set t2 = null
endfunction

function Movement_Frenzy_Conditions takes nothing returns boolean
    return GetSpellAbilityId() == Movement_Frenzy_SpellID()
endfunction

function Movement_Frenzy_Actions takes nothing returns nothing
    local unit c = GetSpellAbilityUnit()
    local unit t = GetSpellTargetUnit()
    local timer tmp1 = CreateTimer() // Controls the lighting effect
    local timer tmp2 = CreateTimer() // Controls the target movement
    local timer tmp3 = CreateTimer() // Controls the spell duration
    local location ltg = GetUnitLoc(t)
    local boolexpr Cnd = Condition( function Movement_Frenzy_Get_Allied_Units )
    local group g = GetUnitsInRangeOfLocMatching( Movement_Frenzy_AOE(), ltg, Cnd)
    local group g1 = GetRandomSubGroup(Movement_Frenzy_Max_Affected_Units(), g)
    local lightning array l
    local integer i = 0
    local unit u1
    local unit u2
    call RemoveLocation(ltg)
    call DestroyGroup(g)
    set g = null
    set g = Movement_Frenzy_CopyGroup(g1)
    call MMSys_Create_Memory(tmp1, false, 0) // Sets a memory slot using tmp1 as index
    call MMSys_Create_Memory(tmp2, false, 0) // Sets the next memory slot using tmp2 as index
    call MMSys_Create_Memory(tmp3, false, 0) // Sets the next memory slot using tmp3 as index
    call MMSys_SetHandleData(tmp1, 1, g1) // Stores the unit group
    loop
        set u1 = FirstOfGroup(g)
        exitwhen CountUnitsInGroup(g) == 1
        call GroupRemoveUnit(g, u1)
        set u2 = FirstOfGroup(g)
        set l[i] = AddLightning("SPLK", true, GetUnitX(u1), GetUnitY(u1), GetUnitX(u2), GetUnitY(u2))
        set i = i + 1
        call MMSys_SetHandleData(tmp1, i + 2, l[i]) // Stores the lightning
        call MMSys_SetHandleData(tmp3, i + 4, l[i]) // Stores the lightning       
    endloop
    call DestroyGroup(g)
    call MMSys_SetHandleData(tmp2, 1, g1) // Stores the unit group
    call MMSys_SetHandleData(tmp3, 1, g1)
    call MMSys_SetHandleData(tmp3, 2, tmp1) // Stores the timer tmp1
    call MMSys_SetHandleData(tmp3, 3, tmp2) // Stores the timer tmp2
    call TimerStart(tmp1, 0.05, true, function Movement_Frenzy_Tmp1)
    call TimerStart(tmp2, 2.0, true, function Movement_Frenzy_Tmp2)
    call TimerStart(tmp3, Movement_Frenzy_SpellDur(), true, function Movement_Frenzy_Tmp3)
    set ltg = null
    set g = null
    set c = null
    set t = null
    set u1 = null
    set u2 = null
endfunction

//===========================================================================
function InitTrig_Movement_Frenzy takes nothing returns nothing
    local trigger t = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( t, Condition( function Movement_Frenzy_Conditions ) )
    call TriggerAddAction( t, function Movement_Frenzy_Actions )
    set t = null
endfunction



Usually the first step is to create an empty memory (set a slot with a handle index) the function is the following:

Collapse Create memory function:
function Movement_Frenzy_Actions takes nothing returns nothing
    ...
    local timer tmp1 = CreateTimer() // Controls the lighting effect
    ...
    call MMSys_Create_Memory(tmp1, false, 0) // Sets a memory slot using tmp1 as index
    ...
endfunction

As you can see, it takes a handle as index, a boolean which indicates if you are going to set a permanent variable and an integer to indicate an index if you use a permanent slot. Because we are using the temporally array pool, we set false and 0 in the second and third parameter.

In order to store and get data, you have the functions:
  • MMSys_SetHandleData and MMSys_SetRealData to store handle and real variables respectivelly.
  • MMSys_GetHandleData and MMSys_GetRealData to get handle or real variables respectivelly.

Here are some examples:
Collapse Get ans set data functions:
function Movement_Frenzy_Actions takes nothing returns nothing
    ...
    call MMSys_SetHandleData(tmp3, 1, g1) // handle index, data index, variable to be stored (handle)
    ...
endfunction

function Movement_Frenzy_Tmp2 takes nothing returns nothing
    ...
    local timer t =  GetExpiredTimer()
    local group g = H2G(MMSys_GetHandleData(t, 1)) // Gets data using the handle index as reference, 
                                                   // getting the handle variable with index 1
    ...
endfunction

To clean the slot for later use by other function, you can do it in the following way:

Collapse Clean memory function:
function Movement_Frenzy_Tmp3 takes nothing returns nothing
    // Kill the spells and variables
    local timer t = GetExpiredTimer()
    ...
    call MMSys_ClearMemorySlot(t) // Free the memory slot
    ...
    call DestroyTimer(t)
    ...
    set t = null
    ...
endfunction


And that's all

Final comment:
Unfortunatelly I'm having problems with the storing part, sometimes it saves the data, other times don't. So I decided to release the code with a test map. I really appreciate if somebody can help me to figure it out what is the bug. And if the system has an impossibility, I really want to know, so we can find other approach.

Any comments, critics, leaks, bug reports, are welcome.

Moyack.

Moyack's Memory System code

Collapse JASS:
//******************************************************************************
//*                                                                            *
//*                          Moyack's Memory System                            *
//*                                                                            *
//*                               version 1.0                                  *
//*                                                                            *
//******************************************************************************
globals
    location array udg_Memory_Pool
    integer udg_PermanentSlots = 0 // Sets the slots which won't be automatically erased, they will be the first n slots
    integer udg_MCounter = udg_PermanentSlots // Sets the variable slot index.
endglobals

//******************************************************************************
//* Return Bug functions                                                       *
//******************************************************************************
constant function H2I takes handle h returns integer
    return h
    return 0
endfunction

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

//******************************************************************************
//* Converters                                                                 *
//* Here you can add the converters you will need in your map.                 *
//******************************************************************************
constant function H2L takes handle h returns location
    // Converts handle to location
    return h
endfunction

constant function H2G takes handle h returns group
    // Converts handle to group
    return h
endfunction

constant function H2U takes handle h returns unit
    // Converts handle to unit
    return h
endfunction

constant function H2T takes handle h returns timer
    // Converts handle to timer
    return h
endfunction

constant function H2Ltn takes handle h returns lightning
    // Converts handle to lightning
    return h
endfunction
//******************************************************************************
//* End converters                                                             *
//******************************************************************************



//******************************************************************************
//* this functions make internal memory management (slot detection and edit)   *
//******************************************************************************
function MMSys_GetFreeSlot takes boolean IncludePermanent returns integer
    // Gets first free slot index
    local integer i
    if IncludePermanent then
        set i = 0
    else
        set i = udg_PermanentSlots
    endif
    loop
        exitwhen udg_Memory_Pool[i] == null
        set i = i + 1
    endloop
    return i
endfunction

function MMSys_MemoryGetID takes location memory returns integer
    // Determines which memory slot correspond with the memory variable
    // That location variable must be part of the udg_Memory_Pool array
    local integer i = 0
    loop
        exitwhen memory == udg_Memory_Pool[i]
        set i = i + 1
    endloop
    return i
endfunction

function MMSys_GetMemorybyHandleIndex takes handle HandleIndex returns location
    // Gets the main memory slot tagged with the handle Index
    local integer hi = H2I(HandleIndex)
    local integer c = 0
    loop
        exitwhen R2I(GetLocationX(udg_Memory_Pool[c])) == hi
        set c = c + 1
    endloop
    return udg_Memory_Pool[c]
endfunction

function MMSys_EraseMemory takes location mem returns nothing
    // Erase recursivelly the data
    local location m = H2L(I2H(R2I(GetLocationY(mem))))
    if m != null then
        call MMSys_EraseMemory(m)
    endif
    call RemoveLocation(m)
    call RemoveLocation(mem)
    set m = null
    set mem = null
endfunction

function MMSys_Count takes location mem returns integer
    // Internal Counter function
    local integer i = 0
    local location l = Location(GetLocationX(mem), GetLocationY(mem))
    loop
        exitwhen GetLocationY(l) == 0
        set l = H2L(I2H(R2I(GetLocationY(l))))
        set i = i + 1
    endloop
    call RemoveLocation(l)
    set l = null
    return i
endfunction

function MMSys_SetData takes location mem, integer index, real Data returns nothing
    // Sets a data in a submemory
    local real x
    local real y
    local location lc = H2L(I2H(R2I(GetLocationY(mem))))
    if index == 1 then // Sets the data
        set y = GetLocationY(lc)
        call RemoveLocation(lc)
        set lc = Location(Data, y)
        set x = GetLocationX(mem)
        set y = I2R(H2I(lc))
        call RemoveLocation(mem)
        set mem = Location(x, y)
    elseif lc == null then // Fills empty submemories with zeros
        set lc = Location(0, 0)
        set x = GetLocationX(mem)
        set y = I2R(H2I(lc))
        call RemoveLocation(mem)
        set mem = Location(x, y)
        call MMSys_SetData(lc, index - 1, Data)
    elseif index != 1 then // Search in the next submemory
        call MMSys_SetData(lc, index - 1, Data)
    endif
endfunction

function MMSys_GetData takes location mem, integer index returns real
    // Gets a data froma submemory
    local real y = GetLocationY(mem)
    local location l
    local real r = 0
    if index > 0 then
        if y > 0 and index != 1 then
            set l = H2L(I2H(R2I(y)))
            call MMSys_GetData(l, index - 1)
        elseif index == 1 then
            set r = GetLocationX(mem)
        endif
    endif
    return r
endfunction
//******************************************************************************
//* End internal Functions                                                     *
//******************************************************************************



//******************************************************************************
//* Here's the main functions                                                  *
//******************************************************************************
function MMSys_ClearMemorySlot takes handle HandleIndex returns nothing
    // Clear a memory Slot
    local location mem = MMSys_GetMemorybyHandleIndex(HandleIndex)
    call MMSys_EraseMemory(mem)
endfunction

function MMSys_CountData takes handle HandleIndex returns integer
    // Returns the number of variables stored in a memory
    local integer i = 0
    local location mem = MMSys_GetMemorybyHandleIndex(HandleIndex)
    set i = MMSys_Count(mem)
    return i
endfunction

function MMSys_SetRealData takes handle HandleIndex, integer IndexData, real value returns nothing
    // Sets the real data in the memory alocation, creating a new index if neccesary
    local location mem = MMSys_GetMemorybyHandleIndex(HandleIndex)
    call MMSys_SetData(mem, IndexData, value)
endfunction

function MMSys_SetHandleData takes handle HandleIndex, integer IndexData, handle NewData returns nothing
    // Sets the handle data in the memory alocation
    local location mem = MMSys_GetMemorybyHandleIndex(HandleIndex)
    call MMSys_SetData(mem, IndexData, I2R(H2I(NewData)))
endfunction

function MMSys_GetRealData takes handle HandleIndex, integer IndexData returns real
    // Gets the handle data in the memory alocation
    local location mem = MMSys_GetMemorybyHandleIndex(HandleIndex)
    return MMSys_GetData(mem, IndexData)
endfunction

function MMSys_GetHandleData takes handle HandleIndex, integer IndexData returns handle
    // Gets the handle data in the memory alocation
    local location mem = MMSys_GetMemorybyHandleIndex(HandleIndex)
    return I2H(R2I(MMSys_GetData(mem, IndexData)))
endfunction

function MMSys_SetNewStandardIndex takes nothing returns integer
    // Autoset a new memory slot
    if udg_MCounter == JASS_MAX_ARRAY_SIZE then
        set udg_MCounter = udg_PermanentSlots
    else
        set udg_MCounter = MMSys_GetFreeSlot(false)
    endif
    return udg_MCounter
endfunction

function MMSys_Create_Memory takes handle HandleIndex, boolean Permanent, integer PermIndex returns boolean
    // Creates a memory slot.
    local integer i = H2I(HandleIndex)
    local integer c = MMSys_SetNewStandardIndex()
    if Permanent then // Saves in the permanent section
        if PermIndex < udg_PermanentSlots then
            call MMSys_EraseMemory(udg_Memory_Pool[PermIndex])
            set udg_Memory_Pool[PermIndex] = Location(I2R(i), 0)
            return true
        else
            return false
        endif
    else // Saves in the temporal section
        call MMSys_EraseMemory(udg_Memory_Pool[c])
        set udg_Memory_Pool[c] = Location(I2R(i), 0)
        return true 
    endif
endfunction

function MMSys_Set_PermanentPoolSize takes integer size returns nothing
    // Sets the size of the Permanent Pool
    local integer i = 0
    set udg_PermanentSlots = size
    loop
        exitwhen i == size
        call MMSys_EraseMemory(udg_Memory_Pool[i])
        set i = i + 1    
    endloop
endfunction
//******************************************************************************
//* End main functions                                                         *
//******************************************************************************

Attached Images
File type: jpgMMSys.jpg (46.3 KB)
Attached Files
File type: w3xMoyack Memory System.w3x (20.1 KB)
12-30-2006, 11:28 PM#2
Vexorian
Use grimoire(war3err.dll), it is the best (the only) debug environment out there.

I did and it seems you are calling RemoveLocation(null) thousands of times and there is also an op limit hit in the GetMemoryByHandleWhatever function.

That said I don't see why would this system be used above the n alternatives to gamecache already made, so if there is a reason please explain?
12-30-2006, 11:38 PM#3
moyack
It is a system that I'm developing for my project Power of Corruption, and I consider this memory concept interesting and efficient (I want to know if I'm wrong). I just wanted to get some help with it, and if somebody likes it, well, it can use it (when it works 100% fine).

My intention with this system is to share it, and know if it has something really bad or good. That's all.

For that reason I want to know the opinion of good jassers like you.
12-31-2006, 12:44 AM#4
PipeDream
So at a glance it looks to me like you have an array of lists from which you parcel out slots. I think it could be improved enormously if you replaced the parceller (GetFreeSlot) with a stack. Another thing you could do is replace your lists with tries. Take a number and break it into base 2. Then go through the bits, each time you hit 1 go left through the trie otherwise go right.
12-31-2006, 03:33 AM#5
moyack
Interesting ideas I can say. I added the test map.

I was thinking in something (probably is the reason for a bug with my system) When you create a new location using set l = Location(x, y) if x and/or y are bigger that the map boundaries, the location will be modified to the "closest" inside the map?? In theory it should save the values that you entered.
12-31-2006, 06:04 AM#6
PipeDream
Nah, locations 'just work'
---
Here's some discussion we had on the topic a few months ago:
http://www.wc3campaigns.net/showthread.php?t=85748
12-31-2006, 11:56 PM#7
moyack
Thanks PipeDream, I hadn't seen before and it's very interesting.

I'll work with the system (as soon as I fix a problem with my video card, it's broken the hardware acceleration) and I'll try to follow your comments (because I have to figure it out how to implement them :P)