2022-04-12 23:28:25 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2022-05-28 23:03:44 +00:00
|
|
|
"fmt"
|
2022-04-12 23:28:25 +00:00
|
|
|
"sync"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"hilbish/util"
|
|
|
|
|
|
|
|
rt "github.com/arnodel/golua/runtime"
|
|
|
|
)
|
|
|
|
|
2022-12-15 04:00:54 +00:00
|
|
|
var timers *timersModule
|
2022-05-28 23:03:44 +00:00
|
|
|
var timerMetaKey = rt.StringValue("hshtimer")
|
|
|
|
|
2022-12-15 04:00:54 +00:00
|
|
|
type timersModule struct {
|
2022-04-12 23:28:25 +00:00
|
|
|
mu *sync.RWMutex
|
2022-05-22 01:58:58 +00:00
|
|
|
wg *sync.WaitGroup
|
2022-04-12 23:28:25 +00:00
|
|
|
timers map[int]*timer
|
|
|
|
latestID int
|
|
|
|
running int
|
|
|
|
}
|
|
|
|
|
2022-12-15 04:00:54 +00:00
|
|
|
func newTimersModule() *timersModule {
|
|
|
|
return &timersModule{
|
2022-04-12 23:28:25 +00:00
|
|
|
timers: make(map[int]*timer),
|
|
|
|
latestID: 0,
|
|
|
|
mu: &sync.RWMutex{},
|
2022-05-22 01:58:58 +00:00
|
|
|
wg: &sync.WaitGroup{},
|
2022-04-12 23:28:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-12-15 04:00:54 +00:00
|
|
|
func (th *timersModule) wait() {
|
2022-05-22 01:58:58 +00:00
|
|
|
th.wg.Wait()
|
|
|
|
}
|
|
|
|
|
2022-12-15 04:00:54 +00:00
|
|
|
func (th *timersModule) create(typ timerType, dur time.Duration, fun *rt.Closure) *timer {
|
2022-04-12 23:28:25 +00:00
|
|
|
th.mu.Lock()
|
|
|
|
defer th.mu.Unlock()
|
|
|
|
|
|
|
|
th.latestID++
|
|
|
|
t := &timer{
|
|
|
|
typ: typ,
|
|
|
|
fun: fun,
|
|
|
|
dur: dur,
|
2022-05-22 01:39:14 +00:00
|
|
|
channel: make(chan struct{}, 1),
|
2022-04-12 23:28:25 +00:00
|
|
|
th: th,
|
|
|
|
id: th.latestID,
|
|
|
|
}
|
2022-05-28 23:03:44 +00:00
|
|
|
t.ud = timerUserData(t)
|
|
|
|
|
2022-04-12 23:28:25 +00:00
|
|
|
th.timers[th.latestID] = t
|
|
|
|
|
|
|
|
return t
|
|
|
|
}
|
|
|
|
|
2022-12-15 04:00:54 +00:00
|
|
|
func (th *timersModule) get(id int) *timer {
|
2022-04-12 23:28:25 +00:00
|
|
|
th.mu.RLock()
|
|
|
|
defer th.mu.RUnlock()
|
|
|
|
|
|
|
|
return th.timers[id]
|
|
|
|
}
|
|
|
|
|
2022-12-15 04:00:54 +00:00
|
|
|
// #interface timers
|
|
|
|
// create(type, time, callback)
|
|
|
|
// Creates a timer that runs based on the specified `time` in milliseconds.
|
2022-12-21 00:59:55 +00:00
|
|
|
// The `type` can either be `hilbish.timers.INTERVAL` or `hilbish.timers.TIMEOUT`
|
|
|
|
// --- @param type number
|
|
|
|
// --- @param time number
|
|
|
|
// --- @param callback function
|
2022-12-15 04:00:54 +00:00
|
|
|
func (th *timersModule) luaCreate(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
2022-04-12 23:28:25 +00:00
|
|
|
if err := c.CheckNArgs(3); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
timerTypInt, err := c.IntArg(0)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
ms, err := c.IntArg(1)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
cb, err := c.ClosureArg(2)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
timerTyp := timerType(timerTypInt)
|
|
|
|
tmr := th.create(timerTyp, time.Duration(ms) * time.Millisecond, cb)
|
2022-05-28 23:03:44 +00:00
|
|
|
return c.PushingNext1(t.Runtime, rt.UserDataValue(tmr.ud)), nil
|
2022-04-12 23:28:25 +00:00
|
|
|
}
|
|
|
|
|
2022-12-15 04:00:54 +00:00
|
|
|
// #interface timers
|
2022-12-21 00:59:55 +00:00
|
|
|
// get(id) -> timer (Timer/Table)
|
2022-12-15 04:00:54 +00:00
|
|
|
// Retrieves a timer via its ID.
|
2022-12-21 00:59:55 +00:00
|
|
|
// --- @param id number
|
|
|
|
// --- @returns Timer
|
2022-12-15 04:00:54 +00:00
|
|
|
func (th *timersModule) luaGet(thr *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
2022-04-12 23:28:25 +00:00
|
|
|
if err := c.Check1Arg(); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
id, err := c.IntArg(0)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
t := th.get(int(id))
|
|
|
|
if t != nil {
|
2022-05-28 23:03:44 +00:00
|
|
|
return c.PushingNext1(thr.Runtime, rt.UserDataValue(t.ud)), nil
|
2022-04-12 23:28:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return c.Next(), nil
|
|
|
|
}
|
|
|
|
|
2022-12-15 04:00:54 +00:00
|
|
|
// #interface timers
|
2022-12-21 00:55:56 +00:00
|
|
|
// #field INTERVAL Constant for an interval timer type
|
|
|
|
// #field TIMEOUT Constant for a timeout timer type
|
2022-12-15 04:00:54 +00:00
|
|
|
// #property type What type of timer it is
|
|
|
|
// #property running If the timer is running
|
|
|
|
// #property duration The duration in milliseconds that the timer will run
|
|
|
|
// timeout and interval API
|
2022-12-28 23:32:52 +00:00
|
|
|
/*
|
|
|
|
If you ever want to run a piece of code on a timed interval, or want to wait
|
|
|
|
a few seconds, you don't have to rely on timing tricks, as Hilbish has a
|
|
|
|
timer API to set intervals and timeouts.
|
|
|
|
|
|
|
|
These are the simple functions `hilbish.interval` and `hilbish.timeout` (doc
|
|
|
|
accessible with `doc hilbish`). But if you want slightly more control over
|
|
|
|
them, there is the `hilbish.timers` interface. It allows you to get
|
|
|
|
a timer via ID and control them.
|
|
|
|
|
|
|
|
## Timer Object
|
|
|
|
All functions documented with the `Timer` type refer to a Timer object.
|
|
|
|
|
|
|
|
An example of usage:
|
|
|
|
```
|
|
|
|
local t = hilbish.timers.create(1, 5000, function()
|
|
|
|
print 'hello!'
|
|
|
|
end)
|
|
|
|
```
|
|
|
|
|
|
|
|
t:stop()
|
|
|
|
print(t.running, t.duration, t.type)
|
|
|
|
t:start()
|
|
|
|
*/
|
2022-12-15 04:00:54 +00:00
|
|
|
func (th *timersModule) loader(rtm *rt.Runtime) *rt.Table {
|
2022-05-28 23:03:44 +00:00
|
|
|
timerMethods := rt.NewTable()
|
|
|
|
timerFuncs := map[string]util.LuaExport{
|
|
|
|
"start": {timerStart, 1, false},
|
|
|
|
"stop": {timerStop, 1, false},
|
|
|
|
}
|
|
|
|
util.SetExports(rtm, timerMethods, timerFuncs)
|
|
|
|
|
|
|
|
timerMeta := rt.NewTable()
|
|
|
|
timerIndex := func(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
|
|
|
ti, _ := timerArg(c, 0)
|
|
|
|
|
|
|
|
arg := c.Arg(1)
|
|
|
|
val := timerMethods.Get(arg)
|
|
|
|
|
|
|
|
if val != rt.NilValue {
|
|
|
|
return c.PushingNext1(t.Runtime, val), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
keyStr, _ := arg.TryString()
|
|
|
|
|
|
|
|
switch keyStr {
|
|
|
|
case "type": val = rt.IntValue(int64(ti.typ))
|
|
|
|
case "running": val = rt.BoolValue(ti.running)
|
|
|
|
case "duration": val = rt.IntValue(int64(ti.dur / time.Millisecond))
|
|
|
|
}
|
|
|
|
|
|
|
|
return c.PushingNext1(t.Runtime, val), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
timerMeta.Set(rt.StringValue("__index"), rt.FunctionValue(rt.NewGoFunction(timerIndex, "__index", 2, false)))
|
|
|
|
l.SetRegistry(timerMetaKey, rt.TableValue(timerMeta))
|
|
|
|
|
2022-04-12 23:28:25 +00:00
|
|
|
thExports := map[string]util.LuaExport{
|
|
|
|
"create": {th.luaCreate, 3, false},
|
|
|
|
"get": {th.luaGet, 1, false},
|
|
|
|
}
|
|
|
|
|
|
|
|
luaTh := rt.NewTable()
|
|
|
|
util.SetExports(rtm, luaTh, thExports)
|
|
|
|
|
2022-12-21 00:55:56 +00:00
|
|
|
util.SetField(rtm, luaTh, "INTERVAL", rt.IntValue(0))
|
|
|
|
util.SetField(rtm, luaTh, "TIMEOUT", rt.IntValue(1))
|
|
|
|
|
2022-04-12 23:28:25 +00:00
|
|
|
return luaTh
|
|
|
|
}
|
2022-05-28 23:03:44 +00:00
|
|
|
|
|
|
|
func timerArg(c *rt.GoCont, arg int) (*timer, error) {
|
|
|
|
j, ok := valueToTimer(c.Arg(arg))
|
|
|
|
if !ok {
|
|
|
|
return nil, fmt.Errorf("#%d must be a timer", arg + 1)
|
|
|
|
}
|
|
|
|
|
|
|
|
return j, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func valueToTimer(val rt.Value) (*timer, bool) {
|
|
|
|
u, ok := val.TryUserData()
|
|
|
|
if !ok {
|
|
|
|
return nil, false
|
|
|
|
}
|
|
|
|
|
|
|
|
j, ok := u.Value().(*timer)
|
|
|
|
return j, ok
|
|
|
|
}
|
|
|
|
|
|
|
|
func timerUserData(j *timer) *rt.UserData {
|
|
|
|
timerMeta := l.Registry(timerMetaKey)
|
|
|
|
return rt.NewUserData(j, timerMeta.AsTable())
|
|
|
|
}
|