zarklord_klei

World Options and Mod Changes

Recommended Posts

penguin0616    918
3 hours ago, zarklord_klei said:

Happy modding!

It's going to be very happy modding :angel:

Especially since "containers" now returns the params table. No more replacement hell for them :>

Edited by penguin0616
  • Like 1
  • Big Ups 1

Share this post


Link to post
Share on other sites
zarklord_klei    3303
5 minutes ago, TheSkylarr said:

Does this also run on the world selection screen, or only the creation screen?

No server mods run on the world selection screen.

  • Like 1
  • Thanks 1

Share this post


Link to post
Share on other sites
TheSkylarr    18
1 hour ago, zarklord_klei said:

No server mods run on the world selection screen.

Oh right, haha. I was wondering if this would finally solve the problem of character mods not being able to show their saveslot portrait, but that makes sense.

Share this post


Link to post
Share on other sites
TheSkylarr    18
Just now, zarklord_klei said:

I do plan on adding something to solve this problem, no promises on when though.

Good to hear! I just made a really janky client mod that does this, but the way I'm doing it is terrible cause I don't have enough experience, so I would love to see another QOL update add this eventually.

Share this post


Link to post
Share on other sites
ADM    8586

Hey, so I've been experimenting with all this and I'm sure it's gonna make my mod configs much cleaner, sounds neat.

However I'm having some... troubles :

-- string file

GLOBAL.STRINGS.UI.CUSTOMIZATIONSCREEN.CHERRY_TREE = "Cherry Trees"

-- modworldgenmain

AddCustomizeGroup(LEVELCATEGORY.WORLDGEN, "cherry", "Cherry Forest")

AddCustomizeItem(LEVELCATEGORY.WORLDGEN, "cherry", "cherry_tree", {order = 1, value = "default", world = {"forest"}, image = "images/customization/worldgen_cherrytree.tex", atlas = "images/customization/worldgen_cherrytree.xml"})

-- modservercreationmain

FrontEndAssets = {
	Asset("ATLAS", "images/customization/worldgen_cherrytree.xml"),
	Asset("IMAGE", "images/customization/worldgen_cherrytree.tex"),
}

Whatever I've been trying to do there just copy the Starting Season config : its name, image, and options :

worldgen.thumb.jpg.e78902eb0f74026a0e7f05d2341bf999.jpg

Something else, I'm not sure if that is my fault or not but whenever I scroll the game crash :
(only while the configs have mod options of course)

worldgen2.thumb.png.9062bc998427575d76f24004a1437651.png

Share this post


Link to post
Share on other sites
zarklord_klei    3303
12 hours ago, ADM said:

--SNIP--

I think it might be a missing spinner description.
try setting

desc = GetCustomizeDescription(whatever description you want)

in the item settings table.

  • Like 1
  • Thanks 2

Share this post


Link to post
Share on other sites
Tranoze    284

I use GLOBAL.io to write/read custom log file for my mod, and in this update read/write file always return "Bad file descriptor" error.

  • Thanks 1

Share this post


Link to post
Share on other sites
Serpens    565

Ok, so you implemented a way for mods to add their own world-settings or world generation settings in world creation screen...

But what happens afterwards? In your example you added a worldseed option. But how to read out what option the user picked? And how to change eg the worldseed according to the setting?

And more important for me: How to change exisiting world settings/creation options, like forcing a specific season and other stuff with my mod?
I currently use AddLevelPreInitAny to change the tasksetdata directly ("set_pieces", "required_prefabs" and for settings also "overrides"). Is this still the way to go? Or are there better ways now?

https://steamcommunity.com/sharedfiles/filedetails/?id=1847959350
https://steamcommunity.com/sharedfiles/filedetails/?id=756229217

edit: I have huge problems with my adventure and teleportato mod. it seems AddLevelPreInitAny function triggers twice now. I guess one time for world creation and one time for worldsettings, after world is created. But only in the first case there is a "overrides" I can change. I can successfully change the start_season, but I can not change the season or day length it seems..@zarklord_klei Or what code should I use to change those settings with a mod? 
Also the whole Level and Chapter design does not work anymore, this might be related to the "io.read/write" already mentioned above by Tranoze.

