diff --git a/lua/entities/gmod_wire_expression2/base/compiler.lua b/lua/entities/gmod_wire_expression2/base/compiler.lua index 8f7a5fa83a..202faba047 100644 --- a/lua/entities/gmod_wire_expression2/base/compiler.lua +++ b/lua/entities/gmod_wire_expression2/base/compiler.lua @@ -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 @@ -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) diff --git a/lua/entities/gmod_wire_expression2/core/e2lib.lua b/lua/entities/gmod_wire_expression2/core/e2lib.lua index 167bf434b9..0449bc7bee 100644 --- a/lua/entities/gmod_wire_expression2/core/e2lib.lua +++ b/lua/entities/gmod_wire_expression2/core/e2lib.lua @@ -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) @@ -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 diff --git a/lua/entities/gmod_wire_expression2/core/timer.lua b/lua/entities/gmod_wire_expression2/core/timer.lua index a2c1b97550..4b8679b4b2 100644 --- a/lua/entities/gmod_wire_expression2/core/timer.lua +++ b/lua/entities/gmod_wire_expression2/core/timer.lua @@ -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) @@ -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 @@ -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 @@ -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() diff --git a/lua/entities/gmod_wire_expression2/init.lua b/lua/entities/gmod_wire_expression2/init.lua index 3f8fe34db9..b21b3db219 100644 --- a/lua/entities/gmod_wire_expression2/init.lua +++ b/lua/entities/gmod_wire_expression2/init.lua @@ -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") @@ -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) diff --git a/lua/wire/client/e2descriptions.lua b/lua/wire/client/e2descriptions.lua index 39b6a0e5db..a95190f286 100644 --- a/lua/wire/client/e2descriptions.lua +++ b/lua/wire/client/e2descriptions.lua @@ -967,15 +967,33 @@ E2Helper.Descriptions["tickInterval()"] = "Returns the time (in seconds) between E2Helper.Descriptions["curtime()"] = "Returns the current game time since server-start in seconds" E2Helper.Descriptions["realtime()"] = "Returns the current real time since server-start in seconds" E2Helper.Descriptions["systime()"] = "Returns a highly accurate time (also in seconds) since the server was started. Ideal for benchmarking" -E2Helper.Descriptions["clk(s)"] = "Returns 1 if the current execution was caused by the inserted name" -E2Helper.Descriptions["clk()"] = "Returns 1 if the current execution was caused by the interval" -E2Helper.Descriptions["clkName()"] = "Returns the name of the timer that caused current execution" +E2Helper.Descriptions["clk(s)"] = "DEPRECATED. Use 'timer(sf)' (lambda timer) instead! Returns 1 if the current execution was caused by the inserted name" +E2Helper.Descriptions["clk()"] = "DEPRECATED. Use 'timer(sf)' (lambda timer) instead! Returns 1 if the current execution was caused by the interval" +E2Helper.Descriptions["clkName()"] = "DEPRECATED. Use 'timer(sf)' (lambda timer) instead! Returns the name of the timer that caused current execution" E2Helper.Descriptions["getTimers()"] = "Returns an array of all timers used in the E2" -E2Helper.Descriptions["interval(n)"] = "Sets a one-time timer with name \"interval\" and delay in milliseconds (minimum delay for timers is 10ms)" +E2Helper.Descriptions["interval(n)"] = "DEPRECATED. Use 'timer(sf)' (lambda timer) instead! Sets a one-time timer with name \"interval\" and delay in milliseconds (minimum delay for timers is 10ms)" E2Helper.Descriptions["runOnTick(n)"] = "DEPRECATED. Use 'event tick()' instead! 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["timer(sn)"] = "DEPRECATED. Use 'timer(sf)' (lambda timer) instead! 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" +E2Helper.Descriptions["timerSetDelay(sn)"] = "Sets the delay of the timer with the name S to N seconds" +E2Helper.Descriptions["timerSetReps(sn)"] = "Sets the number of repetitions of the timer with the name S to N" +E2Helper.Descriptions["timerAdjust(snn)"] = "Sets the delay and repetitions of the timer with the name S to N seconds and N repetitions" +--E2Helper.Descriptions["timerSetCallback(sf)"] = "Sets the callback function of the timer with the name S to the function F" +E2Helper.Descriptions["timerGetDelay(s)"] = "Returns the delay of the timer with the name S" +E2Helper.Descriptions["timerGetReps(s)"] = "Returns the number of repetitions of the timer with the name S" +E2Helper.Descriptions["timerGetCallback(s)"] = "Returns the callback function of the timer with the name S" +E2Helper.Descriptions["timer(snnf)"] = "Creates a callback timer with the name S, delay of N seconds, N repeats and callback function F. NOTE: If the timer finished running, it will not exist (and you won't be able to restart/configure it)" +E2Helper.Descriptions["timer(nnf)"] = "Creates a simple callback timer with delay of N seconds, N repeats and callback function F. Returns autogenerated name which you can use. NOTE: If the timer finished running, it will not exist (and you won't be able to restart/configure it)" +E2Helper.Descriptions["timer(nf)"] = "Creates a simple callback timer delay of N seconds, 1 repeat(one-time timer) and callback function F. Returns autogenerated name which you can use. NOTE: If the timer finished running, it will not exist (and you won't be able to restart/configure it)" +E2Helper.Descriptions["timer(snf)"] = "Creates a callback timer with the name S, delay of N seconds, 1 repeat(one-time timer) and callback function F. NOTE: If the timer finished running, it will not exist (and you won't be able to restart/configure it)" +E2Helper.Descriptions["timerRestart(s)"] = "Restarts the callback timer with the name S. Uses the same amount of repetitions/delay/callback function as when was created. NOTE: If the timer finished running, it will not exist (and you won't be able to restart/configure it)" +E2Helper.Descriptions["timerExists(s)"] = "Returns 1 if a callback timer with the name S exists. 0 otherwise" +E2Helper.Descriptions["timerPause(s)"] = "Pauses the callback timer with the name S"--. Returns 1 if successful, 0 if the timer is already stopped" +E2Helper.Descriptions["timerResume(s)"] = "Resumes the callback timer with the name S"--. Returns 1 if successful, 0 if the timer is already running" +E2Helper.Descriptions["timerToggle(s)"] = "Toggles the callback timer with the name S. Returns 1 if timer is now resumed, 0 if paused" +E2Helper.Descriptions["timerRepsLeft(s)"] = "Returns the number of repetitions left for the callback timer with the name S." +E2Helper.Descriptions["timerTimeLeft(s)"] = "Returns the time left for the callback timer with the name S until the next execution" -- Unit conversion E2Helper.Descriptions["toUnit(sn)"] = "Converts default garrysmod units to specified units"