Merge remote-tracking branch 'upstream/stable-0.4' into sync
This commit is contained in:
commit
695e833477
|
@ -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/
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -139,4 +139,7 @@ cmake .. \
|
|||
|
||||
make package -j $(nproc)
|
||||
|
||||
[ "x$NO_PACKAGE" = "x" ] && make package
|
||||
|
||||
exit 0
|
||||
# EOF
|
||||
|
|
|
@ -139,4 +139,7 @@ cmake .. \
|
|||
|
||||
make package -j $(nproc)
|
||||
|
||||
[ "x$NO_PACKAGE" = "x" ] && make package
|
||||
|
||||
exit 0
|
||||
# EOF
|
||||
|
|
|
@ -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'
|
||||
}
|
||||
|
|
|
@ -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 @@
|
||||
|
||||
--- 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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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 = ""
|
||||
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
|
||||
|
||||
if last_line ~= "" then
|
||||
last_line = 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
|
||||
|
||||
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
|
||||
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
|
||||
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,32 +593,15 @@ 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)
|
||||
function core.get_color_escape_sequence(color)
|
||||
return ESCAPE_CHAR .. "(c@" .. color .. ")"
|
||||
end
|
||||
end
|
||||
|
||||
function core.get_background_escape_sequence(color)
|
||||
function core.get_background_escape_sequence(color)
|
||||
return ESCAPE_CHAR .. "(b@" .. color .. ")"
|
||||
end
|
||||
end
|
||||
|
||||
function core.colorize(color, message)
|
||||
function core.colorize(color, message)
|
||||
local lines = tostring(message):split("\n", true)
|
||||
local color_code = core.get_color_escape_sequence(color)
|
||||
|
||||
|
@ -676,10 +610,9 @@ else
|
|||
end
|
||||
|
||||
return table.concat(lines, "\n") .. core.get_color_escape_sequence("#ffffff")
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
function core.strip_foreground_colors(str)
|
||||
return (str:gsub(ESCAPE_CHAR .. "%(c@[^)]+%)", ""))
|
||||
end
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
@ -195,6 +231,12 @@ function core.get_node_drops(nodename, toolname)
|
|||
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,29 +436,28 @@ 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)
|
||||
end
|
||||
local item = itemstack:take_item(cnt)
|
||||
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)
|
||||
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
|
||||
end
|
||||
-- If we reach this, adding the object to the
|
||||
-- environment failed
|
||||
end
|
||||
|
@ -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,8 +493,10 @@ end
|
|||
|
||||
function core.item_eat(hp_change, replace_with_item)
|
||||
return function(itemstack, user, pointed_thing) -- closure
|
||||
if user then
|
||||
return core.do_item_eat(hp_change, replace_with_item, itemstack, user, pointed_thing)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function core.node_punch(pos, node, puncher, pointed_thing)
|
||||
|
@ -434,10 +512,21 @@ 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)
|
||||
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,
|
||||
|
@ -447,35 +536,36 @@ function core.handle_node_drops(pos, drops, digger)
|
|||
core.add_item(p, left)
|
||||
end
|
||||
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())
|
||||
|
||||
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)
|
||||
|
@ -491,6 +581,7 @@ function core.node_dig(pos, node, digger)
|
|||
end
|
||||
end
|
||||
digger:set_wielded_item(wielded)
|
||||
end
|
||||
|
||||
-- Handle drops
|
||||
core.handle_node_drops(pos, drops, digger)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
-- 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)
|
||||
|
|
|
@ -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)))
|
||||
|
|
|
@ -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 .. ";"
|
||||
|
||||
|
|
|
@ -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 = {}
|
||||
|
|
|
@ -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 ..
|
||||
|
|
|
@ -133,7 +133,7 @@ local function instrument_register(func, func_name)
|
|||
return func(instrument {
|
||||
func = callback,
|
||||
func_name = register_name
|
||||
}), ...
|
||||
}, ...)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -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]
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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`
|
||||
|
|
283
doc/lua_api.txt
283
doc/lua_api.txt
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
* `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
|
||||
|
@ -4674,3 +4809,25 @@ The Biome API is still in an experimental phase and subject to change.
|
|||
-- ^ 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
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
Minetest Lua Mainmenu API Reference 0.4.16
|
||||
Minetest Lua Mainmenu API Reference 0.4.17
|
||||
========================================
|
||||
|
||||
Introduction
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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})
|
||||
endif(NOT APPLE)
|
||||
|
||||
# Prefer local iconv if installed
|
||||
find_library(ICONV_LIBRARY iconv)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -134,7 +134,6 @@ public:
|
|||
bool large_cave_is_flat;
|
||||
bool flooded;
|
||||
|
||||
s16 max_stone_y;
|
||||
v3s16 node_min;
|
||||
v3s16 node_max;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,7 +434,6 @@ 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;
|
||||
|
||||
if (envEvent.player_damage.send_to_server)
|
||||
|
@ -449,7 +445,6 @@ void Client::step(float dtime)
|
|||
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) {
|
||||
u16 breath = envEvent.player_breath.amount;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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(); }
|
||||
|
|
|
@ -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,15 +477,17 @@ 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) {
|
||||
for (std::vector<MeshBufList>::iterator it = lists.begin(); it != lists.end();
|
||||
++it) {
|
||||
timecheck_counter++;
|
||||
if (timecheck_counter > 50) {
|
||||
timecheck_counter = 0;
|
||||
int time2 = time(0);
|
||||
time_t time2 = time(0);
|
||||
if (time2 > time1 + 4) {
|
||||
infostream << "ClientMap::renderMap(): "
|
||||
"Rendering takes ages, returning."
|
||||
|
@ -497,18 +496,15 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
|
|||
}
|
||||
}
|
||||
|
||||
MeshBufList &list = *i;
|
||||
driver->setMaterial((*it).m);
|
||||
|
||||
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();
|
||||
for (std::vector<scene::IMeshBuffer*>::iterator it2 = (*it).bufs.begin();
|
||||
it2 != (*it).bufs.end(); ++it2) {
|
||||
driver->drawMeshBuffer(*it2);
|
||||
vertex_count += (*it2)->getVertexCount();
|
||||
meshbuffer_count++;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
} // ScopeProfiler
|
||||
|
||||
|
|
|
@ -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@"
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;*/
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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++) {
|
||||
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_data[vi].getContent() == dp.c_wall) {
|
||||
vm->m_flags[vi] |= VMANIP_FLAG_DUNGEON_UNTOUCHABLE;
|
||||
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)
|
||||
}
|
||||
} 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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -146,6 +146,9 @@ EmergeManager::~EmergeManager()
|
|||
}
|
||||
|
||||
delete thread;
|
||||
|
||||
// Mapgen init might not be finished if there is an error during startup.
|
||||
if (m_mapgens.size() > i)
|
||||
delete m_mapgens[i];
|
||||
}
|
||||
|
||||
|
@ -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));
|
||||
|
||||
/*
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -341,14 +341,33 @@ 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;
|
||||
}
|
||||
|
||||
if (font_config_prefix == "mono_") {
|
||||
const std::string &mono_font_path = m_settings->getDefault("mono_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,
|
||||
mono_font_path.c_str(), size, true, true,
|
||||
font_shadow, font_shadow_alpha);
|
||||
|
||||
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;
|
||||
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");
|
||||
|
||||
|
@ -356,20 +375,39 @@ 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;
|
||||
}
|
||||
|
||||
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
|
||||
#if defined(__ANDROID__) || defined(__IOS__)
|
||||
porting::notifyAbortLoading();
|
||||
#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;
|
||||
}
|
||||
|
|
63
src/game.cpp
63
src/game.cpp
|
@ -185,6 +185,7 @@ struct LocalFormspecHandler : public TextDest
|
|||
#endif
|
||||
|
||||
// Don't disable this part when modding is disabled, it's used in builtin
|
||||
if (m_client && m_client->getScript())
|
||||
m_client->getScript()->on_formspec_input(m_formname, fields);
|
||||
}
|
||||
|
||||
|
@ -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,7 +1323,8 @@ 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,
|
||||
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);
|
||||
|
@ -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,7 +3808,8 @@ void Game::handlePointingAtNothing(const ItemStack &playerItem)
|
|||
}
|
||||
|
||||
|
||||
void Game::handlePointingAtNode(const PointedThing &pointed, const ItemDefinition &playeritem_def,
|
||||
void Game::handlePointingAtNode(const PointedThing &pointed,
|
||||
const ItemDefinition &playeritem_def, const ItemStack &playeritem,
|
||||
const ToolCapabilities &playeritem_toolcap, f32 dtime)
|
||||
{
|
||||
v3s16 nodepos = pointed.node_undersurface;
|
||||
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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::ITexture *retval = m_driver->findTexture(name.c_str());
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
video::IImage *image = m_driver->createImageFromFile(name.c_str());
|
||||
if (image) {
|
||||
if (!image)
|
||||
return NULL;
|
||||
|
||||
image = Align2Npot2(image, m_driver);
|
||||
video::ITexture* retval = m_driver->addTexture(name.c_str(), image);
|
||||
retval = m_driver->addTexture(name.c_str(), image);
|
||||
image->drop();
|
||||
return retval;
|
||||
}
|
||||
#endif
|
||||
#else
|
||||
return m_driver->getTexture(name.c_str());
|
||||
#endif
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
|
|
@ -3683,19 +3683,25 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
|
|||
a->from_i = m_selected_item->i;
|
||||
m_invmgr->inventoryAction(a);
|
||||
} else if (craft_amount > 0) {
|
||||
assert(s.isValid());
|
||||
|
||||
// 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
|
||||
|
||||
// Send IACTION_CRAFT
|
||||
|
||||
assert(s.isValid());
|
||||
assert(inv_s);
|
||||
|
||||
// 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
|
||||
if (m_selected_amount == 0) {
|
||||
|
|
|
@ -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()) {
|
||||
|
|
22
src/hud.cpp
22
src/hud.cpp
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,7 +28,8 @@ void ItemStackMetadata::deSerialize(std::istream &is)
|
|||
|
||||
m_stringvars.clear();
|
||||
|
||||
if (!in.empty() && in[0] == DESERIALIZE_START) {
|
||||
if (!in.empty()) {
|
||||
if (in[0] == DESERIALIZE_START) {
|
||||
Strfnd fnd(in);
|
||||
fnd.to(1);
|
||||
while (!fnd.at_end()) {
|
||||
|
@ -40,4 +41,5 @@ void ItemStackMetadata::deSerialize(std::istream &is)
|
|||
// BACKWARDS COMPATIBILITY
|
||||
m_stringvars[""] = in;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
continue;
|
||||
// Get position of current standing node
|
||||
const v3s16 current_node = floatToInt(position - v3f(0, position_y_mod, 0), BS);
|
||||
|
||||
// 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;
|
||||
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;
|
||||
}
|
||||
|
||||
if (ok)
|
||||
// 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;
|
||||
|
||||
|
||||
// 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;
|
||||
m_sneak_node = p;
|
||||
new_sneak_node_exists = true;
|
||||
}
|
||||
|
||||
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
|
||||
// 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 true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#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,28 +338,85 @@ 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;
|
||||
|
||||
// (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,
|
||||
|
@ -322,18 +426,21 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d,
|
|||
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 (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);
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -377,8 +377,6 @@ public:
|
|||
*/
|
||||
ServerMapSector *createSector(v2s16 p);
|
||||
|
||||
bool saoPositionOverLimit(const v3f &p);
|
||||
|
||||
/*
|
||||
Blocks are generated by using these and makeBlock().
|
||||
*/
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,8 +1000,11 @@ void GenerateNotifier::getEvents(
|
|||
|
||||
event_map[name].push_back(gn.pos);
|
||||
}
|
||||
}
|
||||
|
||||
if (!peek_events)
|
||||
|
||||
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);
|
||||
}
|
||||
|
|
21
src/mapgen.h
21
src/mapgen.h
|
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -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(¶ms->np_terrain_base, seed, csize.X, csize.Z);
|
||||
noise_terrain_alt = new Noise(¶ms->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;
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 "
|
||||
|
|
|
@ -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[])
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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("")
|
||||
{
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -69,7 +69,12 @@ bool ScriptApiItem::item_OnPlace(ItemStack &item,
|
|||
|
||||
// Call function
|
||||
LuaItemStack::create(L, item);
|
||||
|
||||
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
|
||||
|
|
|
@ -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);
|
||||
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -250,8 +250,7 @@ void ScriptApiSecurity::initializeSecurityClient()
|
|||
"clock",
|
||||
"date",
|
||||
"difftime",
|
||||
"time",
|
||||
"setlocale",
|
||||
"time"
|
||||
};
|
||||
static const char *debug_whitelist[] = {
|
||||
"getinfo",
|
||||
|
|
|
@ -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,10 +536,12 @@ 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);
|
||||
if (!obj->isGone()) {
|
||||
// Insert object reference into table
|
||||
script->objectrefGetOrCreate(L, obj);
|
||||
lua_rawseti(L, -2, ++i);
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -651,22 +652,31 @@ 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;
|
||||
|
@ -682,7 +692,7 @@ int ModApiEnvMod::l_find_nodes_in_area(lua_State *L)
|
|||
}
|
||||
}
|
||||
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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
Loading…
Reference in New Issue