Page 1 of 1

[Mod] Portal API for player-constructed portals [nether]

Posted: Sun Feb 23, 2020 08:58
by DrFrankenstone
The Nether mod now provides an API to make it easy to define custom portals, and for other realm-ish mods to add their own portal mechanic.

This means Minetest servers can contain multiple realms that are discoverable and explorable organically, and allows game progression / gating and tech-trees. It can implement game mechanics like the Nether portal's fast-travel, or allow emergent portal gameplay.

Two playable portal examples are provided, in addition to the Nether portal itself, and can be enabled in the mod options:
  • Floatlands portal
  • Surface portal
Those and other playable portals are demo'd in the video below:

Image

If custom portals have been defined then players can discover how to build them either in the help [doc] modpack (if installed) or the Book of Portals, which appears in dungeon chests if the help modpack is not installed:

Image

Image

How to define a portal

From portal_api.txt

Essentially you pick a node type to have your portals built from, a shape in which the portals must be built, and provide 3 functions for portals to find their destinations with:
  • find_realm_anchorPos(surface_origin_pos)
  • find_surface_anchorPos(realm_origin_pos)
  • is_within_realm(pos)
Optionally decorate by choosing portal colors, particles, media etc.

It uses the concept of a realm for each type of portal. If a portal is outside its realm then it links to a portal inside the realm, if a portal is inside its realm then it links to the outside.

You get to decide what constitutes your realm by implementing the function is_within_realm(position). For example, the Nether realm is defined as existing at a certain depth and anything above that depth is considered outside the Realm.

API and Nether Relationship

You can disable the Nether & its portal if you wish to use the Portals API without the Nether. The Nether mod depends on default and stairs, so currently the portals API does as well, however it does not need to. A forked API could theoretically be separated from the Nether mod if a version without default dependency was needed.

The Nether provides a new node type similar to obsidian that's earmarked for custom portals - basalts (natural, hewn, and chiselled), allowing other mods to have portals which players cannot construct until having reached and explored the Nether.

License:
(see README.txt)
Sourcecode: ISC
Textures: CC BY-SA

Depends:
default
stairs
many soft dependencies

GitHub:
https://github.com/minetest-mods/nether

Re: [Mod] Portal API for player-constructed portals [nether]

Posted: Thu Mar 12, 2020 14:51
by DrFrankenstone
FreeGamers wrote:I implemented some portals with the API. They came our pretty nicely. I used the api.txt and examples.lua as templates. They are pretty basic implementations but I tested them and tweaked them. They definitely work well as early implementations for my game. Players now have a way to access the moon and martian biomes on their own without an admin setting things up with other portal mods beforehand. :)

https://www.notabug.org/FreeGamers/Sara ... portal.lua
https://www.notabug.org/FreeGamers/Sara ... portal.lua

If you have any suggestions feel free to let me know. I am still get a bit familiar with the functions and such that are in this.
Awesome!

Those Martian and Lunar portals are fine as they are so don't feel like they need more tweaks. That said, this thread could do with some tweaking examples and there's certainly room to play...

Adding special effects
You could go way over the top here, like adding a slow corruption effect via the on_run_wormhole event, but for some much lower hanging fruit there's that Stargate ignition effect you see at the start of the portals api demo video.

I probably overvalue ignition effects because I'm always rebuilding portals in test worlds where the database is set to dummy, or I have portals hooked up to switches. On a multiplayer server I imagine most portals will be turned on once, and only the player that ignited it will witness the ignition sequence, but regardless, a simple particle effect is free to add...
Spoiler

Code: Select all

