Merge remote-tracking branch 'upstream/stable-0.4' into sync

master
MoNTE48 2019-04-01 20:18:54 +02:00
commit e98d421aff
121 changed files with 2083 additions and 1448 deletions

3
.gitignore vendored
View File

@ -77,6 +77,7 @@ src/cmake_config_githash.h
src/lua/build/
locale/
.directory
.gradle/
*.cbp
*.layout
*.o
@ -84,6 +85,8 @@ locale/
*.ninja
.ninja*
*.gch
*.iml
test_config.h
cmake-build-debug/
cmake-build-release/

View File

@ -16,12 +16,13 @@ set(CMAKE_CXX_STANDARD 11)
set(VERSION_MAJOR 1)
set(VERSION_MINOR 1)
set(VERSION_PATCH 10)
set(VERSION_TWEAK 0)
set(VERSION_EXTRA "" CACHE STRING "Stuff to append to version string")
# Change to false for releases
set(DEVELOPMENT_BUILD FALSE)
set(VERSION_STRING "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}")
set(VERSION_STRING "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}.${VERSION_TWEAK}")
if(VERSION_EXTRA)
set(VERSION_STRING ${VERSION_STRING}-${VERSION_EXTRA})
elseif(DEVELOPMENT_BUILD)

3
build/WindowsApp/buildwin32.sh Executable file → Normal file
View File

@ -139,4 +139,7 @@ cmake .. \
make package -j $(nproc)
[ "x$NO_PACKAGE" = "x" ] && make package
exit 0
# EOF

3
build/WindowsApp/buildwin64.sh Executable file → Normal file
View File

@ -139,4 +139,7 @@ cmake .. \
make package -j $(nproc)
[ "x$NO_PACKAGE" = "x" ] && make package
exit 0
# EOF

View File