PS:
since you are a very good developer, could you please bump these bug reports:
https://forums.kleientertainment.com/klei-bug-tracker/dont-starve-together/addtaskpreinit-does-not-affect-optionaltasks-intended-r25702/

https://forums.kleientertainment.com/klei-bug-tracker/dont-starve-together/actionspick-not-correct-with-mouseclick-works-with-spacebar-r20816/

 

edit4:
The only working way I found, to change eg season lenght via mod is the following, but I really doubt this is the intended way:

local WorldSettings_Overrides = require("worldsettings_overrides")

-- and within any function that runs after the world was created:
    WorldSettings_Overrides.Post["autumn"]("noseason")

 

Edited by Serpens
added more info

Share this post


Link to post
Share on other sites
zarklord_klei    3303
On 3/12/2021 at 12:55 AM, Tranoze said:

I use GLOBAL.io to write/read custom log file for my mod, and in this update read/write file always return "Bad file descriptor" error.

This should be resolved now 

 

On 3/12/2021 at 6:51 AM, Serpens said:

Ok, so you implemented a way for mods to add their own world-settings or world generation settings in world creation screen...

But what happens afterwards? In your example you added a worldseed option. But how to read out what option the user picked? And how to change eg the worldseed according to the setting?

And more important for me: How to change exisiting world settings/creation options, like forcing a specific season and other stuff with my mod?
I currently use AddLevelPreInitAny to change the tasksetdata directly ("set_pieces", "required_prefabs" and for settings also "overrides"). Is this still the way to go? Or are there better ways now?

https://steamcommunity.com/sharedfiles/filedetails/?id=1847959350
https://steamcommunity.com/sharedfiles/filedetails/?id=756229217

edit: I have huge problems with my adventure and teleportato mod. it seems AddLevelPreInitAny function triggers twice now. I guess one time for world creation and one time for worldsettings, after world is created. But only in the first case there is a "overrides" I can change. I can successfully change the start_season, but I can not change the season or day length it seems..@zarklord_klei Or what code should I use to change those settings with a mod? 
Also the whole Level and Chapter design does not work anymore, this might be related to the "io.read/write" already mentioned above by Tranoze.

PS:
since you are a very good developer, could you please bump these bug reports:
https://forums.kleientertainment.com/klei-bug-tracker/dont-starve-together/addtaskpreinit-does-not-affect-optionaltasks-intended-r25702/

https://forums.kleientertainment.com/klei-bug-tracker/dont-starve-together/actionspick-not-correct-with-mouseclick-works-with-spacebar-r20816/

 

edit4:
The only working way I found, to change eg season lenght via mod is the following, but I really doubt this is the intended way:


local WorldSettings_Overrides = require("worldsettings_overrides")

-- and within any function that runs after the world was created:
    WorldSettings_Overrides.Post["autumn"]("noseason")

 

I will take a look at this on monday.

  • Like 1
  • Thanks 2

Share this post


Link to post
Share on other sites
Serpens    565
1 hour ago, zarklord_klei said:

This should be resolved now 

 

I will take a look at this on monday.

thank you very much.
For easier work, I will summarize how I used to change genration-settings (this is an "easy" example, copy pasted and merged from my scripts, so may not be functional, but shows what I did previously).

in modworldgenmain :

local function IsModLoaded(modname) -- there is no TheNet on world generation, so we use this to find out about gem api
    return GLOBAL.KnownModIndex:IsModEnabled(modname) or GLOBAL.KnownModIndex:IsModForceEnabled(modname)
end
local GEMAPIActive = IsModLoaded("workshop-1378549454")

