Jump to content

Just wanna know why would there be so many "inst"


Recommended Posts

Just started modding in DST. But after one day's workout, I can only manage to modify some tuning data.

Tried really hard to look into other's mods. However, whenever things get complicated, there will be an "inst" attached to (be the parameter of) the functions. Then I was really puzzled.

Is there any possibility I can understand this in a convenient way? If I can be made to understand how mainmod.lua works, it would be even better. I'm programming in C#, driven crazy by lua..... Appreciation is put in advance if anyone can help!!

Link to comment
Share on other sites

"inst" is just a variable.

Lua has this type called "table". It's basically a pointer to a container, like a vector or a list.

But you don't dereference it, you just use the pointer as the container itself.

If you have

local a = {1, 2}
local b = {3, 4}

b = a

What happens now is that b now points to the table a. If I do b[1] = 5, then a[1] will be 5 too, because the referred table is the same.

And the table {3, 4} will end up for garbage collection.

-- function assigns inst to could_be_named_inst variable argument
-- we could use inst to not change names for every single function
-- after this, the table pointed at by inst will have the "world" value on the "hello" key
local function DoThisToEntity(could_be_named_inst)
	could_be_named_inst.hello = world
end


-- Ask the C++ code to provide me something to write on
local inst = CreateEntity()

-- I got my table, do something with it
DoThisToEntity(inst)

You track down where a function is called, to know what the argument is supposed to be.

Since most functions have "inst" as a table argument referring to an entity, you can skip the tracking part, because you can assume what the argument would probably be.

I suppose this could be equated more to a C# reference instead of a pointer.

Link to comment
Share on other sites

5 hours ago, DarkXero said:

"inst" is just a variable.

Lua has this type called "table". It's basically a pointer to a container, like a vector or a list.

But you don't dereference it, you just use the pointer as the container itself.

If you have


local a = {1, 2}
local b = {3, 4}

b = a

What happens now is that b now points to the table a. If I do b[1] = 5, then a[1] will be 5 too, because the referred table is the same.

And the table {3, 4} will end up for garbage collection.


-- function assigns inst to could_be_named_inst variable argument
-- we could use inst to not change names for every single function
-- after this, the table pointed at by inst will have the "world" value on the "hello" key
local function DoThisToEntity(could_be_named_inst)
	could_be_named_inst.hello = world
end


-- Ask the C++ code to provide me something to write on
local inst = CreateEntity()

-- I got my table, do something with it
DoThisToEntity(inst)

You track down where a function is called, to know what the argument is supposed to be.

Since most functions have "inst" as a table argument referring to an entity, you can skip the tracking part, because you can assume what the argument would probably be.

I suppose this could be equated more to a C# reference instead of a pointer.

Hugely thx.

If that is the case, I'm guessing that lua isn't strict with types of variables then... Since non-parameter had ever been declared to be "table inst". And number aslo seems not to be declared by "number blabla", as if whatever an identifier is would be considered by the computer itself. Maybe this could be the maginificent difference between C.

By the way I'm kinda puzzled again by the mods I found myself.

Like, you see, if I'm supposed to change some values of the game, I can do:

local TUNING = GLOBAL.TUNING

TUNING.WILSON_HEALTH = 155

Then the character's hp gets increased.

And because of this, my understanding is, mainmod.lua will replace some identical items with the vanilla game.

But if I'm in want of modifying some of the tables (specifically the rocks' more loot):

SetSharedLootTable( 'rock1',
{
    {'rocks',  1.00},
    {'rocks',  1.00},
    {'rocks',  1.00},
    {'rocks',  1.00},
    {'rocks',  1.00},
    {'rocks',  1.00},
    {'nitre',  1.00},
    {'flint',  1.00},
    {'nitre',  0.25},
    {'flint',  0.60},
})

I put this in the main, and the game simply crashed in no time.

Maybe I should use something liek GLOBAL.SetSharedLootTable? I tried but it just ain't working.

Then my understanding is that only the values get replaced, tables and functions does not accept overriding.

So I have absolutely no idea whether I'm getting right on this. But if it is wrong, how am I supposed to override these tables or functions like "local function OnWork(inst, worker, workleft)" of the rocks — then maybe I can make a rock infinite?

Link to comment
Share on other sites

1 hour ago, wch2 said:

If that is the case, I'm guessing that lua isn't strict with types of variables then... Since non-parameter had ever been declared to be "table inst". And number aslo seems not to be declared by "number blabla", as if whatever an identifier is would be considered by the computer itself. Maybe this could be the maginificent difference between C.

A variable can be anything. You just assign the value and that's it.

1 hour ago, wch2 said:

Then the character's hp gets increased.

And because of this, my understanding is, mainmod.lua will replace some identical items with the vanilla game.

modmain.lua is a file with lua code that gets run.

It has its own environment (like namespace std of C++) which holds the functions you see in modutil.lua that follow "env.", like AddPrefabPostInit, and it also has access to the GLOBAL table that holds all variables defined.

The game declares the TUNING table like:

TUNING = {}

in tuning.lua. So the variable gets added to the GLOBAL table.

Then you see the game populate the table with numbers that it will use during gameplay.

Like how the WILSON_HEALTH key is used to introduce the 150.

Knowing that you have access to GLOBAL, and that TUNING exists, you can access the table and modify the value, with:

local TUNING = GLOBAL.TUNING

TUNING.WILSON_HEALTH = 155

You retrieve the TUNING table reference from GLOBAL, and then you change the value you want.

1 hour ago, wch2 said:

I put this in the main, and the game simply crashed in no time.

Maybe I should use something liek GLOBAL.SetSharedLootTable? I tried but it just ain't working.

Then my understanding is that only the values get replaced, tables and functions does not accept overriding.

If you use SetSharedLootTable, lua will look into env, and not GLOBAL, for the function, and won't find it.

So when you call the function SetSharedLootTable(), it crashes, because you can't call a nil value.

The second issue, is that when you do GLOBAL.SetSharedLootTable, the function wasn't inserted into GLOBAL yet.

So it has a nil value again.

In this particular case you want to do this:

-- Variable holding a function
local MyPostInit = function()
	-- Variable holding a table, that holds tables, that hold pairs string-float
	local MyLootTable = {
		{'rocks',  1.00},
		{'rocks',  1.00},
		{'rocks',  1.00},
		{'rocks',  1.00},
		{'rocks',  1.00},
		{'rocks',  1.00},
		{'nitre',  1.00},
		{'flint',  1.00},
		{'nitre',  0.25},
		{'flint',  0.60},
	}
	-- SetSharedLootTable is ready now, use it
	-- Give it the id, and the new loot
	GLOBAL.SetSharedLootTable('rock1', MyLootTable)
	-- Check the log to see successful message
	print("Rocks ready!")
end

-- This AddSimPostInit function is provided to us by the mod environment
-- Very much like how lua provides us with pairs, table.insert, type, functions for GLOBAL usage
-- It adds the function we give it, to a table that the game loops through after the world loads
-- This means lootdropper.lua in scripts/components and rocks.lua in scripts/prefabs were already loaded
-- Which respectively means that SetSharedLootTable was loaded into GLOBAL, and that our "rock1" table will overwrite the previous one
AddSimPostInit(MyPostInit)
1 hour ago, wch2 said:

So I have absolutely no idea whether I'm getting right on this. But if it is wrong, how am I supposed to override these tables or functions like "local function OnWork(inst, worker, workleft)" of the rocks — then maybe I can make a rock infinite?

You can't override local variables, like how you can't access variables defined inside a IF statement from outside of it.

But in most cases, you can go around that kind of problems.

In this particular case:

-- Get a reference to globals
local SpawnPrefab = GLOBAL.SpawnPrefab
local TUNING = GLOBAL.TUNING

-- Copy and modify old OnWork function
local function OnWork(inst, worker, workleft)
	if workleft <= 0 then
		local pt = inst:GetPosition()
		SpawnPrefab("rock_break_fx").Transform:SetPosition(pt:Get())
		inst.components.lootdropper:DropLoot(pt)

		if inst.showCloudFXwhenRemoved then
			local fx = SpawnPrefab("collapse_small")
			fx.Transform:SetPosition(inst.Transform:GetWorldPosition())
		end

		inst:Remove()
	else
		inst.AnimState:PlayAnimation(
			(workleft < TUNING.ROCKS_MINE / 3 and "low") or
			(workleft < TUNING.ROCKS_MINE * 2 / 3 and "med") or
			"full"
		)
	end
	print("Mining in progress...")
