• 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.

[JASS] SingleTimerSystem

Status
Not open for further replies.
Level 12
Joined
Aug 20, 2007
Messages
866
This is a system I have been messing around with for the past few days

I just finished it, and have not tested it in-game

If there are any improvements any good vJASS-ers can add to it, that would be awesome

Here is the whole textfile in a JASS tag...

JASS:
//(Now I'm gonna write up the IndexSystem textmacro, this index system happens to be used with the globals in the system
//So if you don't know how, you shouldn't change it, but if you are good at vJASS and know what your doing, you can change
//it to a struct)
//*The textmacro has been requested to be placed at the top of the script*

//! textmacro IndexSystem takes R

globals
    integer $R$_COUNTER = 0
    integer $R$_INDEX = -1
    integer array $R$_RECYCLE
    boolean array $R$_TRACK
endglobals

function Index takes nothing returns integer
    local integer ret
    if $R$_RECYCLE[0] == 0 then
    //Recycle 'bin' is empty
      set $R$_INDEX = $R$_INDEX + 1
      set $R$_TRACK[$R$_INDEX] = false
      return $R$_INDEX
    endif
    //Not empty
    set $R$_COUNTER = $R$_COUNTER - 1
    set ret = $R$_RECYCLE[$R$_COUNTER]
    set $R$_RECYCLE[$R$_COUNTER] = 0
    set $R$_TRACK[ret] = false
    return ret
    //Returns last recycled index
endfunction

function Recycle takes integer i returns nothing
    if $R$_TRACK[i] then
      return
    endif
    set $R$_TRACK[i] = true
    set $R$_RECYCLE[$R$_COUNTER] = i
    set $R$_COUNTER = $R$_COUNTER + 1
    //Sets last recycled index = i
endfunction

//! endtextmacro

library SingleTimerSystem

//! runtextmacro IndexSystem("TimedFunction")

globals
    private integer array TimeOut
    private integer array Working
    private integer array GetStruct
    private string array Function
    private integer array Stored
    private integer Max = 0
    private timer T = CreateTimer() //Might bug

    //These globals are not private, they are to be used in your called functions
    boolean Stop = false
    integer Temp_GetStruct
endglobals

private constant function Interval takes nothing returns real
    return .01
endfunction

private constant function NumberOfPlaces takes nothing returns integer
    return 2
endfunction
//This is a constant that allows you to determine the number of places the system is accurate to


//These you do not need to mess with
private constant function K takes nothing returns integer
    return Pow(10,NumberOfPlaces())
endfunction

private constant function Interval2 takes nothing returns integer
    return R2I(Interval() * K())
endfunction

prviate function Loop takes nothing returns nothing
    local integer i = 0
    local integer s
    local integer max = Max
    loop
      exitwhen i == max
      set s = Stored[i]
      set Working[s] = Working[s] - Interval2()
      if Working[s] <= 0 then
        set Temp_GetStruct = GetStruct[s]
        call ExecuteFunc(Function[s])
        //Calls the desired function, with the attached struct as a temporary global integer

        //Stop is a temporary global boolean that must be set at the end of each function, true stops the functions
        //execution, false continues it
        //You don't need to change it to false, just true, the system automatically resets it to false
        if Stop or TimeOut[s] == 0 then
          set Stored[i] = Stored[Max]
          call TimedFunction_Recycle(s)
          set Max = Max - 1
          if Max == 0 then
            call PauseTimer(T)
          endif
          set i = i - 1
          set Stop = false
        endif
        //Deallocates

        //Whether or not the instance had been deallocated, the Working[] is reset to its original timeout in order
        //to continue if it is a PeriodicFunction call
        set Working[s] = Timeout[s]
      endif

      set i = i + 1
      //Continues loop
    endloop
endfunction

function PeriodFunc takes real time, string func, integer struct returns nothing
    local integer s = TimedFunction_Index()
    set Function[s] = func
    set GetStruct[s] = struct
    set TimeOut[s] = R2I(time * K())
    set Working[s] = R2I(time * K())
    set Stored[max] = s
    if Max == 0 then
      call TimerStart(T, Interval(), true, function Loop)
    endif
    set Max = Max + 1
endfunction
//This is for calling functions that will be called a number of times each timeout before
//a condition in your function tells the function to stop being called

function TimedFunc takes real time, string func, integer struct returns nothing
    local integer s = TimedFunction_Index()
    set Function[s] = func
    set GetStruct[s] = struct
    set TimeOut[s] = 0
    set Working[s] = R2I(time * K())
    set Stored[max] = s
    if Max == 0 then
      call TimerStart(T, Interval(), true, function Loop)
    endif
    set Max = Max + 1
