Cleaning up specific lua scripts

classic Classic list List threaded Threaded
24 messages Options
12
Reply | Threaded
Open this post in threaded view
|

Cleaning up specific lua scripts

Ricky Haggett

Hey folks,

 

I’m working on a game which uses lua extensively, on a platform where memory is limited, and loading is slow, so I’d like to minimise my memory footprint and load times..

 

When the game starts, I create a lua vm, and load a bunch of global scripts that I want to keep around for the duration of the game..

 

Then each level has a .lua script file containing the script just for that level – a whole bunch of functions and tables.

 

Ideally, I’d be able to isolate the level-specific functions from the global ones, and then between levels set the level-specific functions/tables to nil and do a lua garbage collect – so the memory for the level-specific scripts gets cleaned up but the global stuff stays around - I don’t want to destroy / recreate my lua vm between levels or set the whole of global to nil, because this hurts my load times.

 

Is there a way of doing this programmatically? I was thinking maybe I could compare the state of global before and after loading a level-script to figure out the level specific stuff? But maybe there’s an easier way?

 

Cheers,

 

Ricky

Reply | Threaded
Open this post in threaded view
|

Re: Cleaning up specific lua scripts

steve donovan
On Wed, Mar 24, 2010 at 1:00 PM, Ricky Haggett <[hidden email]> wrote:
> Is there a way of doing this programmatically? I was thinking maybe I could
> compare the state of global before and after loading a level-script to
> figure out the level specific stuff? But maybe there’s an easier way?

Would not collectgarbage() do the job? May have to call it a few
times, use the 'count' argument before & after to see what you've
reclaimed.  And (of course) make sure there are genuinely no
references hanging around, non-weak tables being the big culprit
Reply | Threaded
Open this post in threaded view
|

RE: Cleaning up specific lua scripts

Ricky Haggett
Hey Steve,

Collectgarbage will only work if I set the functions/tables I want to delete
to nil - the question is how I differentiate the level-specific ones I do
want destroying from the global ones I don't..

Cheers,

Ricky


-----Original Message-----
From: [hidden email]
[mailto:[hidden email]] On Behalf Of steve donovan
Sent: 24 March 2010 11:08
To: Lua list
Subject: Re: Cleaning up specific lua scripts

On Wed, Mar 24, 2010 at 1:00 PM, Ricky Haggett <[hidden email]> wrote:
> Is there a way of doing this programmatically? I was thinking maybe I
could
> compare the state of global before and after loading a level-script to
> figure out the level specific stuff? But maybe there's an easier way?

Would not collectgarbage() do the job? May have to call it a few
times, use the 'count' argument before & after to see what you've
reclaimed.  And (of course) make sure there are genuinely no
references hanging around, non-weak tables being the big culprit

Reply | Threaded
Open this post in threaded view
|

Re: Cleaning up specific lua scripts

steve donovan
On Wed, Mar 24, 2010 at 1:17 PM, Ricky Haggett <[hidden email]> wrote:
> Collectgarbage will only work if I set the functions/tables I want to delete
> to nil - the question is how I differentiate the level-specific ones I do
> want destroying from the global ones I don't..

Very true - the obvious solution is keep level-specific items in
separate tables, within a global table. Or would this be awkward?
Reply | Threaded
Open this post in threaded view
|

RE: Cleaning up specific lua scripts

Ricky Haggett
Yep, we could store a table of all of the functions / tables within a
specific level script, but it would be awkward for the designers to
maintain, and a potential source of error...

It seemed like a problem which would be common enough for someone to have
come up with an elegant and foolproof solution :)


-----Original Message-----
From: [hidden email]
[mailto:[hidden email]] On Behalf Of steve donovan
Sent: 24 March 2010 11:23
To: Lua list
Subject: Re: Cleaning up specific lua scripts

On Wed, Mar 24, 2010 at 1:17 PM, Ricky Haggett <[hidden email]> wrote:
> Collectgarbage will only work if I set the functions/tables I want to
delete
> to nil - the question is how I differentiate the level-specific ones I do
> want destroying from the global ones I don't..

Very true - the obvious solution is keep level-specific items in
separate tables, within a global table. Or would this be awkward?

Reply | Threaded
Open this post in threaded view
|

Re: Cleaning up specific lua scripts

Enrico Colombini
Ricky Haggett wrote:
> Yep, we could store a table of all of the functions / tables within a
> specific level script, but it would be awkward for the designers to
> maintain, and a potential source of error...

