标题：TimerDataSystem
简介
TimerDataSystem是魔兽地图编辑器脚本Jass2中给预创建的计时器绑定数据执行动作的一个系统。
DataSystem的概念起源于2005年的ReturnBug+GC时代，因为GC的读写效率不佳，而产生以GC储存数组编号，再以变量数组做为主要储存空间的技术，因为变量数组的读写速度是最快的，即使是现在的HashTable也比不上变量数组，所以DataSystem目前仍是最优秀的数据储存系统。至于为什么这种东西可以叫做DataSystem，至今已不可考，反正魔兽地图高手们口耳相传的都是这个词，所以久而久之也没有人去质疑为什么这东西叫做DataSystem。
效率对比
1.24下    TimerDataSystem 与 timer+hashtable方法对比:
高频创建删除每0.01秒循环创建N个循环10次的0秒计时器，以某古董机的FPS到5为准。

NFPSTimerDataSystem305timer + hashtable2~3 (2.7)5同时执行测试N个循环周期为0.001秒的计时器不停循环，以某古董机FPS到5为准。

NFPSTimerDataSystem545timer + hashtable465
War3版本兼容性
1.24可以直接使用TDS，而对于1.20的war3，只需要实现GetHandleId就可以。
function GetHandleId takes handle h returns integer
return h
return 174
endfunction
另外还有方法使TimerDataSystem完全支持1.20与1.24，在词条最后。
实现原理
TimerDataSystem需要预创建计时器，在地图初始化时创建足够地图使用的计时器数量（同时执行计时器的最大数量）然后用数组来存取数据，每次使用时创建不重复的索引作为数组下标，在TimerStart所调用的函数中可以获得这个索引来达到传递数据的目的。但因为是数组，所以有着最大下标8191的限制，但是我相信没有人的地图里会同时执行着8000多个计时器吧。
那么我们来看看TDS是怎样工作的：
假设我们有一个函数来获得不重复的索引：CreateTimerId
然后拥有一个函数可以在TimerStart执行的函数内获得这个索引：GetTimerId
最后还有一个函数可以释放这个索引，以便重复使用：DestroyTimerId
于是我们就可以用这个索引+数组来存储数据，写出来的代码会是下面这样：
function test_Action takes nothing returns nothing
local integer id=GetTimerId() //获得计时器所对应的索引
call BJDebugMsg(I2S(udg_int1[id]))//使用数据做动作
call DestroyTimerId(id) //使用完毕释放索引
endfunction
function test takes nothing returns nothing
local integer id=CreateTimerId() //获得不重复索引做下标
set udg_int1[id]=123 //使用专用的数组来存储数据
call TimerStart(udg_timer[id],0.01,false,function test_Action) //开启预创建的计时器
endfunction
数据被专用的数组存储着，在被计时器运行的函数内也可以获得正确的索引来做动作。ID回收CreateTimerId与DestroyTimerId使用id回收机制，记录id被回收的顺序，再次创建时首先创建到被删除的id的位置，实现起来较简单，如下：
全局变量: Top  Last  Used
function CreateTimerId takes nothing returns integer
set Top = Top + 1
if Last[Top] == 0 then //若无删除的id,则直接返回
set Used[Top] = true
return Top
endif
set Used[Last[Top]] = true
return Last[Top] //否则返回被删除的id
endfunction
function DestroyTimerId takes integer tid returns nothing
if Used[tid] then //只对占用的id操作
call PauseTimer(Timer[tid])
set Last[Top] = tid //记录被删除的id
set Used[tid] = false
set Top = Top - 1
endif
endfunction获取索引而GetTimerId却有许多不同的做法，在计时器执行的函数内，唯一可以捕获的是到期的计时器，所以只能从计时器上来做文章。这里主要介绍两种方法：
一、将对应索引绑定到预创建的计时器上。
我们知道GC的读取速度与变量相差无几，所以可以在计时器预创建时将其所对应的索引绑定到计时器上。
1.20:
function GetTimerId takes nothing returns integer
return GetStoredInteger(udg_GC,"TDS",GetHandleId(GetExpiredTimer()))
endfunction
而在1.24+的版本中可以用hashtable
function GetTimerId takes nothing returns integer
return LoadInteger(udg_HT,'_TDS',GetHandleId(GetExpiredTimer()))
endfunction
二、这里有一种更为巧妙的办法，1.20与1.24通用：根据地图初始化时，假如没有被删除的handle，创建的handle的id都是连续的，于是只需要知道所创建的第一个timer的handle值，任何一个timer的索引就可以用handleid-首handleid来得到：
function GetTimerId takes nothing returns integer
return GetHandleId(GetExpiredTimer()) - FirstTimerId
endfunction
这种方法的效率最高，但是使用时注意所有要删除handle的动作安排在TDS初始化之后吧。
library版实现
代码//    使用方法:
//    local integer id=CreateTimerId() //建立索引
//    set TDS_integer1[id]=123 //存储数据
//    call TimerStart(TDS_Timer[id], 0.03, true, function xxxx) //启动计时器
//    ---------------
//    local integer id=GetTimerId() //获得索引
//    call DestroyTimerId(id) //删除索引
library TDS initializer TDS_init
globals
private integer FirstTimerId
private integer Top=0
private integer array Last
public timer array Timer
private boolean array Used
private boolean array Const
//---------------------以下为存储数据使用的数组
public integer array integer1
public integer array integer2
public player array player1
public boolean array boolean1
public real array real1
public unit array unit1
public string array string1
public texttag array texttag1
endglobals
function CreateTimerId takes nothing returns integer
set Top = Top + 1
if Last[Top] == 0 then
set Used[Top] = true
return Top
endif
set Used[Last[Top]] = true
return Last[Top]
endfunction
function CreateConstTimerId takes nothing returns integer
//创建一个永久id,,(在任何情况下不会被DestroyTimerId删除
set Top = Top + 1
if Last[Top] == 0 then
set Used[Top] = true
set Const[Top]=true
return Top
endif
set Used[Last[Top]] = true
set Const[Last[Top]]=true
return Last[Top]
endfunction
function GetTimerId takes nothing returns integer
return GetHandleId(GetExpiredTimer()) - FirstTimerId
endfunction
function DestroyTimerId takes integer tid returns nothing
if Const[tid] then
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"|cffff8080>>>>>>> DestroyTimerId试图删除永久性id被禁止 id="+I2S(tid)+"|r")
elseif Used[tid] then //只对占用的id操作
call PauseTimer(Timer[tid])
set Last[Top] = tid
set Used[tid] = false
set Top = Top - 1
endif
endfunction
private function TDS_init takes nothing returns nothing
local integer c = 1
local location l = Location(0,0)
set FirstTimerId = GetHandleId(l)
call RemoveLocation(l)
set l = null
loop
exitwhen c >= 8000
set Timer[c] = CreateTimer()
set c = c + 1
endloop
endfunction
endlibrary实例//倒计时函数
//call CountDown(3,"秒后游戏开始")
function CountDownAction takes nothing returns nothing
local integer id=GetTimerId()
set TDS_integer1[id]=TDS_integer1[id]-1
if TDS_integer1[id]<0 then
call DestroyTimerId(id)
else
call DisplayTextToPlayer(GetLocalPlayer(),0,0,I2S(TDS_integer1[id])+TDS_string1[id])
endif
endfunction
function CountDown takes integer n,string text returns nothing
local integer id=CreateTimerId()
set TDS_integer1[id]=n
set TDS_string1[id]=text
call DisplayTextToPlayer(GetLocalPlayer(),0,0,I2S(TDS_integer1[id])+TDS_string1[id])
call TimerStart(TDS_Timer[id],1,true,function CountDownAction)
endfunction
struct版实现
代码//    使用方法:
//    local Timer t=Timer.Create() //创建
//    set t.integer1=123    //存储数据
//    call t.Start(0.03, true, function xxx)     //启动计时
//    ------------------
//    local Timer t=Timer.Get()    //函数内获得
//    if (t.integer>0) then    //使用数据
//        call t.Destroy()    //释放
//    endif
globals
constant integer Timer__Capacity=8000
endglobals
struct Timer [Timer__Capacity]
//------自定义数据------
integer integer1
integer integer2
player player1
boolean boolean1
string string1
//----------------------
boolean Const //改变此值可以使之不会被删除 
static integer FirstTimerId
static timer array Timers [Timer__Capacity]
method Start takes real timeout, boolean periodic, code handlerFunc returns nothing
call TimerStart(Timers[this],timeout,periodic,handlerFunc)
endmethod
static method Get takes nothing returns thistype
//return LoadInteger(ht,'_tid',GetHandleId(GetExpiredTimer()))
return GetHandleId(GetExpiredTimer()) - Timer.FirstTimerId
endmethod
static method Create takes nothing returns thistype
return Timer.allocate()
endmethod
method Destroy takes nothing returns nothing
if this.Const then
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"|cffff8080>>>>>>> Timer.Destroy试图删除永久性id被禁止 id="+I2S(this)+"|r")
else
call PauseTimer(Timers[this])
call this.deallocate()
endif
endmethod
method destroy takes nothing returns nothing
call this.Destroy()
endmethod
static method onInit takes nothing returns nothing
local integer c = 1
local location l = Location(0,0)
set Timer.FirstTimerId = GetHandleId(l)
call RemoveLocation(l)
set l = null
loop
exitwhen c > Timer__Capacity
set Timer.Timers[c] = CreateTimer()
//call SaveInteger(ht,'_tid',GetHandleId(Timer.Timers[c]),c)
set c = c + 1
endloop
endmethod
endstruct实例//倒计时函数
//call CountDown(3,"秒后游戏开始")
function CountDownAction takes nothing returns nothing
local Timer t=Timer.Get()
set t.integer1=t.integer1-1
if t.integer1<0 then
call t.Destroy()
else
call DisplayTextToPlayer(GetLocalPlayer(),0,0,I2S(t.integer1)+t.string1)
endif
endfunction
function CountDown takes integer n,string text returns nothing
local Timer t=Timer.Create()
set t.integer1=n
set t.string1=text
call DisplayTextToPlayer(GetLocalPlayer(),0,0,I2S(t.integer1)+t.string1)
call t.Start(1,true,function CountDownAction)
endfunction
注意事项
TDS的索引使用完毕后应该删除，以便释放id下次使用。否则会造成泄漏，而有可能超过预创建的计时器数量。