• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

lua require ersatz

Status
Not open for further replies.
Level 17
Joined
Apr 27, 2008
Messages
2,455
So, i was looking for a way to build library and requirements directly inside the editor.
I thought about that :

Lua:
do
   
    local function runGlobalInit()

        local n = 0
        local q_pos= {} -- relative order of packages according requirements and initializers
        local packages={} -- found packages in global env
        local packages_name = {}
        local req_list = {}

        local function isCircleReq(pack,req)

            if (req_list[pack])[req] and (req_list[req])[pack] then
                local s = "requirement error : packages "..pack.." and "..req.." need both each others"
                print(s) ; error(s)
                return true
            end

        end
   
        for pack,code in pairs(_ENV) do -- searching for all packages in global env

            if not packages[pack] and type(code)=="table" and type(code.init)=="function" then -- matching packages

                packages[pack]=code
                table.insert(packages_name,pack)
                req_list[pack]={}

            end

        end

        table.sort(packages_name) -- sorting table so everyone will have the same input
     
        for _ , pack in ipairs(packages_name) do

            local code = packages[pack]
            if not q_pos[pack] then -- package not yet in the list
                n = n+1
                q_pos[pack] = n

            end
            if code.uses then
         
                for _ , req in ipairs(code.uses) do -- scrolling package requirements
               
                    if packages[req] then -- valid package requirement
               
                        if not q_pos[req] then -- required package not yet in the list
                 
                            for key,value in pairs(q_pos) do
                                q_pos[key] = value+1
                            end
               
                            n = n+1
                            q_pos[req] = 1

                        elseif q_pos[req] > q_pos[pack] then
                            q_pos[req] , q_pos[pack] = q_pos[pack] , q_pos[req]

                        end

                        (req_list[pack])[req] = true
                        if isCircleReq(pack,req) then return end

                        for k , v in pairs(req_list[req]) do

                            req_list[pack][k]=true
                            if isCircleReq(pack,k) then return end

                        end
                     end
                 end
            end
 
        end

        local t = {}

            for k,v in pairs(q_pos) do t[v]=k end
            for _ , v in ipairs(t) do
                local f = packages[v].init
                if f then f() end
            end
    end

    local old = InitGlobals
    if old then
        function InitGlobals()
            old()
            runGlobalInit()
        end
    else
    runGlobalInit()
    end

end

the InitGlobals hook is directly copied/paste from Bribe's library

Then, all you need is to build packages :
If you want an initializer create a method spelled init.
Each initializers will be called in a correct order (checking for requirements), unlike Bribe's method, but i know it can be changed, i'm just saying.
If you want requirements create a table in your package spelled uses

Like these examples :

Lua:
Aa={}

function Aa.init()
   print("A init")
end

Aa.uses={
"Cc";
"Bb";
}

Dd={}

Dd.uses={
"Aa"
}

function Dd.init()
   print("D init")
end

Bb={
}

function Bb.init()
   print("B init")
end

Cc={
}

function Cc.init()
   print("C init")
end

I'm totally aware that it doesn't handle cycle requirements, (A requires B and B requires A).
In this case it should raise an error.
For now it's just a proof of concept, it can be improved later.
What do you think about that concept ?


EDIT : Requirement cycle error detected now.
It won't raise an error if there is no initializer on a circle requirement (A uses B, B uses A).

Because it's simply how it was build.
It only handles initializers orders according requirements.
Functions, global variables, list, whatever ... of packages will already be on the global space when the InitGlobals hook will be done, so it only needs to do that.

But that also mean you must use functions and call them from initializers instead of just writing code outside the package (unless you don't care when it runs).
 
Last edited:
Level 17
Joined
Apr 27, 2008
Messages
2,455
Yes and no, i mean i can't argue with the syntax highlighting, or even checking code errors, auto completion and all the usual stuff associated when you're writing code in a proper ide.
But i'm not sure what you want to say about
TriggerHappy said:
you have no control over the build process
, that's basically what i'm trying to bring.

Of course we can't compare it as the require built in lua, it's just an ersatz.

One of a "problem" with lua, it's too permissive.
Redefine functions is ok on a personal map, but as a public resource it shouldn't be done, or at the least possible.
Because it's too easy to screw other resources.

I've read Bribe init library and i was thinking we don't especially need all kind of init hooks, the one after GUI global variable default setting was good enough, as long we can control the order of initializers.

At the point an initializer is called, all packages will have been opened (excepted nested functions/tables won't be on the global space), and it's done automatically by this script (i mean the initializer order handling part), as long you follow the pattern.

As you know, we naturally build packages with tables in LUA, for various reasons, including a control of global name space.
So it shouldn't be a notable effort to follow the pattern, and it's optional anyway.

Also, even if you can test many lua code outside a map, you can't really when it comes to lot of "userdata", such as widgets, events, etc.
Even if some can be simulated in a way or an other (but yeah writing code with a proper ide and injecting code in the map is still the best solution).

But tbh i've just learned LUA, i was still on old days fashion (vjass time), maybe we can just assume the user will use "professional" tools and require, putting resources in separated files.

I just thought we needed a guideline when submitting lua resources as a public resource, especially for GUI users which don't wan't/can't handle all the extra stuff needed, doing it the "right way"

Now, don't take me wrong if it's useless it's useless, i won't fight for it.
It was put on the lab section for a good reason:ogre_hurrhurr:
 
Level 17
Joined
Apr 27, 2008
Messages
2,455
Rewritten the code, using ipair on a sorted table instead of pair when doing requirements iteration, so the output is always the same for everyone.

You're also not allowed to redefine the same package several times like this :
Lua:
packageA = {} ...
-- then somewhere else
packageA = {}
It still doesn't handle cycle requirements, will probably do it one boring day.
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
This is an interesting concept. Its API can extend off of Global Initialization with the following replacement code:

Lua:
OnGlobalInit(function()
    local nameMap, nameList = {}, {}
    
    for name,code in pairs(_ENV) do -- searching for all mapped names in global env

        if not nameMap[name] and type(code)=="table" and type(code.init)=="function" then -- matching nameMap

            nameMap[name]=code
            table.insert(nameList,name)
        end
    end

    table.sort(nameList) -- sorting table so everyone will have the same input

    for _,name in ipairs(nameList) do
        local code = nameMap[name]
        local init = code.init
        if code.uses then
            init = function()
                Require(table.unpack(code.uses))
                code.init()
            end
        end
        OnGlobalInit(name, init)
    end

end)
 
Status
Not open for further replies.
Top