Ensure to be loaded last

Post Reply
User avatar
SegFault22
Member
Posts: 872
Joined: Mon May 21, 2012 03:17
Location: NaN

Ensure to be loaded last

by SegFault22 » Post

I'm working on a mod where it would be a great advantage to "force" the mod to be loaded last. I am working on an algorithm to list the mods which add items, the items which they add, and to add compatibility with the materials api/system (while respecting those mods' naming conventions, when applicable). For mods which add materials that are also added by other mods, all of the items (which are basically the same thing, but are added by separate mods) get "unified" and are treated as the same thing in recipes - the items from the mods would still be defined as separate items (so they would still stack differently), but they would be interchangeable in recipes.

If any mods which add their own materials are loaded after this mod, their items would not be detected, and compatibility with other mods' similar items would not be available. I don't want to have to name the mod with special characters that put it at the end of the list, so is there some way (other than abusing the currently existing dependencies system) to force the mod to be loaded last?

User avatar
BrandonReese
Member
Posts: 839
Joined: Wed Sep 12, 2012 00:44
GitHub: bremaweb
IRC: BrandonReese
In-game: BrandonReese
Location: USA

Re: Ensure to be loaded last

by BrandonReese » Post

Will it not work to use minetest.after to run your function x seconds after everything has loaded?

User avatar
HeroOfTheWinds
Member
Posts: 470
Joined: Wed Apr 23, 2014 23:16
GitHub: HeroOfTheWinds
IRC: WindHero
Location: Hawaii

Re: Ensure to be loaded last

by HeroOfTheWinds » Post

Considering that such a mod must have some sort of formspec or chat dialogue, it would not need to preload the other mods. It can merely compile the list when requested. And to do so, it would be very easy to reference the registered_* tables to find those items. (perhaps even tweak them?)

Since your mod causes craft items to be interchangeable if they are duplicates, this function would run whenever someone tries to craft an applicable object. Hence, it is after all mods are loaded.
Nam ex spatio, omnes res venire possunt.
Why let the ground limit you when you can reach for the sky?
Back to college now, yay for sophomore year schedules. :P

User avatar
rubenwardy
Moderator
Posts: 6978
Joined: Tue Jun 12, 2012 18:11
GitHub: rubenwardy
IRC: rubenwardy
In-game: rubenwardy
Location: Bristol, United Kingdom
Contact:

Re: Ensure to be loaded last

by rubenwardy » Post

If you're adding groups to items, it needs to be done at load time as node def are sent to the client, and crafting is client side predicted. I'm not absolutely sure, confirmation on this would be needed.

My food mod handles multiple dependecies in a similar way. Although, it soft depends and adds groups to a known list of items, rather than detecting from the item name.

How about going through registered_items, then overriding register_item to catch any mods loaded after your one?
Renewed Tab (my browser add-on) | Donate | Mods | Minetest Modding Book

Hello profile reader

User avatar
SegFault22
Member
Posts: 872
Joined: Mon May 21, 2012 03:17
Location: NaN

Re: Ensure to be loaded last

by SegFault22 » Post

I would prefer not to use minetest.after for that, because any function (especially one registering items) which is executed after the timer has expired would not be usable by (or visible to) the function which makes the compatibility work. It also would leave the system idle for some time during loading, while it waits for the timer to expire. Unless there is some way to make minetest.after strictly adhere to executing the function only after all of the other mods in the mods folder have loaded, I will (unfortunately) have to get users to include every mod (which they want compatibility enabled for) in the depends.txt list of the mod.
It might be possible to include entries in the depends.txt list, followed by the question-mark-sign, for each known mod which adds considerable resource/material items, but that method offers no detection of mods which are not already known to add materials.
I will use minetest.after() for some similar issues, but not in the mod-loading/item-registering level

edit1: I intend to make registered crafting-grid recipes use groups instead of specific items, that way the items in the specified group can be used, regardless of what mod they are from. Also, since it is not possible to register more items after the mods are all loaded and the game (server) is initialized, the mod must load after the other mods (which add materials) have all been loaded, so that items (such as plates made from metals, nuggets for all metals, dust piles for all metals, and such) can be registered for the materials added by other mods, which are not included in my mod.