on_ignite = function(portalDef, anchorPos, orientation)

	-- make some sparks fly
	local p1, p2 = portalDef.shape:get_p1_and_p2_from_anchorPos(anchorPos, orientation)
	local pos = vector.divide(vector.add(p1, p2), 2)

	local textureName = portalDef.particle_texture
	if type(textureName) == "table" then textureName = textureName.name end

	minetest.add_particlespawner({
		amount = 110,
		time   = 0.12,
		minpos = {x = pos.x - 1.5, y = pos.y - 1.5, z = pos.z - 1.5},
		maxpos = {x = pos.x + 1.5, y = pos.y - 1,   z = pos.z + 1.5},
		minvel = {x = 0, y = 12, z = 0},
		maxvel = {x = 0, y = 15, z = 0},
		minacc = {x = 0, y = 0, z = 0},
		maxacc = {x = 0, y = 0, z = 0},
		minexptime = 0.1,
		maxexptime = 0.6,
		minsize = 0.7 * portalDef.particle_texture_scale,
		maxsize = 0.9 * portalDef.particle_texture_scale,
		collisiondetection = false,
		texture = textureName .. "^[colorize:#FFF:alpha",
		animation = portalDef.particle_texture_animation,
		glow = 8
	})
end,
Spoiler

Code: Select all

on_ignite = function(portalDef, anchorPos, orientation)

	-- make some sparks fly
	local p1, p2 = portalDef.shape:get_p1_and_p2_from_anchorPos(anchorPos, orientation)
	local pos = vector.divide(vector.add(p1, p2), 2)

	local textureName = portalDef.particle_texture
	if type(textureName) == "table" then textureName = textureName.name end

	local velocity
	if orientation == 0 then
		velocity = {x = 0, y = 0, z = 15}
	else
		velocity = {x = 15, y = 0, z = 0}
	end

	local particleSpawnerDef = {
		amount = 200,
		time   = 0.1,
		minpos = {x = pos.x - 2, y = pos.y - 2, z = pos.z - 2},
		maxpos = {x = pos.x + 2, y = pos.y + 2, z = pos.z + 2},
		minvel = velocity,
		maxvel = velocity,
		minacc = {x = 0, y = 0, z = 0},
		maxacc = {x = 0, y = 0, z = 0},
		minexptime = 0.1,
		maxexptime = 0.5,
		minsize = 1.0 * portalDef.particle_texture_scale,
		maxsize = 1.4 * portalDef.particle_texture_scale,
		collisiondetection = false,
		texture = textureName .. "^[colorize:#F20:alpha",
		animation = portalDef.particle_texture_animation,
		glow = 8
	}

	minetest.add_particlespawner(particleSpawnerDef)

	velocity = vector.multiply(velocity, -1);
	particleSpawnerDef.minvel, particleSpawnerDef.maxvel = velocity, velocity
	minetest.add_particlespawner(particleSpawnerDef)
end,
Hunting for better spawn locations
Currently the Lunar/Martian portals don't change your x or z coordinates, only y, that can be good from a gameplay point of view because it's a very simple predictable mechanic that players can find uses for. However, it also means that if the x, z coordinate is not a suitable location on the surface for a portal then the portal will be placed underground. In contrast, the Surface-travel portal (in portal_examples.lua) will hunt around for a suitable location on the surface, it doesn't always find one but it's much better at spawning on the surface than the default implementation of find_surface_anchorPos().

The nature of the Surface-travel portal means there's no trade-off in having it hunt for a good location, but for other portals, keeping the simplicity of a portal that preserves x and z position might be more important than reducing underground spawns.
Spoiler
The trick to this one is that the Portals API uses minetest.get_spawn_level() to obtain the mapgen ground height for placing the portal, and get_spawn_level returns nil if the location is too high, or is water, or in mgv7 is too close to rivers, so if you implement a find_surface_anchorPos function in the portal which searches for a location where minetest.get_spawn_level() doesn't return nil then you can get a portal on the surface (unless a player has built something on the surface at that location)

Code: Select all