endfunction
//This is for calling functions that will only be called once in a timeout

endlibrary

//Notes

//The stored numbers become stacked when looped, in order to preserve the 'max' stored number, it takes the slot of
//the removed number. This can become tricky, because although the index is preserved, it is not called until the next
//loop, so all I need to do is set i = i - 1, to negate the bottom i + 1, and run the max slot in the same loop

//The purpose of the system is to only use one timer in your whole map, which should make a significant difference in speed
//Although, if you'll notice the code isn't too efficient, so I am gambling on the fact that timers are obscenely large

//You'll notice that included is an index system textmacro, I include it because I like being able to index globals
//without relying on structs

//when calling TimedFunction(), the TimeOut[] is set to 0, so you do not need to include the 'Stop' at the end of the
//function

//Ex.

function IamPeriodic takes nothing returns nothing
    local integer dat = Temp_GetStruct
    local boolean end = true
    if end then
      set Stop = true
    endif
endfunction
//The periodic functions are assumed to have some sort of condition in which the periodic function stops running

function ImaSingleShotFunction takes nothing returns nothing
    local integer dat = Temp_GetStruct
    //Actions
    //Does not need the 'Stop'
endfunction

//Again, this system totally relies on the fact that timers take up too much memory, this system goes through a seriously
//large number of repetitions in a few seconds (let's say 10 projectiles, for 5 seconds each, equals 5000 loops)
//Whereas using an attachment system might have one-third the loops, but 10 timers are going
//(one-third loops because 10 timers each going at [a common timeout] .035, less than a third, plus the memory for 10 timers)

//If you'd like, you can improve efficiency by changing the interval constant to another number, but remember the first
//restriction, all the timeout numbers must be multiples of that constant, .02 shouldn't be too much of a stretch

//Since many people recommend using .035 as the interval to use for timers, you could use it here if you'd like, although
//that would create

//Added a series of constants to allow for total customization to your maps specifications

//The system will accomodate for approximate timeouts that don't match your interval, although it will not be extremely
//accurate, and if you want to handle extremely small timeouts, I recommend using an attachment system

//As long as when you change the Interval() you remember to change the appropriate number of places the interval
//goes to, the system should work just peachy

//As is, our system has been set with an Interval() of .01, and the number of places .01 goes to is 2, this you probably
//would like to change to something more practical (maybe .02), but I have no computer handy that has WC3 installed and
//cannot test this

//Function names shortened from 'PeriodicFunction' to 'PeriodFunc', 'TimedFunction' to 'TimedFunc'

//Restrictions

//1, Timeouts are only accurate to the decimal place you have set, [Timeouts must be a multiple of the Interval() constant]

//In our example, since we set it to 2 and our timeout is .01
//These examples are...{ .02 Good, .021 Bad, 2.0 Good, 2.01 Good, 2.001 Bad }

//2, Timeouts cannot be lower than the Interval() constant, a little obvious if you understood restrictions #1
//for 0.0 timeouts, use QUICK_TIMER(), using this would just be dumb

//Example Using Periodic Function

struct GoUp
  unit u
endstruct

function Increase takes nothing returns nothing
    local GoUp d = Temp_GetStruct
    local unit u = d.u
    local real h = GetUnitFlyHeight(u)
    call SetUnitFlyHeight(u, h + 10)
    if h+10 >= 1000 then
      set d.u = null
      call d.destroy()
      set Stop = true
    endif
endfunction

function IncreaseHeight takes unit u returns nothing
    local GoUp d = GoUp.create()
    set d.u = u
    call PeriodFunc(.02, "Increase", d)
endfunction
//The string is supposed to be the name of the function that will be called, if your using vJASS scopes and librarys, please
//read up on the prefix constants

//Please note that these examples are nonsense, and useless, I needed to make them very quickly, and hence did not make
//anything useful

//Example using Timed Function

struct KillMe
  unit u
endstruct

function Kill takes nothing returns nothing
    local KillMe d = Temp_GetStruct
    call KillUnit(d.u)
    set d.u = null
    call d.destroy()
endfunction

function KillInTime takes unit u, real time returns nothing
    local KillMe d = KillMe.create()
    set d.u = u
    call TimedFunc(time, "Kill", d)
endfunction

//In this example, you do not need to add the 'Stop'

//Notice, you also do not need to create and destroy any timers, which, if your anything like me, leaves you feeling like
//your missing something

And the system is uploaded as how I originally wrote it as a textfile
 

Attachments

  • SingleTimerSystem.txt
    9.5 KB · Views: 97
Last edited:

LeP