You could load the level into a table, use setfenv to have level
functions see this table as 'global', and add true global functions to
the table when you load a level.

   Enrico
Reply | Threaded
Open this post in threaded view
|

RE: Cleaning up specific lua scripts

Ricky Haggett
Hey Enrico,

How would I go about loading the level script into a table?

I support both compiled and uncompiled lua, so I load my levels with:

error = luaL_loadbuffer(lua, buffer, length, filename);
lua_pcall (lua, 0, 0, 0);

.. if they're compiled or:

error = luaL_dostring(lua, scriptString);

if they're uncompiled...

Also, if I used setfenv to change the environment, aren't I just moving the
problem? If I then have to add global functions to this same table, how will
I differentiate them from the level functions when I need to clean up?

I am thinking maybe my solution is going to be (shallow) copying _G before
loading a level, then comparing the copy with _G after the level load. Any
objects which don't exist in the copy, but do exist in _G should be the
level-specific ones
Cheers,

Ricky

-----Original Message-----
From: [hidden email]
[mailto:[hidden email]] On Behalf Of Enrico Colombini
Sent: 24 March 2010 11:47
To: Lua list
Subject: Re: Cleaning up specific lua scripts

Ricky Haggett wrote:
> Yep, we could store a table of all of the functions / tables within a
> specific level script, but it would be awkward for the designers to
> maintain, and a potential source of error...

You could load the level into a table, use setfenv to have level
functions see this table as 'global', and add true global functions to
the table when you load a level.

   Enrico

Reply | Threaded
Open this post in threaded view
|

Re: Cleaning up specific lua scripts

steve donovan
On Wed, Mar 24, 2010 at 2:06 PM, Ricky Haggett <[hidden email]> wrote:
> Also, if I used setfenv to change the environment, aren't I just moving the
> problem? If I then have to add global functions to this same table, how will
> I differentiate them from the level functions when I need to clean up?

You could use the same trick that module('name',package.seeall) uses.
This creates a module and uses setfenv to make it the current
environment, but package.seeall adds a metatable to this table with
__index set to _G so that any unknown values will be looked up
globally.

So, your table will only contain level-specific stuff, and it will
still be able to access global stuff.

steve d.
Reply | Threaded
Open this post in threaded view
|

Re: Cleaning up specific lua scripts

Fabian Peña
In reply to this post by Ricky Haggett
Ricky Haggett escribió:

Hey folks,

 

I’m working on a game which uses lua extensively, on a platform where memory is limited, and loading is slow, so I’d like to minimise my memory footprint and load times..

 

When the game starts, I create a lua vm, and load a bunch of global scripts that I want to keep around for the duration of the game..

 

Then each level has a .lua script file containing the script just for that level – a whole bunch of functions and tables.

 

Ideally, I’d be able to isolate the level-specific functions from the global ones, and then between levels set the level-specific functions/tables to nil and do a lua garbage collect – so the memory for the level-specific scripts gets cleaned up but the global stuff stays around - I don’t want to destroy / recreate my lua vm between levels or set the whole of global to nil, because this hurts my load times.

 

Is there a way of doing this programmatically? I was thinking maybe I could compare the state of global before and after loading a level-script to figure out the level specific stuff? But maybe there’s an easier way?

 

Cheers,

 

Ricky

I usually use something like this, to provide a closed execution environment to user scripts and eliminate unnecesary scripts and data.
Good luck!!


function createScriptEnvironment(scriptName,available_functions)
    local file,script
    local proc,err
    local environment={}
    environment._G = environment
    for k,v in pairs(available_functions or {}) do
        environment[k]=v
    end
    file = io.open(scriptName,"rb")
    if file then
        script = file:read("*a");
        io.close(file);
        proc,err = loadstring(script)
        if err then
            error(err)
        end
   
        setfenv(proc,environment)
        local _,err = pcall(proc)
        if err then
            error(err)
        end
        return environment
    else
        error("Cant open script " .. scriptName);
    end
end
gamelevel = createScriptEnvironment("levelone.lua",{
    print=print;                                        -- api to be available into game level scripts
    io = io;
    string = string;
    table = table;
    -- etc etc etc
})
if gamelevel.start then
    gamelevel.start()
else
    error ("can't find entry point 'start' at current game level")
end
gamelevel = nil                                            -- dispose all script data
collectgarbage();


