Initial commit.

master
Robert Zenz 2015-07-23 19:47:37 +02:00
commit 309d71d72d
14 changed files with 1009 additions and 0 deletions

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "deps/utils"]
path = deps/utils
url = https://github.com/RobertZenz/minetest-australopithecus-utils.git

24
LICENSE Normal file
View File

@ -0,0 +1,24 @@
Copyright (c) 2014, Robert 'Bobby' Zenz
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

11
Makefile Normal file
View File

@ -0,0 +1,11 @@
doc := doc
all: doc
clean:
$(RM) -R $(doc)
.PHONY: doc
doc:
luadoc -d $(doc) mods/spawn_usher

11
README Normal file
View File

@ -0,0 +1,11 @@
minetest-australopithecus-spawn-usher
=====================================
A simple system that corrects the spawn of players without knowing anything
about the mapgen that is used.
To activate the system, add it to your `depends.txt` and call this in your
`init.lua.`
spawnusher.activate()

1
README.md Symbolic link
View File

@ -0,0 +1 @@
README

1
deps/utils vendored Submodule

@ -0,0 +1 @@
Subproject commit 2ae4c72b3d14d74c7b6ac9e919c02164f521b745

View File

@ -0,0 +1,94 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
<title>Luadocs for mods/spawn_usher/init.lua</title>
<link rel="stylesheet" href="../../../luadoc.css" type="text/css" />
<!--meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/-->
</head>
<body>
<div id="container">
<div id="product">
<div id="product_logo"></div>
<div id="product_name"><big><b></b></big></div>
<div id="product_description"></div>
</div> <!-- id="product" -->
<div id="main">
<div id="navigation">
<h1>LuaDoc</h1>
<ul>
<li><a href="../../../index.html">Index</a></li>
</ul>
<!-- Module list -->
<!-- File list -->
<h1>Files</h1>
<ul>
<li><strong>mods/spawn_usher/init.lua</strong></li>
<li>
<a href="../../../files/mods/spawn_usher/spawnusher.html">mods/spawn_usher/spawnusher.lua</a>
</li>
</ul>
</div> <!-- id="navigation" -->
<div id="content">
<h1>File <code>mods/spawn_usher/init.lua</code></h1>
<br/>
<br/>
</div> <!-- id="content" -->
</div> <!-- id="main" -->
<div id="about">
<p><a href="http://validator.w3.org/check?uri=referer"><img src="http://www.w3.org/Icons/valid-xhtml10" alt="Valid XHTML 1.0!" height="31" width="88" /></a></p>
</div> <!-- id="about" -->
</div> <!-- id="container" -->
</body>
</html>

View File