LeP

Level 13
Joined
Feb 13, 2008
Messages
543
And you should use TriggerEvaluate instead of ExecuteFunc (TriggerEvaluate is faster).

p.s.: this is a free written script, isnt it?
There're alot of errors
example:
ExecuteFunc <-> ExecuteFunction
Or <-> or
^ <-> Pow
And inline functions or use constants
 
Level 17
Joined
Apr 27, 2008
Messages
2,455
And you should use TriggerEvaluate instead of ExecuteFunc (TriggerEvaluate is faster).

p.s.: this is a free written script, isnt it?
There're alot of errors
example:
ExecuteFunc <-> ExecuteFunction
Or <-> or
^ <-> Pow
And inline functions or use constants

TriggerEvaluate don't create an other thread.
The functions with only one line would be inlined with the new jasshelper.

And yes it's a free written script, he said it.
 
Level 12
Joined
Aug 20, 2007
Messages
866
Ok, I will fix spelling errors, I wrote it as quickly as I could

Hmmm.... Yeah I really can't figure out a better way to do it without using ExecuteFunction(), because if you take a deep look, each line of the loop must be executed and finished before the next line, or else you could get un-wanted errors
 

LeP

LeP

Level 13
Joined
Feb 13, 2008
Messages
543
Why do you need "multithreading"?
And TriggerExecute ist faster than ExecuteFunc and creates a new "thread".

Btw. TimerStart() is much faster than TriggerExecute and also create a new "thread" but you have to store return-bugged integer for this, which ist not recommended...
 
Level 12
Joined
Aug 20, 2007
Messages
866
Ok... many of the errors are fixed *I hope*, I don't really know for sure, I do not have access to a computer that has WC3 installed

Its pretty obvious LeP, if you would read the code, you'd understand that "each line of the loop must be executed and finished before the next"

If you call TriggerEvaluate (which uses a handle).... oh wait a second

I can use TriggerEvaluate, because it returns a boolean, I am going to update the code, this may take a minute

