From 51c6c5229e0217ea79036cc413a02c338e0db623 Mon Sep 17 00:00:00 2001 From: Fernando-A-Rocha Date: Sat, 14 Jan 2023 14:35:53 +0000 Subject: [PATCH 1/2] Add Async library for some foreach loops + fix newmodels-example --- [examples]/newmodels-example/meta.xml | 9 +- [examples]/newmodels-example/server.lua | 21 +- [examples]/sampobj_reloaded/meta.xml | 2 +- [examples]/unittest_newmodels/meta.xml | 2 +- newmodels/_config.lua | 9 + newmodels/async_s.lua | 459 ++++++++++++++++++++++++ newmodels/meta.xml | 3 +- newmodels/server.lua | 105 +++--- 8 files changed, 540 insertions(+), 70 deletions(-) create mode 100644 newmodels/async_s.lua diff --git a/[examples]/newmodels-example/meta.xml b/[examples]/newmodels-example/meta.xml index f46610e..cd14d2a 100644 --- a/[examples]/newmodels-example/meta.xml +++ b/[examples]/newmodels-example/meta.xml @@ -3,12 +3,9 @@ Its version now matches the main newmodels version! https://github.com/Fernando-A-Rocha/mta-add-models --> - - - + + + diff --git a/[examples]/newmodels-example/server.lua b/[examples]/newmodels-example/server.lua index cc14765..63fa76c 100644 --- a/[examples]/newmodels-example/server.lua +++ b/[examples]/newmodels-example/server.lua @@ -31,6 +31,8 @@ function (startedResource) if not startUpChecks() then return end + local listToAdd = {} + for k,mod in pairs(myMods) do local uid = mod[1] @@ -48,17 +50,18 @@ function (startedResource) if not baseid then outputDebugString("Failed to get vehicle model from name: "..tostring(mod[2]), 0, 255,55,55) else - local worked, reason = exports.newmodels:addExternalMod_CustomFilenames( - et, uid, baseid, name, - dff, txd, col - ) - - if not worked then - outputDebugString(reason or "Unknown error", 0, 255,110,61) - end + -- ARGS: elementType, id, base_id, name, path_dff, path_txd, path_col, ignoreTXD, ignoreDFF, ignoreCOL, metaDownloadFalse + listToAdd[#listToAdd+1] = {et, uid, baseid, name, dff, txd, col, false, false, false, false} end end + local count, reason = exports.newmodels:addExternalMods_CustomFileNames(listToAdd) + if not count then + outputDebugString("[newmodels-example] Failed to add models: "..tostring(reason), 0, 255,110,61) + return + end + + checkPossibleExistingElements() end) @@ -320,7 +323,7 @@ function testVehiclesCmd(thePlayer, cmd) for elementType, mods in pairs(modList) do if elementType == elementType2 then for k,mod in pairs(mods) do - local veh = createVehicle(400, x,y,z,rx,ry,rz) + local veh = createVehicle(mod.base_id, x,y,z,rx,ry,rz) if veh then setElementData(veh, data_name, mod.id) table.insert(spawned_vehs, veh) diff --git a/[examples]/sampobj_reloaded/meta.xml b/[examples]/sampobj_reloaded/meta.xml index ef03b67..f8230b9 100644 --- a/[examples]/sampobj_reloaded/meta.xml +++ b/[examples]/sampobj_reloaded/meta.xml @@ -6,7 +6,7 @@ diff --git a/[examples]/unittest_newmodels/meta.xml b/[examples]/unittest_newmodels/meta.xml index 0a65285..6f104e3 100644 --- a/[examples]/unittest_newmodels/meta.xml +++ b/[examples]/unittest_newmodels/meta.xml @@ -6,7 +6,7 @@ diff --git a/newmodels/_config.lua b/newmodels/_config.lua index ef7139f..631effc 100644 --- a/newmodels/_config.lua +++ b/newmodels/_config.lua @@ -47,3 +47,12 @@ START_STOP_MESSAGES = true -- enable resouce start/stop automatic chat messages SEE_ALLOCATED_TABLE = true -- automatically executes /allocatedids on startup ENABLE_DEBUG_MESSAGES = true -- toggle all debug console messages CHAT_DEBUG_MESSAGES = true -- make debug console messages to go chatbox (better readability imo) + +--[[ + MTA:SA Async library Settings + + Async:setPriority("low"); -- better fps + Async:setPriority("normal"); -- medium + Async:setPriority("high"); -- better perfomance +]] +ASYNC_PRIORITY = "normal" diff --git a/newmodels/async_s.lua b/newmodels/async_s.lua new file mode 100644 index 0000000..a425214 --- /dev/null +++ b/newmodels/async_s.lua @@ -0,0 +1,459 @@ +-- https://github.com/inlife/mta-lua-async + +local function loadClass() + --------- + -- Start of slither.lua dependency + --------- + + local _LICENSE = -- zlib / libpng + [[ + Copyright (c) 2011-2014 Bart van Strien + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + ]] + + local class = + { + _VERSION = "Slither 20140904", + -- I have no better versioning scheme, deal with it + _DESCRIPTION = "Slither is a pythonic class library for lua", + _URL = "http://bitbucket.org/bartbes/slither", + _LICENSE = _LICENSE, + } + + local function stringtotable(path) + local t = _G + local name + + for part in path:gmatch("[^%.]+") do + t = name and t[name] or t + name = part + end + + return t, name + end + + local function class_generator(name, b, t) + local parents = {} + for _, v in ipairs(b) do + parents[v] = true + for _, v in ipairs(v.__parents__) do + parents[v] = true + end + end + + local temp = { __parents__ = {} } + for i, v in pairs(parents) do + table.insert(temp.__parents__, i) + end + + local class = setmetatable(temp, { + __index = function(self, key) + if key == "__class__" then return temp end + if key == "__name__" then return name end + if t[key] ~= nil then return t[key] end + for i, v in ipairs(b) do + if v[key] ~= nil then return v[key] end + end + if tostring(key):match("^__.+__$") then return end + if self.__getattr__ then + return self:__getattr__(key) + end + end, + + __newindex = function(self, key, value) + t[key] = value + end, + + allocate = function(instance) + local smt = getmetatable(temp) + local mt = {__index = smt.__index} + + function mt:__newindex(key, value) + if self.__setattr__ then + return self:__setattr__(key, value) + else + return rawset(self, key, value) + end + end + + if temp.__cmp__ then + if not smt.eq or not smt.lt then + function smt.eq(a, b) + return a.__cmp__(a, b) == 0 + end + function smt.lt(a, b) + return a.__cmp__(a, b) < 0 + end + end + mt.__eq = smt.eq + mt.__lt = smt.lt + end + + for i, v in pairs{ + __call__ = "__call", __len__ = "__len", + __add__ = "__add", __sub__ = "__sub", + __mul__ = "__mul", __div__ = "__div", + __mod__ = "__mod", __pow__ = "__pow", + __neg__ = "__unm", __concat__ = "__concat", + __str__ = "__tostring", + } do + if temp[i] then mt[v] = temp[i] end + end + + return setmetatable(instance or {}, mt) + end, + + __call = function(self, ...) + local instance = getmetatable(self).allocate() + if instance.__init__ then instance:__init__(...) end + return instance + end + }) + + for i, v in ipairs(t.__attributes__ or {}) do + class = v(class) or class + end + + return class + end + + local function inheritance_handler(set, name, ...) + local args = {...} + + for i = 1, select("#", ...) do + if args[i] == nil then + error("nil passed to class, check the parents") + end + end + + local t = nil + if #args == 1 and type(args[1]) == "table" and not args[1].__class__ then + t = args[1] + args = {} + end + + for i, v in ipairs(args) do + if type(v) == "string" then + local t, name = stringtotable(v) + args[i] = t[name] + end + end + + local func = function(t) + local class = class_generator(name, args, t) + if set then + local root_table, name = stringtotable(name) + root_table[name] = class + end + return class + end + + if t then + return func(t) + else + return func + end + end + + function class.private(name) + return function(...) + return inheritance_handler(false, name, ...) + end + end + + class = setmetatable(class, { + __call = function(self, name) + return function(...) + return inheritance_handler(true, name, ...) + end + end, + }) + + + function class.issubclass(class, parents) + if parents.__class__ then parents = {parents} end + for i, v in ipairs(parents) do + local found = true + if v ~= class then + found = false + for _, p in ipairs(class.__parents__) do + if v == p then + found = true + break + end + end + end + if not found then return false end + end + return true + end + + function class.isinstance(obj, parents) + return type(obj) == "table" and obj.__class__ and class.issubclass(obj.__class__, parents) + end + + -- Export a Class Commons interface + -- to allow interoperability between + -- class libraries. + -- See https://github.com/bartbes/Class-Commons + -- + -- NOTE: Implicitly global, as per specification, unfortunately there's no nice + -- way to both provide this extra interface, and use locals. + if common_class ~= false then + common = {} + function common.class(name, prototype, superclass) + prototype.__init__ = prototype.init + return class_generator(name, {superclass}, prototype) + end + + function common.instance(class, ...) + return class(...) + end + end + + --------- + -- End of slither.lua dependency + --------- + + return class; +end + +local class = loadClass(); + +--- GTA:MTA Lua async thread scheduler. +-- @author Inlife +-- @license MIT +-- @url https://github.com/Inlife/mta-lua-async +-- @dependency slither.lua https://bitbucket.org/bartbes/slither + +class "_Async" { + + -- Constructor mehtod + -- Starts timer to manage scheduler + -- @access public + -- @usage local asyncmanager = async(); + __init__ = function(self) + + self.threads = {}; + self.resting = 50; -- in ms (resting time) + self.maxtime = 200; -- in ms (max thread iteration time) + self.current = 0; -- starting frame (resting) + self.state = "suspended"; -- current scheduler executor state + self.debug = false; + self.priority = { + low = {500, 50}, -- better fps + normal = {200, 200}, -- medium + high = {50, 500} -- better perfomance + }; + + self:setPriority("normal"); + end, + + + -- Switch scheduler state + -- @access private + -- @param boolean [istimer] Identifies whether or not + -- switcher was called from main loop + switch = function(self, istimer) + self.state = "running"; + + if (self.current + 1 <= #self.threads) then + self.current = self.current + 1; + self:execute(self.current); + else + self.current = 0; + + if (#self.threads <= 0) then + self.state = "suspended"; + return; + end + + -- setTimer(function theFunction, int timeInterval, int timesToExecute) + -- (GTA:MTA server scripting function) + -- For other environments use alternatives. + setTimer(function() + self:switch(); + end, self.resting, 1); + end + end, + + + -- Managing thread (resuming, removing) + -- In case of "dead" thread, removing, and skipping to the next (recursive) + -- @access private + -- @param int id Thread id (in table async.threads) + execute = function(self, id) + local thread = self.threads[id]; + + if (thread == nil or coroutine.status(thread) == "dead") then + table.remove(self.threads, id); + self:switch(); + else + coroutine.resume(thread); + self:switch(); + end + end, + + + -- Adding thread + -- @access private + -- @param function func Function to operate with + add = function(self, func) + local thread = coroutine.create(func); + table.insert(self.threads, thread); + end, + + + -- Set priority for executor + -- Use before you call 'iterate' or 'foreach' + -- @access public + -- @param string|int param1 "low"|"normal"|"high" or number to set 'resting' time + -- @param int|void param2 number to set 'maxtime' of thread + -- @usage async:setPriority("normal"); + -- @usage async:setPriority(50, 200); + setPriority = function(self, param1, param2) + if (type(param1) == "string") then + if (self.priority[param1] ~= nil) then + self.resting = self.priority[param1][1]; + self.maxtime = self.priority[param1][2]; + end + else + self.resting = param1; + self.maxtime = param2; + end + end, + + -- Set debug mode enabled/disabled + -- @access public + -- @param boolean value true - enabled, false - disabled + -- @usage async:setDebug(true); + setDebug = function(self, value) + self.debug = value; + end, + + + -- Iterate on interval (for cycle) + -- @access public + -- @param int from Iterate from + -- @param int to Iterate to + -- @param function func Iterate using func + -- Function func params: + -- @param int [i] Iteration index + -- @param function [callback] Callback function, called when execution finished + -- Usage: + -- @usage async:iterate(1, 10000, function(i) + -- outputDebugString(i); + -- end); + iterate = function(self, from, to, func, callback) + self:add(function() + local a = getTickCount(); + local lastresume = getTickCount(); + for i = from, to do + func(i); + + -- int getTickCount() + -- (GTA:MTA server scripting function) + -- For other environments use alternatives. + if getTickCount() > lastresume + self.maxtime then + coroutine.yield() + lastresume = getTickCount() + end + end + if (self.debug) then + outputDebugString("[DEBUG]Async iterate: " .. (getTickCount() - a) .. "ms"); + end + if (callback) then + callback(); + end + end); + + self:switch(); + end, + + -- Iterate over array (foreach cycle) + -- @access public + -- @param table array Input array + -- @param function func Iterate using func + -- Function func params: + -- @param int [v] Iteration value + -- @param int [k] Iteration key + -- @param function [callback] Callback function, called when execution finished + -- Usage: + -- @usage async:foreach(vehicles, function(vehicle, id) + -- outputDebugString(vehicle.title); + -- end); + foreach = function(self, array, func, callback) + self:add(function() + local a = getTickCount(); + local lastresume = getTickCount(); + for k,v in ipairs(array) do + func(v,k); + + -- int getTickCount() + -- (GTA:MTA server scripting function) + -- For other environments use alternatives. + if getTickCount() > lastresume + self.maxtime then + coroutine.yield() + lastresume = getTickCount() + end + end + if (self.debug) then + outputDebugString("[DEBUG]Async foreach: " .. (getTickCount() - a) .. "ms"); + end + if (callback) then + callback(); + end + end); + + self:switch(); + end, +} + +-- Async Singleton wrapper +Async = { + instance = nil, +}; + +-- After first call, creates an instance and stores it +local function getInstance() + if Async.instance == nil then + Async.instance = _Async(); + end + + return Async.instance; +end + +-- proxy methods for public members +function Async:setDebug(...) + getInstance():setDebug(...); +end + +function Async:setPriority(...) + getInstance():setPriority(...); +end + +function Async:iterate(...) + getInstance():iterate(...); +end + +function Async:foreach(...) + getInstance():foreach(...); +end diff --git a/newmodels/meta.xml b/newmodels/meta.xml index b5fb1e5..8a663c8 100644 --- a/newmodels/meta.xml +++ b/newmodels/meta.xml @@ -1,6 +1,6 @@ + description="minimalistic library for adding new models to your server" version="2.0.2" type="script"/>