@ -0,0 +1,261 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
<title>Luadocs for mods/spawn_usher/spawnusher.lua</title>
<link rel="stylesheet" href="../../../luadoc.css" type="text/css" />
<!--meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/-->
</head>
<body>
<div id="container">
<div id="product">
<div id="product_logo"></div>
<div id="product_name"><big><b></b></big></div>
<div id="product_description"></div>
</div> <!-- id="product" -->
<div id="main">
<div id="navigation">
<h1>LuaDoc</h1>
<ul>
<li><a href="../../../index.html">Index</a></li>
</ul>
<!-- Module list -->
<!-- File list -->
<h1>Files</h1>
<ul>
<li>
<a href="../../../files/mods/spawn_usher/init.html">mods/spawn_usher/init.lua</a>
</li>
<li><strong>mods/spawn_usher/spawnusher.lua</strong></li>
</ul>
</div> <!-- id="navigation" -->
<div id="content">
<h1>File <code>mods/spawn_usher/spawnusher.lua</code></h1>
<h2>Functions</h2>
<table class="function_list">
<tr>
<td class="name" nowrap><a href="#spawnusher.activate">spawnusher.activate</a>&nbsp;(require_air_bubble_size, retry_time)</td>
<td class="summary">Activates the spawn usher system.</td>
</tr>
<tr>
<td class="name" nowrap><a href="#spawnusher.is_air_bubble">spawnusher.is_air_bubble</a>&nbsp;(start_pos)</td>
<td class="summary">Tests if the given position is an air bubble big enough.</td>
</tr>
<tr>
<td class="name" nowrap><a href="#spawnusher.move_later">spawnusher.move_later</a>&nbsp;(player, current_pos)</td>
<td class="summary">Schedules the player to be moved later.</td>
</tr>
<tr>
<td class="name" nowrap><a href="#spawnusher.move_player">spawnusher.move_player</a>&nbsp;(player)</td>
<td class="summary">Moves the player to a safe location.</td>
</tr>
<tr>
<td class="name" nowrap><a href="#spawnusher.move_players">spawnusher.move_players</a>&nbsp;()</td>
<td class="summary">Move all players that could not be placed so far.</td>
</tr>
</table>
<br/>
<br/>
<h2><a name="functions"></a>Functions</h2>
<dl class="function">
<dt><a name="spawnusher.activate"></a><strong>spawnusher.activate</strong>&nbsp;(require_air_bubble_size, retry_time)</dt>
<dd>
Activates the spawn usher system.
<h3>Parameters</h3>
<ul>
<li>
require_air_bubble_size: Optional. The size/height of the bubble of air that is required for the player to spawn. Defaults to 2.
</li>
<li>
retry_time: Optional. This is the time that passes between tries to place to the player.
</li>
</ul>
</dd>
<dt><a name="spawnusher.is_air_bubble"></a><strong>spawnusher.is_air_bubble</strong>&nbsp;(start_pos)</dt>
<dd>
Tests if the given position is an air bubble big enough.
<h3>Parameters</h3>
<ul>
<li>
start_pos: The position at which to check.
</li>
</ul>
<h3>Return value:</h3>
true if at the given position is an air bubble big enough.
</dd>
<dt><a name="spawnusher.move_later"></a><strong>spawnusher.move_later</strong>&nbsp;(player, current_pos)</dt>
<dd>
Schedules the player to be moved later. Also moves the player to the given position.
<h3>Parameters</h3>
<ul>
<li>
player: The player object.
</li>
<li>
current_pos: The current position to which the player will be moved.
</li>
</ul>
</dd>
<dt><a name="spawnusher.move_player"></a><strong>spawnusher.move_player</strong>&nbsp;(player)</dt>
<dd>
Moves the player to a safe location.
<h3>Parameters</h3>
<ul>
<li>
player: The player object.
</li>
</ul>
</dd>
<dt><a name="spawnusher.move_players"></a><strong>spawnusher.move_players</strong>&nbsp;()</dt>
<dd>
Move all players that could not be placed so far.
</dd>
</dl>
</div> <!-- id="content" -->
</div> <!-- id="main" -->
<div id="about">
<p><a href="http://validator.w3.org/check?uri=referer"><img src="http://www.w3.org/Icons/valid-xhtml10" alt="Valid XHTML 1.0!" height="31" width="88" /></a></p>
</div> <!-- id="about" -->
</div> <!-- id="container" -->
</body>
</html>

94
doc/index.html Normal file
View File