{I don't know whether or not TriggerEvaluate actually returns a boolean, I will need to check this}

EDIT - Yes, TriggerEvaluate() returns a boolean... I am editing the system to incorporate TriggerEvaluate() rather than ExecuteFunc(), although I seriously doubt ExecuteFunc() is slower than TriggerEvaluate() with all the extra function calls needed to create a trigger, add code to it, evaluate, and then destroy the trigger
 

LeP

LeP

Level 13
Joined
Feb 13, 2008
Messages
543
TriggerEvaluate returns a boolean.

JASS:
function test takes nothing returns boolean
    local unit u=<whatever>
    [do something with u...]
    return GetWidgetLife(u)<.405
endfunction

function init takes nothing returns nothing
    local trigger t=CreateTrigger()
    call TriggerAddCondition(t, Condition(function test))
    if TriggerEvaluate(t) then
        //the unit us dead...
    endif
endfunction

And again: TriggerExecute() is faster than ExecuteFunc().
 
Level 12
Joined
Aug 20, 2007
Messages
866
Here is the 2nd version, I will keep the textfile of the original, in case it turns out to be more efficient

The 2nd version must make use of a bug in order to function properly...

JASS:
//(Now I'm gonna write up the IndexSystem textmacro, this index system happens to be used with the globals in the system
//So if you don't know how, you shouldn't change it, but if you are good at vJASS and know what your doing, you can change
//it to a struct)
//*The textmacro has been requested to be placed at the top of the script*

//! textmacro IndexSystem takes R

globals
    integer $R$_COUNTER = 0
    integer $R$_INDEX = -1
    integer array $R$_RECYCLE
    boolean array $R$_TRACK
endglobals

function Index takes nothing returns integer
    local integer ret
    if $R$_RECYCLE[0] == 0 then
    //Recycle 'bin' is empty
      set $R$_INDEX = $R$_INDEX + 1
      set $R$_TRACK[$R$_INDEX] = false
      return $R$_INDEX
    endif
    //Not empty
    set $R$_COUNTER = $R$_COUNTER - 1
    set ret = $R$_RECYCLE[$R$_COUNTER]
    set $R$_RECYCLE[$R$_COUNTER] = 0
    set $R$_TRACK[ret] = false
    return ret
    //Returns last recycled index
endfunction

function Recycle takes integer i returns nothing
    if $R$_TRACK[i] then
      return
    endif
    set $R$_TRACK[i] = true
    set $R$_RECYCLE[$R$_COUNTER] = i
    set $R$_COUNTER = $R$_COUNTER + 1
    //Sets last recycled index = i
endfunction

//! endtextmacro

library SingleTimerSystem

//! runtextmacro IndexSystem("TimedFunction")

//Some functions used internally, this system makes use of the pointer to integer bug
private function C2I takes code c returns integer
    return c
    return 0
endfunction

private function GetCode takes integer i returns code
    return i
    return null
endfunction
//

globals
    private integer array TimeOut
    private integer array Working
    private integer array GetStruct
    private integer array Function
    private integer array Stored
    private integer Max = 0
    private timer T = CreateTimer() //Might bug

    //This global is not private, it is to be used in your called functions
    integer Temp_GetStruct
endglobals

private constant function Interval takes nothing returns real
    return .01
endfunction

private constant function NumberOfPlaces takes nothing returns integer
    return 2
endfunction
//This is a constant that allows you to determine the number of places the system is accurate to


//These you do not need to mess with
private constant function K takes nothing returns integer
    return Pow(10,NumberOfPlaces())
endfunction

private constant function Interval2 takes nothing returns integer
    return R2I(Interval() * K())
endfunction

private function Loop takes nothing returns nothing
    local integer i = 0
    local integer s
    local integer max = Max
    local trigger t
    local code c
    loop
      exitwhen i == max
      set s = Stored[i]
      set Working[s] = Working[s] - Interval2()
      if Working[s] <= 0 then
        set c = GetCode(Function[s])
        set t = CreateTrigger()
        call TriggerAddCondition(t,Condition(c))
        set Temp_GetStruct = GetStruct[s]
        if EvaluateTrigger(t) then
          //The function is called and checked, if it returns false, it does not stop, if it returns true, it is deallocated
          //The Temp_GetStruct is a variable that can be used to retrieve your struct data
          set Stored[i] = Stored[Max]
          call TimedFunction_Recycle(s)
          set Max = Max - 1
          if Max == 0 then
            call PauseTimer(T)
          endif
          call TriggerClearConditions(t)
          call DestroyTrigger(t)
          set t = null
          set i = i - 1
        endif
        //Deallocates

        //Whether or not the instance had been deallocated, the Working[] is reset to its original timeout in order
        //to continue if it is a PeriodicFunction call
        set Working[s] = Timeout[s]
      endif

      set i = i + 1
      //Continues loop
    endloop
endfunction

function PeriodFunc takes real time, code func, integer struct returns nothing
    local integer s = TimedFunction_Index()
    set Function[s] = C2I(func)
    set GetStruct[s] = struct
    set TimeOut[s] = R2I(time * K())
    set Working[s] = R2I(time * K())
    set Stored[max] = s
    if Max == 0 then
      call TimerStart(T, Interval(), true, function Loop)
    endif
    set Max = Max + 1
endfunction
//This is for calling functions that will be called a number of times each timeout before
//a condition in your function tells the function to stop being called

function TimedFunc takes real time, code func, integer struct returns nothing
    local integer s = TimedFunction_Index()
    set Function[s] = C2I(func)
    set GetStruct[s] = struct
    set TimeOut[s] = 0
    set Working[s] = R2I(time * K())
    set Stored[max] = s
    if Max == 0 then
      call TimerStart(T, Interval(), true, function Loop)
    endif
    set Max = Max + 1
endfunction
//This is for calling functions that will only be called once in a timeout

endlibrary

//Notes

//The stored numbers become stacked when looped, in order to preserve the 'max' stored number, it takes the slot of
//the removed number. This can become tricky, because although the index is preserved, it is not called until the next
//loop, so all I need to do is set i = i - 1, to negate the bottom i + 1, and run the max slot in the same loop

//The purpose of the system is to only use one timer in your whole map, which should make a significant difference in speed
//Although, if you'll notice the code isn't too efficient, so I am gambling on the fact that timers are obscenely large

//You'll notice that included is an index system textmacro, I include it because I like being able to index globals
//without relying on structs

//Every function called must return a boolean, one-shot functions must return true, periodic returns true when
//you want it to stop

//Ex.

global
    boolean StopMe = false
endglobal

function IamPeriodic takes nothing returns boolean
    //local integer dat = Temp_GetStruct This line must be in every one of the functions that are to be called
    if StopMe then
      return true
    endif
    return false
endfunction
//The periodic functions are assumed to have some sort of condition in which the periodic function stops running

function ImaSingleShotFunction takes nothing returns boolean
    local integer dat = Temp_GetStruct
    //Actions
    return true
endfunction
//One-shot types return true

//Again, this system totally relies on the fact that timers take up too much memory, this system goes through a seriously
//large number of repetitions in a few seconds (let's say 10 projectiles, for 5 seconds each, equals 5000 loops)
//Whereas using an attachment system might have one-third the loops, but 10 timers are going
//(one-third loops because 10 timers each going at [a common timeout] .035, less than a third, plus the memory for 10 timers)

//If you'd like, you can improve efficiency by changing the interval constant to another number, but remember the first
//restriction, all the timeout numbers must be multiples of that constant, .02 shouldn't be too much of a stretch

//Since many people recommend using .035 as the interval to use for timers, you could use it here if you'd like, although
//that could create a few problems, you'd need to make sure every timed function's timeout is a multiple of .035
//as well as changing the 2nd constant to 3

//Added a series of constants to allow for total customization to your maps specifications

//The system will accomodate for approximate timeouts that don't match your interval, although it will not be extremely
//accurate, and if you want to handle extremely small timeouts, I recommend using an attachment system

//As long as when you change the Interval() you remember to change the appropriate number of places the interval
//goes to, the system should work just peachy

//As is, our system has been set with an Interval() of .01, and the number of places .01 goes to is 2, this you probably
//would like to change to something more practical (maybe .02), but I have no computer handy that has WC3 installed and
//cannot test this

//Function names shortened from 'PeriodicFunction' to 'PeriodFunc', 'TimedFunction' to 'TimedFunc'

//Restrictions

//1, Timeouts are only accurate to the decimal place you have set, [Timeouts must be a multiple of the Interval() constant]

//In our example, since we set it to 2 and our timeout is .01
//These examples are...{ .02 Good, .021 Bad, 2.0 Good, 2.01 Good, 2.001 Bad }

//2, Timeouts cannot be lower than the Interval() constant, a little obvious if you understood restrictions #1
//for 0.0 timeouts, use QUICK_TIMER(), using this would just be dumb

//Example Using Periodic Function

struct GoUp
  unit u
endstruct

function Increase takes nothing returns boolean
    local GoUp d = Temp_GetStruct
    local unit u = d.u
    local real h = GetUnitFlyHeight(u)
    call SetUnitFlyHeight(u, h + 10)
    if h+10 >= 1000 then
      set d.u = null
      call d.destroy()
      return true
    endif
    return false
endfunction

function IncreaseHeight takes unit u returns nothing
    local GoUp d = GoUp.create()
    set d.u = u
    call PeriodFunc(.02, function Increase, d)
endfunction

//Please note that these examples are nonsense, and useless, I needed to make them very quickly, and hence did not make
//anything useful

//Example using Timed Function

struct KillMe
  unit u
endstruct

function Kill takes nothing returns boolean
    local KillMe d = Temp_GetStruct
    call KillUnit(d.u)
    set d.u = null
    call d.destroy()
    return true
endfunction

function KillInTime takes unit u, real time returns nothing
    local KillMe d = KillMe.create()
    set d.u = u
    call TimedFunc(time, function Kill, d)
endfunction

//Notice, you also do not need to create and destroy any timers, which, if your anything like me, leaves you feeling like
//your missing something

//*Again, if you do not put 'return true' anywhere on your timed functions, you will have problems,
//your computer could even freeze!!!*

And the attached textfile of the original

EDIT - I apologize for minor spelling errors, again, I do not have access to a computer with wc3 installed
 

Attachments

  • SingleTimerSystem2.txt
    10 KB · Views: 129

LeP

LeP

Level 13
Joined
Feb 13, 2008
Messages
543
You cant just return-bug code...
Hmm, i cant explain it very goof due my bad english...
Code is worth 1000 words :D
JASS:
private function unsafecode takes integer i returns code
    return i
    return null
endfunction

private function int2code takes integer i returns code
    local code c = unsafecode(i)
    return c
endfunction

private function code2int takes code c returns integer
    return c
    return 0
endfunction

e: http://wc3campaigns.net/showthread.php?t=99738&highlight=int2code
 
Level 12
Joined
Aug 20, 2007
Messages
866
You cant just return-bug code...
Hmm, i cant explain it very goof due my bad english...
Code is worth 1000 words :D
JASS:
private function unsafecode takes integer i returns code
    return i
    return null
endfunction

private function int2code takes integer i returns code
    local code c = unsafecode(i)
    return c
endfunction

private function code2int takes code c returns integer
    return c
    return 0
endfunction

e: Single unit events for any unit - Wc3campaigns

Yes, you can... and the original version did not use the return bug

You can use the pointer bug to store the code as an integer, and when you need to use it, you must set it to a variable, it will not return properly used as a parameter, but it can be set to a variable

Doing it the way you suggested would only slow the code down by one function call, as this does not require the functions usage as a parameter
 
Status
Not open for further replies.
Top