------- levelone.lua
-- level one test
a = {}
foo = {1,2,3,4,5}
function start()
    print("Level ONE")
end


Reply | Threaded
Open this post in threaded view
|

Re: Cleaning up specific lua scripts

Enrico Colombini
In reply to this post by Ricky Haggett
Ricky Haggett wrote:
> How would I go about loading the level script into a table?

Here is a simple example. This is your level:

-------------------------
-- this is "level1.lua"

function a()
     b()
end

function b()
     print("Ships: " .. ships)
end

ships = 5

-------------------------

and this is the level loader:

-------------------------
-- this is "loader.lua"

level = {} -- the table containing a level

-- read "lev1.lua" into level
local t = assert(loadfile("lev1.lua")) -- could use loadstring
setfenv(t, level)
local r, err = pcall(t) -- execute loaded chunk to define stuff
r = r or error('error reading level')

-- add global functions to level (just an example)
level.print = print

-- call function a() from outside the level table
level.a()

-------------------------

> Also, if I used setfenv to change the environment, aren't I just moving the
> problem? If I then have to add global functions to this same table, how will
> I differentiate them from the level functions when I need to clean up?

No need to differentiate, just throw away the level table:

   level = nil

If you must access many global values/functions, a trick such as Steve
suggested would be better than copying them to 'level'. The copy, on the
other hand, allows fine control over global accesses (there are other,
possibly better, intermediate solutions).

> I am thinking maybe my solution is going to be (shallow) copying _G before
> loading a level, then comparing the copy with _G after the level load. Any
> objects which don't exist in the copy, but do exist in _G should be the
> level-specific ones

Too much work, I prefer lazy solutions :-)

   Enrico
Reply | Threaded
Open this post in threaded view
|

RE: Cleaning up specific lua scripts

Ricky Haggett
Hey everyone, (and thanks very much to all of you for the help!)

So, it seems like there a couple of equivalent options. One is using
setfenv, and then linking in the global stuff I need (as in Enrico's
solution below or Fabio's code)..

Another is maybe to put make my level script files modules - i.e. put
module("level1", package.seeall) at the top of level1.lua and then require
"level1" after that level is loaded..

But it seems like, either way, I would then have to access all the
functions/objects in my level code with level1.a().  This is problematic,
because I already have a load of places in xml files (which describe the
level graphically and structurally) where I am just calling a();  .. which I
don't want to change..

An ideal solution would be a module-like structure, but where all the
functions in my level remain global (accessible from the outside without
doing level.a() ) ..





Also, as an aside, I'm loading my scripts in C, with
luaL_loadbuffer+lua_pcall or luaL_dostring. It seems that these functions
return only an error int - is there no C equivalent for the lua code:
proc,err = loadstring(script) ?

Cheers,

Ricky




-----Original Message-----
From: [hidden email]
[mailto:[hidden email]] On Behalf Of Enrico Colombini
Sent: 24 March 2010 13:08
To: Lua list
Subject: Re: Cleaning up specific lua scripts

Ricky Haggett wrote:
> How would I go about loading the level script into a table?

Here is a simple example. This is your level:

-------------------------
-- this is "level1.lua"

function a()
     b()
end

function b()
     print("Ships: " .. ships)
end

ships = 5

-------------------------

and this is the level loader:

-------------------------
-- this is "loader.lua"

level = {} -- the table containing a level

-- read "lev1.lua" into level
local t = assert(loadfile("lev1.lua")) -- could use loadstring
setfenv(t, level)
local r, err = pcall(t) -- execute loaded chunk to define stuff
r = r or error('error reading level')

-- add global functions to level (just an example)
level.print = print

-- call function a() from outside the level table
level.a()

-------------------------

> Also, if I used setfenv to change the environment, aren't I just moving
the
> problem? If I then have to add global functions to this same table, how
will
> I differentiate them from the level functions when I need to clean up?

No need to differentiate, just throw away the level table:

   level = nil

If you must access many global values/functions, a trick such as Steve
suggested would be better than copying them to 'level'. The copy, on the
other hand, allows fine control over global accesses (there are other,
possibly better, intermediate solutions).

> I am thinking maybe my solution is going to be (shallow) copying _G before
> loading a level, then comparing the copy with _G after the level load. Any
> objects which don't exist in the copy, but do exist in _G should be the
> level-specific ones

Too much work, I prefer lazy solutions :-)

   Enrico

Reply | Threaded
Open this post in threaded view
|

Re: Cleaning up specific lua scripts