User avatar
rubenwardy
Moderator
Posts: 6978
Joined: Tue Jun 12, 2012 18:11
GitHub: rubenwardy
IRC: rubenwardy
In-game: rubenwardy
Location: Bristol, United Kingdom
Contact:

Re: Ensure to be loaded last

by rubenwardy » Post

minetest.after never runs during the load stage, always during game ticks.

My suggestion would work, you can register other items in your overridden register_item.

Please note, I think register_node calls register_item, so no need to override the former.
Renewed Tab (my browser add-on) | Donate | Mods | Minetest Modding Book

Hello profile reader

User avatar
SegFault22
Member
Posts: 872
Joined: Mon May 21, 2012 03:17
Location: NaN

Re: Ensure to be loaded last

by SegFault22 » Post

If minetest.after never runs during the load phase, the code registering items made from the materials (added by other mods) would be called after the server starts tick-ing. If I remember correctly, items can only be registered during the load phase. If there is some way to register items after the game (server) is initialized, there would have to be some way to prevent already existing items from turning into unknowns, after the game is initialized but before the items are registered. If there is some way around those problems, it would be possible to use minetest.after() for the purpose.

User avatar
rubenwardy
Moderator
Posts: 6978
Joined: Tue Jun 12, 2012 18:11
GitHub: rubenwardy
IRC: rubenwardy
In-game: rubenwardy
Location: Bristol, United Kingdom
Contact:

Re: Ensure to be loaded last

by rubenwardy » Post

minetest.after is not a solution to this problem if you need to register craft recipes or items.

Why would this not work?

Code: Select all

minetest.register_craft({
	output = "mymod:item",
	recipe = {
		{"group:ore"}
	}
})

for name, data in pairs(minetest.registered_items) do
	if name:find("ore") >= 0 then
		local g = {}
		if data.groups then
			for k, v in pairs(data.groups) do
				g[k] = v
			end
		end
		g.ore = 1
		minetest.override_item(name, {groups = g})
	end
end


local old_reg = minetest.register_item
function minetest.register_item(name, date)	
	if name:find("ore") >= 0 then
		if data.groups then		
			local g = {}
			for k, v in pairs(data.groups) do
				g[k] = v
			end
			g.ore = 1
			data.groups = g
		else
			data.groups = {ore = 1}
		end
	end
	
	old_reg(name, data)
end
Renewed Tab (my browser add-on) | Donate | Mods | Minetest Modding Book

Hello profile reader

User avatar
SegFault22
Member
Posts: 872
Joined: Mon May 21, 2012 03:17
Location: NaN

Re: Ensure to be loaded last

by SegFault22 » Post

A lot of that code will work for a lot of the things I'm making the mod to do. However, the code that reads the registered items list must only execute after other mods have registered their materials, because the items (ingots, lumps, nuggets, piles of dust or grains, crystals, etc.) have to be registered before they can be read from the list and interpreted by the list-reading function as materials, for which to add items/recipes and change item definitions to include groups (which would be used as reference in crafting or other processing recipes, such that different items made of an identical material can be used interchangeably). If there is no way (by the current api) to force a mod to load last, I can get around the problem by adding all known mods which add materials to the optional dependencies (so that the mod is loaded after those mods only if they are present) - however, if a user using the mod also uses a mod which adds materials, but such mod is not indexed in the list as adding materials, they will have to add the optional dependency (and possibly report it so that the mod id can be added to the optional dependencies).
Thank you for the sample of code, some of it is structured in a way that I haven't used yet - I see where it could be used to make the existing code for the mod more efficient, and introduces more options for making the rest of the code

User avatar
Shnikety
New member
Posts: 4
Joined: Sun Jun 11, 2017 00:45

Re: Ensure to be loaded last

by Shnikety » Post