@ -17,13 +17,13 @@ allprojects {
apply plugin: 'com.android.application'
android {
compileSdkVersion 27
buildToolsVersion "27.0.3"
compileSdkVersion 28
buildToolsVersion "28.0.3"
defaultConfig {
applicationId "mobi.MultiCraft"
minSdkVersion 16
targetSdkVersion 27
targetSdkVersion 28
versionCode 92
}
Properties props = new Properties()
@ -73,5 +73,5 @@ android.applicationVariants.all { variant ->
}
dependencies {
implementation 'com.android.support:support-compat:27.1.1'
implementation 'com.android.support:support-compat:28.0.0'
}

View File

@ -1,12 +1,13 @@
--- irrlicht/source/Irrlicht/CEGLManager.cpp.orig 2018-04-24 19:27:51.034727946 +0200
+++ irrlicht/source/Irrlicht/CEGLManager.cpp 2018-04-24 19:27:55.084614618 +0200
@@ -8,6 +8,9 @@
#include "irrString.h"
#include "os.h"
+#if defined(_IRR_COMPILE_WITH_ANDROID_DEVICE_)
+#include <android/native_activity.h>
+#endif
namespace irr
{
--- irrlicht/source/Irrlicht/CEGLManager.cpp.orig 2018-06-10 16:58:11.357709173 +0200
+++ irrlicht/source/Irrlicht/CEGLManager.cpp 2018-06-10 16:58:25.100709843 +0200
@@ -9,6 +9,10 @@
#include "irrString.h"
#include "os.h"
+#if defined(_IRR_COMPILE_WITH_ANDROID_DEVICE_)
+#include <android/native_activity.h>
+#endif
+
namespace irr
{
namespace video

View File

@ -0,0 +1,79 @@
package net.minetest.minetest;
import android.Manifest;
import android.app.Activity;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class MainActivity extends Activity {
private final static int PERMISSIONS = 1;
private static final String[] REQUIRED_SDK_PERMISSIONS = new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE};
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
checkPermission();
} else {
next();
}
}
protected void checkPermission() {
final List<String> missingPermissions = new ArrayList<String>();
// check required permission
for (final String permission : REQUIRED_SDK_PERMISSIONS) {
final int result = ContextCompat.checkSelfPermission(this, permission);
if (result != PackageManager.PERMISSION_GRANTED) {
missingPermissions.add(permission);
}
}
if (!missingPermissions.isEmpty()) {
// request permission
final String[] permissions = missingPermissions
.toArray(new String[missingPermissions.size()]);
ActivityCompat.requestPermissions(this, permissions, PERMISSIONS);
} else {
final int[] grantResults = new int[REQUIRED_SDK_PERMISSIONS.length];
Arrays.fill(grantResults, PackageManager.PERMISSION_GRANTED);
onRequestPermissionsResult(PERMISSIONS, REQUIRED_SDK_PERMISSIONS,
grantResults);
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[],
@NonNull int[] grantResults) {
switch (requestCode) {
case PERMISSIONS:
for (int index = 0; index < permissions.length; index++) {
if (grantResults[index] != PackageManager.PERMISSION_GRANTED) {
// permission not granted - toast and exit
Toast.makeText(this, R.string.not_granted, Toast.LENGTH_LONG).show();
finish();
return;
}
}
// permission were granted - run
next();
break;
}
}
public void next() {
Intent intent = new Intent(this, MtNativeActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(intent);
}
}

View File

@ -120,7 +120,12 @@ end
-- The dumped and level arguments are internal-only.
function dump(o, indent, nested, level)
if type(o) ~= "table" then
local t = type(o)
if not level and t == "userdata" then
-- when userdata (e.g. player) is passed directly, print its metatable:
return "userdata metatable: " .. dump(getmetatable(o))
end
if t ~= "table" then
return basic_dump(o)
end
-- Contains table -> true/nil of currently nested tables
@ -308,59 +313,25 @@ function core.formspec_escape(text)
end
function core.wrap_text(text, charlimit)
local retval = {}
local current_idx = 1
local start,stop = string_find(text, " ", current_idx)
local nl_start,nl_stop = string_find(text, "\n", current_idx)
local gotnewline = false
if nl_start ~= nil and (start == nil or nl_start < start) then
start = nl_start
stop = nl_stop
gotnewline = true
end
local last_line = ""
while start ~= nil do
if string.len(last_line) + (stop-start) > charlimit then
retval[#retval + 1] = last_line
last_line = ""
end
if last_line ~= "" then
last_line = last_line .. " "
end
last_line = last_line .. string_sub(text, current_idx, stop - 1)
if gotnewline then
retval[#retval + 1] = last_line
last_line = ""
gotnewline = false
end
current_idx = stop+1
start,stop = string_find(text, " ", current_idx)
nl_start,nl_stop = string_find(text, "\n", current_idx)
if nl_start ~= nil and (start == nil or nl_start < start) then
start = nl_start
stop = nl_stop
gotnewline = true
end
function core.wrap_text(text, max_length, as_table)
local result = {}
local line = {}
if #text <= max_length then
return as_table and {text} or text
end
--add last part of text
if string.len(last_line) + (string.len(text) - current_idx) > charlimit then
retval[#retval + 1] = last_line
retval[#retval + 1] = string_sub(text, current_idx)
else
last_line = last_line .. " " .. string_sub(text, current_idx)
retval[#retval + 1] = last_line
for word in text:gmatch('%S+') do
local cur_length = #table.concat(line, ' ')
if cur_length > 0 and cur_length + #word + 1 >= max_length then
-- word wouldn't fit on current line, move to next line
table.insert(result, table.concat(line, ' '))
line = {}
end
table.insert(line, word)
end
return retval
table.insert(result, table.concat(line, ' '))
return as_table and result or table.concat(result, '\n')
end
--------------------------------------------------------------------------------
@ -370,7 +341,7 @@ if INIT == "game" then
local dirs2 = {20, 23, 22, 21}
function core.rotate_and_place(itemstack, placer, pointed_thing,
infinitestacks, orient_flags)
infinitestacks, orient_flags, prevent_after_place)
orient_flags = orient_flags or {}
local unode = core.get_node_or_nil(pointed_thing.under)
@ -379,41 +350,20 @@ if INIT == "game" then
end
local undef = core.registered_nodes[unode.name]
if undef and undef.on_rightclick then
undef.on_rightclick(pointed_thing.under, unode, placer,
return undef.on_rightclick(pointed_thing.under, unode, placer,
itemstack, pointed_thing)
return
end
local fdir = core.dir_to_facedir(placer:get_look_dir())
local wield_name = itemstack:get_name()
local fdir = placer and core.dir_to_facedir(placer:get_look_dir()) or 0
local above = pointed_thing.above
local under = pointed_thing.under
local iswall = (above.y == under.y)
local isceiling = not iswall and (above.y < under.y)
local anode = core.get_node_or_nil(above)
if not anode then
return
end
local pos = pointed_thing.above
local node = anode
if undef and undef.buildable_to then
pos = pointed_thing.under
node = unode
iswall = false
end
if core.is_protected(pos, placer:get_player_name()) then
core.record_protection_violation(pos,
placer:get_player_name())
return
end
local ndef = core.registered_nodes[node.name]
if not ndef or not ndef.buildable_to then
return
end
if orient_flags.force_floor then
iswall = false
isceiling = false
@ -427,31 +377,26 @@ if INIT == "game" then
iswall = not iswall
end
local param2 = fdir
if iswall then
core.set_node(pos, {name = wield_name,
param2 = dirs1[fdir + 1]})
param2 = dirs1[fdir + 1]
elseif isceiling then
if orient_flags.force_facedir then
core.set_node(pos, {name = wield_name,
param2 = 20})
cparam2 = 20
else
core.set_node(pos, {name = wield_name,
param2 = dirs2[fdir + 1]})
param2 = dirs2[fdir + 1]
end
else -- place right side up
if orient_flags.force_facedir then
core.set_node(pos, {name = wield_name,
param2 = 0})
else
core.set_node(pos, {name = wield_name,
param2 = fdir})
param2 = 0
end
end
if not infinitestacks then
itemstack:take_item()
return itemstack
end
local old_itemstack = ItemStack(itemstack)
local new_itemstack, removed = core.item_place_node(
itemstack, placer, pointed_thing, param2, prevent_after_place
)
return infinitestacks and old_itemstack or new_itemstack
end
@ -459,12 +404,18 @@ if INIT == "game" then
--Wrapper for rotate_and_place() to check for sneak and assume Creative mode
--implies infinite stacks when performing a 6d rotation.
--------------------------------------------------------------------------------
local creative_mode_cache = core.settings:get_bool("creative_mode")
local function is_creative(name)
return creative_mode_cache or
core.check_player_privs(name, {creative = true})
end
core.rotate_node = function(itemstack, placer, pointed_thing)
local name = placer and placer:get_player_name() or ""
local invert_wall = placer and placer:get_player_control().sneak or false
core.rotate_and_place(itemstack, placer, pointed_thing,
core.settings:get_bool("creative_mode"),
{invert_wall = placer:get_player_control().sneak})
is_creative(name),
{invert_wall = invert_wall}, true)
return itemstack
end
end
@ -642,44 +593,26 @@ end
local ESCAPE_CHAR = string.char(0x1b)
-- Client-side mods don't have access to settings
if core.settings and core.settings:get_bool("disable_escape_sequences") then
function core.get_color_escape_sequence(color)
return ""
end
function core.get_background_escape_sequence(color)
return ""
end
function core.colorize(color, message)
return message
end
else
function core.get_color_escape_sequence(color)
return ESCAPE_CHAR .. "(c@" .. color .. ")"
end
function core.get_background_escape_sequence(color)
return ESCAPE_CHAR .. "(b@" .. color .. ")"
end
function core.colorize(color, message)
local lines = tostring(message):split("\n", true)
local color_code = core.get_color_escape_sequence(color)
for i, line in ipairs(lines) do
lines[i] = color_code .. line
end
return table.concat(lines, "\n") .. core.get_color_escape_sequence("#ffffff")
end
function core.get_color_escape_sequence(color)
return ESCAPE_CHAR .. "(c@" .. color .. ")"
end
function core.get_background_escape_sequence(color)
return ESCAPE_CHAR .. "(b@" .. color .. ")"
end
function core.colorize(color, message)
local lines = tostring(message):split("\n", true)
local color_code = core.get_color_escape_sequence(color)
for i, line in ipairs(lines) do
lines[i] = color_code .. line
end
return table.concat(lines, "\n") .. core.get_color_escape_sequence("#ffffff")
end
function core.strip_foreground_colors(str)
return (str:gsub(ESCAPE_CHAR .. "%(c@[^)]+%)", ""))
end

View File

@ -67,16 +67,15 @@ local function save_auth_file()
assert(type(stuff.privileges) == "table")
assert(stuff.last_login == nil or type(stuff.last_login) == "number")
end
local file, errmsg = io.open(core.auth_file_path, 'w+b')
if not file then
error(core.auth_file_path.." could not be opened for writing: "..errmsg)
end
local content = {}
for name, stuff in pairs(core.auth_table) do
local priv_string = core.privs_to_string(stuff.privileges)
local parts = {name, stuff.password, priv_string, stuff.last_login or ""}
file:write(table.concat(parts, ":").."\n")
content[#content + 1] = table.concat(parts, ":")
end
if not core.safe_file_write(core.auth_file_path, table.concat(content, "\n")) then
error(core.auth_file_path.." could not be written to")
end
io.close(file)
end
read_auth_file()

View File

@ -653,8 +653,8 @@ core.register_chatcommand("pulverize", {
core.rollback_punch_callbacks = {}
core.register_on_punchnode(function(pos, node, puncher)
local name = puncher:get_player_name()
if core.rollback_punch_callbacks[name] then
local name = puncher and puncher:get_player_name()
if name and core.rollback_punch_callbacks[name] then
core.rollback_punch_callbacks[name](pos, node, puncher)
core.rollback_punch_callbacks[name] = nil
end
@ -814,7 +814,7 @@ core.register_chatcommand("shutdown", {
message = message or ""
if delay ~= "" then
delay = tonumber(param) or 0
delay = tonumber(delay) or 0
else
delay = 0
core.log("action", name .. " shuts down server")

View File

@ -60,8 +60,13 @@ core.register_entity(":__builtin:falling_node", {
local pos = self.object:getpos()
-- Position of bottom center point
local bcp = {x = pos.x, y = pos.y - 0.7, z = pos.z}
-- Avoid bugs caused by an unloaded node below
-- 'bcn' is nil for unloaded nodes
local bcn = core.get_node_or_nil(bcp)
-- Delete on contact with ignore at world edges
if bcn and bcn.name == "ignore" then
self.object:remove()
return
end
local bcd = bcn and core.registered_nodes[bcn.name]
if bcn and
(not bcd or bcd.walkable or
@ -93,7 +98,7 @@ core.register_entity(":__builtin:falling_node", {
core.remove_node(np)
if nd and nd.buildable_to == false then
-- Add dropped items
local drops = core.get_node_drops(n2.name, "")
local drops = core.get_node_drops(n2, "")
for _, dropped_item in pairs(drops) do
core.add_item(np, dropped_item)
end
@ -145,9 +150,9 @@ function core.spawn_falling_node(pos)
end
local function drop_attached_node(p)
local nn = core.get_node(p).name
local n = core.get_node(p)
core.remove_node(p)
for _, item in pairs(core.get_node_drops(nn, "")) do
for _, item in pairs(core.get_node_drops(n, "")) do
local pos = {
x = p.x + math.random()/2 - 0.25,
y = p.y + math.random()/2 - 0.25,

View File

@ -155,11 +155,45 @@ function core.yaw_to_dir(yaw)
return {x = -math.sin(yaw), y = 0, z = math.cos(yaw)}
end
function core.get_node_drops(nodename, toolname)
function core.is_colored_paramtype(ptype)
return (ptype == "color") or (ptype == "colorfacedir") or
(ptype == "colorwallmounted")
end
function core.strip_param2_color(param2, paramtype2)
if not core.is_colored_paramtype(paramtype2) then
return nil
end
if paramtype2 == "colorfacedir" then
param2 = math.floor(param2 / 32) * 32
elseif paramtype2 == "colorwallmounted" then
param2 = math.floor(param2 / 8) * 8
end
-- paramtype2 == "color" requires no modification.
return param2
end
function core.get_node_drops(node, toolname)
-- Compatibility, if node is string
local nodename = node
local param2 = 0
-- New format, if node is table
if (type(node) == "table") then
nodename = node.name
param2 = node.param2
end
local def = core.registered_nodes[nodename]
local drop = def and def.drop
local ptype = def and def.paramtype2
-- get color, if there is color (otherwise nil)
local palette_index = core.strip_param2_color(param2, ptype)
if drop == nil then
-- default drop
if palette_index then
local stack = ItemStack(nodename)
stack:get_meta():set_int("palette_index", palette_index)
return {stack:to_string()}
end
return {nodename}
elseif type(drop) == "string" then
-- itemstring drop
@ -181,6 +215,8 @@ function core.get_node_drops(nodename, toolname)
end
if item.tools ~= nil then
good_tool = false
end
if item.tools ~= nil and toolname then
for _, tool in ipairs(item.tools) do
if tool:sub(1, 1) == '~' then
good_tool = toolname:find(tool:sub(2)) ~= nil
@ -191,10 +227,16 @@ function core.get_node_drops(nodename, toolname)
break
end
end
end
end
if good_rarity and good_tool then
got_count = got_count + 1
for _, add_item in ipairs(item.items) do
-- add color, if necessary
if item.inherit_color and palette_index then
local stack = ItemStack(add_item)
stack:get_meta():set_int("palette_index", palette_index)
add_item = stack:to_string()
end
got_items[#got_items+1] = add_item
end
if drop.max_items ~= nil and got_count == drop.max_items then
@ -205,7 +247,22 @@ function core.get_node_drops(nodename, toolname)
return got_items
end
function core.item_place_node(itemstack, placer, pointed_thing, param2)
local function user_name(user)
return user and user:get_player_name() or ""
end
local function is_protected(pos, name)
return core.is_protected(pos, name) and
not minetest.check_player_privs(name, "protection_bypass")
end
-- Returns a logging function. For empty names, does not log.
local function make_log(name)
return name ~= "" and core.log or function() end
end
function core.item_place_node(itemstack, placer, pointed_thing, param2,
prevent_after_place)
local def = itemstack:get_definition()
if def.type ~= "node" or pointed_thing.type ~= "node" then
return itemstack, false
@ -215,10 +272,11 @@ function core.item_place_node(itemstack, placer, pointed_thing, param2)
local oldnode_under = core.get_node_or_nil(under)
local above = pointed_thing.above
local oldnode_above = core.get_node_or_nil(above)
local playername = placer:get_player_name()
local playername = user_name(placer)
local log = make_log(playername)
if not oldnode_under or not oldnode_above then
core.log("info", playername .. " tried to place"
log("info", playername .. " tried to place"
.. " node in unloaded position " .. core.pos_to_string(above))
return itemstack, false
end
@ -229,7 +287,7 @@ function core.item_place_node(itemstack, placer, pointed_thing, param2)
olddef_above = olddef_above or core.nodedef_default
if not olddef_above.buildable_to and not olddef_under.buildable_to then
core.log("info", playername .. " tried to place"
log("info", playername .. " tried to place"
.. " node in invalid position " .. core.pos_to_string(above)
.. ", replacing " .. oldnode_above.name)
return itemstack, false
@ -240,13 +298,12 @@ function core.item_place_node(itemstack, placer, pointed_thing, param2)
-- If node under is buildable_to, place into it instead (eg. snow)
if olddef_under.buildable_to then
core.log("info", "node under is buildable to")
log("info", "node under is buildable to")
place_to = {x = under.x, y = under.y, z = under.z}
end
if core.is_protected(place_to, playername) and
not minetest.check_player_privs(placer, "protection_bypass") then
core.log("action", playername
if is_protected(place_to, playername) then
log("action", playername
.. " tried to place " .. def.name
.. " at protected position "
.. core.pos_to_string(place_to))
@ -254,11 +311,11 @@ function core.item_place_node(itemstack, placer, pointed_thing, param2)
return itemstack
end
core.log("action", playername .. " places node "
log("action", playername .. " places node "
.. def.name .. " at " .. core.pos_to_string(place_to))
local oldnode = core.get_node(place_to)
local newnode = {name = def.name, param1 = 0, param2 = param2}
local newnode = {name = def.name, param1 = 0, param2 = param2 or 0}
-- Calculate direction for wall mounted stuff like torches and signs
if def.place_param2 ~= nil then
@ -274,7 +331,7 @@ function core.item_place_node(itemstack, placer, pointed_thing, param2)
-- Calculate the direction for furnaces and chests and stuff
elseif (def.paramtype2 == "facedir" or
def.paramtype2 == "colorfacedir") and not param2 then
local placer_pos = placer:getpos()
local placer_pos = placer and placer:getpos()
if placer_pos then
local dir = {
x = above.x - placer_pos.x,
@ -282,14 +339,33 @@ function core.item_place_node(itemstack, placer, pointed_thing, param2)
z = above.z - placer_pos.z
}
newnode.param2 = core.dir_to_facedir(dir)
core.log("action", "facedir: " .. newnode.param2)
log("action", "facedir: " .. newnode.param2)
end
end
local metatable = itemstack:get_meta():to_table().fields
-- Transfer color information
if metatable.palette_index and not def.place_param2 then
local color_divisor = nil
if def.paramtype2 == "color" then
color_divisor = 1
elseif def.paramtype2 == "colorwallmounted" then
color_divisor = 8
elseif def.paramtype2 == "colorfacedir" then
color_divisor = 32
end
if color_divisor then
local color = math.floor(metatable.palette_index / color_divisor)
local other = newnode.param2 % color_divisor
newnode.param2 = color * color_divisor + other
end
end
-- Check if the node is attached and if it can be placed there
if core.get_item_group(def.name, "attached_node") ~= 0 and
not builtin_shared.check_attached_node(place_to, newnode) then
core.log("action", "attached node " .. def.name ..
log("action", "attached node " .. def.name ..
" can not be placed at " .. core.pos_to_string(place_to))
return itemstack, false
end
@ -300,7 +376,7 @@ function core.item_place_node(itemstack, placer, pointed_thing, param2)
local take_item = true
-- Run callback
if def.after_place_node then
if def.after_place_node and not prevent_after_place then
-- Deepcopy place_to and pointed_thing because callback can modify it
local place_to_copy = {x=place_to.x, y=place_to.y, z=place_to.z}
local pointed_thing_copy = copy_pointed_thing(pointed_thing)
@ -360,28 +436,27 @@ function core.item_secondary_use(itemstack, placer)
end
function core.item_drop(itemstack, dropper, pos)
if dropper and dropper:is_player() then
local v = dropper:get_look_dir()
local p = {x=pos.x, y=pos.y+1.2, z=pos.z}
local cs = itemstack:get_count()
local dropper_is_player = dropper and dropper:is_player()
local p = table.copy(pos)
local cnt = itemstack:get_count()
if dropper_is_player then
p.y = p.y + 1.2
if dropper:get_player_control().sneak then
cs = 1
cnt = 1
end
local item = itemstack:take_item(cs)
local obj = core.add_item(p, item)
if obj then
v.x = v.x*2
v.y = v.y*2 + 2
v.z = v.z*2
obj:setvelocity(v)
end
local item = itemstack:take_item(cnt)
local obj = core.add_item(p, item)
if obj then
if dropper_is_player then
local dir = dropper:get_look_dir()
dir.x = dir.x * 2.9
dir.y = dir.y * 2.9 + 2
dir.z = dir.z * 2.9
obj:set_velocity(dir)
obj:get_luaentity().dropped_by = dropper:get_player_name()
return itemstack
end
else
if core.add_item(pos, itemstack) then
return itemstack
end
return itemstack
end
-- If we reach this, adding the object to the
-- environment failed
@ -402,7 +477,8 @@ function core.do_item_eat(hp_change, replace_with_item, itemstack, user, pointed
itemstack:add_item(replace_with_item)
else
local inv = user:get_inventory()
if inv:room_for_item("main", {name=replace_with_item}) then
-- Check if inv is null, since non-players don't have one
if inv and inv:room_for_item("main", {name=replace_with_item}) then
inv:add_item("main", replace_with_item)
else
local pos = user:getpos()
@ -417,7 +493,9 @@ end
function core.item_eat(hp_change, replace_with_item)
return function(itemstack, user, pointed_thing) -- closure
return core.do_item_eat(hp_change, replace_with_item, itemstack, user, pointed_thing)
if user then
return core.do_item_eat(hp_change, replace_with_item, itemstack, user, pointed_thing)
end
end
end
@ -434,63 +512,76 @@ end
function core.handle_node_drops(pos, drops, digger)
-- Add dropped items to object's inventory
if digger:get_inventory() then
local _, dropped_item
for _, dropped_item in ipairs(drops) do
local left = digger:get_inventory():add_item("main", dropped_item)
if not left:is_empty() then
local p = {
x = pos.x + math.random()/2-0.25,
y = pos.y + math.random()/2-0.25,
z = pos.z + math.random()/2-0.25,
}
core.add_item(p, left)
end
local inv = digger and digger:get_inventory()
local give_item
if inv then
give_item = function(item)
return inv:add_item("main", item)
end
else
give_item = function(item)
-- itemstring to ItemStack for left:is_empty()
return ItemStack(item)
end
end
for _, dropped_item in pairs(drops) do
local left = give_item(dropped_item)
if not left:is_empty() then
local p = {
x = pos.x + math.random()/2-0.25,
y = pos.y + math.random()/2-0.25,
z = pos.z + math.random()/2-0.25,
}
core.add_item(p, left)
end
end
end
function core.node_dig(pos, node, digger)
local diggername = user_name(digger)
local log = make_log(diggername)
local def = core.registered_nodes[node.name]
if def and (not def.diggable or
(def.can_dig and not def.can_dig(pos, digger))) then
core.log("info", digger:get_player_name() .. " tried to dig "
log("info", diggername .. " tried to dig "
.. node.name .. " which is not diggable "
.. core.pos_to_string(pos))
return
end
if core.is_protected(pos, digger:get_player_name()) and
not minetest.check_player_privs(digger, "protection_bypass") then
core.log("action", digger:get_player_name()
if is_protected(pos, diggername) then
log("action", diggername
.. " tried to dig " .. node.name
.. " at protected position "
.. core.pos_to_string(pos))
core.record_protection_violation(pos, digger:get_player_name())
core.record_protection_violation(pos, diggername)
return
end
core.log('action', digger:get_player_name() .. " digs "
log('action', diggername .. " digs "
.. node.name .. " at " .. core.pos_to_string(pos))
local wielded = digger:get_wielded_item()
local drops = core.get_node_drops(node.name, wielded:get_name())
local wielded = digger and digger:get_wielded_item()
local drops = core.get_node_drops(node, wielded and wielded:get_name())
local wdef = wielded:get_definition()
local tp = wielded:get_tool_capabilities()
local dp = core.get_dig_params(def and def.groups, tp)
if wdef and wdef.after_use then
wielded = wdef.after_use(wielded, digger, node, dp) or wielded
else
-- Wear out tool
if not core.settings:get_bool("creative_mode") then
wielded:add_wear(dp.wear)
if wielded:get_count() == 0 and wdef.sound and wdef.sound.breaks then
core.sound_play(wdef.sound.breaks, {pos = pos, gain = 0.5})
if wielded then
local wdef = wielded:get_definition()
local tp = wielded:get_tool_capabilities()
local dp = core.get_dig_params(def and def.groups, tp)
if wdef and wdef.after_use then
wielded = wdef.after_use(wielded, digger, node, dp) or wielded
else
-- Wear out tool
if not core.settings:get_bool("creative_mode") then
wielded:add_wear(dp.wear)
if wielded:get_count() == 0 and wdef.sound and wdef.sound.breaks then
core.sound_play(wdef.sound.breaks, {pos = pos, gain = 0.5})
end
end
end
digger:set_wielded_item(wielded)
end
digger:set_wielded_item(wielded)
-- Handle drops
core.handle_node_drops(pos, drops, digger)

View File

@ -174,19 +174,18 @@ core.register_entity(":__builtin:item", {
local p = self.object:getpos()
p.y = p.y - 0.5
local node = core.get_node_or_nil(p)
local in_unloaded = (node == nil)
if in_unloaded then
-- Don't infinetly fall into unloaded map
self.object:setvelocity({x = 0, y = 0, z = 0})
self.object:setacceleration({x = 0, y = 0, z = 0})
self.physical_state = false
self.object:set_properties({physical = false})
-- Delete in 'ignore' nodes
if node and node.name == "ignore" then
self.itemstring = ""
self.object:remove()
return
end
local nn = node.name
-- If node is not registered or node is walkably solid and resting on nodebox
-- If node is nil (unloaded area), or node is not registered, or node is
-- walkably solid and item is resting on nodebox
local v = self.object:getvelocity()
if not core.registered_nodes[nn] or core.registered_nodes[nn].walkable and v.y == 0 then
if not node or not core.registered_nodes[node.name] or
core.registered_nodes[node.name].walkable and v.y == 0 then
if self.physical_state then
local own_stack = ItemStack(self.object:get_luaentity().itemstring)
-- Merge with close entities of the same item

View File

@ -5,12 +5,11 @@
--
function core.check_player_privs(name, ...)
local arg_type = type(name)
if (arg_type == "userdata" or arg_type == "table") and
name.get_player_name then -- If it quacks like a Player...
if core.is_player(name) then
name = name:get_player_name()
elseif arg_type ~= "string" then
error("Invalid core.check_player_privs argument type: " .. arg_type, 2)
elseif type(name) ~= "string" then
error("core.check_player_privs expects a player or playername as " ..
"argument.", 2)
end
local requested_privs = {...}
@ -70,6 +69,16 @@ function core.get_connected_players()
return temp_table
end
function core.is_player(player)
-- a table being a player is also supported because it quacks sufficiently
-- like a player if it has the is_player function
local t = type(player)
return (t == "userdata" or t == "table") and
type(player.is_player) == "function" and player:is_player()
end
function minetest.player_exists(name)
return minetest.get_auth_handler().get_auth(name) ~= nil
end

View File

@ -116,6 +116,8 @@ function core.register_item(name, itemdef)
end
itemdef.name = name
local is_overriding = core.registered_items[name]
-- Apply defaults and add to registered_* table
if itemdef.type == "node" then
-- Use the nodebox as selection box if it's not set manually
@ -177,7 +179,13 @@ function core.register_item(name, itemdef)
--core.log("Registering item: " .. itemdef.name)
core.registered_items[itemdef.name] = itemdef
core.registered_aliases[itemdef.name] = nil
register_item_raw(itemdef)
-- Used to allow builtin to register ignore to registered_items
if name ~= "ignore" then
register_item_raw(itemdef)
elseif is_overriding then
core.log("warning", "Attempted redefinition of \"ignore\"")
end
end
function core.unregister_item(name)

View File

@ -21,7 +21,6 @@ if core.print then
core.print = nil -- don't pollute our namespace
end
math.randomseed(os.time())
os.setlocale("C", "numeric")
minetest = core
-- Load other files
@ -47,7 +46,6 @@ elseif INIT == "mainmenu" then
elseif INIT == "async" then
dofile(asyncpath .. "init.lua")
elseif INIT == "client" then
os.setlocale = nil
dofile(clientpath .. "init.lua")
else
error(("Unrecognized builtin initialization type %s!"):format(tostring(INIT)))

View File

@ -249,7 +249,7 @@ end
--------------------------------------------------------------------------------
function text2textlist(xpos, ypos, width, height, tl_name, textlen, text, transparency)
local textlines = core.wrap_text(text, textlen)
local textlines = core.wrap_text(text, textlen, true)
local retval = "textlist[" .. xpos .. "," .. ypos .. ";" .. width ..
"," .. height .. ";" .. tl_name .. ";"

View File

@ -75,7 +75,7 @@ local function get_formspec(tabview, name, tabdata)
if error == nil then
local descriptiontext = descriptionfile:read("*all")
descriptionlines = core.wrap_text(descriptiontext, 42)
descriptionlines = core.wrap_text(descriptiontext, 42, true)
descriptionfile:close()
else
descriptionlines = {}

View File

@ -124,7 +124,7 @@ end
local function formspec(tabview, name, tabdata)
local tab_string =
"box[0,0;3.5,4.5;#999999]" ..
"box[0,0;3.75,4.5;#999999]" ..
"checkbox[0.25,0;cb_smooth_lighting;" .. fgettext("Smooth Lighting") .. ";"
.. dump(core.settings:get_bool("smooth_lighting")) .. "]" ..
"checkbox[0.25,0.5;cb_particles;" .. fgettext("Particles") .. ";"
@ -135,34 +135,34 @@ local function formspec(tabview, name, tabdata)
.. dump(core.settings:get_bool("opaque_water")) .. "]" ..
"checkbox[0.25,2.0;cb_connected_glass;" .. fgettext("Connected Glass") .. ";"
.. dump(core.settings:get_bool("connected_glass")) .. "]" ..
"dropdown[0.25,2.8;3.3;dd_node_highlighting;" .. dd_options.node_highlighting[1] .. ";"
"dropdown[0.25,2.8;3.5;dd_node_highlighting;" .. dd_options.node_highlighting[1] .. ";"
.. getSettingIndex.NodeHighlighting() .. "]" ..
"dropdown[0.25,3.6;3.3;dd_leaves_style;" .. dd_options.leaves[1] .. ";"
"dropdown[0.25,3.6;3.5;dd_leaves_style;" .. dd_options.leaves[1] .. ";"
.. getSettingIndex.Leaves() .. "]" ..
"box[3.75,0;3.75,4.45;#999999]" ..
"label[3.85,0.1;" .. fgettext("Texturing:") .. "]" ..
"dropdown[3.85,0.55;3.85;dd_filters;" .. dd_options.filters[1] .. ";"
"box[4,0;3.75,4.5;#999999]" ..
"label[4.25,0.1;" .. fgettext("Texturing:") .. "]" ..
"dropdown[4.25,0.55;3.5;dd_filters;" .. dd_options.filters[1] .. ";"
.. getSettingIndex.Filter() .. "]" ..
"dropdown[3.85,1.35;3.85;dd_mipmap;" .. dd_options.mipmap[1] .. ";"
"dropdown[4.25,1.35;3.5;dd_mipmap;" .. dd_options.mipmap[1] .. ";"
.. getSettingIndex.Mipmap() .. "]" ..
"label[3.85,2.15;" .. fgettext("Antialiasing:") .. "]" ..
"dropdown[3.85,2.6;3.85;dd_antialiasing;" .. dd_options.antialiasing[1] .. ";"
"label[4.25,2.15;" .. fgettext("Antialiasing:") .. "]" ..
"dropdown[4.25,2.6;3.5;dd_antialiasing;" .. dd_options.antialiasing[1] .. ";"
.. getSettingIndex.Antialiasing() .. "]" ..
"label[3.85,3.45;" .. fgettext("Screen:") .. "]" ..
"checkbox[3.85,3.6;cb_autosave_screensize;" .. fgettext("Autosave screen size") .. ";"
"label[4.25,3.45;" .. fgettext("Screen:") .. "]" ..
"checkbox[4.25,3.6;cb_autosave_screensize;" .. fgettext("Autosave screen size") .. ";"
.. dump(core.settings:get_bool("autosave_screensize")) .. "]" ..
"box[7.75,0;4,4.4;#999999]" ..
"checkbox[8,0;cb_shaders;" .. fgettext("Shaders") .. ";"
"box[8,0;3.75,4.5;#999999]" ..
"checkbox[8.25,0;cb_shaders;" .. fgettext("Shaders") .. ";"
.. dump(core.settings:get_bool("enable_shaders")) .. "]"
if PLATFORM ~= "Android" or PLATFORM ~= "iOS" then
tab_string = tab_string ..
"button[8,4.85;3.75,0.5;btn_change_keys;"
"button[8,4.75;4,1;btn_change_keys;"
.. fgettext("Change keys") .. "]"
end
tab_string = tab_string ..
"button[0,4.85;3.75,0.5;btn_advanced_settings;"
"button[0,4.75;4,1;btn_advanced_settings;"
.. fgettext("Advanced Settings") .. "]"
@ -175,19 +175,19 @@ local function formspec(tabview, name, tabdata)
if core.settings:get_bool("enable_shaders") then
tab_string = tab_string ..
"checkbox[8,0.5;cb_bumpmapping;" .. fgettext("Bump Mapping") .. ";"
"checkbox[8.25,0.5;cb_bumpmapping;" .. fgettext("Bump Mapping") .. ";"
.. dump(core.settings:get_bool("enable_bumpmapping")) .. "]" ..
"checkbox[8,1;cb_tonemapping;" .. fgettext("Tone Mapping") .. ";"
"checkbox[8.25,1;cb_tonemapping;" .. fgettext("Tone Mapping") .. ";"
.. dump(core.settings:get_bool("tone_mapping")) .. "]" ..
"checkbox[8,1.5;cb_generate_normalmaps;" .. fgettext("Normal Mapping") .. ";"
"checkbox[8.25,1.5;cb_generate_normalmaps;" .. fgettext("Normal Mapping") .. ";"
.. dump(core.settings:get_bool("generate_normalmaps")) .. "]" ..
"checkbox[8,2;cb_parallax;" .. fgettext("Parallax Occlusion") .. ";"
"checkbox[8.25,2;cb_parallax;" .. fgettext("Parallax Occlusion") .. ";"
.. dump(core.settings:get_bool("enable_parallax_occlusion")) .. "]" ..
"checkbox[8,2.5;cb_waving_water;" .. fgettext("Waving Water") .. ";"
"checkbox[8.25,2.5;cb_waving_water;" .. fgettext("Waving Water") .. ";"
.. dump(core.settings:get_bool("enable_waving_water")) .. "]" ..
"checkbox[8,3;cb_waving_leaves;" .. fgettext("Waving Leaves") .. ";"
"checkbox[8.25,3;cb_waving_leaves;" .. fgettext("Waving Leaves") .. ";"
.. dump(core.settings:get_bool("enable_waving_leaves")) .. "]" ..
"checkbox[8,3.5;cb_waving_plants;" .. fgettext("Waving Plants") .. ";"
"checkbox[8.25,3.5;cb_waving_plants;" .. fgettext("Waving Plants") .. ";"
.. dump(core.settings:get_bool("enable_waving_plants")) .. "]"
else
tab_string = tab_string ..

View File

@ -133,7 +133,7 @@ local function instrument_register(func, func_name)
return func(instrument {
func = callback,
func_name = register_name
}), ...
}, ...)
end
end

View File

@ -166,10 +166,6 @@ keymap_cmd (Command key) key /
# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3
keymap_cmd_local (Command key) key .
# Key for opening the chat console.
# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3
keyman_console (Console key) key KEY_F10
# Key for toggling unlimited view range.
# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3
keymap_rangeselect (Range select key) key KEY_KEY_R
@ -476,9 +472,11 @@ pause_fps_max (FPS in pause menu) int 20
# View distance in nodes.
viewing_range (Viewing range) int 100 20 4000
# Far view distance in nodes.
# Only for Android.
viewing_range_secondary (Initial secondary viewing range) int 150
# Camera near plane distance in nodes, between 0 and 0.5
# Most users will not need to change this.
# Increasing can reduce artifacting on weaker GPUs.
# 0.1 = Default, 0.25 = Good value for weaker tablets.
near_plane (Near plane) float 0.1 0 0.5
# Width component of the initial window size.
screenW (Screen width) int 800
@ -724,10 +722,9 @@ server_announce (Announce server) bool false
# If you want to announce your ipv6 address, use serverlist_url = v6.servers.minetest.net.
serverlist_url (Serverlist URL) string servers.minetest.net
# Disable escape sequences, e.g. chat coloring.
# Use this if you want to run a server with pre-0.4.14 clients and you want to disable
# the escape sequences generated by mods.
disable_escape_sequences (Disable escape sequences) bool false
# Remove color codes from incoming chat messages
# Use this to stop players from being able to use color in their messages
strip_color_codes (Strip color codes) bool false
[*Network]

View File

@ -9,7 +9,7 @@
FIND_PATH(LUA_INCLUDE_DIR luajit.h
HINTS
$ENV{LUA_DIR}
PATH_SUFFIXES include/luajit-2.0 include/luajit-5_1-2.0 include
PATH_SUFFIXES include/luajit-2.1 include/luajit-2.0 include/luajit-5_1-2.1 include/luajit-5_1-2.0 include
PATHS
~/Library/Frameworks
/Library/Frameworks

View File

@ -628,6 +628,9 @@ Minetest namespace reference
version entirely. To check for the presence of engine features, test
whether the functions exported by the wanted features exist. For example:
`if minetest.nodeupdate then ... end`.
* `minetest.sha1(data, [raw])`: returns the sha1 hash of data
* `data`: string of data to hash
* `raw`: return raw bytes instead of hex digits, default: false
### Logging
* `minetest.debug(...)`
@ -1117,15 +1120,15 @@ The following functions provide escape sequences:
`minetest.get_color_escape_sequence(color) ..
message ..
minetest.get_color_escape_sequence("#ffffff")`
* `color.get_background_escape_sequence(color)`
* `minetest.get_background_escape_sequence(color)`
* `color` is a [ColorString](#colorstring)
* The escape sequence sets the background of the whole text element to
`color`. Only defined for item descriptions and tooltips.
* `color.strip_foreground_colors(str)`
* `minetest.strip_foreground_colors(str)`
* Removes foreground colors added by `get_color_escape_sequence`.
* `color.strip_background_colors(str)`
* `minetest.strip_background_colors(str)`
* Removes background colors added by `get_background_escape_sequence`.
* `color.strip_colors(str)`
* `minetest.strip_colors(str)`
* Removes all color escape sequences.
`ColorString`

View File

@ -1,4 +1,4 @@
Minetest Lua Modding API Reference 0.4.16
Minetest Lua Modding API Reference 0.4.17
=========================================
* More information at <http://www.minetest.net/>
* Developer Wiki: <http://dev.minetest.net/>
@ -211,7 +211,8 @@ when registering it.
The `:` prefix can also be used for maintaining backwards compatibility.
### Aliases
Aliases
-------
Aliases can be added by using `minetest.register_alias(name, convert_to)` or
`minetest.register_alias_force(name, convert_to)`.
@ -232,6 +233,75 @@ you have an item called `epiclylongmodname:stuff`, you could do
and be able to use `/giveme stuff`.
Mapgen aliases
--------------
In a game, a certain number of these must be set to tell core mapgens which
of the game's nodes are to be used by the core mapgens. For example:
minetest.register_alias("mapgen_stone", "default:stone")
### Aliases needed for all mapgens except Mapgen v6
Base terrain:
"mapgen_stone"
"mapgen_water_source"
"mapgen_river_water_source"
Caves:
"mapgen_lava_source"
Dungeons:
Only needed for registered biomes where 'node_stone' is stone:
"mapgen_cobble"
"mapgen_stair_cobble"
"mapgen_mossycobble"
Only needed for registered biomes where 'node_stone' is desert stone:
"mapgen_desert_stone"
"mapgen_stair_desert_stone"
Only needed for registered biomes where 'node_stone' is sandstone:
"mapgen_sandstone"
"mapgen_sandstonebrick"
"mapgen_stair_sandstone_block"
### Aliases needed for Mapgen v6
Terrain and biomes:
"mapgen_stone"
"mapgen_water_source"
"mapgen_lava_source"
"mapgen_dirt"
"mapgen_dirt_with_grass"
"mapgen_sand"
"mapgen_gravel"
"mapgen_desert_stone"
"mapgen_desert_sand"
"mapgen_dirt_with_snow"
"mapgen_snowblock"
"mapgen_snow"
"mapgen_ice"
Flora:
"mapgen_tree"
"mapgen_leaves"
"mapgen_apple"
"mapgen_jungletree"
"mapgen_jungleleaves"
"mapgen_junglegrass"
"mapgen_pine_tree"
"mapgen_pine_needles"
Dungeons:
"mapgen_cobble"
"mapgen_stair_cobble"
"mapgen_mossycobble"
"mapgen_stair_desert_stone"
Textures
--------
Mods should generally prefix their textures with `modname_`, e.g. given
@ -531,9 +601,26 @@ for conversion.
If the `ItemStack`'s metadata contains the `color` field, it will be
lost on placement, because nodes on the map can only use palettes.
If the `ItemStack`'s metadata contains the `palette_index` field, you
currently must manually convert between it and the node's `param2` with
custom `on_place` and `on_dig` callbacks.
If the `ItemStack`'s metadata contains the `palette_index` field, it is
automatically transferred between node and item forms by the engine,
when a player digs or places a colored node.
You can disable this feature by setting the `drop` field of the node
to itself (without metadata).
To transfer the color to a special drop, you need a drop table.
Example:
minetest.register_node("mod:stone", {
description = "Stone",
tiles = {"default_stone.png"},
paramtype2 = "color",
palette = "palette.png",
drop = {
items = {
-- assume that mod:cobblestone also has the same palette
{items = {"mod:cobblestone"}, inherit_color = true },
}
}
})
### Colored items in craft recipes
Craft recipes only support item strings, but fortunately item strings
@ -578,7 +665,7 @@ Example (colored grass block):
description = "Dirt with Grass",
-- Regular tiles, as usual
-- The dirt tile disables palette coloring
tiles = {{name = "default_grass.png"},
tiles = {{name = "default_grass.png"},
{name = "default_dirt.png", color = "white"}},
-- Overlay tiles: define them in the same style
-- The top and bottom tile does not have overlay
@ -792,6 +879,11 @@ node definition:
0 = y+ 1 = z+ 2 = z- 3 = x+ 4 = x- 5 = y-
facedir modulo 4 = rotation around that axis
paramtype2 == "leveled"
^ Only valid for "nodebox" with type = "leveled".
The level of the top face of the nodebox is stored in param2.
The other faces are defined by 'fixed = {}' like 'type = "fixed"' nodeboxes.
The nodebox height is param2 / 64 nodes.
The maximum accepted value of param2 is 127.
paramtype2 == "degrotate"
^ The rotation of this node is stored in param2. Plants are rotated this way.
Values range 0 - 179. The value stored in param2 is multiplied by two to
@ -2060,15 +2152,15 @@ The following functions provide escape sequences:
`minetest.get_color_escape_sequence(color) ..
message ..
minetest.get_color_escape_sequence("#ffffff")`
* `color.get_background_escape_sequence(color)`
* `minetest.get_background_escape_sequence(color)`
* `color` is a ColorString
* The escape sequence sets the background of the whole text element to
`color`. Only defined for item descriptions and tooltips.
* `color.strip_foreground_colors(str)`
* `minetest.strip_foreground_colors(str)`
* Removes foreground colors added by `get_color_escape_sequence`.
* `color.strip_background_colors(str)`
* `minetest.strip_background_colors(str)`
* Removes background colors added by `get_background_escape_sequence`.
* `color.strip_colors(str)`
* `minetest.strip_colors(str)`
* Removes all color escape sequences.
Spatial Vectors
@ -2111,9 +2203,11 @@ Helper functions
* e.g. `string:split("a,b", ",") == {"a","b"}`
* `string:trim()`
* e.g. `string.trim("\n \t\tfoo bar\t ") == "foo bar"`
* `minetest.wrap_text(str, limit)`: returns a string
* Adds new lines to the string to keep it within the specified character limit
* `minetest.wrap_text(str, limit, [as_table])`: returns a string or table
* Adds newlines to the string to keep it within the specified character limit
Note that returned lines may be longer than the limit since it only splits at word borders.
* limit: Maximal amount of characters in one line
* as_table: optional, if true return table of lines instead of string
* `minetest.pos_to_string({x=X,y=Y,z=Z}, decimal_places))`: returns string `"(X,Y,Z)"`
* Convert position to a printable string
Optional: 'decimal_places' will round the x, y and z of the pos to the given decimal place.
@ -2181,7 +2275,7 @@ Helper functions
max_jitter = 0.5, -- maximum packet time jitter
avg_jitter = 0.03, -- average packet time jitter
connection_uptime = 200, -- seconds since client connected
prot_vers = 31, -- protocol version used by client
protocol_version = 32, -- protocol version used by client
-- following information is available on debug build only!!!
-- DO NOT USE IN MODS
--ser_vers = 26, -- serialization version used by client
@ -2199,6 +2293,10 @@ Helper functions
* nil: return all entries,
* true: return only subdirectory names, or
* false: return only file names.
* `minetest.safe_file_write(path, content)`: returns boolean indicating success
* Replaces contents of file at path with new contents in a safe (atomic) way.
Use this instead of below code when writing e.g. database files:
`local f = io.open(path, "wb"); f:write(content); f:close()`
* `minetest.get_version()`: returns a table containing components of the
engine version. Components:
* `project`: Name of the project, eg, "Minetest"
@ -2210,6 +2308,9 @@ Helper functions
version entirely. To check for the presence of engine features, test
whether the functions exported by the wanted features exist. For example:
`if minetest.nodeupdate then ... end`.
* `minetest.sha1(data, [raw])`: returns the sha1 hash of data
* `data`: string of data to hash
* `raw`: return raw bytes instead of hex digits, default: false
### Logging
* `minetest.debug(...)`
@ -2229,6 +2330,8 @@ Call these functions only at load time!
* `minetest.register_craftitem(name, item definition)`
* `minetest.unregister_item(name)`
* `minetest.register_alias(name, convert_to)`
* Also use this to set the 'mapgen aliases' needed in a game for the core
* mapgens. See 'Mapgen aliases' section above.
* `minetest.register_alias_force(name, convert_to)`
* `minetest.register_craft(recipe)`
* Check recipe table syntax for different types below.
@ -2263,6 +2366,7 @@ Call these functions only at load time!
* `minetest.register_on_placenode(func(pos, newnode, placer, oldnode, itemstack, pointed_thing))`
* Called when a node has been placed
* If return `true` no item is taken from `itemstack`
* `placer` may be any valid ObjectRef or nil.
* **Not recommended**; use `on_construct` or `after_place_node` in node definition
whenever possible
* `minetest.register_on_dignode(func(pos, oldnode, digger))`
@ -2356,8 +2460,9 @@ Call these functions only at load time!
* `definition`: `{ description = "description text", give_to_singleplayer = boolean}`
the default of `give_to_singleplayer` is true
* To allow players with `basic_privs` to grant, see `basic_privs` minetest.conf setting.
* `minetest.register_authentication_handler(handler)`
* See `minetest.builtin_auth_handler` in `builtin.lua` for reference
* `minetest.register_authentication_handler(authentication handler definition)`
* Registers an auth handler that overrides the builtin one
* This function can be called by a single mod once only.
### Setting-related
* `minetest.settings`: Settings object containing all of the settings from the
@ -2366,37 +2471,44 @@ Call these functions only at load time!
parses it as a position (in the format `(1,2,3)`). Returns a position or nil.
### Authentication
* `minetest.notify_authentication_modified(name)`
* Should be called by the authentication handler if privileges changes.
* To report everybody, set `name=nil`.
* `minetest.check_password_entry(name, entry, password)`
* Returns true if the "db entry" for a player with name matches given
* password, false otherwise.
* The "db entry" is the usually player-individual value that is derived
* from the player's chosen password and stored on the server in order to allow
* authentication whenever the player desires to log in.
* Only use this function for making it possible to log in via the password from
* via protocols like IRC, other uses for inside the game are frowned upon.
* `minetest.get_password_hash(name, raw_password)`
* Convert a name-password pair to a password hash that Minetest can use.
* The returned value alone is not a good basis for password checks based
* on comparing the password hash in the database with the password hash
* from the function, with an externally provided password, as the hash
* in the db might use the new SRP verifier format.
* For this purpose, use `minetest.check_password_entry` instead.
* `minetest.string_to_privs(str)`: returns `{priv1=true,...}`
* `minetest.privs_to_string(privs)`: returns `"priv1,priv2,..."`
* Convert between two privilege representations
* `minetest.set_player_password(name, password_hash)`
* `minetest.set_player_privs(name, {priv1=true,...})`
* `minetest.get_player_privs(name) -> {priv1=true,...}`
* `minetest.auth_reload()`
* `minetest.check_player_privs(player_or_name, ...)`: returns `bool, missing_privs`
* A quickhand for checking privileges.
* `player_or_name`: Either a Player object or the name of a player.
* `...` is either a list of strings, e.g. `"priva", "privb"` or
a table, e.g. `{ priva = true, privb = true }`.
* `minetest.get_player_ip(name)`: returns an IP address string
* `player_or_name`: Either a Player object or the name of a player.
* `...` is either a list of strings, e.g. `"priva", "privb"` or
a table, e.g. `{ priva = true, privb = true }`.
* `minetest.check_password_entry(name, entry, password)`
* Returns true if the "password entry" for a player with name matches given
password, false otherwise.
* The "password entry" is the password representation generated by the engine
as returned as part of a `get_auth()` call on the auth handler.
* Only use this function for making it possible to log in via password from
external protocols such as IRC, other uses are frowned upon.
* `minetest.get_password_hash(name, raw_password)`
* Convert a name-password pair to a password hash that Minetest can use.
* The returned value alone is not a good basis for password checks based
on comparing the password hash in the database with the password hash
from the function, with an externally provided password, as the hash
in the db might use the new SRP verifier format.
* For this purpose, use `minetest.check_password_entry` instead.
* `minetest.get_player_ip(name)`: returns an IP address string for the player `name`
* The player needs to be online for this to be successful.
* `minetest.get_auth_handler()`: Return the currently active auth handler
* See the `Authentication handler definition`
* Use this to e.g. get the authentication data for a player:
`local auth_data = minetest.get_auth_handler().get_auth(playername)`
* `minetest.notify_authentication_modified(name)`
* Must be called by the authentication handler for privilege changes.
* `name`: string; if omitted, all auth data should be considered modified
* `minetest.set_player_password(name, password_hash)`: Set password hash of player `name`
* `minetest.set_player_privs(name, {priv1=true,...})`: Set privileges of player `name`
* `minetest.auth_reload()`
* See `reload()` in authentication handler definition
`minetest.set_player_password`, `minetest_set_player_privs`, `minetest_get_player_privs`
and `minetest.auth_reload` call the authetification handler.
@ -2462,12 +2574,15 @@ and `minetest.auth_reload` call the authetification handler.
* `nodenames`: e.g. `{"ignore", "group:tree"}` or `"default:dirt"`
* `search_center` is an optional boolean (default: `false`)
If true `pos` is also checked for the nodes
* `minetest.find_nodes_in_area(minp, maxp, nodenames)`: returns a list of positions
* returns as second value a table with the count of the individual nodes found
* `minetest.find_nodes_in_area(pos1, pos2, nodenames)`: returns a list of positions
* `nodenames`: e.g. `{"ignore", "group:tree"}` or `"default:dirt"`
* `minetest.find_nodes_in_area_under_air(minp, maxp, nodenames)`: returns a list of positions
* returned positions are nodes with a node air above
* First return value: Table with all node positions
* Second return value: Table with the count of each node with the node name as index
* Area volume is limited to 4,096,000 nodes
* `minetest.find_nodes_in_area_under_air(pos1, pos2, nodenames)`: returns a list of positions
* `nodenames`: e.g. `{"ignore", "group:tree"}` or `"default:dirt"`
* Return value: Table with all node positions with a node air above
* Area volume is limited to 4,096,000 nodes
* `minetest.get_perlin(noiseparams)`
* `minetest.get_perlin(seeddiff, octaves, persistence, scale)`
* Return world-specific perlin noise (`int(worldseed)+seeddiff`)
@ -2676,6 +2791,13 @@ and `minetest.auth_reload` call the authetification handler.
* Convert a vector into a yaw (angle)
* `minetest.yaw_to_dir(yaw)`
* Convert yaw (angle) to a vector
* `minetest.is_colored_paramtype(ptype)`
* Returns a boolean. Returns `true` if the given `paramtype2` contains color
information (`color`, `colorwallmounted` or `colorfacedir`).
* `minetest.strip_param2_color(param2, paramtype2)`
* Removes everything but the color information from the
given `param2` value.
* Returns `nil` if the given `paramtype2` does not contain color information
* `minetest.get_node_drops(nodename, toolname)`
* Returns list of item names.
* **Note**: This will be removed or modified in a future version.
@ -2711,9 +2833,9 @@ and `minetest.auth_reload` call the authetification handler.
* Example query for `"default:gold_ingot"` will return table:
{
[1]={type = "cooking", width = 3, output = "default:gold_ingot",
[1]={method = "cooking", width = 3, output = "default:gold_ingot",
items = {1 = "default:gold_lump"}},
[2]={type = "normal", width = 1, output = "default:gold_ingot 9",
[2]={method = "normal", width = 1, output = "default:gold_ingot 9",
items = {1 = "default:goldblock"}}
}
* `minetest.handle_node_drops(pos, drops, digger)`
@ -2735,9 +2857,11 @@ and `minetest.auth_reload` call the authetification handler.
### Defaults for the `on_*` item definition functions
These functions return the leftover itemstack.
* `minetest.item_place_node(itemstack, placer, pointed_thing, param2)`
* `minetest.item_place_node(itemstack, placer, pointed_thing[, param2, prevent_after_place])`
* Place item as a node
* `param2` overrides `facedir` and wallmounted `param2`
* `prevent_after_place`: if set to `true`, `after_place_node` is not called
for the newly placed node to prevent a callback and placement loop
* returns `itemstack, success`
* `minetest.item_place_object(itemstack, placer, pointed_thing)`
* Place item as-is
@ -2893,6 +3017,7 @@ These functions return the leftover itemstack.
### Misc.
* `minetest.get_connected_players()`: returns list of `ObjectRefs`
* `minetest.is_player(o)`: boolean, whether `o` is a player
* `minetest.player_exists(name)`: boolean, whether player exists (regardless of online status)
* `minetest.hud_replace_builtin(name, hud_definition)`
* Replaces definition of a builtin hud element
@ -2960,6 +3085,7 @@ These functions return the leftover itemstack.
* Returns true, if player `name` shouldn't be abled to dig at `pos` or do other
actions, defineable by mods, due to some mod-defined ownership-like concept.
Returns false or nil, if the player is allowed to do such actions.
* `name` will be "" for non-players or unknown players.
* This function should be overridden by protection mods and should be used to
check if a player can interact at a position.
* This function should call the old version of itself if the position is not
@ -2976,25 +3102,29 @@ These functions return the leftover itemstack.
* `minetest.record_protection_violation(pos, name)`
* This function calls functions registered with
`minetest.register_on_protection_violation`.
* `minetest.rotate_and_place(itemstack, placer, pointed_thing, infinitestacks, orient_flags)`
* `minetest.rotate_and_place(itemstack, placer, pointed_thing[, infinitestacks,
orient_flags, prevent_after_place])`
* Attempt to predict the desired orientation of the facedir-capable node
defined by `itemstack`, and place it accordingly (on-wall, on the floor, or
hanging from the ceiling). Stacks are handled normally if the `infinitestacks`
field is false or omitted (else, the itemstack is not changed). `orient_flags`
is an optional table containing extra tweaks to the placement code:
* `invert_wall`: if `true`, place wall-orientation on the ground and ground-
orientation on the wall.
defined by `itemstack`, and place it accordingly (on-wall, on the floor,
or hanging from the ceiling).
* `infinitestacks`: if `true`, the itemstack is not changed. Otherwise the
stacks are handled normally.
* `orient_flags`: Optional table containing extra tweaks to the placement code:
* `invert_wall`: if `true`, place wall-orientation on the ground and
ground-orientation on the wall.
* `force_wall` : if `true`, always place the node in wall orientation.
* `force_ceiling`: if `true`, always place on the ceiling.
* `force_floor`: if `true`, always place the node on the floor.
* `force_facedir`: if `true`, forcefully reset the facedir to north when placing on
the floor or ceiling
* The first four options are mutually-exclusive; the last in the list takes
precedence over the first.
* `force_facedir`: if `true`, forcefully reset the facedir to north
when placing on the floor or ceiling.
* The first four options are mutually-exclusive; the last in the list
takes precedence over the first.
* `prevent_after_place` is directly passed to `minetest.item_place_node`
* Returns the new itemstack after placement
* `minetest.rotate_node(itemstack, placer, pointed_thing)`
* calls `rotate_and_place()` with infinitestacks set according to the state of
the creative mode setting, and checks for "sneak" to set the `invert_wall`
parameter.
* calls `rotate_and_place()` with `infinitestacks` set according to the state
of the creative mode setting, checks for "sneak" to set the `invert_wall`
parameter and `prevent_after_place` set to `true`.
* `minetest.forceload_block(pos[, transient])`
* forceloads the position `pos`.
@ -3150,7 +3280,7 @@ This is basically a reference to a C++ `ServerActiveObject`
* `set_attach(parent, bone, position, rotation)`
* `bone`: string
* `position`: `{x=num, y=num, z=num}` (relative)
* `rotation`: `{x=num, y=num, z=num}`
* `rotation`: `{x=num, y=num, z=num}` = Rotation on each axis, in degrees
* `get_attach()`: returns parent, bone, position, rotation or nil if it isn't attached
* `set_detach()`
* `set_bone_position(bone, position, rotation)`
@ -3218,7 +3348,7 @@ This is basically a reference to a C++ `ServerActiveObject`
* `11`: bubbles bar is not shown
* `set_attribute(attribute, value)`:
* Sets an extra attribute with value on player.
* `value` must be a string.
* `value` must be a string, or a number which will be converted to a string.
* If `value` is `nil`, remove attribute from player.
* `get_attribute(attribute)`:
* Returns value (a string) for extra attribute.
@ -3322,8 +3452,9 @@ An `InvRef` is a reference to an inventory.
* `add_item(listname, stack)`: add item somewhere in list, returns leftover `ItemStack`
* `room_for_item(listname, stack):` returns `true` if the stack of items
can be fully added to the list
* `contains_item(listname, stack)`: returns `true` if the stack of items
can be fully taken from the list
* `contains_item(listname, stack, [match_meta])`: returns `true` if
the stack of items can be fully taken from the list.
If `match_meta` is false, only the items' names are compared (default: `false`).
* `remove_item(listname, stack)`: take as many items as specified from the list,
returns the items that were actually removed (as an `ItemStack`) -- note that
any item metadata is ignored, so attempting to remove a specific unique
@ -4212,6 +4343,7 @@ Definition tables
{
items = {"foo:bar", "baz:frob"}, -- Items to drop.
rarity = 1, -- Probability of dropping is 1 / rarity.
inherit_color = true, -- To inherit palette color from the node
},
},
},
@ -4242,6 +4374,7 @@ Definition tables
^ Called after constructing node when node was placed using
minetest.item_place_node / minetest.place_node
^ If return true no item is taken from itemstack
^ `placer` may be any valid ObjectRef or nil
^ default: nil ]]
after_dig_node = func(pos, oldnode, oldmetadata, digger), --[[
^ oldmetadata is in table format
@ -4257,9 +4390,11 @@ Definition tables
^ By default: Calls minetest.register_on_punchnode callbacks ]]
on_rightclick = func(pos, node, clicker, itemstack, pointed_thing), --[[
^ default: nil
^ if defined, itemstack will hold clicker's wielded item
^ itemstack will hold clicker's wielded item
^ Shall return the leftover itemstack
^ Note: pointed_thing can be nil, if a mod calls this function ]]
^ Note: pointed_thing can be nil, if a mod calls this function
This function does not get triggered by clients <=0.4.16 if the
"formspec" node metadata field is set ]]
on_dig = func(pos, node, digger), --[[
^ default: minetest.node_dig
@ -4673,4 +4808,26 @@ The Biome API is still in an experimental phase and subject to change.
code = 200,
-- ^ HTTP status code
data = "response"
}
}
### Authentication handler definition
{
get_auth = func(name),
-- ^ Get authentication data for existing player `name` (`nil` if player doesn't exist)
-- ^ returns following structure `{password=<string>, privileges=<table>, last_login=<number or nil>}`
create_auth = func(name, password),
-- ^ Create new auth data for player `name`
-- ^ Note that `password` is not plain-text but an arbitrary representation decided by the engine
set_password = func(name, password),
-- ^ Set password of player `name` to `password`
Auth data should be created if not present
set_privileges = func(name, privileges),
-- ^ Set privileges of player `name`
-- ^ `privileges` is in table form, auth data should be created if not present
reload = func(),
-- ^ Reload authentication data from the storage location
-- ^ Returns boolean indicating success
record_login = func(name),
-- ^ Called when player joins, used for keeping track of last_login
}

View File

@ -1,4 +1,4 @@
Minetest Lua Mainmenu API Reference 0.4.16
Minetest Lua Mainmenu API Reference 0.4.17
========================================
Introduction

View File

@ -161,11 +161,6 @@
# type: key
# keymap_cmd_local = .
# Key for opening the chat console.
# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3
# type: key
# keyman_console = KEY_F10
# Key for toggling unlimited view range.
# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3
# type: key
@ -344,8 +339,8 @@
# serverlist_file = favoriteservers.txt
# Maximum size of the out chat queue. 0 to disable queueing and -1 to make the queue size unlimited
# type: int min: -1
max_out_chat_queue_size = 20
# type: int
# max_out_chat_queue_size = 20
## Graphics
@ -555,7 +550,7 @@ max_out_chat_queue_size = 20
# type: int
# screenH = 600
# Save the window size automatically when modified.
# Save window size automatically when modified.
# type: bool
# autosave_screensize = true
@ -866,11 +861,10 @@ max_out_chat_queue_size = 20
# type: string
# serverlist_url = servers.minetest.net
# Disable escape sequences, e.g. chat coloring.
# Use this if you want to run a server with pre-0.4.14 clients and you want to disable
# the escape sequences generated by mods.
# Remove color codes from incoming chat messages
# Use this to stop players from being able to use color in their messages
# type: bool
# disable_escape_sequences = false
# strip_color_codes = false
## Network
@ -1845,3 +1839,4 @@ max_out_chat_queue_size = 20
# Print the engine's profiling data in regular intervals (in seconds). 0 = disable. Useful for developers.
# type: int
# profiler_print_interval = 0

View File

@ -268,7 +268,7 @@ if(WIN32)
else() # Probably MinGW = GCC
set(PLATFORM_LIBS "")
endif()
set(PLATFORM_LIBS ws2_32.lib shlwapi.lib ${PLATFORM_LIBS})
set(PLATFORM_LIBS ws2_32.lib version.lib shlwapi.lib ${PLATFORM_LIBS})
# Zlib stuff
set(ZLIB_INCLUDE_DIR "${PROJECT_SOURCE_DIR}/../../zlib/zlib-1.2.5"
@ -325,10 +325,12 @@ else()
endif(HAVE_LIBRT)
endif(APPLE)
if(NOT APPLE)
# This way Xxf86vm is found on OpenBSD too
find_library(XXF86VM_LIBRARY Xxf86vm)
mark_as_advanced(XXF86VM_LIBRARY)
set(CLIENT_PLATFORM_LIBS ${CLIENT_PLATFORM_LIBS} ${XXF86VM_LIBRARY})
find_library(XXF86VM_LIBRARY Xxf86vm)
mark_as_advanced(XXF86VM_LIBRARY)
set(CLIENT_PLATFORM_LIBS ${CLIENT_PLATFORM_LIBS} ${XXF86VM_LIBRARY})
endif(NOT APPLE)
# Prefer local iconv if installed
find_library(ICONV_LIBRARY iconv)

View File

@ -388,8 +388,9 @@ void Camera::update(LocalPlayer* player, f32 frametime, f32 busytime,
// *100.0 helps in large map coordinates
m_cameranode->setTarget(my_cp-intToFloat(m_camera_offset, BS) + 100 * m_camera_direction);
// update the camera position in front-view mode to render blocks behind player
if (m_camera_mode == CAMERA_MODE_THIRD_FRONT)
// update the camera position in third-person mode to render blocks behind player
// and correctly apply liquid post FX.
if (m_camera_mode != CAMERA_MODE_FIRST)
m_camera_position = my_cp;
// Get FOV
@ -489,7 +490,9 @@ void Camera::update(LocalPlayer* player, f32 frametime, f32 busytime,
void Camera::updateViewingRange()
{
f32 viewing_range = g_settings->getFloat("viewing_range");
f32 near_plane = g_settings->getFloat("near_plane");
m_draw_control.wanted_range = viewing_range;
m_cameranode->setNearValue(rangelim(near_plane, 0.0f, 0.5f) * BS);
if (m_draw_control.range_all) {
m_cameranode->setFarValue(100000.0);
return;

View File

@ -332,7 +332,7 @@ void CavesRandomWalk::makeCave(MMVManip *vm, v3s16 nmin, v3s16 nmax,
route_y_min = 0;
// Allow half a diameter + 7 over stone surface
route_y_max = -of.Y + max_stone_y + max_tunnel_diameter / 2 + 7;
route_y_max = -of.Y + max_stone_height + max_tunnel_diameter / 2 + 7;
// Limit maximum to area
route_y_max = rangelim(route_y_max, 0, ar.Y - 1);

View File

@ -134,7 +134,6 @@ public:
bool large_cave_is_flat;
bool flooded;
s16 max_stone_y;
v3s16 node_min;
v3s16 node_max;

View File

@ -31,7 +31,7 @@
#ifndef __IRR_USTRING_H_INCLUDED__
#define __IRR_USTRING_H_INCLUDED__
#if (__cplusplus > 199711L) || (_MSC_VER >= 1600) || defined(__GXX_EXPERIMENTAL_CXX0X__)
#if (__cplusplus > 199711L) || (defined(_MSC_VER) && _MSC_VER >= 1600) || defined(__GXX_EXPERIMENTAL_CXX0X__)
# define USTRING_CPP0X
# if defined(__GXX_EXPERIMENTAL_CXX0X__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 5)))
# define USTRING_CPP0X_NEWLITERALS

View File

@ -73,7 +73,6 @@ Client::Client(
m_connection_reinit_timer(0.1),
m_avg_rtt_timer(0.0),
m_playerpos_send_timer(0.0),
m_ignore_damage_timer(0.0),
m_tsrc(tsrc),
m_shsrc(shsrc),
m_itemdef(itemdef),
@ -239,6 +238,8 @@ Client::~Client()
m_shutdown = true;
m_con.Disconnect();
deleteAuthData();
m_mesh_update_thread.stop();
m_mesh_update_thread.wait();
while (!m_mesh_update_thread.m_queue_out.empty()) {
@ -266,6 +267,7 @@ Client::~Client()
}
delete m_minimap;
delete m_media_downloader;
}
void Client::connect(Address address, bool is_local_server)
@ -283,14 +285,9 @@ void Client::step(float dtime)
DSTACK(FUNCTION_NAME);
// Limit a bit
if(dtime > 2.0)
if (dtime > 2.0)
dtime = 2.0;
if(m_ignore_damage_timer > dtime)
m_ignore_damage_timer -= dtime;
else
m_ignore_damage_timer = 0.0;
m_animation_time += dtime;
if(m_animation_time > 60.0)
m_animation_time -= 60.0;
@ -437,18 +434,16 @@ void Client::step(float dtime)
ClientEnvEvent envEvent = m_env.getClientEnvEvent();
if (envEvent.type == CEE_PLAYER_DAMAGE) {
if (m_ignore_damage_timer <= 0) {
u8 damage = envEvent.player_damage.amount;
u8 damage = envEvent.player_damage.amount;
if (envEvent.player_damage.send_to_server)
sendDamage(damage);
if (envEvent.player_damage.send_to_server)
sendDamage(damage);
// Add to ClientEvent queue
ClientEvent event;
event.type = CE_PLAYER_DAMAGE;
event.player_damage.amount = damage;
m_client_event_queue.push(event);
}
// Add to ClientEvent queue
ClientEvent event;
event.type = CE_PLAYER_DAMAGE;
event.player_damage.amount = damage;
m_client_event_queue.push(event);
}
// Protocol v29 or greater obsoleted this event
else if (envEvent.type == CEE_PLAYER_BREATH && m_proto_ver < 29) {

View File

@ -574,7 +574,6 @@ private:
float m_connection_reinit_timer;
float m_avg_rtt_timer;
float m_playerpos_send_timer;
float m_ignore_damage_timer; // Used after server moves player
IntervalLimiter m_map_timer_and_unload_interval;
IWritableTextureSource *m_tsrc;

View File

@ -60,6 +60,8 @@ struct JoystickButtonCmb : public JoystickCombination {
this->key = key;
}
virtual ~JoystickButtonCmb() {}
virtual bool isTriggered(const irr::SEvent::SJoystickEvent &ev) const;
u32 filter_mask;
@ -77,6 +79,8 @@ struct JoystickAxisCmb : public JoystickCombination {
this->key = key;
}
virtual ~JoystickAxisCmb() {}
virtual bool isTriggered(const irr::SEvent::SJoystickEvent &ev) const;
u16 axis_to_compare;

View File

@ -1139,13 +1139,14 @@ video::IImage * Align2Npot2(video::IImage * image,
core::dimension2d<u32> dim = image->getDimension();
std::string extensions = (char*) glGetString(GL_EXTENSIONS);
// Only GLES2 is trusted to correctly report npot support
if (get_GL_major_version() > 1 &&
extensions.find("GL_OES_texture_npot") != std::string::npos) {
// Note: we cache the boolean result. GL context will never change on Android.
static const bool hasNPotSupport = get_GL_major_version() > 1 &&
glGetString(GL_EXTENSIONS) &&
strstr(glGetString(GL_EXTENSIONS), "GL_OES_texture_npot");
if (hasNPotSupport)
return image;
}
unsigned int height = npot2(dim.Height);
unsigned int width = npot2(dim.Width);
@ -1811,7 +1812,8 @@ bool TextureSource::generateImagePart(std::string part_of_name,
* mix high- and low-res textures, or for mods with least-common-denominator
* textures that don't have the resources to offer high-res alternatives.
*/
s32 scaleto = g_settings->getS32("texture_min_size");
const bool filter = m_setting_trilinear_filter || m_setting_bilinear_filter;
const s32 scaleto = filter ? g_settings->getS32("texture_min_size") : 1;
if (scaleto > 1) {
const core::dimension2d<u32> dim = baseimg->getDimension();

View File

@ -159,7 +159,8 @@ enum MaterialType{
TILE_MATERIAL_LIQUID_TRANSPARENT,
TILE_MATERIAL_LIQUID_OPAQUE,
TILE_MATERIAL_WAVING_LEAVES,
TILE_MATERIAL_WAVING_PLANTS
TILE_MATERIAL_WAVING_PLANTS,
TILE_MATERIAL_OPAQUE
};
// Material flags
@ -243,18 +244,20 @@ struct TileLayer
void applyMaterialOptions(video::SMaterial &material) const
{
switch (material_type) {
case TILE_MATERIAL_OPAQUE:
case TILE_MATERIAL_LIQUID_OPAQUE:
material.MaterialType = video::EMT_SOLID;
break;
case TILE_MATERIAL_BASIC:
case TILE_MATERIAL_WAVING_LEAVES:
case TILE_MATERIAL_WAVING_PLANTS:
material.MaterialTypeParam = 0.5;
material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
break;
case TILE_MATERIAL_ALPHA:
case TILE_MATERIAL_LIQUID_TRANSPARENT:
material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
break;
case TILE_MATERIAL_LIQUID_OPAQUE:
material.MaterialType = video::EMT_SOLID;
break;
}
material.BackfaceCulling = (material_flags & MATERIAL_FLAG_BACKFACE_CULLING)
? true : false;

View File

@ -633,6 +633,16 @@ std::vector<u16> ClientInterface::getClientIDs(ClientState min_state)
return reply;
}
/**
* Verify if user limit was reached.
* User limit count all clients from HelloSent state (MT protocol user) to Active state
* @return true if user limit was reached
*/
bool ClientInterface::isUserLimitReached()
{
return getClientIDs(CS_HelloSent).size() >= g_settings->getU16("max_users");
}
void ClientInterface::step(float dtime)
{
m_print_info_timer += dtime;

View File

@ -449,6 +449,9 @@ public:
/* get list of active client id's */
std::vector<u16> getClientIDs(ClientState min_state=CS_Active);
/* verify is server user limit was reached */
bool isUserLimitReached();
/* get list of client player names */
const std::vector<std::string> &getPlayerNames() const { return m_clients_names; }
@ -493,7 +496,6 @@ public:
}
static std::string state2Name(ClientState state);
protected:
//TODO find way to avoid this functions
void lock() { m_clients_mutex.lock(); }

View File

@ -294,49 +294,46 @@ void ClientMap::updateDrawList(video::IVideoDriver* driver)
struct MeshBufList
{
/*!
* Specifies in which layer the list is.
* All lists which are in a lower layer are rendered before this list.
*/
u8 layer;
video::SMaterial m;
std::vector<scene::IMeshBuffer*> bufs;
};
struct MeshBufListList
{
std::vector<MeshBufList> lists;
/*!
* Stores the mesh buffers of the world.
* The array index is the material's layer.
* The vector part groups vertices by material.
*/
std::vector<MeshBufList> lists[MAX_TILE_LAYERS];
void clear()
{
lists.clear();
for (int l = 0; l < MAX_TILE_LAYERS; l++)
lists[l].clear();
}
void add(scene::IMeshBuffer *buf, u8 layer)
{
// Append to the correct layer
std::vector<MeshBufList> &list = lists[layer];
const video::SMaterial &m = buf->getMaterial();
for(std::vector<MeshBufList>::iterator i = lists.begin();
i != lists.end(); ++i){
MeshBufList &l = *i;
for (std::vector<MeshBufList>::iterator it = list.begin(); it != list.end();
++it) {
// comparing a full material is quite expensive so we don't do it if
// not even first texture is equal
if (l.m.TextureLayer[0].Texture != m.TextureLayer[0].Texture)
if ((*it).m.TextureLayer[0].Texture != m.TextureLayer[0].Texture)
continue;
if(l.layer != layer)
continue;
if (l.m == m) {
l.bufs.push_back(buf);
if ((*it).m == m) {
(*it).bufs.push_back(buf);
return;
}
}
MeshBufList l;
l.layer = layer;
l.m = m;
l.bufs.push_back(buf);
lists.push_back(l);
list.push_back(l);
}
};
@ -364,7 +361,7 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
Measuring time is very useful for long delays when the
machine is swapping a lot.
*/
int time1 = time(0);
time_t time1 = time(0);
/*
Get animation parameters
@ -480,35 +477,34 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
}
}
std::vector<MeshBufList> &lists = drawbufs.lists;
// Render all layers in order
for (int layer = 0; layer < MAX_TILE_LAYERS; layer++) {
std::vector<MeshBufList> &lists = drawbufs.lists[layer];
int timecheck_counter = 0;
for (std::vector<MeshBufList>::iterator i = lists.begin();
i != lists.end(); ++i) {
timecheck_counter++;
if (timecheck_counter > 50) {
timecheck_counter = 0;
int time2 = time(0);
if (time2 > time1 + 4) {
infostream << "ClientMap::renderMap(): "
"Rendering takes ages, returning."
<< std::endl;
return;
int timecheck_counter = 0;
for (std::vector<MeshBufList>::iterator it = lists.begin(); it != lists.end();
++it) {
timecheck_counter++;
if (timecheck_counter > 50) {
timecheck_counter = 0;
time_t time2 = time(0);
if (time2 > time1 + 4) {
infostream << "ClientMap::renderMap(): "
"Rendering takes ages, returning."
<< std::endl;
return;
}
}
driver->setMaterial((*it).m);
for (std::vector<scene::IMeshBuffer*>::iterator it2 = (*it).bufs.begin();
it2 != (*it).bufs.end(); ++it2) {
driver->drawMeshBuffer(*it2);
vertex_count += (*it2)->getVertexCount();
meshbuffer_count++;
}
}
MeshBufList &list = *i;
driver->setMaterial(list.m);
for (std::vector<scene::IMeshBuffer*>::iterator j = list.bufs.begin();
j != list.bufs.end(); ++j) {
scene::IMeshBuffer *buf = *j;
driver->drawMeshBuffer(buf);
vertex_count += buf->getVertexCount();
meshbuffer_count++;
}
}
} // ScopeProfiler

View File

@ -8,6 +8,7 @@
#define VERSION_MAJOR @VERSION_MAJOR@
#define VERSION_MINOR @VERSION_MINOR@
#define VERSION_PATCH @VERSION_PATCH@
#define VERSION_TWEAK @VERSION_TWEAK@
#define VERSION_EXTRA "@VERSION_EXTRA@"
#define VERSION_STRING "@VERSION_STRING@"
#define PRODUCT_VERSION_STRING "@VERSION_MAJOR@.@VERSION_MINOR@"

View File

@ -258,27 +258,32 @@ collisionMoveResult collisionMoveSimple(Environment *env, IGameDef *gamedef,
//TimeTaker tt2("collisionMoveSimple collect boxes");
ScopeProfiler sp(g_profiler, "collisionMoveSimple collect boxes avg", SPT_AVG);
v3s16 oldpos_i = floatToInt(*pos_f, BS);
v3s16 newpos_i = floatToInt(*pos_f + *speed_f * dtime, BS);
s16 min_x = MYMIN(oldpos_i.X, newpos_i.X) + (box_0.MinEdge.X / BS) - 1;
s16 min_y = MYMIN(oldpos_i.Y, newpos_i.Y) + (box_0.MinEdge.Y / BS) - 1;
s16 min_z = MYMIN(oldpos_i.Z, newpos_i.Z) + (box_0.MinEdge.Z / BS) - 1;
s16 max_x = MYMAX(oldpos_i.X, newpos_i.X) + (box_0.MaxEdge.X / BS) + 1;
s16 max_y = MYMAX(oldpos_i.Y, newpos_i.Y) + (box_0.MaxEdge.Y / BS) + 1;
s16 max_z = MYMAX(oldpos_i.Z, newpos_i.Z) + (box_0.MaxEdge.Z / BS) + 1;
v3f newpos_f = *pos_f + *speed_f * dtime;
v3f minpos_f(
MYMIN(pos_f->X, newpos_f.X),
MYMIN(pos_f->Y, newpos_f.Y) + 0.01 * BS, // bias rounding, player often at +/-n.5
MYMIN(pos_f->Z, newpos_f.Z)
);
v3f maxpos_f(
MYMAX(pos_f->X, newpos_f.X),
MYMAX(pos_f->Y, newpos_f.Y),
MYMAX(pos_f->Z, newpos_f.Z)
);
v3s16 min = floatToInt(minpos_f + box_0.MinEdge, BS) - v3s16(1, 1, 1);
v3s16 max = floatToInt(maxpos_f + box_0.MaxEdge, BS) + v3s16(1, 1, 1);
bool any_position_valid = false;
for(s16 x = min_x; x <= max_x; x++)
for(s16 y = min_y; y <= max_y; y++)
for(s16 z = min_z; z <= max_z; z++)
for(s16 x = min.X; x <= max.X; x++)
for(s16 y = min.Y; y <= max.Y; y++)
for(s16 z = min.Z; z <= max.Z; z++)
{
v3s16 p(x,y,z);
bool is_position_valid;
MapNode n = map->getNodeNoEx(p, &is_position_valid);
if (is_position_valid) {
if (is_position_valid && n.getContent() != CONTENT_IGNORE) {
// Object collides into walkable nodes
any_position_valid = true;
@ -328,7 +333,8 @@ collisionMoveResult collisionMoveSimple(Environment *env, IGameDef *gamedef,
false, n_bouncy_value, p, box));
}
} else {
// Collide with unloaded nodes
// Collide with unloaded nodes (position invalid) and loaded
// CONTENT_IGNORE nodes (position valid)
aabb3f box = getNodeBox(p, BS);
cinfo.push_back(NearbyCollisionInfo(true, false, 0, p, box));
}
@ -336,6 +342,8 @@ collisionMoveResult collisionMoveSimple(Environment *env, IGameDef *gamedef,
// Do not move if world has not loaded yet, since custom node boxes
// are not available for collision detection.
// This also intentionally occurs in the case of the object being positioned
// solely on loaded CONTENT_IGNORE nodes, no matter where they come from.
if (!any_position_valid) {
*speed_f = v3f(0, 0, 0);
return result;

View File

@ -1183,16 +1183,17 @@ void GenericCAO::step(float dtime, ClientEnvironment *env)
float moved = lastpos.getDistanceFrom(pos_translator.vect_show);
m_step_distance_counter += moved;
if(m_step_distance_counter > 1.5*BS)
{
m_step_distance_counter = 0;
if(!m_is_local_player && m_prop.makes_footstep_sound)
{
if (m_step_distance_counter > 1.5f * BS) {
m_step_distance_counter = 0.0f;
if (!m_is_local_player && m_prop.makes_footstep_sound) {
INodeDefManager *ndef = m_client->ndef();
v3s16 p = floatToInt(getPosition() + v3f(0,
(m_prop.collisionbox.MinEdge.Y-0.5)*BS, 0), BS);
v3s16 p = floatToInt(getPosition() +
v3f(0.0f, (m_prop.collisionbox.MinEdge.Y - 0.5f) * BS, 0.0f), BS);
MapNode n = m_env->getMap().getNodeNoEx(p);
SimpleSoundSpec spec = ndef->get(n).sound_footstep;
// Reduce footstep gain, as non-local-player footsteps are
// somehow louder.
spec.gain *= 0.6f;
m_client->sound()->playSoundAt(spec, false, getPosition());
}
}
@ -1353,6 +1354,13 @@ void GenericCAO::updateTextures(std::string mod)
material.setFlag(video::EMF_LIGHTING, false);
material.setFlag(video::EMF_BILINEAR_FILTER, false);
// don't filter low-res textures, makes them look blurry
// player models have a res of 64
const core::dimension2d<u32> &size = texture->getOriginalSize();
const u32 res = std::min(size.Height, size.Width);
use_trilinear_filter &= res > 64;
use_bilinear_filter &= res > 64;
m_animated_meshnode->getMaterial(i)
.setFlag(video::EMF_TRILINEAR_FILTER, use_trilinear_filter);
m_animated_meshnode->getMaterial(i)

View File

@ -43,7 +43,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
// Corresponding offsets are listed in g_27dirs
#define FRAMED_NEIGHBOR_COUNT 18
static constexpr v3s16 light_dirs[8] = {
static const v3s16 light_dirs[8] = {
v3s16(-1, -1, -1),
v3s16(-1, -1, 1),
v3s16(-1, 1, -1),

View File

@ -59,7 +59,7 @@ public:
m_age += dtime;
if(m_age > 10)
{
m_removed = true;
m_pending_removal = true;
return;
}
@ -406,20 +406,6 @@ void LuaEntitySAO::step(float dtime, bool send_recommended)
m_env->getScriptIface()->luaentity_Step(m_id, dtime);
}
// Remove LuaEntity beyond terrain edges
{
ServerMap *map = dynamic_cast<ServerMap *>(&m_env->getMap());
assert(map);
if (!m_pending_deactivation &&
map->saoPositionOverLimit(m_base_position)) {
infostream << "Remove SAO " << m_id << "(" << m_init_name
<< "), outside of limits" << std::endl;
m_pending_deactivation = true;
m_removed = true;
return;
}
}
if(send_recommended == false)
return;
@ -555,9 +541,9 @@ int LuaEntitySAO::punch(v3f dir,
ServerActiveObject *puncher,
float time_from_last_punch)
{
if (!m_registered){
if (!m_registered) {
// Delete unknown LuaEntities when punched
m_removed = true;
m_pending_removal = true;
return 0;
}
@ -601,7 +587,7 @@ int LuaEntitySAO::punch(v3f dir,
}
if (getHP() == 0)
m_removed = true;
m_pending_removal = true;
@ -1361,11 +1347,10 @@ void PlayerSAO::setWieldIndex(int i)
}
}
// Erase the peer id and make the object for removal
void PlayerSAO::disconnected()
{
m_peer_id = 0;
m_removed = true;
m_pending_removal = true;
}
void PlayerSAO::unlinkPlayerSessionAndSave()
@ -1402,26 +1387,38 @@ bool PlayerSAO::checkMovementCheat()
too, and much more lightweight.
*/
float player_max_speed = 0;
float player_max_walk = 0; // horizontal movement
float player_max_jump = 0; // vertical upwards movement
if (m_privs.count("fast") != 0) {
// Fast speed
player_max_speed = m_player->movement_speed_fast * m_physics_override_speed;
} else {
// Normal speed
player_max_speed = m_player->movement_speed_walk * m_physics_override_speed;
}
// Tolerance. The lag pool does this a bit.
//player_max_speed *= 2.5;
if (m_privs.count("fast") != 0)
player_max_walk = m_player->movement_speed_fast; // Fast speed
else
player_max_walk = m_player->movement_speed_walk; // Normal speed
player_max_walk *= m_physics_override_speed;
player_max_jump = m_player->movement_speed_jump * m_physics_override_jump;
// FIXME: Bouncy nodes cause practically unbound increase in Y speed,
// until this can be verified correctly, tolerate higher jumping speeds
player_max_jump *= 2.0;
// Don't divide by zero!
if (player_max_walk < 0.0001f)
player_max_walk = 0.0001f;
if (player_max_jump < 0.0001f)
player_max_jump = 0.0001f;
v3f diff = (m_base_position - m_last_good_position);
float d_vert = diff.Y;
diff.Y = 0;
float d_horiz = diff.getLength();
float required_time = d_horiz / player_max_speed;
float required_time = d_horiz / player_max_walk;
if (d_vert > 0 && d_vert / player_max_speed > required_time)
required_time = d_vert / player_max_speed; // Moving upwards
// FIXME: Checking downwards movement is not easily possible currently,
// the server could calculate speed differences to examine the gravity
if (d_vert > 0) {
// In certain cases (water, ladders) walking speed is applied vertically
float s = MYMAX(player_max_jump, player_max_walk);
required_time = MYMAX(required_time, d_vert / s);
}
if (m_move_pool.grab(required_time)) {
m_last_good_position = m_base_position;

View File

@ -923,8 +923,19 @@ public:
<< " against " << def->dump() << std::endl;*/
if (def->check(input, gamedef)) {
// Check if the crafted node/item exists
CraftOutput out = def->getOutput(input, gamedef);
ItemStack is;
is.deSerialize(out.item, gamedef->idef());
if (!is.isKnown(gamedef->idef())) {
infostream << "trying to craft non-existent "
<< out.item << ", ignoring recipe" << std::endl;
continue;
}
// Get output, then decrement input (if requested)
output = def->getOutput(input, gamedef);
output = out;
if (decrementInput)
def->decrementInput(input, output_replacement, gamedef);
/*errorstream << "Check RETURNS TRUE" << std::endl;*/

View File

@ -129,8 +129,9 @@ void set_default_settings(Settings *settings)
settings->setDefault("fps_max", "60");
settings->setDefault("pause_fps_max", "10");
settings->setDefault("viewing_range", "100");
settings->setDefault("screen_w", "800");
settings->setDefault("screen_h", "600");
settings->setDefault("near_plane", "0.1");
settings->setDefault("screenW", "800");
settings->setDefault("screenH", "600");
settings->setDefault("autosave_screensize", "true");
settings->setDefault("fullscreen", "false");
settings->setDefault("fullscreen_bpp", "24");
@ -259,6 +260,7 @@ void set_default_settings(Settings *settings)
// Server
settings->setDefault("disable_escape_sequences", "false");
settings->setDefault("strip_color_codes", "false");
// Network
settings->setDefault("enable_ipv6", "true");
@ -371,8 +373,8 @@ void set_default_settings(Settings *settings)
// Mobile Platform
#if defined(__ANDROID__) || defined(__IOS__)
settings->setDefault("screen_w", "0");
settings->setDefault("screen_h", "0");
settings->setDefault("screenW", "0");
settings->setDefault("screenH", "0");
settings->setDefault("fps_max", "35");
settings->setDefault("enable_shaders", "false");
settings->setDefault("fullscreen", "true");

View File

@ -431,8 +431,10 @@ void DungeonGen::makeCorridor(v3s16 doorplace, v3s16 doordir,
VMANIP_FLAG_DUNGEON_UNTOUCHABLE,
MapNode(dp.c_wall),
0);
makeHole(p);
makeHole(p - dir);
makeFill(p, dp.holesize, VMANIP_FLAG_DUNGEON_UNTOUCHABLE,
MapNode(CONTENT_AIR), VMANIP_FLAG_DUNGEON_INSIDE);
makeFill(p - dir, dp.holesize, VMANIP_FLAG_DUNGEON_UNTOUCHABLE,
MapNode(CONTENT_AIR), VMANIP_FLAG_DUNGEON_INSIDE);
// TODO: fix stairs code so it works 100%
// (quite difficult)
@ -451,16 +453,21 @@ void DungeonGen::makeCorridor(v3s16 doorplace, v3s16 doordir,
v3s16 swv = (dir.Z != 0) ? v3s16(1, 0, 0) : v3s16(0, 0, 1);
for (u16 st = 0; st < stair_width; st++) {
u32 vi = vm->m_area.index(ps.X - dir.X, ps.Y - 1, ps.Z - dir.Z);
if (vm->m_area.contains(ps + v3s16(-dir.X, -1, -dir.Z)) &&
vm->m_data[vi].getContent() == dp.c_wall)
vm->m_data[vi] = MapNode(dp.c_stair, 0, facedir);
vi = vm->m_area.index(ps.X, ps.Y, ps.Z);
if (vm->m_area.contains(ps) &&
vm->m_data[vi].getContent() == dp.c_wall)
vm->m_data[vi] = MapNode(dp.c_stair, 0, facedir);
if (make_stairs == -1) {
u32 vi = vm->m_area.index(ps.X - dir.X, ps.Y - 1, ps.Z - dir.Z);
if (vm->m_area.contains(ps + v3s16(-dir.X, -1, -dir.Z)) &&
vm->m_data[vi].getContent() == dp.c_wall) {
vm->m_flags[vi] |= VMANIP_FLAG_DUNGEON_UNTOUCHABLE;
vm->m_data[vi] = MapNode(dp.c_stair, 0, facedir);
}
} else if (make_stairs == 1) {
u32 vi = vm->m_area.index(ps.X, ps.Y - 1, ps.Z);
if (vm->m_area.contains(ps + v3s16(0, -1, 0)) &&
vm->m_data[vi].getContent() == dp.c_wall) {
vm->m_flags[vi] |= VMANIP_FLAG_DUNGEON_UNTOUCHABLE;
vm->m_data[vi] = MapNode(dp.c_stair, 0, facedir);
}
}
ps += swv;
}
}

View File

@ -146,7 +146,10 @@ EmergeManager::~EmergeManager()
}
delete thread;
delete m_mapgens[i];
// Mapgen init might not be finished if there is an error during startup.
if (m_mapgens.size() > i)
delete m_mapgens[i];
}
delete biomemgr;
@ -570,6 +573,12 @@ MapBlock *EmergeThread::finishGen(v3s16 pos, BlockMakeData *bmdata,
m_server->setAsyncFatalError("Lua: finishGen" + std::string(e.what()));
}
/*
Clear generate notifier events
*/
Mapgen *mg = m_emerge->getCurrentMapgen();
mg->gennotify.clearEvents();
EMERGE_DBG_OUT("ended up with: " << analyze_block(block));
/*

View File

@ -27,6 +27,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "log.h"
#include "config.h"
#include "porting.h"
#ifdef __ANDROID__
#include "settings.h" // For g_settings
#endif
namespace fs
{
@ -741,4 +744,3 @@ bool Rename(const std::string &from, const std::string &to)
}
} // namespace fs

View File

@ -341,24 +341,62 @@ void FontEngine::initFont(unsigned int basesize, FontMode mode)
font_path.c_str(), size, true, true, font_shadow,
font_shadow_alpha);
if (font != NULL) {
if (font) {
m_font_cache[mode][basesize] = font;
return;
}
// try fallback font
errorstream << "FontEngine: failed to load: " << font_path << ", trying to fall back "
"to fallback font" << std::endl;
if (font_config_prefix == "mono_") {
const std::string &mono_font_path = m_settings->getDefault("mono_font_path");
font_path = g_settings->get(font_config_prefix + "fallback_font_path");
if (font_path != mono_font_path) {
// try original mono font
errorstream << "FontEngine: failed to load custom mono "
"font: " << font_path << ", trying to fall back to "
"original mono font" << std::endl;
font = gui::CGUITTFont::createTTFont(m_env,
font_path.c_str(), size, true, true, font_shadow,
font_shadow_alpha);
font = gui::CGUITTFont::createTTFont(m_env,
mono_font_path.c_str(), size, true, true,
font_shadow, font_shadow_alpha);
if (font != NULL) {
m_font_cache[mode][basesize] = font;
return;
if (font) {
m_font_cache[mode][basesize] = font;
return;
}
}
} else {
// try fallback font
errorstream << "FontEngine: failed to load: " << font_path <<
", trying to fall back to fallback font" << std::endl;
font_path = g_settings->get(font_config_prefix + "fallback_font_path");
font = gui::CGUITTFont::createTTFont(m_env,
font_path.c_str(), size, true, true, font_shadow,
font_shadow_alpha);
if (font) {
m_font_cache[mode][basesize] = font;
return;
}
const std::string &fallback_font_path = m_settings->getDefault("fallback_font_path");
if (font_path != fallback_font_path) {
// try original fallback font
errorstream << "FontEngine: failed to load custom fallback "
"font: " << font_path << ", trying to fall back to "
"original fallback font" << std::endl;
font = gui::CGUITTFont::createTTFont(m_env,
fallback_font_path.c_str(), size, true, true,
font_shadow, font_shadow_alpha);
if (font) {
m_font_cache[mode][basesize] = font;
return;
}
}
}
// give up
@ -367,9 +405,9 @@ void FontEngine::initFont(unsigned int basesize, FontMode mode)
#endif
errorstream << "FontEngine: failed to load freetype font: "
<< font_path << std::endl;
errorstream << "minetest can not continue without a valid font. Please correct "
"the 'font_path' setting or install the font file in the proper "
"location" << std::endl;
errorstream << "minetest can not continue without a valid font. "
"Please correct the 'font_path' setting or install the font "
"file in the proper location" << std::endl;
abort();
}
#endif
@ -471,7 +509,7 @@ void FontEngine::initSimpleFont(unsigned int basesize, FontMode mode)
}
}
if (font != NULL) {
if (font) {
font->grab();
m_font_cache[mode][basesize] = font;
}

View File

@ -185,7 +185,8 @@ struct LocalFormspecHandler : public TextDest
#endif
// Don't disable this part when modding is disabled, it's used in builtin
m_client->getScript()->on_formspec_input(m_formname, fields);
if (m_client && m_client->getScript())
m_client->getScript()->on_formspec_input(m_formname, fields);
}
Client *m_client;
@ -789,8 +790,8 @@ public:
};
bool nodePlacementPrediction(Client &client,
const ItemDefinition &playeritem_def, v3s16 nodepos, v3s16 neighbourpos)
bool nodePlacementPrediction(Client &client, const ItemDefinition &playeritem_def,
const ItemStack &playeritem, v3s16 nodepos, v3s16 neighbourpos)
{
std::string prediction = playeritem_def.node_placement_prediction;
INodeDefManager *nodedef = client.ndef();
@ -833,11 +834,13 @@ bool nodePlacementPrediction(Client &client,
return false;
}
const ContentFeatures &predicted_f = nodedef->get(id);
// Predict param2 for facedir and wallmounted nodes
u8 param2 = 0;
if (nodedef->get(id).param_type_2 == CPT2_WALLMOUNTED ||
nodedef->get(id).param_type_2 == CPT2_COLORED_WALLMOUNTED) {
if (predicted_f.param_type_2 == CPT2_WALLMOUNTED ||
predicted_f.param_type_2 == CPT2_COLORED_WALLMOUNTED) {
v3s16 dir = nodepos - neighbourpos;
if (abs(dir.Y) > MYMAX(abs(dir.X), abs(dir.Z))) {
@ -849,8 +852,8 @@ bool nodePlacementPrediction(Client &client,
}
}
if (nodedef->get(id).param_type_2 == CPT2_FACEDIR ||
nodedef->get(id).param_type_2 == CPT2_COLORED_FACEDIR) {
if (predicted_f.param_type_2 == CPT2_FACEDIR ||
predicted_f.param_type_2 == CPT2_COLORED_FACEDIR) {
v3s16 dir = nodepos - floatToInt(client.getEnv().getLocalPlayer()->getPosition(), BS);
if (abs(dir.X) > abs(dir.Z)) {
@ -863,7 +866,7 @@ bool nodePlacementPrediction(Client &client,
assert(param2 <= 5);
//Check attachment if node is in group attached_node
if (((ItemGroupList) nodedef->get(id).groups)["attached_node"] != 0) {
if (((ItemGroupList) predicted_f.groups)["attached_node"] != 0) {
static v3s16 wallmounted_dirs[8] = {
v3s16(0, 1, 0),
v3s16(0, -1, 0),
@ -874,8 +877,8 @@ bool nodePlacementPrediction(Client &client,
};
v3s16 pp;
if (nodedef->get(id).param_type_2 == CPT2_WALLMOUNTED ||
nodedef->get(id).param_type_2 == CPT2_COLORED_WALLMOUNTED)
if (predicted_f.param_type_2 == CPT2_WALLMOUNTED ||
predicted_f.param_type_2 == CPT2_COLORED_WALLMOUNTED)
pp = p + wallmounted_dirs[param2];
else
pp = p + v3s16(0, -1, 0);
@ -884,6 +887,28 @@ bool nodePlacementPrediction(Client &client,
return false;
}
// Apply color
if ((predicted_f.param_type_2 == CPT2_COLOR
|| predicted_f.param_type_2 == CPT2_COLORED_FACEDIR
|| predicted_f.param_type_2 == CPT2_COLORED_WALLMOUNTED)) {
const std::string &indexstr = playeritem.metadata.getString(
"palette_index", 0);
if (!indexstr.empty()) {
s32 index = mystoi(indexstr);
if (predicted_f.param_type_2 == CPT2_COLOR) {
param2 = index;
} else if (predicted_f.param_type_2
== CPT2_COLORED_WALLMOUNTED) {
// param2 = pure palette index + other
param2 = (index & 0xf8) | (param2 & 0x07);
} else if (predicted_f.param_type_2
== CPT2_COLORED_FACEDIR) {
// param2 = pure palette index + other
param2 = (index & 0xe0) | (param2 & 0x1f);
}
}
}
// Add node to client map
MapNode n(id, 0, param2);
@ -1298,8 +1323,9 @@ protected:
const core::line3d<f32> &shootline, bool liquids_pointable,
bool look_for_object, const v3s16 &camera_offset);
void handlePointingAtNothing(const ItemStack &playerItem);
void handlePointingAtNode(const PointedThing &pointed, const ItemDefinition &playeritem_def,
const ToolCapabilities &playeritem_toolcap, f32 dtime);
void handlePointingAtNode(const PointedThing &pointed,
const ItemDefinition &playeritem_def, const ItemStack &playeritem,
const ToolCapabilities &playeritem_toolcap, f32 dtime);
void handlePointingAtObject(const PointedThing &pointed, const ItemStack &playeritem,
const v3f &player_position, bool show_debug);
void handleDigging(const PointedThing &pointed, const v3s16 &nodepos,
@ -2983,8 +3009,8 @@ void Game::toggleFullViewRange()
};
#else
static const wchar_t *msg[] = {
L"Disabled full viewing range",
L"Enabled full viewing range"
L"Normal view range",
L"Infinite view range"
};
#endif
@ -3646,7 +3672,8 @@ void Game::processPlayerInteraction(f32 dtime, bool show_hud, bool show_debug)
if (playeritem.name.empty() && hand_def.tool_capabilities != NULL) {
playeritem_toolcap = *hand_def.tool_capabilities;
}
handlePointingAtNode(pointed, playeritem_def, playeritem_toolcap, dtime);
handlePointingAtNode(pointed, playeritem_def, playeritem,
playeritem_toolcap, dtime);
} else if (pointed.type == POINTEDTHING_OBJECT) {
handlePointingAtObject(pointed, playeritem, player_position, show_debug);
} else if (isLeftPressed()) {
@ -3781,8 +3808,9 @@ void Game::handlePointingAtNothing(const ItemStack &playerItem)
}
void Game::handlePointingAtNode(const PointedThing &pointed, const ItemDefinition &playeritem_def,
const ToolCapabilities &playeritem_toolcap, f32 dtime)
void Game::handlePointingAtNode(const PointedThing &pointed,
const ItemDefinition &playeritem_def, const ItemStack &playeritem,
const ToolCapabilities &playeritem_toolcap, f32 dtime)
{
v3s16 nodepos = pointed.node_undersurface;
v3s16 neighbourpos = pointed.node_abovesurface;
@ -3833,6 +3861,11 @@ void Game::handlePointingAtNode(const PointedThing &pointed, const ItemDefinitio
if (meta && meta->getString("formspec") != "" && !random_input
&& !isKeyDown(KeyType::SNEAK)) {
// Report right click to server
if (nodedef_manager->get(map.getNodeNoEx(nodepos)).rightclickable) {
client->interact(3, pointed);
}
infostream << "Launching custom inventory view" << std::endl;
InventoryLocation inventoryloc;
@ -3855,7 +3888,7 @@ void Game::handlePointingAtNode(const PointedThing &pointed, const ItemDefinitio
// If the wielded item has node placement prediction,
// make that happen
bool placed = nodePlacementPrediction(*client,
playeritem_def,
playeritem_def, playeritem,
nodepos, neighbourpos);
if (placed) {

View File

@ -52,12 +52,13 @@ extern wchar_t *utf8_to_wide_c(const char *str);
// The returned string is allocated using new
inline const wchar_t *wgettext(const char *str)
{
return utf8_to_wide_c(gettext(str));
// We must check here that is not an empty string to avoid trying to translate it
return str[0] ? utf8_to_wide_c(gettext(str)) : utf8_to_wide_c("");
}
inline std::string strgettext(const std::string &text)
{
return gettext(text.c_str());
return text.empty() ? "" : gettext(text.c_str());
}
#endif

View File

@ -204,8 +204,8 @@ void GUIChatConsole::draw()
// scale current console height to new window size
if (m_screensize.Y != 0)
m_height = m_height * screensize.Y / m_screensize.Y;
m_desired_height = m_desired_height_fraction * m_screensize.Y;
m_screensize = screensize;
m_desired_height = m_desired_height_fraction * m_screensize.Y;
reformatConsole();
}
@ -231,6 +231,7 @@ void GUIChatConsole::reformatConsole()
s32 rows = m_desired_height / m_fontsize.Y - 1; // make room for the input prompt
if (cols <= 0 || rows <= 0)
cols = rows = 0;
recalculateConsolePosition();
m_chat_backend->reformat(cols, rows);
}

View File

@ -86,24 +86,30 @@ MenuTextureSource::~MenuTextureSource()
/******************************************************************************/
video::ITexture* MenuTextureSource::getTexture(const std::string &name, u32 *id)
{
if(id)
if (id)
*id = 0;
if(name.empty())
if (name.empty())
return NULL;
m_to_delete.insert(name);
#if defined(__ANDROID__) || defined(__IOS__)
if (m_driver->findTexture(name.c_str()) != NULL)
return m_driver->findTexture(name.c_str());
video::IImage *image = m_driver->createImageFromFile(name.c_str());
if (image) {
image = Align2Npot2(image, m_driver);
video::ITexture* retval = m_driver->addTexture(name.c_str(), image);
image->drop();
video::ITexture *retval = m_driver->findTexture(name.c_str());
if (retval)
return retval;
}
#endif
video::IImage *image = m_driver->createImageFromFile(name.c_str());
if (!image)
return NULL;
image = Align2Npot2(image, m_driver);
retval = m_driver->addTexture(name.c_str(), image);
image->drop();
return retval;
#else
return m_driver->getTexture(name.c_str());
#endif
}
/******************************************************************************/

View File

@ -3683,18 +3683,24 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
a->from_i = m_selected_item->i;
m_invmgr->inventoryAction(a);
} else if (craft_amount > 0) {
m_selected_content_guess = ItemStack(); // Clear
// Send IACTION_CRAFT
assert(s.isValid());
assert(inv_s);
// if there are no items selected or the selected item
// belongs to craftresult list, proceed with crafting
if (m_selected_item == NULL ||
!m_selected_item->isValid() || m_selected_item->listname == "craftresult") {
m_selected_content_guess = ItemStack(); // Clear
assert(inv_s);
infostream << "Handing IACTION_CRAFT to manager" << std::endl;
ICraftAction *a = new ICraftAction();
a->count = craft_amount;
a->craft_inv = s.inventoryloc;
m_invmgr->inventoryAction(a);
// Send IACTION_CRAFT
infostream << "Handing IACTION_CRAFT to manager" << std::endl;
ICraftAction *a = new ICraftAction();
a->count = craft_amount;
a->craft_inv = s.inventoryloc;
m_invmgr->inventoryAction(a);
}
}
// If m_selected_amount has been decreased to zero, deselect

View File

@ -248,6 +248,7 @@ HTTPFetchOngoing::HTTPFetchOngoing(const HTTPFetchRequest &request_,
curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1);
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 1);
curl_easy_setopt(curl, CURLOPT_ENCODING, "gzip");
std::string bind_address = g_settings->get("bind_address");
if (!bind_address.empty()) {

View File

@ -409,24 +409,32 @@ void Hud::drawStatbar(v2s32 pos, u16 corner, u16 drawdir, std::string texture,
p.Y -= g_settings->getU16("hud_move_upwards");
v2s32 steppos;
core::rect<s32> srchalfrect, dsthalfrect;
switch (drawdir) {
case HUD_DIR_RIGHT_LEFT:
steppos = v2s32(-1, 0);
srchalfrect = core::rect<s32>(srcd.Width / 2, 0, srcd.Width, srcd.Height);
dsthalfrect = core::rect<s32>(dstd.Width / 2, 0, dstd.Width, dstd.Height);
break;
case HUD_DIR_TOP_BOTTOM:
steppos = v2s32(0, 1);
srchalfrect = core::rect<s32>(0, 0, srcd.Width, srcd.Height / 2);
dsthalfrect = core::rect<s32>(0, 0, dstd.Width, dstd.Height / 2);
break;
case HUD_DIR_BOTTOM_TOP:
steppos = v2s32(0, -1);
srchalfrect = core::rect<s32>(0, srcd.Height / 2, srcd.Width, srcd.Height);
dsthalfrect = core::rect<s32>(0, dstd.Height / 2, dstd.Width, dstd.Height);
break;
default:
steppos = v2s32(1, 0);
srchalfrect = core::rect<s32>(0, 0, srcd.Width / 2, srcd.Height);
dsthalfrect = core::rect<s32>(0, 0, dstd.Width / 2, dstd.Height);
}
steppos.X *= dstd.Width;
steppos.Y *= dstd.Height;
for (s32 i = 0; i < count / 2; i++)
{
for (s32 i = 0; i < count / 2; i++) {
core::rect<s32> srcrect(0, 0, srcd.Width, srcd.Height);
core::rect<s32> dstrect(0,0, dstd.Width, dstd.Height);
@ -435,13 +443,9 @@ void Hud::drawStatbar(v2s32 pos, u16 corner, u16 drawdir, std::string texture,
p += steppos;
}
if (count % 2 == 1)
{
core::rect<s32> srcrect(0, 0, srcd.Width / 2, srcd.Height);
core::rect<s32> dstrect(0,0, dstd.Width / 2, dstd.Height);
dstrect += p;
draw2DImageFilterScaled(driver, stat_texture, dstrect, srcrect, NULL, colors, true);
if (count % 2 == 1) {
dsthalfrect += p;
draw2DImageFilterScaled(driver, stat_texture, dsthalfrect, srchalfrect, NULL, colors, true);
}
}

View File

@ -1418,14 +1418,14 @@ void intlGUIEditBox::calculateScrollPos()
// todo: adjust scrollbar
}
// vertical scroll position
if (FrameRect.LowerRightCorner.Y < CurrentTextRect.LowerRightCorner.Y + VScrollPos)
VScrollPos = CurrentTextRect.LowerRightCorner.Y - FrameRect.LowerRightCorner.Y + VScrollPos;
if (!WordWrap && !MultiLine)
return;
else if (FrameRect.UpperLeftCorner.Y > CurrentTextRect.UpperLeftCorner.Y + VScrollPos)
VScrollPos = CurrentTextRect.UpperLeftCorner.Y - FrameRect.UpperLeftCorner.Y + VScrollPos;
else
VScrollPos = 0;
// vertical scroll position
if (FrameRect.LowerRightCorner.Y < CurrentTextRect.LowerRightCorner.Y)
VScrollPos += CurrentTextRect.LowerRightCorner.Y - FrameRect.LowerRightCorner.Y; // scrolling downwards
else if (FrameRect.UpperLeftCorner.Y > CurrentTextRect.UpperLeftCorner.Y)
VScrollPos += CurrentTextRect.UpperLeftCorner.Y - FrameRect.UpperLeftCorner.Y; // scrolling upwards
// todo: adjust scrollbar
}

View File

@ -659,7 +659,7 @@ bool InventoryList::roomForItem(const ItemStack &item_) const
return false;
}
bool InventoryList::containsItem(const ItemStack &item) const
bool InventoryList::containsItem(const ItemStack &item, bool match_meta) const
{
u32 count = item.count;
if(count == 0)
@ -670,9 +670,9 @@ bool InventoryList::containsItem(const ItemStack &item) const
{
if(count == 0)
break;
if(i->name == item.name)
{
if(i->count >= count)
if (i->name == item.name
&& (!match_meta || (i->metadata == item.metadata))) {
if (i->count >= count)
return true;
else
count -= i->count;

View File

@ -223,9 +223,10 @@ public:
// Checks whether there is room for a given item
bool roomForItem(const ItemStack &item) const;
// Checks whether the given count of the given item name
// Checks whether the given count of the given item
// exists in this inventory list.
bool containsItem(const ItemStack &item) const;
// If match_meta is false, only the items' names are compared.
bool containsItem(const ItemStack &item, bool match_meta) const;
// Removes the given count of the given item name from
// this inventory list. Walks the list in reverse order.

View File

@ -25,6 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <vector3d.h>
typedef core::vector3df v3f;
typedef core::vector3d<double> v3d;
typedef core::vector3d<s16> v3s16;
typedef core::vector3d<u16> v3u16;
typedef core::vector3d<s32> v3s32;

View File

@ -13,11 +13,11 @@ void ItemStackMetadata::serialize(std::ostream &os) const
{
std::ostringstream os2;
os2 << DESERIALIZE_START;
for (StringMap::const_iterator
it = m_stringvars.begin();
it != m_stringvars.end(); ++it) {
os2 << it->first << DESERIALIZE_KV_DELIM
<< it->second << DESERIALIZE_PAIR_DELIM;
for (StringMap::const_iterator it = m_stringvars.begin(); it != m_stringvars.end();
++it) {
if (!(*it).first.empty() || !(*it).second.empty())
os2 << (*it).first << DESERIALIZE_KV_DELIM
<< (*it).second << DESERIALIZE_PAIR_DELIM;
}
os << serializeJsonStringIfNeeded(os2.str());
}
@ -28,16 +28,18 @@ void ItemStackMetadata::deSerialize(std::istream &is)
m_stringvars.clear();
if (!in.empty() && in[0] == DESERIALIZE_START) {
Strfnd fnd(in);
fnd.to(1);
while (!fnd.at_end()) {
std::string name = fnd.next(DESERIALIZE_KV_DELIM_STR);
std::string var = fnd.next(DESERIALIZE_PAIR_DELIM_STR);
m_stringvars[name] = var;
if (!in.empty()) {
if (in[0] == DESERIALIZE_START) {
Strfnd fnd(in);
fnd.to(1);
while (!fnd.at_end()) {
std::string name = fnd.next(DESERIALIZE_KV_DELIM_STR);
std::string var = fnd.next(DESERIALIZE_PAIR_DELIM_STR);
m_stringvars[name] = var;
}
} else {
// BACKWARDS COMPATIBILITY
m_stringvars[""] = in;
}
} else {
// BACKWARDS COMPATIBILITY
m_stringvars[""] = in;
}
}

View File

@ -34,7 +34,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
LocalPlayer::LocalPlayer(Client *client, const char *name):
Player(name, client->idef()),
parent(0),
parent(NULL),
hp(PLAYER_MAX_HP),
isAttached(false),
touching_ground(false),
@ -53,8 +53,8 @@ LocalPlayer::LocalPlayer(Client *client, const char *name):
overridePosition(v3f(0,0,0)),
last_position(v3f(0,0,0)),
last_speed(v3f(0,0,0)),
last_pitch(0),
last_yaw(0),
last_pitch(0.0f),
last_yaw(0.0f),
last_keyPressed(0),
last_camera_fov(0),
last_wanted_range(0),
@ -67,13 +67,12 @@ LocalPlayer::LocalPlayer(Client *client, const char *name):
hurt_tilt_timer(0.0f),
hurt_tilt_strength(0.0f),
m_position(0,0,0),
m_sneak_node(32767,32767,32767),
m_sneak_node_bb_ymax(0), // To support temporary option for old move code
m_sneak_node_bb_top(0,0,0,0,0,0),
m_sneak_node(32767, 32767, 32767),
m_sneak_node_bb_top(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f),
m_sneak_node_exists(false),
m_need_to_get_new_sneak_node(true),
m_sneak_ladder_detected(false),
m_ledge_detected(false),
m_sneak_node_bb_ymax(0.0f),
m_need_to_get_new_sneak_node(true),
m_old_node_below(32767,32767,32767),
m_old_node_below_type("air"),
m_can_jump(false),
@ -96,91 +95,142 @@ LocalPlayer::~LocalPlayer()
{
}
static aabb3f getTopBoundingBox(const std::vector<aabb3f> &nodeboxes)
static aabb3f getNodeBoundingBox(const std::vector<aabb3f> &nodeboxes)
{
if (nodeboxes.size() == 0)
return aabb3f(0, 0, 0, 0, 0, 0);
aabb3f b_max;
b_max.reset(-BS, -BS, -BS);
for (std::vector<aabb3f>::const_iterator it = nodeboxes.begin();
it != nodeboxes.end(); ++it) {
aabb3f box = *it;
if (box.MaxEdge.Y > b_max.MaxEdge.Y)
b_max = box;
else if (box.MaxEdge.Y == b_max.MaxEdge.Y)
b_max.addInternalBox(box);
}
return aabb3f(v3f(b_max.MinEdge.X, b_max.MaxEdge.Y, b_max.MinEdge.Z), b_max.MaxEdge);
std::vector<aabb3f>::const_iterator it = nodeboxes.begin();
b_max = aabb3f(it->MinEdge, it->MaxEdge);
++it;
for (; it != nodeboxes.end(); ++it)
b_max.addInternalBox(*it);
return b_max;
}
#define GETNODE(map, p3, v2, y, valid) \
(map)->getNodeNoEx((p3) + v3s16((v2).X, y, (v2).Y), valid)
// pos is the node the player is standing inside(!)
static bool detectSneakLadder(Map *map, INodeDefManager *nodemgr, v3s16 pos)
bool LocalPlayer::updateSneakNode(Map *map, const v3f &position,
const v3f &sneak_max)
{
// Detects a structure known as "sneak ladder" or "sneak elevator"
// that relies on bugs to provide a fast means of vertical transportation,
// the bugs have since been fixed but this function remains to keep it working.
// NOTE: This is just entirely a huge hack and causes way too many problems.
bool is_valid_position;
static const v3s16 dir9_center[9] = {
v3s16( 0, 0, 0),
v3s16( 1, 0, 0),
v3s16(-1, 0, 0),
v3s16( 0, 0, 1),
v3s16( 0, 0, -1),
v3s16( 1, 0, 1),
v3s16(-1, 0, 1),
v3s16( 1, 0, -1),
v3s16(-1, 0, -1)
};
INodeDefManager *nodemgr = m_client->ndef();
MapNode node;
// X/Z vectors for 4 neighboring nodes
static const v2s16 vecs[] = { v2s16(-1, 0), v2s16(1, 0), v2s16(0, -1), v2s16(0, 1) };
bool is_valid_position;
bool new_sneak_node_exists = m_sneak_node_exists;
for (u16 i = 0; i < ARRLEN(vecs); i++) {
const v2s16 vec = vecs[i];
// We want the top of the sneak node to be below the players feet
f32 position_y_mod = 0.05 * BS;
if (m_sneak_node_exists)
position_y_mod = m_sneak_node_bb_top.MaxEdge.Y - position_y_mod;
// walkability of bottom & top node should differ
node = GETNODE(map, pos, vec, 0, &is_valid_position);
if (!is_valid_position)
continue;
bool w = nodemgr->get(node).walkable;
node = GETNODE(map, pos, vec, 1, &is_valid_position);
if (!is_valid_position || w == nodemgr->get(node).walkable)
// Get position of current standing node
const v3s16 current_node = floatToInt(position - v3f(0, position_y_mod, 0), BS);
if (current_node != m_sneak_node) {
new_sneak_node_exists = false;
} else {
node = map->getNodeNoEx(current_node, &is_valid_position);
if (!is_valid_position || !nodemgr->get(node).walkable)
new_sneak_node_exists = false;
}
// Keep old sneak node
if (new_sneak_node_exists)
return true;
// Get new sneak node
m_sneak_ladder_detected = false;
f32 min_distance_f = 100000.0 * BS;
for (s16 d = 0; d < 9; d++) {
const v3s16 p = current_node + dir9_center[d];
const v3f pf = intToFloat(p, BS);
const v2f diff(position.X - pf.X, position.Z - pf.Z);
f32 distance_f = diff.getLength();
if (distance_f > min_distance_f ||
fabs(diff.X) > (.5 + .1) * BS + sneak_max.X ||
fabs(diff.Y) > (.5 + .1) * BS + sneak_max.Z)
continue;
// check one more node above OR below with corresponding walkability
node = GETNODE(map, pos, vec, -1, &is_valid_position);
bool ok = is_valid_position && w != nodemgr->get(node).walkable;
if (!ok) {
node = GETNODE(map, pos, vec, 2, &is_valid_position);
ok = is_valid_position && w == nodemgr->get(node).walkable;
// The node to be sneaked on has to be walkable
node = map->getNodeNoEx(p, &is_valid_position);
if (!is_valid_position || !nodemgr->get(node).walkable)
continue;
// And the node(s) above have to be nonwalkable
bool ok = true;
if (!physics_override_sneak_glitch) {
u16 height = ceilf(
(m_collisionbox.MaxEdge.Y - m_collisionbox.MinEdge.Y) / BS
);
for (u16 y = 1; y <= height; y++) {
node = map->getNodeNoEx(p + v3s16(0, y, 0), &is_valid_position);
if (!is_valid_position || nodemgr->get(node).walkable) {
ok = false;
break;
}
}
} else {
// legacy behaviour: check just one node
node = map->getNodeNoEx(p + v3s16(0, 1, 0), &is_valid_position);
ok = is_valid_position && !nodemgr->get(node).walkable;
}
if (!ok)
continue;
if (ok)
return true;
min_distance_f = distance_f;
m_sneak_node = p;
new_sneak_node_exists = true;
}
return false;
}
if (!new_sneak_node_exists)
return false;
static bool detectLedge(Map *map, INodeDefManager *nodemgr, v3s16 pos)
{
bool is_valid_position;
MapNode node;
// X/Z vectors for 4 neighboring nodes
static const v2s16 vecs[] = {v2s16(-1, 0), v2s16(1, 0), v2s16(0, -1), v2s16(0, 1)};
// Update saved top bounding box of sneak node
node = map->getNodeNoEx(m_sneak_node);
std::vector<aabb3f> nodeboxes;
node.getCollisionBoxes(nodemgr, &nodeboxes);
m_sneak_node_bb_top = getNodeBoundingBox(nodeboxes);
for (u16 i = 0; i < ARRLEN(vecs); i++) {
const v2s16 vec = vecs[i];
node = GETNODE(map, pos, vec, 1, &is_valid_position);
if (physics_override_sneak_glitch) {
// Detect sneak ladder:
// Node two meters above sneak node must be solid
node = map->getNodeNoEx(m_sneak_node + v3s16(0, 2, 0),
&is_valid_position);
if (is_valid_position && nodemgr->get(node).walkable) {
// Ledge exists
node = GETNODE(map, pos, vec, 2, &is_valid_position);
if (is_valid_position && !nodemgr->get(node).walkable)
// Space above ledge exists
return true;
// Node three meters above: must be non-solid
node = map->getNodeNoEx(m_sneak_node + v3s16(0, 3, 0),
&is_valid_position);
m_sneak_ladder_detected = is_valid_position &&
!nodemgr->get(node).walkable;
}
}
return false;
return true;
}
#undef GETNODE
void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d,
std::vector<CollisionInfo> *collision_info)
{
if (!collision_info || collision_info->empty()) {
// Node below the feet, update each ClientEnvironment::step()
m_standing_node = floatToInt(m_position, BS) - v3s16(0, 1, 0);
}
// Temporary option for old move code
if (!physics_override_new_move) {
old_move(dtime, env, pos_max_d, collision_info);
@ -193,10 +243,8 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d,
v3f position = getPosition();
// Copy parent position if local player is attached
if(isAttached)
{
if (isAttached) {
setPosition(overridePosition);
m_sneak_node_exists = false;
return;
}
@ -204,11 +252,11 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d,
bool fly_allowed = m_client->checkLocalPrivilege("fly");
bool noclip = m_client->checkLocalPrivilege("noclip") &&
g_settings->getBool("noclip");
bool free_move = noclip && fly_allowed && g_settings->getBool("free_move");
if (free_move) {
bool free_move = g_settings->getBool("free_move") && fly_allowed;
if (noclip && free_move) {
position += m_speed * dtime;
setPosition(position);
m_sneak_node_exists = false;
return;
}
@ -279,7 +327,6 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d,
|| nodemgr->get(node2.getContent()).climbable) && !free_move;
}
/*
Collision uncertainty radius
Make it a bit larger than the maximum distance of movement
@ -291,49 +338,109 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d,
// This should always apply, otherwise there are glitches
sanity_check(d > pos_max_d);
// TODO: this shouldn't be hardcoded but transmitted from server
float player_stepheight = (touching_ground) ? (BS * 0.6f) : (BS * 0.2f);
#if defined(__ANDROID__) || defined(__IOS__)
if (touching_ground)
player_stepheight += (0.6f * BS);
#endif
v3f accel_f = v3f(0,0,0);
collisionMoveResult result = collisionMoveSimple(env, m_client,
pos_max_d, m_collisionbox, player_stepheight, dtime,
&position, &m_speed, accel_f);
bool could_sneak = control.sneak && !free_move && !in_liquid &&
!is_climbing && physics_override_sneak;
// Add new collisions to the vector
if (collision_info && !free_move) {
v3f diff = intToFloat(m_standing_node, BS) - position;
f32 distance = diff.getLength();
// Force update each ClientEnvironment::step()
bool is_first = collision_info->empty();
for (std::vector<CollisionInfo>::const_iterator
colinfo = result.collisions.begin();
colinfo != result.collisions.end(); ++colinfo) {
collision_info->push_back(*colinfo);
if (colinfo->type != COLLISION_NODE ||
colinfo->new_speed.Y != 0 ||
(could_sneak && m_sneak_node_exists))
continue;
diff = intToFloat(colinfo->node_p, BS) - position;
// Find nearest colliding node
f32 len = diff.getLength();
if (is_first || len < distance) {
m_standing_node = colinfo->node_p;
distance = len;
}
}
}
/*
If the player's feet touch the topside of any node, this is
set to true.
Player is allowed to jump when this is true.
*/
bool touching_ground_was = touching_ground;
touching_ground = result.touching_ground;
bool sneak_can_jump = false;
// Max. distance (X, Z) over border for sneaking determined by collision box
// * 0.49 to keep the center just barely on the node
v3f sneak_max = m_collisionbox.getExtent() * 0.49;
if (m_sneak_ladder_detected) {
// restore legacy behaviour (this makes the m_speed.Y hack necessary)
sneak_max = v3f(0.4 * BS, 0, 0.4 * BS);
}
/*
If sneaking, keep in range from the last walked node and don't
fall off from it
If sneaking, keep on top of last walked node and don't fall off
*/
if (control.sneak && m_sneak_node_exists &&
!(fly_allowed && g_settings->getBool("free_move")) &&
!in_liquid && !is_climbing &&
physics_override_sneak) {
if (could_sneak && m_sneak_node_exists) {
const v3f sn_f = intToFloat(m_sneak_node, BS);
const v3f bmin = sn_f + m_sneak_node_bb_top.MinEdge;
const v3f bmax = sn_f + m_sneak_node_bb_top.MaxEdge;
const v3f old_pos = position;
const v3f old_speed = m_speed;
f32 y_diff = bmax.Y - position.Y;
m_standing_node = m_sneak_node;
position.X = rangelim(position.X,
// (BS * 0.6f) is the basic stepheight while standing on ground
if (y_diff < BS * 0.6f) {
// Only center player when they're on the node
position.X = rangelim(position.X,
bmin.X - sneak_max.X, bmax.X + sneak_max.X);
position.Z = rangelim(position.Z,
position.Z = rangelim(position.Z,
bmin.Z - sneak_max.Z, bmax.Z + sneak_max.Z);
if (position.X != old_pos.X)
m_speed.X = 0;
if (position.Z != old_pos.Z)
m_speed.Z = 0;
// Because we keep the player collision box on the node, limiting
// position.Y is not necessary but useful to prevent players from
// being inside a node if sneaking on e.g. the lower part of a stair
if (!m_sneak_ladder_detected) {
position.Y = MYMAX(position.Y, bmax.Y);
} else {
// legacy behaviour that sometimes causes some weird slow sinking
m_speed.Y = MYMAX(m_speed.Y, 0);
if (position.X != old_pos.X)
m_speed.X = 0;
if (position.Z != old_pos.Z)
m_speed.Z = 0;
}
if (collision_info != NULL &&
if (y_diff > 0 && m_speed.Y < 0 &&
(physics_override_sneak_glitch || y_diff < BS * 0.6f)) {
// Move player to the maximal height when falling or when
// the ledge is climbed on the next step.
position.Y = bmax.Y;
m_speed.Y = 0;
}
// Allow jumping on node edges while sneaking
if (m_speed.Y == 0 || m_sneak_ladder_detected)
sneak_can_jump = true;
if (collision_info &&
m_speed.Y - old_speed.Y > BS) {
// Collide with sneak node, report fall damage
CollisionInfo sn_info;
@ -344,144 +451,24 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d,
}
}
// TODO: this shouldn't be hardcoded but transmitted from server
float player_stepheight = (touching_ground) ? (BS * 0.6f) : (BS * 0.2f);
#if defined(__ANDROID__) || defined(__IOS__)
player_stepheight += (0.6f * BS);
#endif
v3f accel_f = v3f(0,0,0);
collisionMoveResult result = collisionMoveSimple(env, m_client,
pos_max_d, m_collisionbox, player_stepheight, dtime,
&position, &m_speed, accel_f);
/*
If the player's feet touch the topside of any node, this is
set to true.
Player is allowed to jump when this is true.
Find the next sneak node if necessary
*/
bool touching_ground_was = touching_ground;
touching_ground = result.touching_ground;
bool new_sneak_node_exists = false;
// We want the top of the sneak node to be below the players feet
f32 position_y_mod;
if (m_sneak_node_exists)
position_y_mod = m_sneak_node_bb_top.MaxEdge.Y - 0.05 * BS;
else
position_y_mod = (0.5 - 0.05) * BS;
v3s16 current_node = floatToInt(position - v3f(0, position_y_mod, 0), BS);
/*
Check the nodes under the player to see from which node the
player is sneaking from, if any. If the node from under
the player has been removed, the player falls.
*/
if (m_sneak_node_exists &&
nodemgr->get(map->getNodeNoEx(m_old_node_below)).name == "air" &&
m_old_node_below_type != "air") {
// Old node appears to have been removed; that is,
// it wasn't air before but now it is
m_need_to_get_new_sneak_node = false;
m_sneak_node_exists = false;
} else if (nodemgr->get(map->getNodeNoEx(current_node)).name != "air") {
// We are on something, so make sure to recalculate the sneak
// node.
m_need_to_get_new_sneak_node = true;
}
if (m_need_to_get_new_sneak_node && physics_override_sneak) {
v2f player_p2df(position.X, position.Z);
f32 min_distance_f = 100000.0 * BS;
v3s16 new_sneak_node = m_sneak_node;
for(s16 x=-1; x<=1; x++)
for(s16 z=-1; z<=1; z++)
{
v3s16 p = current_node + v3s16(x,0,z);
v3f pf = intToFloat(p, BS);
v2f node_p2df(pf.X, pf.Z);
f32 distance_f = player_p2df.getDistanceFrom(node_p2df);
if (distance_f > min_distance_f ||
fabs(player_p2df.X-node_p2df.X) > (.5+.1)*BS + sneak_max.X ||
fabs(player_p2df.Y-node_p2df.Y) > (.5+.1)*BS + sneak_max.Z)
continue;
// The node to be sneaked on has to be walkable
node = map->getNodeNoEx(p, &is_valid_position);
if (!is_valid_position || !nodemgr->get(node).walkable)
continue;
// And the node(s) above have to be nonwalkable
bool ok = true;
if (!physics_override_sneak_glitch) {
u16 height = ceilf(
(m_collisionbox.MaxEdge.Y - m_collisionbox.MinEdge.Y) / BS
);
for (u16 y = 1; y <= height; y++) {
node = map->getNodeNoEx(p + v3s16(0,y,0), &is_valid_position);
if (!is_valid_position || nodemgr->get(node).walkable) {
ok = false;
break;
}
}
} else {
// legacy behaviour: check just one node
node = map->getNodeNoEx(p + v3s16(0,1,0), &is_valid_position);
ok = is_valid_position && !nodemgr->get(node).walkable;
}
if (!ok)
continue;
min_distance_f = distance_f;
new_sneak_node = p;
}
bool sneak_node_found = (min_distance_f < 100000.0 * BS);
m_sneak_node = new_sneak_node;
m_sneak_node_exists = sneak_node_found;
if (sneak_node_found) {
// Update saved top bounding box of sneak node
MapNode n = map->getNodeNoEx(m_sneak_node);
std::vector<aabb3f> nodeboxes;
n.getCollisionBoxes(nodemgr, &nodeboxes);
m_sneak_node_bb_top = getTopBoundingBox(nodeboxes);
m_sneak_ladder_detected = physics_override_sneak_glitch &&
detectSneakLadder(map, nodemgr, floatToInt(position, BS));
} else {
m_sneak_ladder_detected = false;
}
}
/*
If 'sneak glitch' enabled detect ledge for old sneak-jump
behaviour of climbing onto a ledge 2 nodes up.
*/
if (physics_override_sneak_glitch && control.sneak && control.jump)
m_ledge_detected = detectLedge(map, nodemgr, floatToInt(position, BS));
if (could_sneak)
new_sneak_node_exists = updateSneakNode(map, position, sneak_max);
/*
Set new position but keep sneak node set
*/
bool sneak_node_exists = m_sneak_node_exists;
setPosition(position);
m_sneak_node_exists = sneak_node_exists;
m_sneak_node_exists = new_sneak_node_exists;
/*
Report collisions
*/
// Dont report if flying
if(collision_info && !(g_settings->getBool("free_move") && fly_allowed)) {
for(size_t i=0; i<result.collisions.size(); i++) {
const CollisionInfo &info = result.collisions[i];
collision_info->push_back(info);
}
}
if(!result.standing_on_object && !touching_ground_was && touching_ground) {
MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround");
m_client->event()->put(e);
@ -501,22 +488,15 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d,
}
}
/*
Update the node last under the player
*/
m_old_node_below = floatToInt(position - v3f(0,BS/2,0), BS);
m_old_node_below_type = nodemgr->get(map->getNodeNoEx(m_old_node_below)).name;
/*
Check properties of the node on which the player is standing
*/
const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(getStandingNodePos()));
const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(m_standing_node));
// Determine if jumping is possible
m_can_jump = touching_ground && !in_liquid;
m_can_jump = (touching_ground && !in_liquid && !is_climbing)
|| sneak_can_jump;
if (itemgroup_get(f.groups, "disable_jump"))
m_can_jump = false;
else if (control.sneak && m_sneak_ladder_detected && !in_liquid && !is_climbing)
m_can_jump = true;
// Jump key pressed while jumping off from a bouncy block
if (m_can_jump && control.jump && itemgroup_get(f.groups, "bouncy") &&
@ -705,17 +685,8 @@ void LocalPlayer::applyControl(float dtime)
*/
v3f speedJ = getSpeed();
if(speedJ.Y >= -0.5 * BS) {
if (m_ledge_detected) {
// Limit jump speed to a minimum that allows
// jumping up onto a ledge 2 nodes up.
speedJ.Y = movement_speed_jump *
MYMAX(physics_override_jump, 1.3f);
setSpeed(speedJ);
m_ledge_detected = false;
} else {
speedJ.Y = movement_speed_jump * physics_override_jump;
setSpeed(speedJ);
}
speedJ.Y = movement_speed_jump * physics_override_jump;
setSpeed(speedJ);
MtEvent *e = new SimpleTriggerEvent("PlayerJump");
m_client->event()->put(e);
@ -772,7 +743,7 @@ v3s16 LocalPlayer::getStandingNodePos()
{
if(m_sneak_node_exists)
return m_sneak_node;
return floatToInt(getPosition() - v3f(0, BS, 0), BS);
return m_standing_node;
}
v3s16 LocalPlayer::getFootstepNodePos()
@ -958,7 +929,7 @@ void LocalPlayer::old_move(f32 dtime, Environment *env, f32 pos_max_d,
// this shouldn't be hardcoded but transmitted from server
float player_stepheight = touching_ground ? (BS * 0.6) : (BS * 0.2);
#ifdef __ANDROID__
#if defined(__ANDROID__) || defined(__IOS__)
player_stepheight += (0.6 * BS);
#endif

View File

@ -22,6 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "player.h"
#include "environment.h"
#include "constants.h"
#include <list>
class Client;
@ -46,6 +47,8 @@ public:
ClientActiveObject *parent;
// Initialize hp to 0, so that no hearts will be shown if server
// doesn't support health points
u16 hp;
bool isAttached;
bool touching_ground;
@ -108,7 +111,7 @@ public:
void setCAO(GenericCAO *toset)
{
assert(m_cao == NULL); // Pre-condition
assert(!m_cao); // Pre-condition
m_cao = toset;
}
@ -140,30 +143,31 @@ public:
private:
void accelerateHorizontal(const v3f &target_speed, const f32 max_increase);
void accelerateVertical(const v3f &target_speed, const f32 max_increase);
bool updateSneakNode(Map *map, const v3f &position, const v3f &sneak_max);
v3f m_position;
v3s16 m_standing_node;
v3s16 m_sneak_node;
// Stores the max player uplift by m_sneak_node
// To support temporary option for old move code
f32 m_sneak_node_bb_ymax;
// Stores the top bounding box of m_sneak_node
aabb3f m_sneak_node_bb_top;
// Whether the player is allowed to sneak
bool m_sneak_node_exists;
// Whether recalculation of m_sneak_node and its top bbox is needed
bool m_need_to_get_new_sneak_node;
// Whether a "sneak ladder" structure is detected at the players pos
// see detectSneakLadder() in the .cpp for more info (always false if disabled)
bool m_sneak_ladder_detected;
// Whether a 2-node-up ledge is detected at the players pos,
// see detectLedge() in the .cpp for more info (always false if disabled).
bool m_ledge_detected;
// ***** Variables for temporary option of the old move code *****
// Stores the max player uplift by m_sneak_node
f32 m_sneak_node_bb_ymax;
// Whether recalculation of m_sneak_node and its top bbox is needed
bool m_need_to_get_new_sneak_node;
// Node below player, used to determine whether it has been removed,
// and its old type
v3s16 m_old_node_below;
std::string m_old_node_below_type;
// ***** End of variables for temporary option *****
bool m_can_jump;
u16 m_breath;
f32 m_yaw;

View File

@ -373,13 +373,10 @@ void StringBuffer::push_back(char c)
flush(std::string(buffer, buffer_index));
buffer_index = 0;
} else {
int index = buffer_index;
buffer[index++] = c;
if (index >= BUFFER_LENGTH) {
buffer[buffer_index++] = c;
if (buffer_index >= BUFFER_LENGTH) {
flush(std::string(buffer, buffer_index));
buffer_index = 0;
} else {
buffer_index = index;
}
}
}

View File

@ -1377,11 +1377,6 @@ s16 ServerMap::getWaterLevel()
return getMapgenParams()->water_level;
}
bool ServerMap::saoPositionOverLimit(const v3f &p)
{
return getMapgenParams()->saoPosOverLimit(p);
}
bool ServerMap::blockpos_over_mapgen_limit(v3s16 p)
{
const s16 mapgen_limit_bp = rangelim(
@ -2734,6 +2729,7 @@ void MMVManip::blitBackAll(std::map<v3s16, MapBlock*> *modified_blocks,
continue;
block->copyFrom(*this);
block->raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_VMANIP);
if(modified_blocks)
(*modified_blocks)[p] = block;

View File

@ -377,8 +377,6 @@ public:
*/
ServerMapSector *createSector(v2s16 p);
bool saoPositionOverLimit(const v3f &p);
/*
Blocks are generated by using these and makeBlock().
*/

View File

@ -122,7 +122,8 @@ public:
#define MOD_REASON_STATIC_DATA_REMOVED (1 << 16)
#define MOD_REASON_STATIC_DATA_CHANGED (1 << 17)
#define MOD_REASON_EXPIRE_DAYNIGHTDIFF (1 << 18)
#define MOD_REASON_UNKNOWN (1 << 19)
#define MOD_REASON_VMANIP (1 << 19)
#define MOD_REASON_UNKNOWN (1 << 20)
////
//// MapBlock itself

View File

@ -815,7 +815,16 @@ void MapgenBasic::dustTopNodes()
}
content_t c = vm->m_data[vi].getContent();
if (!ndef->get(c).buildable_to && c != CONTENT_IGNORE && c != biome->c_dust) {
NodeDrawType dtype = ndef->get(c).drawtype;
// Only place on walkable cubic non-liquid nodes
// Dust check needed due to vertical overgeneration
if ((dtype == NDT_NORMAL ||
dtype == NDT_ALLFACES_OPTIONAL ||
dtype == NDT_GLASSLIKE_FRAMED_OPTIONAL ||
dtype == NDT_GLASSLIKE ||
dtype == NDT_GLASSLIKE_FRAMED ||
dtype == NDT_ALLFACES) &&
ndef->get(c).walkable && c != biome->c_dust) {
vm->m_area.add_y(em, vi, 1);
vm->m_data[vi] = MapNode(biome->c_dust);
}
@ -979,8 +988,7 @@ bool GenerateNotifier::addEvent(GenNotifyType type, v3s16 pos, u32 id)
void GenerateNotifier::getEvents(
std::map<std::string, std::vector<v3s16> > &event_map,
bool peek_events)
std::map<std::string, std::vector<v3s16> > &event_map)
{
std::list<GenNotifyEvent>::iterator it;
@ -992,9 +1000,12 @@ void GenerateNotifier::getEvents(
event_map[name].push_back(gn.pos);
}
}
if (!peek_events)
m_notify_events.clear();
void GenerateNotifier::clearEvents()
{
m_notify_events.clear();
}
@ -1055,16 +1066,15 @@ void MapgenParams::writeParams(Settings *settings) const
bparams->writeParams(settings);
}
// Calculate edges of outermost generated mapchunks (less than
// 'mapgen_limit'), and corresponding exact limits for SAO entities.
// Calculate exact edges of the outermost mapchunks that are within the
// set 'mapgen_limit'.
void MapgenParams::calcMapgenEdges()
{
// Central chunk offset, in blocks
s16 ccoff_b = -chunksize / 2;
// Chunksize, in nodes
s32 csize_n = chunksize * MAP_BLOCKSIZE;
// Minp/maxp of central chunk, in nodes
s16 ccmin = ccoff_b * MAP_BLOCKSIZE;
s16 ccmax = ccmin + csize_n - 1;
@ -1083,25 +1093,17 @@ void MapgenParams::calcMapgenEdges()
s16 numcmin = MYMAX((ccfmin - mapgen_limit_min) / csize_n, 0);
s16 numcmax = MYMAX((mapgen_limit_max - ccfmax) / csize_n, 0);
// Mapgen edges, in nodes
// These values may be useful later as additional class members
s16 mapgen_edge_min = ccmin - numcmin * csize_n;
s16 mapgen_edge_max = ccmax + numcmax * csize_n;
// SAO position limits, in Irrlicht units
m_sao_limit_min = mapgen_edge_min * BS - 3.0f;
m_sao_limit_max = mapgen_edge_max * BS + 3.0f;
mapgen_edge_min = ccmin - numcmin * csize_n;
mapgen_edge_max = ccmax + numcmax * csize_n;
m_mapgen_edges_calculated = true;
}
bool MapgenParams::saoPosOverLimit(const v3f &p)
s32 MapgenParams::getSpawnRangeMax()
{
if (!m_sao_limit_calculated) {
if (!m_mapgen_edges_calculated)
calcMapgenEdges();
m_sao_limit_calculated = true;
}
return p.X < m_sao_limit_min ||
p.X > m_sao_limit_max ||
p.Y < m_sao_limit_min ||
p.Y > m_sao_limit_max ||
p.Z < m_sao_limit_min ||
p.Z > m_sao_limit_max;
return MYMIN(-mapgen_edge_min, mapgen_edge_max);
}

View File

@ -101,8 +101,8 @@ public:
void setNotifyOnDecoIds(std::set<u32> *notify_on_deco_ids);
bool addEvent(GenNotifyType type, v3s16 pos, u32 id=0);
void getEvents(std::map<std::string, std::vector<v3s16> > &event_map,
bool peek_events=false);
void getEvents(std::map<std::string, std::vector<v3s16> > &event_map);
void clearEvents();
private:
u32 m_notify_on;
@ -132,6 +132,9 @@ struct MapgenParams {
BiomeParams *bparams;
s16 mapgen_edge_min;
s16 mapgen_edge_max;
MapgenParams() :
mgtype(MAPGEN_DEFAULT),
chunksize(5),
@ -140,9 +143,10 @@ struct MapgenParams {
mapgen_limit(MAX_MAP_GENERATION_LIMIT),
flags(MG_CAVES | MG_LIGHT | MG_DECORATIONS),
bparams(NULL),
m_sao_limit_min(MAX_MAP_GENERATION_LIMIT * BS),
m_sao_limit_max(MAX_MAP_GENERATION_LIMIT * BS),
m_sao_limit_calculated(false)
mapgen_edge_min(-MAX_MAP_GENERATION_LIMIT),
mapgen_edge_max(MAX_MAP_GENERATION_LIMIT),
m_mapgen_edges_calculated(false)
{
}
@ -151,13 +155,12 @@ struct MapgenParams {
virtual void readParams(const Settings *settings);
virtual void writeParams(Settings *settings) const;
bool saoPosOverLimit(const v3f &p);
s32 getSpawnRangeMax();
private:
void calcMapgenEdges();
float m_sao_limit_min;
float m_sao_limit_max;
bool m_sao_limit_calculated;
bool m_mapgen_edges_calculated;
};

View File

@ -57,13 +57,17 @@ MapgenV7::MapgenV7(int mapgenid, MapgenV7Params *params, EmergeManager *emerge)
this->spflags = params->spflags;
this->cave_width = params->cave_width;
this->float_mount_density = params->float_mount_density;
this->float_mount_height = params->float_mount_height;
this->floatland_level = params->floatland_level;
this->shadow_limit = params->shadow_limit;
this->cavern_limit = params->cavern_limit;
this->cavern_taper = params->cavern_taper;
this->cavern_threshold = params->cavern_threshold;
// This is to avoid a divide-by-zero.
// Parameter will be saved to map_meta.txt in limited form.
params->float_mount_height = MYMAX(params->float_mount_height, 1.0f);
this->float_mount_height = params->float_mount_height;
// 2D noise
noise_terrain_base = new Noise(&params->np_terrain_base, seed, csize.X, csize.Z);
noise_terrain_alt = new Noise(&params->np_terrain_alt, seed, csize.X, csize.Z);
@ -376,7 +380,8 @@ float MapgenV7::baseTerrainLevelFromMap(int index)
bool MapgenV7::getMountainTerrainAtPoint(s16 x, s16 y, s16 z)
{
float mnt_h_n = NoisePerlin2D(&noise_mount_height->np, x, z, seed);
float mnt_h_n =
MYMAX(NoisePerlin2D(&noise_mount_height->np, x, z, seed), 1.0f);
float density_gradient = -((float)y / mnt_h_n);
float mnt_n = NoisePerlin3D(&noise_mountain->np, x, y, z, seed);
@ -386,7 +391,7 @@ bool MapgenV7::getMountainTerrainAtPoint(s16 x, s16 y, s16 z)
bool MapgenV7::getMountainTerrainFromMap(int idx_xyz, int idx_xz, s16 y)
{
float mounthn = noise_mount_height->result[idx_xz];
float mounthn = MYMAX(noise_mount_height->result[idx_xz], 1.0f);
float density_gradient = -((float)y / mounthn);
float mountn = noise_mountain->result[idx_xyz];
@ -415,7 +420,8 @@ void MapgenV7::floatBaseExtentFromMap(s16 *float_base_min, s16 *float_base_max,
float n_base = noise_floatland_base->result[idx_xz];
if (n_base > 0.0f) {
float n_base_height = noise_float_base_height->result[idx_xz];
float n_base_height =
MYMAX(noise_float_base_height->result[idx_xz], 1.0f);
float amp = n_base * n_base_height;
float ridge = n_base_height / 3.0f;
base_min = floatland_level - amp / 1.5f;

View File

@ -249,18 +249,15 @@ void transformNodeBox(const MapNode &n, const NodeBox &nodebox,
int facedir = n.getFaceDir(nodemgr);
u8 axisdir = facedir>>2;
facedir&=0x03;
for(std::vector<aabb3f>::const_iterator
for (std::vector<aabb3f>::const_iterator
i = fixed.begin();
i != fixed.end(); ++i)
{
i != fixed.end(); ++i) {
aabb3f box = *i;
if (nodebox.type == NODEBOX_LEVELED) {
box.MaxEdge.Y = -BS/2 + BS*((float)1/LEVELED_MAX) * n.getLevel(nodemgr);
}
if (nodebox.type == NODEBOX_LEVELED)
box.MaxEdge.Y = (-0.5f + n.getLevel(nodemgr) / 64.0f) * BS;
switch (axisdir)
{
switch (axisdir) {
case 0:
if(facedir == 1)
{

View File

@ -103,8 +103,8 @@ enum Rotation {
#define LIQUID_INFINITY_MASK 0x80 //0b10000000
// mask for param2, now as for liquid
#define LEVELED_MASK 0x3F
// mask for leveled nodebox param2
#define LEVELED_MASK 0x7F
#define LEVELED_MAX LEVELED_MASK

View File

@ -580,10 +580,6 @@ void Client::handleCommand_MovePlayer(NetworkPacket* pkt)
event.player_force_move.pitch = pitch;
event.player_force_move.yaw = yaw;
m_client_event_queue.push(event);
// Ignore damage for a few seconds, so that the player doesn't
// get damage from falling on ground
m_ignore_damage_timer = 3.0;
}
void Client::handleCommand_DeathScreen(NetworkPacket* pkt)
@ -1172,9 +1168,21 @@ void Client::handleCommand_HudSetParam(NetworkPacket* pkt)
player->hud_hotbar_itemcount = hotbar_itemcount;
}
else if (param == HUD_PARAM_HOTBAR_IMAGE) {
// If value not empty verify image exists in texture source
if (value != "" && !getTextureSource()->isKnownSourceImage(value)) {
errorstream << "Server sent wrong Hud hotbar image (sent value: '"
<< value << "')" << std::endl;
return;
}
player->hotbar_image = value;
}
else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
// If value not empty verify image exists in texture source
if (value != "" && !getTextureSource()->isKnownSourceImage(value)) {
errorstream << "Server sent wrong Hud hotbar selected image (sent value: '"
<< value << "')" << std::endl;
return;
}
player->hotbar_selected_image = value;
}
}

View File

@ -58,9 +58,11 @@ void NetworkPacket::putRawPacket(u8 *data, u32 datasize, u16 peer_id)
m_datasize = datasize - 2;
m_peer_id = peer_id;
m_data.resize(m_datasize);
// split command and datas
m_command = readU16(&data[0]);
m_data = std::vector<u8>(&data[2], &data[2 + m_datasize]);
memcpy(m_data.data(), &data[2], m_datasize);
}
const char* NetworkPacket::getString(u32 from_offset)

View File

@ -211,7 +211,7 @@ void Server::handleCommand_Init(NetworkPacket* pkt)
// Enforce user limit.
// Don't enforce for users that have some admin right
if (m_clients.getClientIDs(CS_Created).size() >= g_settings->getU16("max_users") &&
if (m_clients.isUserLimitReached() &&
!checkPriv(playername, "server") &&
!checkPriv(playername, "ban") &&
!checkPriv(playername, "privs") &&
@ -520,7 +520,7 @@ void Server::handleCommand_Init_Legacy(NetworkPacket* pkt)
// Enforce user limit.
// Don't enforce for users that have some admin right
if (m_clients.getClientIDs(CS_Created).size() >= g_settings->getU16("max_users") &&
if (m_clients.isUserLimitReached() &&
!checkPriv(playername, "server") &&
!checkPriv(playername, "ban") &&
!checkPriv(playername, "privs") &&
@ -943,6 +943,18 @@ void Server::handleCommand_InventoryAction(NetworkPacket* pkt)
(ma->to_inv.type == InventoryLocation::PLAYER) &&
(ma->to_inv.name == player->getName());
InventoryLocation *remote = from_inv_is_current_player ?
&ma->to_inv : &ma->from_inv;
// Check for out-of-range interaction
if (remote->type == InventoryLocation::NODEMETA) {
v3f node_pos = intToFloat(remote->p, BS);
v3f player_pos = player->getPlayerSAO()->getBasePosition();
f32 d = player_pos.getDistanceFrom(node_pos);
if (!checkInteractDistance(player, d, "inventory"))
return;
}
/*
Disable moving items out of craftpreview
*/
@ -1257,6 +1269,37 @@ void Server::handleCommand_Respawn(NetworkPacket* pkt)
// the previous addition has been successfully removed
}
bool Server::checkInteractDistance(RemotePlayer *player, const f32 d, const std::string what)
{
PlayerSAO *playersao = player->getPlayerSAO();
const InventoryList *hlist = playersao->getInventory()->getList("hand");
const ItemDefinition &playeritem_def =
playersao->getWieldedItem().getDefinition(m_itemdef);
const ItemDefinition &hand_def =
hlist ? hlist->getItem(0).getDefinition(m_itemdef) : m_itemdef->get("");
float max_d = BS * playeritem_def.range;
float max_d_hand = BS * hand_def.range;
if (max_d < 0 && max_d_hand >= 0)
max_d = max_d_hand;
else if (max_d < 0)
max_d = BS * 4.0f;
// cube diagonal: sqrt(3) = 1.732
if (d > max_d * 1.732) {
actionstream << "Player " << player->getName()
<< " tried to access " << what
<< " from too far: "
<< "d=" << d <<", max_d=" << max_d
<< ". ignoring." << std::endl;
// Call callbacks
m_script->on_cheat(playersao, "interacted_too_far");
return false;
}
return true;
}
void Server::handleCommand_Interact(NetworkPacket* pkt)
{
/*
@ -1380,33 +1423,13 @@ void Server::handleCommand_Interact(NetworkPacket* pkt)
*/
static const bool enable_anticheat = !g_settings->getBool("disable_anticheat");
if ((action == 0 || action == 2 || action == 3 || action == 4) &&
(enable_anticheat && !isSingleplayer())) {
enable_anticheat && !isSingleplayer()) {
float d = player_pos.getDistanceFrom(pointed_pos_under);
const ItemDefinition &playeritem_def =
playersao->getWieldedItem().getDefinition(m_itemdef);
float max_d = BS * playeritem_def.range;
InventoryList *hlist = playersao->getInventory()->getList("hand");
const ItemDefinition &hand_def =
hlist ? (hlist->getItem(0).getDefinition(m_itemdef)) : (m_itemdef->get(""));
float max_d_hand = BS * hand_def.range;
if (max_d < 0 && max_d_hand >= 0)
max_d = max_d_hand;
else if (max_d < 0)
max_d = BS * 4.0;
// cube diagonal: sqrt(3) = 1.73
if (d > max_d * 1.73) {
actionstream << "Player " << player->getName()
<< " tried to access " << pointed.dump()
<< " from too far: "
<< "d=" << d <<", max_d=" << max_d
<< ". ignoring." << std::endl;
if (!checkInteractDistance(player, d, pointed.dump())) {
// Re-send block to revert change on client-side
RemoteClient *client = getClient(pkt->getPeerId());
v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
client->SetBlockNotSent(blockpos);
// Call callbacks
m_script->on_cheat(playersao, "interacted_too_far");
// Do nothing else
return;
}
}
@ -1441,8 +1464,8 @@ void Server::handleCommand_Interact(NetworkPacket* pkt)
playersao->noCheatDigStart(p_under);
}
else if (pointed.type == POINTEDTHING_OBJECT) {
// Skip if object has been removed
if (pointed_object->m_removed)
// Skip if object can't be interacted with anymore
if (pointed_object->isGone())
return;
actionstream<<player->getName()<<" punches object "
@ -1600,8 +1623,8 @@ void Server::handleCommand_Interact(NetworkPacket* pkt)
if (pointed.type == POINTEDTHING_OBJECT) {
// Right click object
// Skip if object has been removed
if (pointed_object->m_removed)
// Skip if object can't be interacted with anymore
if (pointed_object->isGone())
return;
actionstream << player->getName() << " right-clicks object "

View File

@ -682,6 +682,8 @@ void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc
switch (drawtype) {
default:
case NDT_NORMAL:
material_type = (alpha == 255) ?
TILE_MATERIAL_OPAQUE : TILE_MATERIAL_ALPHA;
solidness = 2;
break;
case NDT_AIRLIKE:
@ -778,6 +780,16 @@ void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc
tile_shader[j] = shdsrc->getShader("nodes_shader",
material_type, drawtype);
}
u8 overlay_material = material_type;
if (overlay_material == TILE_MATERIAL_OPAQUE)
overlay_material = TILE_MATERIAL_BASIC;
else if (overlay_material == TILE_MATERIAL_LIQUID_OPAQUE)
overlay_material = TILE_MATERIAL_LIQUID_TRANSPARENT;
u32 overlay_shader[6];
for (u16 j = 0; j < 6; j++) {
overlay_shader[j] = shdsrc->getShader("nodes_shader",
overlay_material, drawtype);
}
// Tiles (fill in f->tiles[])
for (u16 j = 0; j < 6; j++) {
@ -786,8 +798,8 @@ void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc
tdef[j].backface_culling, material_type);
if (tdef_overlay[j].name != "")
fillTileAttribs(tsrc, &tiles[j].layers[1], &tdef_overlay[j],
tile_shader[j], tsettings.use_normal_texture,
tdef[j].backface_culling, material_type);
overlay_shader[j], tsettings.use_normal_texture,
tdef[j].backface_culling, overlay_material);
}
// Special tiles (fill in f->special_tiles[])

View File

@ -130,7 +130,9 @@ s32 PcgRandom::range(s32 min, s32 max)
if (max < min)
throw PrngException("Invalid range (max < min)");
u32 bound = max - min + 1;
// We have to cast to s64 because otherwise this could overflow,
// and signed overflow is undefined behavior.
u32 bound = (s64)max - (s64)min + 1;
return range(bound) + min;
}

View File

@ -290,6 +290,59 @@ ParticleSpawner::ParticleSpawner(IGameDef* gamedef, scene::ISceneManager *smgr,
ParticleSpawner::~ParticleSpawner() {}
void ParticleSpawner::spawnParticle(ClientEnvironment *env, float radius,
bool is_attached, const v3f &attached_pos, float attached_yaw)
{
v3f ppos = m_player->getPosition() / BS;
v3f pos = random_v3f(m_minpos, m_maxpos);
// Need to apply this first or the following check
// will be wrong for attached spawners
if (is_attached) {
pos.rotateXZBy(attached_yaw);
pos += attached_pos;
}
if (pos.getDistanceFrom(ppos) > radius)
return;
v3f vel = random_v3f(m_minvel, m_maxvel);
v3f acc = random_v3f(m_minacc, m_maxacc);
if (is_attached) {
// Apply attachment yaw
vel.rotateXZBy(attached_yaw);
acc.rotateXZBy(attached_yaw);
}
float exptime = rand() / (float)RAND_MAX
* (m_maxexptime - m_minexptime)
+ m_minexptime;
float size = rand() / (float)RAND_MAX
* (m_maxsize - m_minsize)
+ m_minsize;
m_particlemanager->addParticle(new Particle(
m_gamedef,
m_smgr,
m_player,
env,
pos,
vel,
acc,
exptime,
size,
m_collisiondetection,
m_collision_removal,
m_vertical,
m_texture,
v2f(0.0, 0.0),
v2f(1.0, 1.0),
m_animation,
m_glow
));
}
void ParticleSpawner::step(float dtime, ClientEnvironment* env)
{
m_time += dtime;
@ -311,122 +364,33 @@ void ParticleSpawner::step(float dtime, ClientEnvironment* env)
}
}
if (m_spawntime != 0) // Spawner exists for a predefined timespan
{
for(std::vector<float>::iterator i = m_spawntimes.begin();
i != m_spawntimes.end();)
{
if ((*i) <= m_time && m_amount > 0)
{
if (m_spawntime != 0) {
// Spawner exists for a predefined timespan
for (std::vector<float>::iterator i = m_spawntimes.begin();
i != m_spawntimes.end();) {
if ((*i) <= m_time && m_amount > 0) {
m_amount--;
// Pretend to, but don't actually spawn a particle if it is
// attached to an unloaded object or distant from player.
if (!unloaded) {
v3f ppos = m_player->getPosition() / BS;
v3f pos = random_v3f(m_minpos, m_maxpos);
if (!unloaded)
spawnParticle(env, radius, is_attached, attached_pos, attached_yaw);
if (pos.getDistanceFrom(ppos) <= radius) {
v3f vel = random_v3f(m_minvel, m_maxvel);
v3f acc = random_v3f(m_minacc, m_maxacc);
if (is_attached) {
// Apply attachment yaw and position
pos.rotateXZBy(attached_yaw);
pos += attached_pos;
vel.rotateXZBy(attached_yaw);
acc.rotateXZBy(attached_yaw);
}
float exptime = rand()/(float)RAND_MAX
*(m_maxexptime-m_minexptime)
+m_minexptime;
float size = rand()/(float)RAND_MAX
*(m_maxsize-m_minsize)
+m_minsize;
Particle* toadd = new Particle(
m_gamedef,
m_smgr,
m_player,
env,
pos,
vel,
acc,
exptime,
size,
m_collisiondetection,
m_collision_removal,
m_vertical,
m_texture,
v2f(0.0, 0.0),
v2f(1.0, 1.0),
m_animation,
m_glow);
m_particlemanager->addParticle(toadd);
}
}
i = m_spawntimes.erase(i);
}
else
{
} else {
++i;
}
}
}
else // Spawner exists for an infinity timespan, spawn on a per-second base
{
} else {
// Spawner exists for an infinity timespan, spawn on a per-second base
// Skip this step if attached to an unloaded object
if (unloaded)
return;
for (int i = 0; i <= m_amount; i++)
{
if (rand()/(float)RAND_MAX < dtime)
{
// Do not spawn particle if distant from player
v3f ppos = m_player->getPosition() / BS;
v3f pos = random_v3f(m_minpos, m_maxpos);
if (pos.getDistanceFrom(ppos) <= radius) {
v3f vel = random_v3f(m_minvel, m_maxvel);
v3f acc = random_v3f(m_minacc, m_maxacc);
if (is_attached) {
// Apply attachment yaw and position
pos.rotateXZBy(attached_yaw);
pos += attached_pos;
vel.rotateXZBy(attached_yaw);
acc.rotateXZBy(attached_yaw);
}
float exptime = rand()/(float)RAND_MAX
*(m_maxexptime-m_minexptime)
+m_minexptime;
float size = rand()/(float)RAND_MAX
*(m_maxsize-m_minsize)
+m_minsize;
Particle* toadd = new Particle(
m_gamedef,
m_smgr,
m_player,
env,
pos,
vel,
acc,
exptime,
size,
m_collisiondetection,
m_collision_removal,
m_vertical,
m_texture,
v2f(0.0, 0.0),
v2f(1.0, 1.0),
m_animation,
m_glow);
m_particlemanager->addParticle(toadd);
}
}
for (int i = 0; i <= m_amount; i++) {
if (rand() / (float)RAND_MAX < dtime)
spawnParticle(env, radius, is_attached, attached_pos, attached_yaw);
}
}
}

View File

@ -117,7 +117,7 @@ private:
class ParticleSpawner
{
public:
public:
ParticleSpawner(IGameDef* gamedef,
scene::ISceneManager *smgr,
LocalPlayer *player,
@ -144,8 +144,12 @@ class ParticleSpawner
bool get_expired ()
{ return (m_amount <= 0) && m_spawntime != 0; }
private:
ParticleManager* m_particlemanager;
private:
void spawnParticle(ClientEnvironment *env, float radius,
bool is_attached, const v3f &attached_pos,
float attached_yaw);
ParticleManager *m_particlemanager;
float m_time;
IGameDef *m_gamedef;
scene::ISceneManager *m_smgr;

View File

@ -32,6 +32,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <windows.h>
#include <wincrypt.h>
#include <algorithm>
#include <shlwapi.h>
#endif
#if !defined(_WIN32)
#include <unistd.h>
@ -182,20 +183,26 @@ bool detectMSVCBuildDir(const std::string &path)
std::string get_sysinfo()
{
#ifdef _WIN32
OSVERSIONINFO osvi;
std::ostringstream oss;
std::string tmp;
ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
GetVersionEx(&osvi);
tmp = osvi.szCSDVersion;
std::replace(tmp.begin(), tmp.end(), ' ', '_');
oss << "Windows/" << osvi.dwMajorVersion << "."
<< osvi.dwMinorVersion;
if (osvi.szCSDVersion[0])
oss << "-" << tmp;
oss << " ";
std::ostringstream oss;
LPSTR filePath = new char[MAX_PATH];
UINT blockSize;
VS_FIXEDFILEINFO *fixedFileInfo;
GetSystemDirectoryA(filePath, MAX_PATH);
PathAppendA(filePath, "kernel32.dll");
DWORD dwVersionSize = GetFileVersionInfoSizeA(filePath, NULL);
LPBYTE lpVersionInfo = new BYTE[dwVersionSize];
GetFileVersionInfoA(filePath, 0, dwVersionSize, lpVersionInfo);
VerQueryValueA(lpVersionInfo, "\\", (LPVOID *)&fixedFileInfo, &blockSize);
oss << "Windows/"
<< HIWORD(fixedFileInfo->dwProductVersionMS) << '.' // Major
<< LOWORD(fixedFileInfo->dwProductVersionMS) << '.' // Minor
<< HIWORD(fixedFileInfo->dwProductVersionLS) << ' '; // Build
#ifdef _WIN64
oss << "x86_64";
#else
@ -206,6 +213,9 @@ std::string get_sysinfo()
oss << "x86";
#endif
delete[] lpVersionInfo;
delete[] filePath;
return oss.str();
#else
struct utsname osinfo;

View File

@ -28,7 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
ReflowScan::ReflowScan(Map *map, INodeDefManager *ndef) :
m_map(map),
m_ndef(ndef),
m_liquid_queue(nullptr)
m_liquid_queue(NULL)
{
}
@ -42,7 +42,7 @@ void ReflowScan::scan(MapBlock *block, UniqueQueue<v3s16> *liquid_queue)
// scanned block. Blocks are only added to the lookup if they are really
// needed. The lookup is indexed manually to use the same index in a
// bit-array (of uint32 type) which stores for unloaded blocks that they
// were already fetched from Map but were actually nullptr.
// were already fetched from Map but were actually NULL.
memset(m_lookup, 0, sizeof(m_lookup));
int block_idx = 1 + (1 * 9) + (1 * 3);
m_lookup[block_idx] = block;

View File

@ -43,6 +43,7 @@ RemotePlayer::RemotePlayer(const char *name, IItemDefManager *idef):
m_last_chat_message_sent(time(NULL)),
m_chat_message_allowance(5.0f),
m_message_rate_overhead(0),
m_day_night_ratio_do_override(false),
hud_hotbar_image(""),
hud_hotbar_selected_image("")
{

View File

@ -196,6 +196,44 @@ v3f check_v3f(lua_State *L, int index)
return pos;
}
v3d read_v3d(lua_State *L, int index)
{
v3d pos;
CHECK_POS_TAB(index);
lua_getfield(L, index, "x");
pos.X = lua_tonumber(L, -1);
lua_pop(L, 1);
lua_getfield(L, index, "y");
pos.Y = lua_tonumber(L, -1);
lua_pop(L, 1);
lua_getfield(L, index, "z");
pos.Z = lua_tonumber(L, -1);
lua_pop(L, 1);
return pos;
}
v3d check_v3d(lua_State *L, int index)
{
v3d pos;
CHECK_POS_TAB(index);
lua_getfield(L, index, "x");
CHECK_POS_COORD("x");
pos.X = lua_tonumber(L, -1);
CHECK_FLOAT_RANGE(pos.X, "x")
lua_pop(L, 1);
lua_getfield(L, index, "y");
CHECK_POS_COORD("y");
pos.Y = lua_tonumber(L, -1);
CHECK_FLOAT_RANGE(pos.Y, "y")
lua_pop(L, 1);
lua_getfield(L, index, "z");
CHECK_POS_COORD("z");
pos.Z = lua_tonumber(L, -1);
CHECK_FLOAT_RANGE(pos.Z, "z")
lua_pop(L, 1);
return pos;
}
void push_ARGB8(lua_State *L, video::SColor color)
{
lua_newtable(L);
@ -234,15 +272,15 @@ void push_v3s16(lua_State *L, v3s16 p)
v3s16 read_v3s16(lua_State *L, int index)
{
// Correct rounding at <0
v3f pf = read_v3f(L, index);
return floatToInt(pf, 1.0);
v3d pf = read_v3d(L, index);
return doubleToInt(pf, 1.0);
}
v3s16 check_v3s16(lua_State *L, int index)
{
// Correct rounding at <0
v3f pf = check_v3f(L, index);
return floatToInt(pf, 1.0);
v3d pf = check_v3d(L, index);
return doubleToInt(pf, 1.0);
}
bool read_color(lua_State *L, int index, video::SColor *color)

View File

@ -178,9 +178,17 @@ void log_deprecated(lua_State *L, const std::string &message)
}
if (do_log) {
warningstream << message << std::endl;
// L can be NULL if we get called by log_deprecated(const std::string &msg)
// from scripting_game.cpp.
warningstream << message;
if (L) { // L can be NULL if we get called from scripting_game.cpp
lua_Debug ar;
if (!lua_getstack(L, 2, &ar))
FATAL_ERROR_IF(!lua_getstack(L, 1, &ar), "lua_getstack() failed");
FATAL_ERROR_IF(!lua_getinfo(L, "Sl", &ar), "lua_getinfo() failed");
warningstream << " (at " << ar.short_src << ":" << ar.currentline << ")";
}
warningstream << std::endl;
if (L) {
if (do_error)
script_error(L, LUA_ERRRUN, NULL, NULL);

View File

@ -119,6 +119,9 @@ ScriptApiBase::ScriptApiBase() :
m_environment = NULL;
m_guiengine = NULL;
// Make sure Lua uses the right locale
setlocale(LC_NUMERIC, "C");
}
ScriptApiBase::~ScriptApiBase()
@ -322,6 +325,10 @@ void ScriptApiBase::objectrefGetOrCreate(lua_State *L,
ObjectRef::create(L, cobj);
} else {
push_objectRef(L, cobj->getId());
if (cobj->isGone())
warningstream << "ScriptApiBase::objectrefGetOrCreate(): "
<< "Pushing ObjectRef to removed/deactivated object"
<< ", this is probably a bug." << std::endl;
}
}

View File

@ -69,7 +69,12 @@ bool ScriptApiItem::item_OnPlace(ItemStack &item,
// Call function
LuaItemStack::create(L, item);
objectrefGetOrCreate(L, placer);
if (!placer)
lua_pushnil(L);
else
objectrefGetOrCreate(L, placer);
pushPointedThing(pointed);
PCALL_RES(lua_pcall(L, 3, 1, error_handler));
if (!lua_isnil(L, -1)) {
@ -206,7 +211,8 @@ bool ScriptApiItem::item_CraftPredict(ItemStack &item, ServerActiveObject *user,
// function onto the stack
// If core.registered_items[name] doesn't exist, core.nodedef_default
// is tried instead so unknown items can still be manipulated to some degree
bool ScriptApiItem::getItemCallback(const char *name, const char *callbackname)
bool ScriptApiItem::getItemCallback(const char *name, const char *callbackname,
const v3s16 *p)
{
lua_State* L = getStack();
@ -217,10 +223,12 @@ bool ScriptApiItem::getItemCallback(const char *name, const char *callbackname)
lua_getfield(L, -1, name);
lua_remove(L, -2); // Remove registered_items
// Should be a table
if(lua_type(L, -1) != LUA_TTABLE)
{
if (lua_type(L, -1) != LUA_TTABLE) {
// Report error and clean up
errorstream << "Item \"" << name << "\" not defined" << std::endl;
errorstream << "Item \"" << name << "\" not defined";
if (p)
errorstream << " at position " << PP(*p);
errorstream << std::endl;
lua_pop(L, 1);
// Try core.nodedef_default instead

View File

@ -53,7 +53,7 @@ protected:
friend class LuaItemStack;
friend class ModApiItemMod;
bool getItemCallback(const char *name, const char *callbackname);
bool getItemCallback(const char *name, const char *callbackname, const v3s16 *p = NULL);
void pushPointedThing(const PointedThing& pointed);
};

View File

@ -107,7 +107,7 @@ bool ScriptApiNode::node_on_punch(v3s16 p, MapNode node,
INodeDefManager *ndef = getServer()->ndef();
// Push callback function on stack
if (!getItemCallback(ndef->get(node).name.c_str(), "on_punch"))
if (!getItemCallback(ndef->get(node).name.c_str(), "on_punch", &p))
return false;
// Call function
@ -130,7 +130,7 @@ bool ScriptApiNode::node_on_dig(v3s16 p, MapNode node,
INodeDefManager *ndef = getServer()->ndef();
// Push callback function on stack
if (!getItemCallback(ndef->get(node).name.c_str(), "on_dig"))
if (!getItemCallback(ndef->get(node).name.c_str(), "on_dig", &p))
return false;
// Call function
@ -151,7 +151,7 @@ void ScriptApiNode::node_on_construct(v3s16 p, MapNode node)
INodeDefManager *ndef = getServer()->ndef();
// Push callback function on stack
if (!getItemCallback(ndef->get(node).name.c_str(), "on_construct"))
if (!getItemCallback(ndef->get(node).name.c_str(), "on_construct", &p))
return;
// Call function
@ -169,7 +169,7 @@ void ScriptApiNode::node_on_destruct(v3s16 p, MapNode node)
INodeDefManager *ndef = getServer()->ndef();
// Push callback function on stack
if (!getItemCallback(ndef->get(node).name.c_str(), "on_destruct"))
if (!getItemCallback(ndef->get(node).name.c_str(), "on_destruct", &p))
return;
// Call function
@ -187,7 +187,7 @@ bool ScriptApiNode::node_on_flood(v3s16 p, MapNode node, MapNode newnode)
INodeDefManager *ndef = getServer()->ndef();
// Push callback function on stack
if (!getItemCallback(ndef->get(node).name.c_str(), "on_flood"))
if (!getItemCallback(ndef->get(node).name.c_str(), "on_flood", &p))
return false;
// Call function
@ -208,7 +208,7 @@ void ScriptApiNode::node_after_destruct(v3s16 p, MapNode node)
INodeDefManager *ndef = getServer()->ndef();
// Push callback function on stack
if (!getItemCallback(ndef->get(node).name.c_str(), "after_destruct"))
if (!getItemCallback(ndef->get(node).name.c_str(), "after_destruct", &p))
return;
// Call function
@ -227,7 +227,7 @@ bool ScriptApiNode::node_on_timer(v3s16 p, MapNode node, f32 dtime)
INodeDefManager *ndef = getServer()->ndef();
// Push callback function on stack
if (!getItemCallback(ndef->get(node).name.c_str(), "on_timer"))
if (!getItemCallback(ndef->get(node).name.c_str(), "on_timer", &p))
return false;
// Call function
@ -255,7 +255,7 @@ void ScriptApiNode::node_on_receive_fields(v3s16 p,
return;
// Push callback function on stack
if (!getItemCallback(ndef->get(node).name.c_str(), "on_receive_fields"))
if (!getItemCallback(ndef->get(node).name.c_str(), "on_receive_fields", &p))
return;
// Call function

View File

@ -45,7 +45,7 @@ int ScriptApiNodemeta::nodemeta_inventory_AllowMove(v3s16 p,
// Push callback function on stack
std::string nodename = ndef->get(node).name;
if (!getItemCallback(nodename.c_str(), "allow_metadata_inventory_move"))
if (!getItemCallback(nodename.c_str(), "allow_metadata_inventory_move", &p))
return count;
// function(pos, from_list, from_index, to_list, to_index, count, player)
@ -83,7 +83,7 @@ int ScriptApiNodemeta::nodemeta_inventory_AllowPut(v3s16 p,
// Push callback function on stack
std::string nodename = ndef->get(node).name;
if (!getItemCallback(nodename.c_str(), "allow_metadata_inventory_put"))
if (!getItemCallback(nodename.c_str(), "allow_metadata_inventory_put", &p))
return stack.count;
// Call function(pos, listname, index, stack, player)
@ -119,7 +119,7 @@ int ScriptApiNodemeta::nodemeta_inventory_AllowTake(v3s16 p,
// Push callback function on stack
std::string nodename = ndef->get(node).name;
if (!getItemCallback(nodename.c_str(), "allow_metadata_inventory_take"))
if (!getItemCallback(nodename.c_str(), "allow_metadata_inventory_take", &p))
return stack.count;
// Call function(pos, listname, index, count, player)
@ -156,7 +156,7 @@ void ScriptApiNodemeta::nodemeta_inventory_OnMove(v3s16 p,
// Push callback function on stack
std::string nodename = ndef->get(node).name;
if (!getItemCallback(nodename.c_str(), "on_metadata_inventory_move"))
if (!getItemCallback(nodename.c_str(), "on_metadata_inventory_move", &p))
return;
// function(pos, from_list, from_index, to_list, to_index, count, player)
@ -189,7 +189,7 @@ void ScriptApiNodemeta::nodemeta_inventory_OnPut(v3s16 p,
// Push callback function on stack
std::string nodename = ndef->get(node).name;
if (!getItemCallback(nodename.c_str(), "on_metadata_inventory_put"))
if (!getItemCallback(nodename.c_str(), "on_metadata_inventory_put", &p))
return;
// Call function(pos, listname, index, stack, player)
@ -220,7 +220,7 @@ void ScriptApiNodemeta::nodemeta_inventory_OnTake(v3s16 p,
// Push callback function on stack
std::string nodename = ndef->get(node).name;
if (!getItemCallback(nodename.c_str(), "on_metadata_inventory_take"))
if (!getItemCallback(nodename.c_str(), "on_metadata_inventory_take", &p))
return;
// Call function(pos, listname, index, stack, player)

View File

@ -249,9 +249,8 @@ void ScriptApiSecurity::initializeSecurityClient()
static const char *os_whitelist[] = {
"clock",
"date",
"difftime",
"time",
"setlocale",
"difftime",
"time"
};
static const char *debug_whitelist[] = {
"getinfo",

View File

@ -291,8 +291,7 @@ int ModApiEnvMod::l_place_node(lua_State *L)
pointed.type = POINTEDTHING_NODE;
pointed.node_abovesurface = pos;
pointed.node_undersurface = pos + v3s16(0,-1,0);
// Place it with a NULL placer (appears in Lua as a non-functional
// ObjectRef)
// Place it with a NULL placer (appears in Lua as nil)
bool success = scriptIfaceItem->item_OnPlace(item, NULL, pointed);
lua_pushboolean(L, success);
return 1;
@ -537,9 +536,11 @@ int ModApiEnvMod::l_get_objects_inside_radius(lua_State *L)
std::vector<u16>::const_iterator iter = ids.begin();
for(u32 i = 0; iter != ids.end(); ++iter) {
ServerActiveObject *obj = env->getActiveObject(*iter);
// Insert object reference into table
script->objectrefGetOrCreate(L, obj);
lua_rawseti(L, -2, ++i);
if (!obj->isGone()) {
// Insert object reference into table
script->objectrefGetOrCreate(L, obj);
lua_rawseti(L, -2, ++i);
}
}
return 1;
}
@ -651,38 +652,47 @@ int ModApiEnvMod::l_find_nodes_in_area(lua_State *L)
INodeDefManager *ndef = getServer(L)->ndef();
v3s16 minp = read_v3s16(L, 1);
v3s16 maxp = read_v3s16(L, 2);
sortBoxVerticies(minp, maxp);
v3s16 cube = maxp - minp + 1;
// Volume limit equal to 8 default mapchunks, (80 * 2) ^ 3 = 4,096,000
if ((u64)cube.X * (u64)cube.Y * (u64)cube.Z > 4096000) {
luaL_error(L, "find_nodes_in_area(): area volume"
" exceeds allowed value of 4096000");
return 0;
}
std::set<content_t> filter;
if(lua_istable(L, 3)) {
int table = 3;
if (lua_istable(L, 3)) {
lua_pushnil(L);
while(lua_next(L, table) != 0) {
while (lua_next(L, 3) != 0) {
// key at index -2 and value at index -1
luaL_checktype(L, -1, LUA_TSTRING);
ndef->getIds(lua_tostring(L, -1), filter);
// removes value, keeps key for next iteration
lua_pop(L, 1);
}
} else if(lua_isstring(L, 3)) {
} else if (lua_isstring(L, 3)) {
ndef->getIds(lua_tostring(L, 3), filter);
}
std::map<content_t, u16> individual_count;
UNORDERED_MAP<content_t, u32> individual_count;
lua_newtable(L);
u64 i = 0;
for (s16 x = minp.X; x <= maxp.X; x++)
for (s16 y = minp.Y; y <= maxp.Y; y++)
for (s16 z = minp.Z; z <= maxp.Z; z++) {
v3s16 p(x, y, z);
content_t c = env->getMap().getNodeNoEx(p).getContent();
if (filter.count(c) != 0) {
push_v3s16(L, p);
lua_rawseti(L, -2, ++i);
individual_count[c]++;
}
for (s16 y = minp.Y; y <= maxp.Y; y++)
for (s16 z = minp.Z; z <= maxp.Z; z++) {
v3s16 p(x, y, z);
content_t c = env->getMap().getNodeNoEx(p).getContent();
if (filter.count(c) != 0) {
push_v3s16(L, p);
lua_rawseti(L, -2, ++i);
individual_count[c]++;
}
}
lua_newtable(L);
for (std::set<content_t>::iterator it = filter.begin();
for (std::set<content_t>::const_iterator it = filter.begin();
it != filter.end(); ++it) {
lua_pushnumber(L, individual_count[*it]);
lua_setfield(L, -2, ndef->get(*it).name.c_str());
@ -706,12 +716,21 @@ int ModApiEnvMod::l_find_nodes_in_area_under_air(lua_State *L)
INodeDefManager *ndef = getServer(L)->ndef();
v3s16 minp = read_v3s16(L, 1);
v3s16 maxp = read_v3s16(L, 2);
sortBoxVerticies(minp, maxp);
v3s16 cube = maxp - minp + 1;
// Volume limit equal to 8 default mapchunks, (80 * 2) ^ 3 = 4,096,000
if ((u64)cube.X * (u64)cube.Y * (u64)cube.Z > 4096000) {
luaL_error(L, "find_nodes_in_area_under_air(): area volume"
" exceeds allowed value of 4096000");
return 0;
}
std::set<content_t> filter;
if (lua_istable(L, 3)) {
int table = 3;
lua_pushnil(L);
while(lua_next(L, table) != 0) {
while (lua_next(L, 3) != 0) {
// key at index -2 and value at index -1
luaL_checktype(L, -1, LUA_TSTRING);
ndef->getIds(lua_tostring(L, -1), filter);
@ -732,7 +751,7 @@ int ModApiEnvMod::l_find_nodes_in_area_under_air(lua_State *L)
for (; y <= maxp.Y; y++) {
v3s16 psurf(x, y + 1, z);
content_t csurf = env->getMap().getNodeNoEx(psurf).getContent();
if(c != CONTENT_AIR && csurf == CONTENT_AIR &&
if (c != CONTENT_AIR && csurf == CONTENT_AIR &&
filter.count(c) != 0) {
push_v3s16(L, v3s16(x, y, z));
lua_rawseti(L, -2, ++i);

View File

@ -325,8 +325,8 @@ int InvRef::l_room_for_item(lua_State *L)
return 1;
}
// contains_item(self, listname, itemstack or itemstring or table or nil) -> true/false
// Returns true if the list contains the given count of the given item name
// contains_item(self, listname, itemstack or itemstring or table or nil, [match_meta]) -> true/false
// Returns true if the list contains the given count of the given item
int InvRef::l_contains_item(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
@ -334,8 +334,11 @@ int InvRef::l_contains_item(lua_State *L)
const char *listname = luaL_checkstring(L, 2);
ItemStack item = read_item(L, 3, getServer(L)->idef());
InventoryList *list = getlist(L, ref, listname);
if(list){
lua_pushboolean(L, list->containsItem(item));
bool match_meta = false;
if (lua_isboolean(L, 4))
match_meta = lua_toboolean(L, 4);
if (list) {
lua_pushboolean(L, list->containsItem(item, match_meta));
} else {
lua_pushboolean(L, false);
}

View File

@ -93,7 +93,7 @@ private:
// Returns true if the item completely fits into the list
static int l_room_for_item(lua_State *L);
// contains_item(self, listname, itemstack or itemstring or table or nil) -> true/false
// contains_item(self, listname, itemstack or itemstring or table or nil, [match_meta]) -> true/false
// Returns true if the list contains the given count of the given item name
static int l_contains_item(lua_State *L);

View File

@ -95,7 +95,7 @@ int MetaDataRef::l_get_int(lua_State *L)
MAP_LOCK_REQUIRED;
MetaDataRef *ref = checkobject(L, 1);
std::string name = lua_tostring(L, 2);
std::string name = luaL_checkstring(L, 2);
Metadata *meta = ref->getmeta(false);
if (meta == NULL) {
@ -114,8 +114,8 @@ int MetaDataRef::l_set_int(lua_State *L)
MAP_LOCK_REQUIRED;
MetaDataRef *ref = checkobject(L, 1);
std::string name = lua_tostring(L, 2);
int a = lua_tointeger(L, 3);
std::string name = luaL_checkstring(L, 2);
int a = luaL_checkint(L, 3);
std::string str = itos(a);
Metadata *meta = ref->getmeta(true);
@ -133,7 +133,7 @@ int MetaDataRef::l_get_float(lua_State *L)
MAP_LOCK_REQUIRED;
MetaDataRef *ref = checkobject(L, 1);
std::string name = lua_tostring(L, 2);
std::string name = luaL_checkstring(L, 2);
Metadata *meta = ref->getmeta(false);
if (meta == NULL) {
@ -152,8 +152,8 @@ int MetaDataRef::l_set_float(lua_State *L)
MAP_LOCK_REQUIRED;
MetaDataRef *ref = checkobject(L, 1);
std::string name = lua_tostring(L, 2);
float a = lua_tonumber(L, 3);
std::string name = luaL_checkstring(L, 2);
float a = luaL_checknumber(L, 3);
std::string str = ftos(a);
Metadata *meta = ref->getmeta(true);

View File

@ -158,7 +158,7 @@ bool NodeMetaRef::handleFromTable(lua_State *L, int table, Metadata *_meta)
lua_pushnil(L);
while (lua_next(L, inventorytable) != 0) {
// key at index -2 and value at index -1
std::string name = lua_tostring(L, -2);
std::string name = luaL_checkstring(L, -2);
read_inventory_list(L, -1, inv, name.c_str(), getServer(L));
lua_pop(L, 1); // Remove value, keep key for next iteration
}

View File

@ -316,7 +316,7 @@ int LuaPerlinNoiseMap::l_getMapSlice(lua_State *L)
Noise *n = o->noise;
if (use_buffer)
lua_pushvalue(L, 3);
lua_pushvalue(L, 4);
else
lua_newtable(L);

View File

@ -23,6 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "lua_api/l_item.h"
#include "common/c_converter.h"
#include "common/c_content.h"
#include "util/cpp11_container.h"
#include "log.h"
#include "tool.h"
#include "serverobject.h"
@ -138,15 +139,15 @@ int ObjectRef::l_remove(lua_State *L)
return 0;
const UNORDERED_SET<int> &child_ids = co->getAttachmentChildIds();
UNORDERED_SET<int>::const_iterator it;
for (it = child_ids.begin(); it != child_ids.end(); ++it) {
for (UNORDERED_SET<int>::const_iterator it = child_ids.begin(); it != child_ids.end();
++it) {
// Child can be NULL if it was deleted earlier
if (ServerActiveObject *child = env->getActiveObject(*it))
child->setAttachment(0, "", v3f(0, 0, 0), v3f(0, 0, 0));
}
verbosestream<<"ObjectRef::l_remove(): id="<<co->getId()<<std::endl;
co->m_removed = true;
verbosestream << "ObjectRef::l_remove(): id=" << co->getId() << std::endl;
co->m_pending_removal = true;
return 0;
}

View File

@ -74,7 +74,7 @@ int ModApiServer::l_chat_send_all(lua_State *L)
// Get server from registry
Server *server = getServer(L);
// Send
server->notifyPlayers(narrow_to_wide(text));
server->notifyPlayers(utf8_to_wide(text));
return 0;
}
@ -88,7 +88,7 @@ int ModApiServer::l_chat_send_player(lua_State *L)
// Get server from registry
Server *server = getServer(L);
// Send
server->notifyPlayer(name, narrow_to_wide(text));
server->notifyPlayer(name, utf8_to_wide(text));
return 0;
}

View File

@ -36,6 +36,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "util/base64.h"
#include "config.h"
#include "version.h"
#include "util/hex.h"
#include "util/sha1.h"
#include <algorithm>
@ -354,6 +356,23 @@ int ModApiUtil::l_get_dir_list(lua_State *L)
return 1;
}
// safe_file_write(path, content)
int ModApiUtil::l_safe_file_write(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
const char *path = luaL_checkstring(L, 1);
size_t size;
const char *content = luaL_checklstring(L, 2, &size);
CHECK_SECURE_PATH(L, path, true);
bool ret = fs::safeWriteToFile(path, std::string(content, size));
lua_pushboolean(L, ret);
return 1;
}
// request_insecure_environment()
int ModApiUtil::l_request_insecure_environment(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
@ -422,6 +441,32 @@ int ModApiUtil::l_get_version(lua_State *L)
return 1;
}
int ModApiUtil::l_sha1(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
size_t size;
const char *data = luaL_checklstring(L, 1, &size);
bool hex = !lua_isboolean(L, 2) || !lua_toboolean(L, 2);
// Compute actual checksum of data
std::string data_sha1;
{
SHA1 ctx;
ctx.addBytes(data, size);
unsigned char *data_tmpdigest = ctx.getDigest();
data_sha1.assign((char*) data_tmpdigest, 20);
free(data_tmpdigest);
}
if (hex) {
std::string sha1_hex = hex_encode(data_sha1);
lua_pushstring(L, sha1_hex.c_str());
} else {
lua_pushlstring(L, data_sha1.data(), data_sha1.size());
}
return 1;
}
void ModApiUtil::Initialize(lua_State *L, int top)
{
@ -447,6 +492,7 @@ void ModApiUtil::Initialize(lua_State *L, int top)
API_FCT(mkdir);
API_FCT(get_dir_list);
API_FCT(safe_file_write);
API_FCT(request_insecure_environment);
@ -454,6 +500,7 @@ void ModApiUtil::Initialize(lua_State *L, int top)
API_FCT(decode_base64);
API_FCT(get_version);
API_FCT(sha1);
LuaSettings::create(L, g_settings, g_settings_path);
lua_setfield(L, top, "settings");
@ -479,6 +526,7 @@ void ModApiUtil::InitializeClient(lua_State *L, int top)
API_FCT(decode_base64);
API_FCT(get_version);
API_FCT(sha1);
}
void ModApiUtil::InitializeAsync(lua_State *L, int top)
@ -504,6 +552,7 @@ void ModApiUtil::InitializeAsync(lua_State *L, int top)
API_FCT(decode_base64);
API_FCT(get_version);
API_FCT(sha1);
LuaSettings::create(L, g_settings, g_settings_path);
lua_setfield(L, top, "settings");

Some files were not shown because too many files have changed in this diff Show More