Jump to content

[Solved] Advanced guide for modworldgenmain? is called twice


Recommended Posts

Hi,

I already made several mods with modworldgenmain and know the basics.

But currently I have a problem, that modworldgenmain is called twice everytime the game starts, while at the first time, many GLOBAL variables are unknown/empty. And this first time, the modworldgenmains are not called in the correct order for several mods (not in order of their priority).

What I want to achieve:
Mod 1 runs first and puts some info into a GLOBAL table.
Mod 2 runs last and his modworldgenmain should do something depending on what was put into that table by Mod 1.

Problem:
At world generation, modworldgenmain runs once, before any modmain and without any order.
After that the mods are loaded in correct order, so only in this second run, the table was actually filled.

At opening an already generated world: It seems to be the other way round? First are the mods laoded and after that modworldgenmain without any information about my table.

So I thought it would be time to find out, why is it called twice. What is done in the first/second run. And how to differentiate between those two.

Anyone knows that?

Edited by Serpens
Link to comment
Share on other sites

  • Developer

You have some things majorly confused, I'm gonna clear some things up, but I'm gonna introduce a couple terms so its clear what I'm referring to.
modfrontendmain = modworldgenmain, when its called on the frontend in the ServerCreationScreen.
modbackendmain = modworldgenmain, when its called on the backend, which is world gen, and normal world loading.
worldgen_main = when the game solely loads modbackendmain, and doesn't load modmain, this environment is completely separate from main.
main = when the game loads both modbackendmain and then modmain right after, this environment is completely separate from worldgen_main.
environment = the environment that you can access with GLOBAL inside modmain/modworldgenmain, different environments means that you can't(easily) pass between different GLOBAL environments.

alright, with all the terms defined here is all the ways these files get loaded.

on the frontend:
modfrontendmain gets loaded for all server mods, in a undefinable order, order is never predictable, so don't even think about directly relying on order.
modfrontendmain can get loaded and unloaded multiple times, so if the user keeps enabling and disabling the mod, whatever code is supposed to run, can run multiple times.

on the backend:
during worldgen_main, all mods get just modbackendmain loaded in the worldgen_main environment, with load priority used.
during main, all mods get modbackendmain and modmain(right after modbackendmain) loaded in the main environment, with load priority used.

EDIT: as for the solution to your problem about the table thing, you can do something like this:

--YOUR MOD:
GLOBAL.global("YOURUNIQUETABLENAME")--this line is for something to do with a file called strict.lua, if you care investigate yourself, otherwise just do this.
for i, v in ipairs(GLOBAL.YOURUNIQUETABLENAME or {}) do
    --do your thing with if it already existed in the table
end