find_surface_anchorPos = function(realm_anchorPos)
	-- A portal definition doesn't normally need to provide a find_surface_anchorPos() function,
	-- since find_surface_target_y() will be used by default, but we can increase the chances
	-- of a surface portal spawning on the ground instead of underground if we test many locations
	-- for suitability instead of one.

	local search_radius = 150 -- any portal within this area will do

	-- a y_factor of 0 makes the search ignore the altitude of the portals, provided they 
	-- are the right type in the right realm.
	local existing_portal_location, existing_portal_orientation =
		nether.find_nearest_working_portal("martian_portal", {x = realm_anchorPos.x, y = 0, z = realm_anchorPos.z}, search_radius, 0)

	if existing_portal_location ~= nil then
		-- use the existing portal that was found near realm_anchorPos.x, realm_anchorPos.z
		return existing_portal_location, existing_portal_orientation
	else
		-- find a good location for the new portal
		local adj_x, adj_z = 0, 0

		-- Deterministically look for a location near realm_anchorPos where get_spawn_level() can give
		-- us a surface height, since nether.find_surface_target_y() works *much* better when
		-- it can use get_spawn_level()
		local prng = PcgRandom( -- seed the prng so that opening portals in the immediate vacinity will try the same random locations
			math.floor(realm_anchorPos.x / 20) * 65732 +
			math.floor(realm_anchorPos.z / 20) * 729   +
			minetest.get_mapgen_setting("seed") * 3
		)

		local attemptLimit = 20 -- how many attempts we'll make at finding a good location
		for attempt = 1, attemptLimit do
			if minetest.get_spawn_level == nil or minetest.get_spawn_level(realm_anchorPos.x + adj_x, realm_anchorPos.z + adj_z) ~= nil then
				-- Found a location which will be at ground level - unless a player has built there.
				-- Or this is MT 0.4 which does not have get_spawn_level(), so there's no point looking
				-- at any further further random locations.
				break
			end
			-- setting adj_x & adj_z after first check so that portals always check directly below themselves first
			adj_x = math.floor(prng:rand_normal_dist(-search_radius, search_radius, 2) + 0.5)
			adj_z = math.floor(prng:rand_normal_dist(-search_radius, search_radius, 2) + 0.5)
			if attempt == attemptLimit then adj_x, adj_z = 0, 0 end -- Couldn't find good location, using the natural location directly under the portal
		end

		local destination_pos = {x = realm_anchorPos.x + adj_x, y = 0, z = realm_anchorPos.z + adj_z}
		destination_pos.y = nether.find_surface_target_y(destination_pos.x, destination_pos.z, "martian_portal")

		return destination_pos
	end
end,
Using decorative facedir nodes
Ornate portals are possible if you use a chiselled or carved node as the frame node.

(This is a terrible example, one day I will make a better one)
Image

Re: [Mod] Portal API for player-constructed portals [nether]

Posted: Thu Mar 12, 2020 17:23
by CalebJ
This looks like a really nice API. I wish it could be split away from nether so that each person could customize it for their own worldgen, is that possible? :-)

Re: [Mod] Portal API for player-constructed portals [nether]

Posted: Thu Mar 12, 2020 19:57
by texmex
This is rad! I’ll look to integrate it in my project when the time for realms comes.

Re: [Mod] Portal API for player-constructed portals [nether]

Posted: Thu Mar 12, 2020 20:46
by DrFrankenstone
CalebJ wrote:This looks like a really nice API. I wish it could be split away from nether so that each person could customize it for their own worldgen, is that possible? :-)
What sort of customization did you have in mind?

It could be split, but there's an option in the Nether mod that turns the Nether off, so you can already use the API without having the Nether.

cloudlands has a soft dependency on nether and checks whether the API is present, if the api is present then it registers a cloudlands portal.

Re: [Mod] Portal API for player-constructed portals [nether]

Posted: Fri Mar 13, 2020 05:39
by FreeGamers
Great video. I havent seen this post before somehow. I will respond soon and will optimize some things. I like the stargate like portal. Makes me miss meseportals a bit more.

Re: [Mod] Portal API for player-constructed portals [nether]

Posted: Sat Mar 14, 2020 19:14
by texmex
How does your node pattern match algorithm compare to multiblock?

Re: [Mod] Portal API for player-constructed portals [nether]

Posted: Sat Mar 14, 2020 23:31
by DrFrankenstone
texmex wrote:How does your node pattern match algorithm compare to multiblock?
A limitation of this API is that each shape definition has only one size: A shape definition provides an apply_func_to_frame_nodes() function and an apply_func_to_wormhole_nodes() function, and code which calls those functions is not expected to know or provide any extra size information. It's not very conducive to having a regex definition, but is forwards and backwards compatible with the original Nether mod.

