Archived

This topic is now archived and is closed to further replies.

squeek

Code Tips and Tricks

46 posts in this topic

require is required

So true, so true...

 

If you add a component to a player prefab using AddSimPostInit, OnSave and OnLoad for that component will simply not work (note: this is only the case for SimPostInit, not PrefabPostInit). I have not investigated the reason why [...]

The reason is when the SimPostInit is fired, the savedata has already been loaded and passed to the existing OnLoad methods at the time, so it's too late for an added component to catch any savedata. OnSave will actually work, but since OnLoad doesn't it is pointless.

 

Utilizing zero-time schedules

One more importantant use case of zero-time schedules in prefab post inits is that when they run the savedata for the prefab and its components has already been loaded, which ensures both that you'll have access to the proper loaded state of the prefab and that OnLoad/OnLoadPostPass/LoadPostPass methods won't override changes you make.

Very useful tips, that will certainly be essential for many modders.

1 person likes this

Share this post


Link to post
Share on other sites

In addendum to your mention of require...

 

Many lua functions will not work without loading the correct requirements first, for example...

 

math = require "math"

string = require "string"

table = require "table"

 

These allow you to do some very important functions such as math.floor() or string.find() or table.insert()

 

Apparently I'm an idiot, you do not need to require these though you do need to access them through the GLOBAL variable, like so. 

 

math = GLOBAL.math

string = GLOBAL.string

table = GLOBAL.table

 

/Sits Quietly in the corner

1 person likes this

Share this post


Link to post
Share on other sites

In addendum to your mention of require...

 

Many lua functions will not work without loading the correct requirements first, for example...

 

math = require "math"

string = require "string"

table = require "table"

 

These allow you to do some very important functions such as math.floor() or string.find() or table.insert()

Really  o.0

I never "require" any of these and they always work. I assumed that they were either required in a very basic vanilla file or are just standard.

1 person likes this

Share this post


Link to post
Share on other sites

Really  o.0

I never "require" any of these and they always work. I assumed that they were either required in a very basic vanilla file or are just standard.

Those are all pre-required by Lua itself* when it starts, and they are placed in the mod environment by the game. So you don't need to require() them, but at the same time it is harmless to do so. For other modules of the standard library, writing things like

debug = GLOBAL.require "debug"
is functionally identical to

debug = GLOBAL.debug
from within the mod environment (in the global one, once again it is redundant/harmless).

* Aaaaactually, since Lua is embedded in the engine, it is it that calls luaL_openlibs(L) to load them, but I digress, the point is they have been require()'d already before any code runs.

1 person likes this

Share this post


Link to post
Share on other sites

Those are all pre-required by Lua itself* when it starts, and they are placed in the mod environment by the game. So you don't need to require() them, but at the same time it is harmless to do so. For other modules of the standard library, writing things like

debug = GLOBAL.require "debug"
is functionally identical to

debug = GLOBAL.debug
from within the mod environment (in the global one, once again it is redundant/harmless).

* Aaaaactually, since Lua is embedded in the engine, it is it that calls luaL_openlibs(L) to load them, but I digress, the point is they have been require()'d already before any code runs.

 

weird cause when i don't require them i get errors...

 

Ah I see what you mean so instead of doing the require i can do

 

math = GLOBAL.math

string = GLOBAL.string

table = GLOBAL.table

 

 

That will teach me to offer any knowledge about lua, I know  what works but I honestly rarely know why as all my knowledge has been earned by looking at code and messing with my code until it runs. Sometimes like the above I come up with solutions that work but are not the correct solutions.

 

 

@simplex

&

@squeek

 

Speaking of GLOBAL do you you guys know why sometimes I can't access the GLOBAL variable from files for example I built a component file and I could not access it I ended up having to create a funtion that passed GLOBAL to my compnent and stored it so my functions could use it. 

../mods/amanager/scripts/components/amanager_2.lua:258: variable 'GLOBAL' is not declaredLUA ERROR stack traceback:        =[C] in function 'error'../data/scripts/strict.lua(23,1)../mods/amanager/scripts/components/amanager_2.lua(258,1) in function 'EvalExplored'