Eike Decker
Loading the levels with a different environment is surely the way to
go to provide simple measures to get rid of level dependent data that
is no longer needed once the level is finished. Nothing sucks more
than data / functions that still roam around at a later point. I can
imagine a lot of bugginess with that way of development, e.g. one
level defines a certain function which the following level does not
handle - calling such a function (belonging to a previous level) ends
up in a pretty undefined state. If everything is global, of course,
the outcome can be that everything works... but well, not really that
nice if you have to load level 3 before being able to load level 4.

However, the convenience that you are asking for, the ability to call
level dependent functions as globals, is however somewhat puzzling me
- because it doesn't seem to me to be a huge convenience at all and
despite the small win, it comes with a huge lack of transparency. The
statement

level.dosomething()

vs.

dosomething()

is carrying much more information that is useful when reading code.
The latter one is indistinguishable from anything else that's being
defined by your application. It looks like an application function,
but it's not. The first one however makes a clear statement: It's a
level's function (If you are really lazy, you could shorten it to
something like "lvl.", "lv." or even "l." - still, it would remain
informative).

Whenever someone else is supposed to read / write code, this small
statement will be a blessing. I can perfectly visualize someone
editing the files and regularly saying things like "WTF does this
function come from and why doesn't it exist anymore?" mixed with agony
and pain, trying to find out what should be done to get the
application running.

Usually, it's good to be a lazy programmer, but I wonder if you aren't
asking for too much laziness here...

Cheers,
Eike

PS.: you could always set the level's environment table as the
environment table of your application code (after it has been loaded!)
so that you would be able to call level function without any
prefixing, though you might end up writing values to your level
environments within code that is actually supposed to be application
values, so this would just be a swap of prefixing variables.

2010/3/24 Ricky Haggett <[hidden email]>:

> Hey everyone, (and thanks very much to all of you for the help!)
>
> So, it seems like there a couple of equivalent options. One is using
> setfenv, and then linking in the global stuff I need (as in Enrico's
> solution below or Fabio's code)..
>
> Another is maybe to put make my level script files modules - i.e. put
> module("level1", package.seeall) at the top of level1.lua and then require
> "level1" after that level is loaded..
>
> But it seems like, either way, I would then have to access all the
> functions/objects in my level code with level1.a().  This is problematic,
> because I already have a load of places in xml files (which describe the
> level graphically and structurally) where I am just calling a();  .. which I
> don't want to change..
>
> An ideal solution would be a module-like structure, but where all the
> functions in my level remain global (accessible from the outside without
> doing level.a() ) ..
>
>
>
>
>
> Also, as an aside, I'm loading my scripts in C, with
> luaL_loadbuffer+lua_pcall or luaL_dostring. It seems that these functions
> return only an error int - is there no C equivalent for the lua code:
> proc,err = loadstring(script) ?
>
> Cheers,
>
> Ricky
>
>
>
>
> -----Original Message-----
> From: [hidden email]
> [mailto:[hidden email]] On Behalf Of Enrico Colombini
> Sent: 24 March 2010 13:08
> To: Lua list
> Subject: Re: Cleaning up specific lua scripts
>
> Ricky Haggett wrote:
>> How would I go about loading the level script into a table?
>
> Here is a simple example. This is your level:
>
> -------------------------
> -- this is "level1.lua"
>
> function a()
>     b()
> end
>
> function b()
>     print("Ships: " .. ships)
> end
>
> ships = 5
>
> -------------------------
>
> and this is the level loader:
>
> -------------------------
> -- this is "loader.lua"
>
> level = {} -- the table containing a level
>
> -- read "lev1.lua" into level
> local t = assert(loadfile("lev1.lua")) -- could use loadstring
> setfenv(t, level)
> local r, err = pcall(t) -- execute loaded chunk to define stuff
> r = r or error('error reading level')
>
> -- add global functions to level (just an example)
> level.print = print
>
> -- call function a() from outside the level table
> level.a()
>
> -------------------------
>
>> Also, if I used setfenv to change the environment, aren't I just moving
> the
>> problem? If I then have to add global functions to this same table, how
> will
>> I differentiate them from the level functions when I need to clean up?
>
> No need to differentiate, just throw away the level table:
>
>   level = nil
>
> If you must access many global values/functions, a trick such as Steve
> suggested would be better than copying them to 'level'. The copy, on the
> other hand, allows fine control over global accesses (there are other,
> possibly better, intermediate solutions).
>
>> I am thinking maybe my solution is going to be (shallow) copying _G before
>> loading a level, then comparing the copy with _G after the level load. Any
>> objects which don't exist in the copy, but do exist in _G should be the
>> level-specific ones
>
> Too much work, I prefer lazy solutions :-)
>
>   Enrico
>
>
Reply | Threaded
Open this post in threaded view
|