AddLevelPreInitAny(function(tasksetdata)
    local _overrides = nil
    if GEMAPIActive then -- to prevent gem api to revert our changes in rare cases
        _overrides = GLOBAL.shallowcopy(tasksetdata.overrides)
    end
    if tasksetdata.location == "forest" then
        tasksetdata.tasks = {"Tentacle-Blocked Spider Swamp"}
        tasksetdata.numoptionaltasks = 0
        tasksetdata.optionaltasks = {}
        tasksetdata.set_pieces = {}
        tasksetdata.required_setpieces = {}
        table.insert(tasksetdata.required_setpieces,adventureportal)
        tasksetdata.numrandom_set_pieces = 0
        tasksetdata.random_set_pieces = {}
        tasksetdata.required_prefabs = {"spawnpoint_master","adventure_portal"}
        tasksetdata.ocean_prefill_setpieces = {} -- delete any ocean stuff
        tasksetdata.ocean_population = {} -- delete any ocean stuff
        
        if tasksetdata.overrides==nil then
            tasksetdata.overrides = {}
        end
        tasksetdata.overrides.world_size  =  "small"
        tasksetdata.overrides.wormhole_prefab = "wormhole"
        tasksetdata.overrides.layout_mode = "LinkNodesByKeys"
        tasksetdata.overrides.deerclops  =  (GetModConfigData("difficulty")==0 and "never") or (GetModConfigData("difficulty")==1 and "never") or (GetModConfigData("difficulty")==2 and "rare") or (GetModConfigData("difficulty")==3 and "default") or "never"
        tasksetdata.overrides.dragonfly  =  "never"
        tasksetdata.overrides.bearger  =  (GetModConfigData("difficulty")==0 and "never") or (GetModConfigData("difficulty")==1 and "never") or (GetModConfigData("difficulty")==2 and "rare") or (GetModConfigData("difficulty")==3 and "default") or "never"
        tasksetdata.overrides.goosemoose  =  (GetModConfigData("difficulty")==0 and "never") or (GetModConfigData("difficulty")==1 and "never") or (GetModConfigData("difficulty")==2 and "rare") or (GetModConfigData("difficulty")==3 and "default") or "never"
        tasksetdata.overrides.antlion = "never"
        tasksetdata.overrides.season_start  =  "autumn"
        tasksetdata.overrides.day = "noday"
        tasksetdata.overrides.autumn = "veryshortseason"
        tasksetdata.overrides.winter = "veryshortseason"
        tasksetdata.overrides.spring = "veryshortseason"
        tasksetdata.overrides.summer = "veryshortseason"
        tasksetdata.overrides.keep_disconnected_tiles = true
        tasksetdata.overrides.no_joining_islands = true
        tasksetdata.overrides["has_ocean"] = false
    end
    if GEMAPIActive and _overrides~=nil then -- to prevent gem api to revert our changes in rare cases
        local overrides_to_block = {}
        for k, v in pairs(tasksetdata.overrides) do
            --original blockoverrides updateanyways are 3 things that could be in this table that arent actually overrides.
            if k ~= "original" and k ~= "blockoverrides" and k ~= "updateanyways" and _overrides[k] ~= v then
                table.insert(overrides_to_block, k)
            end
        end
    	GLOBAL.gemrun("overridesblocker", tasksetdata.overrides, modname, overrides_to_block)
    end
end)

All new "world_settings", so the ones you could change after the world was generated, are no longer affected by this code. So no way to change season lenghts, or day length and stuff like this with this code.

And I got "overrides index nil" errors (after recent update), so I had to add these lines:

        if tasksetdata.overrides==nil then
            tasksetdata.overrides = {}
        end

and I noticed, that AddLevelPreInitAny seems to be called twice now, one time with overrides not being nil and a second time after the world was generated with it being nil. Not 100% sure though.


Anyway, when you gave me the code to "prevent gem api to revert my changes", you already said that you did not expect anyone to change generation-settings like this. So maybe this also means and especially meanwhile after this QoL update, that there is a much better way to achieve this? Which is it?

Edited by Serpens

Share this post


Link to post
Share on other sites
Serpens    565
On 2/22/2021 at 8:47 PM, penguin0616 said:

It's going to be very happy modding :angel:

Especially since "containers" now returns the params table. No more replacement hell for them :>