Multiblock doesn't appear to look for different orientations, and the portal API isn't perfect here either - it grew from the Nether portal code and parts of it still assume a symmetric portal like the Nether portal, where only two orientations need to be checked for instead of 4. And parts of it will check two orientations even for an x/z symmetric shape like PortalShape_Platform where only one check is needed.

Defining a new portal shape could be made simpler by it accepting a pattern like multiblock which it could then analyse and generate all the necessary functions from, though you'd still have to provide an image for the help modpack or book of portals. However, I wasn't sure whether alternate portal shapes would even be used by anyone. There's a fourth portal shape I was tempted to make but didn't because until people are using the API it's hard to know what's needed and whether you're wasting your time. If there's a desired shape, let me know.

(There are some other shape limitations, for instance a portal frame must be made of all the same nodetype, this is not a fundamental limitation and could be changed, but I'm not sure such a change would be worthwhile)

I should add that custom portal shapes don't need to be merged into the Nether mod before you can use them - they're just a table with functions, so new ones could be made and posted here.

Re: [Mod] Portal API for player-constructed portals [nether]

Posted: Wed Mar 18, 2020 04:45
by CalebJ
That's quite nice, thanks for the information! :)

Re: [Mod] Portal API for player-constructed portals [nether]

Posted: Sun Apr 19, 2020 09:45
by FreeGamers
Player is reporting that nether portals tend to connect to other player's portals underground sometimes. Not sure if this is just intentional or oversight or just not important :) Just figured I'd mention it either way.

Re: [Mod] Portal API for player-constructed portals [nether]

Posted: Sun Apr 19, 2020 12:35
by DrFrankenstone
FreeGamers wrote:Player is reporting that nether portals tend to connect to other player's portals underground sometimes. Not sure if this is just intentional or oversight or just not important :) Just figured I'd mention it either way.
Thanks for passing that along. The portals in the Nether mod behave similarly to the Minecraft portal:
  • Opening a portal to the surface will link to the closest existing portal within a 64 block radius, if none is found then one will be created.
  • Opening a portal to the Nether will link to the closest existing portal within an 8 block radius, if none is found then one will be created.
The Nether mod specifies this behaviour (i.e. it's not something forced by the Portal API), and what the player describes sounds like something that happens in Minecraft. e.g you build a portal and it links to a nearby one instead of creating a portal at the location you calculated it should appear. The way I solve that in both games is to go to the location where the portal should have appeared, build a portal there, and when you step through that new portal then your original portal will be the closest one to the target location and the two will connect.

Your neighbour's portal will then be extinguished, because the Portals API only allows 1:1 connections (Minecraft doesn't have that limitation) so you effectively just disconnected your portal from your neighbours, but when your neighbour re-ignites their portal it will connect to the portal which is closest to its target location - presumably the one it was originally connected to.

What the player is describing is vague, so it's possible they're facing a different issue, but I just tested the behaviour I described above and it was working as expected, and the nether highway on Sara's Survival Server does have many nether portals in close proximity.

Re: [Mod] Portal API for player-constructed portals [nether]

Posted: Thu May 21, 2020 02:39
by FreeGamers
Not sure if this has been mentioned or not.. allow me to be blunt.

The nether is boring. Not much diversity. Its much too dark. I tried adding mobs to it in my game to spruce it up a bit but someone has recently called it boring and I have to agree with them. We were brainstorming for ways to improve it but decided it might make more sense to bring the conversation here.

I suggested perhaps the eventual addition of different biomes (such as other game) which seemed to spruce it up.

Re: [Mod] Portal API for player-constructed portals [nether]

Posted: Thu May 21, 2020 03:09
by MisterE
So I would like to suggest that we add some LBMs to the nether so that decorations and other interesting things can be added.

Here are some ideas for things that could be added:

FIre tree

fire grass

pulsing glowstones (which are a rounded mesh instead of a cube) and have a chance of exploding when mined. They can be crafted into tnt blocks.

Nether crystals

man-eating flora, which grow slowly and cover more area

Re: [Mod] Portal API for player-constructed portals [nether]

Posted: Thu May 21, 2020 06:27
by DrFrankenstone
...allow me to be blunt. The nether is boring.
I would like to suggest that we add some LBMs to the nether so that decorations and other interesting things can be added.
Agreed. I made a small start creating some pull requests along these lines, but someone has to review/approve/pull them, and even once merged the contentDB version won't be updated because it's in PilzAdam's contentDB account and I think he left.
(those PRs can be playtested together here)

There are hundreds of PRs waiting for approval in the main Minetest project, and I imagine the minetest-mods projects are a lower priority for the devs' time. I think Krock has given me the ability to review/merge PRs in the nether mod project though, so I can help with other people's PRs (I assume one should refrain from reviewing/merging one's own code).

Note that anyone can help test, or review code - feedback can be given without having permissions to merge - this saves devs needing to figure it out. Also, my own PRs might not be doing things the best way.

Re: [Mod] Portal API for player-constructed portals [nether]

Posted: Thu May 21, 2020 14:40
by FreeGamers
Thanks for the follow up. I really appreciate all the work you've put into this mod. The recent portal API update was very cool and resolved a major design problem I had with my game.

I wonder if rubenwardy would help transfer ownership or maintenance of nether on ContentDB for you.

Do you have any suggestions on learning to write biome generation? I assume its only a matter of time before I have to learn this more anyways. If I come up with new nether ideas, I hope I can share here.

The mobs_mc pack had some really good assets for minecraft mobs, but I don't know how much we want to replicate the exact atmosphere and function of this. I assume some deviation is a good idea.

Re: [Mod] Portal API for player-constructed portals [nether]

Posted: Thu May 21, 2020 23:39
by MisterE
Perhaps this can be temporarily fixed by creating a dependent mod that adds the appropriate LBMs. I may start on such a mob soon

Re: [Mod] Portal API for player-constructed portals [nether]

Posted: Wed May 27, 2020 16:29
by FreeGamers
One the players on my server showed me an infinite portal-material glitch he can do with this mod.

It depends on a magic_mirror item in the game which can teleport players back to their beds instantly.

Here is the method used to create the item duplication using nether's portal API:

Description: Exploit (Material duplication): Nether portal is exploitable for infinite portal-materials if a magic_mirror is used.

Method:
First, create a portal and use it to warp to the exit portal.
Then, break the exit portal and gather materials from it.
Then, use magic_mirror to return home.
Then re-initiate portal entrance. The portal and materials will be regenerated.

I could just remove the magic_mirror from the game as a temporary way to try to prevent this. But I think it can be done if a player uses two pair of portals as well. Can we come up with a solution for this?

Re: [Mod] Portal API for player-constructed portals [nether]

Posted: Mon Jun 08, 2020 12:26
by DrFrankenstone
FreeGamers wrote:
Wed May 27, 2020 16:29
First, create a portal and use it to warp to the exit portal.
Then, break the exit portal and gather materials from it.
I used to do that in Minecraft :)
From a gameplay perspective I'm fine with it, as building a portal for the first time is a bit like unlocking a realm, if a player is clever with the portal mechanics the cleverness is rewarded by not having to grind so much for a second portal into the same realm.

However, if it's not desirable, I can think of some solutions...
  • The Minecraft solution: Mining portal frame material is hard (in Minecraft it takes ages with unenchanted picks). Minetest has the option of having frame nodes that quickly wear though your picks
  • Each ignition costs a mese shard. This can be sidestepped using mesecon ignition, but it does provide a potential solution - disable mesecon portal ignitions (if mesecons are installed) and/or make the consumed-on-ignition item even more expensive.
  • When a portal breaks, its whole frame could disappear (instead of disappear it could turn to stone, or turn into a cracked version of the frame node, or only half the nodes disappear, but those would prevent a new portal from being able to appear there). You could use the portal's on_extinguish event to do this, but that will be triggered for both portal ends - so both portals would disappear, unless you did something like test for player distance when the event is triggered and decide which portal to remove based on that.

Code: Select all

local function obliterate_node(node_pos)
  -- do what you gotta do
  ⁝
end

nether.register_portal("my_portal", {
  ⁝
  on_extinguish = function(portalDef, anchorPos, orientation)
    portalDef.shape.apply_func_to_frame_nodes(anchorPos, orientation, obliterate_node)
  end,
  ⁝
})