end

-- These are the prefabs declared in rocks.lua
-- All of them use the baserock_fn function as common ground
-- In baserock_fn is where OnWork gets attached to the workable component as a callback during work
local prefabs_that_have_that_OnWork = {
	"rock1", "rock2", "rock_flintless", "rock_flintless_med", "rock_flintless_low", "rock_moon", "rock_petrified_tree",
	"rock_petrified_tree_med", "rock_petrified_tree_tall", "rock_petrified_tree_short", "rock_petrified_tree_old",
}

-- Iterate through the table
-- k will be a number in this case: 1, 2, 3, 4, 5, ...
-- v will be the string value
for k, v in pairs(prefabs_that_have_that_OnWork) do
	-- This mod environment function provides us with a way to give the game function to run on prefabs that it spawns
	-- With AddPrefabPostInit("rock1", fn), the game will run fn(inst) whenever it spawns a prefab named "rock1"
	AddPrefabPostInit(v, function(inst)
		-- We make use of that to set our new function, the way the old function did
		inst.components.workable:SetOnWorkCallback(OnWork)
	end)
end

 

Be prepared to smash your head against the wall repeatedly.

Link to comment
Share on other sites

4 hours ago, DarkXero said:

Be prepared to smash your head against the wall repeatedly.

Em... Come around to say thx with my head bleeding... Just joking :)

Although my head isn't bleeding, I'm having serious headache anyways.

-- Variable holding a function
local MyPostInit = function()
	-- Variable holding a table, that holds tables, that hold pairs string-float
	local MyLootTable = {
		{'rocks',  1.00},
		{'rocks',  1.00},
		{'rocks',  1.00},
		{'rocks',  1.00},
		{'rocks',  1.00},
		{'rocks',  1.00},
		{'nitre',  1.00},
		{'flint',  1.00},
		{'nitre',  0.25},
		{'flint',  0.60},
	}
	-- SetSharedLootTable is ready now, use it
	-- Give it the id, and the new loot
	GLOBAL.SetSharedLootTable('rock1', MyLootTable)
	-- Check the log to see successful message
	print("Rocks ready!")
end

-- This AddSimPostInit function is provided to us by the mod environment
-- Very much like how lua provides us with pairs, table.insert, type, functions for GLOBAL usage
-- It adds the function we give it, to a table that the game loops through after the world loads
-- This means lootdropper.lua in scripts/components and rocks.lua in scripts/prefabs were already loaded
-- Which respectively means that SetSharedLootTable was loaded into GLOBAL, and that our "rock1" table will overwrite the previous one
AddSimPostInit(MyPostInit)

So, I've managed to understand these.

A table can be changed and modified. But since this is a local table and the env doesn't provide us with straight-forward table changing functions and if we directly modify the table, the table won't get inserted to the game, we gotta do something like this to pack our table changing into a function and use the function the game provided to insert this function into the game...

This is understandable. But maybe we're not actually modifying a table? SetSharedLootTable is already a function....

I don't know what I'm talking about. I'm just getting definitely chaotic with my mind. Let's just leave this alone for now.

The principle of this is still replacement, right? I'm quite spliting hairs about this because I figured the game as a process and I wanna know what the game was doing. We finally inserted our own function "MyPostInit" by means of "AddSimPostInit" aiming to set the loot table by ourself.

However, "This means lootdropper.lua in scripts/components and rocks.lua in scripts/prefabs were already loaded" explains that "AddSimPostInit" will insert stuff after these things got loaded. So the "rock1" of the game, its loot has already been set by the codes defined in rocks.lua. If we do "AddSimPostInit" now, what is happening inside the game? Maybe..

game:" Nice! my rock will drop 3 rocks when mined"

me:" I want the rock to drop 6 rocks when mined!". And I input "AddSimPostInit" with evil laughter on my face.

game:" What? You mean 6 rocks? But I have already been ordered to keep it as 3 rocks."

me:" No! I wanna 6! Not 3! It's 6!"

game:" OKay...."

What do you say? And by the way, my log file is missing. Where is it?