@ -0,0 +1,94 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
<title> Luadocs Index</title>
<link rel="stylesheet" href="luadoc.css" type="text/css" />
<!--meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/-->
</head>
<body>
<div id="container">
<div id="product">
<div id="product_logo"></div>
<div id="product_name"><big><b></b></big></div>
<div id="product_description"></div>
</div> <!-- id="product" -->
<div id="main">
<div id="navigation">
<h1>LuaDoc</h1>
<ul>
<li><strong>Index</strong></li>
</ul>
<!-- Module list -->
<!-- File list -->
<h1>Files</h1>
<ul>
<li>
<a href="files/mods/spawn_usher/init.html">mods/spawn_usher/init.lua</a>
</li>
<li>
<a href="files/mods/spawn_usher/spawnusher.html">mods/spawn_usher/spawnusher.lua</a>
</li>
</ul>
</div> <!-- id="navigation" -->
<div id="content">
<h2>Files</h2>
<table class="file_list">
<!--<tr><td colspan="2">Files</td></tr>-->
<tr>
<td class="name"><a href="files/mods/spawn_usher/init.html">mods/spawn_usher/init.lua</a></td>
<td class="summary"></td>
</tr>
<tr>
<td class="name"><a href="files/mods/spawn_usher/spawnusher.html">mods/spawn_usher/spawnusher.lua</a></td>
<td class="summary"></td>
</tr>
</table>
</div> <!-- id="content" -->
</div> <!-- id="main" -->
<div id="about">
<p><a href="http://validator.w3.org/check?uri=referer"><img src="http://www.w3.org/Icons/valid-xhtml10" alt="Valid XHTML 1.0!" height="31" width="88" /></a></p>
</div> <!-- id="about" -->
</div> <!-- id="container" -->
</body>
</html>

286
doc/luadoc.css Normal file
View File