This function isn't running while loading it happens during the game so GLOBAL definitely exists but for some reason the component can't access the variable GLOBAL.

Share this post


Link to post
Share on other sites

Ah I see what you mean so instead of doing the require i can do

 

math = GLOBAL.math

string = GLOBAL.string

table = GLOBAL.table

You don't need that. Those are already in the mod environment. Not every module of the standard library is, but those are.

From mods.lua:

function CreateEnvironment(modname)	local modutil = require("modutil")	require("recipe") -- for Ingredient	local env = 	{		TUNING=TUNING,		CHARACTERLIST = CHARACTERLIST,		modname = modname,		pairs = pairs,		ipairs = ipairs,		print = print,		math = math,		table = table,		type = type,		string = string,		tostring = tostring,		Class = Class,		GLOBAL = _G,		MODROOT = "../mods/"..modname.."/",		Prefab = Prefab,		Asset = Asset,		Ingredient = Ingredient,	}	env.env = env	--install our crazy loader!	env.modimport = function(modulename)		print("modimport: "..env.MODROOT..modulename)        local result = kleiloadlua(env.MODROOT..modulename)		if result == nil then			error("Error in modimport: "..modulename.." not found!")		elseif type(result) == "string" then			error("Error in modimport: "..ModInfoname(modname).." importing "..modulename.."!\n"..result)		else        	setfenv(result, env.env)            result()        end	end	modutil.InsertPostInitFunctions(env)	return envend

 

Speaking of GLOBAL do you you guys know why sometimes I can't access the GLOBAL variable from files for example I built a component file and I could not access it I ended up having to create a funtion that passed GLOBAL to my compnent and stored it so my functions could use it.

Component code already runs in the global environment. Instead of writing GLOBAL.some_var just write some_var. The global environment is also stored in a variable within the global environment, called _G, but this is rarely relevant. Usually, you'd only use it in circumstances such as

local x = "local value"_G.x = "global value"
1 person likes this

Share this post


Link to post
Share on other sites

You don't need that. Those are already in the mod environment. Not every module of the standard library is, but those are.

 

Can you explain then why without requiring the string or math or table some of my mods will crash saying no such function exists? That is why i started using require on them as it happened and that is what fixed it?

 

 

Thanks for the tip about GLOBAL btw

Share this post


Link to post
Share on other sites

Can you explain then why without requiring the string or math or table some of my mods will crash saying no such function exists? That is why i started using require on them as it happened and that is what fixed it?

 

 

Thanks for the tip about GLOBAL btw

Can you tell me one of your mods which crashes without it? 'cause I'm quite curious what's going on there.

 

From mods.lua:function CreateEnvironment(modname)

    local modutil = require("modutil")

    require("recipe") -- for Ingredient

    local env =

    {

        TUNING=TUNING,

        CHARACTERLIST = CHARACTERLIST,

        modname = modname,

        pairs = pairs,

        ipairs = ipairs,

        print = print,

        math = math,

        table = table,

        type = type,

        string = string,

        tostring = tostring,

        Class = Class,

        GLOBAL = _G,

        MODROOT = "../mods/"..modname.."/",

        Prefab = Prefab,

        Asset = Asset,

        Ingredient = Ingredient,

    }

    env.env = env

    --install our crazy loader!

    env.modimport = function(modulename)

        print("modimport: "..env.MODROOT..modulename)

local result = kleiloadlua(env.MODROOT..modulename)

        if result == nil then

            error("Error in modimport: "..modulename.." not found!")

        elseif type(result) == "string" then

            error("Error in modimport: "..ModInfoname(modname).." importing "..modulename.."!\n"..result)

        else

    setfenv(result, env.env)

result()

end

    end

    modutil.InsertPostInitFunctions(env)

    return env

end

Wait, TUNING is in the MOD env?

Share this post


Link to post
Share on other sites

Can you explain then why without requiring the string or math or table some of my mods will crash saying no such function exists? That is why i started using require on them as it happened and that is what fixed it?]

Can you tell me one of your mods which crashes without it? 'cause I'm quite curious what's going on there.