Re: Cleaning up specific lua scripts

Enrico Colombini
In reply to this post by Ricky Haggett
> But it seems like, either way, I would then have to access all the
> functions/objects in my level code with level1.a().  This is problematic,
> because I already have a load of places in xml files (which describe the
> level graphically and structurally) where I am just calling a();  .. which I
> don't want to change..

If they're level-related, perhaps they too should be loaded into the
level table.

Making this sort of changes after the design phase, when there is
(possibly a lot of) code around, always tends to be messy.

   Enrico
Reply | Threaded
Open this post in threaded view
|

RE: Cleaning up specific lua scripts

Ricky Haggett
In reply to this post by Eike Decker
Hey Eike,

I have no overlap in the code between levels. All of the code that is shared
by the levels is loaded once at the start in a bunch of global scripts.  The
level specific code is just that - specific to only one level, so the order
you load the levels is irrelevant and any single level works stand-alone

I don't think having level.doSomething adds much to the readability. To give
a more concrete (and made up :) example, say there's a level (defined in
level1.xml and level1.lua) which contains a purple teapot. And the xml tag
which describes the purple teapot might have an attribute
luaClickedFunction="purpleTeapotClicked".  I don't see any benefit in having
the designer prefix every level specific function they call with 'level1.' -
this will just cause more confusion - it's obvious from the context of the
functions and objectnames whether something is a global or level-specific..
ideally the solution would be completely transparent at the level design
level.

Ricky





-----Original Message-----
From: [hidden email]
[mailto:[hidden email]] On Behalf Of Eike Decker
Sent: 24 March 2010 15:01
To: Lua list
Subject: Re: Cleaning up specific lua scripts

Loading the levels with a different environment is surely the way to
go to provide simple measures to get rid of level dependent data that
is no longer needed once the level is finished. Nothing sucks more
than data / functions that still roam around at a later point. I can
imagine a lot of bugginess with that way of development, e.g. one
level defines a certain function which the following level does not
handle - calling such a function (belonging to a previous level) ends
up in a pretty undefined state. If everything is global, of course,
the outcome can be that everything works... but well, not really that
nice if you have to load level 3 before being able to load level 4.

However, the convenience that you are asking for, the ability to call
level dependent functions as globals, is however somewhat puzzling me
- because it doesn't seem to me to be a huge convenience at all and
despite the small win, it comes with a huge lack of transparency. The
statement

level.dosomething()

vs.

dosomething()

is carrying much more information that is useful when reading code.
The latter one is indistinguishable from anything else that's being
defined by your application. It looks like an application function,
but it's not. The first one however makes a clear statement: It's a
level's function (If you are really lazy, you could shorten it to
something like "lvl.", "lv." or even "l." - still, it would remain
informative).

Whenever someone else is supposed to read / write code, this small
statement will be a blessing. I can perfectly visualize someone
editing the files and regularly saying things like "WTF does this
function come from and why doesn't it exist anymore?" mixed with agony
and pain, trying to find out what should be done to get the
application running.

Usually, it's good to be a lazy programmer, but I wonder if you aren't
asking for too much laziness here...

Cheers,
Eike

PS.: you could always set the level's environment table as the
environment table of your application code (after it has been loaded!)
so that you would be able to call level function without any
prefixing, though you might end up writing values to your level
environments within code that is actually supposed to be application
values, so this would just be a swap of prefixing variables.

2010/3/24 Ricky Haggett <[hidden email]>:

> Hey everyone, (and thanks very much to all of you for the help!)
>
> So, it seems like there a couple of equivalent options. One is using
> setfenv, and then linking in the global stuff I need (as in Enrico's
> solution below or Fabio's code)..
>
> Another is maybe to put make my level script files modules - i.e. put
> module("level1", package.seeall) at the top of level1.lua and then require
> "level1" after that level is loaded..
>
> But it seems like, either way, I would then have to access all the
> functions/objects in my level code with level1.a().  This is problematic,
> because I already have a load of places in xml files (which describe the
> level graphically and structurally) where I am just calling a();  .. which
I

> don't want to change..
>
> An ideal solution would be a module-like structure, but where all the
> functions in my level remain global (accessible from the outside without
> doing level.a() ) ..
>
>
>
>
>
> Also, as an aside, I'm loading my scripts in C, with
> luaL_loadbuffer+lua_pcall or luaL_dostring. It seems that these functions
> return only an error int - is there no C equivalent for the lua code:
> proc,err = loadstring(script) ?
>
> Cheers,
>
> Ricky
>
>
>
>
> -----Original Message-----
> From: [hidden email]
> [mailto:[hidden email]] On Behalf Of Enrico Colombini
> Sent: 24 March 2010 13:08
> To: Lua list
> Subject: Re: Cleaning up specific lua scripts
>
> Ricky Haggett wrote:
>> How would I go about loading the level script into a table?
>
> Here is a simple example. This is your level:
>
> -------------------------
> -- this is "level1.lua"
>
> function a()
>     b()
> end
>
> function b()
>     print("Ships: " .. ships)
> end
>
> ships = 5
>
> -------------------------
>
> and this is the level loader:
>
> -------------------------
> -- this is "loader.lua"
>
> level = {} -- the table containing a level
>
> -- read "lev1.lua" into level
> local t = assert(loadfile("lev1.lua")) -- could use loadstring
> setfenv(t, level)
> local r, err = pcall(t) -- execute loaded chunk to define stuff
> r = r or error('error reading level')
>
> -- add global functions to level (just an example)
> level.print = print
>
> -- call function a() from outside the level table
> level.a()
>
> -------------------------
>
>> Also, if I used setfenv to change the environment, aren't I just moving
> the
>> problem? If I then have to add global functions to this same table, how
> will
>> I differentiate them from the level functions when I need to clean up?
>
> No need to differentiate, just throw away the level table:
>
>   level = nil
>
> If you must access many global values/functions, a trick such as Steve
> suggested would be better than copying them to 'level'. The copy, on the
> other hand, allows fine control over global accesses (there are other,
> possibly better, intermediate solutions).
>
>> I am thinking maybe my solution is going to be (shallow) copying _G
before
>> loading a level, then comparing the copy with _G after the level load.
Any
>> objects which don't exist in the copy, but do exist in _G should be the
>> level-specific ones
>
> Too much work, I prefer lazy solutions :-)
>
>   Enrico
>
>

Reply | Threaded
Open this post in threaded view
|

Re: Cleaning up specific lua scripts

Jonathan Castello-2
On Wed, Mar 24, 2010 at 8:18 AM, Ricky Haggett <[hidden email]> wrote:

> I don't think having level.doSomething adds much to the readability. To give
> a more concrete (and made up :) example, say there's a level (defined in
> level1.xml and level1.lua) which contains a purple teapot. And the xml tag
> which describes the purple teapot might have an attribute
> luaClickedFunction="purpleTeapotClicked".  I don't see any benefit in having
> the designer prefix every level specific function they call with 'level1.' -
> this will just cause more confusion - it's obvious from the context of the
> functions and objectnames whether something is a global or level-specific..
> ideally the solution would be completely transparent at the level design
> level.
>
> Ricky

You could run the scripts marked in the XML files using the level
table as your execution environment, following Enrico's technique.
Something like this:

----
do
  local function inner_exec()
    dosomething()
  end
  function level_exec(leve)
    setfenv(inner_exec, level)
    return inner_exec()
  end
end

level = {}

-- load the file
local func = assert(loadfile("lev1.lua"))
setfenv(func, level)
local r, err = pcall(func)
r = r or error('error reading level: ' .. err)

 -- do stuff in the context of the level
level_exec(level)
----

That's the basic idea, anyways... Notice how inner_exec calls
dosomething(), not level.dosomething(), because when it's its
environment is the same as the level file's. You could make that
function as simple or complex as you want, or use a closure per level
instead of sharing the same one, but that's the basic idea.

~Jonathan
Reply | Threaded
Open this post in threaded view
|

Re: Cleaning up specific lua scripts

Pierre-Yves Gérardy
In reply to this post by Ricky Haggett
On Wed, Mar 24, 2010 at 12:30, Ricky Haggett <[hidden email]> wrote:
> Yep, we could store a table of all of the functions / tables within a
> specific level script, but it would be awkward for the designers to
> maintain, and a potential source of error...

You could also do it the other way around:
after you've loaded your global scripts do