( I'm also having big troubles with the second part modifying the function overriding, but let's get this through first and I need a bit more time to look into your explanations! Maybe ask you later... Never enough thx I'll give ya)

Link to comment
Share on other sites

11 hours ago, wch2 said:

A table can be changed and modified. But since this is a local table and the env doesn't provide us with straight-forward table changing functions and if we directly modify the table, the table won't get inserted to the game, we gotta do something like this to pack our table changing into a function and use the function the game provided to insert this function into the game...

This is understandable. But maybe we're not actually modifying a table? SetSharedLootTable is already a function....

The env doesn't provide us with anything to alter local variables in another scope.

Which you can see in complaint 3 here.

If you want direct access to the loot table going by id of rock1, you need to track down where it is.

Which is a GLOBAL variable with a table, that occurs very much like SetSharedLootTable.

In lootdropper.lua:

LootTables = {}
function SetSharedLootTable(name, table)
	LootTables[name] = table
end

So, as I said, these globals aren't actually globals until the game loads lootdropper.lua.

local MyPostInit = function()
	local loot_table = GLOBAL.LootTables["rock1"]
	print("Now we can edit the values directly.")
end

AddSimPostInit(MyPostInit)
11 hours ago, wch2 said:

The principle of this is still replacement, right? I'm quite spliting hairs about this because I figured the game as a process and I wanna know what the game was doing. We finally inserted our own function "MyPostInit" by means of "AddSimPostInit" aiming to set the loot table by ourself.

Game loads files like constants.lua and tuning.lua.

Game registers prefabs.

Game loads mods, registers mod prefabs and runs modmain.

Game loads game files like prefabs. <== here is where lootdropper.lua and rocks.lua get loaded and make the globals we need

Game loads world. <== here is where MyPostInit runs

Game loads host player (nil if dedicated, you if player hosted).

So as you can see, we need AddSimPostInit for this very particular case where the globals exist after our mod loads.

11 hours ago, wch2 said:

What do you say? And by the way, my log file is missing. Where is it?

Go to MyDocuments/Klei/DoNotStarveTogether

client_log if you are a client of a dedicated server, or you are hosting a server without caves from the game client.

/Cluster_X/Master(or Caves)/server_log if your server was dedicated, or you are hosting a server with caves from the game client.

X being the save slot of your server. There are currently 5 slots.

If you are hosting a server without caves, then you can press Ctrl+L to display a fraction of the log.

Edited by DarkXero
Link to comment
Share on other sites

14 hours ago, DarkXero said:

Game loads files like constants.lua and tuning.lua.

Game registers prefabs.

Game loads mods, registers mod prefabs and runs modmain.

Game loads game files like prefabs. <== here is where lootdropper.lua and rocks.lua get loaded and make the globals we need

Game loads world. <== here is where MyPostInit runs

Game loads host player (nil if dedicated, you if player hosted).

So as you can see, we need AddSimPostInit for this very particular case where the globals exist after our mod loads.

Loud and clear.

And I wanna ask for some details of the second part now... hope you don't mind!

The aim is to override the some of the local functions of the game right?

Actually I'm having serious problems about the working proccess of the OnWork function, but I think this is not the priority.

What I'm confused are still the table and the env function, which means these:

local prefabs_that_have_that_OnWork = 
{
	"rock1", 
	"rock2", 
	"rock_flintless", 
	"rock_flintless_med", 
	"rock_flintless_low", 
	"rock_moon", 
	"rock_petrified_tree",
	"rock_petrified_tree_med", 
	"rock_petrified_tree_tall", 
	"rock_petrified_tree_short", 
	"rock_petrified_tree_old",
}

for k, v in pairs(prefabs_that_have_that_OnWork) do
	AddPrefabPostInit(v, function(inst)
		inst.components.workable:SetOnWorkCallback(OnWork)
	end)
	end

Now that I have a bit interpretation about AddSimPostInit, I can guess this AddPrefabPostInit also does the same thing but it is of different type.

The key thing here may be Prefab, something you also mentioned lots of times. Then first question is what Prefab is...

Does it refer to some loaded functions or variables?

And second question, get puzzled about this "for" stuff.

I think in this particular situation, it's more like foreach in C right? However, prefabs_that_have_that_OnWork is only a string table, why would you use 2 variables, namely k,v?

Thirdly, about this specific detail:

(v, function(inst)
		inst.components.workable:SetOnWorkCallback(OnWork)
	end)

I'm thinking that AddPrefabPostInit has two parameters, where v refers to the prefab and the function(inst) refers to the function.

But this function(inst), Is this a kind of lua grammers? Create a new function without identifier?

inst.components.workable:SetOnWorkCallback(OnWork)

Ad finally, this is where I definitely cannot get any comprehension. So... I don't even know what I want to ask. I just have absolutely no idea why this sentense is here.

I realized that I'm having you teach me both the lua and modding in the game, not a good student. Learnt a lot anyways. Thx!

Edited by wch2
Link to comment
Share on other sites

14 hours ago, wch2 said:

The key thing here may be Prefab, something you also mentioned lots of times. Then first question is what Prefab is...

Does it refer to some loaded functions or variables?

A prefab is, conceptually, a prefabricated entity.

Each file in the prefabs folder, end in

return Prefab("abigail", fn, assets)

where the first parameter is a string acting as the unique identifier of the prefab.

where the second parameter is a function the game will execute to obtain the entity to place in game.

where the third parameter is a table where you specify assets that the game will need for it (zip builds, images, atlases).

Each prefab file in the prefabs folder may return one or more prefabs.

In practice, what a Prefab is, is defined in the file prefabs.lua.

It's a class. You know, like in object oriented programming. But lua doesn't really have classes.

A class is a function that you give it some arguments (like a constructor function) and it returns a very fancy table, like an object.

You better not focus on this: you wouldn't understand now. Or never, if you don't want to delve into advanced lua.

14 hours ago, wch2 said:

The aim is to override the some of the local functions of the game right?

Actually I'm having serious problems about the working proccess of the OnWork function, but I think this is not the priority.

Local functions are in a black box, to you.

You have to be VERY specific about your goals, because there isn't a cookie cutter solution.

Sometimes there isn't even a clean solution.

14 hours ago, wch2 said:

And second question, get puzzled about this "for" stuff.

I think in this particular situation, it's more like foreach in C right? However, prefabs_that_have_that_OnWork is only a string table, why would you use 2 variables, namely k,v?

https://www.lua.org/pil/7.3.html

The pairs iterator returns three values, I save the key and the value in the variables k and v.

If I did

for v in pairs(table) do

then v would get the key value, and the other two values (including the table value) would be lost. And I don't want that.

If you want to do that though, you can build the following table

local prefabs_that_have_that_OnWork = {
	["rock1"] = true,
	["rock2"] = true,
}

Here, "rock1" and "rock2" aren't values of the table anymore. They are now string keys for the hash table lua will make.

14 hours ago, wch2 said:

I'm thinking that AddPrefabPostInit has two parameters, where v refers to the prefab and the function(inst) refers to the function.

But this function(inst), Is this a kind of lua grammers? Create a new function without identifier?

Yes, AddPrefabPostInit has two parameters, as defined in modutil.lua.

env.AddPrefabPostInit = function(prefab, fn)

prefab, which is the id of the prefab.

fn, which is the function that will be stored in a table, and will executed when the prefab is spawned.

I could have done

local myfunc = function(inst)
	-- Do stuff
end

AddPrefabPostInit(v, myfunc)

also.

And yes, lua functions are like integers, or strings, or tables. Regular values.

14 hours ago, wch2 said:

Ad finally, this is where I definitely cannot get any comprehension. So... I don't even know what I want to ask. I just have absolutely no idea why this sentense is here.

I realized that I'm having you teach me both the lua and modding in the game, not a good student. Learnt a lot anyways. Thx!

It's on the prefab file you want to edit.

Look at the fn that generates the entity:

    inst:AddComponent("workable")
    inst.components.workable:SetWorkAction(ACTIONS.MINE)
    inst.components.workable:SetWorkLeft(TUNING.ROCKS_MINE)
    inst.components.workable:SetOnWorkCallback(OnWork)

You have inst = {}. Then inst = { components = {} }. Then inst = { components = { workable = {} } }.

Except workable isn't an empty table, it has elements.

Like SetOnWorkCallback. This key gives you access to a value that is a function, that accepts a function.

You call it by adding () to it. And you give it your function OnWork.

Doing this, you append a function to a key in the workable table. And another method uses the function in that key when the rock is mined.

 

Don't try to make sense of everything to the very core. That comes later. You are just going to get confused.

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

×
  • Create New...