This.

Wait, TUNING is in the MOD env?

Yes, as far as I can remember. ;P

Share this post


Link to post
Share on other sites

Can you tell me one of your mods which crashes without it? 'cause I'm quite curious what's going on there.

When I can find it I will.

 

Like I said it happened so I made assumptions because doing the require trick fixed it I then started doing it in all my files since that is what I thought fixed it though I just noticed that in some files I missed declaring those and still used the functions so had I been paying attention I would have known there was something up.

 

Its also possible I made two changes and the other change fixed it but I almost always make one change at a time when I'm tracking down an error as multiple changes won't help me find out whats wring so honestly I doubt it but who knows. I'm going to go through my mods and remove all the requires for those and see what happens.

Share this post


Link to post
Share on other sites

Yes, as far as I can remember. ;P

Yes, I needed to check it myself before I believed it though  xD

But why TUNING and not STRINGS? I don't get it...

EDIT: Also Ingredient but not Recipe... I'm confused...

Share this post


Link to post
Share on other sites

Yes, I needed to check it myself before I believed it though  xD

But why TUNING and not STRINGS? I don't get it...

I don't know... When the modding API roadmap was being designed, I argued for the presence of STRINGS (along with quite a few other things), but the only thing that was ever added to the mod environment other than post/pre init hooks was Ingredient. ;P

Share this post


Link to post
Share on other sites

I don't know... When the modding API roadmap was being designed, I argued for the presence of STRINGS (along with quite a few other things), but the only thing that was ever added to the mod environment other than post/pre init hooks was Ingredient. ;P

I think I remember... I remember people saying "either everything or nothing" which makes sense for me, because I know that within a week I will have forgotten that Ingredient is there and will call it like I usually do.

Share this post


Link to post
Share on other sites

I removed all the requires and they still work so no clue how it happened I distinctly remember that fix working so my guess is I made the stupid mistake of doing two potential fixes at once one of which worked and the other being adding the require which was redundant and covered up what ever else actually worked. :(

 

 

Alright I figure it out 

 

In one file at some point I was using tostring and tonumber I had delclared tonumber first so my assumption is as tonumber does have to be required (I just checked it) that to fix it it I require'd tonumber and tostring at the same time and then for some reason the tostring require stuck in my head and the next time I need a string function I for some reason thought I needed to require that as well and then it just kept snowballing and since the requires didn't stop anything from running I never questioned it.

Share this post


Link to post
Share on other sites

Ahhh, okay that makes sense. Though I don't know where tonumber would be of any help  : P

Share this post


Link to post
Share on other sites

Alright I figure it out 

 

In one file at some point I was using tostring and tonumber I had delclared tonumber first so my assumption is as tonumber does have to be required (I just checked it) that to fix it it I require'd tonumber and tostring at the same time and then for some reason the tostring require stuck in my head and the next time I need a string function I for some reason thought I needed to require that as well and then it just kept snowballing and since the requires didn't stop anything from running I never questioned it.

tonumber is a basic Lua function not a part of any library. Not even sure what require you'd even use to get it (require "tonumber" gives me an module 'tonumber' not found error).

In modmain.lua:

print("tonumber mod env", tostring(tonumber))print("tonumber global env", tostring(GLOBAL.tonumber))
outputs:

../mods/CodeTipsAndTricks/modmain.lua(1,1) tonumber mod env	nil	../mods/CodeTipsAndTricks/modmain.lua(2,1) tonumber global env	function: 0E3AECA0
Basically, you should never have to use require for default Lua functions or any Lua library.

Share this post


Link to post
Share on other sites

tonumber is a basic Lua function not a part of any library. Not even sure what require you'd even use to get it (require "tonumber" gives me an module 'tonumber' not found error).

In modmain.lua:

print("tonumber mod env", tostring(tonumber))print("tonumber global env", tostring(GLOBAL.tonumber))
outputs:

../mods/CodeTipsAndTricks/modmain.lua(1,1) tonumber mod env	nil	../mods/CodeTipsAndTricks/modmain.lua(2,1) tonumber global env	function: 0E3AECA0
Basically, you should never have to use require for default Lua functions or any Lua library.

 

Ok I know I had that require in my mod...

 

NM I though that is what I had done but checking an old version what I had done was actually 

 

require = GLOBAL.require

tonumber = GLOBAL.tonumber

tostring= GLOBAL.tostring

 

So basically as you can see this whole thing is a comedy of errors with me the fool as I said much earlier I'm not entirely sure where I cam across this misconception I just know I did. Obviously it was my mistake.

Share this post


Link to post
Share on other sites

Ahhh, okay that makes sense. Though I don't know where tonumber would be of any help  : P

Very, very rarely it's useful. For example, I used it here to fix a faulty Spriter (.scml) file. But that was an external script, not part of a mod. ;P

1 person likes this

Share this post


Link to post
Share on other sites

Adding components to all players

credit to simplex for this workaround

If you add a component to a player prefab using AddSimPostInit, OnSave and OnLoad for that component will simply not work (note: this is only the case for SimPostInit, not PrefabPostInit). I have not investigated the reason why, but to get around it you should use the following workaround:

-- need to add the component in here, otherwise OnSave doesn't work rightAddPrefabPostInit("world", function(inst)	GLOBAL.assert( GLOBAL.GetPlayer() == nil )	local player_prefab = GLOBAL.SaveGameIndex:GetSlotCharacter()	-- Unfortunately, we can't add new postinits by now. So we have to do	-- it the hard way...	GLOBAL.TheSim:LoadPrefabs( {player_prefab} )	local oldfn = GLOBAL.Prefabs[player_prefab].fn	GLOBAL.Prefabs[player_prefab].fn = function()		local inst = oldfn()		-- Add components here.		inst:AddComponent("yourcomponent")		return inst	endend)