globals={}

function cleanLevel()
    for k,_ in pairs(_G) do
        if not globals[k] then _G[k] = nil
    end
end

for k,_ in pairs(_G) do
    globals[k]=true
end

globals will index all globals present at that point (including itself
and cleanlevel() ), and cleanLevel() will only delete the globals not
referenced there. Case solved :-)

Cheers,
-- Pierre-Yves
Reply | Threaded
Open this post in threaded view
|

RE: Cleaning up specific lua scripts

Ricky Haggett
Pierre,

This is a simpler and more elegant version of what I implemented yesterday afternoon, which also seemed to do the job, albeit with more fuss..  I'm definitely going to upgrade to your code today..

Thanks,

Ricky

-----Original Message-----
From: [hidden email] [mailto:[hidden email]] On Behalf Of Pierre-Yves Gérardy
Sent: 24 March 2010 19:27
To: Lua list
Subject: Re: Cleaning up specific lua scripts

On Wed, Mar 24, 2010 at 12:30, Ricky Haggett <[hidden email]> wrote:
> Yep, we could store a table of all of the functions / tables within a
> specific level script, but it would be awkward for the designers to
> maintain, and a potential source of error...

You could also do it the other way around:
after you've loaded your global scripts do

globals={}

function cleanLevel()
    for k,_ in pairs(_G) do
        if not globals[k] then _G[k] = nil
    end
end

for k,_ in pairs(_G) do
    globals[k]=true
end

globals will index all globals present at that point (including itself
and cleanlevel() ), and cleanLevel() will only delete the globals not
referenced there. Case solved :-)

Cheers,
-- Pierre-Yves

No virus found in this incoming message.
Checked by AVG - www.avg.com
Version: 9.0.791 / Virus Database: 271.1.1/2767 - Release Date: 03/24/10 07:33:00

Reply | Threaded
Open this post in threaded view
|

RE: Cleaning up specific lua scripts

Aurora-4

Ricky , what's the time now?
 
 
2010-03-25

bravefly

发件人: Ricky Haggett
发送时间: 2010-03-25  16:14:42
收件人: 'Lua list'
抄送:
主题: RE: Cleaning up specific lua scripts
Pierre,
This is a simpler and more elegant version of what I implemented yesterday afternoon, which also seemed to do the job, albeit with more fuss..  I'm definitely going to upgrade to your code today..
Thanks,
Ricky
-----Original Message-----
From: [hidden email] [mailto:[hidden email]] On Behalf Of Pierre-Yves Gérardy
Sent: 24 March 2010 19:27
To: Lua list
Subject: Re: Cleaning up specific lua scripts
On Wed, Mar 24, 2010 at 12:30, Ricky Haggett <[hidden email]> wrote:
> Yep, we could store a table of all of the functions / tables within a
> specific level script, but it would be awkward for the designers to
> maintain, and a potential source of error...
You could also do it the other way around:
after you've loaded your global scripts do
globals={}
function cleanLevel()
    for k,_ in pairs(_G) do
        if not globals[k] then _G[k] = nil
    end
end
for k,_ in pairs(_G) do
    globals[k]=true
end
globals will index all globals present at that point (including itself
and cleanlevel() ), and cleanLevel() will only delete the globals not
referenced there. Case solved :-)
Cheers,
-- Pierre-Yves
No virus found in this incoming message.
Checked by AVG - www.avg.com 
Version: 9.0.791 / Virus Database: 271.1.1/2767 - Release Date: 03/24/10 07:33:00
Reply | Threaded
Open this post in threaded view
|

RE: Cleaning up specific lua scripts

Ricky Haggett

Here in rainy old London it just turned 9am :)

 

From: [hidden email] [mailto:[hidden email]] On Behalf Of bravefly
Sent: 25 March 2010 08:18
To: Lua list
Subject: RE: Cleaning up specific lua scripts

 

Ricky , what's the time now?

 

 

2010-03-25

bravefly


发件人: Ricky Haggett

发送时间: 2010-03-25  16:14:42

收件人: 'Lua list'

抄送:

题: RE: Cleaning up specific lua scripts

Pierre,

This is a simpler and more elegant version of what I implemented yesterday afternoon, which also seemed to do the job, albeit with more fuss..  I'm definitely going to upgrade to your code today..

Thanks,

Ricky

-----Original Message-----

From: [hidden email] [mailto:[hidden email]] On Behalf Of Pierre-Yves Gérardy