can you elaborate on this? found nothing abouts this in changelog and no "params" in any container file I searched.

Share this post


Link to post
Share on other sites
penguin0616    918
7 hours ago, Serpens said:

can you elaborate on this? found nothing abouts this in changelog and no "params" in any container file I searched.

It wasn't in the changelog.

image.png.d126d3211128a10bec3e21077e01814e.png

  • Like 1

Share this post


Link to post
Share on other sites
Serpens    565
1 hour ago, penguin0616 said:

It wasn't in the changelog.

image.png.d126d3211128a10bec3e21077e01814e.png

thank you. and can you provide examples which code simnplifies know to what with help of this?

Is it for mod-containers? I currentlyknow 2 ways to add mod containers, while both of them are really too big hassle:
1) Override containers widgetsetup for your specific prefab, return old function for others.
2) Don't override, but pass your params to the widgetsetup call as data. current problem: you need to copy paste the params,if you want the same like already existing -> I guess this is the place where the containers.params kicks in? So now you can do sth like:
inst.components.container:WidgetSetup("shadowBMduelist",containers.params.chester) ? But unfortunately, all those mods which are using the first attempt, always forget to return "data" in their "old_widgetsetup" call, which makes all mods using the second method incompatibel to them =/

 

Edited by Serpens

Share this post


Link to post
Share on other sites
penguin0616    918

@Serpens The new system makes it so you no longer need to override widgetsetup or recursively search for upvalues until you find the params table.

Now to make a container, you can just add it directly to the params table, just like how all the other containers work. 

Example:

-- modmain.lua
local containers = require("containers")

containers.params.another_krampus_sack = containers.params.krampus_sack

containers.params.even_more_krampus_sack =
{
    widget =
    {
        slotpos = {},
        animbank = "ui_krampusbag_2x8",
        animbuild = "ui_krampusbag_2x8",
        pos = Vector3(-5, -120, 0),
    },
    issidewidget = true,
    type = "pack",
    openlimit = 1,
}

for y = 0, 6 do
    table.insert(containers.params.even_more_krampus_sack.widget.slotpos, Vector3(-162, -75 * y + 240, 0))
    table.insert(containers.params.even_more_krampus_sack.widget.slotpos, Vector3(-162 + 75, -75 * y + 240, 0))
end

 

Edited by penguin0616
  • Like 2

Share this post


Link to post
Share on other sites
Serpens    565
On 3/13/2021 at 5:38 PM, zarklord_klei said:

[worldgeneration modding]

I will take a look at this on monday.

any news on this? is this a bug that will be fixed or do I need new code to force specific settings?

Share this post


Link to post
Share on other sites
zarklord_klei    3303
10 hours ago, Serpens said:

any news on this? is this a bug that will be fixed or do I need new code to force specific settings?

Oops, I completely forgot, I'll try and get an answer for you.

  • Like 1
  • Thanks 2

Share this post


Link to post
Share on other sites
zarklord_klei    3303
On 3/12/2021 at 6:51 AM, Serpens said:

But what happens afterwards? In your example you added a worldseed option. But how to read out what option the user picked? And how to change eg the worldseed according to the setting?

For world settings, simply create a function in worldsettings_overrides.lua's Pre or Post table(depending on what the setting affects) with the same name as the setting, that function will get called with either "default" or the option selected by the user, for world generation it's more complicated, I would suggest looking at forest_map.lua to see ways to do that.

On 3/12/2021 at 6:51 AM, Serpens said:

I currently use AddLevelPreInitAny to change the tasksetdata directly ("set_pieces", "required_prefabs" and for settings also "overrides"). Is this still the way to go? Or are there better ways now?

This is still the correct way to do this for world generation options.

On 3/13/2021 at 12:52 PM, Serpens said:

All new "world_settings", so the ones you could change after the world was generated, are no longer affected by this code. So no way to change season lenghts, or day length and stuff like this with this code.

On 3/12/2021 at 6:51 AM, Serpens said:

And more important for me: How to change exisiting world settings/creation options, like forcing a specific season and other stuff with my mod?