This technique no longer works due to how the DLC re-loads all prefabs twice for every mod that is enabled. See: this post

EDIT: No longer the case (as of March 3rd, 2014 patch). This technique still works.

Share this post


Link to post
Share on other sites

Oh one of this guys who really understand how lua works is still here :-)

 

Can i ask something from guru? whether there is any possibility to determine the existence of a custom(non vanilla) component?

like PrefabExists but for components.

Share this post


Link to post
Share on other sites

Oh one of this guys who really understand how lua works is still here :-)

 

Can i ask something from guru? whether there is any possibility to determine the existence of a custom(non vanilla) component?

like PrefabExists but for components.

You can check if a file exists in any of the search paths (main game files, any mod folders) like so:

softresolvefilepath("scripts/components/componentfiletofind.lua")
It'll return nil if it's not found, and the filename/filepath if it is (see util.lua for where softresolvefilepath is defined).

As an example, here's what a ComponentExists function would look like:

function ComponentExists(component_name)	return softresolvefilepath("scripts/components/"..component_name..".lua") ~= nilend

Share this post


Link to post
Share on other sites

hm but mod, which contain this components can be disabled, but i got your idea.

 

i was hope for some lua magic with getmetatable or such for testing local variable Components inside entityscript.lua

Share this post


Link to post
Share on other sites

If you want to test for a specific mod being enabled, you can use this:

 

-- checks if a mod with the given name is enabled (name parameter needs to match what's in the mod's modinfo.lua)local function IsModEnabled(name)	for _, moddir in ipairs( _G.KnownModIndex:GetModsToLoad() ) do		local its_modinfo = _G.KnownModIndex:GetModInfo(moddir)		if its_modinfo.name == name then			return true		end	end	return falseendif IsModEnabled( "Name Of The Mod" ) then	-- do stuffend
(the above code was adapted from some code that simplex wrote for his Mod Testing Toolbox mod)

I'm not familiar with any methods of extracting local variables from a file. I'm also not really sure why that Components table in entityscript.lua is local (or why there's no global method of accessing it). Even if there was, though, it wouldn't be that useful because components only get loaded into it after they've been used (so if a components/customcomponent.lua existed, it wouldn't be added into the Components table until after it was added to a prefab).

Share this post


Link to post
Share on other sites