@ -0,0 +1,286 @@
body {
margin-left: 1em;
margin-right: 1em;
font-family: arial, helvetica, geneva, sans-serif;
background-color:#ffffff; margin:0px;
}
code {
font-family: "Andale Mono", monospace;
}
tt {
font-family: "Andale Mono", monospace;
}
body, td, th { font-size: 11pt; }
h1, h2, h3, h4 { margin-left: 0em; }
textarea, pre, tt { font-size:10pt; }
body, td, th { color:#000000; }
small { font-size:0.85em; }
h1 { font-size:1.5em; }
h2 { font-size:1.25em; }
h3 { font-size:1.15em; }
h4 { font-size:1.06em; }
a:link { font-weight:bold; color: #004080; text-decoration: none; }
a:visited { font-weight:bold; color: #006699; text-decoration: none; }
a:link:hover { text-decoration:underline; }
hr { color:#cccccc }
img { border-width: 0px; }
h3 { padding-top: 1em; }
p { margin-left: 1em; }
p.name {
font-family: "Andale Mono", monospace;
padding-top: 1em;
margin-left: 0em;
}
blockquote { margin-left: 3em; }
pre.example {
background-color: rgb(245, 245, 245);
border-top-width: 1px;
border-right-width: 1px;
border-bottom-width: 1px;
border-left-width: 1px;
border-top-style: solid;
border-right-style: solid;
border-bottom-style: solid;
border-left-style: solid;
border-top-color: silver;
border-right-color: silver;
border-bottom-color: silver;
border-left-color: silver;
padding: 1em;
margin-left: 1em;
margin-right: 1em;
font-family: "Andale Mono", monospace;
font-size: smaller;
}
hr {
margin-left: 0em;
background: #00007f;
border: 0px;
height: 1px;
}
ul { list-style-type: disc; }
table.index { border: 1px #00007f; }
table.index td { text-align: left; vertical-align: top; }
table.index ul { padding-top: 0em; margin-top: 0em; }
table {
border: 1px solid black;
border-collapse: collapse;
margin-left: auto;
margin-right: auto;
}
th {
border: 1px solid black;
padding: 0.5em;
}
td {
border: 1px solid black;
padding: 0.5em;
}
div.header, div.footer { margin-left: 0em; }
#container
{
margin-left: 1em;
margin-right: 1em;
background-color: #f0f0f0;
}
#product
{
text-align: center;
border-bottom: 1px solid #cccccc;
background-color: #ffffff;
}
#product big {
font-size: 2em;
}
#product_logo
{
}
#product_name
{
}
#product_description
{
}
#main
{
background-color: #f0f0f0;
border-left: 2px solid #cccccc;
}
#navigation
{
float: left;
width: 18em;
margin: 0;
vertical-align: top;
background-color: #f0f0f0;
overflow:visible;
}
#navigation h1 {
background-color:#e7e7e7;
font-size:1.1em;
color:#000000;
text-align:left;
margin:0px;
padding:0.2em;
border-top:1px solid #dddddd;
border-bottom:1px solid #dddddd;
}
#navigation ul
{
font-size:1em;
list-style-type: none;
padding: 0;
margin: 1px;
}
#navigation li
{
text-indent: -1em;
margin: 0em 0em 0em 0.5em;
display: block;
padding: 3px 0px 0px 12px;
}
#navigation li li a
{
padding: 0px 3px 0px -1em;
}
#content
{
margin-left: 18em;
padding: 1em;
border-left: 2px solid #cccccc;
border-right: 2px solid #cccccc;
background-color: #ffffff;
}
#about
{
clear: both;
margin: 0;
padding: 5px;
border-top: 2px solid #cccccc;
background-color: #ffffff;
}
@media print {
body {
font: 12pt "Times New Roman", "TimeNR", Times, serif;
}
a { font-weight:bold; color: #004080; text-decoration: underline; }
#main { background-color: #ffffff; border-left: 0px; }
#container { margin-left: 2%; margin-right: 2%; background-color: #ffffff; }
#content { margin-left: 0px; padding: 1em; border-left: 0px; border-right: 0px; background-color: #ffffff; }
#navigation { display: none;
}
pre.example {
font-family: "Andale Mono", monospace;
font-size: 10pt;
page-break-inside: avoid;
}
}
table.module_list td
{
border-width: 1px;
padding: 3px;
border-style: solid;
border-color: #cccccc;
}
table.module_list td.name { background-color: #f0f0f0; }
table.module_list td.summary { width: 100%; }
table.file_list
{
border-width: 1px;
border-style: solid;
border-color: #cccccc;
border-collapse: collapse;
}
table.file_list td
{
border-width: 1px;
padding: 3px;
border-style: solid;
border-color: #cccccc;
}
table.file_list td.name { background-color: #f0f0f0; }
table.file_list td.summary { width: 100%; }
table.function_list
{
border-width: 1px;
border-style: solid;
border-color: #cccccc;
border-collapse: collapse;
}
table.function_list td
{
border-width: 1px;
padding: 3px;
border-style: solid;
border-color: #cccccc;
}
table.function_list td.name { background-color: #f0f0f0; }
table.function_list td.summary { width: 100%; }
table.table_list
{
border-width: 1px;
border-style: solid;
border-color: #cccccc;
border-collapse: collapse;
}
table.table_list td
{
border-width: 1px;
padding: 3px;
border-style: solid;
border-color: #cccccc;
}
table.table_list td.name { background-color: #f0f0f0; }
table.table_list td.summary { width: 100%; }
dl.function dt {border-top: 1px solid #ccc; padding-top: 1em;}
dl.function dd {padding-bottom: 1em;}
dl.function h3 {padding: 0; margin: 0; font-size: medium;}
dl.table dt {border-top: 1px solid #ccc; padding-top: 1em;}
dl.table dd {padding-bottom: 1em;}
dl.table h3 {padding: 0; margin: 0; font-size: medium;}
#TODO: make module_list, file_list, function_list, table_list inherit from a list

View File

@ -0,0 +1 @@
utils

32
mods/spawn_usher/init.lua Normal file
View File

@ -0,0 +1,32 @@
--[[
Copyright (c) 2015, Robert 'Bobby' Zenz
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--]]
-- Get the base path.
local base_path = minetest.get_modpath(minetest.get_current_modname())
dofile(base_path .. "/spawnusher.lua")

View File

@ -0,0 +1,189 @@
--[[
Copyright (c) 2015, Robert 'Bobby' Zenz
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--]]
--- Spawn usher is a system that allows to correct the spawn position of players
-- without knowing anything about the mapgen.
--
-- The system will register callbacks for newplayer and respawnplayer and will
-- try to find an air bubble, either upwards or downwards, which the player can
-- fit into. If an air bubble is found, the player will be moved there. If
-- the block is not loaded, it will be tried again after a certain amount of
-- time.
--
-- The only function that should be called from clients is activate.
spawnusher = {
players = List:new(),
required_bubble_size = 2,
retry_time = 0.5,
scheduled = false
}
--- Activates the spawn usher system.
--
-- @param require_air_bubble_size Optional. The size/height of the bubble of air
-- that is required for the player to spawn.
-- Defaults to 2.
-- @param retry_time Optional. This is the time that passes between tries to
-- place to the player.
function spawnusher.activate(require_air_bubble_size, retry_time)
minetest.register_on_newplayer(spawnusher.move_player)
minetest.register_on_respawnplayer(spawnusher.move_player)
end
--- Tests if the given position is an air bubble big enough.
--
-- @param start_pos The position at which to check.
-- @return true if at the given position is an air bubble big enough.
function spawnusher.is_air_bubble(start_pos)
local pos = {
x = start_pos.x,
y = start_pos.y,
z = start_pos.z
}
for counter = 1, spawnusher.required_bubble_size, 1 do
pos.y = pos.y + 1
if minetest.get_node(pos).name ~= "air" then
return false
end
end
return true
end
--- Schedules the player to be moved later. Also moves the player to the given
-- position.
--
-- @param player The player object.
-- @param current_pos The current position to which the player will be moved.
function spawnusher.move_later(player, current_pos)
player:setpos(current_pos)
spawnusher.players:add(player)
-- Override the physics of the player to make sure that the player does
-- not fall while we wait.
player:set_physics_override({
speed = 0,
jump = 0,
gravity = 0,
sneak = false,
sneak_glitch = false
})
if not spawnusher.scheduled then
spawnusher.scheduled = true
minetest.after(spawnusher.retry_time, spawnusher.move_players)
end
end
--- Moves the player to a safe location.
--
-- @param player The player object.
function spawnusher.move_player(player)
local pos = player:getpos()
-- Could be while true, but at least this is halfway sane.
while mathutil.in_range(pos.y, -31000, 31000) do
local current = minetest.get_node(pos).name
if current ~= "air" and current ~= "ignore" then
-- The current node is neither air nor ignore, that means it
-- is "solid", so we walk upwards looking for air.
pos.y = pos.y + 1
elseif current == "air" then
-- The current node is air, now we will check if the node below it
-- is also air, if yes we will move downwards, if not we will check
-- if here is an air bubble.
local beneath_pos = {
x = pos.x,
y = pos.y - 1,
z = pos.z
}
local beneath_node = minetest.get_node(beneath_pos).name
if beneath_node == "air" then
-- The node below is air, move two downwards looking for
-- a "solid" node.
pos.y = pos.y - 2
elseif beneath_node == "ignore" then
-- The node below is ignore, means we will have to try again
-- later.
spawnusher.move_later(player, pos)
return
elseif spawnusher.is_air_bubble(pos) then
-- Awesome! Place the user here.
player:setpos(pos)
-- Reset the physics override.
player:set_physics_override({
speed = 1,
jump = 1,
gravity = 1,
sneak = true,
sneak_glitch = true
})
return
else
-- The node beneath is neither air nor ignore and there is no
-- air bubble big enough, lets go upwards and see if that
-- helps.
pos.y = pos.y + 2
end
elseif current == "ignore" then
-- The current node is ignore, which means we need to retry later.
spawnusher.move_later(player, pos)
return
end
end
end
--- Move all players that could not be placed so far.
function spawnusher.move_players()
-- Copy the list to make sure that no one adds a player while we iterate
-- over it. Though, I'm not sure if that is actually possible, but the Java
-- programmer does not stop to scream "race condition" without this.
local to_move_players = spawnusher.players
spawnusher.players = List:new()
to_move_players:foreach(function(player, index)
spawnusher.move_player(player)
end)
-- If there are still players that could not be placed, schedule it again.
if spawnusher.players:size() > 0 then
minetest.after(spawnusher.retry_time, spawnusher.move_players)
else
spawnusher.scheduled = false
end
end

1
mods/utils Symbolic link
View File

@ -0,0 +1 @@
../deps/utils/utils