This is an idea for an api for use with two dimensional array(s) of object-subtypes.
ie:
  • not just a bucket that can be filled with various liquids but also various
    types of buckets that can do the same.

    or tools made of various metals:
    add a metal and a new shovel, sword, pick, and axe are auto-generated;
    add another tool and stone, bronze, and steel versions of it are created.
This automatically registers recipes as-needed and in any sequence
allowing mods to cooperate together through a common dependency on the api.

Unfortunately, it doesn't auto register nodes by reading node groups so this would be most useful in a game where mods could be altered to make use of it. Perhaps using minetest.get_modnames to find mods and create recipes with not-necessarily-defined-yet nodes in the same way that dye mod does with flowers would be a good solution.

Code: Select all

if not minetest then
	minetest = {}
	minetest.log = print
	minetest.after = function(t,f) return f end
end

local meta = {} -- registration meta helper
meta.registered = {}
meta.register = {}

local func_flags = {} -- helper for method 'new_registration_func'

-- registration function for creating new categories
-- this registers two wrapper functions that loop through either of two tables
-- calling the main registration function as needed
function meta:new(tbl1_name, tbl2_name, registration_function, flag1, flag2)
	local tbl1, tbl2 = {}, {}

	-- register global tables
	-- only the first table is saved by default
	if flag1 ~= false then self.registered[tbl1_name] = tbl1 end
	if flag2 == true  then self.registered[tbl2_name] = tbl2 end

	-- this calls register(tbl1_k, tbl2_k, tbl1_v, tbl2_v)
	local register = registration_function

	-- default registration function for a new tbl1 key
	local func1 = function(new_key, new_val)
		tbl1[new_key] = new_val

		for tbl2_k,tbl2_v in pairs(tbl2) do
			register(new_key, tbl2_k, new_val, tbl2_v)
		end
	end

	-- default registration function for a new tbl2 key
	local func2 = function(new_key, new_val)
		tbl2[new_key] = new_val

		for tbl1_k,tbl1_v in pairs(tbl1) do
			register(tbl1_k, new_key, tbl1_v, new_val)
		end
	end

	-- assign registration function for tbl1
	self.register[tbl1_name] =
		( self.register[tbl1_name] and func_flags[tbl1_name] ) and
			function(...) return func1( self.register[tbl1_name](...) ) end
		or func1

	-- assign registration function for tbl2
	self.register[tbl2_name] =
		( self.register[tbl2_name] and func_flags[tbl2_name] ) and
			function(...) return func2( self.register[tbl2_name](...) ) end
		or func2
end

-- overwrite or add-to the default registration functions
-- flag causes the old or default registration function to be called with the
-- return value of the new registration function
function meta:new_registration_func(name, new_func, flag)
	local old_func = self.register[name]
	if flag and old_func then
		self.register[name] = function(...)
			return old_func( new_func(...) )
		end
	else
		-- in case 'new_registration_func' is called before 'new'
		func_flags[name] = flag

		materials_api.register[name] = new_func
	end
end

materials_api = meta

minetest.after(0, function()
	material_api.new = nil
end)

---------- example usage ----------------

-- create new category
materials_api:new("foo", "bar", function(foo_name, bar_name, foo_val, bar_val)
	-- do some application level stuff here...

	minetest.log("action", "[materials_api] Registered: ".. foo_name .." ".. bar_name)
	if not foo_val then minetest.log("warning", "no foo val") end
	if not bar_val then minetest.log("warning", "no bar val") end
end)

-- register new item for bar
materials_api.register.bar("beer", {
	image_overlay = "material_bar_beer.png"
})

-- register new item for foo
materials_api.register.foo("banana", {
	image = "material_foo_banana.png"
})

-- register new item for foo
materials_api.register.foo("poptart", {
	image = "material_foo_poptart.png"
})

-- register new item for bar
materials_api.register.bar("daiquiri", {
	image_overlay = "material_bar_daiquiri.png"
})

-- register new item for foo
materials_api.register.foo("strawberry", {
	image = "material_foo_strawberry.png"
})

Post Reply

Who is online

Users browsing this forum: No registered users and 16 guests