I would recommend modifying the functions in WorldSettings_Overrides.Pre/Post to either run custom logic, or fake a different setting, like this:

local WSO = require("worldsettings_overrides")
local _day = WSO.Post.day

function WSO.Post.day(difficulty)
	if some_check_for_adventure_mode then
		--force a setting
		difficulty = "onlynight"
	end
	_day(difficulty)
end
--or
function WSO.Post.day(difficulty)
	if some_check_for_adventure_mode then
		--completely custom setting.
		TheWorld:PushEvent("ms_setseasonsegmodifier", day = 0.2, dusk = 1.5, night = 1.3)
		return
	end
	_day(difficulty)
end

 

  • Thanks 2

Share this post


Link to post
Share on other sites
Serpens    565
On 3/23/2021 at 7:39 PM, zarklord_klei said:

For world settings, simply create a function in worldsettings_overrides.lua's Pre or Post table(depending on what the setting affects) with the same name as the setting, that function will get called with either "default" or the option selected by the user, for world generation it's more complicated, I would suggest looking at forest_map.lua to see ways to do that.

This is still the correct way to do this for world generation options.

I would recommend modifying the functions in WorldSettings_Overrides.Pre/Post to either run custom logic, or fake a different setting, like this:


local WSO = require("worldsettings_overrides")
local _day = WSO.Post.day

function WSO.Post.day(difficulty)
	if some_check_for_adventure_mode then
		--force a setting
		difficulty = "onlynight"
	end
	_day(difficulty)
end
--or
function WSO.Post.day(difficulty)
	if some_check_for_adventure_mode then
		--completely custom setting.
		TheWorld:PushEvent("ms_setseasonsegmodifier", day = 0.2, dusk = 1.5, night = 1.3)
		return
	end
	_day(difficulty)
end

 

thank you. but none of them is a good solution, making it incompatible to other mods that want to change seasons...

My mod should change the season once when starting the world the first time. After that, all other mods should be able to do whatever they want (eg. an item which is able to change season length or whatever).
So I guess I will do a PostInit for the world, then call WorldSettings_Overrides.Post["autumn"]("noseason") or similar (or directly the PushEvent) and try to remember, that I called it once, to not call it again on next loading.
Still no good solution, but better.

edit:
ok, only calling this does not work, because the change is not saved O.ô really mod-unfriendly (because it means an item can not easily change season length or so midgame).
Will see if I can instead override the setting... ... but this also won't help -.- because it seems when loading a game, it will always take the settings chosen in the worldscreen.
All in all I come to the conclusion, that your code is indeed the only viable way to achieve my goal, but it still makes it incompatible to other mods doing the same...

edit2:
Ah no, it would be better if my mod could change the settings on the worldscreen after world was generated. This way the game would save the setting and the users are able to change them, if they don't like them.
But how to do this!?

edit3:
Solution:
because of steam apiv2 I forgot to look at your (Zarklord) worldsettings mod (because I did not find the new location of mods). But I will now do it, like you did it there, simply changing eg. TheWorld.topology.overrides.day and then reload the world once per game. This will change the settings on the worldscreen and if the user is unhappy with it, they can simply change it to whatever they want.


Modding after that QoL update:

Spoiler

All in all the changes of this update regrading modding are horrible, especially APIv2 from steam. I really consider, after so many years, to stop modding DST and abandon my mods...

edit:
Especially because there are no notes about how to deal with many of these changes. Ok, there was never a modding documentation for DST, but when you do so drastic changes, I still expected some kind of introduction and how to deal with it. I'm no expert modder, but I'm a good one and can read/understand the lua game code to find out many things myself. But I really struggled to fix my mods and steam APIv2 makes it even more complicated.

edit2:
don't get me wrong, I like your work Zarklord very much! You are an important addition to the DST dev team and pushing important improvements. And it looks like you are the only dev that is coding and watching the forum at the same time, maybe DST can afford another dev to help in the forum, at least to communicate big game changes and their effect on mods.



 

Edited by Serpens
  • Sad 1

Share this post


Link to post
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