GLOBAL.YOURUNIQUETABLENAME = GLOBAL.setmetatable({}, {__newindex = function(t, k, v)--t is the table, k is the key they attempted to store it at, v is the value they were storing
    --do your thing with if it attempts to get added to the table.
end})--look up lua metatables to understand why this works.
--OTHER MODS:
GLOBAL.global("YOURUNIQUETABLENAME")--same deal, this can be called multiple times without harm.  
--if this is undefined, define it.
if GLOBAL.YOURUNIQUETABLENAME == nil then GLOBAL.YOURUNIQUETABLENAME = {} end
--add your thing the callback list
--DO NOT APPEND YOUR FUNCTION ANYWHERE BUT THEN END
GLOBAL.YOURUNIQUETABLENAME[#GLOBAL.YOURUNIQUETABLENAME + 1] = YOURVALUE --DO NOT use table.insert, that bypasses the __newindex metatable value.

 

Edited by Zarklord
Link to comment
Share on other sites

18 minutes ago, Zarklord said:

You have some things majorly confused, I'm gonna clear some things up, but I'm gonna introduce a couple terms so its clear what I'm referring to.
modfrontendmain = modworldgenmain, when its called on the frontend in the ServerCreationScreen.
modbackendmain = modworldgenmain, when its called on the backend, which is world gen, and normal world loading.
worldgen_main = when the game solely loads modbackendmain, and doesn't load modmain, this environment is completely separate from main.
main = when the game loads both modbackendmain and then modmain right after, this environment is completely separate from worldgen_main.
environment = the environment that you can access with GLOBAL inside modmain/modworldgenmain, different environments means that you can't(easily) pass between different GLOBAL environments.

alright, with all the terms defined here is all the ways these files get loaded.

on the frontend:
modfrontendmain gets loaded for all server mods, in a undefinable order, order is never predictable, so don't even think about directly relying on order.
modfrontendmain can get loaded and unloaded multiple times, so if the user keeps enabling and disabling the mod, whatever code is supposed to run, can run multiple times.

on the backend:
during worldgen_main, all mods get just modbackendmain loaded in the worldgen_main environment, with load priority used.
during main, all mods get modbackendmain and modmain(right after modbackendmain) loaded in the main environment, with load priority used.

uff... that sounds very complicated.
And can you translate this somehow into typical stuff that is done within modmain and stuff that is done within modworldgenmain? Cause currently it is very hard to understand, if I can't connect this with something I "know".
I think some commands are not executed during the modfrontendmain for the modworldgenmain.

In the end:
I want to make an API mod that is used by other mods to generate a custom world. So the other mods will put some taskdata into a global table and my API mod should generate the world accordingly. Also the modmain of API mod should do somehting depending of the conent of global table. How should the mods look like to make this a success? Problem right now is, that every now and then and depending on where I put the code, this table is nil.

edit:
btw do you know the difference between task and level? Their preinit functions look the same I think, both have required_prefabs and stuff like this in them and both work, so not sure which I should use to change the world generation.

Edited by Serpens
Link to comment
Share on other sites

  • Developer
25 minutes ago, Serpens said:

uff... that sounds very complicated.
And can you translate this somehow into typical stuff that is done within modmain and stuff that is done within modworldgenmain? Cause currently it is very hard to understand, if I can't connect this with something I "know".
I think some commands are not executed during the modfrontendmain for the modworldgenmain.

In the end:
I want to make an API mod that is used by other mods to generate a custom world. So the other mods will put some taskdata into a global table and my API mod should generate the world accordingly. Also the modmain of API mod should do somehting depending of the conent of global table. How should the mods look like to make this a success? Problem right now is, that every now and then and depending on where I put the code, this table is nil.

If I simplify it, it loses some of its information, if their is a term(or terms) that I'm using that your not familiar with, by all means ask and I can tell you and update my main post.

Link to comment
Share on other sites

1 hour ago, Zarklord said:

If I simplify it, it loses some of its information, if their is a term(or terms) that I'm using that your not familiar with, by all means ask and I can tell you and update my main post.

In case it is really important to understand your terms and there is really no easier way, I need examples to understand it. I'm bad at theory, I always need examples and try/error myself to understand things. And how they are connected or if one is a generic term of others. Currently those are only a handful words and I have absolutely no clue what to do with them :D This is of course because I have no programming knowledge and also no server/game knowledge. All knowledge I have is modding knowledge, so use existing "easy" tools provided from the developers to modders, although I would call me already advanced, while you seem to be an expert. And I already have experience learning and teaching stuff and know that it sounds super easy for you, but super complicated for me. Therefore I need easier words/examples.
But I understand if that would be too much work for you and I'm quite sure this knowledge would be far beyond the scope of the mod I want to make.

wow... you are using alot of words and code (your edit in your first post) I have never heard of wihtin my ~some years of DST modding, never needed it before. So.. is this complicated "global(" and "metatable" and "always adding at the end of table" needed, if I instead simply use the TUNING list?
I just want to clarify if this complicated stuff is something I really need to solve the problem, or if there are easier ways, eg using existing TUNING table.

Another problem is of course that I don't understand what exactly is currently happening within my mod, where I already use the TUNING list. I would like to tell you what I have/tried and what the result was, but it is beyond my understanding.
But my current code would be (other mods load first, at last the API mod):

-- Other mod in modmain:

local _G = GLOBAL
if not _G.TUNING.TELEPORTATOMOD then
    _G.TUNING.TELEPORTATOMOD = {}
end
if not _G.TUNING.TELEPORTATOMOD.WORLDS then
    _G.TUNING.TELEPORTATOMOD.WORLDS = {}
end
local WORLDS = _G.TUNING.TELEPORTATOMOD.WORLDS
local function AdventurePortalWorld(tasksetdata)
    if not tasksetdata.ordered_story_setpieces then
        tasksetdata.ordered_story_setpieces = {}
    end
    table.insert(tasksetdata.ordered_story_setpieces,adventureportal)
    tasksetdata.tasks = {"Swamp start"}
    tasksetdata.numoptionaltasks = 0
    tasksetdata.optionaltasks = {}
    tasksetdata.set_pieces = {}
    tasksetdata.required_prefabs = {"spawnpoint_master","adventure_portal"}
    tasksetdata.overrides={...}
    return tasksetdata
end
table.insert(WORLDS, {name="Maxwells Door", taskdatafunction_forest=AdventurePortalWorld}) -- this list should contain several worlds and api mod will choose a random one

-- ##########################
-- API mod in modworldgenmain:
local _G = GLOBAL
if _G.TUNING.TELEPORTATOMOD then
    local WORLDS = _G.TUNING.TELEPORTATOMOD.WORLDS
    if _G.next(WORLDS) then
        --- put the taskdata from a random WORLDS entry into AddTaskSetPreInitAny
    end
end

-- ##########################
-- API mod in modmain:
local _G = GLOBAL
local WORLDS = _G.TUNING.TELEPORTATOMOD.WORLDS
if _G.next(WORLDS) then
    AddPrefabPostInit... -- eg. add teleportato and init it and so on
end

This code does not work, the world is not changed, cause AddTaskSetPreInitAny is not called I guess (at least not at the time it would be necessary, cause _G.TUNING.TELEPORTATOMOD was nil.

edit:
I think I understood why you made such a complicated code. You assume a different priority of mods. So first the API mod and then the other mods, and you added this "__newindex" function so our API mod is "notified" when someone else is putting new stuff into the table.
Hmm... do you really think this order is better? My API mod should not react to something being added. It just should choose a random entry from that WORLDS list and generate the world from this chosen entry.
So with your solution my API mod has to add the stuff to a local table when __newidex is called. But it sounds like this will have exactly the same problems I already have with my TUNING solution, so I guess this does not solve the base problem (described in my code above). does it?

Edited by Serpens
Link to comment
Share on other sites

  • Developer
5 hours ago, Serpens said:

In case it is really important to understand your terms and there is really no easier way, I need examples to understand it. I'm bad at theory, I always need examples and try/error myself to understand things. And how they are connected or if one is a generic term of others. Currently those are only a handful words and I have absolutely no clue what to do with them :D This is of course because I have no programming knowledge and also no server/game knowledge. All knowledge I have is modding knowledge, so use existing "easy" tools provided from the developers to modders, although I would call me already advanced, while you seem to be an expert. And I already have experience learning and teaching stuff and know that it sounds super easy for you, but super complicated for me. Therefore I need easier words/examples.
But I understand if that would be too much work for you and I'm quite sure this knowledge would be far beyond the scope of the mod I want to make.

wow... you are using alot of words and code (your edit in your first post) I have never heard of wihtin my ~some years of DST modding, never needed it before. So.. is this complicated "global(" and "metatable" and "always adding at the end of table" needed, if I instead simply use the TUNING list?
I just want to clarify if this complicated stuff is something I really need to solve the problem, or if there are easier ways, eg using existing TUNING table.

Another problem is of course that I don't understand what exactly is currently happening within my mod, where I already use the TUNING list. I would like to tell you what I have/tried and what the result was, but it is beyond my understanding.
But my current code would be (other mods load first, at last the API mod):


-- Other mod in modmain:

local _G = GLOBAL
if not _G.TUNING.TELEPORTATOMOD then
    _G.TUNING.TELEPORTATOMOD = {}
end
if not _G.TUNING.TELEPORTATOMOD.WORLDS then
    _G.TUNING.TELEPORTATOMOD.WORLDS = {}
end
local WORLDS = _G.TUNING.TELEPORTATOMOD.WORLDS
local function AdventurePortalWorld(tasksetdata)
    if not tasksetdata.ordered_story_setpieces then
        tasksetdata.ordered_story_setpieces = {}
    end
    table.insert(tasksetdata.ordered_story_setpieces,adventureportal)
    tasksetdata.tasks = {"Swamp start"}
    tasksetdata.numoptionaltasks = 0
    tasksetdata.optionaltasks = {}
    tasksetdata.set_pieces = {}
    tasksetdata.required_prefabs = {"spawnpoint_master","adventure_portal"}
    tasksetdata.overrides={...}
    return tasksetdata
end
table.insert(WORLDS, {name="Maxwells Door", taskdatafunction_forest=AdventurePortalWorld}) -- this list should contain several worlds and api mod will choose a random one

-- ##########################
-- API mod in modworldgenmain:
local _G = GLOBAL
if _G.TUNING.TELEPORTATOMOD then
    local WORLDS = _G.TUNING.TELEPORTATOMOD.WORLDS
    if _G.next(WORLDS) then
        --- put the taskdata from a random WORLDS entry into AddTaskSetPreInitAny
    end
end

-- ##########################
-- API mod in modmain:
local _G = GLOBAL
local WORLDS = _G.TUNING.TELEPORTATOMOD.WORLDS
if _G.next(WORLDS) then
    AddPrefabPostInit... -- eg. add teleportato and init it and so on
end

This code does not work, the world is not changed, cause AddTaskSetPreInitAny is not called I guess (at least not at the time it would be necessary, cause _G.TUNING.TELEPORTATOMOD was nil.

edit:
I think I understood why you made such a complicated code. You assume a different priority of mods. So first the API mod and then the other mods, and you added this "__newindex" function so our API mod is "notified" when someone else is putting new stuff into the table.
Hmm... do you really think this order is better? My API mod should not react to something being added. It just should choose a random entry from that WORLDS list and generate the world from this chosen entry.
So with your solution my API mod has to add the stuff to a local table when __newidex is called. But it sounds like this will have exactly the same problems I already have with my TUNING solution, so I guess this does not solve the base problem (described in my code above). does it?

whenever your doing something that will affect worldgen, ALL code must be contained inside modworldgenmain.lua. modmain.lua doesn't run when the world is generating.

Link to comment
Share on other sites

6 minutes ago, Zarklord said:

whenever your doing something that will affect worldgen, ALL code must be contained inside modworldgenmain.lua. modmain.lua doesn't run when the world is generating.

of course, but why are you writing this? My code should be correct regarding this matter.
The modmain of "other mod" is only putting something into the table, so the modworldgenmain of API mod can use it. And the modmain of API mod is only doing modmain stuff, like AddPrefabPostInit and so on.
What else should I do instead? Putting the table.insert stuff into modworldgenmain form "other mod" won't change anything, right?

Edited by Serpens
Link to comment
Share on other sites

  • Developer
Just now, Serpens said:

of course, but why are you writing this? My code should be correct regarding this matter.
The modmain of "other mod" is only putting something into the table, so the modworldgenmain of API mod can use it.
What else should I do instead? Putting the table.insert stuff into modworldgenmain form "other mod" won't change anything, right?

yes, it will. modmain from any mod wont run during the entire duration of worldgen. meaning you would always be checking an empty table.
moving the code to modworldgenmain will cause the WORLDS table to not be empty.

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...