# This can be read from ${PROJECT_NAME} after project() is called
project(minetest)
-set(PROJECT_NAME_CAPITALIZED "Minetest")
+set(PROJECT_NAME_CAPITALIZED "Dragonfire")
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED TRUE)
set(VERSION_MAJOR 5)
set(VERSION_MINOR 6)
set(VERSION_PATCH 0)
-set(VERSION_EXTRA "" CACHE STRING "Stuff to append to version string")
+set(VERSION_EXTRA "dragonfire" CACHE STRING "Stuff to append to version string")
# Change to false for releases
-set(DEVELOPMENT_BUILD TRUE)
+set(DEVELOPMENT_BUILD FALSE)
set(VERSION_STRING "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}")
if(VERSION_EXTRA)
# IrrlichtMtConfig.cmake
message(FATAL_ERROR "Could not find IrrlichtMtConfig.cmake in ${IRRLICHTMT_BUILD_DIR}/cmake.")
endif()
- # This is done here so that relative search paths are more reasonable
elseif(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/lib/irrlichtmt")
message(STATUS "Using user-provided IrrlichtMt at subdirectory 'lib/irrlichtmt'")
if(BUILD_CLIENT)
include(MinetestFindIrrlichtHeaders)
if(NOT IRRLICHT_INCLUDE_DIR)
- message(FATAL_ERROR "Irrlicht or IrrlichtMt headers are required to build the server, but none found.\n${explanation_msg}")
+ message(FATAL_ERROR "IrrlichtMt headers are required to build the server, but none found.\n${explanation_msg}")
endif()
- message(STATUS "Found Irrlicht headers: ${IRRLICHT_INCLUDE_DIR}")
+ message(STATUS "Found IrrlichtMt headers: ${IRRLICHT_INCLUDE_DIR}")
add_library(IrrlichtMt::IrrlichtMt INTERFACE IMPORTED)
# Note that we can't use target_include_directories() since that doesn't work for IMPORTED targets before CMake 3.11
set_target_properties(IrrlichtMt::IrrlichtMt PROPERTIES
install(FILES "doc/minetest.6" "doc/minetestserver.6" DESTINATION "${MANDIR}/man6")
install(FILES "misc/net.minetest.minetest.desktop" DESTINATION "${XDG_APPS_DIR}")
install(FILES "misc/net.minetest.minetest.appdata.xml" DESTINATION "${APPDATADIR}")
- install(FILES "misc/minetest.svg" DESTINATION "${ICONDIR}/hicolor/scalable/apps")
- install(FILES "misc/minetest-xorg-icon-128.png"
+ install(FILES "misc/dragonfire.svg" DESTINATION "${ICONDIR}/hicolor/scalable/apps")
+ install(FILES "misc/dragonfire-xorg-icon-128.png"
DESTINATION "${ICONDIR}/hicolor/128x128/apps"
- RENAME "minetest.png")
+ RENAME "dragonfire.png")
endif()
if(APPLE)
- install(FILES "misc/minetest-icon.icns" DESTINATION "${SHAREDIR}")
+ install(FILES "misc/dragonfire-icon.icns" DESTINATION "${SHAREDIR}")
install(FILES "misc/Info.plist" DESTINATION "${BUNDLE_PATH}/Contents")
endif()
set(CPACK_CREATE_DESKTOP_LINKS ${PROJECT_NAME})
set(CPACK_PACKAGING_INSTALL_PREFIX "/${PROJECT_NAME_CAPITALIZED}")
- set(CPACK_WIX_PRODUCT_ICON "${CMAKE_CURRENT_SOURCE_DIR}/misc/minetest-icon.ico")
+ set(CPACK_WIX_PRODUCT_ICON "${CMAKE_CURRENT_SOURCE_DIR}/misc/dragonfire-icon.ico")
# Supported languages can be found at
# http://wixtoolset.org/documentation/manual/v3/wixui/wixui_localization.html
#set(CPACK_WIX_CULTURES "ar-SA,bg-BG,ca-ES,hr-HR,cs-CZ,da-DK,nl-NL,en-US,et-EE,fi-FI,fr-FR,de-DE")
In case you downloaded the source code
--------------------------------------
-If you downloaded the Minetest Engine source code in which this file is
+If you downloaded the Dragonfire Client source code in which this file is
contained, you probably want to download the [Minetest Game](https://github.com/minetest/minetest_game/)
project too. See its README.txt for more information.
Download source (this is the URL to the latest of source repository, which might not work at all times) using Git:
- git clone --depth 1 https://github.com/minetest/minetest.git
- cd minetest
+ git clone --depth 1 https://github.com/dragonfireclient/dragonfireclient
+ cd dragonfireclient
Download minetest_game (otherwise only the "Development Test" game is available) using Git:
- You can disable the client build by specifying `-DBUILD_CLIENT=FALSE`.
- You can select between Release and Debug build by `-DCMAKE_BUILD_TYPE=<Debug or Release>`.
- Debug build is slower, but gives much more useful output in a debugger.
- - If you build a bare server you don't need to have the Irrlicht or IrrlichtMt library installed.
- - In that case use `-DIRRLICHT_INCLUDE_DIR=/some/where/irrlicht/include`.
+ - If you build a bare server you don't need to compile IrrlichtMt, just the headers suffice.
+ - In that case use `-DIRRLICHT_INCLUDE_DIR=/some/where/irrlichtmt/include`.
- Minetest will use the IrrlichtMt package that is found first, given by the following order:
1. Specified `IRRLICHTMT_BUILD_DIR` CMake variable
--------------------------------------------------------------------------------
function string:trim()
- return (self:gsub("^%s*(.-)%s*$", "%1"))
+ return self:match("^%s*(.-)%s*$")
end
--------------------------------------------------------------------------------
return math.ceil(x - 0.5)
end
-
+ local formspec_escapes = {
+ ["\\"] = "\\\\",
+ ["["] = "\\[",
+ ["]"] = "\\]",
+ [";"] = "\\;",
+ [","] = "\\,"
+ }
function core.formspec_escape(text)
- if text ~= nil then
- text = string.gsub(text,"\\","\\\\")
- text = string.gsub(text,"%]","\\]")
- text = string.gsub(text,"%[","\\[")
- text = string.gsub(text,";","\\;")
- text = string.gsub(text,",","\\,")
- end
- return text
+ -- Use explicit character set instead of dot here because it doubles the performance
+ return text and text:gsub("[\\%[%];,]", formspec_escapes)
end
return as_table and {text} or text
end
- 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
+ local line_length = 0
+ for word in text:gmatch("%S+") do
+ if line_length > 0 and line_length + #word + 1 >= max_length then
-- word wouldn't fit on current line, move to next line
- table.insert(result, table.concat(line, ' '))
- line = {}
+ table.insert(result, table.concat(line, " "))
+ line = {word}
+ line_length = #word
+ else
+ table.insert(line, word)
+ line_length = line_length + 1 + #word
end
- table.insert(line, word)
end
- table.insert(result, table.concat(line, ' '))
- return as_table and result or table.concat(result, '\n')
+ table.insert(result, table.concat(line, " "))
+ return as_table and result or table.concat(result, "\n")
end
--------------------------------------------------------------------------------
return nil
end
- local x, y, z = string.match(value, "^([%d.-]+)[, ] *([%d.-]+)[, ] *([%d.-]+)$")
- if x and y and z then
- x = tonumber(x)
- y = tonumber(y)
- z = tonumber(z)
- return vector.new(x, y, z)
- end
- x, y, z = string.match(value, "^%( *([%d.-]+)[, ] *([%d.-]+)[, ] *([%d.-]+) *%)$")
+ value = value:match("^%((.-)%)$") or value -- strip parentheses
+
+ local x, y, z = value:trim():match("^([%d.-]+)[,%s]%s*([%d.-]+)[,%s]%s*([%d.-]+)$")
if x and y and z then
x = tonumber(x)
y = tonumber(y)
z = tonumber(z)
return vector.new(x, y, z)
end
+
return nil
end
--------------------------------------------------------------------------------
- function core.string_to_area(value)
- local p1, p2 = unpack(value:split(") ("))
- if p1 == nil or p2 == nil then
- return nil
- end
- p1 = core.string_to_pos(p1 .. ")")
- p2 = core.string_to_pos("(" .. p2)
- if p1 == nil or p2 == nil then
- return nil
+ do
+ local rel_num_cap = "(~?-?%d*%.?%d*)" -- may be overly permissive as this will be tonumber'ed anyways
+ local num_delim = "[,%s]%s*"
+ local pattern = "^" .. table.concat({rel_num_cap, rel_num_cap, rel_num_cap}, num_delim) .. "$"
+
+ local function parse_area_string(pos, relative_to)
+ local pp = {}
+ pp.x, pp.y, pp.z = pos:trim():match(pattern)
+ return core.parse_coordinates(pp.x, pp.y, pp.z, relative_to)
end
- return p1, p2
- end
+ function core.string_to_area(value, relative_to)
+ local p1, p2 = value:match("^%((.-)%)%s*%((.-)%)$")
+ if not p1 then
+ return
+ end
- local function test_string_to_area()
- local p1, p2 = core.string_to_area("(10.0, 5, -2) ( 30.2, 4, -12.53)")
- assert(p1.x == 10.0 and p1.y == 5 and p1.z == -2)
- assert(p2.x == 30.2 and p2.y == 4 and p2.z == -12.53)
+ p1 = parse_area_string(p1, relative_to)
+ p2 = parse_area_string(p2, relative_to)
- p1, p2 = core.string_to_area("(10.0, 5, -2 30.2, 4, -12.53")
- assert(p1 == nil and p2 == nil)
+ if p1 == nil or p2 == nil then
+ return
+ end
- p1, p2 = core.string_to_area("(10.0, 5,) -2 fgdf2, 4, -12.53")
- assert(p1 == nil and p2 == nil)
+ return p1, p2
+ end
end
- test_string_to_area()
-
--------------------------------------------------------------------------------
function table.copy(t, seen)
local n = {}
end
+function table.combine(t, other)
+ other = other or {}
+ for k, v in pairs(other) do
+ if type(v) == "table" and type(t[k]) == "table" then
+ table.combine(t[k], v)
+ else
+ t[k] = v
+ end
+ end
+end
+
--------------------------------------------------------------------------------
-- mainmenu only functions
--------------------------------------------------------------------------------
return table.concat(lines, "\n") .. core.get_color_escape_sequence("#ffffff")
end
+local function rgb_to_hex(rgb)
+ local hexadecimal = "#"
+
+ for key, value in pairs(rgb) do
+ local hex = ""
+
+ while(value > 0)do
+ local index = math.fmod(value, 16) + 1
+ value = math.floor(value / 16)
+ hex = string.sub("0123456789ABCDEF", index, index) .. hex
+ end
+
+ if(string.len(hex) == 0)then
+ hex = "00"
+ elseif(string.len(hex) == 1)then
+ hex = "0" .. hex
+ end
+
+ hexadecimal = hexadecimal .. hex
+ end
+
+ return hexadecimal
+end
+
+local function color_from_hue(hue)
+ local h = hue / 60
+ local c = 255
+ local x = (1 - math.abs(h % 2 - 1)) * 255
+
+ local i = math.floor(h)
+ if i == 0 then
+ return rgb_to_hex({c, x, 0})
+ elseif i == 1 then
+ return rgb_to_hex({x, c, 0})
+ elseif i == 2 then
+ return rgb_to_hex({0, c, x})
+ elseif i == 3 then
+ return rgb_to_hex({0, x, c})
+ elseif i == 4 then
+ return rgb_to_hex({x, 0, c})
+ else
+ return rgb_to_hex({c, 0, x})
+ end
+end
+
+function core.rainbow(input)
+ local step = 360 / input:len()
+ local hue = 0
+ local output = ""
+ for i = 1, input:len() do
+ local char = input:sub(i, i)
+ if char:match("%s") then
+ output = output .. char
+ else
+ output = output .. core.get_color_escape_sequence(color_from_hue(hue)) .. char
+ end
+ hue = hue + step
+ end
+ return output
+end
function core.strip_foreground_colors(str)
return (str:gsub(ESCAPE_CHAR .. "%(c@[^)]+%)", ""))
return function(str, ...) return core.translate(textdomain or "", str, ...) end
end
+function core.get_pointed_thing_position(pointed_thing, above)
+ if pointed_thing.type == "node" then
+ if above then
+ -- The position where a node would be placed
+ return pointed_thing.above
+ end
+ -- The position where a node would be dug
+ return pointed_thing.under
+ elseif pointed_thing.type == "object" then
+ return pointed_thing.ref and pointed_thing.ref:get_pos()
+ end
+end
+
--------------------------------------------------------------------------------
-- Returns the exact coordinate of a pointed surface
--------------------------------------------------------------------------------
return number ~= number
end
+ --[[ Helper function for parsing an optionally relative number
+ of a chat command parameter, using the chat command tilde notation.
+
+ Parameters:
+ * arg: String snippet containing the number; possible values:
+ * "<number>": return as number
+ * "~<number>": return relative_to + <number>
+ * "~": return relative_to
+ * Anything else will return `nil`
+ * relative_to: Number to which the `arg` number might be relative to
+
+ Returns:
+ A number or `nil`, depending on `arg.
+
+ Examples:
+ * `core.parse_relative_number("5", 10)` returns 5
+ * `core.parse_relative_number("~5", 10)` returns 15
+ * `core.parse_relative_number("~", 10)` returns 10
+ ]]
+ function core.parse_relative_number(arg, relative_to)
+ if not arg then
+ return nil
+ elseif arg == "~" then
+ return relative_to
+ elseif string.sub(arg, 1, 1) == "~" then
+ local number = tonumber(string.sub(arg, 2))
+ if not number then
+ return nil
+ end
+ if core.is_nan(number) or number == math.huge or number == -math.huge then
+ return nil
+ end
+ return relative_to + number
+ else
+ local number = tonumber(arg)
+ if core.is_nan(number) or number == math.huge or number == -math.huge then
+ return nil
+ end
+ return number
+ end
+ end
+
+ --[[ Helper function to parse coordinates that might be relative
+ to another position; supports chat command tilde notation.
+ Intended to be used in chat command parameter parsing.
+
+ Parameters:
+ * x, y, z: Parsed x, y, and z coordinates as strings
+ * relative_to: Position to which to compare the position
+
+ Syntax of x, y and z:
+ * "<number>": return as number
+ * "~<number>": return <number> + player position on this axis
+ * "~": return player position on this axis
+
+ Returns: a vector or nil for invalid input or if player does not exist
+ ]]
+ function core.parse_coordinates(x, y, z, relative_to)
+ if not relative_to then
+ x, y, z = tonumber(x), tonumber(y), tonumber(z)
+ return x and y and z and { x = x, y = y, z = z }
+ end
+ local rx = core.parse_relative_number(x, relative_to.x)
+ local ry = core.parse_relative_number(y, relative_to.y)
+ local rz = core.parse_relative_number(z, relative_to.z)
+ return rx and ry and rz and { x = rx, y = ry, z = rz }
+ end
++
+function core.inventorycube(img1, img2, img3)
+ img2 = img2 or img1
+ img3 = img3 or img1
+ return "[inventorycube"
+ .. "{" .. img1:gsub("%^", "&")
+ .. "{" .. img2:gsub("%^", "&")
+ .. "{" .. img3:gsub("%^", "&")
+end
max_drop_level=1,
groupcaps={
crumbly={maxlevel=2, uses=20, times={[1]=1.60, [2]=1.20, [3]=0.80}}
- }
+ },
damage_groups = {fleshy=2},
}
`minetest.get_color_escape_sequence(color) ..
message ..
minetest.get_color_escape_sequence("#ffffff")`
+* `minetest.rainbow(message)`:
+ * Rainbow colorizes the message.
* `minetest.get_background_escape_sequence(color)`
* `color` is a ColorString
* The escape sequence sets the background of the whole text element to
* `minetest.string_to_pos(string)`: returns a position or `nil`
* Same but in reverse.
* If the string can't be parsed to a position, nothing is returned.
- * `minetest.string_to_area("(X1, Y1, Z1) (X2, Y2, Z2)")`: returns two positions
+ * `minetest.string_to_area("(X1, Y1, Z1) (X2, Y2, Z2)", relative_to)`:
+ * returns two positions
* Converts a string representing an area box into two positions
+ * X1, Y1, ... Z2 are coordinates
+ * `relative_to`: Optional. If set to a position, each coordinate
+ can use the tilde notation for relative positions
+ * Tilde notation: "~": Relative coordinate
+ "~<number>": Relative coordinate plus <number>
+ * Example: `minetest.string_to_area("(1,2,3) (~5,~-5,~)", {x=10,y=10,z=10})`
+ returns `{x=1,y=2,z=3}, {x=15,y=5,z=10}`
* `minetest.formspec_escape(string)`: returns a string
* escapes the characters "[", "]", "\", "," and ";", which can not be used
in formspecs.
* `intensity` sets the intensity of the shadows from 0 (no shadows, default) to 1 (blackness)
* `get_lighting()`: returns the current state of lighting for the player.
* Result is a table with the same fields as `light_definition` in `set_lighting`.
+ * `respawn()`: Respawns the player using the same mechanism as the death screen,
+ including calling on_respawnplayer callbacks.
`PcgRandom`
-----------
spawn_by = "default:water",
-- Node (or list of nodes) that the decoration only spawns next to.
- -- Checks two horizontal planes of 8 neighbouring nodes (including
- -- diagonal neighbours), one plane level with the 'place_on' node and a
- -- plane one node above that.
+ -- Checks the 8 neighbouring nodes on the same Y, and also the ones
+ -- at Y+1, excluding both center nodes.
num_spawn_by = 1,
-- Number of spawn_by nodes that must be surrounding the decoration
#include "filesys.h"
#include "mapblock_mesh.h"
#include "mapblock.h"
+#include "mapsector.h"
#include "minimap.h"
#include "modchannels.h"
#include "content/mods.h"
bool ipv6,
GameUI *game_ui
):
+ m_mesh_update_thread(this),
m_tsrc(tsrc),
m_shsrc(shsrc),
m_itemdef(itemdef),
m_sound(sound),
m_event(event),
m_rendering_engine(rendering_engine),
- m_mesh_update_thread(this),
m_env(
new ClientMap(this, rendering_engine, control, 666),
tsrc, this
// Run a callback when mods are loaded
m_script->on_mods_loaded();
+ m_script->init_cheats();
+
// Create objects if they're ready
if (m_state == LC_Ready)
m_script->on_client_ready(m_env.getLocalPlayer());
if (envEvent.type == CEE_PLAYER_DAMAGE) {
u16 damage = envEvent.player_damage.amount;
- if (envEvent.player_damage.send_to_server)
+ if (envEvent.player_damage.send_to_server && ! g_settings->getBool("prevent_natural_damage"))
sendDamage(damage);
// Add to ClientEvent queue
{
int num_processed_meshes = 0;
std::vector<v3s16> blocks_to_ack;
+ bool force_update_shadows = false;
while (!m_mesh_update_thread.m_queue_out.empty())
{
num_processed_meshes++;
if (is_empty)
delete r.mesh;
- else
+ else {
// Replace with the new mesh
block->mesh = r.mesh;
+ force_update_shadows = true;
+ }
}
} else {
delete r.mesh;
if (num_processed_meshes > 0)
g_profiler->graphAdd("num_processed_meshes", num_processed_meshes);
+
+ auto shadow_renderer = RenderingEngine::get_shadow_renderer();
+ if (shadow_renderer && force_update_shadows)
+ shadow_renderer->setForceUpdateShadowMap();
}
/*
m_access_denied = true;
if (timeout)
m_access_denied_reason = gettext("Connection timed out.");
- else
+ else if (m_access_denied_reason.empty())
m_access_denied_reason = gettext("Connection aborted (protocol error?).");
}
// Will fill up 12 + 12 + 4 + 4 + 4 bytes
void writePlayerPos(LocalPlayer *myplayer, ClientMap *clientMap, NetworkPacket *pkt)
{
- v3f pf = myplayer->getPosition() * 100;
- v3f sf = myplayer->getSpeed() * 100;
+ v3f pf = myplayer->getLegitPosition() * 100;
+ v3f sf = myplayer->getSendSpeed() * 100;
s32 pitch = myplayer->getPitch() * 100;
s32 yaw = myplayer->getYaw() * 100;
u32 keyPressed = myplayer->control.getKeysPressed();
Send(&pkt);
}
-void Client::sendPlayerPos()
+void Client::sendPlayerPos(v3f pos)
{
LocalPlayer *player = m_env.getLocalPlayer();
if (!player)
u32 keyPressed = player->control.getKeysPressed();
if (
- player->last_position == player->getPosition() &&
- player->last_speed == player->getSpeed() &&
+ player->last_position == pos &&
+ player->last_speed == player->getSendSpeed() &&
player->last_pitch == player->getPitch() &&
player->last_yaw == player->getYaw() &&
player->last_keyPressed == keyPressed &&
player->last_wanted_range == wanted_range)
return;
- player->last_position = player->getPosition();
- player->last_speed = player->getSpeed();
+ player->last_position = pos;
+ player->last_speed = player->getSendSpeed();
player->last_pitch = player->getPitch();
player->last_yaw = player->getYaw();
player->last_keyPressed = keyPressed;
Send(&pkt);
}
+void Client::sendPlayerPos()
+{
+ LocalPlayer *player = m_env.getLocalPlayer();
+ if (!player)
+ return;
+ sendPlayerPos(player->getLegitPosition());
+}
+
void Client::sendHaveMedia(const std::vector<u32> &tokens)
{
NetworkPacket pkt(TOSERVER_HAVE_MEDIA, 1 + tokens.size() * 4);
case InventoryLocation::UNDEFINED:
{}
break;
+ case InventoryLocation::PLAYER:
case InventoryLocation::CURRENT_PLAYER:
{
LocalPlayer *player = m_env.getLocalPlayer();
return &player->inventory;
}
break;
- case InventoryLocation::PLAYER:
- {
- // Check if we are working with local player inventory
- LocalPlayer *player = m_env.getLocalPlayer();
- if (!player || strcmp(player->getName(), loc.name.c_str()) != 0)
- return NULL;
- return &player->inventory;
- }
- break;
case InventoryLocation::NODEMETA:
{
NodeMetadata *meta = m_env.getMap().getNodeMetadata(loc.p);
addUpdateMeshTask(blockpos + v3s16(0, 0, -1), false, urgent);
}
+void Client::updateAllMapBlocks()
+{
+ v3s16 currentBlock = getNodeBlockPos(floatToInt(m_env.getLocalPlayer()->getPosition(), BS));
+
+ for (s16 X = currentBlock.X - 2; X <= currentBlock.X + 2; X++)
+ for (s16 Y = currentBlock.Y - 2; Y <= currentBlock.Y + 2; Y++)
+ for (s16 Z = currentBlock.Z - 2; Z <= currentBlock.Z + 2; Z++)
+ addUpdateMeshTask(v3s16(X, Y, Z), false, true);
+
+ Map &map = m_env.getMap();
+
+ std::vector<v3s16> positions;
+ map.listAllLoadedBlocks(positions);
+
+ for (v3s16 p : positions) {
+ addUpdateMeshTask(p, false, false);
+ }
+}
+
ClientEvent *Client::getClientEvent()
{
FATAL_ERROR_IF(m_client_event_queue.empty(),
{
return m_itemdef;
}
+IWritableItemDefManager* Client::getWritableItemDefManager()
+{
+ return m_itemdef;
+}
const NodeDefManager* Client::getNodeDefManager()
{
return m_nodedef;
}
+NodeDefManager* Client::getWritableNodeDefManager()
+{
+ return m_nodedef;
+}
ICraftDefManager* Client::getCraftDefManager()
{
return NULL;
void MeshMakeData::setSmoothLighting(bool smooth_lighting)
{
- m_smooth_lighting = smooth_lighting;
+ m_smooth_lighting = smooth_lighting && ! g_settings->getBool("fullbright");
}
/*
u8 light = n.getLight(bank, ndef);
if (light > 0)
light = rangelim(light + increment, 0, LIGHT_SUN);
+ if(g_settings->getBool("fullbright"))
+ return 255;
return decode_light(light);
}
ndef->get(n2).light_source);
if(light_source > light)
light = light_source;
-
+ if(g_settings->getBool("fullbright"))
+ return 255;
return decode_light(light);
}
u8 c1 = f1.solidness;
u8 c2 = f2.solidness;
+
if (c1 == c2)
return 0;
else if (c2 == 0)
c2 = f2.visual_solidness;
+
if (c1 == c2) {
*equivalent = true;
// If same solidness, liquid takes precense
tile.rotation = tile.world_aligned ? 0 : dir_to_tile[tile_index + 1];
}
+std::set<content_t> splitToContentT(std::string str, const NodeDefManager *ndef)
+{
+ str += "\n";
+ std::set<content_t> dat;
+ std::string buf;
+ for (char c : str) {
+ if (c == ',' || c == '\n') {
+ if (! buf.empty()) {
+ dat.insert(ndef->getId(buf));
+ }
+ buf.clear();
+ } else if (c != ' ') {
+ buf += c;
+ }
+ }
+ return dat;
+}
+
static void getTileInfo(
// Input:
MeshMakeData *data,
v3s16 &face_dir_corrected,
u16 *lights,
u8 &waving,
- TileSpec &tile
- )
+ TileSpec &tile,
+ // lol more Input
+ bool xray,
+ std::set<content_t> xraySet)
{
VoxelManipulator &vmanip = data->m_vmanip;
const NodeDefManager *ndef = data->m_client->ndef();
const MapNode &n0 = vmanip.getNodeRefUnsafe(blockpos_nodes + p);
+ content_t c0 = n0.getContent();
+ if (xray && xraySet.find(c0) != xraySet.end())
+ c0 = CONTENT_AIR;
// Don't even try to get n1 if n0 is already CONTENT_IGNORE
- if (n0.getContent() == CONTENT_IGNORE) {
+ if (c0 == CONTENT_IGNORE) {
makes_face = false;
return;
}
const MapNode &n1 = vmanip.getNodeRefUnsafeCheckFlags(blockpos_nodes + p + face_dir);
- if (n1.getContent() == CONTENT_IGNORE) {
+ content_t c1 = n1.getContent();
+ if (xray && xraySet.find(c1) != xraySet.end())
+ c1 = CONTENT_AIR;
+
+ if (c1 == CONTENT_IGNORE) {
makes_face = false;
return;
}
// This is hackish
bool equivalent = false;
- u8 mf = face_contents(n0.getContent(), n1.getContent(),
+ u8 mf = face_contents(c0, c1,
&equivalent, ndef);
if (mf == 0) {
v3s16 translate_dir,
const v3f &&translate_dir_f,
const v3s16 &&face_dir,
- std::vector<FastFace> &dest)
+ std::vector<FastFace> &dest,
+ bool xray,
+ std::set<content_t> xraySet)
{
static thread_local const bool waving_liquids =
g_settings->getBool("enable_shaders") &&
// Get info of first tile
getTileInfo(data, p, face_dir,
makes_face, p_corrected, face_dir_corrected,
- lights, waving, tile);
+ lights, waving, tile, xray, xraySet);
// Unroll this variable which has a significant build cost
TileSpec next_tile;
next_makes_face, next_p_corrected,
next_face_dir_corrected, next_lights,
waving,
- next_tile);
+ next_tile,
+ xray,
+ xraySet);
if (!force_not_tiling
&& next_makes_face == makes_face
}
static void updateAllFastFaceRows(MeshMakeData *data,
- std::vector<FastFace> &dest)
+ std::vector<FastFace> &dest, bool xray, std::set<content_t> xraySet)
{
/*
Go through every y,z and get top(y+) faces in rows of x+
v3s16(1, 0, 0), //dir
v3f (1, 0, 0),
v3s16(0, 1, 0), //face dir
- dest);
+ dest,
+ xray,
+ xraySet);
/*
Go through every x,y and get right(x+) faces in rows of z+
v3s16(0, 0, 1), //dir
v3f (0, 0, 1),
v3s16(1, 0, 0), //face dir
- dest);
+ dest,
+ xray,
+ xraySet);
/*
Go through every y,z and get back(z+) faces in rows of x+
v3s16(1, 0, 0), //dir
v3f (1, 0, 0),
v3s16(0, 0, 1), //face dir
- dest);
+ dest,
+ xray,
+ xraySet);
}
static void applyTileColor(PreMeshBuffer &pmb)
std::vector<FastFace> fastfaces_new;
fastfaces_new.reserve(512);
+ /*
+ X-Ray
+ */
+ bool xray = g_settings->getBool("xray");
+ std::set<content_t> xraySet, nodeESPSet;
+ if (xray)
+ xraySet = splitToContentT(g_settings->get("xray_nodes"), data->m_client->ndef());
+
+ nodeESPSet = splitToContentT(g_settings->get("node_esp_nodes"), data->m_client->ndef());
/*
We are including the faces of the trailing edges of the block.
{
// 4-23ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
//TimeTaker timer2("updateAllFastFaceRows()");
- updateAllFastFaceRows(data, fastfaces_new);
+ updateAllFastFaceRows(data, fastfaces_new, xray, xraySet);
}
// End of slow part
+ /*
+ NodeESP
+ */
+ {
+ v3s16 blockpos_nodes = data->m_blockpos * MAP_BLOCKSIZE;
+ for (s16 x = 0; x < MAP_BLOCKSIZE; x++) {
+ for (s16 y = 0; y < MAP_BLOCKSIZE; y++) {
+ for (s16 z = 0; z < MAP_BLOCKSIZE; z++) {
+ v3s16 pos = v3s16(x, y, z) + blockpos_nodes;
+ const MapNode &node = data->m_vmanip.getNodeRefUnsafeCheckFlags(pos);
+ if (nodeESPSet.find(node.getContent()) != nodeESPSet.end())
+ esp_nodes.insert(pos);
+ }
+ }
+ }
+ }
+
/*
Convert FastFaces to MeshCollector
*/
scene::SMeshBuffer *buf = new scene::SMeshBuffer();
buf->Material = material;
- switch (p.layer.material_type) {
- // list of transparent materials taken from tile.h
- case TILE_MATERIAL_ALPHA:
- case TILE_MATERIAL_LIQUID_TRANSPARENT:
- case TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT:
- {
- buf->append(&p.vertices[0], p.vertices.size(),
- &p.indices[0], 0);
-
- MeshTriangle t;
- t.buffer = buf;
- for (u32 i = 0; i < p.indices.size(); i += 3) {
- t.p1 = p.indices[i];
- t.p2 = p.indices[i + 1];
- t.p3 = p.indices[i + 2];
- t.updateAttributes();
- m_transparent_triangles.push_back(t);
- }
+ if (p.layer.isTransparent()) {
+ buf->append(&p.vertices[0], p.vertices.size(), nullptr, 0);
+
+ MeshTriangle t;
+ t.buffer = buf;
+ m_transparent_triangles.reserve(p.indices.size() / 3);
+ for (u32 i = 0; i < p.indices.size(); i += 3) {
+ t.p1 = p.indices[i];
+ t.p2 = p.indices[i + 1];
+ t.p3 = p.indices[i + 2];
+ t.updateAttributes();
+ m_transparent_triangles.push_back(t);
}
- break;
- default:
+ } else {
buf->append(&p.vertices[0], p.vertices.size(),
&p.indices[0], p.indices.size());
- break;
}
mesh->addMeshBuffer(buf);
buf->drop();
#if defined(XORG_USED)
#if RUN_IN_PLACE
return setXorgWindowIconFromPath(
- porting::path_share + "/misc/" PROJECT_NAME "-xorg-icon-128.png");
+ porting::path_share + "/misc/dragonfire-xorg-icon-128.png");
#else
// We have semi-support for reading in-place data if we are
// compiled with RUN_IN_PLACE. Don't break with this and
// also try the path_share location.
return setXorgWindowIconFromPath(
- ICON_DIR "/hicolor/128x128/apps/" PROJECT_NAME ".png") ||
- setXorgWindowIconFromPath(porting::path_share + "/misc/" PROJECT_NAME
- "-xorg-icon-128.png");
+ ICON_DIR "/hicolor/128x128/apps/dragonfire.png") ||
+ setXorgWindowIconFromPath(porting::path_share + "/misc/dragonfire-xorg-icon-128.png");
#endif
#elif defined(_WIN32)
HWND hWnd; // Window handle
#endif
- v2u32 RenderingEngine::getDisplaySize()
- {
- IrrlichtDevice *nulldevice = createDevice(video::EDT_NULL);
-
- core::dimension2d<u32> deskres =
- nulldevice->getVideoModeList()->getDesktopResolution();
- nulldevice->drop();
-
- return deskres;
- }
-
#else // __ANDROID__
float RenderingEngine::getDisplayDensity()
{
return porting::getDisplayDensity();
}
- v2u32 RenderingEngine::getDisplaySize()
- {
- return porting::getDisplaySize();
- }
#endif // __ANDROID__
return sector;
}
- std::map<v2s16, MapSector*>::iterator n = m_sectors.find(p);
+ auto n = m_sectors.find(p);
if (n == m_sectors.end())
return NULL;
return block;
}
+void Map::listAllLoadedBlocks(std::vector<v3s16> &dst)
+{
+ for (auto §or_it : m_sectors) {
+ MapSector *sector = sector_it.second;
+
+ MapBlockVect blocks;
+ sector->getBlocks(blocks);
+
+ for (MapBlock *block : blocks) {
+ v3s16 p = block->getPos();
+ dst.push_back(p);
+ }
+ }
+}
+
bool Map::isValidPosition(v3s16 p)
{
v3s16 blockpos = getNodeBlockPos(p);
dbase_ro->listAllLoadableBlocks(dst);
}
-void ServerMap::listAllLoadedBlocks(std::vector<v3s16> &dst)
-{
- for (auto §or_it : m_sectors) {
- MapSector *sector = sector_it.second;
-
- MapBlockVect blocks;
- sector->getBlocks(blocks);
-
- for (MapBlock *block : blocks) {
- v3s16 p = block->getPos();
- dst.push_back(p);
- }
- }
-}
-
MapDatabase *ServerMap::createDatabase(
const std::string &name,
const std::string &savedir,
MapBlock * getBlockNoCreate(v3s16 p);
// Returns NULL if not found
MapBlock * getBlockNoCreateNoEx(v3s16 p);
-
+
+ void listAllLoadedBlocks(std::vector<v3s16> &dst);
+
/* Server overrides */
virtual MapBlock * emergeBlock(v3s16 p, bool create_blank=true)
{ return getBlockNoCreateNoEx(p); }
std::set<MapEventReceiver*> m_event_receivers;
- std::map<v2s16, MapSector*> m_sectors;
+ std::unordered_map<v2s16, MapSector*> m_sectors;
// Be sure to set this to NULL when the cached sector is deleted
MapSector *m_sector_cache = nullptr;
void save(ModifiedState save_level) override;
void listAllLoadableBlocks(std::vector<v3s16> &dst);
- void listAllLoadedBlocks(std::vector<v3s16> &dst);
MapgenParams *getMapgenParams();
wall_bottom = aabb3f(-BS/2, -BS/2, -BS/2, BS/2, -BS/2+BS/16., BS/2);
wall_side = aabb3f(-BS/2, -BS/2, -BS/2, -BS/2+BS/16., BS/2, BS/2);
// no default for other parts
- connect_top.clear();
- connect_bottom.clear();
- connect_front.clear();
- connect_left.clear();
- connect_back.clear();
- connect_right.clear();
- disconnected_top.clear();
- disconnected_bottom.clear();
- disconnected_front.clear();
- disconnected_left.clear();
- disconnected_back.clear();
- disconnected_right.clear();
- disconnected.clear();
- disconnected_sides.clear();
+ connected.reset();
}
void NodeBox::serialize(std::ostream &os, u16 protocol_version) const
writeV3F32(os, wall_side.MinEdge);
writeV3F32(os, wall_side.MaxEdge);
break;
- case NODEBOX_CONNECTED:
+ case NODEBOX_CONNECTED: {
writeU8(os, type);
#define WRITEBOX(box) \
writeV3F32(os, i.MaxEdge); \
};
+ const auto &c = getConnected();
+
WRITEBOX(fixed);
- WRITEBOX(connect_top);
- WRITEBOX(connect_bottom);
- WRITEBOX(connect_front);
- WRITEBOX(connect_left);
- WRITEBOX(connect_back);
- WRITEBOX(connect_right);
- WRITEBOX(disconnected_top);
- WRITEBOX(disconnected_bottom);
- WRITEBOX(disconnected_front);
- WRITEBOX(disconnected_left);
- WRITEBOX(disconnected_back);
- WRITEBOX(disconnected_right);
- WRITEBOX(disconnected);
- WRITEBOX(disconnected_sides);
+ WRITEBOX(c.connect_top);
+ WRITEBOX(c.connect_bottom);
+ WRITEBOX(c.connect_front);
+ WRITEBOX(c.connect_left);
+ WRITEBOX(c.connect_back);
+ WRITEBOX(c.connect_right);
+ WRITEBOX(c.disconnected_top);
+ WRITEBOX(c.disconnected_bottom);
+ WRITEBOX(c.disconnected_front);
+ WRITEBOX(c.disconnected_left);
+ WRITEBOX(c.disconnected_back);
+ WRITEBOX(c.disconnected_right);
+ WRITEBOX(c.disconnected);
+ WRITEBOX(c.disconnected_sides);
break;
+ }
default:
writeU8(os, type);
break;
u16 count;
+ auto &c = getConnected();
+
READBOXES(fixed);
- READBOXES(connect_top);
- READBOXES(connect_bottom);
- READBOXES(connect_front);
- READBOXES(connect_left);
- READBOXES(connect_back);
- READBOXES(connect_right);
- READBOXES(disconnected_top);
- READBOXES(disconnected_bottom);
- READBOXES(disconnected_front);
- READBOXES(disconnected_left);
- READBOXES(disconnected_back);
- READBOXES(disconnected_right);
- READBOXES(disconnected);
- READBOXES(disconnected_sides);
+ READBOXES(c.connect_top);
+ READBOXES(c.connect_bottom);
+ READBOXES(c.connect_front);
+ READBOXES(c.connect_left);
+ READBOXES(c.connect_back);
+ READBOXES(c.connect_right);
+ READBOXES(c.disconnected_top);
+ READBOXES(c.disconnected_bottom);
+ READBOXES(c.disconnected_front);
+ READBOXES(c.disconnected_left);
+ READBOXES(c.disconnected_back);
+ READBOXES(c.disconnected_right);
+ READBOXES(c.disconnected);
+ READBOXES(c.disconnected_sides);
}
}
Cached stuff
*/
#ifndef SERVER
- solidness = 2;
+ solidness = 0;
visual_solidness = 0;
backface_culling = true;
drowning = 0;
light_source = 0;
damage_per_second = 0;
- node_box = NodeBox();
- selection_box = NodeBox();
- collision_box = NodeBox();
+ node_box.reset();
+ selection_box.reset();
+ collision_box.reset();
waving = 0;
legacy_facedir_simple = false;
legacy_wallmounted = false;
solidness = 0;
visual_solidness = 1;
} else {
- drawtype = NDT_NORMAL;
- solidness = 2;
+ if (waving >= 1) {
+ // waving nodes must make faces so there are no gaps
+ drawtype = NDT_ALLFACES;
+ solidness = 0;
+ visual_solidness = 1;
+ } else {
+ drawtype = NDT_NORMAL;
+ solidness = 2;
+ }
for (TileDef &td : tdef)
td.name += std::string("^[noalpha");
}
{
ContentFeatures f;
f.name = "unknown";
- TileDef unknownTile;
- unknownTile.name = "unknown_node.png";
for (int t = 0; t < 6; t++)
- f.tiledef[t] = unknownTile;
+ f.tiledef[t].name = "unknown_node.png";
// Insert directly into containers
content_t c = CONTENT_UNKNOWN;
m_content_features[c] = f;
break;
}
case NODEBOX_CONNECTED: {
+ const auto &c = nodebox.getConnected();
// Add all possible connected boxes
- boxVectorUnion(nodebox.fixed, box_union);
- boxVectorUnion(nodebox.connect_top, box_union);
- boxVectorUnion(nodebox.connect_bottom, box_union);
- boxVectorUnion(nodebox.connect_front, box_union);
- boxVectorUnion(nodebox.connect_left, box_union);
- boxVectorUnion(nodebox.connect_back, box_union);
- boxVectorUnion(nodebox.connect_right, box_union);
- boxVectorUnion(nodebox.disconnected_top, box_union);
- boxVectorUnion(nodebox.disconnected_bottom, box_union);
- boxVectorUnion(nodebox.disconnected_front, box_union);
- boxVectorUnion(nodebox.disconnected_left, box_union);
- boxVectorUnion(nodebox.disconnected_back, box_union);
- boxVectorUnion(nodebox.disconnected_right, box_union);
- boxVectorUnion(nodebox.disconnected, box_union);
- boxVectorUnion(nodebox.disconnected_sides, box_union);
+ boxVectorUnion(nodebox.fixed, box_union);
+ boxVectorUnion(c.connect_top, box_union);
+ boxVectorUnion(c.connect_bottom, box_union);
+ boxVectorUnion(c.connect_front, box_union);
+ boxVectorUnion(c.connect_left, box_union);
+ boxVectorUnion(c.connect_back, box_union);
+ boxVectorUnion(c.connect_right, box_union);
+ boxVectorUnion(c.disconnected_top, box_union);
+ boxVectorUnion(c.disconnected_bottom, box_union);
+ boxVectorUnion(c.disconnected_front, box_union);
+ boxVectorUnion(c.disconnected_left, box_union);
+ boxVectorUnion(c.disconnected_back, box_union);
+ boxVectorUnion(c.disconnected_right, box_union);
+ boxVectorUnion(c.disconnected, box_union);
+ boxVectorUnion(c.disconnected_sides, box_union);
break;
}
default: {
// IWritableNodeDefManager
-content_t NodeDefManager::set(const std::string &name, const ContentFeatures &def)
+content_t NodeDefManager::set(const std::string &name, const ContentFeatures &d)
{
+ ContentFeatures def = d;
+
// Pre-conditions
assert(name != "");
assert(name != "ignore");
assert(name == def.name);
content_t id = CONTENT_IGNORE;
- if (!m_name_id_mapping.getId(name, id)) { // ignore aliases
+
+ if (m_name_id_mapping.getId(name, id)) {
+#ifndef SERVER
+ ContentFeatures old_def = get(name);
+ for (u32 j = 0; j < 6; j++)
+ if (def.tiledef[j].name.empty())
+ def.tiledef[j] = old_def.tiledef[j];
+ for (u32 j = 0; j < 6; j++)
+ if (def.tiledef_overlay[j].name.empty())
+ def.tiledef_overlay[j] = old_def.tiledef_overlay[j];
+ for (u32 j = 0; j < CF_SPECIAL_COUNT; j++)
+ if (def.tiledef_special[j].name.empty())
+ def.tiledef_special[j] = old_def.tiledef_special[j];
+#endif
+ } else {
// Get new id
id = allocateId();
if (id == CONTENT_IGNORE) {
#include "object_properties.h"
#include "collision.h"
#include "cpp_api/s_node.h"
+#include "lua_api/l_clientobject.h"
#include "lua_api/l_object.h"
#include "lua_api/l_item.h"
#include "common/c_internal.h"
}
push_groups(L, i.groups);
lua_setfield(L, -2, "groups");
+ lua_newtable(L);
push_soundspec(L, i.sound_place);
- lua_setfield(L, -2, "sound_place");
+ lua_setfield(L, -2, "place");
push_soundspec(L, i.sound_place_failed);
- lua_setfield(L, -2, "sound_place_failed");
+ lua_setfield(L, -2, "place_failed");
+ lua_setfield(L, -2, "sounds");
lua_pushstring(L, i.node_placement_prediction.c_str());
lua_setfield(L, -2, "node_placement_prediction");
}
if (getintfield(L, -1, "hp_max", hp_max)) {
prop->hp_max = (u16)rangelim(hp_max, 0, U16_MAX);
- if (prop->hp_max < sao->getHP()) {
+ if (sao && prop->hp_max < sao->getHP()) {
PlayerHPChangeReason reason(PlayerHPChangeReason::SET_HP);
sao->setHP(prop->hp_max, reason);
}
}
if (getintfield(L, -1, "breath_max", prop->breath_max)) {
- if (sao->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
+ if (sao && sao->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
PlayerSAO *player = (PlayerSAO *)sao;
if (prop->breath_max < player->getBreath())
player->setBreath(prop->breath_max);
return tiledef;
}
+/******************************************************************************/
+void push_tiledef(lua_State *L, TileDef tiledef)
+{
+ lua_newtable(L);
+ setstringfield(L, -1, "name", tiledef.name);
+ setboolfield(L, -1, "backface_culling", tiledef.backface_culling);
+ setboolfield(L, -1, "tileable_horizontal", tiledef.tileable_horizontal);
+ setboolfield(L, -1, "tileable_vertical", tiledef.tileable_vertical);
+ std::string align_style;
+ switch (tiledef.align_style) {
+ case ALIGN_STYLE_USER_DEFINED:
+ align_style = "user";
+ break;
+ case ALIGN_STYLE_WORLD:
+ align_style = "world";
+ break;
+ default:
+ align_style = "node";
+ }
+ setstringfield(L, -1, "align_style", align_style);
+ setintfield(L, -1, "scale", tiledef.scale);
+ if (tiledef.has_color) {
+ push_ARGB8(L, tiledef.color);
+ lua_setfield(L, -2, "color");
+ }
+ push_animation_definition(L, tiledef.animation);
+ lua_setfield(L, -2, "animation");
+}
+
/******************************************************************************/
void read_content_features(lua_State *L, ContentFeatures &f, int index)
{
std::string drawtype(ScriptApiNode::es_DrawType[(int)c.drawtype].str);
std::string liquid_type(ScriptApiNode::es_LiquidType[(int)c.liquid_type].str);
- /* Missing "tiles" because I don't see a usecase (at least not yet). */
+ lua_newtable(L);
+
+ // tiles
+ lua_newtable(L);
+ for (int i = 0; i < 6; i++) {
+ push_tiledef(L, c.tiledef[i]);
+ lua_rawseti(L, -2, i + 1);
+ }
+ lua_setfield(L, -2, "tiles");
+ // overlay_tiles
lua_newtable(L);
+ for (int i = 0; i < 6; i++) {
+ push_tiledef(L, c.tiledef_overlay[i]);
+ lua_rawseti(L, -2, i + 1);
+ }
+ lua_setfield(L, -2, "overlay_tiles");
+
+ // special_tiles
+ lua_newtable(L);
+ for (int i = 0; i < CF_SPECIAL_COUNT; i++) {
+ push_tiledef(L, c.tiledef_special[i]);
+ lua_rawseti(L, -2, i + 1);
+ }
+ lua_setfield(L, -2, "special_tiles");
+
lua_pushboolean(L, c.has_on_construct);
lua_setfield(L, -2, "has_on_construct");
lua_pushboolean(L, c.has_on_destruct);
lua_setfield(L, -2, "collision_box");
lua_newtable(L);
push_soundspec(L, c.sound_footstep);
- lua_setfield(L, -2, "sound_footstep");
+ lua_setfield(L, -2, "footstep");
push_soundspec(L, c.sound_dig);
- lua_setfield(L, -2, "sound_dig");
+ lua_setfield(L, -2, "dig");
push_soundspec(L, c.sound_dug);
- lua_setfield(L, -2, "sound_dug");
+ lua_setfield(L, -2, "dug");
lua_setfield(L, -2, "sounds");
lua_pushboolean(L, c.legacy_facedir_simple);
lua_setfield(L, -2, "legacy_facedir_simple");
push_aabb3f(L, box.wall_side);
lua_setfield(L, -2, "wall_side");
break;
- case NODEBOX_CONNECTED:
+ case NODEBOX_CONNECTED: {
lua_pushstring(L, "connected");
lua_setfield(L, -2, "type");
- push_box(L, box.connect_top);
+ const auto &c = box.getConnected();
+ push_box(L, c.connect_top);
lua_setfield(L, -2, "connect_top");
- push_box(L, box.connect_bottom);
+ push_box(L, c.connect_bottom);
lua_setfield(L, -2, "connect_bottom");
- push_box(L, box.connect_front);
+ push_box(L, c.connect_front);
lua_setfield(L, -2, "connect_front");
- push_box(L, box.connect_back);
+ push_box(L, c.connect_back);
lua_setfield(L, -2, "connect_back");
- push_box(L, box.connect_left);
+ push_box(L, c.connect_left);
lua_setfield(L, -2, "connect_left");
- push_box(L, box.connect_right);
+ push_box(L, c.connect_right);
lua_setfield(L, -2, "connect_right");
+ // half the boxes are missing here?
break;
+ }
default:
FATAL_ERROR("Invalid box.type");
break;
NODEBOXREAD(nodebox.wall_top, "wall_top");
NODEBOXREAD(nodebox.wall_bottom, "wall_bottom");
NODEBOXREAD(nodebox.wall_side, "wall_side");
- NODEBOXREADVEC(nodebox.connect_top, "connect_top");
- NODEBOXREADVEC(nodebox.connect_bottom, "connect_bottom");
- NODEBOXREADVEC(nodebox.connect_front, "connect_front");
- NODEBOXREADVEC(nodebox.connect_left, "connect_left");
- NODEBOXREADVEC(nodebox.connect_back, "connect_back");
- NODEBOXREADVEC(nodebox.connect_right, "connect_right");
- NODEBOXREADVEC(nodebox.disconnected_top, "disconnected_top");
- NODEBOXREADVEC(nodebox.disconnected_bottom, "disconnected_bottom");
- NODEBOXREADVEC(nodebox.disconnected_front, "disconnected_front");
- NODEBOXREADVEC(nodebox.disconnected_left, "disconnected_left");
- NODEBOXREADVEC(nodebox.disconnected_back, "disconnected_back");
- NODEBOXREADVEC(nodebox.disconnected_right, "disconnected_right");
- NODEBOXREADVEC(nodebox.disconnected, "disconnected");
- NODEBOXREADVEC(nodebox.disconnected_sides, "disconnected_sides");
+
+ if (nodebox.type == NODEBOX_CONNECTED) {
+ auto &c = nodebox.getConnected();
+ NODEBOXREADVEC(c.connect_top, "connect_top");
+ NODEBOXREADVEC(c.connect_bottom, "connect_bottom");
+ NODEBOXREADVEC(c.connect_front, "connect_front");
+ NODEBOXREADVEC(c.connect_left, "connect_left");
+ NODEBOXREADVEC(c.connect_back, "connect_back");
+ NODEBOXREADVEC(c.connect_right, "connect_right");
+ NODEBOXREADVEC(c.disconnected_top, "disconnected_top");
+ NODEBOXREADVEC(c.disconnected_bottom, "disconnected_bottom");
+ NODEBOXREADVEC(c.disconnected_front, "disconnected_front");
+ NODEBOXREADVEC(c.disconnected_left, "disconnected_left");
+ NODEBOXREADVEC(c.disconnected_back, "disconnected_back");
+ NODEBOXREADVEC(c.disconnected_right, "disconnected_right");
+ NODEBOXREADVEC(c.disconnected, "disconnected");
+ NODEBOXREADVEC(c.disconnected_sides, "disconnected_sides");
+ }
return nodebox;
}
return anim;
}
+void push_animation_definition(lua_State *L, struct TileAnimationParams anim)
+{
+ switch (anim.type) {
+ case TAT_NONE:
+ lua_pushnil(L);
+ break;
+ case TAT_VERTICAL_FRAMES:
+ lua_newtable(L);
+ setstringfield(L, -1, "type", "vertical_frames");
+ setfloatfield(L, -1, "aspect_w", anim.vertical_frames.aspect_w);
+ setfloatfield(L, -1, "aspect_h", anim.vertical_frames.aspect_h);
+ setfloatfield(L, -1, "length", anim.vertical_frames.length);
+ break;
+ case TAT_SHEET_2D:
+ lua_newtable(L);
+ setstringfield(L, -1, "type", "sheet_2d");
+ setintfield(L, -1, "frames_w", anim.sheet_2d.frames_w);
+ setintfield(L, -1, "frames_h", anim.sheet_2d.frames_h);
+ setintfield(L, -1, "frame_length", anim.sheet_2d.frame_length);
+ break;
+ }
+}
+
/******************************************************************************/
ToolCapabilities read_tool_capabilities(
lua_State *L, int table)
} else if (pointed.type == POINTEDTHING_OBJECT) {
lua_pushstring(L, "object");
lua_setfield(L, -2, "type");
-
- if (csm) {
- lua_pushinteger(L, pointed.object_id);
- lua_setfield(L, -2, "id");
- } else {
- push_objectRef(L, pointed.object_id);
- lua_setfield(L, -2, "ref");
- }
+ push_objectRef(L, pointed.object_id);
+ lua_setfield(L, -2, "ref");
} else {
lua_pushstring(L, "nothing");
lua_setfield(L, -2, "type");
lua_setfield(L, -2, "collisions");
/**/
}
+
+/******************************************************************************/
+void push_physics_override(lua_State *L, float speed, float jump, float gravity, bool sneak, bool sneak_glitch, bool new_move)
+{
+ lua_createtable(L, 0, 6);
+
+ lua_pushnumber(L, speed);
+ lua_setfield(L, -2, "speed");
+
+ lua_pushnumber(L, jump);
+ lua_setfield(L, -2, "jump");
+
+ lua_pushnumber(L, gravity);
+ lua_setfield(L, -2, "gravity");
+
+ lua_pushboolean(L, sneak);
+ lua_setfield(L, -2, "sneak");
+
+ lua_pushboolean(L, sneak_glitch);
+ lua_setfield(L, -2, "sneak_glitch");
+
+ lua_pushboolean(L, new_move);
+ lua_setfield(L, -2, "new_move");
+}
"bit"
};
static const char *io_whitelist[] = {
+ "open",
"close",
"flush",
"read",
"gethook",
"traceback",
"getinfo",
- "getmetatable",
- "setmetatable",
"upvalueid",
"sethook",
"debug",
copy_safe(L, io_whitelist, sizeof(io_whitelist));
// And replace unsafe ones
- SECURE_API(io, open);
+ //SECURE_API(io, open);
SECURE_API(io, input);
SECURE_API(io, output);
SECURE_API(io, lines);
"getinfo", // used by builtin and unset before mods load
"traceback"
};
-
#if USE_LUAJIT
static const char *jit_whitelist[] = {
"arch",
lua_State *L = getStack();
int thread = getThread(L);
+ // Backup globals to the registry
+ lua_getglobal(L, "_G");
+ lua_rawseti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_GLOBALS_BACKUP);
+
// create an empty environment
createEmptyEnv(L);
SECURE_API(g, require);
lua_pop(L, 2);
-
-
// Copy safe OS functions
lua_getglobal(L, "os");
lua_newtable(L);
copy_safe(L, debug_whitelist, sizeof(debug_whitelist));
lua_setfield(L, -3, "debug");
lua_pop(L, 1); // Pop old debug
+
#if USE_LUAJIT
// Copy safe jit functions, if they exist
#include "server.h"
#include "inventory.h"
#include "log.h"
-
+#include "script/cpp_api/s_base.h"
+#ifndef SERVER
+#include "client/client.h"
+#include "client/renderingengine.h"
+#include "client/shader.h"
+#endif
// garbage collector
int LuaItemStack::gc_object(lua_State *L)
// Get the writable item and node definition managers from the server
IWritableItemDefManager *idef =
- getServer(L)->getWritableItemDefManager();
+ getGameDef(L)->getWritableItemDefManager();
NodeDefManager *ndef =
- getServer(L)->getWritableNodeDefManager();
+ getGameDef(L)->getWritableNodeDefManager();
// Check if name is defined
std::string name;
// be done
if (f.name == "ignore")
return 0;
+ // This would break everything
+ if (f.name.empty())
+ throw LuaError("Cannot register node with empty name");
content_t id = ndef->set(f.name, f);
+ itos(MAX_REGISTERED_CONTENT+1)
+ ") exceeded (" + name + ")");
}
+
}
-
+
return 0; /* number of results */
}
std::string name = luaL_checkstring(L, 1);
IWritableItemDefManager *idef =
- getServer(L)->getWritableItemDefManager();
+ getGameDef(L)->getWritableItemDefManager();
// Unregister the node
if (idef->get(name).type == ITEM_NODE) {
NodeDefManager *ndef =
- getServer(L)->getWritableNodeDefManager();
+ getGameDef(L)->getWritableNodeDefManager();
ndef->removeNode(name);
}
// Get the writable item definition manager from the server
IWritableItemDefManager *idef =
- getServer(L)->getWritableItemDefManager();
+ getGameDef(L)->getWritableItemDefManager();
idef->registerAlias(name, convert_to);
/*
Update m_list
*/
- m_list.clear();
- for (v3s16 p : newlist) {
- m_list.insert(p);
- }
+ m_list = std::move(newlist);
}
/*
/* Add object to environment */
addActiveObject(playersao);
+ // Update active blocks asap so objects in those blocks appear on the client
+ m_force_update_active_blocks = true;
+
return playersao;
}
// Tell the object about removal
obj->removingFromEnvironment();
// Deregister in scripting api
- m_script->removeObjectReference(obj);
+ m_script->removeObjectReference(dynamic_cast<ActiveObject *>(obj));
// Delete active object
if (obj->environmentDeletes())
/*
Manage active block list
*/
- if (m_active_blocks_management_interval.step(dtime, m_cache_active_block_mgmt_interval)) {
+ if (m_active_blocks_mgmt_interval.step(dtime, m_cache_active_block_mgmt_interval) ||
+ m_force_update_active_blocks) {
ScopeProfiler sp(g_profiler, "ServerEnv: update active blocks", SPT_AVG);
+
/*
Get player block positions
*/
std::vector<PlayerSAO*> players;
- for (RemotePlayer *player: m_players) {
+ players.reserve(m_players.size());
+ for (RemotePlayer *player : m_players) {
// Ignore disconnected players
if (player->getPeerId() == PEER_ID_INEXISTENT)
continue;
m_active_blocks.update(players, active_block_range, active_object_range,
blocks_removed, blocks_added);
- m_active_block_gauge->set(m_active_blocks.size());
-
/*
Handle removed blocks
*/
for (const v3s16 &p: blocks_added) {
MapBlock *block = m_map->getBlockOrEmerge(p);
if (!block) {
- m_active_blocks.m_list.erase(p);
- m_active_blocks.m_abm_list.erase(p);
+ // TODO: The blocks removed here will only be picked up again
+ // on the next cycle. To minimize the latency of objects being
+ // activated we could remember the blocks pending activating
+ // and activate them instantly as soon as they're loaded.
+ m_active_blocks.remove(p);
continue;
}
activateBlock(block);
}
+
+ // Some blocks may be removed again by the code above so do this here
+ m_active_block_gauge->set(m_active_blocks.size());
}
+ m_force_update_active_blocks = false;
/*
Mess around in active blocks
}
// Register reference in scripting api (must be done before post-init)
- m_script->addObjectReference(object);
+ m_script->addObjectReference(dynamic_cast<ActiveObject *>(object));
// Post-initialize object
object->addedToEnvironment(dtime_s);
// Tell the object about removal
obj->removingFromEnvironment();
// Deregister in scripting api
- m_script->removeObjectReference(obj);
+ m_script->removeObjectReference(dynamic_cast<ActiveObject *>(obj));
// Delete
if (obj->environmentDeletes())
// Tell the object about removal
obj->removingFromEnvironment();
// Deregister in scripting api
- m_script->removeObjectReference(obj);
+ m_script->removeObjectReference(dynamic_cast<ActiveObject *>(obj));
// Delete active object
if (obj->environmentDeletes())