Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New timer functions with lambdas #2843

Closed
wants to merge 14 commits into from
2 changes: 1 addition & 1 deletion data/expression2/tests/runtime/base/lambdas.txt
Original file line number Diff line number Diff line change
Expand Up @@ -84,4 +84,4 @@ function wrapper() {
}

wrapper()
assert(Ran)
assert(Ran)
4 changes: 2 additions & 2 deletions lua/entities/gmod_wire_expression2/base/compiler.lua
Original file line number Diff line number Diff line change
Expand Up @@ -1360,6 +1360,8 @@ local CompileVisitors = {
function(args)
local s_scopes, s_scope, s_scopeid = state.Scopes, state.Scope, state.ScopeID

state.prf = state.prf + 15

local scope = { vclk = {} }
state.Scopes = inherited_scopes
state.ScopeID = after
Expand Down Expand Up @@ -1792,8 +1794,6 @@ local CompileVisitors = {
end
end, ret_type
elseif expr_ty == "f" then
self.scope.data.ops = self.scope.data.ops + 15 -- Since functions are 10 ops, this is pretty lenient. I will decrease this slightly when functions are made static and cheaper.

local nargs = #args
local sig = table.concat(arg_types)

Expand Down
15 changes: 4 additions & 11 deletions lua/entities/gmod_wire_expression2/core/e2lib.lua
Original file line number Diff line number Diff line change
Expand Up @@ -83,20 +83,13 @@ end

E2Lib.Lambda = Function

--- Call the function without doing any type checking.
--- Only use this when you check self:Args() yourself to ensure you have the correct signature function.
--- Call the function without doing any type checking, and outside of the proper entity context.
--- **ONLY USE THIS IF YOU KNOW WHAT YOU'RE DOING.** This could hit perf without actually erroring the chip.
--- Also make sure to check self:Args() yourself to ensure you have the correct signature function.
function Function:UnsafeCall(args)
return self.fn(args)
end

function Function:Call(args, types)
Denneisk marked this conversation as resolved.
Show resolved Hide resolved
if self.arg_sig == types then
return self.fn(args)
else
error("Incorrect arguments passed to lambda")
end
end

function Function:Args()
return self.arg_sig
end
Expand All @@ -105,7 +98,7 @@ function Function:Ret()
return self.ret
end

--- If given the correct arguments, returns the inner untyped function you can call.
--- If given the correct arguments, returns the inner untyped function you can then call with ENT:Execute(f).
--- Otherwise, throws an error to the given E2 Context.
---@param arg_sig string
---@param ctx RuntimeContext
Expand Down
168 changes: 96 additions & 72 deletions lua/entities/gmod_wire_expression2/core/timer.lua
Original file line number Diff line number Diff line change
@@ -1,121 +1,142 @@
/******************************************************************************\
Timer support
\******************************************************************************/
--[[
Timers
]]

local timerid = 0
---@type table<Entity, table<string, true>>
local Timers = {}

local function Execute(self, name)
self.data.timer.runner = name

self.data['timer'].timers[name] = nil

if(self.entity and self.entity.Execute) then
self.entity:Execute()
end

if !self.data['timer'].timers[name] then
timer.Remove("e2_" .. self.data['timer'].timerid .. "_" .. name)
end

self.data.timer.runner = nil
local function addTimer(self, name, delay, reps, callback)
Timers[self][name] = true
timer.Create(("e2timer_%p_%s"):format(self, name), math.max(delay, 1e-2), reps, callback)
end

local function AddTimer(self, name, delay)
if delay < 10 then delay = 10 end

local timerName = "e2_" .. self.data.timer.timerid .. "_" .. name

if self.data.timer.runner == name and timer.Exists(timerName) then
timer.Adjust(timerName, delay / 1000, 2, function()
Execute(self, name)
end)
timer.Start(timerName)
elseif !self.data['timer'].timers[name] then
timer.Create(timerName, delay / 1000, 2, function()
Execute(self, name)
end)
end

self.data['timer'].timers[name] = true
local function removeTimer(self, name)
Timers[self][name] = nil
timer.Remove(("e2timer_%p_%s"):format(self, name))
end

local function RemoveTimer(self, name)
if self.data['timer'].timers[name] then
timer.Remove("e2_" .. self.data['timer'].timerid .. "_" .. name)
self.data['timer'].timers[name] = nil
end
end

/******************************************************************************/

registerCallback("construct", function(self)
self.data['timer'] = {}
self.data['timer'].timerid = timerid
self.data['timer'].timers = {}

timerid = timerid + 1
Timers[self] = {}
end)

registerCallback("destruct", function(self)
for name,_ in pairs(self.data['timer'].timers) do
RemoveTimer(self, name)
for name in pairs(Timers[self]) do
removeTimer(self, name)
end
end)

/******************************************************************************/
Timers[self] = nil
end)

__e2setcost(20)

---@param self RuntimeContext
local function MAKE_TRIGGER(id, self)
return function()
self.data.timer = id
removeTimer(self, id)

if self.entity and self.entity.Execute then
self.entity:Execute()
end

self.data.timer = nil
end
end

[deprecated = "Use the timer function with callbacks instead"]
e2function void interval(rv1)
AddTimer(self, "interval", rv1)
addTimer(self, "interval", rv1 / 1000, 1, MAKE_TRIGGER("interval", self))
end

[deprecated = "Use the timer function with callbacks instead"]
e2function void timer(string rv1, rv2)
AddTimer(self, rv1, rv2)
addTimer(self, rv1, rv2 / 1000, 1, MAKE_TRIGGER(rv1, self))
end

__e2setcost(5)

e2function void stoptimer(string rv1)
RemoveTimer(self, rv1)
removeTimer(self, rv1)
end

__e2setcost(1)

[nodiscard]
[nodiscard, deprecated = "Use the timer function with callbacks instead"]
e2function number clk()
return self.data.timer.runner == "interval" and 1 or 0
return self.data.timer == "interval" and 1 or 0
end

[nodiscard]
[nodiscard, deprecated = "Use the timer function with callbacks instead"]
e2function number clk(string rv1)
return self.data.timer.runner == rv1 and 1 or 0
return self.data.timer == rv1 and 1 or 0
end

[nodiscard]
[nodiscard, deprecated = "Use the timer function with callbacks instead"]
e2function string clkName()
return self.data.timer.runner or ""
return self.data.timer or ""
end

__e2setcost(5)

[deprecated = "Use the timer function with callbacks instead"]
e2function array getTimers()
local ret = {}
local ret, timers = {}, Timers[self]
if not timers then return ret end

self.prf = self.prf + #timers * 2

local i = 0
for name in pairs( self.data.timer.timers ) do
for name in pairs(timers) do
i = i + 1
ret[i] = name
end
self.prf = self.prf + i * 5

return ret
end

e2function void stopAllTimers()
for name in pairs(self.data.timer.timers) do
self.prf = self.prf + 5
RemoveTimer(self,name)
local timers = Timers[self]
if not timers then return end

self.prf = self.prf + #timers * 2

for name in pairs(timers) do
removeTimer(self, name)
end
end

/******************************************************************************/
--[[
Timers 2.0
]]

__e2setcost(10)

-- Create "anonymous" timer using address of arguments, which should be different for each function call.
-- Definitely hacky, but should work properly. I think this is better than just incrementing a number infinitely.
e2function void timer(number delay, function callback)
local fn, ent = callback:Unwrap("", self), self.entity
addTimer(self,("%p"):format(args), delay, 1, function()
ent:Execute(fn)
Denneisk marked this conversation as resolved.
Show resolved Hide resolved
end)
end

e2function void timer(string name, number delay, function callback)
local fn, ent = callback:Unwrap("", self), self.entity
addTimer(self, name, delay, 1, function()
ent:Execute(fn)
end)
end

e2function void timer(string name, number delay, number reps, function callback)
local fn, ent = callback:Unwrap("", self), self.entity
addTimer(self, name, delay, reps, function()
ent:Execute(fn)
end)
end

--[[
Time Monitoring
]]

[nodiscard]
e2function number curtime()
Expand All @@ -132,7 +153,9 @@ e2function number systime()
return SysTime()
end

-----------------------------------------------------------------------------------
--[[
Datetime
]]

local function luaDateToE2Table( time, utc )
local ret = E2Lib.newE2Table()
Expand Down Expand Up @@ -170,13 +193,14 @@ e2function table dateUTC()
return luaDateToE2Table(nil,true)
end

-- Returns the specified time formatted neatly in a table using UTC
[nodiscard]
e2function table dateUTC( time )
return luaDateToE2Table(time,true)
end

-- This function has a strange and slightly misleading name, but changing it might break older E2s, so I'm leaving it
-- It's essentially the same as the date function above
[nodiscard]
e2function number time(string component)
local ostime = os.date("!*t")
local ret = ostime[component]
Expand All @@ -188,7 +212,7 @@ end
-----------------------------------------------------------------------------------

__e2setcost(2)
-- Returns the time in seconds

[nodiscard]
e2function number time()
return os.time()
Expand Down
9 changes: 7 additions & 2 deletions lua/entities/gmod_wire_expression2/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -111,9 +111,12 @@ function ENT:UpdatePerf()

end

function ENT:Execute()
function ENT:Execute(script, args)
if self.error or not self.context or self.context.resetting then return end

script = script or self.script
args = args or self.context

self:PCallHook('preexecute')

self.context.stackdepth = self.context.stackdepth + 1
Expand All @@ -124,7 +127,7 @@ function ENT:Execute()

local bench = SysTime()

local ok, msg = pcall(self.script, self.context)
local ok, msg = pcall(script, args)

if not ok then
local _catchable, msg, trace = E2Lib.unpackException(msg)
Expand Down Expand Up @@ -180,6 +183,8 @@ function ENT:Execute()
if self.error then
self:Destruct()
end

return msg
end

---@param evt string
Expand Down
6 changes: 6 additions & 0 deletions lua/wire/client/e2descriptions.lua
Original file line number Diff line number Diff line change
Expand Up @@ -942,9 +942,15 @@ E2Helper.Descriptions["getTimers()"] = "Returns an array of all timers used in t
E2Helper.Descriptions["interval(n)"] = "Sets a one-time timer with name \"interval\" and delay in milliseconds (minimum delay for timers is 10ms)"
E2Helper.Descriptions["runOnTick(n)"] = "If set to 1, the expression will execute once every game tick"
E2Helper.Descriptions["timer(sn)"] = "Sets a one-time timer with entered name and delay in milliseconds"

E2Helper.Descriptions["stoptimer(s)"] = "Stops a timer, can stop interval with stoptimer(\"interval\")"
E2Helper.Descriptions["stopAllTimers()"] = "Stops all timers"

-- Timers 2.0
E2Helper.Descriptions["timer(nf)"] = "Sets a callback to run after n seconds"
E2Helper.Descriptions["timer(snf)"] = "Sets a named timer to run a callback after n seconds"
E2Helper.Descriptions["timer(snnf)"] = "Sets a named timer to run a callback after n seconds, repeating n2 times"

-- Unit conversion
E2Helper.Descriptions["toUnit(sn)"] = "Converts default garrysmod units to specified units"
E2Helper.Descriptions["fromUnit(sn)"] = "Converts specified units to default garrysmod units"
Expand Down
Loading