Skip to content

Commit

Permalink
Lambda timers (#3142)
Browse files Browse the repository at this point in the history
* init

* Add getters + adjust method

* deprecate original timers

* Remove trailing whitespace from e2lib.lua

* Bring back previously removed code lol

* Get Vurv's internals implementations

* Use ent:Execute instead of UnsafeExtCall

* Slight indexing refactor

* Update e2descriptions.lua

* fix

* Return UpdatePerf dep injection in ENT:Execute()

* Use args instead of selfTbl.context when provided

* Update init.lua
  • Loading branch information
deltamolfar authored Nov 1, 2024
1 parent a67cd48 commit 1a7fa75
Show file tree
Hide file tree
Showing 5 changed files with 277 additions and 17 deletions.
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 @@ -1440,6 +1440,8 @@ local CompileVisitors = {
function(args)
local s_scopes, s_scope, s_scopeid = state.Scopes, state.Scope, state.ScopeID

state.prf = state.prf + 10

local scope = { vclk = {} }
state.Scopes = inherited_scopes
state.ScopeID = after
Expand Down Expand Up @@ -1872,8 +1874,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
4 changes: 2 additions & 2 deletions lua/entities/gmod_wire_expression2/core/e2lib.lua
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,7 @@ end
function Function:ExtCall(args, types, ctx)
if self.arg_sig == types then
local success,ret = pcall(self.fn,args)
if success then
if success then
return ret
else
local _,msg,trace = E2Lib.unpackException(ret)
Expand All @@ -291,7 +291,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
251 changes: 246 additions & 5 deletions lua/entities/gmod_wire_expression2/core/timer.lua
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,79 @@ local function RemoveTimer(self, name)
end
end

-- Lambda timers

local luaTimers = {
/*EXAMPLE:
'[342]e2entity' = {
[342]e2entity_gmod_wire_expression2_luatimer_examplename = {
context = {...} (e2 context),
callback = {...} (e2 callback),
delay = 1,
repetitions = 1
}
}
*/
}

local luaTimerIncrementalKeys = {}

local function luaTimerGetNextIncrementalKey(self)
local key = (luaTimerIncrementalKeys[self.entity:EntIndex()] or 0)+1
luaTimerIncrementalKeys[self.entity:EntIndex()] = key
return key
end

local function luaTimerGetInternalName(entIndex, name)
return entIndex .. '_gmod_wire_expression2_luatimer_' .. name
end

local function luaTimerExists(self, name)
local tbl = luaTimers[self.entity:EntIndex()]
return tbl and tbl[name] and true or false
end

local function luaTimerCreate(self, name, delay, repetitions, callback)
local entIndex = self.entity:EntIndex()

if not luaTimers[entIndex] then
luaTimers[entIndex] = {}
elseif luaTimerExists(self, name) then
return self:throw("Timer with name " .. name .. " already exists", nil)
end

local internalName = luaTimerGetInternalName(self.entity:EntIndex(), name)
local callback, ent = callback:Unwrap("", self), self.entity

luaTimers[entIndex][name] = {
ent = ent,
callback = callback,
delay = delay,
repetitions = repetitions
}

timer.Create(internalName, delay, repetitions, function()
ent:Execute(callback)

if timer.RepsLeft(internalName) == 0 then
luaTimers[name] = nil
end
end)
end

local function luaTimerRemove(self, name)
local entIndex = self.entity:EntIndex()
if not luaTimers[entIndex] then
luaTimers[entIndex] = {}
return self:throw("Timer with name " .. name .. " does not exist", nil)
elseif not luaTimerExists(self, name) then
return self:throw("Timer with name " .. name .. " does not exist", nil)
end

timer.Remove(luaTimerGetInternalName(self.entity:EntIndex(), name))
luaTimers[entIndex][name] = nil
end

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

registerCallback("construct", function(self)
Expand All @@ -60,39 +133,47 @@ registerCallback("destruct", function(self)
for name,_ in pairs(self.data['timer'].timers) do
RemoveTimer(self, name)
end

local entIndex = self.entity:EntIndex()
for k, _ in pairs(luaTimers[entIndex] or {}) do
timer.Remove(luaTimerGetInternalName(entIndex, k))
end

luaTimers[entIndex] = nil
end)

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

__e2setcost(20)

[deprecated = "Use lambda timers instead"]
e2function void interval(rv1)
AddTimer(self, "interval", rv1)
end

[deprecated = "Use lambda timers instead"]
e2function void timer(string rv1, rv2)
AddTimer(self, rv1, rv2)
end

__e2setcost(5)

e2function void stoptimer(string rv1)
RemoveTimer(self, rv1)
pcall(luaTimerRemove, self, rv1)
end

__e2setcost(1)

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

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

[nodiscard]
[nodiscard, deprecated = "Use lambda timers instead"]
e2function string clkName()
return self.data.timer.runner or ""
end
Expand All @@ -104,6 +185,12 @@ e2function array getTimers()
i = i + 1
ret[i] = name
end

for k, _ in pairs( luaTimers[self.entity:EntIndex()] or {} ) do
i = i + 1
ret[i] = k
end

self.prf = self.prf + i * 5
return ret
end
Expand All @@ -113,10 +200,164 @@ e2function void stopAllTimers()
self.prf = self.prf + 5
RemoveTimer(self,name)
end

for k, _ in pairs(luaTimers[self.entity:EntIndex()] or {}) do
self.prf = self.prf + 5
luaTimerRemove(self, k)
end
end

/******************************************************************************/
-- Lambda timers

__e2setcost(10)
e2function void timer(string name, number delay, number repetitions, function callback)
luaTimerCreate(self, name, delay, repetitions, callback)
end

e2function string timer(number delay, number repetitions, function callback)
local name = "simpletimer_"..luaTimerGetNextIncrementalKey(self)
luaTimerCreate(self, name, delay, repetitions, callback)
return name
end

e2function string timer(number delay, function callback)
local name = "simpletimer_"..luaTimerGetNextIncrementalKey(self)
luaTimerCreate(self, name, delay, 1, callback)
return name
end

e2function void timer(string name, number delay, function callback)
luaTimerCreate(self, name, delay, 1, callback)
end

__e2setcost(5)
e2function void timerSetDelay(string name, number delay)
if not luaTimerExists(self, name) then
return self:throw("Timer with name " .. name .. " does not exist", nil)
end

local entIndex = self.entity:EntIndex()
luaTimers[entIndex][name].delay = delay
timer.Adjust(luaTimerGetInternalName(entIndex, name), delay, 0)
end

e2function number timerSetReps(string name, number repetitions)
if not luaTimerExists(self, name) then
return self:throw("Timer with name " .. name .. " does not exist", nil)
end

local entIndex = self.entity:EntIndex()
luaTimers[entIndex][name].repetitions = repetitions
timer.Adjust(luaTimerGetInternalName(entIndex, name), luaTimers[entIndex][name].delay, repetitions)
end

e2function void timerAdjust(string name, number delay, number repetitions)
if not luaTimerExists(self, name) then
return self:throw("Timer with name " .. name .. " does not exist", nil)
end

local entIndex = self.entity:EntIndex()
luaTimers[entIndex][name].delay = delay
luaTimers[entIndex][name].repetitions = repetitions
timer.Adjust(luaTimerGetInternalName(entIndex, name), delay, repetitions)
end


__e2setcost(1)
[nodiscard]
e2function number timerGetDelay(string name)
if not luaTimerExists(self, name) then
return self:throw("Timer with name " .. name .. " does not exist", 0)
end

return luaTimers[self.entity:EntIndex()][name].delay
end

[nodiscard]
e2function number timerGetReps(string name)
if not luaTimerExists(self, name) then
return self:throw("Timer with name " .. name .. " does not exist", 0)
end

return luaTimers[self.entity:EntIndex()][name].repetitions
end

[nodiscard]
e2function function timerGetCallback(string name)
if not luaTimerExists(self, name) then
return self:throw("Timer with name " .. name .. " does not exist", "")
end

return luaTimers[self.entity:EntIndex()][name].callback
end

__e2setcost(5)
e2function void timerRestart(string name)
if not luaTimerExists(self, name) then
return self:throw("Timer with name " .. name .. " does not exist", nil)
end

local entIndex = self.entity:EntIndex()
local internalName = luaTimerGetInternalName(entIndex, name)

timer.Adjust(internalName, luaTimers[entIndex][name].delay, luaTimers[entIndex][name].repetitions)
end

__e2setcost(1)
[nodiscard]
e2function number timerExists(string name)
return luaTimerExists(self, name) and 1 or 0
end

e2function void timerPause(string name)
if not luaTimerExists(self, name) then
return self:throw("Timer with name " .. name .. " does not exist", 0)
end

--return timer.Pause(luaTimerGetInternalName(self.entity:EntIndex(), name)) and 1 or 0 -- This is commented due to timer.Pause being broken for some reason. It just does not return anything.
timer.Pause(luaTimerGetInternalName(self.entity:EntIndex(), name))
end

e2function void timerResume(string name)
if not luaTimerExists(self, name) then
return self:throw("Timer with name " .. name .. " does not exist", 0)
end

-- return timer.UnPause(luaTimerGetInternalName(self.entity:EntIndex(), name)) and 1 or 0 -- This is commented due to timer.Pause being broken for some reason. It just does not return anything.
timer.UnPause(luaTimerGetInternalName(self.entity:EntIndex(), name))
end

e2function number timerToggle(string name)
if not luaTimerExists(self, name) then
return self:throw("Timer with name " .. name .. " does not exist", 0)
end

--return timer.Toggle(luaTimerGetInternalName(self.entity:EntIndex(), name)) and 1 or 0 -- This is commented due to timer.Pause being broken for some reason. It just does not return anything.
timer.Toggle(luaTimerGetInternalName(self.entity:EntIndex(), name))
end

__e2setcost(5)
[nodiscard]
e2function number timerRepsLeft(string name)
if not luaTimerExists(self, name) then
return self:throw("Timer with name " .. name .. " does not exist", 0)
end

return timer.RepsLeft(luaTimerGetInternalName(self.entity:EntIndex(), name))
end

[nodiscard]
e2function number timerTimeLeft(string name)
if not luaTimerExists(self, name) then
return self:throw("Timer with name " .. name .. " does not exist", 0)
end

return timer.TimeLeft(luaTimerGetInternalName(self.entity:EntIndex(), name))
end

/******************************************************************************/
__e2setcost(1)
[nodiscard]
e2function number curtime()
return CurTime()
Expand Down
7 changes: 4 additions & 3 deletions lua/entities/gmod_wire_expression2/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -116,9 +116,10 @@ function ENT:UpdatePerf(selfTbl)
context.time = 0
end

function ENT:Execute()
function ENT:Execute(script, context)
local selfTbl = self:GetTable()
local context = selfTbl.context
context = context or selfTbl.context
script = script or selfTbl.script
if not context or selfTbl.error or context.resetting then return end

self:PCallHook("preexecute")
Expand All @@ -131,7 +132,7 @@ function ENT:Execute()

local bench = SysTime()

local ok, msg = pcall(selfTbl.script, context)
local ok, msg = pcall(script, context)

if not ok then
local _catchable, msg, trace = E2Lib.unpackException(msg)
Expand Down
Loading

0 comments on commit 1a7fa75

Please sign in to comment.