Sent: 24 March 2010 19:27

To: Lua list

Subject: Re: Cleaning up specific lua scripts

On Wed, Mar 24, 2010 at 12:30, Ricky Haggett <[hidden email]> wrote:

> Yep, we could store a table of all of the functions / tables within a

> specific level script, but it would be awkward for the designers to

> maintain, and a potential source of error...

You could also do it the other way around:

after you've loaded your global scripts do

globals={}

function cleanLevel()

    for k,_ in pairs(_G) do

        if not globals[k] then _G[k] = nil

    end

end

for k,_ in pairs(_G) do

    globals[k]=true

end

globals will index all globals present at that point (including itself

and cleanlevel() ), and cleanLevel() will only delete the globals not

referenced there. Case solved :-)

Cheers,

-- Pierre-Yves

No virus found in this incoming message.

Checked by AVG - www.avg.com 

Version: 9.0.791 / Virus Database: 271.1.1/2767 - Release Date: 03/24/10 07:33:00

No virus found in this incoming message.
Checked by AVG - www.avg.com
Version: 9.0.791 / Virus Database: 271.1.1/2767 - Release Date: 03/24/10 07:33:00

Reply | Threaded
Open this post in threaded view
|

Re: Cleaning up specific lua scripts

Pierre-Yves Gérardy
In reply to this post by Ricky Haggett
I'm glad I could help :-)
You could add a collectgarbage() call at the end of cleanLevel().

That said, the setfenv() solution is probably more elegant and more robust.




local protectMT = {__newindex=error}
local levelMT = {index=_G}

local MTindex

local function protectTable(tbl,index)
    for k,v in pairs(tbl) do
        if
            type(v)==table and
            getmetatable(v) ~= protectMT -- prevents from getting lost in
        then                             -- circular references.
            index[k]={getmetatable(v),{})
            protectTable(v,index[k][2])
            setmetatable(v,protectMT)
        end
    end
end

local function unprotectTable(tbl,index)
    for k,v in pairs(tbl) do
         if type(v)=="table" then
              setmetatable(v,index[k][1])
              unprotectTable(v,index[k][2])
        end
    end
end


local loadLevel = function(levelpath)
    local levelenv=setmetatable({},levelMT)
    local levelcode=loadfile(levelpath)
    setfenv(levelcode,levelenv)
    protectTable(_G,MTindex)
    -- it's now impossible to add or modify any global, or any
subtable of a global.
    local ret = levelcode()
    unprotectTable(_G,MTindex)
    return ret
end

To give back the control from a level, return from it's main chunk
(e.g., return true on level completion and false on death).

local success = loadlevel(level1)
collectgarbage()

if success then
    loadNextLevel()
else
    restartLevel()
end

The above code is not tested and may contain syntax or semantic
errors, but you get the idea :-)

Cheers,
-- Pierre-Yves



On Thu, Mar 25, 2010 at 09:14, Ricky Haggett <[hidden email]> wrote:

> Pierre,
>
> This is a simpler and more elegant version of what I implemented yesterday afternoon, which also seemed to do the job, albeit with more fuss..  I'm definitely going to upgrade to your code today..
>
> Thanks,
>
> Ricky
>
> -----Original Message-----
> From: [hidden email] [mailto:[hidden email]] On Behalf Of Pierre-Yves Gérardy
> Sent: 24 March 2010 19:27
> To: Lua list
> Subject: Re: Cleaning up specific lua scripts
>
> On Wed, Mar 24, 2010 at 12:30, Ricky Haggett <[hidden email]> wrote:
>> Yep, we could store a table of all of the functions / tables within a
>> specific level script, but it would be awkward for the designers to
>> maintain, and a potential source of error...
>
> You could also do it the other way around:
> after you've loaded your global scripts do
>
> globals={}
>
> function cleanLevel()
>    for k,_ in pairs(_G) do
>        if not globals[k] then _G[k] = nil
>    end
> end
>
> for k,_ in pairs(_G) do
>    globals[k]=true
> end
>
> globals will index all globals present at that point (including itself
> and cleanlevel() ), and cleanLevel() will only delete the globals not
> referenced there. Case solved :-)
>
> Cheers,
> -- Pierre-Yves
>
> No virus found in this incoming message.
> Checked by AVG - www.avg.com
> Version: 9.0.791 / Virus Database: 271.1.1/2767 - Release Date: 03/24/10 07:33:00
>
>
12