aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt20
-rw-r--r--src/activeobject.h8
-rw-r--r--src/ban.h2
-rw-r--r--src/camera.cpp24
-rw-r--r--src/camera.h23
-rw-r--r--src/cavegen.cpp28
-rw-r--r--src/cavegen.h46
-rw-r--r--src/cguittfont/irrUString.h2
-rw-r--r--src/chat.cpp27
-rw-r--r--src/chat.h11
-rw-r--r--src/client.cpp172
-rw-r--r--src/client.h57
-rw-r--r--src/client/CMakeLists.txt1
-rw-r--r--src/client/clientlauncher.cpp25
-rw-r--r--src/client/clientlauncher.h36
-rw-r--r--src/client/inputhandler.cpp119
-rw-r--r--src/client/inputhandler.h360
-rw-r--r--src/client/joystick_controller.cpp8
-rw-r--r--src/client/joystick_controller.h6
-rw-r--r--src/client/keys.h5
-rw-r--r--src/client/tile.cpp23
-rw-r--r--src/client/tile.h122
-rw-r--r--src/clientenvironment.cpp26
-rw-r--r--src/clientenvironment.h5
-rw-r--r--src/clientiface.cpp16
-rw-r--r--src/clientiface.h30
-rw-r--r--src/clientmap.cpp124
-rw-r--r--src/clientmedia.cpp16
-rw-r--r--src/clientmedia.h7
-rw-r--r--src/clientobject.h15
-rw-r--r--src/cloudparams.h33
-rw-r--r--src/clouds.cpp90
-rw-r--r--src/clouds.h54
-rw-r--r--src/cmake_config.h.in1
-rw-r--r--src/collision.cpp34
-rw-r--r--src/constants.h8
-rw-r--r--src/content_abm.cpp6
-rw-r--r--src/content_cao.cpp86
-rw-r--r--src/content_cao.h15
-rw-r--r--src/content_mapblock.cpp161
-rw-r--r--src/content_mapblock.h17
-rw-r--r--src/content_sao.cpp67
-rw-r--r--src/content_sao.h16
-rw-r--r--src/craftdef.cpp13
-rw-r--r--src/craftdef.h11
-rw-r--r--src/database-dummy.h10
-rw-r--r--src/database-files.cpp179
-rw-r--r--src/database-files.h46
-rw-r--r--src/database-leveldb.h4
-rw-r--r--src/database-postgresql.cpp470
-rw-r--r--src/database-postgresql.h115
-rw-r--r--src/database-redis.h2
-rw-r--r--src/database-sqlite3.cpp401
-rw-r--r--src/database-sqlite3.h158
-rw-r--r--src/database.cpp4
-rw-r--r--src/database.h24
-rw-r--r--src/defaultsettings.cpp10
-rw-r--r--src/defaultsettings.h1
-rw-r--r--src/drawscene.cpp6
-rw-r--r--src/drawscene.h2
-rw-r--r--src/dungeongen.cpp31
-rw-r--r--src/emerge.cpp8
-rw-r--r--src/environment.cpp6
-rw-r--r--src/environment.h6
-rw-r--r--src/exceptions.h9
-rw-r--r--src/face_position_cache.cpp110
-rw-r--r--src/face_position_cache.h44
-rw-r--r--src/filecache.h2
-rw-r--r--src/fontengine.cpp68
-rw-r--r--src/game.cpp442
-rw-r--r--src/game.h118
-rw-r--r--src/genericobject.cpp23
-rw-r--r--src/genericobject.h9
-rw-r--r--src/gettext.h5
-rw-r--r--src/gettime.h23
-rw-r--r--src/guiChatConsole.cpp15
-rw-r--r--src/guiChatConsole.h2
-rw-r--r--src/guiEngine.cpp28
-rw-r--r--src/guiEngine.h13
-rw-r--r--src/guiFileSelectMenu.cpp21
-rw-r--r--src/guiFileSelectMenu.h29
-rw-r--r--src/guiFormSpecMenu.cpp245
-rw-r--r--src/guiFormSpecMenu.h162
-rw-r--r--src/guiKeyChangeMenu.cpp24
-rw-r--r--src/guiKeyChangeMenu.h18
-rw-r--r--src/guiPasswordChange.cpp173
-rw-r--r--src/guiPasswordChange.h23
-rw-r--r--src/guiTable.cpp2
-rw-r--r--src/guiTable.h11
-rw-r--r--src/guiVolumeChange.cpp63
-rw-r--r--src/httpfetch.cpp23
-rw-r--r--src/httpfetch.h23
-rw-r--r--src/hud.cpp63
-rw-r--r--src/hud.h6
-rw-r--r--src/intlGUIEditBox.cpp53
-rw-r--r--src/intlGUIEditBox.h2
-rw-r--r--src/inventory.cpp36
-rw-r--r--src/inventory.h12
-rw-r--r--src/inventorymanager.cpp2
-rw-r--r--src/irr_v3d.h1
-rw-r--r--src/irrlichttypes_bloated.h1
-rw-r--r--src/irrlichttypes_extrabloated.h1
-rw-r--r--src/itemgroup.h6
-rw-r--r--src/itemstackmetadata.cpp32
-rw-r--r--src/keycode.h7
-rw-r--r--src/light.h31
-rw-r--r--src/localplayer.cpp469
-rw-r--r--src/localplayer.h57
-rw-r--r--src/log.cpp7
-rw-r--r--src/main.cpp39
-rw-r--r--src/mainmenumanager.h6
-rw-r--r--src/map.cpp53
-rw-r--r--src/map.h44
-rw-r--r--src/map_settings_manager.cpp13
-rw-r--r--src/mapblock.cpp7
-rw-r--r--src/mapblock.h3
-rw-r--r--src/mapblock_mesh.cpp569
-rw-r--r--src/mapblock_mesh.h63
-rw-r--r--src/mapgen.cpp69
-rw-r--r--src/mapgen.h24
-rw-r--r--src/mapgen_flat.cpp18
-rw-r--r--src/mapgen_flat.h11
-rw-r--r--src/mapgen_fractal.cpp4
-rw-r--r--src/mapgen_fractal.h11
-rw-r--r--src/mapgen_singlenode.cpp4
-rw-r--r--src/mapgen_singlenode.h10
-rw-r--r--src/mapgen_v5.cpp10
-rw-r--r--src/mapgen_v5.h12
-rw-r--r--src/mapgen_v6.cpp28
-rw-r--r--src/mapgen_v6.h2
-rw-r--r--src/mapgen_v7.cpp88
-rw-r--r--src/mapgen_v7.h4
-rw-r--r--src/mapgen_valleys.cpp5
-rw-r--r--src/mapgen_valleys.h5
-rw-r--r--src/mapnode.cpp13
-rw-r--r--src/mapnode.h4
-rw-r--r--src/mesh.cpp104
-rw-r--r--src/mesh.h17
-rw-r--r--src/mesh_generator_thread.cpp6
-rw-r--r--src/mesh_generator_thread.h22
-rw-r--r--src/metadata.cpp9
-rw-r--r--src/mg_biome.cpp3
-rw-r--r--src/mg_biome.h3
-rw-r--r--src/mg_decoration.cpp5
-rw-r--r--src/mg_decoration.h3
-rw-r--r--src/mg_ore.cpp6
-rw-r--r--src/mg_ore.h3
-rw-r--r--src/mg_schematic.cpp3
-rw-r--r--src/mg_schematic.h3
-rw-r--r--src/modalMenu.h10
-rw-r--r--src/modifiedstate.h1
-rw-r--r--src/mods.cpp102
-rw-r--r--src/mods.h7
-rw-r--r--src/nameidmapping.cpp12
-rw-r--r--src/nameidmapping.h30
-rw-r--r--src/network/clientopcodes.cpp10
-rw-r--r--src/network/clientpackethandler.cpp105
-rw-r--r--src/network/connection.cpp47
-rw-r--r--src/network/connection.h23
-rw-r--r--src/network/networkpacket.cpp13
-rw-r--r--src/network/networkpacket.h2
-rw-r--r--src/network/networkprotocol.h29
-rw-r--r--src/network/serveropcodes.cpp6
-rw-r--r--src/network/serverpackethandler.cpp85
-rw-r--r--src/nodedef.cpp119
-rw-r--r--src/nodedef.h31
-rw-r--r--src/nodemetadata.cpp63
-rw-r--r--src/nodemetadata.h16
-rw-r--r--src/noise.cpp4
-rw-r--r--src/object_properties.cpp1
-rw-r--r--src/object_properties.h1
-rw-r--r--src/particles.cpp174
-rw-r--r--src/particles.h12
-rw-r--r--src/pathfinder.cpp4
-rw-r--r--src/porting.cpp51
-rw-r--r--src/porting.h163
-rw-r--r--src/profiler.cpp30
-rw-r--r--src/profiler.h44
-rw-r--r--src/reflowscan.cpp4
-rw-r--r--src/remoteplayer.cpp55
-rw-r--r--src/remoteplayer.h20
-rw-r--r--src/rollback_interface.cpp4
-rw-r--r--src/script/CMakeLists.txt4
-rw-r--r--src/script/common/c_content.cpp357
-rw-r--r--src/script/common/c_content.h28
-rw-r--r--src/script/common/c_converter.cpp46
-rw-r--r--src/script/common/c_converter.h2
-rw-r--r--src/script/common/c_internal.cpp14
-rw-r--r--src/script/cpp_api/s_async.cpp33
-rw-r--r--src/script/cpp_api/s_async.h25
-rw-r--r--src/script/cpp_api/s_base.cpp24
-rw-r--r--src/script/cpp_api/s_base.h1
-rw-r--r--src/script/cpp_api/s_client.cpp35
-rw-r--r--src/script/cpp_api/s_client.h6
-rw-r--r--src/script/cpp_api/s_entity.cpp2
-rw-r--r--src/script/cpp_api/s_item.cpp41
-rw-r--r--src/script/cpp_api/s_item.h2
-rw-r--r--src/script/cpp_api/s_mainmenu.cpp8
-rw-r--r--src/script/cpp_api/s_node.cpp37
-rw-r--r--src/script/cpp_api/s_node.h1
-rw-r--r--src/script/cpp_api/s_nodemeta.cpp12
-rw-r--r--src/script/cpp_api/s_security.cpp5
-rw-r--r--src/script/lua_api/CMakeLists.txt1
-rw-r--r--src/script/lua_api/l_base.cpp14
-rw-r--r--src/script/lua_api/l_base.h5
-rw-r--r--src/script/lua_api/l_camera.cpp202
-rw-r--r--src/script/lua_api/l_camera.h44
-rw-r--r--src/script/lua_api/l_client.cpp104
-rw-r--r--src/script/lua_api/l_client.h25
-rw-r--r--src/script/lua_api/l_craft.cpp2
-rw-r--r--src/script/lua_api/l_env.cpp120
-rw-r--r--src/script/lua_api/l_env.h9
-rw-r--r--src/script/lua_api/l_internal.h3
-rw-r--r--src/script/lua_api/l_inventory.cpp11
-rw-r--r--src/script/lua_api/l_inventory.h2
-rw-r--r--src/script/lua_api/l_item.cpp2
-rw-r--r--src/script/lua_api/l_localplayer.cpp58
-rw-r--r--src/script/lua_api/l_localplayer.h8
-rw-r--r--src/script/lua_api/l_mainmenu.cpp38
-rw-r--r--src/script/lua_api/l_mainmenu.h3
-rw-r--r--src/script/lua_api/l_mapgen.cpp8
-rw-r--r--src/script/lua_api/l_metadata.cpp12
-rw-r--r--src/script/lua_api/l_nodemeta.cpp31
-rw-r--r--src/script/lua_api/l_nodemeta.h3
-rw-r--r--src/script/lua_api/l_noise.cpp2
-rw-r--r--src/script/lua_api/l_object.cpp124
-rw-r--r--src/script/lua_api/l_object.h10
-rw-r--r--src/script/lua_api/l_server.cpp80
-rw-r--r--src/script/lua_api/l_server.h13
-rw-r--r--src/script/lua_api/l_settings.cpp83
-rw-r--r--src/script/lua_api/l_settings.h13
-rw-r--r--src/script/lua_api/l_util.cpp159
-rw-r--r--src/script/lua_api/l_util.h23
-rw-r--r--src/script/lua_api/l_vmanip.cpp3
-rw-r--r--src/script/scripting_client.cpp (renamed from src/script/clientscripting.cpp)22
-rw-r--r--src/script/scripting_client.h (renamed from src/script/clientscripting.h)4
-rw-r--r--src/script/scripting_mainmenu.cpp25
-rw-r--r--src/script/scripting_mainmenu.h7
-rw-r--r--src/script/scripting_server.cpp (renamed from src/script/serverscripting.cpp)28
-rw-r--r--src/script/scripting_server.h (renamed from src/script/serverscripting.h)2
-rw-r--r--src/serialization.h5
-rw-r--r--src/server.cpp349
-rw-r--r--src/server.h31
-rw-r--r--src/serverenvironment.cpp629
-rw-r--r--src/serverenvironment.h29
-rw-r--r--src/serverobject.cpp2
-rw-r--r--src/serverobject.h24
-rw-r--r--src/settings.cpp43
-rw-r--r--src/settings.h37
-rw-r--r--src/settings_translation_file.cpp275
-rw-r--r--src/shader.cpp40
-rw-r--r--src/sky.cpp10
-rw-r--r--src/sky.h7
-rw-r--r--src/sound.h32
-rw-r--r--src/sound_openal.cpp108
-rw-r--r--src/threading/event.cpp2
-rw-r--r--src/threading/thread.cpp8
-rw-r--r--src/tool.cpp2
-rw-r--r--src/tool.h8
-rw-r--r--src/touchscreengui.cpp6
-rw-r--r--src/touchscreengui.h16
-rw-r--r--src/treegen.cpp2
-rw-r--r--src/treegen.h2
-rw-r--r--src/unittest/test.cpp8
-rw-r--r--src/unittest/test.h4
-rw-r--r--src/unittest/test_connection.cpp2
-rw-r--r--src/unittest/test_mapnode.cpp3
-rw-r--r--src/unittest/test_nodedef.cpp5
-rw-r--r--src/unittest/test_noderesolver.cpp2
-rw-r--r--src/unittest/test_objdef.cpp4
-rw-r--r--src/unittest/test_player.cpp52
-rw-r--r--src/unittest/test_profiler.cpp3
-rw-r--r--src/unittest/test_serialization.cpp10
-rw-r--r--src/util/basic_macros.h3
-rw-r--r--src/util/cpp11.h2
-rw-r--r--src/util/cpp11_container.h2
-rw-r--r--src/util/numeric.cpp93
-rw-r--r--src/util/numeric.h105
-rw-r--r--src/util/serialize.cpp2
-rw-r--r--src/util/serialize.h6
-rw-r--r--src/util/srp.cpp3
-rw-r--r--src/util/string.cpp1
-rw-r--r--src/util/string.h14
-rw-r--r--src/util/thread.h20
-rw-r--r--src/util/timetaker.cpp27
-rw-r--r--src/util/timetaker.h14
-rw-r--r--src/voxel.cpp12
-rw-r--r--src/voxel.h4
-rw-r--r--src/voxelalgorithms.cpp238
-rw-r--r--src/voxelalgorithms.h9
-rw-r--r--src/wieldmesh.cpp193
-rw-r--r--src/wieldmesh.h59
292 files changed, 8340 insertions, 4770 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 37f72a44d..6663b3c4c 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -268,7 +268,7 @@ if(WIN32)
else() # Probably MinGW = GCC
set(PLATFORM_LIBS "")
endif()
- set(PLATFORM_LIBS ws2_32.lib shlwapi.lib ${PLATFORM_LIBS})
+ set(PLATFORM_LIBS ws2_32.lib version.lib shlwapi.lib ${PLATFORM_LIBS})
# Zlib stuff
set(ZLIB_INCLUDE_DIR "${PROJECT_SOURCE_DIR}/../../zlib/zlib-1.2.5"
@@ -325,10 +325,12 @@ else()
endif(HAVE_LIBRT)
endif(APPLE)
+ if(NOT APPLE)
# This way Xxf86vm is found on OpenBSD too
- find_library(XXF86VM_LIBRARY Xxf86vm)
- mark_as_advanced(XXF86VM_LIBRARY)
- set(CLIENT_PLATFORM_LIBS ${CLIENT_PLATFORM_LIBS} ${XXF86VM_LIBRARY})
+ find_library(XXF86VM_LIBRARY Xxf86vm)
+ mark_as_advanced(XXF86VM_LIBRARY)
+ set(CLIENT_PLATFORM_LIBS ${CLIENT_PLATFORM_LIBS} ${XXF86VM_LIBRARY})
+ endif(NOT APPLE)
# Prefer local iconv if installed
find_library(ICONV_LIBRARY iconv)
@@ -377,6 +379,7 @@ set(common_SRCS
convert_json.cpp
craftdef.cpp
database-dummy.cpp
+ database-files.cpp
database-leveldb.cpp
database-postgresql.cpp
database-redis.cpp
@@ -387,6 +390,7 @@ set(common_SRCS
dungeongen.cpp
emerge.cpp
environment.cpp
+ face_position_cache.cpp
filesys.cpp
genericobject.cpp
gettext.cpp
@@ -752,12 +756,12 @@ else()
set(OTHER_FLAGS "${OTHER_FLAGS} -mthreads -fexceptions")
endif()
- set(CMAKE_CXX_FLAGS_RELEASE "-DNDEBUG ${RELEASE_WARNING_FLAGS} ${WARNING_FLAGS} ${OTHER_FLAGS} -ffast-math -Wall -pipe -funroll-loops")
- if(APPLE)
+ set(CMAKE_CXX_FLAGS_RELEASE "-DNDEBUG ${RELEASE_WARNING_FLAGS} ${WARNING_FLAGS} ${OTHER_FLAGS} -Wall -pipe -funroll-loops")
+ if(CMAKE_SYSTEM_NAME MATCHES "(Darwin|FreeBSD)")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -Os")
else()
- set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3 -fomit-frame-pointer")
- endif(APPLE)
+ set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3 -ffast-math -fomit-frame-pointer")
+ endif(CMAKE_SYSTEM_NAME MATCHES "(Darwin|FreeBSD)")
set(CMAKE_CXX_FLAGS_SEMIDEBUG "-g -O1 -Wall -Wabi ${WARNING_FLAGS} ${OTHER_FLAGS}")
set(CMAKE_CXX_FLAGS_DEBUG "-g -O0 -Wall -Wabi ${WARNING_FLAGS} ${OTHER_FLAGS}")
diff --git a/src/activeobject.h b/src/activeobject.h
index fe6c08514..f349ddef3 100644
--- a/src/activeobject.h
+++ b/src/activeobject.h
@@ -28,9 +28,9 @@ enum ActiveObjectType {
ACTIVEOBJECT_TYPE_TEST = 1,
// Deprecated stuff
ACTIVEOBJECT_TYPE_ITEM = 2,
- ACTIVEOBJECT_TYPE_RAT = 3,
- ACTIVEOBJECT_TYPE_OERKKI1 = 4,
- ACTIVEOBJECT_TYPE_FIREFLY = 5,
+// ACTIVEOBJECT_TYPE_RAT = 3,
+// ACTIVEOBJECT_TYPE_OERKKI1 = 4,
+// ACTIVEOBJECT_TYPE_FIREFLY = 5,
ACTIVEOBJECT_TYPE_MOBV2 = 6,
// End deprecated stuff
ACTIVEOBJECT_TYPE_LUAENTITY = 7,
@@ -43,7 +43,7 @@ enum ActiveObjectType {
struct ActiveObjectMessage
{
- ActiveObjectMessage(u16 id_, bool reliable_=true, std::string data_=""):
+ ActiveObjectMessage(u16 id_, bool reliable_=true, const std::string &data_ = "") :
id(id_),
reliable(reliable_),
datastring(data_)
diff --git a/src/ban.h b/src/ban.h
index d1a49cb15..e35bd0e10 100644
--- a/src/ban.h
+++ b/src/ban.h
@@ -41,12 +41,12 @@ public:
void add(const std::string &ip, const std::string &name);
void remove(const std::string &ip_or_name);
bool isModified();
+
private:
Mutex m_mutex;
std::string m_banfilepath;
StringMap m_ips;
bool m_modified;
-
};
#endif
diff --git a/src/camera.cpp b/src/camera.cpp
index 7e83dadeb..aa1baf957 100644
--- a/src/camera.cpp
+++ b/src/camera.cpp
@@ -33,6 +33,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "util/numeric.h"
#include "constants.h"
#include "fontengine.h"
+#include "script/scripting_client.h"
#define CAMERA_OFFSET_STEP 200
@@ -102,7 +103,6 @@ Camera::Camera(scene::ISceneManager* smgr, MapDrawControl& draw_control,
m_cache_view_bobbing_amount = g_settings->getFloat("view_bobbing_amount");
m_cache_fov = g_settings->getFloat("fov");
m_cache_zoom_fov = g_settings->getFloat("zoom_fov");
- m_cache_view_bobbing = g_settings->getBool("view_bobbing");
m_nametags.clear();
}
@@ -126,6 +126,10 @@ bool Camera::successfullyCreated(std::string &error_message)
} else {
error_message.clear();
}
+
+ if (g_settings->getBool("enable_client_modding")) {
+ m_client->getScript()->on_camera_ready(this);
+ }
return error_message.empty();
}
@@ -280,8 +284,8 @@ void Camera::update(LocalPlayer* player, f32 frametime, f32 busytime,
v3f rel_cam_target = v3f(0,0,1);
v3f rel_cam_up = v3f(0,1,0);
- if (m_view_bobbing_anim != 0 && m_camera_mode < CAMERA_MODE_THIRD)
- {
+ if (m_cache_view_bobbing_amount != 0.0f && m_view_bobbing_anim != 0.0f &&
+ m_camera_mode < CAMERA_MODE_THIRD) {
f32 bobfrac = my_modf(m_view_bobbing_anim * 2);
f32 bobdir = (m_view_bobbing_anim < 0.5) ? 1.0 : -1.0;
@@ -382,8 +386,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
@@ -467,9 +472,7 @@ void Camera::update(LocalPlayer* player, f32 frametime, f32 busytime,
const bool swimming = (movement_XZ || player->swimming_vertical) && player->in_liquid;
const bool climbing = movement_Y && player->is_climbing;
if ((walking || swimming || climbing) &&
- m_cache_view_bobbing &&
- (!g_settings->getBool("free_move") || !m_client->checkLocalPrivilege("fly")))
- {
+ (!g_settings->getBool("free_move") || !m_client->checkLocalPrivilege("fly"))) {
// Start animation
m_view_bobbing_state = 1;
m_view_bobbing_speed = MYMIN(speed.getLength(), 70);
@@ -485,7 +488,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;
@@ -556,9 +561,10 @@ void Camera::drawNametags()
f32 transformed_pos[4] = { pos.X, pos.Y, pos.Z, 1.0f };
trans.multiplyWith1x4Matrix(transformed_pos);
if (transformed_pos[3] > 0) {
+ std::string nametag_colorless = unescape_enriched(nametag->nametag_text);
core::dimension2d<u32> textsize =
g_fontengine->getFont()->getDimension(
- utf8_to_wide(nametag->nametag_text).c_str());
+ utf8_to_wide(nametag_colorless).c_str());
f32 zDiv = transformed_pos[3] == 0.0f ? 1.0f :
core::reciprocal(transformed_pos[3]);
v2u32 screensize = m_driver->getScreenSize();
diff --git a/src/camera.h b/src/camera.h
index f57efdf10..1e4800cba 100644
--- a/src/camera.h
+++ b/src/camera.h
@@ -64,22 +64,6 @@ public:
Client *client);
~Camera();
- // Get player scene node.
- // This node is positioned at the player's torso (without any view bobbing),
- // as given by Player::m_position. Yaw is applied but not pitch.
- inline scene::ISceneNode* getPlayerNode() const
- {
- return m_playernode;
- }
-
- // Get head scene node.
- // It has the eye transformation and pitch applied,
- // but no view bobbing.
- inline scene::ISceneNode* getHeadNode() const
- {
- return m_headnode;
- }
-
// Get camera scene node.
// It has the eye transformation, pitch and view bobbing applied.
inline scene::ICameraSceneNode* getCameraNode() const
@@ -161,6 +145,12 @@ public:
m_camera_mode = CAMERA_MODE_FIRST;
}
+ // Set the current camera mode
+ inline void setCameraMode(CameraMode mode)
+ {
+ m_camera_mode = mode;
+ }
+
//read the current camera mode
inline CameraMode getCameraMode()
{
@@ -231,7 +221,6 @@ private:
f32 m_cache_view_bobbing_amount;
f32 m_cache_fov;
f32 m_cache_zoom_fov;
- bool m_cache_view_bobbing;
std::list<Nametag *> m_nametags;
};
diff --git a/src/cavegen.cpp b/src/cavegen.cpp
index 7993b20e7..07fb629e2 100644
--- a/src/cavegen.cpp
+++ b/src/cavegen.cpp
@@ -160,9 +160,9 @@ CavernsNoise::CavernsNoise(
{
assert(nodedef);
- m_ndef = nodedef;
+ m_ndef = nodedef;
- m_csize = chunksize;
+ m_csize = chunksize;
m_cavern_limit = cavern_limit;
m_cavern_taper = cavern_taper;
m_cavern_threshold = cavern_threshold;
@@ -207,7 +207,7 @@ bool CavernsNoise::generateCaverns(MMVManip *vm, v3s16 nmin, v3s16 nmax)
}
//// Place nodes
- bool has_cavern = false;
+ bool near_cavern = false;
v3s16 em = vm->m_area.getExtent();
u32 index2d = 0;
@@ -229,20 +229,22 @@ bool CavernsNoise::generateCaverns(MMVManip *vm, v3s16 nmin, v3s16 nmax)
vm->m_area.add_y(em, vi, -1),
cavern_amp_index++) {
content_t c = vm->m_data[vi].getContent();
- float nabs_cavern = fabs(noise_cavern->result[index3d]);
- // Caverns generate first but still remove lava and water in case
- // of overgenerated classic caves.
- if (nabs_cavern * cavern_amp[cavern_amp_index] > m_cavern_threshold &&
- (m_ndef->get(c).is_ground_content ||
- c == c_lava_source || c == c_water_source)) {
- vm->m_data[vi] = MapNode(CONTENT_AIR);
- has_cavern = true;
+ float n_absamp_cavern = fabs(noise_cavern->result[index3d]) *
+ cavern_amp[cavern_amp_index];
+ // Disable CavesRandomWalk at a safe distance from caverns
+ // to avoid excessively spreading liquids in caverns.
+ if (n_absamp_cavern > m_cavern_threshold - 0.1f) {
+ near_cavern = true;
+ if (n_absamp_cavern > m_cavern_threshold &&
+ m_ndef->get(c).is_ground_content)
+ vm->m_data[vi] = MapNode(CONTENT_AIR);
}
}
}
delete[] cavern_amp;
- return has_cavern;
+
+ return near_cavern;
}
@@ -330,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);
diff --git a/src/cavegen.h b/src/cavegen.h
index e322c181c..1cb8dd90d 100644
--- a/src/cavegen.h
+++ b/src/cavegen.h
@@ -37,11 +37,12 @@ class GenerateNotifier;
TODO(hmmmm): Remove dependency on biomes
TODO(hmmmm): Find alternative to overgeneration as solution for sunlight issue
*/
-class CavesNoiseIntersection {
+class CavesNoiseIntersection
+{
public:
CavesNoiseIntersection(INodeDefManager *nodedef, BiomeManager *biomemgr,
- v3s16 chunksize, NoiseParams *np_cave1, NoiseParams *np_cave2,
- s32 seed, float cave_width);
+ v3s16 chunksize, NoiseParams *np_cave1, NoiseParams *np_cave2,
+ s32 seed, float cave_width);
~CavesNoiseIntersection();
void generateCaves(MMVManip *vm, v3s16 nmin, v3s16 nmax, u8 *biomemap);
@@ -65,10 +66,12 @@ private:
/*
CavernsNoise is a cave digging algorithm
*/
-class CavernsNoise {
+class CavernsNoise
+{
public:
CavernsNoise(INodeDefManager *nodedef, v3s16 chunksize, NoiseParams *np_cavern,
- s32 seed, float cavern_limit, float cavern_taper, float cavern_threshold);
+ s32 seed, float cavern_limit, float cavern_taper,
+ float cavern_threshold);
~CavernsNoise();
bool generateCaverns(MMVManip *vm, v3s16 nmin, v3s16 nmax);
@@ -105,7 +108,8 @@ private:
This algorithm is very fast, executing in less than 1ms on average for an
80x80x80 chunk of map on a modern processor.
*/
-class CavesRandomWalk {
+class CavesRandomWalk
+{
public:
MMVManip *vm;
INodeDefManager *ndef;
@@ -130,7 +134,6 @@ public:
bool large_cave_is_flat;
bool flooded;
- s16 max_stone_y;
v3s16 node_min;
v3s16 node_max;
@@ -150,18 +153,16 @@ public:
// ndef is a mandatory parameter.
// If gennotify is NULL, generation events are not logged.
- CavesRandomWalk(INodeDefManager *ndef,
- GenerateNotifier *gennotify = NULL,
- s32 seed = 0,
- int water_level = 1,
- content_t water_source = CONTENT_IGNORE,
- content_t lava_source = CONTENT_IGNORE);
+ CavesRandomWalk(INodeDefManager *ndef, GenerateNotifier *gennotify = NULL,
+ s32 seed = 0, int water_level = 1,
+ content_t water_source = CONTENT_IGNORE,
+ content_t lava_source = CONTENT_IGNORE);
// vm and ps are mandatory parameters.
// If heightmap is NULL, the surface level at all points is assumed to
// be water_level.
void makeCave(MMVManip *vm, v3s16 nmin, v3s16 nmax, PseudoRandom *ps,
- bool is_large_cave, int max_stone_height, s16 *heightmap);
+ bool is_large_cave, int max_stone_height, s16 *heightmap);
private:
void makeTunnel(bool dirswitch);
@@ -183,7 +184,8 @@ private:
tl;dr,
*** DO NOT TOUCH THIS CLASS UNLESS YOU KNOW WHAT YOU ARE DOING ***
*/
-class CavesV6 {
+class CavesV6
+{
public:
MMVManip *vm;
INodeDefManager *ndef;
@@ -222,18 +224,16 @@ public:
// ndef is a mandatory parameter.
// If gennotify is NULL, generation events are not logged.
- CavesV6(INodeDefManager *ndef,
- GenerateNotifier *gennotify = NULL,
- int water_level = 1,
- content_t water_source = CONTENT_IGNORE,
- content_t lava_source = CONTENT_IGNORE);
+ CavesV6(INodeDefManager *ndef, GenerateNotifier *gennotify = NULL,
+ int water_level = 1, content_t water_source = CONTENT_IGNORE,
+ content_t lava_source = CONTENT_IGNORE);
// vm, ps, and ps2 are mandatory parameters.
// If heightmap is NULL, the surface level at all points is assumed to
// be water_level.
- void makeCave(MMVManip *vm, v3s16 nmin, v3s16 nmax,
- PseudoRandom *ps, PseudoRandom *ps2,
- bool is_large_cave, int max_stone_height, s16 *heightmap = NULL);
+ void makeCave(MMVManip *vm, v3s16 nmin, v3s16 nmax, PseudoRandom *ps,
+ PseudoRandom *ps2, bool is_large_cave, int max_stone_height,
+ s16 *heightmap = NULL);
private:
void makeTunnel(bool dirswitch);
diff --git a/src/cguittfont/irrUString.h b/src/cguittfont/irrUString.h
index eb7abe5a1..5b10e2367 100644
--- a/src/cguittfont/irrUString.h
+++ b/src/cguittfont/irrUString.h
@@ -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
diff --git a/src/chat.cpp b/src/chat.cpp
index 46555b3dc..ade3fefd3 100644
--- a/src/chat.cpp
+++ b/src/chat.cpp
@@ -77,11 +77,6 @@ u32 ChatBuffer::getLineCount() const
return m_unformatted.size();
}
-u32 ChatBuffer::getScrollback() const
-{
- return m_scrollback;
-}
-
const ChatLine& ChatBuffer::getLine(u32 index) const
{
assert(index < getLineCount()); // pre-condition
@@ -336,7 +331,7 @@ u32 ChatBuffer::formatChatLine(const ChatLine& line, u32 cols,
while (frag_length < remaining_in_input &&
frag_length < remaining_in_output)
{
- if (isspace(line.text.getString()[in_pos + frag_length]))
+ if (iswspace(line.text.getString()[in_pos + frag_length]))
space_pos = frag_length;
++frag_length;
}
@@ -386,7 +381,7 @@ s32 ChatBuffer::getBottomScrollPos() const
-ChatPrompt::ChatPrompt(std::wstring prompt, u32 history_limit):
+ChatPrompt::ChatPrompt(const std::wstring &prompt, u32 history_limit):
m_prompt(prompt),
m_line(L""),
m_history(),
@@ -493,9 +488,9 @@ void ChatPrompt::nickCompletion(const std::list<std::string>& names, bool backwa
{
// no previous nick completion is active
prefix_start = prefix_end = m_cursor;
- while (prefix_start > 0 && !isspace(m_line[prefix_start-1]))
+ while (prefix_start > 0 && !iswspace(m_line[prefix_start-1]))
--prefix_start;
- while (prefix_end < m_line.size() && !isspace(m_line[prefix_end]))
+ while (prefix_end < m_line.size() && !iswspace(m_line[prefix_end]))
++prefix_end;
if (prefix_start == prefix_end)
return;
@@ -524,7 +519,7 @@ void ChatPrompt::nickCompletion(const std::list<std::string>& names, bool backwa
u32 replacement_index = 0;
if (!initial)
{
- while (word_end < m_line.size() && !isspace(m_line[word_end]))
+ while (word_end < m_line.size() && !iswspace(m_line[word_end]))
++word_end;
std::wstring word = m_line.substr(prefix_start, word_end - prefix_start);
@@ -543,7 +538,7 @@ void ChatPrompt::nickCompletion(const std::list<std::string>& names, bool backwa
}
}
std::wstring replacement = completions[replacement_index];
- if (word_end < m_line.size() && isspace(word_end))
+ if (word_end < m_line.size() && iswspace(m_line[word_end]))
++word_end;
// replace existing word with replacement word,
@@ -598,17 +593,17 @@ void ChatPrompt::cursorOperation(CursorOp op, CursorOpDir dir, CursorOpScope sco
case CURSOROP_SCOPE_WORD:
if (dir == CURSOROP_DIR_RIGHT) {
// skip one word to the right
- while (new_cursor < length && isspace(m_line[new_cursor]))
+ while (new_cursor < length && iswspace(m_line[new_cursor]))
new_cursor++;
- while (new_cursor < length && !isspace(m_line[new_cursor]))
+ while (new_cursor < length && !iswspace(m_line[new_cursor]))
new_cursor++;
- while (new_cursor < length && isspace(m_line[new_cursor]))
+ while (new_cursor < length && iswspace(m_line[new_cursor]))
new_cursor++;
} else {
// skip one word to the left
- while (new_cursor >= 1 && isspace(m_line[new_cursor - 1]))
+ while (new_cursor >= 1 && iswspace(m_line[new_cursor - 1]))
new_cursor--;
- while (new_cursor >= 1 && !isspace(m_line[new_cursor - 1]))
+ while (new_cursor >= 1 && !iswspace(m_line[new_cursor - 1]))
new_cursor--;
}
break;
diff --git a/src/chat.h b/src/chat.h
index 11061fd39..b7c6b74b9 100644
--- a/src/chat.h
+++ b/src/chat.h
@@ -38,14 +38,14 @@ struct ChatLine
// message text
EnrichedString text;
- ChatLine(std::wstring a_name, std::wstring a_text):
+ ChatLine(const std::wstring &a_name, const std::wstring &a_text):
age(0.0),
name(a_name),
text(a_text)
{
}
- ChatLine(EnrichedString a_name, EnrichedString a_text):
+ ChatLine(const EnrichedString &a_name, const EnrichedString &a_text):
age(0.0),
name(a_name),
text(a_text)
@@ -86,8 +86,6 @@ public:
// Get number of lines currently in buffer.
u32 getLineCount() const;
- // Get scrollback size, maximum number of lines in buffer.
- u32 getScrollback() const;
// Get reference to i-th chat line.
const ChatLine& getLine(u32 index) const;
@@ -148,7 +146,7 @@ private:
class ChatPrompt
{
public:
- ChatPrompt(std::wstring prompt, u32 history_limit);
+ ChatPrompt(const std::wstring &prompt, u32 history_limit);
~ChatPrompt();
// Input character or string
@@ -162,8 +160,7 @@ public:
std::wstring getLine() const { return m_line; }
// Get section of line that is currently selected
- std::wstring getSelection() const
- { return m_line.substr(m_cursor, m_cursor_len); }
+ std::wstring getSelection() const { return m_line.substr(m_cursor, m_cursor_len); }
// Clear the current line
void clear();
diff --git a/src/client.cpp b/src/client.cpp
index 7b962cd94..abc84b7cf 100644
--- a/src/client.cpp
+++ b/src/client.cpp
@@ -45,7 +45,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "database-sqlite3.h"
#include "serialization.h"
#include "guiscalingfilter.h"
-#include "script/clientscripting.h"
+#include "script/scripting_client.h"
#include "game.h"
extern gui::IGUIEnvironment* guienv;
@@ -57,7 +57,8 @@ extern gui::IGUIEnvironment* guienv;
Client::Client(
IrrlichtDevice *device,
const char *playername,
- std::string password,
+ const std::string &password,
+ const std::string &address_name,
MapDrawControl &control,
IWritableTextureSource *tsrc,
IWritableShaderSource *shsrc,
@@ -72,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),
@@ -89,8 +89,10 @@ Client::Client(
),
m_particle_manager(&m_env),
m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, ipv6, this),
+ m_address_name(address_name),
m_device(device),
m_camera(NULL),
+ m_minimap(NULL),
m_minimap_disabled_by_server(false),
m_server_ser_ver(SER_FMT_VER_INVALID),
m_proto_ver(0),
@@ -101,6 +103,8 @@ Client::Client(
m_animation_time(0),
m_crack_level(-1),
m_crack_pos(0,0,0),
+ m_last_chat_message_sent(time(NULL)),
+ m_chat_message_allowance(5.0f),
m_map_seed(0),
m_password(password),
m_chosen_auth_mech(AUTH_MECHANISM_NONE),
@@ -125,7 +129,9 @@ Client::Client(
// Add local player
m_env.setLocalPlayer(new LocalPlayer(this, playername));
- m_minimap = new Minimap(device, this);
+ if (g_settings->getBool("enable_minimap")) {
+ m_minimap = new Minimap(device, this);
+ }
m_cache_save_interval = g_settings->getU16("server_map_save_interval");
m_modding_enabled = g_settings->getBool("enable_client_modding");
@@ -166,7 +172,7 @@ void Client::initMods()
if (!string_allowed(mod.name, MODNAME_ALLOWED_CHARS)) {
throw ModError("Error loading mod \"" + mod.name +
"\": Mod name does not follow naming conventions: "
- "Only chararacters [a-z0-9_] are allowed.");
+ "Only characters [a-z0-9_] are allowed.");
}
std::string script_path = mod.path + DIR_DELIM + "init.lua";
infostream << " [" << padStringRight(mod.name, 12) << "] [\""
@@ -253,13 +259,11 @@ Client::~Client()
delete m_minimap;
}
-void Client::connect(Address address,
- const std::string &address_name,
- bool is_local_server)
+void Client::connect(Address address, bool is_local_server)
{
DSTACK(FUNCTION_NAME);
- initLocalMapSaving(address, address_name, is_local_server);
+ initLocalMapSaving(address, m_address_name, is_local_server);
m_con.SetTimeoutMs(0);
m_con.Connect(address);
@@ -270,14 +274,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;
@@ -398,6 +397,14 @@ void Client::step(float dtime)
}
/*
+ Send pending messages on out chat queue
+ */
+ if (!m_out_chat_queue.empty() && canSendChatMessage()) {
+ sendChatMessage(m_out_chat_queue.front());
+ m_out_chat_queue.pop();
+ }
+
+ /*
Handle environment
*/
// Control local player (0ms)
@@ -407,32 +414,29 @@ void Client::step(float dtime)
// Step environment
m_env.step(dtime);
+ m_sound->step(dtime);
/*
Get events
*/
- for(;;) {
- ClientEnvEvent event = m_env.getClientEvent();
- if(event.type == CEE_NONE) {
- break;
- }
- else if(event.type == CEE_PLAYER_DAMAGE) {
- if(m_ignore_damage_timer <= 0) {
- u8 damage = event.player_damage.amount;
-
- if(event.player_damage.send_to_server)
- sendDamage(damage);
-
- // Add to ClientEvent queue
- ClientEvent event;
- event.type = CE_PLAYER_DAMAGE;
- event.player_damage.amount = damage;
- m_client_event_queue.push(event);
- }
+ while (m_env.hasClientEnvEvents()) {
+ ClientEnvEvent envEvent = m_env.getClientEnvEvent();
+
+ if (envEvent.type == CEE_PLAYER_DAMAGE) {
+ u8 damage = envEvent.player_damage.amount;
+
+ if (envEvent.player_damage.send_to_server)
+ sendDamage(damage);
+
+ // Add to ClientEvent queue
+ ClientEvent event;
+ event.type = CE_PLAYER_DAMAGE;
+ event.player_damage.amount = damage;
+ m_client_event_queue.push(event);
}
// Protocol v29 or greater obsoleted this event
- else if (event.type == CEE_PLAYER_BREATH && m_proto_ver < 29) {
- u16 breath = event.player_breath.amount;
+ else if (envEvent.type == CEE_PLAYER_BREATH && m_proto_ver < 29) {
+ u16 breath = envEvent.player_breath.amount;
sendBreath(breath);
}
}
@@ -487,19 +491,23 @@ void Client::step(float dtime)
minimap_mapblock = r.mesh->moveMinimapMapblock();
if (minimap_mapblock == NULL)
do_mapper_update = false;
- }
- if (r.mesh && r.mesh->getMesh()->getMeshBufferCount() == 0) {
- delete r.mesh;
- } else {
- // Replace with the new mesh
- block->mesh = r.mesh;
+ bool is_empty = true;
+ for (int l = 0; l < MAX_TILE_LAYERS; l++)
+ if (r.mesh->getMesh(l)->getMeshBufferCount() != 0)
+ is_empty = false;
+
+ if (is_empty)
+ delete r.mesh;
+ else
+ // Replace with the new mesh
+ block->mesh = r.mesh;
}
} else {
delete r.mesh;
}
- if (do_mapper_update)
+ if (m_minimap && do_mapper_update)
m_minimap->addBlock(r.p, minimap_mapblock);
if (r.ack_block_to_server) {
@@ -522,7 +530,6 @@ void Client::step(float dtime)
if (m_media_downloader && m_media_downloader->isStarted()) {
m_media_downloader->step(this);
if (m_media_downloader->isDone()) {
- received_media();
delete m_media_downloader;
m_media_downloader = NULL;
}
@@ -743,14 +750,6 @@ void Client::request_media(const std::vector<std::string> &file_requests)
<< file_requests.size() << " files. packet size)" << std::endl;
}
-void Client::received_media()
-{
- NetworkPacket pkt(TOSERVER_RECEIVED_MEDIA, 0);
- Send(&pkt);
- infostream << "Client: Notifying server that we received all media"
- << std::endl;
-}
-
void Client::initLocalMapSaving(const Address &address,
const std::string &hostname,
bool is_local_server)
@@ -766,7 +765,7 @@ void Client::initLocalMapSaving(const Address &address,
fs::CreateAllDirs(world_path);
- m_localdb = new Database_SQLite3(world_path);
+ m_localdb = new MapDatabaseSQLite3(world_path);
m_localdb->beginSave();
actionstream << "Local map saving started, map will be saved at '" << world_path << "'" << std::endl;
}
@@ -774,7 +773,7 @@ void Client::initLocalMapSaving(const Address &address,
void Client::ReceiveAll()
{
DSTACK(FUNCTION_NAME);
- u32 start_ms = porting::getTimeMs();
+ u64 start_ms = porting::getTimeMs();
for(;;)
{
// Limit time even if there would be huge amounts of data to
@@ -1161,13 +1160,50 @@ void Client::sendInventoryAction(InventoryAction *a)
Send(&pkt);
}
+bool Client::canSendChatMessage() const
+{
+ u32 now = time(NULL);
+ float time_passed = now - m_last_chat_message_sent;
+
+ float virt_chat_message_allowance = m_chat_message_allowance + time_passed *
+ (CLIENT_CHAT_MESSAGE_LIMIT_PER_10S / 8.0f);
+
+ if (virt_chat_message_allowance < 1.0f)
+ return false;
+
+ return true;
+}
+
void Client::sendChatMessage(const std::wstring &message)
{
- NetworkPacket pkt(TOSERVER_CHAT_MESSAGE, 2 + message.size() * sizeof(u16));
+ const s16 max_queue_size = g_settings->getS16("max_out_chat_queue_size");
+ if (canSendChatMessage()) {
+ u32 now = time(NULL);
+ float time_passed = now - m_last_chat_message_sent;
+ m_last_chat_message_sent = time(NULL);
- pkt << message;
+ m_chat_message_allowance += time_passed * (CLIENT_CHAT_MESSAGE_LIMIT_PER_10S / 8.0f);
+ if (m_chat_message_allowance > CLIENT_CHAT_MESSAGE_LIMIT_PER_10S)
+ m_chat_message_allowance = CLIENT_CHAT_MESSAGE_LIMIT_PER_10S;
- Send(&pkt);
+ m_chat_message_allowance -= 1.0f;
+
+ NetworkPacket pkt(TOSERVER_CHAT_MESSAGE, 2 + message.size() * sizeof(u16));
+
+ pkt << message;
+
+ Send(&pkt);
+ } else if (m_out_chat_queue.size() < (u16) max_queue_size || max_queue_size == -1) {
+ m_out_chat_queue.push(message);
+ } else {
+ infostream << "Could not queue chat message because maximum out chat queue size ("
+ << max_queue_size << ") is reached." << std::endl;
+ }
+}
+
+void Client::clearOutChatQueue()
+{
+ m_out_chat_queue = std::queue<std::wstring>();
}
void Client::sendChangePassword(const std::string &oldpassword,
@@ -1601,14 +1637,11 @@ void Client::addUpdateMeshTaskForNode(v3s16 nodepos, bool ack_to_server, bool ur
ClientEvent Client::getClientEvent()
{
- ClientEvent event;
- if (m_client_event_queue.empty()) {
- event.type = CE_NONE;
- }
- else {
- event = m_client_event_queue.front();
- m_client_event_queue.pop();
- }
+ FATAL_ERROR_IF(m_client_event_queue.empty(),
+ "Cannot getClientEvent, queue is empty.");
+
+ ClientEvent event = m_client_event_queue.front();
+ m_client_event_queue.pop();
return event;
}
@@ -1623,7 +1656,7 @@ float Client::mediaReceiveProgress()
typedef struct TextureUpdateArgs {
IrrlichtDevice *device;
gui::IGUIEnvironment *guienv;
- u32 last_time_ms;
+ u64 last_time_ms;
u16 last_percent;
const wchar_t* text_base;
ITextureSource *tsrc;
@@ -1636,10 +1669,10 @@ void texture_update_progress(void *args, u32 progress, u32 max_progress)
// update the loading menu -- if neccessary
bool do_draw = false;
- u32 time_ms = targs->last_time_ms;
+ u64 time_ms = targs->last_time_ms;
if (cur_percent != targs->last_percent) {
targs->last_percent = cur_percent;
- time_ms = getTimeMs();
+ time_ms = porting::getTimeMs();
// only draw when the user will notice something:
do_draw = (time_ms - targs->last_time_ms > 100);
}
@@ -1697,7 +1730,7 @@ void Client::afterContentReceived(IrrlichtDevice *device)
TextureUpdateArgs tu_args;
tu_args.device = device;
tu_args.guienv = guienv;
- tu_args.last_time_ms = getTimeMs();
+ tu_args.last_time_ms = porting::getTimeMs();
tu_args.last_percent = 0;
tu_args.text_base = wgettext("Initializing nodes");
tu_args.tsrc = m_tsrc;
@@ -1729,7 +1762,7 @@ float Client::getRTT()
float Client::getCurRate()
{
- return ( m_con.getLocalStat(con::CUR_INC_RATE) +
+ return (m_con.getLocalStat(con::CUR_INC_RATE) +
m_con.getLocalStat(con::CUR_DL_RATE));
}
@@ -1930,4 +1963,3 @@ std::string Client::getModStoragePath() const
{
return porting::path_user + DIR_DELIM + "client" + DIR_DELIM + "mod_storage";
}
-
diff --git a/src/client.h b/src/client.h
index e7fcb597d..644549d7f 100644
--- a/src/client.h
+++ b/src/client.h
@@ -38,6 +38,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "tileanimation.h"
#include "mesh_generator_thread.h"
+#define CLIENT_CHAT_MESSAGE_LIMIT_PER_10S 10.0f
+
struct MeshMakeData;
class MapBlockMesh;
class IWritableTextureSource;
@@ -49,7 +51,7 @@ class ClientMediaDownloader;
struct MapDrawControl;
class MtEventManager;
struct PointedThing;
-class Database;
+class MapDatabase;
class Minimap;
struct MinimapMapblock;
class Camera;
@@ -77,6 +79,7 @@ enum ClientEventType
CE_HUDCHANGE,
CE_SET_SKY,
CE_OVERRIDE_DAY_NIGHT_RATIO,
+ CE_CLOUD_PARAMS,
};
struct ClientEvent
@@ -173,11 +176,21 @@ struct ClientEvent
video::SColor *bgcolor;
std::string *type;
std::vector<std::string> *params;
+ bool clouds;
} set_sky;
struct{
bool do_override;
float ratio_f;
} override_day_night_ratio;
+ struct {
+ f32 density;
+ u32 color_bright;
+ u32 color_ambient;
+ f32 height;
+ f32 thickness;
+ f32 speed_x;
+ f32 speed_y;
+ } cloud_params;
};
};
@@ -245,7 +258,8 @@ public:
Client(
IrrlichtDevice *device,
const char *playername,
- std::string password,
+ const std::string &password,
+ const std::string &address_name,
MapDrawControl &control,
IWritableTextureSource *tsrc,
IWritableShaderSource *shsrc,
@@ -273,9 +287,7 @@ public:
The name of the local player should already be set when
calling this, as it is sent in the initialization.
*/
- void connect(Address address,
- const std::string &address_name,
- bool is_local_server);
+ void connect(Address address, bool is_local_server);
/*
Stuff that references the environment is valid only as
@@ -311,16 +323,14 @@ public:
void handleCommand_HP(NetworkPacket* pkt);
void handleCommand_Breath(NetworkPacket* pkt);
void handleCommand_MovePlayer(NetworkPacket* pkt);
- void handleCommand_PlayerItem(NetworkPacket* pkt);
void handleCommand_DeathScreen(NetworkPacket* pkt);
void handleCommand_AnnounceMedia(NetworkPacket* pkt);
void handleCommand_Media(NetworkPacket* pkt);
- void handleCommand_ToolDef(NetworkPacket* pkt);
void handleCommand_NodeDef(NetworkPacket* pkt);
- void handleCommand_CraftItemDef(NetworkPacket* pkt);
void handleCommand_ItemDef(NetworkPacket* pkt);
void handleCommand_PlaySound(NetworkPacket* pkt);
void handleCommand_StopSound(NetworkPacket* pkt);
+ void handleCommand_FadeSound(NetworkPacket *pkt);
void handleCommand_Privileges(NetworkPacket* pkt);
void handleCommand_InventoryFormSpec(NetworkPacket* pkt);
void handleCommand_DetachedInventory(NetworkPacket* pkt);
@@ -334,6 +344,7 @@ public:
void handleCommand_HudSetFlags(NetworkPacket* pkt);
void handleCommand_HudSetParam(NetworkPacket* pkt);
void handleCommand_HudSetSky(NetworkPacket* pkt);
+ void handleCommand_CloudParams(NetworkPacket* pkt);
void handleCommand_OverrideDayNightRatio(NetworkPacket* pkt);
void handleCommand_LocalPlayerAnimations(NetworkPacket* pkt);
void handleCommand_EyeOffset(NetworkPacket* pkt);
@@ -351,6 +362,7 @@ public:
const StringMap &fields);
void sendInventoryAction(InventoryAction *a);
void sendChatMessage(const std::wstring &message);
+ void clearOutChatQueue();
void sendChangePassword(const std::string &oldpassword,
const std::string &newpassword);
void sendDamage(u8 damage);
@@ -417,7 +429,8 @@ public:
void updateCameraOffset(v3s16 camera_offset)
{ m_mesh_update_thread.m_camera_offset = camera_offset; }
- // Get event from queue. CE_NONE is returned if queue is empty.
+ bool hasClientEvents() const { return !m_client_event_queue.empty(); }
+ // Get event from queue. If queue is empty, it triggers an assertion failure.
ClientEvent getClientEvent();
bool accessDenied() const { return m_access_denied; }
@@ -457,8 +470,7 @@ public:
Minimap* getMinimap() { return m_minimap; }
void setCamera(Camera* camera) { m_camera = camera; }
- Camera* getCamera ()
- { return m_camera; }
+ Camera* getCamera () { return m_camera; }
bool shouldShowMinimap() const;
@@ -487,8 +499,6 @@ public:
bool loadMedia(const std::string &data, const std::string &filename);
// Send a request for conventional media transfer
void request_media(const std::vector<std::string> &file_requests);
- // Send a notification that no conventional media transfer is needed
- void received_media();
LocalClientState getState() { return m_state; }
@@ -514,6 +524,18 @@ public:
void showGameFog(const bool show = true);
void showGameDebug(const bool show = true);
+ IrrlichtDevice *getDevice() const { return m_device; }
+
+ const Address getServerAddress()
+ {
+ return m_con.GetPeerAddress(PEER_ID_SERVER);
+ }
+
+ const std::string &getAddressName() const
+ {
+ return m_address_name;
+ }
+
private:
// Virtual methods from con::PeerHandler
@@ -546,11 +568,12 @@ private:
inline std::string getPlayerName()
{ return m_env.getLocalPlayer()->getName(); }
+ bool canSendChatMessage() const;
+
float m_packetcounter_timer;
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;
@@ -565,6 +588,7 @@ private:
ClientEnvironment m_env;
ParticleManager m_particle_manager;
con::Connection m_con;
+ std::string m_address_name;
IrrlichtDevice *m_device;
Camera *m_camera;
Minimap *m_minimap;
@@ -592,6 +616,9 @@ private:
//s32 m_daynight_i;
//u32 m_daynight_ratio;
std::queue<std::wstring> m_chat_queue;
+ std::queue<std::wstring> m_out_chat_queue;
+ u32 m_last_chat_message_sent;
+ float m_chat_message_allowance;
// The authentication methods we can use to enter sudo mode (=change password)
u32 m_sudo_auth_methods;
@@ -648,7 +675,7 @@ private:
LocalClientState m_state;
// Used for saving server map to disk client-side
- Database *m_localdb;
+ MapDatabase *m_localdb;
IntervalLimiter m_localdb_save_interval;
u16 m_cache_save_interval;
diff --git a/src/client/CMakeLists.txt b/src/client/CMakeLists.txt
index 5faa186a7..2d274ae68 100644
--- a/src/client/CMakeLists.txt
+++ b/src/client/CMakeLists.txt
@@ -1,5 +1,6 @@
set(client_SRCS
${CMAKE_CURRENT_SOURCE_DIR}/clientlauncher.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/inputhandler.cpp
${CMAKE_CURRENT_SOURCE_DIR}/tile.cpp
${CMAKE_CURRENT_SOURCE_DIR}/joystick_controller.cpp
PARENT_SCOPE
diff --git a/src/client/clientlauncher.cpp b/src/client/clientlauncher.cpp
index 249f6727a..289d1537b 100644
--- a/src/client/clientlauncher.cpp
+++ b/src/client/clientlauncher.cpp
@@ -42,31 +42,15 @@ gui::IGUIEnvironment *guienv = NULL;
gui::IGUIStaticText *guiroot = NULL;
MainMenuManager g_menumgr;
-bool noMenuActive()
+bool isMenuActive()
{
- return g_menumgr.menuCount() == 0;
+ return g_menumgr.menuCount() != 0;
}
// Passed to menus to allow disconnecting and exiting
MainGameCallback *g_gamecallback = NULL;
-// Instance of the time getter
-static TimeGetter *g_timegetter = NULL;
-
-u32 getTimeMs()
-{
- if (g_timegetter == NULL)
- return 0;
- return g_timegetter->getTime(PRECISION_MILLI);
-}
-
-u32 getTime(TimePrecision prec) {
- if (g_timegetter == NULL)
- return 0;
- return g_timegetter->getTime(prec);
-}
-
ClientLauncher::~ClientLauncher()
{
if (receiver)
@@ -96,9 +80,6 @@ bool ClientLauncher::run(GameParams &game_params, const Settings &cmd_args)
return false;
}
- // Create time getter
- g_timegetter = new IrrlichtTimeGetter(device);
-
// Speed tests (done after irrlicht is loaded to get timer)
if (cmd_args.getFlag("speedtests")) {
dstream << "Running speed tests" << std::endl;
@@ -515,7 +496,7 @@ void ClientLauncher::main_menu(MainMenuData *menudata)
infostream << "Waiting for other menus" << std::endl;
while (device->run() && *kill == false) {
- if (noMenuActive())
+ if (!isMenuActive())
break;
driver->beginScene(true, true, video::SColor(255, 128, 128, 128));
guienv->drawAll();
diff --git a/src/client/clientlauncher.h b/src/client/clientlauncher.h
index ab22d7aaa..4ff77bc03 100644
--- a/src/client/clientlauncher.h
+++ b/src/client/clientlauncher.h
@@ -24,42 +24,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "client/inputhandler.h"
#include "gameparams.h"
-// A small helper class
-class TimeGetter
-{
-public:
- virtual u32 getTime(TimePrecision prec) = 0;
-};
-
-// A precise irrlicht one
-class IrrlichtTimeGetter: public TimeGetter
-{
-public:
- IrrlichtTimeGetter(IrrlichtDevice *device):
- m_device(device)
- {}
- u32 getTime(TimePrecision prec)
- {
- if (prec == PRECISION_MILLI) {
- if (m_device == NULL)
- return 0;
- return m_device->getTimer()->getRealTime();
- } else {
- return porting::getTime(prec);
- }
- }
-private:
- IrrlichtDevice *m_device;
-};
-// Not so precise one which works without irrlicht
-class SimpleTimeGetter: public TimeGetter
-{
-public:
- u32 getTime(TimePrecision prec)
- {
- return porting::getTime(prec);
- }
-};
class ClientLauncher
{
diff --git a/src/client/inputhandler.cpp b/src/client/inputhandler.cpp
new file mode 100644
index 000000000..9c7a94c4e
--- /dev/null
+++ b/src/client/inputhandler.cpp
@@ -0,0 +1,119 @@
+/*
+Minetest
+Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
+Copyright (C) 2017 nerzhul, Loic Blot <loic.blot@unix-experience.fr>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "util/numeric.h"
+#include "inputhandler.h"
+#include "mainmenumanager.h"
+
+bool MyEventReceiver::OnEvent(const SEvent &event)
+{
+ /*
+ React to nothing here if a menu is active
+ */
+ if (isMenuActive()) {
+#ifdef HAVE_TOUCHSCREENGUI
+ if (m_touchscreengui) {
+ m_touchscreengui->Toggle(false);
+ }
+#endif
+ return g_menumgr.preprocessEvent(event);
+ }
+
+ // Remember whether each key is down or up
+ if (event.EventType == irr::EET_KEY_INPUT_EVENT) {
+ const KeyPress &keyCode = event.KeyInput;
+ if (keysListenedFor[keyCode]) {
+ if (event.KeyInput.PressedDown) {
+ keyIsDown.set(keyCode);
+ keyWasDown.set(keyCode);
+ } else {
+ keyIsDown.unset(keyCode);
+ }
+ return true;
+ }
+ }
+
+#ifdef HAVE_TOUCHSCREENGUI
+ // case of touchscreengui we have to handle different events
+ if (m_touchscreengui && event.EventType == irr::EET_TOUCH_INPUT_EVENT) {
+ m_touchscreengui->translateEvent(event);
+ return true;
+ }
+#endif
+
+ if (event.EventType == irr::EET_JOYSTICK_INPUT_EVENT) {
+ /* TODO add a check like:
+ if (event.JoystickEvent != joystick_we_listen_for)
+ return false;
+ */
+ return joystick->handleEvent(event.JoystickEvent);
+ }
+ // handle mouse events
+ if (event.EventType == irr::EET_MOUSE_INPUT_EVENT) {
+ if (isMenuActive()) {
+ left_active = false;
+ middle_active = false;
+ right_active = false;
+ } else {
+ left_active = event.MouseInput.isLeftPressed();
+ middle_active = event.MouseInput.isMiddlePressed();
+ right_active = event.MouseInput.isRightPressed();
+
+ if (event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN) {
+ leftclicked = true;
+ }
+ if (event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN) {
+ rightclicked = true;
+ }
+ if (event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP) {
+ leftreleased = true;
+ }
+ if (event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP) {
+ rightreleased = true;
+ }
+ if (event.MouseInput.Event == EMIE_MOUSE_WHEEL) {
+ mouse_wheel += event.MouseInput.Wheel;
+ }
+ }
+ } else if (event.EventType == irr::EET_LOG_TEXT_EVENT) {
+ static const LogLevel irr_loglev_conv[] = {
+ LL_VERBOSE, // ELL_DEBUG
+ LL_INFO, // ELL_INFORMATION
+ LL_WARNING, // ELL_WARNING
+ LL_ERROR, // ELL_ERROR
+ LL_NONE, // ELL_NONE
+ };
+ assert(event.LogEvent.Level < ARRLEN(irr_loglev_conv));
+ g_logger.log(irr_loglev_conv[event.LogEvent.Level],
+ std::string("Irrlicht: ") +
+ (const char *)event.LogEvent.Text);
+ return true;
+ }
+ /* always return false in order to continue processing events */
+ return false;
+}
+
+/*
+ * RandomInputHandler
+ */
+s32 RandomInputHandler::Rand(s32 min, s32 max)
+{
+ return (myrand() % (max - min + 1)) + min;
+}
diff --git a/src/client/inputhandler.h b/src/client/inputhandler.h
index 824b0da2e..7c422d189 100644
--- a/src/client/inputhandler.h
+++ b/src/client/inputhandler.h
@@ -22,104 +22,87 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "irrlichttypes_extrabloated.h"
#include "joystick_controller.h"
+#include <list>
+#include "keycode.h"
-class MyEventReceiver : public IEventReceiver
-{
-public:
- // This is the one method that we have to implement
- virtual bool OnEvent(const SEvent& event)
- {
- /*
- React to nothing here if a menu is active
- */
- if (noMenuActive() == false) {
#ifdef HAVE_TOUCHSCREENGUI
- if (m_touchscreengui != 0) {
- m_touchscreengui->Toggle(false);
- }
+#include "touchscreengui.h"
#endif
- return g_menumgr.preprocessEvent(event);
- }
- // Remember whether each key is down or up
- if (event.EventType == irr::EET_KEY_INPUT_EVENT) {
- const KeyPress &keyCode = event.KeyInput;
- if (keysListenedFor[keyCode]) {
- if (event.KeyInput.PressedDown) {
- keyIsDown.set(keyCode);
- keyWasDown.set(keyCode);
- } else {
- keyIsDown.unset(keyCode);
- }
- return true;
- }
- }
+class KeyList : private std::list<KeyPress>
+{
+ typedef std::list<KeyPress> super;
+ typedef super::iterator iterator;
+ typedef super::const_iterator const_iterator;
-#ifdef HAVE_TOUCHSCREENGUI
- // case of touchscreengui we have to handle different events
- if ((m_touchscreengui != 0) &&
- (event.EventType == irr::EET_TOUCH_INPUT_EVENT)) {
- m_touchscreengui->translateEvent(event);
- return true;
- }
-#endif
+ virtual const_iterator find(const KeyPress &key) const
+ {
+ const_iterator f(begin());
+ const_iterator e(end());
+
+ while (f != e) {
+ if (*f == key)
+ return f;
- if (event.EventType == irr::EET_JOYSTICK_INPUT_EVENT) {
- /* TODO add a check like:
- if (event.JoystickEvent != joystick_we_listen_for)
- return false;
- */
- return joystick->handleEvent(event.JoystickEvent);
+ ++f;
}
- // handle mouse events
- if (event.EventType == irr::EET_MOUSE_INPUT_EVENT) {
- if (noMenuActive() == false) {
- left_active = false;
- middle_active = false;
- right_active = false;
- } else {
- left_active = event.MouseInput.isLeftPressed();
- middle_active = event.MouseInput.isMiddlePressed();
- right_active = event.MouseInput.isRightPressed();
-
- if (event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN) {
- leftclicked = true;
- }
- if (event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN) {
- rightclicked = true;
- }
- if (event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP) {
- leftreleased = true;
- }
- if (event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP) {
- rightreleased = true;
- }
- if (event.MouseInput.Event == EMIE_MOUSE_WHEEL) {
- mouse_wheel += event.MouseInput.Wheel;
- }
- }
- } else if (event.EventType == irr::EET_LOG_TEXT_EVENT) {
- static const LogLevel irr_loglev_conv[] = {
- LL_VERBOSE, // ELL_DEBUG
- LL_INFO, // ELL_INFORMATION
- LL_WARNING, // ELL_WARNING
- LL_ERROR, // ELL_ERROR
- LL_NONE, // ELL_NONE
- };
- assert(event.LogEvent.Level < ARRLEN(irr_loglev_conv));
- g_logger.log(irr_loglev_conv[event.LogEvent.Level],
- std::string("Irrlicht: ") + (const char*) event.LogEvent.Text);
- return true;
+
+ return e;
+ }
+
+ virtual iterator find(const KeyPress &key)
+ {
+ iterator f(begin());
+ iterator e(end());
+
+ while (f != e) {
+ if (*f == key)
+ return f;
+
+ ++f;
}
- /* always return false in order to continue processing events */
- return false;
+
+ return e;
+ }
+
+public:
+ void clear() { super::clear(); }
+
+ void set(const KeyPress &key)
+ {
+ if (find(key) == end())
+ push_back(key);
}
- bool IsKeyDown(const KeyPress &keyCode) const
+ void unset(const KeyPress &key)
{
- return keyIsDown[keyCode];
+ iterator p(find(key));
+
+ if (p != end())
+ erase(p);
}
+ void toggle(const KeyPress &key)
+ {
+ iterator p(this->find(key));
+
+ if (p != end())
+ erase(p);
+ else
+ push_back(key);
+ }
+
+ bool operator[](const KeyPress &key) const { return find(key) != end(); }
+};
+
+class MyEventReceiver : public IEventReceiver
+{
+public:
+ // This is the one method that we have to implement
+ virtual bool OnEvent(const SEvent &event);
+
+ bool IsKeyDown(const KeyPress &keyCode) const { return keyIsDown[keyCode]; }
+
// Checks whether a key was down and resets the state
bool WasKeyDown(const KeyPress &keyCode)
{
@@ -129,14 +112,8 @@ public:
return b;
}
- void listenForKey(const KeyPress &keyCode)
- {
- keysListenedFor.set(keyCode);
- }
- void dontListenForKeys()
- {
- keysListenedFor.clear();
- }
+ void listenForKey(const KeyPress &keyCode) { keysListenedFor.set(keyCode); }
+ void dontListenForKeys() { keysListenedFor.clear(); }
s32 getMouseWheel()
{
@@ -184,7 +161,7 @@ public:
JoystickController *joystick;
#ifdef HAVE_TOUCHSCREENGUI
- TouchScreenGUI* m_touchscreengui;
+ TouchScreenGUI *m_touchscreengui;
#endif
private:
@@ -200,7 +177,42 @@ private:
KeyList keysListenedFor;
};
+class InputHandler
+{
+public:
+ InputHandler() {}
+ virtual ~InputHandler() {}
+
+ virtual bool isKeyDown(const KeyPress &keyCode) = 0;
+ virtual bool wasKeyDown(const KeyPress &keyCode) = 0;
+
+ virtual void listenForKey(const KeyPress &keyCode) {}
+ virtual void dontListenForKeys() {}
+
+ virtual v2s32 getMousePos() = 0;
+ virtual void setMousePos(s32 x, s32 y) = 0;
+
+ virtual bool getLeftState() = 0;
+ virtual bool getRightState() = 0;
+
+ virtual bool getLeftClicked() = 0;
+ virtual bool getRightClicked() = 0;
+ virtual void resetLeftClicked() = 0;
+ virtual void resetRightClicked() = 0;
+
+ virtual bool getLeftReleased() = 0;
+ virtual bool getRightReleased() = 0;
+ virtual void resetLeftReleased() = 0;
+ virtual void resetRightReleased() = 0;
+ virtual s32 getMouseWheel() = 0;
+
+ virtual void step(float dtime) {}
+
+ virtual void clear() {}
+
+ JoystickController joystick;
+};
/*
Separated input handler
*/
@@ -208,10 +220,8 @@ private:
class RealInputHandler : public InputHandler
{
public:
- RealInputHandler(IrrlichtDevice *device, MyEventReceiver *receiver):
- m_device(device),
- m_receiver(receiver),
- m_mousepos(0,0)
+ RealInputHandler(IrrlichtDevice *device, MyEventReceiver *receiver)
+ : m_device(device), m_receiver(receiver), m_mousepos(0, 0)
{
m_receiver->joystick = &joystick;
}
@@ -227,16 +237,12 @@ public:
{
m_receiver->listenForKey(keyCode);
}
- virtual void dontListenForKeys()
- {
- m_receiver->dontListenForKeys();
- }
+ virtual void dontListenForKeys() { m_receiver->dontListenForKeys(); }
virtual v2s32 getMousePos()
{
if (m_device->getCursorControl()) {
return m_device->getCursorControl()->getPosition();
- }
- else {
+ } else {
return m_mousepos;
}
}
@@ -244,69 +250,36 @@ public:
{
if (m_device->getCursorControl()) {
m_device->getCursorControl()->setPosition(x, y);
- }
- else {
- m_mousepos = v2s32(x,y);
+ } else {
+ m_mousepos = v2s32(x, y);
}
}
- virtual bool getLeftState()
- {
- return m_receiver->left_active;
- }
- virtual bool getRightState()
- {
- return m_receiver->right_active;
- }
+ virtual bool getLeftState() { return m_receiver->left_active; }
+ virtual bool getRightState() { return m_receiver->right_active; }
- virtual bool getLeftClicked()
- {
- return m_receiver->leftclicked;
- }
- virtual bool getRightClicked()
- {
- return m_receiver->rightclicked;
- }
- virtual void resetLeftClicked()
- {
- m_receiver->leftclicked = false;
- }
- virtual void resetRightClicked()
- {
- m_receiver->rightclicked = false;
- }
+ virtual bool getLeftClicked() { return m_receiver->leftclicked; }
+ virtual bool getRightClicked() { return m_receiver->rightclicked; }
+ virtual void resetLeftClicked() { m_receiver->leftclicked = false; }
+ virtual void resetRightClicked() { m_receiver->rightclicked = false; }
- virtual bool getLeftReleased()
- {
- return m_receiver->leftreleased;
- }
- virtual bool getRightReleased()
- {
- return m_receiver->rightreleased;
- }
- virtual void resetLeftReleased()
- {
- m_receiver->leftreleased = false;
- }
- virtual void resetRightReleased()
- {
- m_receiver->rightreleased = false;
- }
+ virtual bool getLeftReleased() { return m_receiver->leftreleased; }
+ virtual bool getRightReleased() { return m_receiver->rightreleased; }
+ virtual void resetLeftReleased() { m_receiver->leftreleased = false; }
+ virtual void resetRightReleased() { m_receiver->rightreleased = false; }
- virtual s32 getMouseWheel()
- {
- return m_receiver->getMouseWheel();
- }
+ virtual s32 getMouseWheel() { return m_receiver->getMouseWheel(); }
void clear()
{
joystick.clear();
m_receiver->clearInput();
}
+
private:
- IrrlichtDevice *m_device;
+ IrrlichtDevice *m_device;
MyEventReceiver *m_receiver;
- v2s32 m_mousepos;
+ v2s32 m_mousepos;
};
class RandomInputHandler : public InputHandler
@@ -322,70 +295,25 @@ public:
rightreleased = false;
keydown.clear();
}
- virtual bool isKeyDown(const KeyPress &keyCode)
- {
- return keydown[keyCode];
- }
- virtual bool wasKeyDown(const KeyPress &keyCode)
- {
- return false;
- }
- virtual v2s32 getMousePos()
- {
- return mousepos;
- }
- virtual void setMousePos(s32 x, s32 y)
- {
- mousepos = v2s32(x, y);
- }
+ virtual bool isKeyDown(const KeyPress &keyCode) { return keydown[keyCode]; }
+ virtual bool wasKeyDown(const KeyPress &keyCode) { return false; }
+ virtual v2s32 getMousePos() { return mousepos; }
+ virtual void setMousePos(s32 x, s32 y) { mousepos = v2s32(x, y); }
- virtual bool getLeftState()
- {
- return leftdown;
- }
- virtual bool getRightState()
- {
- return rightdown;
- }
+ virtual bool getLeftState() { return leftdown; }
+ virtual bool getRightState() { return rightdown; }
- virtual bool getLeftClicked()
- {
- return leftclicked;
- }
- virtual bool getRightClicked()
- {
- return rightclicked;
- }
- virtual void resetLeftClicked()
- {
- leftclicked = false;
- }
- virtual void resetRightClicked()
- {
- rightclicked = false;
- }
+ virtual bool getLeftClicked() { return leftclicked; }
+ virtual bool getRightClicked() { return rightclicked; }
+ virtual void resetLeftClicked() { leftclicked = false; }
+ virtual void resetRightClicked() { rightclicked = false; }
- virtual bool getLeftReleased()
- {
- return leftreleased;
- }
- virtual bool getRightReleased()
- {
- return rightreleased;
- }
- virtual void resetLeftReleased()
- {
- leftreleased = false;
- }
- virtual void resetRightReleased()
- {
- rightreleased = false;
- }
+ virtual bool getLeftReleased() { return leftreleased; }
+ virtual bool getRightReleased() { return rightreleased; }
+ virtual void resetLeftReleased() { leftreleased = false; }
+ virtual void resetRightReleased() { rightreleased = false; }
- virtual s32 getMouseWheel()
- {
- return 0;
- }
+ virtual s32 getMouseWheel() { return 0; }
virtual void step(float dtime)
{
@@ -456,10 +384,8 @@ public:
mousepos += mousespeed;
}
- s32 Rand(s32 min, s32 max)
- {
- return (myrand()%(max-min+1))+min;
- }
+ s32 Rand(s32 min, s32 max);
+
private:
KeyList keydown;
v2s32 mousepos;
diff --git a/src/client/joystick_controller.cpp b/src/client/joystick_controller.cpp
index e6a572adb..905ca6420 100644
--- a/src/client/joystick_controller.cpp
+++ b/src/client/joystick_controller.cpp
@@ -22,6 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "keys.h"
#include "settings.h"
#include "gettime.h"
+#include "porting.h"
#include "../util/string.h"
bool JoystickButtonCmb::isTriggered(const irr::SEvent::SJoystickEvent &ev) const
@@ -185,8 +186,9 @@ void JoystickController::onJoystickConnect(const std::vector<irr::SJoystickInfo>
m_joystick_id = id;
}
-void JoystickController::setLayoutFromControllerName(std::string name) {
- if (lowercase(name).find("xbox") >= 0) {
+void JoystickController::setLayoutFromControllerName(const std::string &name)
+{
+ if (lowercase(name).find("xbox") != std::string::npos) {
m_layout = create_xbox_layout();
} else {
m_layout = create_default_layout();
@@ -198,7 +200,7 @@ bool JoystickController::handleEvent(const irr::SEvent::SJoystickEvent &ev)
if (ev.Joystick != m_joystick_id)
return false;
- m_internal_time = getTimeMs() / 1000.f;
+ m_internal_time = porting::getTimeMs() / 1000.f;
std::bitset<KeyType::INTERNAL_ENUM_COUNT> keys_pressed;
diff --git a/src/client/joystick_controller.h b/src/client/joystick_controller.h
index 867a0c3f2..744315011 100644
--- a/src/client/joystick_controller.h
+++ b/src/client/joystick_controller.h
@@ -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;
@@ -149,7 +153,7 @@ public:
f32 doubling_dtime;
private:
- void setLayoutFromControllerName(std::string name);
+ void setLayoutFromControllerName(const std::string &name);
JoystickLayout m_layout;
diff --git a/src/client/keys.h b/src/client/keys.h
index 76ae38ff0..9478737f6 100644
--- a/src/client/keys.h
+++ b/src/client/keys.h
@@ -50,6 +50,11 @@ public:
FREEMOVE,
FASTMOVE,
NOCLIP,
+ HOTBAR_PREV,
+ HOTBAR_NEXT,
+ MUTE,
+ INC_VOLUME,
+ DEC_VOLUME,
CINEMATIC,
SCREENSHOT,
TOGGLE_HUD,
diff --git a/src/client/tile.cpp b/src/client/tile.cpp
index f0f1800b0..cfdff1bb2 100644
--- a/src/client/tile.cpp
+++ b/src/client/tile.cpp
@@ -1208,17 +1208,17 @@ bool TextureSource::generateImagePart(std::string part_of_name,
#endif
if (image == NULL) {
if (part_of_name != "") {
- if (part_of_name.find("_normal.png") == std::string::npos){
- errorstream<<"generateImage(): Could not load image \""
- <<part_of_name<<"\""<<" while building texture"<<std::endl;
- errorstream<<"generateImage(): Creating a dummy"
- <<" image for \""<<part_of_name<<"\""<<std::endl;
- } else {
- infostream<<"generateImage(): Could not load normal map \""
- <<part_of_name<<"\""<<std::endl;
- infostream<<"generateImage(): Creating a dummy"
- <<" normal map for \""<<part_of_name<<"\""<<std::endl;
+
+ // Do not create normalmap dummies
+ if (part_of_name.find("_normal.png") != std::string::npos) {
+ warningstream << "generateImage(): Could not load normal map \""
+ << part_of_name << "\"" << std::endl;
+ return true;
}
+
+ errorstream << "generateImage(): Could not load image \""
+ << part_of_name << "\" while building texture; "
+ "Creating a dummy image" << std::endl;
}
// Just create a dummy image
@@ -1805,7 +1805,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();
diff --git a/src/client/tile.h b/src/client/tile.h
index 5eec0f2ea..66ca8be1d 100644
--- a/src/client/tile.h
+++ b/src/client/tile.h
@@ -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
@@ -194,72 +195,68 @@ struct FrameSpec
video::ITexture *flags_texture;
};
-struct TileSpec
+#define MAX_TILE_LAYERS 2
+
+//! Defines a layer of a tile.
+struct TileLayer
{
- TileSpec():
- texture_id(0),
+ TileLayer():
texture(NULL),
normal_texture(NULL),
flags_texture(NULL),
+ shader_id(0),
+ texture_id(0),
+ animation_frame_length_ms(0),
+ animation_frame_count(1),
material_type(TILE_MATERIAL_BASIC),
material_flags(
//0 // <- DEBUG, Use the one below
- MATERIAL_FLAG_BACKFACE_CULLING
+ MATERIAL_FLAG_BACKFACE_CULLING |
+ MATERIAL_FLAG_TILEABLE_HORIZONTAL|
+ MATERIAL_FLAG_TILEABLE_VERTICAL
),
- shader_id(0),
- animation_frame_count(1),
- animation_frame_length_ms(0),
- rotation(0),
has_color(false),
- color(),
- emissive_light(0)
+ color()
{
}
/*!
- * Two tiles are equal if they can be appended to
- * the same mesh buffer.
+ * Two layers are equal if they can be merged.
*/
- bool operator==(const TileSpec &other) const
+ bool operator==(const TileLayer &other) const
{
- return (
+ return
texture_id == other.texture_id &&
material_type == other.material_type &&
material_flags == other.material_flags &&
- rotation == other.rotation
- );
+ color == other.color;
}
/*!
- * Two tiles are not equal if they must be in different mesh buffers.
+ * Two tiles are not equal if they must have different vertices.
*/
- bool operator!=(const TileSpec &other) const
+ bool operator!=(const TileLayer &other) const
{
return !(*this == other);
}
-
+
// Sets everything else except the texture in the material
void applyMaterialOptions(video::SMaterial &material) const
{
switch (material_type) {
- case TILE_MATERIAL_BASIC:
- material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
- break;
- case TILE_MATERIAL_ALPHA:
- material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
- break;
- case TILE_MATERIAL_LIQUID_TRANSPARENT:
- material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
- break;
+ case TILE_MATERIAL_OPAQUE:
case TILE_MATERIAL_LIQUID_OPAQUE:
material.MaterialType = video::EMT_SOLID;
break;
+ case TILE_MATERIAL_BASIC:
case TILE_MATERIAL_WAVING_LEAVES:
- material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
- break;
case TILE_MATERIAL_WAVING_PLANTS:
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;
}
material.BackfaceCulling = (material_flags & MATERIAL_FLAG_BACKFACE_CULLING)
? true : false;
@@ -284,30 +281,73 @@ struct TileSpec
material.TextureLayer[1].TextureWrapV = video::ETC_CLAMP_TO_EDGE;
}
}
-
- u32 texture_id;
+
+ bool isTileable() const
+ {
+ return (material_flags & MATERIAL_FLAG_TILEABLE_HORIZONTAL)
+ && (material_flags & MATERIAL_FLAG_TILEABLE_VERTICAL);
+ }
+
+ // Ordered for size, please do not reorder
+
video::ITexture *texture;
video::ITexture *normal_texture;
video::ITexture *flags_texture;
-
- // Material parameters
- u8 material_type;
- u8 material_flags;
+
u32 shader_id;
- // Animation parameters
- u8 animation_frame_count;
+
+ u32 texture_id;
+
u16 animation_frame_length_ms;
- std::vector<FrameSpec> frames;
+ u8 animation_frame_count;
+
+ u8 material_type;
+ u8 material_flags;
- u8 rotation;
//! If true, the tile has its own color.
bool has_color;
+
+ std::vector<FrameSpec> frames;
+
/*!
* The color of the tile, or if the tile does not own
* a color then the color of the node owning this tile.
*/
video::SColor color;
+};
+
+/*!
+ * Defines a face of a node. May have up to two layers.
+ */
+struct TileSpec
+{
+ TileSpec():
+ rotation(0),
+ emissive_light(0)
+ {
+ for (int layer = 0; layer < MAX_TILE_LAYERS; layer++)
+ layers[layer] = TileLayer();
+ }
+
+ /*!
+ * Returns true if this tile can be merged with the other tile.
+ */
+ bool isTileable(const TileSpec &other) const {
+ for (int layer = 0; layer < MAX_TILE_LAYERS; layer++) {
+ if (layers[layer] != other.layers[layer])
+ return false;
+ if (!layers[layer].isTileable())
+ return false;
+ }
+ return rotation == 0
+ && rotation == other.rotation
+ && emissive_light == other.emissive_light;
+ }
+
+ u8 rotation;
//! This much light does the tile emit.
u8 emissive_light;
+ //! The first is base texture, the second is overlay.
+ TileLayer layers[MAX_TILE_LAYERS];
};
#endif
diff --git a/src/clientenvironment.cpp b/src/clientenvironment.cpp
index 4a8bbb066..cc7cb54dd 100644
--- a/src/clientenvironment.cpp
+++ b/src/clientenvironment.cpp
@@ -22,7 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "clientenvironment.h"
#include "clientsimpleobject.h"
#include "clientmap.h"
-#include "clientscripting.h"
+#include "scripting_client.h"
#include "mapblock_mesh.h"
#include "event.h"
#include "collision.h"
@@ -30,6 +30,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "raycast.h"
#include "voxelalgorithms.h"
#include "settings.h"
+#include <algorithm>
/*
ClientEnvironment
@@ -236,11 +237,10 @@ void ClientEnvironment::step(float dtime)
pre_factor = 1.0 + (float)addp/100.0;
}
float speed = pre_factor * speed_diff.getLength();
- if(speed > tolerance)
- {
- f32 damage_f = (speed - tolerance)/BS * post_factor;
- u16 damage = (u16)(damage_f+0.5);
- if(damage != 0){
+ if (speed > tolerance) {
+ f32 damage_f = (speed - tolerance) / BS * post_factor;
+ u8 damage = (u8)MYMIN(damage_f + 0.5, 255);
+ if (damage != 0) {
damageLocalPlayer(damage, true);
MtEvent *e = new SimpleTriggerEvent("PlayerFallingDamage");
m_client->event()->put(e);
@@ -598,15 +598,13 @@ void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
}
}
-ClientEnvEvent ClientEnvironment::getClientEvent()
+ClientEnvEvent ClientEnvironment::getClientEnvEvent()
{
- ClientEnvEvent event;
- if(m_client_event_queue.empty())
- event.type = CEE_NONE;
- else {
- event = m_client_event_queue.front();
- m_client_event_queue.pop();
- }
+ FATAL_ERROR_IF(m_client_event_queue.empty(),
+ "ClientEnvironment::getClientEnvEvent(): queue is empty");
+
+ ClientEnvEvent event = m_client_event_queue.front();
+ m_client_event_queue.pop();
return event;
}
diff --git a/src/clientenvironment.h b/src/clientenvironment.h
index c273ede54..79b4797ad 100644
--- a/src/clientenvironment.h
+++ b/src/clientenvironment.h
@@ -126,8 +126,9 @@ public:
void getActiveObjects(v3f origin, f32 max_d,
std::vector<DistanceSortedActiveObject> &dest);
- // Get event from queue. CEE_NONE is returned if queue is empty.
- ClientEnvEvent getClientEvent();
+ bool hasClientEnvEvents() const { return !m_client_event_queue.empty(); }
+ // Get event from queue. If queue is empty, it triggers an assertion failure.
+ ClientEnvEvent getClientEnvEvent();
/*!
* Gets closest object pointed by the shootline.
diff --git a/src/clientiface.cpp b/src/clientiface.cpp
index 64fa1c6b7..475397279 100644
--- a/src/clientiface.cpp
+++ b/src/clientiface.cpp
@@ -20,7 +20,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <sstream>
#include "clientiface.h"
-#include "util/numeric.h"
#include "remoteplayer.h"
#include "settings.h"
#include "mapblock.h"
@@ -32,6 +31,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "log.h"
#include "network/serveropcodes.h"
#include "util/srp.h"
+#include "face_position_cache.h"
const char *ClientInterface::statenames[] = {
"Invalid",
@@ -590,9 +590,9 @@ void RemoteClient::notifyEvent(ClientStateEvent event)
}
}
-u32 RemoteClient::uptime()
+u64 RemoteClient::uptime() const
{
- return getTime(PRECISION_SECONDS) - m_connection_time;
+ return porting::getTimeS() - m_connection_time;
}
ClientInterface::ClientInterface(con::Connection* con)
@@ -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;
diff --git a/src/clientiface.h b/src/clientiface.h
index 403cd0420..6e7429309 100644
--- a/src/clientiface.h
+++ b/src/clientiface.h
@@ -26,6 +26,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "threading/mutex.h"
#include "network/networkpacket.h"
#include "util/cpp11_container.h"
+#include "porting.h"
#include <list>
#include <vector>
@@ -265,7 +266,7 @@ public:
m_version_patch(0),
m_full_version("unknown"),
m_deployed_compression(0),
- m_connection_time(getTime(PRECISION_SECONDS))
+ m_connection_time(porting::getTimeS())
{
}
~RemoteClient()
@@ -324,14 +325,11 @@ public:
*/
std::set<u16> m_known_objects;
- ClientState getState()
- { return m_state; }
+ ClientState getState() const { return m_state; }
- std::string getName()
- { return m_name; }
+ std::string getName() const { return m_name; }
- void setName(std::string name)
- { m_name = name; }
+ void setName(const std::string &name) { m_name = name; }
/* update internal client state */
void notifyEvent(ClientStateEvent event);
@@ -347,10 +345,11 @@ public:
{ serialization_version = m_pending_serialization_version; }
/* get uptime */
- u32 uptime();
+ u64 uptime() const;
/* set version information */
- void setVersionInfo(u8 major, u8 minor, u8 patch, std::string full) {
+ void setVersionInfo(u8 major, u8 minor, u8 patch, const std::string &full)
+ {
m_version_major = major;
m_version_minor = minor;
m_version_patch = patch;
@@ -358,10 +357,9 @@ public:
}
/* read version information */
- u8 getMajor() { return m_version_major; }
- u8 getMinor() { return m_version_minor; }
- u8 getPatch() { return m_version_patch; }
- std::string getVersion() { return m_full_version; }
+ u8 getMajor() const { return m_version_major; }
+ u8 getMinor() const { return m_version_minor; }
+ u8 getPatch() const { return m_version_patch; }
private:
// Version is stored in here after INIT before INIT2
u8 m_pending_serialization_version;
@@ -434,7 +432,7 @@ private:
/*
time this client was created
*/
- const u32 m_connection_time;
+ const u64 m_connection_time;
};
class ClientInterface {
@@ -451,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; }
@@ -495,7 +496,6 @@ public:
}
static std::string state2Name(ClientState state);
-
protected:
//TODO find way to avoid this functions
void lock() { m_clients_mutex.lock(); }
diff --git a/src/clientmap.cpp b/src/clientmap.cpp
index 4cae03bf2..21937ac81 100644
--- a/src/clientmap.cpp
+++ b/src/clientmap.cpp
@@ -296,34 +296,40 @@ struct MeshBufList
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)
+ 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.m == m) {
- l.bufs.push_back(buf);
+ if ((*it).m == m) {
+ (*it).bufs.push_back(buf);
return;
}
}
MeshBufList l;
l.m = m;
l.bufs.push_back(buf);
- lists.push_back(l);
+ list.push_back(l);
}
};
@@ -351,7 +357,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
@@ -434,63 +440,67 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
MapBlockMesh *mapBlockMesh = block->mesh;
assert(mapBlockMesh);
- scene::IMesh *mesh = mapBlockMesh->getMesh();
- assert(mesh);
+ for (int layer = 0; layer < MAX_TILE_LAYERS; layer++) {
+ scene::IMesh *mesh = mapBlockMesh->getMesh(layer);
+ assert(mesh);
- u32 c = mesh->getMeshBufferCount();
- for (u32 i = 0; i < c; i++)
- {
- scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
+ u32 c = mesh->getMeshBufferCount();
+ for (u32 i = 0; i < c; i++) {
+ scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
- video::SMaterial& material = buf->getMaterial();
- video::IMaterialRenderer* rnd =
+ video::SMaterial& material = buf->getMaterial();
+ video::IMaterialRenderer* rnd =
driver->getMaterialRenderer(material.MaterialType);
- bool transparent = (rnd && rnd->isTransparent());
- if (transparent == is_transparent_pass) {
- if (buf->getVertexCount() == 0)
- errorstream << "Block [" << analyze_block(block)
- << "] contains an empty meshbuf" << std::endl;
-
- material.setFlag(video::EMF_TRILINEAR_FILTER, m_cache_trilinear_filter);
- material.setFlag(video::EMF_BILINEAR_FILTER, m_cache_bilinear_filter);
- material.setFlag(video::EMF_ANISOTROPIC_FILTER, m_cache_anistropic_filter);
- material.setFlag(video::EMF_WIREFRAME, m_control.show_wireframe);
-
- drawbufs.add(buf);
+ bool transparent = (rnd && rnd->isTransparent());
+ if (transparent == is_transparent_pass) {
+ if (buf->getVertexCount() == 0)
+ errorstream << "Block [" << analyze_block(block)
+ << "] contains an empty meshbuf" << std::endl;
+
+ material.setFlag(video::EMF_TRILINEAR_FILTER,
+ m_cache_trilinear_filter);
+ material.setFlag(video::EMF_BILINEAR_FILTER,
+ m_cache_bilinear_filter);
+ material.setFlag(video::EMF_ANISOTROPIC_FILTER,
+ m_cache_anistropic_filter);
+ material.setFlag(video::EMF_WIREFRAME,
+ m_control.show_wireframe);
+
+ drawbufs.add(buf, layer);
+ }
}
}
}
}
- std::vector<MeshBufList> &lists = drawbufs.lists;
-
- int timecheck_counter = 0;
- for (std::vector<MeshBufList>::iterator i = lists.begin();
- i != lists.end(); ++i) {
- timecheck_counter++;
- if (timecheck_counter > 50) {
- timecheck_counter = 0;
- int time2 = time(0);
- if (time2 > time1 + 4) {
- infostream << "ClientMap::renderMap(): "
- "Rendering takes ages, returning."
- << std::endl;
- return;
+ // 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 it = lists.begin(); it != lists.end();
+ ++it) {
+ timecheck_counter++;
+ if (timecheck_counter > 50) {
+ timecheck_counter = 0;
+ time_t time2 = time(0);
+ if (time2 > time1 + 4) {
+ infostream << "ClientMap::renderMap(): "
+ "Rendering takes ages, returning."
+ << std::endl;
+ return;
+ }
}
- }
-
- MeshBufList &list = *i;
- driver->setMaterial(list.m);
+ driver->setMaterial((*it).m);
- for (std::vector<scene::IMeshBuffer*>::iterator j = list.bufs.begin();
- j != list.bufs.end(); ++j) {
- scene::IMeshBuffer *buf = *j;
- driver->drawMeshBuffer(buf);
- vertex_count += buf->getVertexCount();
- meshbuffer_count++;
+ 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
diff --git a/src/clientmedia.cpp b/src/clientmedia.cpp
index bca3f67c2..9c1e430df 100644
--- a/src/clientmedia.cpp
+++ b/src/clientmedia.cpp
@@ -42,12 +42,12 @@ static std::string getMediaCacheDir()
*/
ClientMediaDownloader::ClientMediaDownloader():
- m_media_cache(getMediaCacheDir())
+ m_media_cache(getMediaCacheDir()),
+ m_initial_step_done(false),
+ m_uncached_count(0),
+ m_uncached_received_count(0),
+ m_name_bound("")
{
- m_initial_step_done = false;
- m_name_bound = ""; // works because "" is an invalid file name
- m_uncached_count = 0;
- m_uncached_received_count = 0;
m_httpfetch_caller = HTTPFETCH_DISCARD;
m_httpfetch_active = 0;
m_httpfetch_active_limit = 0;
@@ -69,7 +69,7 @@ ClientMediaDownloader::~ClientMediaDownloader()
delete m_remotes[i];
}
-void ClientMediaDownloader::addFile(std::string name, std::string sha1)
+void ClientMediaDownloader::addFile(const std::string &name, const std::string &sha1)
{
assert(!m_initial_step_done); // pre-condition
@@ -104,7 +104,7 @@ void ClientMediaDownloader::addFile(std::string name, std::string sha1)
m_files.insert(std::make_pair(name, filestatus));
}
-void ClientMediaDownloader::addRemoteServer(std::string baseurl)
+void ClientMediaDownloader::addRemoteServer(const std::string &baseurl)
{
assert(!m_initial_step_done); // pre-condition
@@ -348,7 +348,7 @@ void ClientMediaDownloader::remoteMediaReceived(
std::string name;
{
- std::map<unsigned long, std::string>::iterator it =
+ UNORDERED_MAP<unsigned long, std::string>::iterator it =
m_remote_file_transfers.find(fetch_result.request_id);
assert(it != m_remote_file_transfers.end());
name = it->second;
diff --git a/src/clientmedia.h b/src/clientmedia.h
index 1f0da70d9..3c96dfe8a 100644
--- a/src/clientmedia.h
+++ b/src/clientmedia.h
@@ -26,6 +26,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <map>
#include <set>
#include <vector>
+#include "util/cpp11_container.h"
class Client;
struct HTTPFetchResult;
@@ -58,10 +59,10 @@ public:
}
// Add a file to the list of required file (but don't fetch it yet)
- void addFile(std::string name, std::string sha1);
+ void addFile(const std::string &name, const std::string &sha1);
// Add a remote server to the list; ignored if not built with cURL
- void addRemoteServer(std::string baseurl);
+ void addRemoteServer(const std::string &baseurl);
// Steps the media downloader:
// - May load media into client by calling client->loadMedia()
@@ -137,7 +138,7 @@ private:
s32 m_httpfetch_active;
s32 m_httpfetch_active_limit;
s32 m_outstanding_hash_sets;
- std::map<unsigned long, std::string> m_remote_file_transfers;
+ UNORDERED_MAP<unsigned long, std::string> m_remote_file_transfers;
// All files up to this name have either been received from a
// remote server or failed on all remote servers, so those files
diff --git a/src/clientobject.h b/src/clientobject.h
index 1db5bcf24..aa0ec9c56 100644
--- a/src/clientobject.h
+++ b/src/clientobject.h
@@ -49,18 +49,13 @@ public:
virtual aabb3f *getSelectionBox() { return NULL; }
virtual bool getCollisionBox(aabb3f *toset) const { return false; }
virtual bool collideWithObjects() const { return false; }
- virtual v3f getPosition(){return v3f(0,0,0);}
- virtual float getYaw() const {return 0;}
- virtual scene::ISceneNode *getSceneNode(){return NULL;}
- virtual scene::IMeshSceneNode *getMeshSceneNode(){return NULL;}
- virtual scene::IAnimatedMeshSceneNode *getAnimatedMeshSceneNode(){return NULL;}
- virtual WieldMeshSceneNode *getWieldMeshSceneNode(){return NULL;}
- virtual scene::IBillboardSceneNode *getSpriteSceneNode(){return NULL;}
- virtual bool isPlayer() const {return false;}
+ virtual v3f getPosition(){ return v3f(0,0,0); }
+ virtual float getYaw() const { return 0; }
+ virtual scene::ISceneNode *getSceneNode() { return NULL; }
+ virtual scene::IAnimatedMeshSceneNode *getAnimatedMeshSceneNode() { return NULL; }
virtual bool isLocalPlayer() const {return false;}
- virtual void setAttachments(){}
+ virtual void setAttachments() {}
virtual bool doShowSelectionBox(){return true;}
- virtual void updateCameraOffset(v3s16 camera_offset){};
// Step object in time
virtual void step(float dtime, ClientEnvironment *env){}
diff --git a/src/cloudparams.h b/src/cloudparams.h
new file mode 100644
index 000000000..dafec4b27
--- /dev/null
+++ b/src/cloudparams.h
@@ -0,0 +1,33 @@
+/*
+Minetest
+Copyright (C) 2017 bendeutsch, Ben Deutsch <ben@bendeutsch.de>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef CLOUDPARAMS_HEADER
+#define CLOUDPARAMS_HEADER
+
+struct CloudParams
+{
+ float density;
+ video::SColor color_bright;
+ video::SColor color_ambient;
+ float thickness;
+ float height;
+ v2f speed;
+};
+
+#endif
diff --git a/src/clouds.cpp b/src/clouds.cpp
index 82b63b6b3..10a533cae 100644
--- a/src/clouds.cpp
+++ b/src/clouds.cpp
@@ -32,6 +32,7 @@ irr::scene::ISceneManager *g_menucloudsmgr = NULL;
static void cloud_3d_setting_changed(const std::string &settingname, void *data)
{
+ // TODO: only re-read cloud settings, not height or radius
((Clouds *)data)->readSettings();
}
@@ -44,9 +45,10 @@ Clouds::Clouds(
):
scene::ISceneNode(parent, mgr, id),
m_seed(seed),
- m_camera_pos(0,0),
- m_time(0),
- m_camera_offset(0,0,0)
+ m_camera_pos(0.0f, 0.0f),
+ m_origin(0.0f, 0.0f),
+ m_camera_offset(0.0f, 0.0f, 0.0f),
+ m_color(1.0f, 1.0f, 1.0f, 1.0f)
{
m_material.setFlag(video::EMF_LIGHTING, false);
//m_material.setFlag(video::EMF_BACK_FACE_CULLING, false);
@@ -57,14 +59,18 @@ Clouds::Clouds(
//m_material.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
m_material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
+ m_params.density = 0.4f;
+ m_params.thickness = 16.0f;
+ m_params.color_bright = video::SColor(229, 240, 240, 255);
+ m_params.color_ambient = video::SColor(255, 0, 0, 0);
+ m_params.speed = v2f(0.0f, -2.0f);
+
m_passed_cloud_y = cloudheight;
readSettings();
g_settings->registerChangedCallback("enable_3d_clouds",
&cloud_3d_setting_changed, this);
- m_box = aabb3f(-BS*1000000,m_cloud_y-BS,-BS*1000000,
- BS*1000000,m_cloud_y+BS,BS*1000000);
-
+ updateBox();
}
Clouds::~Clouds()
@@ -88,6 +94,10 @@ void Clouds::OnRegisterSceneNode()
void Clouds::render()
{
+
+ if (m_params.density <= 0.0f)
+ return; // no need to do anything
+
video::IVideoDriver* driver = SceneManager->getVideoDriver();
if(SceneManager->getSceneNodeRenderPass() != scene::ESNRP_TRANSPARENT)
@@ -107,15 +117,12 @@ void Clouds::render()
Clouds move from Z+ towards Z-
*/
- const float cloud_size = BS * 64;
- const v2f cloud_speed(0, -BS * 2);
+ static const float cloud_size = BS * 64.0f;
const float cloud_full_radius = cloud_size * m_cloud_radius_i;
- // Position of cloud noise origin in world coordinates
- v2f world_cloud_origin_pos_f = m_time * cloud_speed;
// Position of cloud noise origin from the camera
- v2f cloud_origin_from_camera_f = world_cloud_origin_pos_f - m_camera_pos;
+ v2f cloud_origin_from_camera_f = m_origin - m_camera_pos;
// The center point of drawing in the noise
v2f center_of_drawing_in_noise_f = -cloud_origin_from_camera_f;
// The integer center point of drawing in the noise
@@ -127,7 +134,7 @@ void Clouds::render()
v2f world_center_of_drawing_in_noise_f = v2f(
center_of_drawing_in_noise_i.X * cloud_size,
center_of_drawing_in_noise_i.Y * cloud_size
- ) + world_cloud_origin_pos_f;
+ ) + m_origin;
/*video::SColor c_top(128,b*240,b*240,b*255);
video::SColor c_side_1(128,b*230,b*230,b*255);
@@ -146,10 +153,6 @@ void Clouds::render()
c_bottom_f.r *= 0.80;
c_bottom_f.g *= 0.80;
c_bottom_f.b *= 0.80;
- c_top_f.a = 0.9;
- c_side_1_f.a = 0.9;
- c_side_2_f.a = 0.9;
- c_bottom_f.a = 0.9;
video::SColor c_top = c_top_f.toSColor();
video::SColor c_side_1 = c_side_1_f.toSColor();
video::SColor c_side_2 = c_side_2_f.toSColor();
@@ -187,11 +190,14 @@ void Clouds::render()
zi + center_of_drawing_in_noise_i.Y
);
- double noise = noise2d_perlin(
+ float noise = noise2d_perlin(
(float)p_in_noise_i.X * cloud_size_noise,
(float)p_in_noise_i.Y * cloud_size_noise,
m_seed, 3, 0.5);
- grid[i] = (noise >= 0.4);
+ // normalize to 0..1 (given 3 octaves)
+ static const float noise_bound = 1.0f + 0.5f + 0.25f;
+ float density = noise / noise_bound * 0.5f + 0.5f;
+ grid[i] = (density < m_params.density);
}
}
@@ -236,8 +242,9 @@ void Clouds::render()
v[3].Color.setBlue(255);
}*/
- f32 rx = cloud_size/2;
- f32 ry = 8 * BS;
+ f32 rx = cloud_size / 2.0f;
+ // if clouds are flat, the top layer should be at the given height
+ f32 ry = m_enable_3d ? m_params.thickness * BS : 0.0f;
f32 rz = cloud_size / 2;
for(int i=0; i<num_faces_to_draw; i++)
@@ -265,8 +272,8 @@ void Clouds::render()
}
v[0].Pos.set(-rx, ry,-rz);
v[1].Pos.set( rx, ry,-rz);
- v[2].Pos.set( rx,-ry,-rz);
- v[3].Pos.set(-rx,-ry,-rz);
+ v[2].Pos.set( rx, 0,-rz);
+ v[3].Pos.set(-rx, 0,-rz);
break;
case 2: //right
if (INAREA(xi + 1, zi, m_cloud_radius_i)) {
@@ -280,8 +287,8 @@ void Clouds::render()
}
v[0].Pos.set( rx, ry,-rz);
v[1].Pos.set( rx, ry, rz);
- v[2].Pos.set( rx,-ry, rz);
- v[3].Pos.set( rx,-ry,-rz);
+ v[2].Pos.set( rx, 0, rz);
+ v[3].Pos.set( rx, 0,-rz);
break;
case 3: // front
if (INAREA(xi, zi + 1, m_cloud_radius_i)) {
@@ -295,8 +302,8 @@ void Clouds::render()
}
v[0].Pos.set( rx, ry, rz);
v[1].Pos.set(-rx, ry, rz);
- v[2].Pos.set(-rx,-ry, rz);
- v[3].Pos.set( rx,-ry, rz);
+ v[2].Pos.set(-rx, 0, rz);
+ v[3].Pos.set( rx, 0, rz);
break;
case 4: // left
if (INAREA(xi-1, zi, m_cloud_radius_i)) {
@@ -310,22 +317,22 @@ void Clouds::render()
}
v[0].Pos.set(-rx, ry, rz);
v[1].Pos.set(-rx, ry,-rz);
- v[2].Pos.set(-rx,-ry,-rz);
- v[3].Pos.set(-rx,-ry, rz);
+ v[2].Pos.set(-rx, 0,-rz);
+ v[3].Pos.set(-rx, 0, rz);
break;
case 5: // bottom
for(int j=0;j<4;j++){
v[j].Color = c_bottom;
v[j].Normal.set(0,-1,0);
}
- v[0].Pos.set( rx,-ry, rz);
- v[1].Pos.set(-rx,-ry, rz);
- v[2].Pos.set(-rx,-ry,-rz);
- v[3].Pos.set( rx,-ry,-rz);
+ v[0].Pos.set( rx, 0, rz);
+ v[1].Pos.set(-rx, 0, rz);
+ v[2].Pos.set(-rx, 0,-rz);
+ v[3].Pos.set( rx, 0,-rz);
break;
}
- v3f pos(p0.X, m_cloud_y, p0.Y);
+ v3f pos(p0.X, m_params.height * BS, p0.Y);
pos -= intToFloat(m_camera_offset, BS);
for(u16 i=0; i<4; i++)
@@ -345,22 +352,25 @@ void Clouds::render()
void Clouds::step(float dtime)
{
- m_time += dtime;
+ m_origin = m_origin + dtime * BS * m_params.speed;
}
-void Clouds::update(v2f camera_p, video::SColorf color)
+void Clouds::update(v2f camera_p, video::SColorf color_diffuse)
{
m_camera_pos = camera_p;
- m_color = color;
- //m_brightness = brightness;
- //dstream<<"m_brightness="<<m_brightness<<std::endl;
+ m_color.r = MYMIN(MYMAX(color_diffuse.r * m_params.color_bright.getRed(),
+ m_params.color_ambient.getRed()), 255) / 255.0f;
+ m_color.g = MYMIN(MYMAX(color_diffuse.g * m_params.color_bright.getGreen(),
+ m_params.color_ambient.getGreen()), 255) / 255.0f;
+ m_color.b = MYMIN(MYMAX(color_diffuse.b * m_params.color_bright.getBlue(),
+ m_params.color_ambient.getBlue()), 255) / 255.0f;
+ m_color.a = m_params.color_bright.getAlpha() / 255.0f;
}
void Clouds::readSettings()
{
- m_cloud_y = BS * (m_passed_cloud_y ? m_passed_cloud_y :
+ m_params.height = (m_passed_cloud_y ? m_passed_cloud_y :
g_settings->getS16("cloud_height"));
m_cloud_radius_i = g_settings->getU16("cloud_radius");
m_enable_3d = g_settings->getBool("enable_3d_clouds");
}
-
diff --git a/src/clouds.h b/src/clouds.h
index 9c6b41786..a0bda28df 100644
--- a/src/clouds.h
+++ b/src/clouds.h
@@ -23,6 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "irrlichttypes_extrabloated.h"
#include <iostream>
#include "constants.h"
+#include "cloudparams.h"
// Menu clouds
class Clouds;
@@ -79,27 +80,68 @@ public:
void updateCameraOffset(v3s16 camera_offset)
{
m_camera_offset = camera_offset;
- m_box = aabb3f(-BS * 1000000, m_cloud_y - BS - BS * camera_offset.Y, -BS * 1000000,
- BS * 1000000, m_cloud_y + BS - BS * camera_offset.Y, BS * 1000000);
+ updateBox();
}
void readSettings();
+ void setDensity(float density)
+ {
+ m_params.density = density;
+ // currently does not need bounding
+ }
+
+ void setColorBright(const video::SColor &color_bright)
+ {
+ m_params.color_bright = color_bright;
+ }
+
+ void setColorAmbient(const video::SColor &color_ambient)
+ {
+ m_params.color_ambient = color_ambient;
+ }
+
+ void setHeight(float height)
+ {
+ m_params.height = height; // add bounding when necessary
+ updateBox();
+ }
+
+ void setSpeed(v2f speed)
+ {
+ m_params.speed = speed;
+ }
+
+ void setThickness(float thickness)
+ {
+ m_params.thickness = thickness;
+ updateBox();
+ }
+
private:
+ void updateBox()
+ {
+ float height_bs = m_params.height * BS;
+ float thickness_bs = m_params.thickness * BS;
+ m_box = aabb3f(-BS * 1000000.0f, height_bs - BS * m_camera_offset.Y, -BS * 1000000.0f,
+ BS * 1000000.0f, height_bs + thickness_bs - BS * m_camera_offset.Y, BS * 1000000.0f);
+ }
+
video::SMaterial m_material;
aabb3f m_box;
s16 m_passed_cloud_y;
- float m_cloud_y;
u16 m_cloud_radius_i;
bool m_enable_3d;
- video::SColorf m_color;
u32 m_seed;
v2f m_camera_pos;
- float m_time;
+ v2f m_origin;
+ v2f m_speed;
v3s16 m_camera_offset;
+ video::SColorf m_color;
+ CloudParams m_params;
+
};
#endif
-
diff --git a/src/cmake_config.h.in b/src/cmake_config.h.in
index 4b731020a..9a57d3de8 100644
--- a/src/cmake_config.h.in
+++ b/src/cmake_config.h.in
@@ -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@"
diff --git a/src/collision.cpp b/src/collision.cpp
index c0891c152..8faf05f54 100644
--- a/src/collision.cpp
+++ b/src/collision.cpp
@@ -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;
diff --git a/src/constants.h b/src/constants.h
index 55ae9daf3..fb9e97cb3 100644
--- a/src/constants.h
+++ b/src/constants.h
@@ -85,7 +85,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
*/
// Size of player's main inventory
-#define PLAYER_INVENTORY_SIZE (8*4)
+#define PLAYER_INVENTORY_SIZE (8 * 4)
// Maximum hit points of a player
#define PLAYER_MAX_HP 20
@@ -110,10 +110,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
// TODO: implement dpi-based scaling for windows and remove this hack
#if defined(_WIN32)
- #define TTF_DEFAULT_FONT_SIZE (18)
+#define TTF_DEFAULT_FONT_SIZE (18)
#else
- #define TTF_DEFAULT_FONT_SIZE (16)
+#define TTF_DEFAULT_FONT_SIZE (16)
#endif
-#define DEFAULT_FONT_SIZE (10)
+#define DEFAULT_FONT_SIZE (10)
#endif
diff --git a/src/content_abm.cpp b/src/content_abm.cpp
index 2ab3a968c..162f93364 100644
--- a/src/content_abm.cpp
+++ b/src/content_abm.cpp
@@ -26,9 +26,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "settings.h"
#include "mapblock.h" // For getNodeBlockPos
#include "map.h"
-#include "serverscripting.h"
+#include "scripting_server.h"
#include "log.h"
-void add_legacy_abms(ServerEnvironment *env, INodeDefManager *nodedef) {
-
+void add_legacy_abms(ServerEnvironment *env, INodeDefManager *nodedef)
+{
}
diff --git a/src/content_cao.cpp b/src/content_cao.cpp
index ac283da88..7f1293ac3 100644
--- a/src/content_cao.cpp
+++ b/src/content_cao.cpp
@@ -44,6 +44,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "camera.h" // CameraModes
#include "wieldmesh.h"
#include "log.h"
+#include <algorithm>
class Settings;
struct ToolCapabilities;
@@ -705,26 +706,11 @@ scene::ISceneNode* GenericCAO::getSceneNode()
return NULL;
}
-scene::IMeshSceneNode* GenericCAO::getMeshSceneNode()
-{
- return m_meshnode;
-}
-
scene::IAnimatedMeshSceneNode* GenericCAO::getAnimatedMeshSceneNode()
{
return m_animated_meshnode;
}
-WieldMeshSceneNode* GenericCAO::getWieldMeshSceneNode()
-{
- return m_wield_meshnode;
-}
-
-scene::IBillboardSceneNode* GenericCAO::getSpriteSceneNode()
-{
- return m_spritenode;
-}
-
void GenericCAO::setChildrenVisible(bool toset)
{
for (std::vector<u16>::size_type i = 0; i < m_children.size(); i++) {
@@ -849,6 +835,11 @@ void GenericCAO::addToScene(scene::ISceneManager *smgr,
video::S3DVertex( dx, dy, 0, 0,0,0, c, 0,0),
video::S3DVertex(-dx, dy, 0, 0,0,0, c, 1,0),
};
+ if (m_is_player) {
+ // Move minimal Y position to 0 (feet position)
+ for (size_t i = 0; i < 4; i++)
+ vertices[i].Pos.Y += dy;
+ }
u16 indices[] = {0,1,2,2,3,0};
buf->append(vertices, 4, indices, 6);
// Set material
@@ -868,6 +859,11 @@ void GenericCAO::addToScene(scene::ISceneManager *smgr,
video::S3DVertex(-dx, dy, 0, 0,0,0, c, 0,0),
video::S3DVertex( dx, dy, 0, 0,0,0, c, 1,0),
};
+ if (m_is_player) {
+ // Move minimal Y position to 0 (feet position)
+ for (size_t i = 0; i < 4; i++)
+ vertices[i].Pos.Y += dy;
+ }
u16 indices[] = {0,1,2,2,3,0};
buf->append(vertices, 4, indices, 6);
// Set material
@@ -1041,12 +1037,9 @@ void GenericCAO::updateNodePos()
void GenericCAO::step(float dtime, ClientEnvironment *env)
{
// Handel model of local player instantly to prevent lags
- if(m_is_local_player)
- {
+ if (m_is_local_player) {
LocalPlayer *player = m_env->getLocalPlayer();
-
- if (m_is_visible)
- {
+ if (m_is_visible) {
int old_anim = player->last_animation;
float old_anim_speed = player->last_animation_speed;
m_position = player->getPosition() + v3f(0,BS,0);
@@ -1054,7 +1047,7 @@ void GenericCAO::step(float dtime, ClientEnvironment *env)
m_acceleration = v3f(0,0,0);
pos_translator.vect_show = m_position;
m_yaw = player->getYaw();
- PlayerControl controls = player->getPlayerControl();
+ const PlayerControl &controls = player->getPlayerControl();
bool walking = false;
if (controls.up || controls.down || controls.left || controls.right ||
@@ -1075,11 +1068,10 @@ void GenericCAO::step(float dtime, ClientEnvironment *env)
m_client->checkLocalPrivilege("fly"))))
new_speed *= 1.5;
// slowdown speed if sneeking
- if(controls.sneak && walking)
+ if (controls.sneak && walking)
new_speed /= 2;
- if(walking && (controls.LMB || controls.RMB))
- {
+ if (walking && (controls.LMB || controls.RMB)) {
new_anim = player->local_animations[3];
player->last_animation = WD_ANIM;
} else if(walking) {
@@ -1092,8 +1084,7 @@ void GenericCAO::step(float dtime, ClientEnvironment *env)
// Apply animations if input detected and not attached
// or set idle animation
- if ((new_anim.X + new_anim.Y) > 0 && !player->isAttached)
- {
+ if ((new_anim.X + new_anim.Y) > 0 && !player->isAttached) {
allow_update = true;
m_animation_range = new_anim;
m_animation_speed = new_speed;
@@ -1101,8 +1092,7 @@ void GenericCAO::step(float dtime, ClientEnvironment *env)
} else {
player->last_animation = NO_ANIM;
- if (old_anim != NO_ANIM)
- {
+ if (old_anim != NO_ANIM) {
m_animation_range = player->local_animations[0];
updateAnimation();
}
@@ -1204,16 +1194,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());
}
}
@@ -1313,7 +1304,7 @@ void GenericCAO::updateTexturePos()
}
}
-void GenericCAO::updateTextures(const std::string mod)
+void GenericCAO::updateTextures(std::string mod)
{
ITextureSource *tsrc = m_client->tsrc();
@@ -1374,6 +1365,13 @@ void GenericCAO::updateTextures(const 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)
@@ -1589,6 +1587,10 @@ void GenericCAO::processMessage(const std::string &data)
m_initial_tx_basepos_set = true;
m_tx_basepos = m_prop.initial_sprite_basepos;
}
+ if (m_is_local_player) {
+ LocalPlayer *player = m_env->getLocalPlayer();
+ player->makes_footstep_sound = m_prop.makes_footstep_sound;
+ }
if ((m_is_player && !m_is_local_player) && m_prop.nametag == "")
m_prop.nametag = m_name;
@@ -1751,7 +1753,7 @@ void GenericCAO::processMessage(const std::string &data)
m_smgr, m_env, m_position,
m_prop.visual_size * BS);
m_env->addSimpleObject(simple);
- } else {
+ } else if (m_reset_textures_timer < 0) {
// TODO: Execute defined fast response
// Flashing shall suffice as there is no definition
m_reset_textures_timer = 0.05;
@@ -1822,10 +1824,12 @@ bool GenericCAO::directReportPunch(v3f dir, const ItemStack *punchitem,
}
// TODO: Execute defined fast response
// Flashing shall suffice as there is no definition
- m_reset_textures_timer = 0.05;
- if(result.damage >= 2)
- m_reset_textures_timer += 0.05 * result.damage;
- updateTextures(m_current_texture_modifier + "^[brighten");
+ if (m_reset_textures_timer < 0) {
+ m_reset_textures_timer = 0.05;
+ if (result.damage >= 2)
+ m_reset_textures_timer += 0.05 * result.damage;
+ updateTextures(m_current_texture_modifier + "^[brighten");
+ }
}
return false;
diff --git a/src/content_cao.h b/src/content_cao.h
index f30e90e21..412cdff12 100644
--- a/src/content_cao.h
+++ b/src/content_cao.h
@@ -146,19 +146,8 @@ public:
scene::ISceneNode *getSceneNode();
- scene::IMeshSceneNode *getMeshSceneNode();
-
scene::IAnimatedMeshSceneNode *getAnimatedMeshSceneNode();
- WieldMeshSceneNode *getWieldMeshSceneNode();
-
- scene::IBillboardSceneNode *getSpriteSceneNode();
-
- inline bool isPlayer() const
- {
- return m_is_player;
- }
-
inline bool isLocalPlayer() const
{
return m_is_local_player;
@@ -200,7 +189,9 @@ public:
void updateTexturePos();
- void updateTextures(const std::string mod);
+ // std::string copy is mandatory as mod can be a class member and there is a swap
+ // on those class members
+ void updateTextures(std::string mod);
void updateAnimation();
diff --git a/src/content_mapblock.cpp b/src/content_mapblock.cpp
index 153dacf42..7e93c1341 100644
--- a/src/content_mapblock.cpp
+++ b/src/content_mapblock.cpp
@@ -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),
@@ -76,24 +76,26 @@ MapblockMeshGenerator::MapblockMeshGenerator(MeshMakeData *input, MeshCollector
void MapblockMeshGenerator::useTile(int index, bool disable_backface_culling)
{
- tile = getNodeTileN(n, p, index, data);
+ getNodeTileN(n, p, index, data, tile);
if (!data->m_smooth_lighting)
- color = encode_light_and_color(light, tile.color, f->light_source);
- if (disable_backface_culling)
- tile.material_flags &= ~MATERIAL_FLAG_BACKFACE_CULLING;
- tile.material_flags |= MATERIAL_FLAG_CRACK_OVERLAY;
+ color = encode_light(light, f->light_source);
+ for (int layer = 0; layer < MAX_TILE_LAYERS; layer++) {
+ tile.layers[layer].material_flags |= MATERIAL_FLAG_CRACK_OVERLAY;
+ if (disable_backface_culling)
+ tile.layers[layer].material_flags &= ~MATERIAL_FLAG_BACKFACE_CULLING;
+ }
}
void MapblockMeshGenerator::useDefaultTile(bool set_color)
{
- tile = getNodeTile(n, p, v3s16(0, 0, 0), data);
+ getNodeTile(n, p, v3s16(0, 0, 0), data, tile);
if (set_color && !data->m_smooth_lighting)
- color = encode_light_and_color(light, tile.color, f->light_source);
+ color = encode_light(light, f->light_source);
}
-TileSpec MapblockMeshGenerator::getTile(const v3s16& direction)
+void MapblockMeshGenerator::getTile(const v3s16& direction, TileSpec &tile)
{
- return getNodeTile(n, p, direction, data);
+ getNodeTile(n, p, direction, data, tile);
}
void MapblockMeshGenerator::drawQuad(v3f *coords, const v3s16 &normal)
@@ -106,7 +108,7 @@ void MapblockMeshGenerator::drawQuad(v3f *coords, const v3s16 &normal)
vertices[j].Pos = coords[j] + origin;
vertices[j].Normal = normal2;
if (data->m_smooth_lighting)
- vertices[j].Color = blendLight(coords[j], tile.color);
+ vertices[j].Color = blendLightColor(coords[j]);
else
vertices[j].Color = color;
if (shade_face)
@@ -137,8 +139,7 @@ void MapblockMeshGenerator::drawCuboid(const aabb3f &box,
video::SColor colors[6];
if (!data->m_smooth_lighting) {
for (int face = 0; face != 6; ++face) {
- int tileindex = MYMIN(face, tilecount - 1);
- colors[face] = encode_light_and_color(light, tiles[tileindex].color, f->light_source);
+ colors[face] = encode_light(light, f->light_source);
}
if (!f->light_source) {
applyFacesShading(colors[0], v3f(0, 1, 0));
@@ -240,9 +241,8 @@ void MapblockMeshGenerator::drawCuboid(const aabb3f &box,
if (data->m_smooth_lighting) {
for (int j = 0; j < 24; ++j) {
- int tileindex = MYMIN(j / 4, tilecount - 1);
- vertices[j].Color = encode_light_and_color(lights[light_indices[j]],
- tiles[tileindex].color, f->light_source);
+ vertices[j].Color = encode_light(lights[light_indices[j]],
+ f->light_source);
if (!f->light_source)
applyFacesShading(vertices[j].Color, vertices[j].Normal);
}
@@ -289,17 +289,16 @@ u16 MapblockMeshGenerator::blendLight(const v3f &vertex_pos)
// Calculates vertex color to be used in mapblock mesh
// vertex_pos - vertex position in the node (coordinates are clamped to [0.0, 1.0] or so)
// tile_color - node's tile color
-video::SColor MapblockMeshGenerator::blendLight(const v3f &vertex_pos,
- video::SColor tile_color)
+video::SColor MapblockMeshGenerator::blendLightColor(const v3f &vertex_pos)
{
u16 light = blendLight(vertex_pos);
- return encode_light_and_color(light, tile_color, f->light_source);
+ return encode_light(light, f->light_source);
}
-video::SColor MapblockMeshGenerator::blendLight(const v3f &vertex_pos,
- const v3f &vertex_normal, video::SColor tile_color)
+video::SColor MapblockMeshGenerator::blendLightColor(const v3f &vertex_pos,
+ const v3f &vertex_normal)
{
- video::SColor color = blendLight(vertex_pos, tile_color);
+ video::SColor color = blendLightColor(vertex_pos);
if (!f->light_source)
applyFacesShading(color, vertex_normal);
return color;
@@ -367,15 +366,20 @@ static TileSpec getSpecialTile(const ContentFeatures &f,
const MapNode &n, u8 i)
{
TileSpec copy = f.special_tiles[i];
- if (!copy.has_color)
- n.getColor(f, &copy.color);
+ for (int layernum = 0; layernum < MAX_TILE_LAYERS; layernum++) {
+ TileLayer *layer = &copy.layers[layernum];
+ if (layer->texture_id == 0)
+ continue;
+ if (!layer->has_color)
+ n.getColor(f, &(layer->color));
+ }
return copy;
}
-void MapblockMeshGenerator::prepareLiquidNodeDrawing(bool flowing)
+void MapblockMeshGenerator::prepareLiquidNodeDrawing()
{
tile_liquid_top = getSpecialTile(*f, n, 0);
- tile_liquid = getSpecialTile(*f, n, flowing ? 1 : 0);
+ tile_liquid = getSpecialTile(*f, n, 1);
MapNode ntop = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(p.X, p.Y + 1, p.Z));
c_flowing = nodedef->getId(f->liquid_alternative_flowing);
@@ -395,20 +399,16 @@ void MapblockMeshGenerator::prepareLiquidNodeDrawing(bool flowing)
light = getInteriorLight(ntop, 0, nodedef);
}
- color_liquid_top = encode_light_and_color(light, tile_liquid_top.color, f->light_source);
- color = encode_light_and_color(light, tile_liquid.color, f->light_source);
+ color_liquid_top = encode_light(light, f->light_source);
+ color = encode_light(light, f->light_source);
}
-void MapblockMeshGenerator::getLiquidNeighborhood(bool flowing)
+void MapblockMeshGenerator::getLiquidNeighborhood()
{
u8 range = rangelim(nodedef->get(c_flowing).liquid_range, 1, 8);
for (int w = -1; w <= 1; w++)
for (int u = -1; u <= 1; u++) {
- // Skip getting unneeded data
- if (!flowing && u && w)
- continue;
-
NeighborData &neighbor = liquid_neighbors[w + 1][u + 1];
v3s16 p2 = p + v3s16(u, 0, w);
MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
@@ -443,13 +443,6 @@ void MapblockMeshGenerator::getLiquidNeighborhood(bool flowing)
}
}
-void MapblockMeshGenerator::resetCornerLevels()
-{
- for (int k = 0; k < 2; k++)
- for (int i = 0; i < 2; i++)
- corner_levels[k][i] = 0.5 * BS;
-}
-
void MapblockMeshGenerator::calculateCornerLevels()
{
for (int k = 0; k < 2; k++)
@@ -490,7 +483,7 @@ f32 MapblockMeshGenerator::getCornerLevel(int i, int k)
return 0;
}
-void MapblockMeshGenerator::drawLiquidSides(bool flowing)
+void MapblockMeshGenerator::drawLiquidSides()
{
struct LiquidFaceDesc {
v3s16 dir; // XZ
@@ -519,17 +512,12 @@ void MapblockMeshGenerator::drawLiquidSides(bool flowing)
// at the top to which it should be connected. Again, unless the face
// there would be inside the liquid
if (neighbor.is_same_liquid) {
- if (!flowing)
- continue;
if (!top_is_same_liquid)
continue;
if (neighbor.top_is_same_liquid)
continue;
}
- if (!flowing && (neighbor.content == CONTENT_IGNORE))
- continue;
-
const ContentFeatures &neighbor_features = nodedef->get(neighbor.content);
// Don't draw face if neighbor is blocking the view
if (neighbor_features.solidness == 2)
@@ -547,7 +535,7 @@ void MapblockMeshGenerator::drawLiquidSides(bool flowing)
else
pos.Y = !top_is_same_liquid ? corner_levels[base.Z][base.X] : 0.5 * BS;
if (data->m_smooth_lighting)
- color = blendLight(pos, tile_liquid.color);
+ color = blendLightColor(pos);
pos += origin;
vertices[j] = video::S3DVertex(pos.X, pos.Y, pos.Z, 0, 0, 0, color, vertex.u, vertex.v);
};
@@ -555,7 +543,7 @@ void MapblockMeshGenerator::drawLiquidSides(bool flowing)
}
}
-void MapblockMeshGenerator::drawLiquidTop(bool flowing)
+void MapblockMeshGenerator::drawLiquidTop()
{
// To get backface culling right, the vertices need to go
// clockwise around the front of the face. And we happened to
@@ -574,49 +562,44 @@ void MapblockMeshGenerator::drawLiquidTop(bool flowing)
int w = corner_resolve[i][1];
vertices[i].Pos.Y += corner_levels[w][u];
if (data->m_smooth_lighting)
- vertices[i].Color = blendLight(vertices[i].Pos, tile_liquid_top.color);
+ vertices[i].Color = blendLightColor(vertices[i].Pos);
vertices[i].Pos += origin;
}
- if (flowing) {
- // Default downwards-flowing texture animation goes from
- // -Z towards +Z, thus the direction is +Z.
- // Rotate texture to make animation go in flow direction
- // Positive if liquid moves towards +Z
- f32 dz = (corner_levels[0][0] + corner_levels[0][1]) -
- (corner_levels[1][0] + corner_levels[1][1]);
- // Positive if liquid moves towards +X
- f32 dx = (corner_levels[0][0] + corner_levels[1][0]) -
- (corner_levels[0][1] + corner_levels[1][1]);
- f32 tcoord_angle = atan2(dz, dx) * core::RADTODEG;
- v2f tcoord_center(0.5, 0.5);
- v2f tcoord_translate(blockpos_nodes.Z + p.Z, blockpos_nodes.X + p.X);
- tcoord_translate.rotateBy(tcoord_angle);
- tcoord_translate.X -= floor(tcoord_translate.X);
- tcoord_translate.Y -= floor(tcoord_translate.Y);
+ // Default downwards-flowing texture animation goes from
+ // -Z towards +Z, thus the direction is +Z.
+ // Rotate texture to make animation go in flow direction
+ // Positive if liquid moves towards +Z
+ f32 dz = (corner_levels[0][0] + corner_levels[0][1]) -
+ (corner_levels[1][0] + corner_levels[1][1]);
+ // Positive if liquid moves towards +X
+ f32 dx = (corner_levels[0][0] + corner_levels[1][0]) -
+ (corner_levels[0][1] + corner_levels[1][1]);
+ f32 tcoord_angle = atan2(dz, dx) * core::RADTODEG;
+ v2f tcoord_center(0.5, 0.5);
+ v2f tcoord_translate(blockpos_nodes.Z + p.Z, blockpos_nodes.X + p.X);
+ tcoord_translate.rotateBy(tcoord_angle);
+ tcoord_translate.X -= floor(tcoord_translate.X);
+ tcoord_translate.Y -= floor(tcoord_translate.Y);
- for (int i = 0; i < 4; i++) {
- vertices[i].TCoords.rotateBy(tcoord_angle, tcoord_center);
- vertices[i].TCoords += tcoord_translate;
- }
-
- std::swap(vertices[0].TCoords, vertices[2].TCoords);
+ for (int i = 0; i < 4; i++) {
+ vertices[i].TCoords.rotateBy(tcoord_angle, tcoord_center);
+ vertices[i].TCoords += tcoord_translate;
}
+ std::swap(vertices[0].TCoords, vertices[2].TCoords);
+
collector->append(tile_liquid_top, vertices, 4, quad_indices, 6);
}
-void MapblockMeshGenerator::drawLiquidNode(bool flowing)
+void MapblockMeshGenerator::drawLiquidNode()
{
- prepareLiquidNodeDrawing(flowing);
- getLiquidNeighborhood(flowing);
- if (flowing)
- calculateCornerLevels();
- else
- resetCornerLevels();
- drawLiquidSides(flowing);
+ prepareLiquidNodeDrawing();
+ getLiquidNeighborhood();
+ calculateCornerLevels();
+ drawLiquidSides();
if (!top_is_same_liquid)
- drawLiquidTop(flowing);
+ drawLiquidTop();
}
void MapblockMeshGenerator::drawGlasslikeNode()
@@ -656,10 +639,12 @@ void MapblockMeshGenerator::drawGlasslikeFramedNode()
{
TileSpec tiles[6];
for (int face = 0; face < 6; face++)
- tiles[face] = getTile(g_6dirs[face]);
+ getTile(g_6dirs[face], tiles[face]);
TileSpec glass_tiles[6];
- if (tiles[0].texture && tiles[3].texture && tiles[4].texture) {
+ if (tiles[1].layers[0].texture &&
+ tiles[2].layers[0].texture &&
+ tiles[3].layers[0].texture) {
glass_tiles[0] = tiles[4];
glass_tiles[1] = tiles[0];
glass_tiles[2] = tiles[4];
@@ -763,7 +748,7 @@ void MapblockMeshGenerator::drawGlasslikeFramedNode()
// Optionally render internal liquid level defined by param2
// Liquid is textured with 1 tile defined in nodedef 'special_tiles'
if (param2 > 0 && f->param_type_2 == CPT2_GLASSLIKE_LIQUID_LEVEL &&
- f->special_tiles[0].texture) {
+ f->special_tiles[0].layers[0].texture) {
// Internal liquid level has param2 range 0 .. 63,
// convert it to -0.5 .. 0.5
float vlev = (param2 / 63.0) * 2.0 - 1.0;
@@ -998,7 +983,8 @@ void MapblockMeshGenerator::drawFencelikeNode()
{
useDefaultTile(false);
TileSpec tile_nocrack = tile;
- tile_nocrack.material_flags &= ~MATERIAL_FLAG_CRACK;
+ for (int layer = 0; layer < MAX_TILE_LAYERS; layer++)
+ tile_nocrack.layers[layer].material_flags &= ~MATERIAL_FLAG_CRACK;
// Put wood the right way around in the posts
TileSpec tile_rot = tile;
@@ -1186,7 +1172,7 @@ void MapblockMeshGenerator::drawNodeboxNode()
TileSpec tiles[6];
for (int face = 0; face < 6; face++) {
// Handles facedir rotation for textures
- tiles[face] = getTile(tile_dirs[face]);
+ getTile(tile_dirs[face], tiles[face]);
}
// locate possible neighboring nodes to connect to
@@ -1253,7 +1239,7 @@ void MapblockMeshGenerator::drawMeshNode()
// vertex right here.
for (int k = 0; k < vertex_count; k++) {
video::S3DVertex &vertex = vertices[k];
- vertex.Color = blendLight(vertex.Pos, vertex.Normal, tile.color);
+ vertex.Color = blendLightColor(vertex.Pos, vertex.Normal);
vertex.Pos += origin;
}
collector->append(tile, vertices, vertex_count,
@@ -1284,8 +1270,7 @@ void MapblockMeshGenerator::drawNode()
else
light = getInteriorLight(n, 1, nodedef);
switch (f->drawtype) {
- case NDT_LIQUID: drawLiquidNode(false); break;
- case NDT_FLOWINGLIQUID: drawLiquidNode(true); break;
+ case NDT_FLOWINGLIQUID: drawLiquidNode(); break;
case NDT_GLASSLIKE: drawGlasslikeNode(); break;
case NDT_GLASSLIKE_FRAMED: drawGlasslikeFramedNode(); break;
case NDT_ALLFACES: drawAllfacesNode(); break;
diff --git a/src/content_mapblock.h b/src/content_mapblock.h
index c8425024f..2c6a4969e 100644
--- a/src/content_mapblock.h
+++ b/src/content_mapblock.h
@@ -60,12 +60,12 @@ public:
// lighting
void getSmoothLightFrame();
u16 blendLight(const v3f &vertex_pos);
- video::SColor blendLight(const v3f &vertex_pos, video::SColor tile_color);
- video::SColor blendLight(const v3f &vertex_pos, const v3f &vertex_normal, video::SColor tile_color);
+ video::SColor blendLightColor(const v3f &vertex_pos);
+ video::SColor blendLightColor(const v3f &vertex_pos, const v3f &vertex_normal);
void useTile(int index, bool disable_backface_culling);
void useDefaultTile(bool set_color = true);
- TileSpec getTile(const v3s16 &direction);
+ void getTile(const v3s16 &direction, TileSpec &tile);
// face drawing
void drawQuad(v3f *vertices, const v3s16 &normal = v3s16(0, 0, 0));
@@ -93,13 +93,12 @@ public:
NeighborData liquid_neighbors[3][3];
f32 corner_levels[2][2];
- void prepareLiquidNodeDrawing(bool flowing);
- void getLiquidNeighborhood(bool flowing);
- void resetCornerLevels();
+ void prepareLiquidNodeDrawing();
+ void getLiquidNeighborhood();
void calculateCornerLevels();
f32 getCornerLevel(int i, int k);
- void drawLiquidSides(bool flowing);
- void drawLiquidTop(bool flowing);
+ void drawLiquidSides();
+ void drawLiquidTop();
// raillike-specific
// name of the group that enables connecting to raillike nodes of different kind
@@ -122,7 +121,7 @@ public:
float offset_h, float offset_v = 0.0);
// drawtypes
- void drawLiquidNode(bool flowing);
+ void drawLiquidNode();
void drawGlasslikeNode();
void drawGlasslikeFramedNode();
void drawAllfacesNode();
diff --git a/src/content_sao.cpp b/src/content_sao.cpp
index 908365397..c22e341a9 100644
--- a/src/content_sao.cpp
+++ b/src/content_sao.cpp
@@ -26,7 +26,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "nodedef.h"
#include "remoteplayer.h"
#include "server.h"
-#include "serverscripting.h"
+#include "scripting_server.h"
#include "genericobject.h"
std::map<u16, ServerActiveObject::Factory> ServerActiveObject::m_types;
@@ -59,7 +59,7 @@ public:
m_age += dtime;
if(m_age > 10)
{
- m_removed = true;
+ m_pending_removal = true;
return;
}
@@ -541,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;
}
@@ -587,7 +587,7 @@ int LuaEntitySAO::punch(v3f dir,
}
if (getHP() == 0)
- m_removed = true;
+ m_pending_removal = true;
@@ -764,9 +764,10 @@ bool LuaEntitySAO::collideWithObjects() const
// No prototype, PlayerSAO does not need to be deserialized
-PlayerSAO::PlayerSAO(ServerEnvironment *env_, u16 peer_id_, bool is_singleplayer):
+PlayerSAO::PlayerSAO(ServerEnvironment *env_, RemotePlayer *player_, u16 peer_id_,
+ bool is_singleplayer):
UnitSAO(env_, v3f(0,0,0)),
- m_player(NULL),
+ m_player(player_),
m_peer_id(peer_id_),
m_inventory(NULL),
m_damage(0),
@@ -788,7 +789,7 @@ PlayerSAO::PlayerSAO(ServerEnvironment *env_, u16 peer_id_, bool is_singleplayer
m_physics_override_jump(1),
m_physics_override_gravity(1),
m_physics_override_sneak(true),
- m_physics_override_sneak_glitch(true),
+ m_physics_override_sneak_glitch(false),
m_physics_override_new_move(true),
m_physics_override_sent(false)
{
@@ -797,7 +798,7 @@ PlayerSAO::PlayerSAO(ServerEnvironment *env_, u16 peer_id_, bool is_singleplayer
m_prop.hp_max = PLAYER_MAX_HP;
m_prop.physical = false;
m_prop.weight = 75;
- m_prop.collisionbox = aabb3f(-1/3.,-1.0,-1/3., 1/3.,1.0,1/3.);
+ m_prop.collisionbox = aabb3f(-0.3f, -1.0f, -0.3f, 0.3f, 0.75f, 0.3f);
// start of default appearance, this should be overwritten by LUA
m_prop.visual = "upright_sprite";
m_prop.visual_size = v2f(1, 2);
@@ -819,7 +820,7 @@ PlayerSAO::~PlayerSAO()
delete m_inventory;
}
-void PlayerSAO::initialize(RemotePlayer *player, const std::set<std::string> &privs)
+void PlayerSAO::finalize(RemotePlayer *player, const std::set<std::string> &privs)
{
assert(player);
m_player = player;
@@ -1346,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()
@@ -1387,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;
-
- if (d_vert > 0 && d_vert / player_max_speed > required_time)
- required_time = d_vert / player_max_speed; // Moving upwards
+ float required_time = d_horiz / player_max_walk;
+
+ // 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;
@@ -1427,7 +1439,8 @@ bool PlayerSAO::checkMovementCheat()
bool PlayerSAO::getCollisionBox(aabb3f *toset) const
{
- *toset = aabb3f(-BS * 0.30, 0.0, -BS * 0.30, BS * 0.30, BS * 1.75, BS * 0.30);
+ *toset = aabb3f(-0.3f * BS, 0.0f, -0.3f * BS, 0.3f * BS, 1.75f * BS, 0.3f * BS);
+
toset->MinEdge += m_base_position;
toset->MaxEdge += m_base_position;
return true;
diff --git a/src/content_sao.h b/src/content_sao.h
index e53e8ecce..9444ee758 100644
--- a/src/content_sao.h
+++ b/src/content_sao.h
@@ -102,6 +102,8 @@ public:
const std::string &data);
void step(float dtime, bool send_recommended);
std::string getClientInitializationData(u16 protocol_version);
+ bool isStaticAllowed() const
+ { return m_prop.static_save; }
void getStaticData(std::string *result) const;
int punch(v3f dir,
const ToolCapabilities *toolcap=NULL,
@@ -194,7 +196,7 @@ class RemotePlayer;
class PlayerSAO : public UnitSAO
{
public:
- PlayerSAO(ServerEnvironment *env_, u16 peer_id_, bool is_singleplayer);
+ PlayerSAO(ServerEnvironment *env_, RemotePlayer *player_, u16 peer_id_, bool is_singleplayer);
~PlayerSAO();
ActiveObjectType getType() const
{ return ACTIVEOBJECT_TYPE_PLAYER; }
@@ -277,6 +279,16 @@ public:
return true;
}
+ inline void removeExtendedAttribute(const std::string &attr)
+ {
+ PlayerAttributes::iterator it = m_extra_attributes.find(attr);
+ if (it == m_extra_attributes.end())
+ return;
+
+ m_extra_attributes.erase(it);
+ m_extended_attributes_modified = true;
+ }
+
inline const PlayerAttributes &getExtendedAttributes()
{
return m_extra_attributes;
@@ -349,7 +361,7 @@ public:
bool getCollisionBox(aabb3f *toset) const;
bool collideWithObjects() const { return true; }
- void initialize(RemotePlayer *player, const std::set<std::string> &privs);
+ void finalize(RemotePlayer *player, const std::set<std::string> &privs);
v3f getEyePosition() const { return m_base_position + getEyeOffset(); }
v3f getEyeOffset() const;
diff --git a/src/craftdef.cpp b/src/craftdef.cpp
index 286d1eada..210fe9f03 100644
--- a/src/craftdef.cpp
+++ b/src/craftdef.cpp
@@ -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;*/
diff --git a/src/craftdef.h b/src/craftdef.h
index 695ee0c2c..bdd741f7c 100644
--- a/src/craftdef.h
+++ b/src/craftdef.h
@@ -97,7 +97,7 @@ struct CraftOutput
CraftOutput():
item(""), time(0)
{}
- CraftOutput(std::string item_, float time_):
+ CraftOutput(const std::string &item_, float time_):
item(item_), time(time_)
{}
std::string dump() const;
@@ -124,7 +124,7 @@ struct CraftReplacements
CraftReplacements():
pairs()
{}
- CraftReplacements(std::vector<std::pair<std::string, std::string> > pairs_):
+ CraftReplacements(const std::vector<std::pair<std::string, std::string> > &pairs_):
pairs(pairs_)
{}
std::string dump() const;
@@ -359,10 +359,13 @@ public:
CraftDefinitionFuel():
recipe(""), hash_inited(false), burntime()
{}
- CraftDefinitionFuel(std::string recipe_,
+ CraftDefinitionFuel(const std::string &recipe_,
float burntime_,
const CraftReplacements &replacements_):
- recipe(recipe_), hash_inited(false), burntime(burntime_), replacements(replacements_)
+ recipe(recipe_),
+ hash_inited(false),
+ burntime(burntime_),
+ replacements(replacements_)
{}
virtual ~CraftDefinitionFuel(){}
diff --git a/src/database-dummy.h b/src/database-dummy.h
index 72100edd0..7d1cb2279 100644
--- a/src/database-dummy.h
+++ b/src/database-dummy.h
@@ -25,7 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "database.h"
#include "irrlichttypes.h"
-class Database_Dummy : public Database
+class Database_Dummy : public MapDatabase, public PlayerDatabase
{
public:
bool saveBlock(const v3s16 &pos, const std::string &data);
@@ -33,9 +33,15 @@ public:
bool deleteBlock(const v3s16 &pos);
void listAllLoadableBlocks(std::vector<v3s16> &dst);
+ void savePlayer(RemotePlayer *player) {}
+ bool loadPlayer(RemotePlayer *player, PlayerSAO *sao) { return true; }
+ bool removePlayer(const std::string &name) { return true; }
+ void listPlayers(std::vector<std::string> &) {}
+
+ void beginSave() {}
+ void endSave() {}
private:
std::map<s64, std::string> m_database;
};
#endif
-
diff --git a/src/database-files.cpp b/src/database-files.cpp
new file mode 100644
index 000000000..3b130f91b
--- /dev/null
+++ b/src/database-files.cpp
@@ -0,0 +1,179 @@
+/*
+Minetest
+Copyright (C) 2017 nerzhul, Loic Blot <loic.blot@unix-experience.fr>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include <cassert>
+#include <json/json.h>
+#include "database-files.h"
+#include "content_sao.h"
+#include "remoteplayer.h"
+#include "settings.h"
+#include "porting.h"
+#include "filesys.h"
+
+// !!! WARNING !!!
+// This backend is intended to be used on Minetest 0.4.16 only for the transition backend
+// for player files
+
+void PlayerDatabaseFiles::serialize(std::ostringstream &os, RemotePlayer *player)
+{
+ // Utilize a Settings object for storing values
+ Settings args;
+ args.setS32("version", 1);
+ args.set("name", player->getName());
+
+ sanity_check(player->getPlayerSAO());
+ args.setS32("hp", player->getPlayerSAO()->getHP());
+ args.setV3F("position", player->getPlayerSAO()->getBasePosition());
+ args.setFloat("pitch", player->getPlayerSAO()->getPitch());
+ args.setFloat("yaw", player->getPlayerSAO()->getYaw());
+ args.setS32("breath", player->getPlayerSAO()->getBreath());
+
+ std::string extended_attrs = "";
+ player->serializeExtraAttributes(extended_attrs);
+ args.set("extended_attributes", extended_attrs);
+
+ args.writeLines(os);
+
+ os << "PlayerArgsEnd\n";
+
+ player->inventory.serialize(os);
+}
+
+void PlayerDatabaseFiles::savePlayer(RemotePlayer *player)
+{
+ std::string savedir = m_savedir + DIR_DELIM;
+ std::string path = savedir + player->getName();
+ bool path_found = false;
+ RemotePlayer testplayer("", NULL);
+
+ for (u32 i = 0; i < PLAYER_FILE_ALTERNATE_TRIES && !path_found; i++) {
+ if (!fs::PathExists(path)) {
+ path_found = true;
+ continue;
+ }
+
+ // Open and deserialize file to check player name
+ std::ifstream is(path.c_str(), std::ios_base::binary);
+ if (!is.good()) {
+ errorstream << "Failed to open " << path << std::endl;
+ return;
+ }
+
+ testplayer.deSerialize(is, path, NULL);
+ is.close();
+ if (strcmp(testplayer.getName(), player->getName()) == 0) {
+ path_found = true;
+ continue;
+ }
+
+ path = savedir + player->getName() + itos(i);
+ }
+
+ if (!path_found) {
+ errorstream << "Didn't find free file for player " << player->getName()
+ << std::endl;
+ return;
+ }
+
+ // Open and serialize file
+ std::ostringstream ss(std::ios_base::binary);
+ serialize(ss, player);
+ if (!fs::safeWriteToFile(path, ss.str())) {
+ infostream << "Failed to write " << path << std::endl;
+ }
+ player->setModified(false);
+}
+
+bool PlayerDatabaseFiles::removePlayer(const std::string &name)
+{
+ std::string players_path = m_savedir + DIR_DELIM;
+ std::string path = players_path + name;
+
+ RemotePlayer temp_player("", NULL);
+ for (u32 i = 0; i < PLAYER_FILE_ALTERNATE_TRIES; i++) {
+ // Open file and deserialize
+ std::ifstream is(path.c_str(), std::ios_base::binary);
+ if (!is.good())
+ continue;
+
+ temp_player.deSerialize(is, path, NULL);
+ is.close();
+
+ if (temp_player.getName() == name) {
+ fs::DeleteSingleFileOrEmptyDirectory(path);
+ return true;
+ }
+
+ path = players_path + name + itos(i);
+ }
+
+ return false;
+}
+
+bool PlayerDatabaseFiles::loadPlayer(RemotePlayer *player, PlayerSAO *sao)
+{
+ std::string players_path = m_savedir + DIR_DELIM;
+ std::string path = players_path + player->getName();
+
+ const std::string player_to_load = player->getName();
+ for (u32 i = 0; i < PLAYER_FILE_ALTERNATE_TRIES; i++) {
+ // Open file and deserialize
+ std::ifstream is(path.c_str(), std::ios_base::binary);
+ if (!is.good())
+ continue;
+
+ player->deSerialize(is, path, sao);
+ is.close();
+
+ if (player->getName() == player_to_load)
+ return true;
+
+ path = players_path + player_to_load + itos(i);
+ }
+
+ infostream << "Player file for player " << player_to_load << " not found" << std::endl;
+ return false;
+}
+
+void PlayerDatabaseFiles::listPlayers(std::vector<std::string> &res)
+{
+ std::vector<fs::DirListNode> files = fs::GetDirListing(m_savedir);
+ // list files into players directory
+ for (std::vector<fs::DirListNode>::const_iterator it = files.begin(); it !=
+ files.end(); ++it) {
+ // Ignore directories
+ if (it->dir)
+ continue;
+
+ const std::string &filename = it->name;
+ std::string full_path = m_savedir + DIR_DELIM + filename;
+ std::ifstream is(full_path.c_str(), std::ios_base::binary);
+ if (!is.good())
+ continue;
+
+ RemotePlayer player(filename.c_str(), NULL);
+ // Null env & dummy peer_id
+ PlayerSAO playerSAO(NULL, &player, 15789, false);
+
+ player.deSerialize(is, "", &playerSAO);
+ is.close();
+
+ res.push_back(player.getName());
+ }
+}
diff --git a/src/database-files.h b/src/database-files.h
new file mode 100644
index 000000000..888362249
--- /dev/null
+++ b/src/database-files.h
@@ -0,0 +1,46 @@
+/*
+Minetest
+Copyright (C) 2017 nerzhul, Loic Blot <loic.blot@unix-experience.fr>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef DATABASE_FILES_HEADER
+#define DATABASE_FILES_HEADER
+
+// !!! WARNING !!!
+// This backend is intended to be used on Minetest 0.4.16 only for the transition backend
+// for player files
+
+#include "database.h"
+
+class PlayerDatabaseFiles : public PlayerDatabase
+{
+public:
+ PlayerDatabaseFiles(const std::string &savedir) : m_savedir(savedir) {}
+ virtual ~PlayerDatabaseFiles() {}
+
+ void savePlayer(RemotePlayer *player);
+ bool loadPlayer(RemotePlayer *player, PlayerSAO *sao);
+ bool removePlayer(const std::string &name);
+ void listPlayers(std::vector<std::string> &res);
+
+private:
+ void serialize(std::ostringstream &os, RemotePlayer *player);
+
+ std::string m_savedir;
+};
+
+#endif
diff --git a/src/database-leveldb.h b/src/database-leveldb.h
index 171946741..52ccebe70 100644
--- a/src/database-leveldb.h
+++ b/src/database-leveldb.h
@@ -28,7 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "database.h"
#include "leveldb/db.h"
-class Database_LevelDB : public Database
+class Database_LevelDB : public MapDatabase
{
public:
Database_LevelDB(const std::string &savedir);
@@ -39,6 +39,8 @@ public:
bool deleteBlock(const v3s16 &pos);
void listAllLoadableBlocks(std::vector<v3s16> &dst);
+ void beginSave() {}
+ void endSave() {}
private:
leveldb::DB *m_database;
};
diff --git a/src/database-postgresql.cpp b/src/database-postgresql.cpp
index 83678fd52..a6b62bad5 100644
--- a/src/database-postgresql.cpp
+++ b/src/database-postgresql.cpp
@@ -39,13 +39,15 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "log.h"
#include "exceptions.h"
#include "settings.h"
+#include "content_sao.h"
+#include "remoteplayer.h"
-Database_PostgreSQL::Database_PostgreSQL(const Settings &conf) :
- m_connect_string(""),
+Database_PostgreSQL::Database_PostgreSQL(const std::string &connect_string) :
+ m_connect_string(connect_string),
m_conn(NULL),
m_pgversion(0)
{
- if (!conf.getNoEx("pgsql_connection", m_connect_string)) {
+ if (m_connect_string.empty()) {
throw SettingNotFoundException(
"Set pgsql_connection string in world.mt to "
"use the postgresql backend\n"
@@ -57,8 +59,6 @@ Database_PostgreSQL::Database_PostgreSQL(const Settings &conf) :
"DELETE rights on the database.\n"
"Don't create mt_user as a SUPERUSER!");
}
-
- connectToDatabase();
}
Database_PostgreSQL::~Database_PostgreSQL()
@@ -118,40 +118,6 @@ bool Database_PostgreSQL::initialized() const
return (PQstatus(m_conn) == CONNECTION_OK);
}
-void Database_PostgreSQL::initStatements()
-{
- prepareStatement("read_block",
- "SELECT data FROM blocks "
- "WHERE posX = $1::int4 AND posY = $2::int4 AND "
- "posZ = $3::int4");
-
- if (m_pgversion < 90500) {
- prepareStatement("write_block_insert",
- "INSERT INTO blocks (posX, posY, posZ, data) SELECT "
- "$1::int4, $2::int4, $3::int4, $4::bytea "
- "WHERE NOT EXISTS (SELECT true FROM blocks "
- "WHERE posX = $1::int4 AND posY = $2::int4 AND "
- "posZ = $3::int4)");
-
- prepareStatement("write_block_update",
- "UPDATE blocks SET data = $4::bytea "
- "WHERE posX = $1::int4 AND posY = $2::int4 AND "
- "posZ = $3::int4");
- } else {
- prepareStatement("write_block",
- "INSERT INTO blocks (posX, posY, posZ, data) VALUES "
- "($1::int4, $2::int4, $3::int4, $4::bytea) "
- "ON CONFLICT ON CONSTRAINT blocks_pkey DO "
- "UPDATE SET data = $4::bytea");
- }
-
- prepareStatement("delete_block", "DELETE FROM blocks WHERE "
- "posX = $1::int4 AND posY = $2::int4 AND posZ = $3::int4");
-
- prepareStatement("list_all_loadable_blocks",
- "SELECT posX, posY, posZ FROM blocks");
-}
-
PGresult *Database_PostgreSQL::checkResults(PGresult *result, bool clear)
{
ExecStatusType statusType = PQresultStatus(result);
@@ -173,30 +139,21 @@ PGresult *Database_PostgreSQL::checkResults(PGresult *result, bool clear)
return result;
}
-void Database_PostgreSQL::createDatabase()
+void Database_PostgreSQL::createTableIfNotExists(const std::string &table_name,
+ const std::string &definition)
{
- PGresult *result = checkResults(PQexec(m_conn,
- "SELECT relname FROM pg_class WHERE relname='blocks';"),
- false);
+ std::string sql_check_table = "SELECT relname FROM pg_class WHERE relname='" +
+ table_name + "';";
+ PGresult *result = checkResults(PQexec(m_conn, sql_check_table.c_str()), false);
// If table doesn't exist, create it
if (!PQntuples(result)) {
- static const char* dbcreate_sql = "CREATE TABLE blocks ("
- "posX INT NOT NULL,"
- "posY INT NOT NULL,"
- "posZ INT NOT NULL,"
- "data BYTEA,"
- "PRIMARY KEY (posX,posY,posZ)"
- ");";
- checkResults(PQexec(m_conn, dbcreate_sql));
+ checkResults(PQexec(m_conn, definition.c_str()));
}
PQclear(result);
-
- infostream << "PostgreSQL: Game Database was inited." << std::endl;
}
-
void Database_PostgreSQL::beginSave()
{
verifyDatabase();
@@ -208,14 +165,70 @@ void Database_PostgreSQL::endSave()
checkResults(PQexec(m_conn, "COMMIT;"));
}
-bool Database_PostgreSQL::saveBlock(const v3s16 &pos,
- const std::string &data)
+MapDatabasePostgreSQL::MapDatabasePostgreSQL(const std::string &connect_string):
+ Database_PostgreSQL(connect_string),
+ MapDatabase()
+{
+ connectToDatabase();
+}
+
+
+void MapDatabasePostgreSQL::createDatabase()
+{
+ createTableIfNotExists("blocks",
+ "CREATE TABLE blocks ("
+ "posX INT NOT NULL,"
+ "posY INT NOT NULL,"
+ "posZ INT NOT NULL,"
+ "data BYTEA,"
+ "PRIMARY KEY (posX,posY,posZ)"
+ ");"
+ );
+
+ infostream << "PostgreSQL: Map Database was initialized." << std::endl;
+}
+
+void MapDatabasePostgreSQL::initStatements()
+{
+ prepareStatement("read_block",
+ "SELECT data FROM blocks "
+ "WHERE posX = $1::int4 AND posY = $2::int4 AND "
+ "posZ = $3::int4");
+
+ if (getPGVersion() < 90500) {
+ prepareStatement("write_block_insert",
+ "INSERT INTO blocks (posX, posY, posZ, data) SELECT "
+ "$1::int4, $2::int4, $3::int4, $4::bytea "
+ "WHERE NOT EXISTS (SELECT true FROM blocks "
+ "WHERE posX = $1::int4 AND posY = $2::int4 AND "
+ "posZ = $3::int4)");
+
+ prepareStatement("write_block_update",
+ "UPDATE blocks SET data = $4::bytea "
+ "WHERE posX = $1::int4 AND posY = $2::int4 AND "
+ "posZ = $3::int4");
+ } else {
+ prepareStatement("write_block",
+ "INSERT INTO blocks (posX, posY, posZ, data) VALUES "
+ "($1::int4, $2::int4, $3::int4, $4::bytea) "
+ "ON CONFLICT ON CONSTRAINT blocks_pkey DO "
+ "UPDATE SET data = $4::bytea");
+ }
+
+ prepareStatement("delete_block", "DELETE FROM blocks WHERE "
+ "posX = $1::int4 AND posY = $2::int4 AND posZ = $3::int4");
+
+ prepareStatement("list_all_loadable_blocks",
+ "SELECT posX, posY, posZ FROM blocks");
+}
+
+bool MapDatabasePostgreSQL::saveBlock(const v3s16 &pos, const std::string &data)
{
// Verify if we don't overflow the platform integer with the mapblock size
if (data.size() > INT_MAX) {
errorstream << "Database_PostgreSQL::saveBlock: Data truncation! "
- << "data.size() over 0xFFFF (== " << data.size()
- << ")" << std::endl;
+ << "data.size() over 0xFFFFFFFF (== " << data.size()
+ << ")" << std::endl;
return false;
}
@@ -232,7 +245,7 @@ bool Database_PostgreSQL::saveBlock(const v3s16 &pos,
};
const int argFmt[] = { 1, 1, 1, 1 };
- if (m_pgversion < 90500) {
+ if (getPGVersion() < 90500) {
execPrepared("write_block_update", ARRLEN(args), args, argLen, argFmt);
execPrepared("write_block_insert", ARRLEN(args), args, argLen, argFmt);
} else {
@@ -241,8 +254,7 @@ bool Database_PostgreSQL::saveBlock(const v3s16 &pos,
return true;
}
-void Database_PostgreSQL::loadBlock(const v3s16 &pos,
- std::string *block)
+void MapDatabasePostgreSQL::loadBlock(const v3s16 &pos, std::string *block)
{
verifyDatabase();
@@ -256,19 +268,17 @@ void Database_PostgreSQL::loadBlock(const v3s16 &pos,
const int argFmt[] = { 1, 1, 1 };
PGresult *results = execPrepared("read_block", ARRLEN(args), args,
- argLen, argFmt, false);
+ argLen, argFmt, false);
*block = "";
- if (PQntuples(results)) {
- *block = std::string(PQgetvalue(results, 0, 0),
- PQgetlength(results, 0, 0));
- }
+ if (PQntuples(results))
+ *block = std::string(PQgetvalue(results, 0, 0), PQgetlength(results, 0, 0));
PQclear(results);
}
-bool Database_PostgreSQL::deleteBlock(const v3s16 &pos)
+bool MapDatabasePostgreSQL::deleteBlock(const v3s16 &pos)
{
verifyDatabase();
@@ -286,18 +296,338 @@ bool Database_PostgreSQL::deleteBlock(const v3s16 &pos)
return true;
}
-void Database_PostgreSQL::listAllLoadableBlocks(std::vector<v3s16> &dst)
+void MapDatabasePostgreSQL::listAllLoadableBlocks(std::vector<v3s16> &dst)
{
verifyDatabase();
PGresult *results = execPrepared("list_all_loadable_blocks", 0,
- NULL, NULL, NULL, false, false);
+ NULL, NULL, NULL, false, false);
int numrows = PQntuples(results);
- for (int row = 0; row < numrows; ++row) {
+ for (int row = 0; row < numrows; ++row)
dst.push_back(pg_to_v3s16(results, 0, 0));
+
+ PQclear(results);
+}
+
+/*
+ * Player Database
+ */
+PlayerDatabasePostgreSQL::PlayerDatabasePostgreSQL(const std::string &connect_string):
+ Database_PostgreSQL(connect_string),
+ PlayerDatabase()
+{
+ connectToDatabase();
+}
+
+
+void PlayerDatabasePostgreSQL::createDatabase()
+{
+ createTableIfNotExists("player",
+ "CREATE TABLE player ("
+ "name VARCHAR(60) NOT NULL,"
+ "pitch NUMERIC(15, 7) NOT NULL,"
+ "yaw NUMERIC(15, 7) NOT NULL,"
+ "posX NUMERIC(15, 7) NOT NULL,"
+ "posY NUMERIC(15, 7) NOT NULL,"
+ "posZ NUMERIC(15, 7) NOT NULL,"
+ "hp INT NOT NULL,"
+ "breath INT NOT NULL,"
+ "creation_date TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT NOW(),"
+ "modification_date TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT NOW(),"
+ "PRIMARY KEY (name)"
+ ");"
+ );
+
+ createTableIfNotExists("player_inventories",
+ "CREATE TABLE player_inventories ("
+ "player VARCHAR(60) NOT NULL,"
+ "inv_id INT NOT NULL,"
+ "inv_width INT NOT NULL,"
+ "inv_name TEXT NOT NULL DEFAULT '',"
+ "inv_size INT NOT NULL,"
+ "PRIMARY KEY(player, inv_id),"
+ "CONSTRAINT player_inventories_fkey FOREIGN KEY (player) REFERENCES "
+ "player (name) ON DELETE CASCADE"
+ ");"
+ );
+
+ createTableIfNotExists("player_inventory_items",
+ "CREATE TABLE player_inventory_items ("
+ "player VARCHAR(60) NOT NULL,"
+ "inv_id INT NOT NULL,"
+ "slot_id INT NOT NULL,"
+ "item TEXT NOT NULL DEFAULT '',"
+ "PRIMARY KEY(player, inv_id, slot_id),"
+ "CONSTRAINT player_inventory_items_fkey FOREIGN KEY (player) REFERENCES "
+ "player (name) ON DELETE CASCADE"
+ ");"
+ );
+
+ createTableIfNotExists("player_metadata",
+ "CREATE TABLE player_metadata ("
+ "player VARCHAR(60) NOT NULL,"
+ "attr VARCHAR(256) NOT NULL,"
+ "value TEXT,"
+ "PRIMARY KEY(player, attr),"
+ "CONSTRAINT player_metadata_fkey FOREIGN KEY (player) REFERENCES "
+ "player (name) ON DELETE CASCADE"
+ ");"
+ );
+
+ infostream << "PostgreSQL: Player Database was inited." << std::endl;
+}
+
+void PlayerDatabasePostgreSQL::initStatements()
+{
+ if (getPGVersion() < 90500) {
+ prepareStatement("create_player",
+ "INSERT INTO player(name, pitch, yaw, posX, posY, posZ, hp, breath) VALUES "
+ "($1, $2, $3, $4, $5, $6, $7::int, $8::int)");
+
+ prepareStatement("update_player",
+ "UPDATE SET pitch = $2, yaw = $3, posX = $4, posY = $5, posZ = $6, hp = $7::int, "
+ "breath = $8::int, modification_date = NOW() WHERE name = $1");
+ } else {
+ prepareStatement("save_player",
+ "INSERT INTO player(name, pitch, yaw, posX, posY, posZ, hp, breath) VALUES "
+ "($1, $2, $3, $4, $5, $6, $7::int, $8::int)"
+ "ON CONFLICT ON CONSTRAINT player_pkey DO UPDATE SET pitch = $2, yaw = $3, "
+ "posX = $4, posY = $5, posZ = $6, hp = $7::int, breath = $8::int, "
+ "modification_date = NOW()");
+ }
+
+ prepareStatement("remove_player", "DELETE FROM player WHERE name = $1");
+
+ prepareStatement("load_player_list", "SELECT name FROM player");
+
+ prepareStatement("remove_player_inventories",
+ "DELETE FROM player_inventories WHERE player = $1");
+
+ prepareStatement("remove_player_inventory_items",
+ "DELETE FROM player_inventory_items WHERE player = $1");
+
+ prepareStatement("add_player_inventory",
+ "INSERT INTO player_inventories (player, inv_id, inv_width, inv_name, inv_size) VALUES "
+ "($1, $2::int, $3::int, $4, $5::int)");
+
+ prepareStatement("add_player_inventory_item",
+ "INSERT INTO player_inventory_items (player, inv_id, slot_id, item) VALUES "
+ "($1, $2::int, $3::int, $4)");
+
+ prepareStatement("load_player_inventories",
+ "SELECT inv_id, inv_width, inv_name, inv_size FROM player_inventories "
+ "WHERE player = $1 ORDER BY inv_id");
+
+ prepareStatement("load_player_inventory_items",
+ "SELECT slot_id, item FROM player_inventory_items WHERE "
+ "player = $1 AND inv_id = $2::int");
+
+ prepareStatement("load_player",
+ "SELECT pitch, yaw, posX, posY, posZ, hp, breath FROM player WHERE name = $1");
+
+ prepareStatement("remove_player_metadata",
+ "DELETE FROM player_metadata WHERE player = $1");
+
+ prepareStatement("save_player_metadata",
+ "INSERT INTO player_metadata (player, attr, value) VALUES ($1, $2, $3)");
+
+ prepareStatement("load_player_metadata",
+ "SELECT attr, value FROM player_metadata WHERE player = $1");
+
+}
+
+bool PlayerDatabasePostgreSQL::playerDataExists(const std::string &playername)
+{
+ verifyDatabase();
+
+ const char *values[] = { playername.c_str() };
+ PGresult *results = execPrepared("load_player", 1, values, false);
+
+ bool res = (PQntuples(results) > 0);
+ PQclear(results);
+ return res;
+}
+
+void PlayerDatabasePostgreSQL::savePlayer(RemotePlayer *player)
+{
+ PlayerSAO* sao = player->getPlayerSAO();
+ if (!sao)
+ return;
+
+ verifyDatabase();
+
+ v3f pos = sao->getBasePosition();
+ std::string pitch = ftos(sao->getPitch());
+ std::string yaw = ftos(sao->getYaw());
+ std::string posx = ftos(pos.X);
+ std::string posy = ftos(pos.Y);
+ std::string posz = ftos(pos.Z);
+ std::string hp = itos(sao->getHP());
+ std::string breath = itos(sao->getBreath());
+ const char *values[] = {
+ player->getName(),
+ pitch.c_str(),
+ yaw.c_str(),
+ posx.c_str(), posy.c_str(), posz.c_str(),
+ hp.c_str(),
+ breath.c_str()
+ };
+
+ const char* rmvalues[] = { player->getName() };
+ beginSave();
+
+ if (getPGVersion() < 90500) {
+ if (!playerDataExists(player->getName()))
+ execPrepared("create_player", 8, values, true, false);
+ else
+ execPrepared("update_player", 8, values, true, false);
+ }
+ else
+ execPrepared("save_player", 8, values, true, false);
+
+ // Write player inventories
+ execPrepared("remove_player_inventories", 1, rmvalues);
+ execPrepared("remove_player_inventory_items", 1, rmvalues);
+
+ std::vector<const InventoryList*> inventory_lists = sao->getInventory()->getLists();
+ for (u16 i = 0; i < inventory_lists.size(); i++) {
+ const InventoryList* list = inventory_lists[i];
+ std::string name = list->getName(), width = itos(list->getWidth()),
+ inv_id = itos(i), lsize = itos(list->getSize());
+
+ const char* inv_values[] = {
+ player->getName(),
+ inv_id.c_str(),
+ width.c_str(),
+ name.c_str(),
+ lsize.c_str()
+ };
+ execPrepared("add_player_inventory", 5, inv_values);
+
+ for (u32 j = 0; j < list->getSize(); j++) {
+ std::ostringstream os;
+ list->getItem(j).serialize(os);
+ std::string itemStr = os.str(), slotId = itos(j);
+
+ const char* invitem_values[] = {
+ player->getName(),
+ inv_id.c_str(),
+ slotId.c_str(),
+ itemStr.c_str()
+ };
+ execPrepared("add_player_inventory_item", 4, invitem_values);
+ }
+ }
+
+ execPrepared("remove_player_metadata", 1, rmvalues);
+ const PlayerAttributes &attrs = sao->getExtendedAttributes();
+ for (PlayerAttributes::const_iterator it = attrs.begin(); it != attrs.end(); ++it) {
+ const char *meta_values[] = {
+ player->getName(),
+ it->first.c_str(),
+ it->second.c_str()
+ };
+ execPrepared("save_player_metadata", 3, meta_values);
}
+ endSave();
+}
+
+bool PlayerDatabasePostgreSQL::loadPlayer(RemotePlayer *player, PlayerSAO *sao)
+{
+ sanity_check(sao);
+ verifyDatabase();
+
+ const char *values[] = { player->getName() };
+ PGresult *results = execPrepared("load_player", 1, values, false, false);
+
+ // Player not found, return not found
+ if (!PQntuples(results)) {
+ PQclear(results);
+ return false;
+ }
+
+ sao->setPitch(pg_to_float(results, 0, 0));
+ sao->setYaw(pg_to_float(results, 0, 1));
+ sao->setBasePosition(v3f(
+ pg_to_float(results, 0, 2),
+ pg_to_float(results, 0, 3),
+ pg_to_float(results, 0, 4))
+ );
+ sao->setHPRaw((s16) pg_to_int(results, 0, 5));
+ sao->setBreath((u16) pg_to_int(results, 0, 6), false);
+
+ PQclear(results);
+
+ // Load inventory
+ results = execPrepared("load_player_inventories", 1, values, false, false);
+
+ int resultCount = PQntuples(results);
+
+ for (int row = 0; row < resultCount; ++row) {
+ InventoryList* invList = player->inventory.
+ addList(PQgetvalue(results, row, 2), pg_to_uint(results, row, 3));
+ invList->setWidth(pg_to_uint(results, row, 1));
+
+ u32 invId = pg_to_uint(results, row, 0);
+ std::string invIdStr = itos(invId);
+
+ const char* values2[] = {
+ player->getName(),
+ invIdStr.c_str()
+ };
+ PGresult *results2 = execPrepared("load_player_inventory_items", 2,
+ values2, false, false);
+
+ int resultCount2 = PQntuples(results2);
+ for (int row2 = 0; row2 < resultCount2; row2++) {
+ const std::string itemStr = PQgetvalue(results2, row2, 1);
+ if (itemStr.length() > 0) {
+ ItemStack stack;
+ stack.deSerialize(itemStr);
+ invList->addItem(pg_to_uint(results2, row2, 0), stack);
+ }
+ }
+ PQclear(results2);
+ }
+
+ PQclear(results);
+
+ results = execPrepared("load_player_metadata", 1, values, false);
+
+ int numrows = PQntuples(results);
+ for (int row = 0; row < numrows; row++) {
+ sao->setExtendedAttribute(PQgetvalue(results, row, 0),PQgetvalue(results, row, 1));
+ }
+
+ PQclear(results);
+
+ return true;
+}
+
+bool PlayerDatabasePostgreSQL::removePlayer(const std::string &name)
+{
+ if (!playerDataExists(name))
+ return false;
+
+ verifyDatabase();
+
+ const char *values[] = { name.c_str() };
+ execPrepared("remove_player", 1, values);
+
+ return true;
+}
+
+void PlayerDatabasePostgreSQL::listPlayers(std::vector<std::string> &res)
+{
+ verifyDatabase();
+
+ PGresult *results = execPrepared("load_player_list", 0, NULL, false);
+
+ int numrows = PQntuples(results);
+ for (int row = 0; row < numrows; row++)
+ res.push_back(PQgetvalue(results, row, 0));
PQclear(results);
}
diff --git a/src/database-postgresql.h b/src/database-postgresql.h
index 1cfa544e3..d6f208fd9 100644
--- a/src/database-postgresql.h
+++ b/src/database-postgresql.h
@@ -27,53 +27,33 @@ with this program; if not, write to the Free Software Foundation, Inc.,
class Settings;
-class Database_PostgreSQL : public Database
+class Database_PostgreSQL: public Database
{
public:
- Database_PostgreSQL(const Settings &conf);
+ Database_PostgreSQL(const std::string &connect_string);
~Database_PostgreSQL();
void beginSave();
void endSave();
- bool saveBlock(const v3s16 &pos, const std::string &data);
- void loadBlock(const v3s16 &pos, std::string *block);
- bool deleteBlock(const v3s16 &pos);
- void listAllLoadableBlocks(std::vector<v3s16> &dst);
bool initialized() const;
-private:
- // Database initialization
- void connectToDatabase();
- void initStatements();
- void createDatabase();
- inline void prepareStatement(const std::string &name, const std::string &sql)
+protected:
+ // Conversion helpers
+ inline int pg_to_int(PGresult *res, int row, int col)
{
- checkResults(PQprepare(m_conn, name.c_str(), sql.c_str(), 0, NULL));
+ return atoi(PQgetvalue(res, row, col));
}
- // Database connectivity checks
- void ping();
- void verifyDatabase();
-
- // Database usage
- PGresult *checkResults(PGresult *res, bool clear = true);
-
- inline PGresult *execPrepared(const char *stmtName, const int paramsNumber,
- const void **params,
- const int *paramsLengths = NULL, const int *paramsFormats = NULL,
- bool clear = true, bool nobinary = true)
+ inline u32 pg_to_uint(PGresult *res, int row, int col)
{
- return checkResults(PQexecPrepared(m_conn, stmtName, paramsNumber,
- (const char* const*) params, paramsLengths, paramsFormats,
- nobinary ? 1 : 0), clear);
+ return (u32) atoi(PQgetvalue(res, row, col));
}
- // Conversion helpers
- inline int pg_to_int(PGresult *res, int row, int col)
+ inline float pg_to_float(PGresult *res, int row, int col)
{
- return atoi(PQgetvalue(res, row, col));
+ return (float) atof(PQgetvalue(res, row, col));
}
inline v3s16 pg_to_v3s16(PGresult *res, int row, int col)
@@ -85,11 +65,86 @@ private:
);
}
+ inline PGresult *execPrepared(const char *stmtName, const int paramsNumber,
+ const void **params,
+ const int *paramsLengths = NULL, const int *paramsFormats = NULL,
+ bool clear = true, bool nobinary = true)
+ {
+ return checkResults(PQexecPrepared(m_conn, stmtName, paramsNumber,
+ (const char* const*) params, paramsLengths, paramsFormats,
+ nobinary ? 1 : 0), clear);
+ }
+
+ inline PGresult *execPrepared(const char *stmtName, const int paramsNumber,
+ const char **params, bool clear = true, bool nobinary = true)
+ {
+ return execPrepared(stmtName, paramsNumber,
+ (const void **)params, NULL, NULL, clear, nobinary);
+ }
+
+ void createTableIfNotExists(const std::string &table_name, const std::string &definition);
+ void verifyDatabase();
+
+ // Database initialization
+ void connectToDatabase();
+ virtual void createDatabase() = 0;
+ virtual void initStatements() = 0;
+ inline void prepareStatement(const std::string &name, const std::string &sql)
+ {
+ checkResults(PQprepare(m_conn, name.c_str(), sql.c_str(), 0, NULL));
+ }
+
+ const int getPGVersion() const { return m_pgversion; }
+private:
+ // Database connectivity checks
+ void ping();
+
+ // Database usage
+ PGresult *checkResults(PGresult *res, bool clear = true);
+
// Attributes
std::string m_connect_string;
PGconn *m_conn;
int m_pgversion;
};
+class MapDatabasePostgreSQL : private Database_PostgreSQL, public MapDatabase
+{
+public:
+ MapDatabasePostgreSQL(const std::string &connect_string);
+ virtual ~MapDatabasePostgreSQL() {}
+
+ bool saveBlock(const v3s16 &pos, const std::string &data);
+ void loadBlock(const v3s16 &pos, std::string *block);
+ bool deleteBlock(const v3s16 &pos);
+ void listAllLoadableBlocks(std::vector<v3s16> &dst);
+
+ void beginSave() { Database_PostgreSQL::beginSave(); }
+ void endSave() { Database_PostgreSQL::endSave(); }
+
+protected:
+ virtual void createDatabase();
+ virtual void initStatements();
+};
+
+class PlayerDatabasePostgreSQL : private Database_PostgreSQL, public PlayerDatabase
+{
+public:
+ PlayerDatabasePostgreSQL(const std::string &connect_string);
+ virtual ~PlayerDatabasePostgreSQL() {}
+
+ void savePlayer(RemotePlayer *player);
+ bool loadPlayer(RemotePlayer *player, PlayerSAO *sao);
+ bool removePlayer(const std::string &name);
+ void listPlayers(std::vector<std::string> &res);
+
+protected:
+ virtual void createDatabase();
+ virtual void initStatements();
+
+private:
+ bool playerDataExists(const std::string &playername);
+};
+
#endif
diff --git a/src/database-redis.h b/src/database-redis.h
index 214bc8dd6..fa15dd8a7 100644
--- a/src/database-redis.h
+++ b/src/database-redis.h
@@ -30,7 +30,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
class Settings;
-class Database_Redis : public Database
+class Database_Redis : public MapDatabase
{
public:
Database_Redis(Settings &conf);
diff --git a/src/database-sqlite3.cpp b/src/database-sqlite3.cpp
index 095d485c0..7bc87a7d0 100644
--- a/src/database-sqlite3.cpp
+++ b/src/database-sqlite3.cpp
@@ -33,6 +33,8 @@ SQLite format specification:
#include "settings.h"
#include "porting.h"
#include "util/string.h"
+#include "content_sao.h"
+#include "remoteplayer.h"
#include <cassert>
@@ -69,7 +71,7 @@ int Database_SQLite3::busyHandler(void *data, int count)
{
s64 &first_time = reinterpret_cast<s64 *>(data)[0];
s64 &prev_time = reinterpret_cast<s64 *>(data)[1];
- s64 cur_time = getTimeMs();
+ s64 cur_time = porting::getTimeMs();
if (count == 0) {
first_time = cur_time;
@@ -111,27 +113,26 @@ int Database_SQLite3::busyHandler(void *data, int count)
}
-Database_SQLite3::Database_SQLite3(const std::string &savedir) :
+Database_SQLite3::Database_SQLite3(const std::string &savedir, const std::string &dbname) :
+ m_database(NULL),
m_initialized(false),
m_savedir(savedir),
- m_database(NULL),
- m_stmt_read(NULL),
- m_stmt_write(NULL),
- m_stmt_list(NULL),
- m_stmt_delete(NULL),
+ m_dbname(dbname),
m_stmt_begin(NULL),
m_stmt_end(NULL)
{
}
-void Database_SQLite3::beginSave() {
+void Database_SQLite3::beginSave()
+{
verifyDatabase();
SQLRES(sqlite3_step(m_stmt_begin), SQLITE_DONE,
"Failed to start SQLite3 transaction");
sqlite3_reset(m_stmt_begin);
}
-void Database_SQLite3::endSave() {
+void Database_SQLite3::endSave()
+{
verifyDatabase();
SQLRES(sqlite3_step(m_stmt_end), SQLITE_DONE,
"Failed to commit SQLite3 transaction");
@@ -142,7 +143,7 @@ void Database_SQLite3::openDatabase()
{
if (m_database) return;
- std::string dbp = m_savedir + DIR_DELIM + "map.sqlite";
+ std::string dbp = m_savedir + DIR_DELIM + m_dbname + ".sqlite";
// Open the database connection
@@ -170,6 +171,8 @@ void Database_SQLite3::openDatabase()
+ itos(g_settings->getU16("sqlite_synchronous"));
SQLOK(sqlite3_exec(m_database, query_str.c_str(), NULL, NULL, NULL),
"Failed to modify sqlite3 synchronous mode");
+ SQLOK(sqlite3_exec(m_database, "PRAGMA foreign_keys = ON", NULL, NULL, NULL),
+ "Failed to enable sqlite3 foreign key support");
}
void Database_SQLite3::verifyDatabase()
@@ -178,8 +181,61 @@ void Database_SQLite3::verifyDatabase()
openDatabase();
- PREPARE_STATEMENT(begin, "BEGIN");
- PREPARE_STATEMENT(end, "COMMIT");
+ PREPARE_STATEMENT(begin, "BEGIN;");
+ PREPARE_STATEMENT(end, "COMMIT;");
+
+ initStatements();
+
+ m_initialized = true;
+}
+
+Database_SQLite3::~Database_SQLite3()
+{
+ FINALIZE_STATEMENT(m_stmt_begin)
+ FINALIZE_STATEMENT(m_stmt_end)
+
+ SQLOK_ERRSTREAM(sqlite3_close(m_database), "Failed to close database");
+}
+
+/*
+ * Map database
+ */
+
+MapDatabaseSQLite3::MapDatabaseSQLite3(const std::string &savedir):
+ Database_SQLite3(savedir, "map"),
+ MapDatabase(),
+ m_stmt_read(NULL),
+ m_stmt_write(NULL),
+ m_stmt_list(NULL),
+ m_stmt_delete(NULL)
+{
+
+}
+
+MapDatabaseSQLite3::~MapDatabaseSQLite3()
+{
+ FINALIZE_STATEMENT(m_stmt_read)
+ FINALIZE_STATEMENT(m_stmt_write)
+ FINALIZE_STATEMENT(m_stmt_list)
+ FINALIZE_STATEMENT(m_stmt_delete)
+}
+
+
+void MapDatabaseSQLite3::createDatabase()
+{
+ assert(m_database); // Pre-condition
+
+ SQLOK(sqlite3_exec(m_database,
+ "CREATE TABLE IF NOT EXISTS `blocks` (\n"
+ " `pos` INT PRIMARY KEY,\n"
+ " `data` BLOB\n"
+ ");\n",
+ NULL, NULL, NULL),
+ "Failed to create database table");
+}
+
+void MapDatabaseSQLite3::initStatements()
+{
PREPARE_STATEMENT(read, "SELECT `data` FROM `blocks` WHERE `pos` = ? LIMIT 1");
#ifdef __ANDROID__
PREPARE_STATEMENT(write, "INSERT INTO `blocks` (`pos`, `data`) VALUES (?, ?)");
@@ -189,18 +245,16 @@ void Database_SQLite3::verifyDatabase()
PREPARE_STATEMENT(delete, "DELETE FROM `blocks` WHERE `pos` = ?");
PREPARE_STATEMENT(list, "SELECT `pos` FROM `blocks`");
- m_initialized = true;
-
verbosestream << "ServerMap: SQLite3 database opened." << std::endl;
}
-inline void Database_SQLite3::bindPos(sqlite3_stmt *stmt, const v3s16 &pos, int index)
+inline void MapDatabaseSQLite3::bindPos(sqlite3_stmt *stmt, const v3s16 &pos, int index)
{
SQLOK(sqlite3_bind_int64(stmt, index, getBlockAsInteger(pos)),
"Internal error: failed to bind query at " __FILE__ ":" TOSTRING(__LINE__));
}
-bool Database_SQLite3::deleteBlock(const v3s16 &pos)
+bool MapDatabaseSQLite3::deleteBlock(const v3s16 &pos)
{
verifyDatabase();
@@ -216,7 +270,7 @@ bool Database_SQLite3::deleteBlock(const v3s16 &pos)
return good;
}
-bool Database_SQLite3::saveBlock(const v3s16 &pos, const std::string &data)
+bool MapDatabaseSQLite3::saveBlock(const v3s16 &pos, const std::string &data)
{
verifyDatabase();
@@ -243,7 +297,7 @@ bool Database_SQLite3::saveBlock(const v3s16 &pos, const std::string &data)
return true;
}
-void Database_SQLite3::loadBlock(const v3s16 &pos, std::string *block)
+void MapDatabaseSQLite3::loadBlock(const v3s16 &pos, std::string *block)
{
verifyDatabase();
@@ -264,37 +318,312 @@ void Database_SQLite3::loadBlock(const v3s16 &pos, std::string *block)
sqlite3_reset(m_stmt_read);
}
-void Database_SQLite3::createDatabase()
+void MapDatabaseSQLite3::listAllLoadableBlocks(std::vector<v3s16> &dst)
+{
+ verifyDatabase();
+
+ while (sqlite3_step(m_stmt_list) == SQLITE_ROW)
+ dst.push_back(getIntegerAsBlock(sqlite3_column_int64(m_stmt_list, 0)));
+
+ sqlite3_reset(m_stmt_list);
+}
+
+/*
+ * Player Database
+ */
+
+PlayerDatabaseSQLite3::PlayerDatabaseSQLite3(const std::string &savedir):
+ Database_SQLite3(savedir, "players"),
+ PlayerDatabase(),
+ m_stmt_player_load(NULL),
+ m_stmt_player_add(NULL),
+ m_stmt_player_update(NULL),
+ m_stmt_player_remove(NULL),
+ m_stmt_player_list(NULL),
+ m_stmt_player_load_inventory(NULL),
+ m_stmt_player_load_inventory_items(NULL),
+ m_stmt_player_add_inventory(NULL),
+ m_stmt_player_add_inventory_items(NULL),
+ m_stmt_player_remove_inventory(NULL),
+ m_stmt_player_remove_inventory_items(NULL),
+ m_stmt_player_metadata_load(NULL),
+ m_stmt_player_metadata_remove(NULL),
+ m_stmt_player_metadata_add(NULL)
+{
+
+}
+PlayerDatabaseSQLite3::~PlayerDatabaseSQLite3()
+{
+ FINALIZE_STATEMENT(m_stmt_player_load)
+ FINALIZE_STATEMENT(m_stmt_player_add)
+ FINALIZE_STATEMENT(m_stmt_player_update)
+ FINALIZE_STATEMENT(m_stmt_player_remove)
+ FINALIZE_STATEMENT(m_stmt_player_list)
+ FINALIZE_STATEMENT(m_stmt_player_add_inventory)
+ FINALIZE_STATEMENT(m_stmt_player_add_inventory_items)
+ FINALIZE_STATEMENT(m_stmt_player_remove_inventory)
+ FINALIZE_STATEMENT(m_stmt_player_remove_inventory_items)
+ FINALIZE_STATEMENT(m_stmt_player_load_inventory)
+ FINALIZE_STATEMENT(m_stmt_player_load_inventory_items)
+ FINALIZE_STATEMENT(m_stmt_player_metadata_load)
+ FINALIZE_STATEMENT(m_stmt_player_metadata_add)
+ FINALIZE_STATEMENT(m_stmt_player_metadata_remove)
+};
+
+
+void PlayerDatabaseSQLite3::createDatabase()
{
assert(m_database); // Pre-condition
+
SQLOK(sqlite3_exec(m_database,
- "CREATE TABLE IF NOT EXISTS `blocks` (\n"
- " `pos` INT PRIMARY KEY,\n"
- " `data` BLOB\n"
- ");\n",
+ "CREATE TABLE IF NOT EXISTS `player` ("
+ "`name` VARCHAR(50) NOT NULL,"
+ "`pitch` NUMERIC(11, 4) NOT NULL,"
+ "`yaw` NUMERIC(11, 4) NOT NULL,"
+ "`posX` NUMERIC(11, 4) NOT NULL,"
+ "`posY` NUMERIC(11, 4) NOT NULL,"
+ "`posZ` NUMERIC(11, 4) NOT NULL,"
+ "`hp` INT NOT NULL,"
+ "`breath` INT NOT NULL,"
+ "`creation_date` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,"
+ "`modification_date` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,"
+ "PRIMARY KEY (`name`));",
NULL, NULL, NULL),
- "Failed to create database table");
+ "Failed to create player table");
+
+ SQLOK(sqlite3_exec(m_database,
+ "CREATE TABLE IF NOT EXISTS `player_metadata` ("
+ " `player` VARCHAR(50) NOT NULL,"
+ " `metadata` VARCHAR(256) NOT NULL,"
+ " `value` TEXT,"
+ " PRIMARY KEY(`player`, `metadata`),"
+ " FOREIGN KEY (`player`) REFERENCES player (`name`) ON DELETE CASCADE );",
+ NULL, NULL, NULL),
+ "Failed to create player metadata table");
+
+ SQLOK(sqlite3_exec(m_database,
+ "CREATE TABLE IF NOT EXISTS `player_inventories` ("
+ " `player` VARCHAR(50) NOT NULL,"
+ " `inv_id` INT NOT NULL,"
+ " `inv_width` INT NOT NULL,"
+ " `inv_name` TEXT NOT NULL DEFAULT '',"
+ " `inv_size` INT NOT NULL,"
+ " PRIMARY KEY(player, inv_id),"
+ " FOREIGN KEY (`player`) REFERENCES player (`name`) ON DELETE CASCADE );",
+ NULL, NULL, NULL),
+ "Failed to create player inventory table");
+
+ SQLOK(sqlite3_exec(m_database,
+ "CREATE TABLE `player_inventory_items` ("
+ " `player` VARCHAR(50) NOT NULL,"
+ " `inv_id` INT NOT NULL,"
+ " `slot_id` INT NOT NULL,"
+ " `item` TEXT NOT NULL DEFAULT '',"
+ " PRIMARY KEY(player, inv_id, slot_id),"
+ " FOREIGN KEY (`player`) REFERENCES player (`name`) ON DELETE CASCADE );",
+ NULL, NULL, NULL),
+ "Failed to create player inventory items table");
}
-void Database_SQLite3::listAllLoadableBlocks(std::vector<v3s16> &dst)
+void PlayerDatabaseSQLite3::initStatements()
+{
+ PREPARE_STATEMENT(player_load, "SELECT `pitch`, `yaw`, `posX`, `posY`, `posZ`, `hp`, "
+ "`breath`"
+ "FROM `player` WHERE `name` = ?")
+ PREPARE_STATEMENT(player_add, "INSERT INTO `player` (`name`, `pitch`, `yaw`, `posX`, "
+ "`posY`, `posZ`, `hp`, `breath`) VALUES (?, ?, ?, ?, ?, ?, ?, ?)")
+ PREPARE_STATEMENT(player_update, "UPDATE `player` SET `pitch` = ?, `yaw` = ?, "
+ "`posX` = ?, `posY` = ?, `posZ` = ?, `hp` = ?, `breath` = ?, "
+ "`modification_date` = CURRENT_TIMESTAMP WHERE `name` = ?")
+ PREPARE_STATEMENT(player_remove, "DELETE FROM `player` WHERE `name` = ?")
+ PREPARE_STATEMENT(player_list, "SELECT `name` FROM `player`")
+
+ PREPARE_STATEMENT(player_add_inventory, "INSERT INTO `player_inventories` "
+ "(`player`, `inv_id`, `inv_width`, `inv_name`, `inv_size`) VALUES (?, ?, ?, ?, ?)")
+ PREPARE_STATEMENT(player_add_inventory_items, "INSERT INTO `player_inventory_items` "
+ "(`player`, `inv_id`, `slot_id`, `item`) VALUES (?, ?, ?, ?)")
+ PREPARE_STATEMENT(player_remove_inventory, "DELETE FROM `player_inventories` "
+ "WHERE `player` = ?")
+ PREPARE_STATEMENT(player_remove_inventory_items, "DELETE FROM `player_inventory_items` "
+ "WHERE `player` = ?")
+ PREPARE_STATEMENT(player_load_inventory, "SELECT `inv_id`, `inv_width`, `inv_name`, "
+ "`inv_size` FROM `player_inventories` WHERE `player` = ? ORDER BY inv_id")
+ PREPARE_STATEMENT(player_load_inventory_items, "SELECT `slot_id`, `item` "
+ "FROM `player_inventory_items` WHERE `player` = ? AND `inv_id` = ?")
+
+ PREPARE_STATEMENT(player_metadata_load, "SELECT `metadata`, `value` FROM "
+ "`player_metadata` WHERE `player` = ?")
+ PREPARE_STATEMENT(player_metadata_add, "INSERT INTO `player_metadata` "
+ "(`player`, `metadata`, `value`) VALUES (?, ?, ?)")
+ PREPARE_STATEMENT(player_metadata_remove, "DELETE FROM `player_metadata` "
+ "WHERE `player` = ?")
+ verbosestream << "ServerEnvironment: SQLite3 database opened (players)." << std::endl;
+}
+
+bool PlayerDatabaseSQLite3::playerDataExists(const std::string &name)
{
verifyDatabase();
+ str_to_sqlite(m_stmt_player_load, 1, name);
+ bool res = (sqlite3_step(m_stmt_player_load) == SQLITE_ROW);
+ sqlite3_reset(m_stmt_player_load);
+ return res;
+}
- while (sqlite3_step(m_stmt_list) == SQLITE_ROW) {
- dst.push_back(getIntegerAsBlock(sqlite3_column_int64(m_stmt_list, 0)));
+void PlayerDatabaseSQLite3::savePlayer(RemotePlayer *player)
+{
+ PlayerSAO* sao = player->getPlayerSAO();
+ sanity_check(sao);
+
+ const v3f &pos = sao->getBasePosition();
+ // Begin save in brace is mandatory
+ if (!playerDataExists(player->getName())) {
+ beginSave();
+ str_to_sqlite(m_stmt_player_add, 1, player->getName());
+ double_to_sqlite(m_stmt_player_add, 2, sao->getPitch());
+ double_to_sqlite(m_stmt_player_add, 3, sao->getYaw());
+ double_to_sqlite(m_stmt_player_add, 4, pos.X);
+ double_to_sqlite(m_stmt_player_add, 5, pos.Y);
+ double_to_sqlite(m_stmt_player_add, 6, pos.Z);
+ int64_to_sqlite(m_stmt_player_add, 7, sao->getHP());
+ int64_to_sqlite(m_stmt_player_add, 8, sao->getBreath());
+
+ sqlite3_vrfy(sqlite3_step(m_stmt_player_add), SQLITE_DONE);
+ sqlite3_reset(m_stmt_player_add);
+ } else {
+ beginSave();
+ double_to_sqlite(m_stmt_player_update, 1, sao->getPitch());
+ double_to_sqlite(m_stmt_player_update, 2, sao->getYaw());
+ double_to_sqlite(m_stmt_player_update, 3, pos.X);
+ double_to_sqlite(m_stmt_player_update, 4, pos.Y);
+ double_to_sqlite(m_stmt_player_update, 5, pos.Z);
+ int64_to_sqlite(m_stmt_player_update, 6, sao->getHP());
+ int64_to_sqlite(m_stmt_player_update, 7, sao->getBreath());
+ str_to_sqlite(m_stmt_player_update, 8, player->getName());
+
+ sqlite3_vrfy(sqlite3_step(m_stmt_player_update), SQLITE_DONE);
+ sqlite3_reset(m_stmt_player_update);
}
- sqlite3_reset(m_stmt_list);
+
+ // Write player inventories
+ str_to_sqlite(m_stmt_player_remove_inventory, 1, player->getName());
+ sqlite3_vrfy(sqlite3_step(m_stmt_player_remove_inventory), SQLITE_DONE);
+ sqlite3_reset(m_stmt_player_remove_inventory);
+
+ str_to_sqlite(m_stmt_player_remove_inventory_items, 1, player->getName());
+ sqlite3_vrfy(sqlite3_step(m_stmt_player_remove_inventory_items), SQLITE_DONE);
+ sqlite3_reset(m_stmt_player_remove_inventory_items);
+
+ std::vector<const InventoryList*> inventory_lists = sao->getInventory()->getLists();
+ for (u16 i = 0; i < inventory_lists.size(); i++) {
+ const InventoryList* list = inventory_lists[i];
+
+ str_to_sqlite(m_stmt_player_add_inventory, 1, player->getName());
+ int_to_sqlite(m_stmt_player_add_inventory, 2, i);
+ int_to_sqlite(m_stmt_player_add_inventory, 3, list->getWidth());
+ str_to_sqlite(m_stmt_player_add_inventory, 4, list->getName());
+ int_to_sqlite(m_stmt_player_add_inventory, 5, list->getSize());
+ sqlite3_vrfy(sqlite3_step(m_stmt_player_add_inventory), SQLITE_DONE);
+ sqlite3_reset(m_stmt_player_add_inventory);
+
+ for (u32 j = 0; j < list->getSize(); j++) {
+ std::ostringstream os;
+ list->getItem(j).serialize(os);
+ std::string itemStr = os.str();
+
+ str_to_sqlite(m_stmt_player_add_inventory_items, 1, player->getName());
+ int_to_sqlite(m_stmt_player_add_inventory_items, 2, i);
+ int_to_sqlite(m_stmt_player_add_inventory_items, 3, j);
+ str_to_sqlite(m_stmt_player_add_inventory_items, 4, itemStr);
+ sqlite3_vrfy(sqlite3_step(m_stmt_player_add_inventory_items), SQLITE_DONE);
+ sqlite3_reset(m_stmt_player_add_inventory_items);
+ }
+ }
+
+ str_to_sqlite(m_stmt_player_metadata_remove, 1, player->getName());
+ sqlite3_vrfy(sqlite3_step(m_stmt_player_metadata_remove), SQLITE_DONE);
+ sqlite3_reset(m_stmt_player_metadata_remove);
+
+ const PlayerAttributes &attrs = sao->getExtendedAttributes();
+ for (PlayerAttributes::const_iterator it = attrs.begin(); it != attrs.end(); ++it) {
+ str_to_sqlite(m_stmt_player_metadata_add, 1, player->getName());
+ str_to_sqlite(m_stmt_player_metadata_add, 2, it->first);
+ str_to_sqlite(m_stmt_player_metadata_add, 3, it->second);
+ sqlite3_vrfy(sqlite3_step(m_stmt_player_metadata_add), SQLITE_DONE);
+ sqlite3_reset(m_stmt_player_metadata_add);
+ }
+
+ endSave();
}
-Database_SQLite3::~Database_SQLite3()
+bool PlayerDatabaseSQLite3::loadPlayer(RemotePlayer *player, PlayerSAO *sao)
{
- FINALIZE_STATEMENT(m_stmt_read)
- FINALIZE_STATEMENT(m_stmt_write)
- FINALIZE_STATEMENT(m_stmt_list)
- FINALIZE_STATEMENT(m_stmt_begin)
- FINALIZE_STATEMENT(m_stmt_end)
- FINALIZE_STATEMENT(m_stmt_delete)
+ verifyDatabase();
- SQLOK_ERRSTREAM(sqlite3_close(m_database), "Failed to close database");
+ str_to_sqlite(m_stmt_player_load, 1, player->getName());
+ if (sqlite3_step(m_stmt_player_load) != SQLITE_ROW) {
+ sqlite3_reset(m_stmt_player_load);
+ return false;
+ }
+ sao->setPitch(sqlite_to_float(m_stmt_player_load, 0));
+ sao->setYaw(sqlite_to_float(m_stmt_player_load, 1));
+ sao->setBasePosition(sqlite_to_v3f(m_stmt_player_load, 2));
+ sao->setHPRaw((s16) MYMIN(sqlite_to_int(m_stmt_player_load, 5), S16_MAX));
+ sao->setBreath((u16) MYMIN(sqlite_to_int(m_stmt_player_load, 6), U16_MAX), false);
+ sqlite3_reset(m_stmt_player_load);
+
+ // Load inventory
+ str_to_sqlite(m_stmt_player_load_inventory, 1, player->getName());
+ while (sqlite3_step(m_stmt_player_load_inventory) == SQLITE_ROW) {
+ InventoryList *invList = player->inventory.addList(
+ sqlite_to_string(m_stmt_player_load_inventory, 2),
+ sqlite_to_uint(m_stmt_player_load_inventory, 3));
+ invList->setWidth(sqlite_to_uint(m_stmt_player_load_inventory, 1));
+
+ u32 invId = sqlite_to_uint(m_stmt_player_load_inventory, 0);
+
+ str_to_sqlite(m_stmt_player_load_inventory_items, 1, player->getName());
+ int_to_sqlite(m_stmt_player_load_inventory_items, 2, invId);
+ while (sqlite3_step(m_stmt_player_load_inventory_items) == SQLITE_ROW) {
+ const std::string itemStr = sqlite_to_string(m_stmt_player_load_inventory_items, 1);
+ if (itemStr.length() > 0) {
+ ItemStack stack;
+ stack.deSerialize(itemStr);
+ invList->addItem(sqlite_to_uint(m_stmt_player_load_inventory_items, 0), stack);
+ }
+ }
+ sqlite3_reset(m_stmt_player_load_inventory_items);
+ }
+
+ sqlite3_reset(m_stmt_player_load_inventory);
+
+ str_to_sqlite(m_stmt_player_metadata_load, 1, sao->getPlayer()->getName());
+ while (sqlite3_step(m_stmt_player_metadata_load) == SQLITE_ROW) {
+ std::string attr = sqlite_to_string(m_stmt_player_metadata_load, 0);
+ std::string value = sqlite_to_string(m_stmt_player_metadata_load, 1);
+
+ sao->setExtendedAttribute(attr, value);
+ }
+ sqlite3_reset(m_stmt_player_metadata_load);
+ return true;
+}
+
+bool PlayerDatabaseSQLite3::removePlayer(const std::string &name)
+{
+ if (!playerDataExists(name))
+ return false;
+
+ str_to_sqlite(m_stmt_player_remove, 1, name);
+ sqlite3_vrfy(sqlite3_step(m_stmt_player_remove), SQLITE_DONE);
+ sqlite3_reset(m_stmt_player_remove);
+ return true;
}
+void PlayerDatabaseSQLite3::listPlayers(std::vector<std::string> &res)
+{
+ verifyDatabase();
+
+ while (sqlite3_step(m_stmt_player_list) == SQLITE_ROW)
+ res.push_back(sqlite_to_string(m_stmt_player_list, 0));
+
+ sqlite3_reset(m_stmt_player_list);
+}
diff --git a/src/database-sqlite3.h b/src/database-sqlite3.h
index 2ab4c8ee9..3244facc9 100644
--- a/src/database-sqlite3.h
+++ b/src/database-sqlite3.h
@@ -20,8 +20,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#ifndef DATABASE_SQLITE3_HEADER
#define DATABASE_SQLITE3_HEADER
+#include <cstring>
#include <string>
#include "database.h"
+#include "exceptions.h"
extern "C" {
#include "sqlite3.h"
@@ -30,37 +32,97 @@ extern "C" {
class Database_SQLite3 : public Database
{
public:
- Database_SQLite3(const std::string &savedir);
- ~Database_SQLite3();
+ virtual ~Database_SQLite3();
void beginSave();
void endSave();
- bool saveBlock(const v3s16 &pos, const std::string &data);
- void loadBlock(const v3s16 &pos, std::string *block);
- bool deleteBlock(const v3s16 &pos);
- void listAllLoadableBlocks(std::vector<v3s16> &dst);
bool initialized() const { return m_initialized; }
+protected:
+ Database_SQLite3(const std::string &savedir, const std::string &dbname);
-private:
- // Open the database
- void openDatabase();
- // Create the database structure
- void createDatabase();
// Open and initialize the database if needed
void verifyDatabase();
- void bindPos(sqlite3_stmt *stmt, const v3s16 &pos, int index = 1);
+ // Convertors
+ inline void str_to_sqlite(sqlite3_stmt *s, int iCol, const std::string &str) const
+ {
+ sqlite3_vrfy(sqlite3_bind_text(s, iCol, str.c_str(), str.size(), NULL));
+ }
+
+ inline void str_to_sqlite(sqlite3_stmt *s, int iCol, const char *str) const
+ {
+ sqlite3_vrfy(sqlite3_bind_text(s, iCol, str, strlen(str), NULL));
+ }
+
+ inline void int_to_sqlite(sqlite3_stmt *s, int iCol, int val) const
+ {
+ sqlite3_vrfy(sqlite3_bind_int(s, iCol, val));
+ }
+
+ inline void int64_to_sqlite(sqlite3_stmt *s, int iCol, s64 val) const
+ {
+ sqlite3_vrfy(sqlite3_bind_int64(s, iCol, (sqlite3_int64) val));
+ }
+
+ inline void double_to_sqlite(sqlite3_stmt *s, int iCol, double val) const
+ {
+ sqlite3_vrfy(sqlite3_bind_double(s, iCol, val));
+ }
+
+ inline std::string sqlite_to_string(sqlite3_stmt *s, int iCol)
+ {
+ const char* text = reinterpret_cast<const char*>(sqlite3_column_text(s, iCol));
+ return std::string(text ? text : "");
+ }
+
+ inline s32 sqlite_to_int(sqlite3_stmt *s, int iCol)
+ {
+ return sqlite3_column_int(s, iCol);
+ }
+
+ inline u32 sqlite_to_uint(sqlite3_stmt *s, int iCol)
+ {
+ return (u32) sqlite3_column_int(s, iCol);
+ }
+
+ inline float sqlite_to_float(sqlite3_stmt *s, int iCol)
+ {
+ return (float) sqlite3_column_double(s, iCol);
+ }
+
+ inline const v3f sqlite_to_v3f(sqlite3_stmt *s, int iCol)
+ {
+ return v3f(sqlite_to_float(s, iCol), sqlite_to_float(s, iCol + 1),
+ sqlite_to_float(s, iCol + 2));
+ }
+
+ // Query verifiers helpers
+ inline void sqlite3_vrfy(int s, const std::string &m = "", int r = SQLITE_OK) const
+ {
+ if (s != r)
+ throw DatabaseException(m + ": " + sqlite3_errmsg(m_database));
+ }
+
+ inline void sqlite3_vrfy(const int s, const int r, const std::string &m = "") const
+ {
+ sqlite3_vrfy(s, m, r);
+ }
+
+ // Create the database structure
+ virtual void createDatabase() = 0;
+ virtual void initStatements() = 0;
+
+ sqlite3 *m_database;
+private:
+ // Open the database
+ void openDatabase();
bool m_initialized;
std::string m_savedir;
+ std::string m_dbname;
- sqlite3 *m_database;
- sqlite3_stmt *m_stmt_read;
- sqlite3_stmt *m_stmt_write;
- sqlite3_stmt *m_stmt_list;
- sqlite3_stmt *m_stmt_delete;
sqlite3_stmt *m_stmt_begin;
sqlite3_stmt *m_stmt_end;
@@ -69,4 +131,66 @@ private:
static int busyHandler(void *data, int count);
};
+class MapDatabaseSQLite3 : private Database_SQLite3, public MapDatabase
+{
+public:
+ MapDatabaseSQLite3(const std::string &savedir);
+ virtual ~MapDatabaseSQLite3();
+
+ bool saveBlock(const v3s16 &pos, const std::string &data);
+ void loadBlock(const v3s16 &pos, std::string *block);
+ bool deleteBlock(const v3s16 &pos);
+ void listAllLoadableBlocks(std::vector<v3s16> &dst);
+
+ void beginSave() { Database_SQLite3::beginSave(); }
+ void endSave() { Database_SQLite3::endSave(); }
+protected:
+ virtual void createDatabase();
+ virtual void initStatements();
+
+private:
+ void bindPos(sqlite3_stmt *stmt, const v3s16 &pos, int index = 1);
+
+ // Map
+ sqlite3_stmt *m_stmt_read;
+ sqlite3_stmt *m_stmt_write;
+ sqlite3_stmt *m_stmt_list;
+ sqlite3_stmt *m_stmt_delete;
+};
+
+class PlayerDatabaseSQLite3 : private Database_SQLite3, public PlayerDatabase
+{
+public:
+ PlayerDatabaseSQLite3(const std::string &savedir);
+ virtual ~PlayerDatabaseSQLite3();
+
+ void savePlayer(RemotePlayer *player);
+ bool loadPlayer(RemotePlayer *player, PlayerSAO *sao);
+ bool removePlayer(const std::string &name);
+ void listPlayers(std::vector<std::string> &res);
+
+protected:
+ virtual void createDatabase();
+ virtual void initStatements();
+
+private:
+ bool playerDataExists(const std::string &name);
+
+ // Players
+ sqlite3_stmt *m_stmt_player_load;
+ sqlite3_stmt *m_stmt_player_add;
+ sqlite3_stmt *m_stmt_player_update;
+ sqlite3_stmt *m_stmt_player_remove;
+ sqlite3_stmt *m_stmt_player_list;
+ sqlite3_stmt *m_stmt_player_load_inventory;
+ sqlite3_stmt *m_stmt_player_load_inventory_items;
+ sqlite3_stmt *m_stmt_player_add_inventory;
+ sqlite3_stmt *m_stmt_player_add_inventory_items;
+ sqlite3_stmt *m_stmt_player_remove_inventory;
+ sqlite3_stmt *m_stmt_player_remove_inventory_items;
+ sqlite3_stmt *m_stmt_player_metadata_load;
+ sqlite3_stmt *m_stmt_player_metadata_remove;
+ sqlite3_stmt *m_stmt_player_metadata_add;
+};
+
#endif
diff --git a/src/database.cpp b/src/database.cpp
index 262d475ec..8e1483893 100644
--- a/src/database.cpp
+++ b/src/database.cpp
@@ -48,7 +48,7 @@ static inline s64 pythonmodulo(s64 i, s16 mod)
}
-s64 Database::getBlockAsInteger(const v3s16 &pos)
+s64 MapDatabase::getBlockAsInteger(const v3s16 &pos)
{
return (u64) pos.Z * 0x1000000 +
(u64) pos.Y * 0x1000 +
@@ -56,7 +56,7 @@ s64 Database::getBlockAsInteger(const v3s16 &pos)
}
-v3s16 Database::getIntegerAsBlock(s64 i)
+v3s16 MapDatabase::getIntegerAsBlock(s64 i)
{
v3s16 pos;
pos.X = unsigned_to_signed(pythonmodulo(i, 4096), 2048);
diff --git a/src/database.h b/src/database.h
index 7213f088a..5a2b844fd 100644
--- a/src/database.h
+++ b/src/database.h
@@ -29,10 +29,15 @@ with this program; if not, write to the Free Software Foundation, Inc.,
class Database
{
public:
- virtual ~Database() {}
+ virtual void beginSave() = 0;
+ virtual void endSave() = 0;
+ virtual bool initialized() const { return true; }
+};
- virtual void beginSave() {}
- virtual void endSave() {}
+class MapDatabase : public Database
+{
+public:
+ virtual ~MapDatabase() {}
virtual bool saveBlock(const v3s16 &pos, const std::string &data) = 0;
virtual void loadBlock(const v3s16 &pos, std::string *block) = 0;
@@ -42,8 +47,19 @@ public:
static v3s16 getIntegerAsBlock(s64 i);
virtual void listAllLoadableBlocks(std::vector<v3s16> &dst) = 0;
+};
- virtual bool initialized() const { return true; }
+class PlayerSAO;
+class RemotePlayer;
+
+class PlayerDatabase
+{
+public:
+ virtual ~PlayerDatabase() {}
+ virtual void savePlayer(RemotePlayer *player) = 0;
+ virtual bool loadPlayer(RemotePlayer *player, PlayerSAO *sao) = 0;
+ virtual bool removePlayer(const std::string &name) = 0;
+ virtual void listPlayers(std::vector<std::string> &res) = 0;
};
#endif
diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp
index 573b5e2d8..181a12b5e 100644
--- a/src/defaultsettings.cpp
+++ b/src/defaultsettings.cpp
@@ -57,6 +57,7 @@ void set_default_settings(Settings *settings)
settings->setDefault("curl_verify_cert", "true");
settings->setDefault("enable_remote_media_server", "true");
settings->setDefault("enable_client_modding", "false");
+ settings->setDefault("max_out_chat_queue_size", "20");
// Keymap
settings->setDefault("remote_port", "30000");
@@ -80,6 +81,11 @@ void set_default_settings(Settings *settings)
settings->setDefault("keymap_freemove", "KEY_KEY_K");
settings->setDefault("keymap_fastmove", "KEY_KEY_J");
settings->setDefault("keymap_noclip", "KEY_KEY_H");
+ settings->setDefault("keymap_hotbar_next", "KEY_KEY_N");
+ settings->setDefault("keymap_hotbar_previous", "KEY_KEY_B");
+ settings->setDefault("keymap_mute", "KEY_KEY_M");
+ settings->setDefault("keymap_increase_volume", "");
+ settings->setDefault("keymap_decrease_volume", "");
settings->setDefault("keymap_cinematic", "");
settings->setDefault("keymap_toggle_hud", "KEY_F1");
settings->setDefault("keymap_toggle_chat", "KEY_F2");
@@ -119,8 +125,10 @@ void set_default_settings(Settings *settings)
settings->setDefault("fps_max", "60");
settings->setDefault("pause_fps_max", "20");
settings->setDefault("viewing_range", "100");
+ 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");
settings->setDefault("vsync", "false");
@@ -171,7 +179,6 @@ void set_default_settings(Settings *settings)
// Effects
settings->setDefault("directional_colored_fog", "true");
- settings->setDefault("view_bobbing", "true");
settings->setDefault("inventory_items_animations", "false");
settings->setDefault("mip_map", "false");
settings->setDefault("anisotropic_filter", "false");
@@ -246,6 +253,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");
diff --git a/src/defaultsettings.h b/src/defaultsettings.h
index 20274a003..21c51396f 100644
--- a/src/defaultsettings.h
+++ b/src/defaultsettings.h
@@ -36,4 +36,3 @@ void set_default_settings(Settings *settings);
void override_default_settings(Settings *settings, Settings *from);
#endif
-
diff --git a/src/drawscene.cpp b/src/drawscene.cpp
index 7d2d1d12f..59f9b8375 100644
--- a/src/drawscene.cpp
+++ b/src/drawscene.cpp
@@ -509,7 +509,7 @@ void draw_plain(Camera &camera, bool show_hud,
void draw_scene(video::IVideoDriver *driver, scene::ISceneManager *smgr,
Camera &camera, Client &client, LocalPlayer *player, Hud &hud,
- Minimap &mapper, gui::IGUIEnvironment *guienv,
+ Minimap *mapper, gui::IGUIEnvironment *guienv,
const v2u32 &screensize, const video::SColor &skycolor,
bool show_hud, bool show_minimap)
{
@@ -584,8 +584,8 @@ void draw_scene(video::IVideoDriver *driver, scene::ISceneManager *smgr,
hud.drawLuaElements(camera.getOffset());
camera.drawNametags();
- if (show_minimap)
- mapper.drawMinimap();
+ if (mapper && show_minimap)
+ mapper->drawMinimap();
}
guienv->drawAll();
diff --git a/src/drawscene.h b/src/drawscene.h
index 4a71b1f4e..99ff1a6bc 100644
--- a/src/drawscene.h
+++ b/src/drawscene.h
@@ -32,7 +32,7 @@ void draw_load_screen(const std::wstring &text, IrrlichtDevice *device,
void draw_scene(video::IVideoDriver *driver, scene::ISceneManager *smgr,
Camera &camera, Client &client, LocalPlayer *player,
- Hud &hud, Minimap &mapper, gui::IGUIEnvironment *guienv,
+ Hud &hud, Minimap *mapper, gui::IGUIEnvironment *guienv,
const v2u32 &screensize, const video::SColor &skycolor,
bool show_hud, bool show_minimap);
diff --git a/src/dungeongen.cpp b/src/dungeongen.cpp
index 6cef3f88d..f8f3df56f 100644
--- a/src/dungeongen.cpp
+++ b/src/dungeongen.cpp
@@ -431,8 +431,10 @@ void DungeonGen::makeCorridor(v3s16 doorplace, v3s16 doordir,
VMANIP_FLAG_DUNGEON_UNTOUCHABLE,
MapNode(dp.c_wall),
0);
- makeHole(p);
- makeHole(p - dir);
+ makeFill(p, dp.holesize, VMANIP_FLAG_DUNGEON_UNTOUCHABLE,
+ MapNode(CONTENT_AIR), VMANIP_FLAG_DUNGEON_INSIDE);
+ makeFill(p - dir, dp.holesize, VMANIP_FLAG_DUNGEON_UNTOUCHABLE,
+ MapNode(CONTENT_AIR), VMANIP_FLAG_DUNGEON_INSIDE);
// TODO: fix stairs code so it works 100%
// (quite difficult)
@@ -451,16 +453,21 @@ void DungeonGen::makeCorridor(v3s16 doorplace, v3s16 doordir,
v3s16 swv = (dir.Z != 0) ? v3s16(1, 0, 0) : v3s16(0, 0, 1);
for (u16 st = 0; st < stair_width; st++) {
- u32 vi = vm->m_area.index(ps.X - dir.X, ps.Y - 1, ps.Z - dir.Z);
- if (vm->m_area.contains(ps + v3s16(-dir.X, -1, -dir.Z)) &&
- vm->m_data[vi].getContent() == dp.c_wall)
- vm->m_data[vi] = MapNode(dp.c_stair, 0, facedir);
-
- vi = vm->m_area.index(ps.X, ps.Y, ps.Z);
- if (vm->m_area.contains(ps) &&
- vm->m_data[vi].getContent() == dp.c_wall)
- vm->m_data[vi] = MapNode(dp.c_stair, 0, facedir);
-
+ if (make_stairs == -1) {
+ u32 vi = vm->m_area.index(ps.X - dir.X, ps.Y - 1, ps.Z - dir.Z);
+ if (vm->m_area.contains(ps + v3s16(-dir.X, -1, -dir.Z)) &&
+ vm->m_data[vi].getContent() == dp.c_wall) {
+ vm->m_flags[vi] |= VMANIP_FLAG_DUNGEON_UNTOUCHABLE;
+ vm->m_data[vi] = MapNode(dp.c_stair, 0, facedir);
+ }
+ } else if (make_stairs == 1) {
+ u32 vi = vm->m_area.index(ps.X, ps.Y - 1, ps.Z);
+ if (vm->m_area.contains(ps + v3s16(0, -1, 0)) &&
+ vm->m_data[vi].getContent() == dp.c_wall) {
+ vm->m_flags[vi] |= VMANIP_FLAG_DUNGEON_UNTOUCHABLE;
+ vm->m_data[vi] = MapNode(dp.c_stair, 0, facedir);
+ }
+ }
ps += swv;
}
}
diff --git a/src/emerge.cpp b/src/emerge.cpp
index 4c3a83f7e..f7f6ff603 100644
--- a/src/emerge.cpp
+++ b/src/emerge.cpp
@@ -40,7 +40,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "mg_schematic.h"
#include "nodedef.h"
#include "profiler.h"
-#include "serverscripting.h"
+#include "scripting_server.h"
#include "server.h"
#include "serverobject.h"
#include "settings.h"
@@ -570,6 +570,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));
/*
diff --git a/src/environment.cpp b/src/environment.cpp
index 9c2ea8896..4e782db81 100644
--- a/src/environment.cpp
+++ b/src/environment.cpp
@@ -21,7 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "environment.h"
#include "collision.h"
#include "serverobject.h"
-#include "serverscripting.h"
+#include "scripting_server.h"
#include "server.h"
#include "daynightratio.h"
#include "emerge.h"
@@ -70,7 +70,7 @@ void Environment::setTimeOfDay(u32 time)
{
MutexAutoLock lock(this->m_time_lock);
if (m_time_of_day > time)
- m_day_count++;
+ ++m_day_count;
m_time_of_day = time;
m_time_of_day_f = (float)time / 24000.0;
}
@@ -103,7 +103,7 @@ void Environment::stepTimeOfDay(float dtime)
// Sync at overflow
if (m_time_of_day + units >= 24000) {
sync_f = true;
- m_day_count++;
+ ++m_day_count;
}
m_time_of_day = (m_time_of_day + units) % 24000;
if (sync_f)
diff --git a/src/environment.h b/src/environment.h
index 52f369817..1de13e9ed 100644
--- a/src/environment.h
+++ b/src/environment.h
@@ -58,7 +58,7 @@ public:
*/
virtual void step(f32 dtime) = 0;
- virtual Map & getMap() = 0;
+ virtual Map &getMap() = 0;
u32 getDayNightRatio();
@@ -78,7 +78,7 @@ public:
// counter used internally when triggering ABMs
u32 m_added_objects;
- IGameDef* getGameDef() { return m_gamedef; }
+ IGameDef *getGameDef() { return m_gamedef; }
protected:
GenericAtomic<float> m_time_of_day_speed;
@@ -117,6 +117,7 @@ protected:
float m_cache_nodetimer_interval;
IGameDef *m_gamedef;
+
private:
Mutex m_time_lock;
@@ -124,4 +125,3 @@ private:
};
#endif
-
diff --git a/src/exceptions.h b/src/exceptions.h
index 67a2d0df6..1b39c6725 100644
--- a/src/exceptions.h
+++ b/src/exceptions.h
@@ -27,10 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
class BaseException : public std::exception
{
public:
- BaseException(const std::string &s) throw()
- {
- m_s = s;
- }
+ BaseException(const std::string &s) throw(): m_s(s) {}
~BaseException() throw() {}
virtual const char * what() const throw()
{
@@ -122,12 +119,12 @@ public:
class ClientStateError : public BaseException {
public:
- ClientStateError(std::string s): BaseException(s) {}
+ ClientStateError(const std::string &s): BaseException(s) {}
};
class PrngException : public BaseException {
public:
- PrngException(std::string s): BaseException(s) {}
+ PrngException(const std::string &s): BaseException(s) {}
};
class ModError : public BaseException {
diff --git a/src/face_position_cache.cpp b/src/face_position_cache.cpp
new file mode 100644
index 000000000..f57e75da9
--- /dev/null
+++ b/src/face_position_cache.cpp
@@ -0,0 +1,110 @@
+/*
+Minetest
+Copyright (C) 2015 Nerzhul, Loic Blot <loic.blot@unix-experience.fr>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "face_position_cache.h"
+#include "threading/mutex_auto_lock.h"
+
+
+UNORDERED_MAP<u16, std::vector<v3s16> > FacePositionCache::cache;
+Mutex FacePositionCache::cache_mutex;
+
+// Calculate the borders of a "d-radius" cube
+const std::vector<v3s16> &FacePositionCache::getFacePositions(u16 d)
+{
+ MutexAutoLock lock(cache_mutex);
+ UNORDERED_MAP<u16, std::vector<v3s16> >::iterator it = cache.find(d);
+ if (it != cache.end())
+ return it->second;
+
+ return generateFacePosition(d);
+}
+
+const std::vector<v3s16> &FacePositionCache::generateFacePosition(u16 d)
+{
+ cache[d] = std::vector<v3s16>();
+ std::vector<v3s16> &c = cache[d];
+ if (d == 0) {
+ c.push_back(v3s16(0,0,0));
+ return c;
+ }
+ if (d == 1) {
+ // This is an optimized sequence of coordinates.
+ c.push_back(v3s16( 0, 1, 0)); // Top
+ c.push_back(v3s16( 0, 0, 1)); // Back
+ c.push_back(v3s16(-1, 0, 0)); // Left
+ c.push_back(v3s16( 1, 0, 0)); // Right
+ c.push_back(v3s16( 0, 0,-1)); // Front
+ c.push_back(v3s16( 0,-1, 0)); // Bottom
+ // 6
+ c.push_back(v3s16(-1, 0, 1)); // Back left
+ c.push_back(v3s16( 1, 0, 1)); // Back right
+ c.push_back(v3s16(-1, 0,-1)); // Front left
+ c.push_back(v3s16( 1, 0,-1)); // Front right
+ c.push_back(v3s16(-1,-1, 0)); // Bottom left
+ c.push_back(v3s16( 1,-1, 0)); // Bottom right
+ c.push_back(v3s16( 0,-1, 1)); // Bottom back
+ c.push_back(v3s16( 0,-1,-1)); // Bottom front
+ c.push_back(v3s16(-1, 1, 0)); // Top left
+ c.push_back(v3s16( 1, 1, 0)); // Top right
+ c.push_back(v3s16( 0, 1, 1)); // Top back
+ c.push_back(v3s16( 0, 1,-1)); // Top front
+ // 18
+ c.push_back(v3s16(-1, 1, 1)); // Top back-left
+ c.push_back(v3s16( 1, 1, 1)); // Top back-right
+ c.push_back(v3s16(-1, 1,-1)); // Top front-left
+ c.push_back(v3s16( 1, 1,-1)); // Top front-right
+ c.push_back(v3s16(-1,-1, 1)); // Bottom back-left
+ c.push_back(v3s16( 1,-1, 1)); // Bottom back-right
+ c.push_back(v3s16(-1,-1,-1)); // Bottom front-left
+ c.push_back(v3s16( 1,-1,-1)); // Bottom front-right
+ // 26
+ return c;
+ }
+
+ // Take blocks in all sides, starting from y=0 and going +-y
+ for (s16 y = 0; y <= d - 1; y++) {
+ // Left and right side, including borders
+ for (s16 z =- d; z <= d; z++) {
+ c.push_back(v3s16( d, y, z));
+ c.push_back(v3s16(-d, y, z));
+ if (y != 0) {
+ c.push_back(v3s16( d, -y, z));
+ c.push_back(v3s16(-d, -y, z));
+ }
+ }
+ // Back and front side, excluding borders
+ for (s16 x = -d + 1; x <= d - 1; x++) {
+ c.push_back(v3s16(x, y, d));
+ c.push_back(v3s16(x, y, -d));
+ if (y != 0) {
+ c.push_back(v3s16(x, -y, d));
+ c.push_back(v3s16(x, -y, -d));
+ }
+ }
+ }
+
+ // Take the bottom and top face with borders
+ // -d < x < d, y = +-d, -d < z < d
+ for (s16 x = -d; x <= d; x++)
+ for (s16 z = -d; z <= d; z++) {
+ c.push_back(v3s16(x, -d, z));
+ c.push_back(v3s16(x, d, z));
+ }
+ return c;
+}
diff --git a/src/face_position_cache.h b/src/face_position_cache.h
new file mode 100644
index 000000000..c1d2841c4
--- /dev/null
+++ b/src/face_position_cache.h
@@ -0,0 +1,44 @@
+/*
+Minetest
+Copyright (C) 2015 Nerzhul, Loic Blot <loic.blot@unix-experience.fr>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef FACE_POSITION_CACHE_HEADER
+#define FACE_POSITION_CACHE_HEADER
+
+#include "irr_v3d.h"
+#include "threading/mutex.h"
+#include "util/cpp11_container.h"
+
+#include <map>
+#include <vector>
+
+/*
+ * This class permits caching getFacePosition call results.
+ * This reduces CPU usage and vector calls.
+ */
+class FacePositionCache {
+public:
+ static const std::vector<v3s16> &getFacePositions(u16 d);
+
+private:
+ static const std::vector<v3s16> &generateFacePosition(u16 d);
+ static UNORDERED_MAP<u16, std::vector<v3s16> > cache;
+ static Mutex cache_mutex;
+};
+
+#endif
diff --git a/src/filecache.h b/src/filecache.h
index f390f71b7..627ab45ed 100644
--- a/src/filecache.h
+++ b/src/filecache.h
@@ -30,7 +30,7 @@ public:
/*
'dir' is the file cache directory to use.
*/
- FileCache(std::string dir) : m_dir(dir) {}
+ FileCache(const std::string &dir) : m_dir(dir) {}
bool update(const std::string &name, const std::string &data);
bool load(const std::string &name, std::ostream &os);
diff --git a/src/fontengine.cpp b/src/fontengine.cpp
index da327c3f6..8eaf53c9f 100644
--- a/src/fontengine.cpp
+++ b/src/fontengine.cpp
@@ -341,32 +341,70 @@ void FontEngine::initFont(unsigned int basesize, FontMode mode)
font_path.c_str(), size, true, true, font_shadow,
font_shadow_alpha);
- if (font != NULL) {
+ if (font) {
m_font_cache[mode][basesize] = font;
return;
}
- // try fallback font
- errorstream << "FontEngine: failed to load: " << font_path << ", trying to fall back "
- "to fallback font" << std::endl;
+ if (font_config_prefix == "mono_") {
+ const std::string &mono_font_path = m_settings->getDefault("mono_font_path");
- font_path = g_settings->get(font_config_prefix + "fallback_font_path");
+ if (font_path != mono_font_path) {
+ // try original mono font
+ errorstream << "FontEngine: failed to load custom mono "
+ "font: " << font_path << ", trying to fall back to "
+ "original mono font" << std::endl;
- font = gui::CGUITTFont::createTTFont(m_env,
- font_path.c_str(), size, true, true, font_shadow,
- font_shadow_alpha);
+ font = gui::CGUITTFont::createTTFont(m_env,
+ mono_font_path.c_str(), size, true, true,
+ font_shadow, font_shadow_alpha);
- if (font != NULL) {
- m_font_cache[mode][basesize] = font;
- return;
+ if (font) {
+ m_font_cache[mode][basesize] = font;
+ return;
+ }
+ }
+ } else {
+ // try fallback font
+ errorstream << "FontEngine: failed to load: " << font_path <<
+ ", trying to fall back to fallback font" << std::endl;
+
+ font_path = g_settings->get(font_config_prefix + "fallback_font_path");
+
+ font = gui::CGUITTFont::createTTFont(m_env,
+ font_path.c_str(), size, true, true, font_shadow,
+ font_shadow_alpha);
+
+ if (font) {
+ m_font_cache[mode][basesize] = font;
+ return;
+ }
+
+ const std::string &fallback_font_path = m_settings->getDefault("fallback_font_path");
+
+ if (font_path != fallback_font_path) {
+ // try original fallback font
+ errorstream << "FontEngine: failed to load custom fallback "
+ "font: " << font_path << ", trying to fall back to "
+ "original fallback font" << std::endl;
+
+ font = gui::CGUITTFont::createTTFont(m_env,
+ fallback_font_path.c_str(), size, true, true,
+ font_shadow, font_shadow_alpha);
+
+ if (font) {
+ m_font_cache[mode][basesize] = font;
+ return;
+ }
+ }
}
// give up
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
@@ -468,7 +506,7 @@ void FontEngine::initSimpleFont(unsigned int basesize, FontMode mode)
}
}
- if (font != NULL) {
+ if (font) {
font->grab();
m_font_cache[mode][basesize] = font;
}
diff --git a/src/game.cpp b/src/game.cpp
index f584a58ef..5ad93b95a 100644
--- a/src/game.cpp
+++ b/src/game.cpp
@@ -22,6 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <iomanip>
#include "camera.h"
#include "client.h"
+#include "client/inputhandler.h"
#include "client/tile.h" // For TextureSource
#include "client/keys.h"
#include "client/joystick_controller.h"
@@ -50,28 +51,20 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "quicktune_shortcutter.h"
#include "server.h"
#include "settings.h"
-#include "shader.h" // For ShaderSource
#include "sky.h"
#include "subgame.h"
#include "tool.h"
+#include "util/basic_macros.h"
#include "util/directiontables.h"
#include "util/pointedthing.h"
#include "irrlicht_changes/static_text.h"
#include "version.h"
-#include "minimap.h"
-#include "mapblock_mesh.h"
-#include "script/clientscripting.h"
-
-#include "sound.h"
+#include "script/scripting_client.h"
#if USE_SOUND
#include "sound_openal.h"
#endif
-#ifdef HAVE_TOUCHSCREENGUI
- #include "touchscreengui.h"
-#endif
-
extern Settings *g_settings;
extern Profiler *g_profiler;
@@ -79,14 +72,15 @@ extern Profiler *g_profiler;
Text input system
*/
-struct TextDestNodeMetadata : public TextDest {
+struct TextDestNodeMetadata : public TextDest
+{
TextDestNodeMetadata(v3s16 p, Client *client)
{
m_p = p;
m_client = client;
}
// This is deprecated I guess? -celeron55
- void gotText(std::wstring text)
+ void gotText(const std::wstring &text)
{
std::string ntext = wide_to_utf8(text);
infostream << "Submitting 'text' field of node at (" << m_p.X << ","
@@ -104,13 +98,14 @@ struct TextDestNodeMetadata : public TextDest {
Client *m_client;
};
-struct TextDestPlayerInventory : public TextDest {
+struct TextDestPlayerInventory : public TextDest
+{
TextDestPlayerInventory(Client *client)
{
m_client = client;
m_formname = "";
}
- TextDestPlayerInventory(Client *client, std::string formname)
+ TextDestPlayerInventory(Client *client, const std::string &formname)
{
m_client = client;
m_formname = formname;
@@ -125,23 +120,18 @@ struct TextDestPlayerInventory : public TextDest {
struct LocalFormspecHandler : public TextDest
{
- LocalFormspecHandler(std::string formname):
- m_client(0)
+ LocalFormspecHandler(const std::string &formname):
+ m_client(NULL)
{
m_formname = formname;
}
- LocalFormspecHandler(std::string formname, Client *client):
+ LocalFormspecHandler(const std::string &formname, Client *client):
m_client(client)
{
m_formname = formname;
}
- void gotText(std::wstring message)
- {
- errorstream << "LocalFormspecHandler::gotText old style message received" << std::endl;
- }
-
void gotText(const StringMap &fields)
{
if (m_formname == "MT_PAUSE_MENU") {
@@ -180,7 +170,8 @@ struct LocalFormspecHandler : public TextDest
}
// Don't disable this part when modding is disabled, it's used in builtin
- m_client->getScript()->on_formspec_input(m_formname, fields);
+ if (m_client && m_client->getScript())
+ m_client->getScript()->on_formspec_input(m_formname, fields);
}
Client *m_client;
@@ -205,7 +196,8 @@ public:
return meta->getString("formspec");
}
- std::string resolveText(std::string str)
+
+ virtual std::string resolveText(const std::string &str)
{
NodeMetadata *meta = m_map->getNodeMetadata(m_p);
@@ -478,6 +470,7 @@ class SoundMaker
ISoundManager *m_sound;
INodeDefManager *m_ndef;
public:
+ bool makes_footstep_sound;
float m_player_step_timer;
SimpleSoundSpec m_player_step_sound;
@@ -487,6 +480,7 @@ public:
SoundMaker(ISoundManager *sound, INodeDefManager *ndef):
m_sound(sound),
m_ndef(ndef),
+ makes_footstep_sound(true),
m_player_step_timer(0)
{
}
@@ -495,7 +489,8 @@ public:
{
if (m_player_step_timer <= 0 && m_player_step_sound.exists()) {
m_player_step_timer = 0.03;
- m_sound->playSound(m_player_step_sound, false);
+ if (makes_footstep_sound)
+ m_sound->playSound(m_player_step_sound, false);
}
}
@@ -569,27 +564,35 @@ public:
class GameOnDemandSoundFetcher: public OnDemandSoundFetcher
{
std::set<std::string> m_fetched;
+private:
+ void paths_insert(std::set<std::string> &dst_paths,
+ const std::string &base,
+ const std::string &name)
+ {
+ dst_paths.insert(base + DIR_DELIM + "sounds" + DIR_DELIM + name + ".ogg");
+ dst_paths.insert(base + DIR_DELIM + "sounds" + DIR_DELIM + name + ".0.ogg");
+ dst_paths.insert(base + DIR_DELIM + "sounds" + DIR_DELIM + name + ".1.ogg");
+ dst_paths.insert(base + DIR_DELIM + "sounds" + DIR_DELIM + name + ".2.ogg");
+ dst_paths.insert(base + DIR_DELIM + "sounds" + DIR_DELIM + name + ".3.ogg");
+ dst_paths.insert(base + DIR_DELIM + "sounds" + DIR_DELIM + name + ".4.ogg");
+ dst_paths.insert(base + DIR_DELIM + "sounds" + DIR_DELIM + name + ".5.ogg");
+ dst_paths.insert(base + DIR_DELIM + "sounds" + DIR_DELIM + name + ".6.ogg");
+ dst_paths.insert(base + DIR_DELIM + "sounds" + DIR_DELIM + name + ".7.ogg");
+ dst_paths.insert(base + DIR_DELIM + "sounds" + DIR_DELIM + name + ".8.ogg");
+ dst_paths.insert(base + DIR_DELIM + "sounds" + DIR_DELIM + name + ".9.ogg");
+ }
public:
void fetchSounds(const std::string &name,
- std::set<std::string> &dst_paths,
- std::set<std::string> &dst_datas)
+ std::set<std::string> &dst_paths,
+ std::set<std::string> &dst_datas)
{
if (m_fetched.count(name))
return;
m_fetched.insert(name);
- std::string base = porting::path_share + DIR_DELIM + "sounds";
- dst_paths.insert(base + DIR_DELIM + name + ".ogg");
- dst_paths.insert(base + DIR_DELIM + name + ".0.ogg");
- dst_paths.insert(base + DIR_DELIM + name + ".1.ogg");
- dst_paths.insert(base + DIR_DELIM + name + ".2.ogg");
- dst_paths.insert(base + DIR_DELIM + name + ".3.ogg");
- dst_paths.insert(base + DIR_DELIM + name + ".4.ogg");
- dst_paths.insert(base + DIR_DELIM + name + ".5.ogg");
- dst_paths.insert(base + DIR_DELIM + name + ".6.ogg");
- dst_paths.insert(base + DIR_DELIM + name + ".7.ogg");
- dst_paths.insert(base + DIR_DELIM + name + ".8.ogg");
- dst_paths.insert(base + DIR_DELIM + name + ".9.ogg");
+
+ paths_insert(dst_paths, porting::path_share, name);
+ paths_insert(dst_paths, porting::path_user, name);
}
};
@@ -713,16 +716,19 @@ public:
m_eye_position_pixel.set(eye_position_array, services);
m_eye_position_vertex.set(eye_position_array, services);
- float minimap_yaw_array[3];
- v3f minimap_yaw = m_client->getMinimap()->getYawVec();
+ if (m_client->getMinimap()) {
+ float minimap_yaw_array[3];
+ v3f minimap_yaw = m_client->getMinimap()->getYawVec();
#if (IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 8)
- minimap_yaw_array[0] = minimap_yaw.X;
- minimap_yaw_array[1] = minimap_yaw.Y;
- minimap_yaw_array[2] = minimap_yaw.Z;
+ minimap_yaw_array[0] = minimap_yaw.X;
+ minimap_yaw_array[1] = minimap_yaw.Y;
+ minimap_yaw_array[2] = minimap_yaw.Z;
#else
- minimap_yaw.getAs3Values(minimap_yaw_array);
+ minimap_yaw.getAs3Values(minimap_yaw_array);
#endif
- m_minimap_yaw.set(minimap_yaw_array, services);
+ m_minimap_yaw.set(minimap_yaw_array, services);
+
+ }
SamplerLayer_t base_tex = 0,
normal_tex = 1,
@@ -769,8 +775,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();
@@ -813,11 +819,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))) {
@@ -829,8 +837,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)) {
@@ -843,7 +851,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),
@@ -854,8 +862,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);
@@ -864,6 +872,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);
@@ -1040,6 +1070,11 @@ void KeyCache::populate()
key[KeyType::FREEMOVE] = getKeySetting("keymap_freemove");
key[KeyType::FASTMOVE] = getKeySetting("keymap_fastmove");
key[KeyType::NOCLIP] = getKeySetting("keymap_noclip");
+ key[KeyType::HOTBAR_PREV] = getKeySetting("keymap_hotbar_previous");
+ key[KeyType::HOTBAR_NEXT] = getKeySetting("keymap_hotbar_next");
+ key[KeyType::MUTE] = getKeySetting("keymap_mute");
+ key[KeyType::INC_VOLUME] = getKeySetting("keymap_increase_volume");
+ key[KeyType::DEC_VOLUME] = getKeySetting("keymap_decrease_volume");
key[KeyType::CINEMATIC] = getKeySetting("keymap_cinematic");
key[KeyType::SCREENSHOT] = getKeySetting("keymap_screenshot");
key[KeyType::TOGGLE_HUD] = getKeySetting("keymap_toggle_hud");
@@ -1111,6 +1146,7 @@ struct GameRunData {
PointedThing pointed_old;
bool digging;
bool ldown_for_dig;
+ bool dig_instantly;
bool left_punch;
bool update_wielded_item_trigger;
bool reset_jump_timer;
@@ -1190,7 +1226,7 @@ protected:
u16 port,
const SubgameSpec &gamespec);
bool initSound();
- bool createSingleplayerServer(const std::string map_dir,
+ bool createSingleplayerServer(const std::string &map_dir,
const SubgameSpec &gamespec, u16 port, std::string *address);
// Client creation
@@ -1266,8 +1302,9 @@ protected:
const core::line3d<f32> &shootline, bool liquids_pointable,
bool look_for_object, const v3s16 &camera_offset);
void handlePointingAtNothing(const ItemStack &playerItem);
- void handlePointingAtNode(const PointedThing &pointed, const ItemDefinition &playeritem_def,
- const ToolCapabilities &playeritem_toolcap, f32 dtime);
+ void handlePointingAtNode(const PointedThing &pointed,
+ const ItemDefinition &playeritem_def, const ItemStack &playeritem,
+ const ToolCapabilities &playeritem_toolcap, f32 dtime);
void handlePointingAtObject(const PointedThing &pointed, const ItemStack &playeritem,
const v3f &player_position, bool show_debug);
void handleDigging(const PointedThing &pointed, const v3s16 &nodepos,
@@ -1622,10 +1659,26 @@ void Game::run()
&& client->checkPrivilege("fast");
#endif
+ irr::core::dimension2d<u32> previous_screen_size(g_settings->getU16("screenW"),
+ g_settings->getU16("screenH"));
+
while (device->run()
&& !(*kill || g_gamecallback->shutdown_requested
|| (server && server->getShutdownRequested()))) {
+ const irr::core::dimension2d<u32> &current_screen_size =
+ device->getVideoDriver()->getScreenSize();
+ // Verify if window size has changed and save it if it's the case
+ // Ensure evaluating settings->getBool after verifying screensize
+ // First condition is cheaper
+ if (previous_screen_size != current_screen_size &&
+ current_screen_size != irr::core::dimension2d<u32>(0,0) &&
+ g_settings->getBool("autosave_screensize")) {
+ g_settings->setU16("screenW", current_screen_size.Width);
+ g_settings->setU16("screenH", current_screen_size.Height);
+ previous_screen_size = current_screen_size;
+ }
+
/* Must be called immediately after a device->run() call because it
* uses device->getTimer()->getTime()
*/
@@ -1674,6 +1727,8 @@ void Game::shutdown()
driver->setRenderTarget(irr::video::ERT_STEREO_BOTH_BUFFERS);
}
#endif
+ if (current_formspec)
+ current_formspec->quitMenu();
showOverlayMessage(wgettext("Shutting down..."), 0, 0, false);
@@ -1780,7 +1835,7 @@ bool Game::initSound()
return true;
}
-bool Game::createSingleplayerServer(const std::string map_dir,
+bool Game::createSingleplayerServer(const std::string &map_dir,
const SubgameSpec &gamespec, u16 port, std::string *address)
{
showOverlayMessage(wgettext("Creating server..."), 0, 5);
@@ -1924,7 +1979,8 @@ bool Game::createClient(const std::string &playername,
}
mapper = client->getMinimap();
- mapper->setMinimapMode(MINIMAP_MODE_OFF);
+ if (mapper)
+ mapper->setMinimapMode(MINIMAP_MODE_OFF);
return true;
}
@@ -2037,7 +2093,7 @@ bool Game::connectToServer(const std::string &playername,
}
client = new Client(device,
- playername.c_str(), password,
+ playername.c_str(), password, *address,
*draw_control, texture_src, shader_src,
itemdef_manager, nodedef_manager, sound, eventmgr,
connect_address.isIPv6(), &flags);
@@ -2049,7 +2105,7 @@ bool Game::connectToServer(const std::string &playername,
connect_address.print(&infostream);
infostream << std::endl;
- client->connect(connect_address, *address,
+ client->connect(connect_address,
simple_singleplayer_mode || local_server_mode);
/*
@@ -2402,7 +2458,7 @@ void Game::updateStats(RunStats *stats, const FpsControl &draw_times,
void Game::processUserInput(f32 dtime)
{
// Reset input if window not active or some menu is active
- if (!device->isWindowActive() || !noMenuActive() || guienv->hasFocus(gui_chat_console)) {
+ if (!device->isWindowActive() || isMenuActive() || guienv->hasFocus(gui_chat_console)) {
input->clear();
#ifdef HAVE_TOUCHSCREENGUI
g_touchscreengui->hide();
@@ -2467,6 +2523,30 @@ void Game::processKeyInput()
toggleFast();
} else if (wasKeyDown(KeyType::NOCLIP)) {
toggleNoClip();
+ } else if (wasKeyDown(KeyType::MUTE)) {
+ float volume = g_settings->getFloat("sound_volume");
+ if (volume < 0.001f) {
+ g_settings->setFloat("sound_volume", 1.0f);
+ m_statustext = narrow_to_wide(gettext("Volume changed to 100%"));
+ } else {
+ g_settings->setFloat("sound_volume", 0.0f);
+ m_statustext = narrow_to_wide(gettext("Volume changed to 0%"));
+ }
+ runData.statustext_time = 0;
+ } else if (wasKeyDown(KeyType::INC_VOLUME)) {
+ float new_volume = rangelim(g_settings->getFloat("sound_volume") + 0.1f, 0.0f, 1.0f);
+ char buf[100];
+ g_settings->setFloat("sound_volume", new_volume);
+ snprintf(buf, sizeof(buf), gettext("Volume changed to %d%%"), myround(new_volume * 100));
+ m_statustext = narrow_to_wide(buf);
+ runData.statustext_time = 0;
+ } else if (wasKeyDown(KeyType::DEC_VOLUME)) {
+ float new_volume = rangelim(g_settings->getFloat("sound_volume") - 0.1f, 0.0f, 1.0f);
+ char buf[100];
+ g_settings->setFloat("sound_volume", new_volume);
+ snprintf(buf, sizeof(buf), gettext("Volume changed to %d%%"), myround(new_volume * 100));
+ m_statustext = narrow_to_wide(buf);
+ runData.statustext_time = 0;
} else if (wasKeyDown(KeyType::CINEMATIC)) {
toggleCinematic();
} else if (wasKeyDown(KeyType::SCREENSHOT)) {
@@ -2534,11 +2614,13 @@ void Game::processItemSelection(u16 *new_playeritem)
s32 dir = wheel;
- if (input->joystick.wasKeyDown(KeyType::SCROLL_DOWN)) {
+ if (input->joystick.wasKeyDown(KeyType::SCROLL_DOWN) ||
+ wasKeyDown(KeyType::HOTBAR_NEXT)) {
dir = -1;
}
- if (input->joystick.wasKeyDown(KeyType::SCROLL_UP)) {
+ if (input->joystick.wasKeyDown(KeyType::SCROLL_UP) ||
+ wasKeyDown(KeyType::HOTBAR_PREV)) {
dir = 1;
}
@@ -2731,7 +2813,7 @@ void Game::toggleHud()
void Game::toggleMinimap(bool shift_pressed)
{
- if (!flags.show_hud || !g_settings->getBool("enable_minimap"))
+ if (!mapper || !flags.show_hud || !g_settings->getBool("enable_minimap"))
return;
if (shift_pressed) {
@@ -2893,8 +2975,8 @@ void Game::decreaseViewRange()
void Game::toggleFullViewRange()
{
static const wchar_t *msg[] = {
- L"Disabled full viewing range",
- L"Enabled full viewing range"
+ L"Normal view range",
+ L"Infinite view range"
};
draw_control->range_all = !draw_control->range_all;
@@ -2906,7 +2988,8 @@ void Game::toggleFullViewRange()
void Game::updateCameraDirection(CameraOrientation *cam, float dtime)
{
- if ((device->isWindowActive() && noMenuActive()) || random_input) {
+ if ((device->isWindowActive() && device->isWindowFocused()
+ && !isMenuActive()) || random_input) {
#ifndef __ANDROID__
if (!random_input) {
@@ -2931,8 +3014,7 @@ void Game::updateCameraDirection(CameraOrientation *cam, float dtime)
device->getCursorControl()->setVisible(true);
#endif
- if (!m_first_loop_after_window_activation)
- m_first_loop_after_window_activation = true;
+ m_first_loop_after_window_activation = true;
}
}
@@ -3048,11 +3130,10 @@ inline void Game::step(f32 *dtime)
void Game::processClientEvents(CameraOrientation *cam)
{
- ClientEvent event = client->getClientEvent();
-
LocalPlayer *player = client->getEnv().getLocalPlayer();
- for ( ; event.type != CE_NONE; event = client->getClientEvent()) {
+ while (client->hasClientEvents()) {
+ ClientEvent event = client->getClientEvent();
switch (event.type) {
case CE_PLAYER_DAMAGE:
@@ -3249,6 +3330,8 @@ void Game::processClientEvents(CameraOrientation *cam)
case CE_SET_SKY:
sky->setVisible(false);
+ // Whether clouds are visible in front of a custom skybox
+ sky->setCloudsEnabled(event.set_sky.clouds);
if (skybox) {
skybox->remove();
@@ -3258,6 +3341,7 @@ void Game::processClientEvents(CameraOrientation *cam)
// Handle according to type
if (*event.set_sky.type == "regular") {
sky->setVisible(true);
+ sky->setCloudsEnabled(true);
} else if (*event.set_sky.type == "skybox" &&
event.set_sky.params->size() == 6) {
sky->setFallbackBgColor(*event.set_sky.bgcolor);
@@ -3289,6 +3373,19 @@ void Game::processClientEvents(CameraOrientation *cam)
event.override_day_night_ratio.ratio_f * 1000);
break;
+ case CE_CLOUD_PARAMS:
+ if (clouds) {
+ clouds->setDensity(event.cloud_params.density);
+ clouds->setColorBright(video::SColor(event.cloud_params.color_bright));
+ clouds->setColorAmbient(video::SColor(event.cloud_params.color_ambient));
+ clouds->setHeight(event.cloud_params.height);
+ clouds->setThickness(event.cloud_params.thickness);
+ clouds->setSpeed(v2f(
+ event.cloud_params.speed_x,
+ event.cloud_params.speed_y));
+ }
+ break;
+
default:
// unknown or unhandled type
break;
@@ -3379,14 +3476,25 @@ void Game::updateSound(f32 dtime)
v3f(0, 0, 0), // velocity
camera->getDirection(),
camera->getCameraNode()->getUpVector());
- sound->setListenerGain(g_settings->getFloat("sound_volume"));
+ // Check if volume is in the proper range, else fix it.
+ float old_volume = g_settings->getFloat("sound_volume");
+ float new_volume = rangelim(old_volume, 0.0f, 1.0f);
+ sound->setListenerGain(new_volume);
- // Update sound maker
- soundmaker->step(dtime);
+ if (old_volume != new_volume) {
+ g_settings->setFloat("sound_volume", new_volume);
+ }
LocalPlayer *player = client->getEnv().getLocalPlayer();
+ // Tell the sound maker whether to make footstep sounds
+ soundmaker->makes_footstep_sound = player->makes_footstep_sound;
+
+ // Update sound maker
+ if (player->makes_footstep_sound)
+ soundmaker->step(dtime);
+
ClientMap &map = client->getEnv().getClientMap();
MapNode n = map.getNodeNoEx(player->getFootstepNodePos());
soundmaker->m_player_step_sound = nodedef_manager->get(n).sound_footstep;
@@ -3443,6 +3551,9 @@ void Game::processPlayerInteraction(f32 dtime, bool show_hud, bool show_debug)
if ((g_settings->getBool("touchtarget")) && (g_touchscreengui)) {
shootline = g_touchscreengui->getShootline();
+ // Scale shootline to the acual distance the player can reach
+ shootline.end = shootline.start
+ + shootline.getVector().normalize() * BS * d;
shootline.start += intToFloat(camera_offset, BS);
shootline.end += intToFloat(camera_offset, BS);
}
@@ -3489,6 +3600,10 @@ void Game::processPlayerInteraction(f32 dtime, bool show_hud, bool show_debug)
client->setCrack(-1, v3s16(0, 0, 0));
runData.dig_time = 0.0;
}
+ } else if (runData.dig_instantly && getLeftReleased()) {
+ // Remove e.g. torches faster when clicking instead of holding LMB
+ runData.nodig_delay_timer = 0;
+ runData.dig_instantly = false;
}
if (!runData.digging && runData.ldown_for_dig && !isLeftPressed()) {
@@ -3505,15 +3620,17 @@ void Game::processPlayerInteraction(f32 dtime, bool show_hud, bool show_debug)
runData.repeat_rightclick_timer = 0;
if (playeritem_def.usable && isLeftPressed()) {
- if (getLeftClicked())
+ if (getLeftClicked() && (!client->moddingEnabled()
+ || !client->getScript()->on_item_use(playeritem, pointed)))
client->interact(4, pointed);
} else if (pointed.type == POINTEDTHING_NODE) {
ToolCapabilities playeritem_toolcap =
playeritem.getToolCapabilities(itemdef_manager);
- if (playeritem.name.empty()) {
+ 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()) {
@@ -3628,12 +3745,9 @@ PointedThing Game::updatePointedThing(
float sin_r = 0.08 * sin(timerf);
float sin_g = 0.08 * sin(timerf + irr::core::PI * 0.5);
float sin_b = 0.08 * sin(timerf + irr::core::PI);
- c.setRed(
- core::clamp(core::round32(c.getRed() * (0.8 + sin_r)), 0, 255));
- c.setGreen(
- core::clamp(core::round32(c.getGreen() * (0.8 + sin_g)), 0, 255));
- c.setBlue(
- core::clamp(core::round32(c.getBlue() * (0.8 + sin_b)), 0, 255));
+ c.setRed(core::clamp(core::round32(c.getRed() * (0.8 + sin_r)), 0, 255));
+ c.setGreen(core::clamp(core::round32(c.getGreen() * (0.8 + sin_g)), 0, 255));
+ c.setBlue(core::clamp(core::round32(c.getBlue() * (0.8 + sin_b)), 0, 255));
// Set mesh final color
hud->setSelectionMeshColor(c);
@@ -3651,8 +3765,9 @@ void Game::handlePointingAtNothing(const ItemStack &playerItem)
}
-void Game::handlePointingAtNode(const PointedThing &pointed, const ItemDefinition &playeritem_def,
- const ToolCapabilities &playeritem_toolcap, f32 dtime)
+void Game::handlePointingAtNode(const PointedThing &pointed,
+ const ItemDefinition &playeritem_def, const ItemStack &playeritem,
+ const ToolCapabilities &playeritem_toolcap, f32 dtime)
{
v3s16 nodepos = pointed.node_undersurface;
v3s16 neighbourpos = pointed.node_abovesurface;
@@ -3662,6 +3777,13 @@ void Game::handlePointingAtNode(const PointedThing &pointed, const ItemDefinitio
*/
ClientMap &map = client->getEnv().getClientMap();
+
+ if (runData.nodig_delay_timer <= 0.0 && isLeftPressed()
+ && client->checkPrivilege("interact")) {
+ handleDigging(pointed, nodepos, playeritem_toolcap, dtime);
+ }
+
+ // This should be done after digging handling
NodeMetadata *meta = map.getNodeMetadata(nodepos);
if (meta) {
@@ -3675,11 +3797,6 @@ void Game::handlePointingAtNode(const PointedThing &pointed, const ItemDefinitio
}
}
- if (runData.nodig_delay_timer <= 0.0 && isLeftPressed()
- && client->checkPrivilege("interact")) {
- handleDigging(pointed, nodepos, playeritem_toolcap, dtime);
- }
-
if ((getRightClicked() ||
runData.repeat_rightclick_timer >= m_repeat_right_click_time) &&
client->checkPrivilege("interact")) {
@@ -3688,6 +3805,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;
@@ -3710,7 +3832,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) {
@@ -3719,6 +3841,9 @@ void Game::handlePointingAtNode(const PointedThing &pointed, const ItemDefinitio
// Read the sound
soundmaker->m_player_rightpunch_sound =
playeritem_def.sound_place;
+
+ if (client->moddingEnabled())
+ client->getScript()->on_placenode(pointed, playeritem_def);
} else {
soundmaker->m_player_rightpunch_sound =
SimpleSoundSpec();
@@ -3801,15 +3926,6 @@ void Game::handleDigging(const PointedThing &pointed, const v3s16 &nodepos,
ClientMap &map = client->getEnv().getClientMap();
MapNode n = client->getEnv().getClientMap().getNodeNoEx(nodepos);
- if (!runData.digging) {
- infostream << "Started digging" << std::endl;
- if (client->moddingEnabled() && client->getScript()->on_punchnode(nodepos, n))
- return;
- client->interact(0, pointed);
- runData.digging = true;
- runData.ldown_for_dig = true;
- }
-
// NOTE: Similar piece of code exists on the server side for
// cheat detection.
// Get digging parameters
@@ -3841,12 +3957,22 @@ void Game::handleDigging(const PointedThing &pointed, const v3s16 &nodepos,
}
}
- if (runData.dig_time_complete >= 0.001) {
+ if (!runData.digging) {
+ infostream << "Started digging" << std::endl;
+ runData.dig_instantly = runData.dig_time_complete == 0;
+ if (client->moddingEnabled() && client->getScript()->on_punchnode(nodepos, n))
+ return;
+ client->interact(0, pointed);
+ runData.digging = true;
+ runData.ldown_for_dig = true;
+ }
+
+ if (!runData.dig_instantly) {
runData.dig_index = (float)crack_animation_length
* runData.dig_time
/ runData.dig_time_complete;
} else {
- // This is for torches
+ // This is for e.g. torches
runData.dig_index = crack_animation_length;
}
@@ -3881,25 +4007,19 @@ void Game::handleDigging(const PointedThing &pointed, const v3s16 &nodepos,
runData.nodig_delay_timer =
runData.dig_time_complete / (float)crack_animation_length;
- // We don't want a corresponding delay to
- // very time consuming nodes
+ // We don't want a corresponding delay to very time consuming nodes
+ // and nodes without digging time (e.g. torches) get a fixed delay.
if (runData.nodig_delay_timer > 0.3)
runData.nodig_delay_timer = 0.3;
-
- // We want a slight delay to very little
- // time consuming nodes
- const float mindelay = 0.15;
-
- if (runData.nodig_delay_timer < mindelay)
- runData.nodig_delay_timer = mindelay;
+ else if (runData.dig_instantly)
+ runData.nodig_delay_timer = 0.15;
bool is_valid_position;
MapNode wasnode = map.getNodeNoEx(nodepos, &is_valid_position);
if (is_valid_position) {
- if (client->moddingEnabled()) {
- if (client->getScript()->on_dignode(nodepos, wasnode)) {
- return;
- }
+ if (client->moddingEnabled() &&
+ client->getScript()->on_dignode(nodepos, wasnode)) {
+ return;
}
client->removeNode(nodepos);
}
@@ -4103,7 +4223,7 @@ void Game::updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime,
if (current_formspec->getReferenceCount() == 1) {
current_formspec->drop();
current_formspec = NULL;
- } else if (!noMenuActive()) {
+ } else if (isMenuActive()) {
guiroot->bringToFront(current_formspec);
}
}
@@ -4117,7 +4237,7 @@ void Game::updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime,
TimeTaker tt_draw("mainloop: draw");
driver->beginScene(true, true, skycolor);
- draw_scene(driver, smgr, *camera, *client, player, *hud, *mapper,
+ draw_scene(driver, smgr, *camera, *client, player, *hud, mapper,
guienv, screensize, skycolor, flags.show_hud,
flags.show_minimap);
@@ -4152,7 +4272,7 @@ void Game::updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime,
/*
Update minimap pos and rotation
*/
- if (flags.show_minimap && flags.show_hud) {
+ if (mapper && flags.show_minimap && flags.show_hud) {
mapper->setPos(floatToInt(player->getPosition(), BS));
mapper->setAngle(player->getYaw());
}
@@ -4425,6 +4545,7 @@ void Game::extendedResourceCleanup()
<< " (note: irrlicht doesn't support removing renderers)" << std::endl;
}
+#define GET_KEY_NAME(KEY) gettext(getKeySetting(#KEY).name())
void Game::showPauseMenu()
{
#ifdef __ANDROID__
@@ -4442,21 +4563,41 @@ void Game::showPauseMenu()
" --> place single item to slot\n"
);
#else
- static const std::string control_text = strgettext("Default Controls:\n"
- "- WASD: move\n"
- "- Space: jump/climb\n"
- "- Shift: sneak/go down\n"
- "- Q: drop item\n"
- "- I: inventory\n"
+ static const std::string control_text_template = strgettext("Controls:\n"
+ "- %s: move forwards\n"
+ "- %s: move backwards\n"
+ "- %s: move left\n"
+ "- %s: move right\n"
+ "- %s: jump/climb\n"
+ "- %s: sneak/go down\n"
+ "- %s: drop item\n"
+ "- %s: inventory\n"
"- Mouse: turn/look\n"
"- Mouse left: dig/punch\n"
"- Mouse right: place/use\n"
"- Mouse wheel: select item\n"
- "- T: chat\n"
+ "- %s: chat\n"
);
+
+ char control_text_buf[600];
+
+ snprintf(control_text_buf, ARRLEN(control_text_buf), control_text_template.c_str(),
+ GET_KEY_NAME(keymap_forward),
+ GET_KEY_NAME(keymap_backward),
+ GET_KEY_NAME(keymap_left),
+ GET_KEY_NAME(keymap_right),
+ GET_KEY_NAME(keymap_jump),
+ GET_KEY_NAME(keymap_sneak),
+ GET_KEY_NAME(keymap_drop),
+ GET_KEY_NAME(keymap_inventory),
+ GET_KEY_NAME(keymap_chat)
+ );
+
+ std::string control_text = std::string(control_text_buf);
+ str_formspec_escape(control_text);
#endif
- float ypos = simple_singleplayer_mode ? 0.5 : 0.1;
+ float ypos = simple_singleplayer_mode ? 0.7f : 0.1f;
std::ostringstream os;
os << FORMSPEC_VERSION_STRING << SIZE_TAG
@@ -4466,6 +4607,8 @@ void Game::showPauseMenu()
if (!simple_singleplayer_mode) {
os << "button_exit[4," << (ypos++) << ";3,0.5;btn_change_password;"
<< strgettext("Change Password") << "]";
+ } else {
+ os << "field[4.95,0;5,1.5;;" << strgettext("Game paused") << ";]";
}
#ifndef __ANDROID__
@@ -4479,10 +4622,43 @@ void Game::showPauseMenu()
os << "button_exit[4," << (ypos++) << ";3,0.5;btn_exit_os;"
<< strgettext("Exit to OS") << "]"
<< "textarea[7.5,0.25;3.9,6.25;;" << control_text << ";]"
- << "textarea[0.4,0.25;3.5,6;;" << PROJECT_NAME_C "\n"
- << g_build_info << "\n"
- << "path_user = " << wrap_rows(porting::path_user, 20)
- << "\n;]";
+ << "textarea[0.4,0.25;3.9,6.25;;" << PROJECT_NAME_C " " VERSION_STRING "\n"
+ << "\n"
+ << strgettext("Game info:") << "\n";
+ const std::string &address = client->getAddressName();
+ static const std::string mode = strgettext("- Mode: ");
+ if (!simple_singleplayer_mode) {
+ Address serverAddress = client->getServerAddress();
+ if (address != "") {
+ os << mode << strgettext("Remote server") << "\n"
+ << strgettext("- Address: ") << address;
+ } else {
+ os << mode << strgettext("Hosting server");
+ }
+ os << "\n" << strgettext("- Port: ") << serverAddress.getPort() << "\n";
+ } else {
+ os << mode << strgettext("Singleplayer") << "\n";
+ }
+ if (simple_singleplayer_mode || address == "") {
+ static const std::string on = strgettext("On");
+ static const std::string off = strgettext("Off");
+ const std::string &damage = g_settings->getBool("enable_damage") ? on : off;
+ const std::string &creative = g_settings->getBool("creative_mode") ? on : off;
+ const std::string &announced = g_settings->getBool("server_announce") ? on : off;
+ os << strgettext("- Damage: ") << damage << "\n"
+ << strgettext("- Creative Mode: ") << creative << "\n";
+ if (!simple_singleplayer_mode) {
+ const std::string &pvp = g_settings->getBool("enable_pvp") ? on : off;
+ os << strgettext("- PvP: ") << pvp << "\n"
+ << strgettext("- Public: ") << announced << "\n";
+ std::string server_name = g_settings->get("server_name");
+ str_formspec_escape(server_name);
+ if (announced == on && server_name != "")
+ os << strgettext("- Server Name: ") << server_name;
+
+ }
+ }
+ os << ";]";
/* Create menu */
/* Note: FormspecFormSource and LocalFormspecHandler *
diff --git a/src/game.h b/src/game.h
index eaedca165..4fb198be8 100644
--- a/src/game.h
+++ b/src/game.h
@@ -22,124 +22,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "irrlichttypes_extrabloated.h"
#include <string>
-#include "client/keys.h"
-#include "client/joystick_controller.h"
-#include "keycode.h"
-#include <list>
-
-class KeyList : protected std::list<KeyPress>
-{
- typedef std::list<KeyPress> super;
- typedef super::iterator iterator;
- typedef super::const_iterator const_iterator;
-
- virtual const_iterator find(const KeyPress &key) const
- {
- const_iterator f(begin());
- const_iterator e(end());
-
- while (f != e) {
- if (*f == key)
- return f;
-
- ++f;
- }
-
- return e;
- }
-
- virtual iterator find(const KeyPress &key)
- {
- iterator f(begin());
- iterator e(end());
-
- while (f != e) {
- if (*f == key)
- return f;
-
- ++f;
- }
-
- return e;
- }
-
-public:
- void clear()
- {
- super::clear();
- }
-
- void set(const KeyPress &key)
- {
- if (find(key) == end())
- push_back(key);
- }
-
- void unset(const KeyPress &key)
- {
- iterator p(find(key));
-
- if (p != end())
- erase(p);
- }
-
- void toggle(const KeyPress &key)
- {
- iterator p(this->find(key));
-
- if (p != end())
- erase(p);
- else
- push_back(key);
- }
-
- bool operator[](const KeyPress &key) const
- {
- return find(key) != end();
- }
-};
-
-class InputHandler
-{
-public:
- InputHandler()
- {
- }
- virtual ~InputHandler()
- {
- }
-
- virtual bool isKeyDown(const KeyPress &keyCode) = 0;
- virtual bool wasKeyDown(const KeyPress &keyCode) = 0;
-
- virtual void listenForKey(const KeyPress &keyCode) {}
- virtual void dontListenForKeys() {}
-
- virtual v2s32 getMousePos() = 0;
- virtual void setMousePos(s32 x, s32 y) = 0;
-
- virtual bool getLeftState() = 0;
- virtual bool getRightState() = 0;
-
- virtual bool getLeftClicked() = 0;
- virtual bool getRightClicked() = 0;
- virtual void resetLeftClicked() = 0;
- virtual void resetRightClicked() = 0;
-
- virtual bool getLeftReleased() = 0;
- virtual bool getRightReleased() = 0;
- virtual void resetLeftReleased() = 0;
- virtual void resetRightReleased() = 0;
-
- virtual s32 getMouseWheel() = 0;
-
- virtual void step(float dtime) {}
-
- virtual void clear() {}
-
- JoystickController joystick;
-};
+class InputHandler;
class ChatBackend; /* to avoid having to include chat.h */
struct SubgameSpec;
diff --git a/src/genericobject.cpp b/src/genericobject.cpp
index 07d2445b4..58f4b997e 100644
--- a/src/genericobject.cpp
+++ b/src/genericobject.cpp
@@ -68,7 +68,7 @@ std::string gob_cmd_update_position(
std::string gob_cmd_set_texture_mod(const std::string &mod)
{
std::ostringstream os(std::ios::binary);
- // command
+ // command
writeU8(os, GENERIC_CMD_SET_TEXTURE_MOD);
// parameters
os<<serializeString(mod);
@@ -95,7 +95,7 @@ std::string gob_cmd_set_sprite(
std::string gob_cmd_punched(s16 damage, s16 result_hp)
{
std::ostringstream os(std::ios::binary);
- // command
+ // command
writeU8(os, GENERIC_CMD_PUNCHED);
// damage
writeS16(os, damage);
@@ -121,7 +121,7 @@ std::string gob_cmd_update_physics_override(float physics_override_speed, float
float physics_override_gravity, bool sneak, bool sneak_glitch, bool new_move)
{
std::ostringstream os(std::ios::binary);
- // command
+ // command
writeU8(os, GENERIC_CMD_SET_PHYSICS_OVERRIDE);
// parameters
writeF1000(os, physics_override_speed);
@@ -137,7 +137,7 @@ std::string gob_cmd_update_physics_override(float physics_override_speed, float
std::string gob_cmd_update_animation(v2f frames, float frame_speed, float frame_blend, bool frame_loop)
{
std::ostringstream os(std::ios::binary);
- // command
+ // command
writeU8(os, GENERIC_CMD_SET_ANIMATION);
// parameters
writeV2F1000(os, frames);
@@ -148,10 +148,11 @@ std::string gob_cmd_update_animation(v2f frames, float frame_speed, float frame_
return os.str();
}
-std::string gob_cmd_update_bone_position(std::string bone, v3f position, v3f rotation)
+std::string gob_cmd_update_bone_position(const std::string &bone, v3f position,
+ v3f rotation)
{
std::ostringstream os(std::ios::binary);
- // command
+ // command
writeU8(os, GENERIC_CMD_SET_BONE_POSITION);
// parameters
os<<serializeString(bone);
@@ -160,10 +161,11 @@ std::string gob_cmd_update_bone_position(std::string bone, v3f position, v3f rot
return os.str();
}
-std::string gob_cmd_update_attachment(int parent_id, std::string bone, v3f position, v3f rotation)
+std::string gob_cmd_update_attachment(int parent_id, const std::string &bone,
+ v3f position, v3f rotation)
{
std::ostringstream os(std::ios::binary);
- // command
+ // command
writeU8(os, GENERIC_CMD_ATTACH_TO);
// parameters
writeS16(os, parent_id);
@@ -184,10 +186,11 @@ std::string gob_cmd_update_nametag_attributes(video::SColor color)
return os.str();
}
-std::string gob_cmd_update_infant(u16 id, u8 type, std::string client_initialization_data)
+std::string gob_cmd_update_infant(u16 id, u8 type,
+ const std::string &client_initialization_data)
{
std::ostringstream os(std::ios::binary);
- // command
+ // command
writeU8(os, GENERIC_CMD_SPAWN_INFANT);
// parameters
writeU16(os, id);
diff --git a/src/genericobject.h b/src/genericobject.h
index 7d2ec4b14..d82650f0e 100644
--- a/src/genericobject.h
+++ b/src/genericobject.h
@@ -73,13 +73,16 @@ std::string gob_cmd_update_physics_override(float physics_override_speed,
std::string gob_cmd_update_animation(v2f frames, float frame_speed, float frame_blend, bool frame_loop);
-std::string gob_cmd_update_bone_position(std::string bone, v3f position, v3f rotation);
+std::string gob_cmd_update_bone_position(const std::string &bone, v3f position,
+ v3f rotation);
-std::string gob_cmd_update_attachment(int parent_id, std::string bone, v3f position, v3f rotation);
+std::string gob_cmd_update_attachment(int parent_id, const std::string &bone,
+ v3f position, v3f rotation);
std::string gob_cmd_update_nametag_attributes(video::SColor color);
-std::string gob_cmd_update_infant(u16 id, u8 type, std::string client_initialization_data);
+std::string gob_cmd_update_infant(u16 id, u8 type,
+ const std::string &client_initialization_data);
#endif
diff --git a/src/gettext.h b/src/gettext.h
index 885d7ca2d..9aa6b3a27 100644
--- a/src/gettext.h
+++ b/src/gettext.h
@@ -51,12 +51,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
diff --git a/src/gettime.h b/src/gettime.h
index b2f09a7bb..4d5a02e1e 100644
--- a/src/gettime.h
+++ b/src/gettime.h
@@ -21,34 +21,17 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#define GETTIME_HEADER
#include "irrlichttypes.h"
+#include <time.h>
+#include <string>
-/*
- Get a millisecond counter value.
- Precision depends on implementation.
- Overflows at any value above 10000000.
-
- Implementation of this is done in:
- Normal build: main.cpp
- Server build: servermain.cpp
-*/
enum TimePrecision
{
- PRECISION_SECONDS = 0,
+ PRECISION_SECONDS,
PRECISION_MILLI,
PRECISION_MICRO,
PRECISION_NANO
};
-extern u32 getTimeMs();
-extern u32 getTime(TimePrecision prec);
-
-/*
- Timestamp stuff
-*/
-
-#include <string>
-#include <time.h>
-
inline std::string getTimestamp()
{
time_t t = time(NULL);
diff --git a/src/guiChatConsole.cpp b/src/guiChatConsole.cpp
index bea5571f4..ba73a58e2 100644
--- a/src/guiChatConsole.cpp
+++ b/src/guiChatConsole.cpp
@@ -55,7 +55,7 @@ GUIChatConsole::GUIChatConsole(
m_client(client),
m_menumgr(menumgr),
m_screensize(v2u32(0,0)),
- m_animate_time_old(0),
+ m_animate_time_old(porting::getTimeMs()),
m_open(false),
m_close_on_enter(false),
m_height(0),
@@ -71,8 +71,6 @@ GUIChatConsole::GUIChatConsole(
m_font(NULL),
m_fontsize(0, 0)
{
- m_animate_time_old = getTimeMs();
-
// load background settings
s32 console_alpha = g_settings->getS32("console_alpha");
m_background_color.setAlpha(clamp_u8(console_alpha));
@@ -124,7 +122,7 @@ void GUIChatConsole::openConsole(f32 scale)
m_desired_height_fraction = scale;
m_desired_height = scale * m_screensize.Y;
reformatConsole();
- m_animate_time_old = getTimeMs();
+ m_animate_time_old = porting::getTimeMs();
IGUIElement::setVisible(true);
Environment->setFocus(this);
m_menumgr->createdMenu(this);
@@ -206,13 +204,13 @@ 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();
}
// Animation
- u32 now = getTimeMs();
+ u64 now = porting::getTimeMs();
animate(now - m_animate_time_old);
m_animate_time_old = now;
@@ -233,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);
}
@@ -629,9 +628,7 @@ bool GUIChatConsole::OnEvent(const SEvent& event)
bool backwards = event.KeyInput.Shift;
prompt.nickCompletion(names, backwards);
return true;
- }
- else if(event.KeyInput.Char != 0 && !event.KeyInput.Control)
- {
+ } else if (!iswcntrl(event.KeyInput.Char) && !event.KeyInput.Control) {
#if defined(__linux__) && (IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 9)
wchar_t wc = L'_';
mbtowc( &wc, (char *) &event.KeyInput.Char, sizeof(event.KeyInput.Char) );
diff --git a/src/guiChatConsole.h b/src/guiChatConsole.h
index 4e3cae13f..0332678c7 100644
--- a/src/guiChatConsole.h
+++ b/src/guiChatConsole.h
@@ -98,7 +98,7 @@ private:
v2u32 m_screensize;
// used to compute how much time passed since last animate()
- u32 m_animate_time_old;
+ u64 m_animate_time_old;
// should the console be opened or closed?
bool m_open;
diff --git a/src/guiEngine.cpp b/src/guiEngine.cpp
index 03fee6b96..2d1bd6d44 100644
--- a/src/guiEngine.cpp
+++ b/src/guiEngine.cpp
@@ -60,7 +60,7 @@ void TextDestGuiEngine::gotText(const StringMap &fields)
}
/******************************************************************************/
-void TextDestGuiEngine::gotText(std::wstring text)
+void TextDestGuiEngine::gotText(const std::wstring &text)
{
m_engine->getScriptIface()->handleMainMenuEvent(wide_to_utf8(text));
}
@@ -262,8 +262,24 @@ void GUIEngine::run()
unsigned int text_height = g_fontengine->getTextHeight();
- while(m_device->run() && (!m_startgame) && (!m_kill))
- {
+ irr::core::dimension2d<u32> previous_screen_size(g_settings->getU16("screenW"),
+ g_settings->getU16("screenH"));
+
+ while (m_device->run() && (!m_startgame) && (!m_kill)) {
+
+ const irr::core::dimension2d<u32> &current_screen_size =
+ m_device->getVideoDriver()->getScreenSize();
+ // Verify if window size has changed and save it if it's the case
+ // Ensure evaluating settings->getBool after verifying screensize
+ // First condition is cheaper
+ if (previous_screen_size != current_screen_size &&
+ current_screen_size != irr::core::dimension2d<u32>(0,0) &&
+ g_settings->getBool("autosave_screensize")) {
+ g_settings->setU16("screenW", current_screen_size.Width);
+ g_settings->setU16("screenH", current_screen_size.Height);
+ previous_screen_size = current_screen_size;
+ }
+
//check if we need to update the "upper left corner"-text
if (text_height != g_fontengine->getTextHeight()) {
updateTopLeftTextSize();
@@ -540,7 +556,7 @@ bool GUIEngine::setTexture(texture_layer layer, std::string texturepath,
}
/******************************************************************************/
-bool GUIEngine::downloadFile(std::string url, std::string target)
+bool GUIEngine::downloadFile(const std::string &url, const std::string &target)
{
#if USE_CURL
std::ofstream target_file(target.c_str(), std::ios::out | std::ios::binary);
@@ -602,8 +618,8 @@ void GUIEngine::stopSound(s32 handle)
}
/******************************************************************************/
-unsigned int GUIEngine::queueAsync(std::string serialized_func,
- std::string serialized_params)
+unsigned int GUIEngine::queueAsync(const std::string &serialized_func,
+ const std::string &serialized_params)
{
return m_script->queueAsync(serialized_func, serialized_params);
}
diff --git a/src/guiEngine.h b/src/guiEngine.h
index a81813d18..e7e5ca05d 100644
--- a/src/guiEngine.h
+++ b/src/guiEngine.h
@@ -80,7 +80,7 @@ public:
* receive text/events transmitted by guiFormSpecMenu
* @param text textual representation of event
*/
- void gotText(std::wstring text);
+ void gotText(const std::wstring &text);
private:
/** target to transmit data to */
@@ -178,7 +178,8 @@ public:
}
/** pass async callback to scriptengine **/
- unsigned int queueAsync(std::string serialized_fct,std::string serialized_params);
+ unsigned int queueAsync(const std::string &serialized_fct,
+ const std::string &serialized_params);
private:
@@ -188,9 +189,6 @@ private:
/** run main menu loop */
void run();
- /** handler to limit frame rate within main menu */
- void limitFrameRate();
-
/** update size of topleftext element */
void updateTopLeftTextSize();
@@ -262,14 +260,11 @@ private:
* @param url url to download
* @param target file to store to
*/
- static bool downloadFile(std::string url,std::string target);
+ static bool downloadFile(const std::string &url, const std::string &target);
/** array containing pointers to current specified texture layers */
image_definition m_textures[TEX_LAYER_MAX];
- /** draw version string in topleft corner */
- void drawVersion();
-
/**
* specify text to appear as top left string
* @param text to set
diff --git a/src/guiFileSelectMenu.cpp b/src/guiFileSelectMenu.cpp
index 0bb02f8a6..65a07be39 100644
--- a/src/guiFileSelectMenu.cpp
+++ b/src/guiFileSelectMenu.cpp
@@ -18,19 +18,16 @@
*/
#include "guiFileSelectMenu.h"
-#include "util/string.h"
-#include <locale.h>
GUIFileSelectMenu::GUIFileSelectMenu(gui::IGUIEnvironment* env,
- gui::IGUIElement* parent, s32 id, IMenuManager *menumgr,
- std::string title, std::string formname) :
-GUIModalMenu(env, parent, id, menumgr)
+ gui::IGUIElement* parent, s32 id, IMenuManager *menumgr,
+ const std::string &title, const std::string &formname) :
+ GUIModalMenu(env, parent, id, menumgr),
+ m_title(utf8_to_wide(title)),
+ m_accepted(false),
+ m_text_dst(NULL),
+ m_formname(formname)
{
- m_title = utf8_to_wide(title);
- m_parent = parent;
- m_formname = formname;
- m_text_dst = 0;
- m_accepted = false;
}
GUIFileSelectMenu::~GUIFileSelectMenu()
@@ -107,16 +104,12 @@ bool GUIFileSelectMenu::OnEvent(const SEvent& event)
acceptInput();
quitMenu();
return true;
- break;
-
case gui::EGET_DIRECTORY_SELECTED:
case gui::EGET_FILE_SELECTED:
m_accepted=true;
acceptInput();
quitMenu();
return true;
- break;
-
default:
//ignore this event
break;
diff --git a/src/guiFileSelectMenu.h b/src/guiFileSelectMenu.h
index e37d3d8df..034823740 100644
--- a/src/guiFileSelectMenu.h
+++ b/src/guiFileSelectMenu.h
@@ -26,14 +26,12 @@
#include "IGUIFileOpenDialog.h"
#include "guiFormSpecMenu.h" //required because of TextDest only !!!
-
-class GUIFileSelectMenu: public GUIModalMenu
+class GUIFileSelectMenu : public GUIModalMenu
{
public:
- GUIFileSelectMenu(gui::IGUIEnvironment* env, gui::IGUIElement* parent,
- s32 id, IMenuManager *menumgr,
- std::string title,
- std::string formid);
+ GUIFileSelectMenu(gui::IGUIEnvironment *env, gui::IGUIElement *parent, s32 id,
+ IMenuManager *menumgr, const std::string &title,
+ const std::string &formid);
~GUIFileSelectMenu();
void removeChildren();
@@ -45,34 +43,21 @@ public:
void drawMenu();
- bool OnEvent(const SEvent& event);
-
- bool isRunning() {
- return m_running;
- }
+ bool OnEvent(const SEvent &event);
- void setTextDest(TextDest * dest) {
- m_text_dst = dest;
- }
+ void setTextDest(TextDest *dest) { m_text_dst = dest; }
private:
void acceptInput();
std::wstring m_title;
bool m_accepted;
- gui::IGUIElement* m_parent;
-
- std::string m_selectedPath;
- gui::IGUIFileOpenDialog* m_fileOpenDialog;
-
- bool m_running;
+ gui::IGUIFileOpenDialog *m_fileOpenDialog;
TextDest *m_text_dst;
std::string m_formname;
};
-
-
#endif /* GUIFILESELECTMENU_H_ */
diff --git a/src/guiFormSpecMenu.cpp b/src/guiFormSpecMenu.cpp
index 330124fe1..c4b072be1 100644
--- a/src/guiFormSpecMenu.cpp
+++ b/src/guiFormSpecMenu.cpp
@@ -42,7 +42,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "filesys.h"
#include "gettime.h"
#include "gettext.h"
-#include "serverscripting.h"
+#include "scripting_server.h"
#include "porting.h"
#include "settings.h"
#include "client.h"
@@ -252,7 +252,7 @@ std::vector<std::string>* GUIFormSpecMenu::getDropDownValues(const std::string &
return NULL;
}
-void GUIFormSpecMenu::parseSize(parserData* data,std::string element)
+void GUIFormSpecMenu::parseSize(parserData* data, const std::string &element)
{
std::vector<std::string> parts = split(element,',');
@@ -278,7 +278,7 @@ void GUIFormSpecMenu::parseSize(parserData* data,std::string element)
errorstream<< "Invalid size element (" << parts.size() << "): '" << element << "'" << std::endl;
}
-void GUIFormSpecMenu::parseContainer(parserData* data, std::string element)
+void GUIFormSpecMenu::parseContainer(parserData* data, const std::string &element)
{
std::vector<std::string> parts = split(element, ',');
@@ -304,7 +304,7 @@ void GUIFormSpecMenu::parseContainerEnd(parserData* data)
}
}
-void GUIFormSpecMenu::parseList(parserData* data,std::string element)
+void GUIFormSpecMenu::parseList(parserData* data, const std::string &element)
{
if (m_client == 0) {
warningstream<<"invalid use of 'list' with m_client==0"<<std::endl;
@@ -359,7 +359,7 @@ void GUIFormSpecMenu::parseList(parserData* data,std::string element)
errorstream<< "Invalid list element(" << parts.size() << "): '" << element << "'" << std::endl;
}
-void GUIFormSpecMenu::parseListRing(parserData* data, std::string element)
+void GUIFormSpecMenu::parseListRing(parserData* data, const std::string &element)
{
if (m_client == 0) {
errorstream << "WARNING: invalid use of 'listring' with m_client==0" << std::endl;
@@ -394,7 +394,7 @@ void GUIFormSpecMenu::parseListRing(parserData* data, std::string element)
<< m_inventorylists.size() << "): '" << element << "'" << std::endl;
}
-void GUIFormSpecMenu::parseCheckbox(parserData* data,std::string element)
+void GUIFormSpecMenu::parseCheckbox(parserData* data, const std::string &element)
{
std::vector<std::string> parts = split(element,';');
@@ -450,7 +450,7 @@ void GUIFormSpecMenu::parseCheckbox(parserData* data,std::string element)
errorstream<< "Invalid checkbox element(" << parts.size() << "): '" << element << "'" << std::endl;
}
-void GUIFormSpecMenu::parseScrollBar(parserData* data, std::string element)
+void GUIFormSpecMenu::parseScrollBar(parserData* data, const std::string &element)
{
std::vector<std::string> parts = split(element,';');
@@ -509,7 +509,7 @@ void GUIFormSpecMenu::parseScrollBar(parserData* data, std::string element)
errorstream<< "Invalid scrollbar element(" << parts.size() << "): '" << element << "'" << std::endl;
}
-void GUIFormSpecMenu::parseImage(parserData* data,std::string element)
+void GUIFormSpecMenu::parseImage(parserData* data, const std::string &element)
{
std::vector<std::string> parts = split(element,';');
@@ -553,7 +553,7 @@ void GUIFormSpecMenu::parseImage(parserData* data,std::string element)
errorstream<< "Invalid image element(" << parts.size() << "): '" << element << "'" << std::endl;
}
-void GUIFormSpecMenu::parseItemImage(parserData* data,std::string element)
+void GUIFormSpecMenu::parseItemImage(parserData* data, const std::string &element)
{
std::vector<std::string> parts = split(element,';');
@@ -583,8 +583,8 @@ void GUIFormSpecMenu::parseItemImage(parserData* data,std::string element)
errorstream<< "Invalid ItemImage element(" << parts.size() << "): '" << element << "'" << std::endl;
}
-void GUIFormSpecMenu::parseButton(parserData* data,std::string element,
- std::string type)
+void GUIFormSpecMenu::parseButton(parserData* data, const std::string &element,
+ const std::string &type)
{
std::vector<std::string> parts = split(element,';');
@@ -638,7 +638,7 @@ void GUIFormSpecMenu::parseButton(parserData* data,std::string element,
errorstream<< "Invalid button element(" << parts.size() << "): '" << element << "'" << std::endl;
}
-void GUIFormSpecMenu::parseBackground(parserData* data,std::string element)
+void GUIFormSpecMenu::parseBackground(parserData* data, const std::string &element)
{
std::vector<std::string> parts = split(element,';');
@@ -676,7 +676,7 @@ void GUIFormSpecMenu::parseBackground(parserData* data,std::string element)
errorstream<< "Invalid background element(" << parts.size() << "): '" << element << "'" << std::endl;
}
-void GUIFormSpecMenu::parseTableOptions(parserData* data,std::string element)
+void GUIFormSpecMenu::parseTableOptions(parserData* data, const std::string &element)
{
std::vector<std::string> parts = split(element,';');
@@ -688,7 +688,7 @@ void GUIFormSpecMenu::parseTableOptions(parserData* data,std::string element)
}
}
-void GUIFormSpecMenu::parseTableColumns(parserData* data,std::string element)
+void GUIFormSpecMenu::parseTableColumns(parserData* data, const std::string &element)
{
std::vector<std::string> parts = split(element,';');
@@ -708,7 +708,7 @@ void GUIFormSpecMenu::parseTableColumns(parserData* data,std::string element)
}
}
-void GUIFormSpecMenu::parseTable(parserData* data,std::string element)
+void GUIFormSpecMenu::parseTable(parserData* data, const std::string &element)
{
std::vector<std::string> parts = split(element,';');
@@ -776,7 +776,7 @@ void GUIFormSpecMenu::parseTable(parserData* data,std::string element)
errorstream<< "Invalid table element(" << parts.size() << "): '" << element << "'" << std::endl;
}
-void GUIFormSpecMenu::parseTextList(parserData* data,std::string element)
+void GUIFormSpecMenu::parseTextList(parserData* data, const std::string &element)
{
std::vector<std::string> parts = split(element,';');
@@ -849,7 +849,7 @@ void GUIFormSpecMenu::parseTextList(parserData* data,std::string element)
}
-void GUIFormSpecMenu::parseDropDown(parserData* data,std::string element)
+void GUIFormSpecMenu::parseDropDown(parserData* data, const std::string &element)
{
std::vector<std::string> parts = split(element,';');
@@ -913,8 +913,7 @@ void GUIFormSpecMenu::parseDropDown(parserData* data,std::string element)
<< element << "'" << std::endl;
}
-void GUIFormSpecMenu::parseFieldCloseOnEnter(parserData *data,
- const std::string &element)
+void GUIFormSpecMenu::parseFieldCloseOnEnter(parserData *data, const std::string &element)
{
std::vector<std::string> parts = split(element,';');
if (parts.size() == 2 ||
@@ -923,7 +922,7 @@ void GUIFormSpecMenu::parseFieldCloseOnEnter(parserData *data,
}
}
-void GUIFormSpecMenu::parsePwdField(parserData* data,std::string element)
+void GUIFormSpecMenu::parsePwdField(parserData* data, const std::string &element)
{
std::vector<std::string> parts = split(element,';');
@@ -1084,8 +1083,8 @@ void GUIFormSpecMenu::parseSimpleField(parserData* data,
m_fields.push_back(spec);
}
-void GUIFormSpecMenu::parseTextArea(parserData* data,
- std::vector<std::string>& parts,std::string type)
+void GUIFormSpecMenu::parseTextArea(parserData* data, std::vector<std::string>& parts,
+ const std::string &type)
{
std::vector<std::string> v_pos = split(parts[0],',');
@@ -1196,8 +1195,8 @@ void GUIFormSpecMenu::parseTextArea(parserData* data,
m_fields.push_back(spec);
}
-void GUIFormSpecMenu::parseField(parserData* data,std::string element,
- std::string type)
+void GUIFormSpecMenu::parseField(parserData* data, const std::string &element,
+ const std::string &type)
{
std::vector<std::string> parts = split(element,';');
@@ -1215,7 +1214,7 @@ void GUIFormSpecMenu::parseField(parserData* data,std::string element,
errorstream<< "Invalid field element(" << parts.size() << "): '" << element << "'" << std::endl;
}
-void GUIFormSpecMenu::parseLabel(parserData* data,std::string element)
+void GUIFormSpecMenu::parseLabel(parserData* data, const std::string &element)
{
std::vector<std::string> parts = split(element,';');
@@ -1271,7 +1270,7 @@ void GUIFormSpecMenu::parseLabel(parserData* data,std::string element)
errorstream<< "Invalid label element(" << parts.size() << "): '" << element << "'" << std::endl;
}
-void GUIFormSpecMenu::parseVertLabel(parserData* data,std::string element)
+void GUIFormSpecMenu::parseVertLabel(parserData* data, const std::string &element)
{
std::vector<std::string> parts = split(element,';');
@@ -1321,8 +1320,8 @@ void GUIFormSpecMenu::parseVertLabel(parserData* data,std::string element)
errorstream<< "Invalid vertlabel element(" << parts.size() << "): '" << element << "'" << std::endl;
}
-void GUIFormSpecMenu::parseImageButton(parserData* data,std::string element,
- std::string type)
+void GUIFormSpecMenu::parseImageButton(parserData* data, const std::string &element,
+ const std::string &type)
{
std::vector<std::string> parts = split(element,';');
@@ -1410,7 +1409,7 @@ void GUIFormSpecMenu::parseImageButton(parserData* data,std::string element,
errorstream<< "Invalid imagebutton element(" << parts.size() << "): '" << element << "'" << std::endl;
}
-void GUIFormSpecMenu::parseTabHeader(parserData* data,std::string element)
+void GUIFormSpecMenu::parseTabHeader(parserData* data, const std::string &element)
{
std::vector<std::string> parts = split(element,';');
@@ -1482,7 +1481,7 @@ void GUIFormSpecMenu::parseTabHeader(parserData* data,std::string element)
<< element << "'" << std::endl;
}
-void GUIFormSpecMenu::parseItemImageButton(parserData* data,std::string element)
+void GUIFormSpecMenu::parseItemImageButton(parserData* data, const std::string &element)
{
if (m_client == 0) {
@@ -1556,7 +1555,7 @@ void GUIFormSpecMenu::parseItemImageButton(parserData* data,std::string element)
errorstream<< "Invalid ItemImagebutton element(" << parts.size() << "): '" << element << "'" << std::endl;
}
-void GUIFormSpecMenu::parseBox(parserData* data,std::string element)
+void GUIFormSpecMenu::parseBox(parserData* data, const std::string &element)
{
std::vector<std::string> parts = split(element,';');
@@ -1592,7 +1591,7 @@ void GUIFormSpecMenu::parseBox(parserData* data,std::string element)
errorstream<< "Invalid Box element(" << parts.size() << "): '" << element << "'" << std::endl;
}
-void GUIFormSpecMenu::parseBackgroundColor(parserData* data,std::string element)
+void GUIFormSpecMenu::parseBackgroundColor(parserData* data, const std::string &element)
{
std::vector<std::string> parts = split(element,';');
@@ -1610,7 +1609,7 @@ void GUIFormSpecMenu::parseBackgroundColor(parserData* data,std::string element)
errorstream<< "Invalid bgcolor element(" << parts.size() << "): '" << element << "'" << std::endl;
}
-void GUIFormSpecMenu::parseListColors(parserData* data,std::string element)
+void GUIFormSpecMenu::parseListColors(parserData* data, const std::string &element)
{
std::vector<std::string> parts = split(element,';');
@@ -1638,7 +1637,7 @@ void GUIFormSpecMenu::parseListColors(parserData* data,std::string element)
errorstream<< "Invalid listcolors element(" << parts.size() << "): '" << element << "'" << std::endl;
}
-void GUIFormSpecMenu::parseTooltip(parserData* data, std::string element)
+void GUIFormSpecMenu::parseTooltip(parserData* data, const std::string &element)
{
std::vector<std::string> parts = split(element,';');
if (parts.size() == 2) {
@@ -1658,7 +1657,7 @@ void GUIFormSpecMenu::parseTooltip(parserData* data, std::string element)
errorstream<< "Invalid tooltip element(" << parts.size() << "): '" << element << "'" << std::endl;
}
-bool GUIFormSpecMenu::parseVersionDirect(std::string data)
+bool GUIFormSpecMenu::parseVersionDirect(const std::string &data)
{
//some prechecks
if (data == "")
@@ -1682,7 +1681,7 @@ bool GUIFormSpecMenu::parseVersionDirect(std::string data)
return false;
}
-bool GUIFormSpecMenu::parseSizeDirect(parserData* data, std::string element)
+bool GUIFormSpecMenu::parseSizeDirect(parserData* data, const std::string &element)
{
if (element == "")
return false;
@@ -1771,10 +1770,11 @@ void GUIFormSpecMenu::parseAnchor(parserData *data, const std::string &element)
return;
}
- errorstream << "Invalid anchor element (" << parts.size() << "): '" << element << "'" << std::endl;
+ errorstream << "Invalid anchor element (" << parts.size() << "): '" << element
+ << "'" << std::endl;
}
-void GUIFormSpecMenu::parseElement(parserData* data, std::string element)
+void GUIFormSpecMenu::parseElement(parserData* data, const std::string &element)
{
//some prechecks
if (element == "")
@@ -2005,6 +2005,7 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
m_tooltips.clear();
m_inventory_rings.clear();
m_static_texts.clear();
+ m_dropdowns.clear();
// Set default values (fits old formspec values)
m_bgcolor = video::SColor(140,0,0,0);
@@ -2398,37 +2399,9 @@ void GUIFormSpecMenu::drawList(const ListDrawSpec &s, int phase,
if (!item.name.empty() && tooltip_text.empty())
tooltip_text = utf8_to_wide(item.name);
}
- if (tooltip_text != L"") {
- std::vector<std::wstring> tt_rows = str_split(tooltip_text, L'\n');
- m_tooltip_element->setBackgroundColor(m_default_tooltip_bgcolor);
- m_tooltip_element->setOverrideColor(m_default_tooltip_color);
- m_tooltip_element->setVisible(true);
- this->bringToFront(m_tooltip_element);
- setStaticText(m_tooltip_element, tooltip_text.c_str());
- s32 tooltip_width = m_tooltip_element->getTextWidth() + m_btn_height;
-#if (IRRLICHT_VERSION_MAJOR <= 1 && IRRLICHT_VERSION_MINOR <= 8 && IRRLICHT_VERSION_REVISION < 2) || USE_FREETYPE == 1
- s32 tooltip_height = m_tooltip_element->getTextHeight() * tt_rows.size() + 5;
-#else
- s32 tooltip_height = m_tooltip_element->getTextHeight() + 5;
-#endif
- v2u32 screenSize = driver->getScreenSize();
- int tooltip_offset_x = m_btn_height;
- int tooltip_offset_y = m_btn_height;
-#ifdef __ANDROID__
- tooltip_offset_x *= 3;
- tooltip_offset_y = 0;
- if (m_pointer.X > (s32)screenSize.X / 2)
- tooltip_offset_x = (tooltip_offset_x + tooltip_width) * -1;
-#endif
- s32 tooltip_x = m_pointer.X + tooltip_offset_x;
- s32 tooltip_y = m_pointer.Y + tooltip_offset_y;
- if (tooltip_x + tooltip_width > (s32)screenSize.X)
- tooltip_x = (s32)screenSize.X - tooltip_width - m_btn_height;
- if (tooltip_y + tooltip_height > (s32)screenSize.Y)
- tooltip_y = (s32)screenSize.Y - tooltip_height - m_btn_height;
- m_tooltip_element->setRelativePosition(core::rect<s32>(
- core::position2d<s32>(tooltip_x, tooltip_y),
- core::dimension2d<s32>(tooltip_width, tooltip_height)));
+ if (!tooltip_text.empty()) {
+ showTooltip(tooltip_text, m_default_tooltip_color,
+ m_default_tooltip_bgcolor);
}
}
}
@@ -2658,53 +2631,33 @@ void GUIFormSpecMenu::drawMenu()
if (hovered != NULL) {
s32 id = hovered->getID();
- u32 delta = 0;
+ u64 delta = 0;
if (id == -1) {
m_old_tooltip_id = id;
m_old_tooltip = L"";
} else {
if (id == m_old_tooltip_id) {
- delta = porting::getDeltaMs(m_hovered_time, getTimeMs());
+ delta = porting::getDeltaMs(m_hovered_time, porting::getTimeMs());
} else {
- m_hovered_time = getTimeMs();
+ m_hovered_time = porting::getTimeMs();
m_old_tooltip_id = id;
}
}
+ // Find and update the current tooltip
if (id != -1 && delta >= m_tooltip_show_delay) {
- for(std::vector<FieldSpec>::iterator iter = m_fields.begin();
+ for (std::vector<FieldSpec>::iterator iter = m_fields.begin();
iter != m_fields.end(); ++iter) {
- if (iter->fid == id && m_tooltips[iter->fname].tooltip != L"") {
- if (m_old_tooltip != m_tooltips[iter->fname].tooltip) {
- m_tooltip_element->setBackgroundColor(m_tooltips[iter->fname].bgcolor);
- m_tooltip_element->setOverrideColor(m_tooltips[iter->fname].color);
- m_old_tooltip = m_tooltips[iter->fname].tooltip;
- setStaticText(m_tooltip_element, m_tooltips[iter->fname].tooltip.c_str());
- std::vector<std::wstring> tt_rows = str_split(m_tooltips[iter->fname].tooltip, L'\n');
- s32 tooltip_width = m_tooltip_element->getTextWidth() + m_btn_height;
- s32 tooltip_height = m_tooltip_element->getTextHeight() * tt_rows.size() + 5;
- int tooltip_offset_x = m_btn_height;
- int tooltip_offset_y = m_btn_height;
-#ifdef __ANDROID__
- tooltip_offset_x *= 3;
- tooltip_offset_y = 0;
- if (m_pointer.X > (s32)screenSize.X / 2)
- tooltip_offset_x = (tooltip_offset_x + tooltip_width) * -1;
-#endif
- s32 tooltip_x = m_pointer.X + tooltip_offset_x;
- s32 tooltip_y = m_pointer.Y + tooltip_offset_y;
- if (tooltip_x + tooltip_width > (s32)screenSize.X)
- tooltip_x = (s32)screenSize.X - tooltip_width - m_btn_height;
- if (tooltip_y + tooltip_height > (s32)screenSize.Y)
- tooltip_y = (s32)screenSize.Y - tooltip_height - m_btn_height;
- m_tooltip_element->setRelativePosition(core::rect<s32>(
- core::position2d<s32>(tooltip_x, tooltip_y),
- core::dimension2d<s32>(tooltip_width, tooltip_height)));
- }
- m_tooltip_element->setVisible(true);
- this->bringToFront(m_tooltip_element);
- break;
- }
+
+ if (iter->fid != id)
+ continue;
+
+ const std::wstring &text = m_tooltips[iter->fname].tooltip;
+ if (!text.empty())
+ showTooltip(text, m_tooltips[iter->fname].color,
+ m_tooltips[iter->fname].bgcolor);
+
+ break;
}
}
}
@@ -2719,6 +2672,53 @@ void GUIFormSpecMenu::drawMenu()
skin->setFont(old_font);
}
+
+void GUIFormSpecMenu::showTooltip(const std::wstring &text,
+ const irr::video::SColor &color, const irr::video::SColor &bgcolor)
+{
+ m_tooltip_element->setOverrideColor(color);
+ m_tooltip_element->setBackgroundColor(bgcolor);
+ m_old_tooltip = text;
+ setStaticText(m_tooltip_element, text.c_str());
+
+ // Tooltip size and offset
+ s32 tooltip_width = m_tooltip_element->getTextWidth() + m_btn_height;
+#if (IRRLICHT_VERSION_MAJOR <= 1 && IRRLICHT_VERSION_MINOR <= 8 && IRRLICHT_VERSION_REVISION < 2) || USE_FREETYPE == 1
+ std::vector<std::wstring> text_rows = str_split(text, L'\n');
+ s32 tooltip_height = m_tooltip_element->getTextHeight() * text_rows.size() + 5;
+#else
+ s32 tooltip_height = m_tooltip_element->getTextHeight() + 5;
+#endif
+ v2u32 screenSize = Environment->getVideoDriver()->getScreenSize();
+ int tooltip_offset_x = m_btn_height;
+ int tooltip_offset_y = m_btn_height;
+#ifdef __ANDROID__
+ tooltip_offset_x *= 3;
+ tooltip_offset_y = 0;
+ if (m_pointer.X > (s32)screenSize.X / 2)
+ tooltip_offset_x = -(tooltip_offset_x + tooltip_width);
+#endif
+
+ // Calculate and set the tooltip position
+ s32 tooltip_x = m_pointer.X + tooltip_offset_x;
+ s32 tooltip_y = m_pointer.Y + tooltip_offset_y;
+ if (tooltip_x + tooltip_width > (s32)screenSize.X)
+ tooltip_x = (s32)screenSize.X - tooltip_width - m_btn_height;
+ if (tooltip_y + tooltip_height > (s32)screenSize.Y)
+ tooltip_y = (s32)screenSize.Y - tooltip_height - m_btn_height;
+
+ m_tooltip_element->setRelativePosition(
+ core::rect<s32>(
+ core::position2d<s32>(tooltip_x, tooltip_y),
+ core::dimension2d<s32>(tooltip_width, tooltip_height)
+ )
+ );
+
+ // Display the tooltip
+ m_tooltip_element->setVisible(true);
+ bringToFront(m_tooltip_element);
+}
+
void GUIFormSpecMenu::updateSelectedItem()
{
// If the selected stack has become empty for some reason, deselect it.
@@ -3244,10 +3244,10 @@ bool GUIFormSpecMenu::DoubleClickDetection(const SEvent event)
m_doubleclickdetect[0].time = m_doubleclickdetect[1].time;
m_doubleclickdetect[1].pos = m_pointer;
- m_doubleclickdetect[1].time = getTimeMs();
+ m_doubleclickdetect[1].time = porting::getTimeMs();
}
else if (event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP) {
- u32 delta = porting::getDeltaMs(m_doubleclickdetect[0].time, getTimeMs());
+ u64 delta = porting::getDeltaMs(m_doubleclickdetect[0].time, porting::getTimeMs());
if (delta > 400) {
return false;
}
@@ -3296,8 +3296,9 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
{
if (event.EventType==EET_KEY_INPUT_EVENT) {
KeyPress kp(event.KeyInput);
- if (event.KeyInput.PressedDown && ( (kp == EscapeKey) ||
- (kp == getKeySetting("keymap_inventory")) || (kp == CancelKey))) {
+ if (event.KeyInput.PressedDown && (
+ (kp == EscapeKey) || (kp == CancelKey) ||
+ ((m_client != NULL) && (kp == getKeySetting("keymap_inventory"))))) {
tryClose();
return true;
} else if (m_client != NULL && event.KeyInput.PressedDown &&
@@ -3680,18 +3681,24 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
a->from_i = m_selected_item->i;
m_invmgr->inventoryAction(a);
} else if (craft_amount > 0) {
- m_selected_content_guess = ItemStack(); // Clear
-
- // Send IACTION_CRAFT
-
assert(s.isValid());
- assert(inv_s);
-
- 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 there are no items selected or the selected item
+ // belongs to craftresult list, proceed with crafting
+ if (m_selected_item == NULL ||
+ !m_selected_item->isValid() || m_selected_item->listname == "craftresult") {
+
+ m_selected_content_guess = ItemStack(); // Clear
+
+ assert(inv_s);
+
+ // 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
diff --git a/src/guiFormSpecMenu.h b/src/guiFormSpecMenu.h
index f4383e987..9eaf60ac6 100644
--- a/src/guiFormSpecMenu.h
+++ b/src/guiFormSpecMenu.h
@@ -55,12 +55,10 @@ typedef enum {
struct TextDest
{
- virtual ~TextDest() {};
+ virtual ~TextDest() {}
// This is deprecated I guess? -celeron55
- virtual void gotText(std::wstring text){}
+ virtual void gotText(const std::wstring &text) {}
virtual void gotText(const StringMap &fields) = 0;
- virtual void setFormName(std::string formname)
- { m_formname = formname;};
std::string m_formname;
};
@@ -71,30 +69,29 @@ public:
virtual ~IFormSource(){}
virtual std::string getForm() = 0;
// Fill in variables in field text
- virtual std::string resolveText(std::string str){ return str; }
+ virtual std::string resolveText(const std::string &str) { return str; }
};
class GUIFormSpecMenu : public GUIModalMenu
{
struct ItemSpec
{
- ItemSpec()
+ ItemSpec() :
+ i(-1)
{
- i = -1;
}
+
ItemSpec(const InventoryLocation &a_inventoryloc,
const std::string &a_listname,
- s32 a_i)
- {
- inventoryloc = a_inventoryloc;
- listname = a_listname;
- i = a_i;
- }
- bool isValid() const
+ s32 a_i) :
+ inventoryloc(a_inventoryloc),
+ listname(a_listname),
+ i(a_i)
{
- return i != -1;
}
+ bool isValid() const { return i != -1; }
+
InventoryLocation inventoryloc;
std::string listname;
s32 i;
@@ -144,7 +141,8 @@ class GUIFormSpecMenu : public GUIModalMenu
ImageDrawSpec():
parent_button(NULL),
clip(false)
- {}
+ {
+ }
ImageDrawSpec(const std::string &a_name,
const std::string &a_item_name,
@@ -157,7 +155,8 @@ class GUIFormSpecMenu : public GUIModalMenu
geom(a_geom),
scale(true),
clip(false)
- {}
+ {
+ }
ImageDrawSpec(const std::string &a_name,
const std::string &a_item_name,
@@ -169,7 +168,8 @@ class GUIFormSpecMenu : public GUIModalMenu
geom(a_geom),
scale(true),
clip(false)
- {}
+ {
+ }
ImageDrawSpec(const std::string &a_name,
const v2s32 &a_pos, const v2s32 &a_geom, bool clip=false):
@@ -179,7 +179,8 @@ class GUIFormSpecMenu : public GUIModalMenu
geom(a_geom),
scale(true),
clip(clip)
- {}
+ {
+ }
ImageDrawSpec(const std::string &a_name,
const v2s32 &a_pos):
@@ -188,7 +189,8 @@ class GUIFormSpecMenu : public GUIModalMenu
pos(a_pos),
scale(false),
clip(false)
- {}
+ {
+ }
std::string name;
std::string item_name;
@@ -208,14 +210,14 @@ class GUIFormSpecMenu : public GUIModalMenu
const std::wstring &default_text, int id) :
fname(name),
flabel(label),
+ fdefault(unescape_enriched(default_text)),
fid(id),
send(false),
ftype(f_Unknown),
is_exit(false)
{
- //flabel = unescape_enriched(label);
- fdefault = unescape_enriched(default_text);
}
+
std::string fname;
std::wstring flabel;
std::wstring fdefault;
@@ -226,7 +228,8 @@ class GUIFormSpecMenu : public GUIModalMenu
core::rect<s32> rect;
};
- struct BoxDrawSpec {
+ struct BoxDrawSpec
+ {
BoxDrawSpec(v2s32 a_pos, v2s32 a_geom,irr::video::SColor a_color):
pos(a_pos),
geom(a_geom),
@@ -238,45 +241,46 @@ class GUIFormSpecMenu : public GUIModalMenu
irr::video::SColor color;
};
- struct TooltipSpec {
- TooltipSpec()
- {
- }
- TooltipSpec(std::string a_tooltip, irr::video::SColor a_bgcolor,
+ struct TooltipSpec
+ {
+ TooltipSpec() {}
+ TooltipSpec(const std::string &a_tooltip, irr::video::SColor a_bgcolor,
irr::video::SColor a_color):
+ tooltip(utf8_to_wide(a_tooltip)),
bgcolor(a_bgcolor),
color(a_color)
{
- //tooltip = unescape_enriched(utf8_to_wide(a_tooltip));
- tooltip = utf8_to_wide(a_tooltip);
}
+
std::wstring tooltip;
irr::video::SColor bgcolor;
irr::video::SColor color;
};
- struct StaticTextSpec {
+ struct StaticTextSpec
+ {
StaticTextSpec():
parent_button(NULL)
{
}
+
StaticTextSpec(const std::wstring &a_text,
const core::rect<s32> &a_rect):
+ text(a_text),
rect(a_rect),
parent_button(NULL)
{
- //text = unescape_enriched(a_text);
- text = a_text;
}
+
StaticTextSpec(const std::wstring &a_text,
const core::rect<s32> &a_rect,
gui::IGUIButton *a_parent_button):
+ text(a_text),
rect(a_rect),
parent_button(a_parent_button)
{
- //text = unescape_enriched(a_text);
- text = a_text;
}
+
std::wstring text;
core::rect<s32> rect;
gui::IGUIButton *parent_button;
@@ -296,7 +300,7 @@ public:
~GUIFormSpecMenu();
void setFormSpec(const std::string &formspec_string,
- InventoryLocation current_inventory_location)
+ const InventoryLocation &current_inventory_location)
{
m_formspec_string = formspec_string;
m_current_inventory_location = current_inventory_location;
@@ -416,8 +420,8 @@ protected:
v2s32 m_old_pointer; // Mouse position after previous mouse event
gui::IGUIStaticText *m_tooltip_element;
- u32 m_tooltip_show_delay;
- s32 m_hovered_time;
+ u64 m_tooltip_show_delay;
+ u64 m_hovered_time;
s32 m_old_tooltip_id;
std::wstring m_old_tooltip;
@@ -469,41 +473,43 @@ private:
fs_key_pendig current_keys_pending;
std::string current_field_enter_pending;
- void parseElement(parserData* data, std::string element);
+ void parseElement(parserData* data, const std::string &element);
- void parseSize(parserData* data, std::string element);
- void parseContainer(parserData* data, std::string element);
+ void parseSize(parserData* data, const std::string &element);
+ void parseContainer(parserData* data, const std::string &element);
void parseContainerEnd(parserData* data);
- void parseList(parserData* data, std::string element);
- void parseListRing(parserData* data, std::string element);
- void parseCheckbox(parserData* data, std::string element);
- void parseImage(parserData* data, std::string element);
- void parseItemImage(parserData* data,std::string element);
- void parseButton(parserData* data,std::string element,std::string typ);
- void parseBackground(parserData* data,std::string element);
- void parseTableOptions(parserData* data,std::string element);
- void parseTableColumns(parserData* data,std::string element);
- void parseTable(parserData* data,std::string element);
- void parseTextList(parserData* data,std::string element);
- void parseDropDown(parserData* data,std::string element);
+ void parseList(parserData* data, const std::string &element);
+ void parseListRing(parserData* data, const std::string &element);
+ void parseCheckbox(parserData* data, const std::string &element);
+ void parseImage(parserData* data, const std::string &element);
+ void parseItemImage(parserData* data, const std::string &element);
+ void parseButton(parserData* data, const std::string &element,
+ const std::string &typ);
+ void parseBackground(parserData* data, const std::string &element);
+ void parseTableOptions(parserData* data, const std::string &element);
+ void parseTableColumns(parserData* data, const std::string &element);
+ void parseTable(parserData* data, const std::string &element);
+ void parseTextList(parserData* data, const std::string &element);
+ void parseDropDown(parserData* data, const std::string &element);
void parseFieldCloseOnEnter(parserData *data, const std::string &element);
- void parsePwdField(parserData* data,std::string element);
- void parseField(parserData* data,std::string element,std::string type);
+ void parsePwdField(parserData* data, const std::string &element);
+ void parseField(parserData* data, const std::string &element, const std::string &type);
void parseSimpleField(parserData* data,std::vector<std::string> &parts);
void parseTextArea(parserData* data,std::vector<std::string>& parts,
- std::string type);
- void parseLabel(parserData* data,std::string element);
- void parseVertLabel(parserData* data,std::string element);
- void parseImageButton(parserData* data,std::string element,std::string type);
- void parseItemImageButton(parserData* data,std::string element);
- void parseTabHeader(parserData* data,std::string element);
- void parseBox(parserData* data,std::string element);
- void parseBackgroundColor(parserData* data,std::string element);
- void parseListColors(parserData* data,std::string element);
- void parseTooltip(parserData* data,std::string element);
- bool parseVersionDirect(std::string data);
- bool parseSizeDirect(parserData* data, std::string element);
- void parseScrollBar(parserData* data, std::string element);
+ const std::string &type);
+ void parseLabel(parserData* data, const std::string &element);
+ void parseVertLabel(parserData* data, const std::string &element);
+ void parseImageButton(parserData* data, const std::string &element,
+ const std::string &type);
+ void parseItemImageButton(parserData* data, const std::string &element);
+ void parseTabHeader(parserData* data, const std::string &element);
+ void parseBox(parserData* data, const std::string &element);
+ void parseBackgroundColor(parserData* data, const std::string &element);
+ void parseListColors(parserData* data, const std::string &element);
+ void parseTooltip(parserData* data, const std::string &element);
+ bool parseVersionDirect(const std::string &data);
+ bool parseSizeDirect(parserData* data, const std::string &element);
+ void parseScrollBar(parserData* data, const std::string &element);
bool parsePositionDirect(parserData *data, const std::string &element);
void parsePosition(parserData *data, const std::string &element);
bool parseAnchorDirect(parserData *data, const std::string &element);
@@ -511,6 +517,9 @@ private:
void tryClose();
+ void showTooltip(const std::wstring &text, const irr::video::SColor &color,
+ const irr::video::SColor &bgcolor);
+
/**
* check if event is part of a double click
* @param event event to evaluate
@@ -521,7 +530,7 @@ private:
struct clickpos
{
v2s32 pos;
- s32 time;
+ s64 time;
};
clickpos m_doubleclickdetect[2];
@@ -548,23 +557,22 @@ private:
class FormspecFormSource: public IFormSource
{
public:
- FormspecFormSource(const std::string &formspec)
+ FormspecFormSource(const std::string &formspec):
+ m_formspec(formspec)
{
- m_formspec = formspec;
}
~FormspecFormSource()
- {}
-
- void setForm(const std::string &formspec) {
- m_formspec = FORMSPEC_VERSION_STRING + formspec;
+ {
}
- std::string getForm()
+ void setForm(const std::string &formspec)
{
- return m_formspec;
+ m_formspec = FORMSPEC_VERSION_STRING + formspec;
}
+ std::string getForm() { return m_formspec; }
+
std::string m_formspec;
};
diff --git a/src/guiKeyChangeMenu.cpp b/src/guiKeyChangeMenu.cpp
index e85ee8271..ae53c56f9 100644
--- a/src/guiKeyChangeMenu.cpp
+++ b/src/guiKeyChangeMenu.cpp
@@ -58,6 +58,11 @@ enum
GUI_ID_KEY_SNEAK_BUTTON,
GUI_ID_KEY_DROP_BUTTON,
GUI_ID_KEY_INVENTORY_BUTTON,
+ GUI_ID_KEY_HOTBAR_PREV_BUTTON,
+ GUI_ID_KEY_HOTBAR_NEXT_BUTTON,
+ GUI_ID_KEY_MUTE_BUTTON,
+ GUI_ID_KEY_DEC_VOLUME_BUTTON,
+ GUI_ID_KEY_INC_VOLUME_BUTTON,
GUI_ID_KEY_DUMP_BUTTON,
GUI_ID_KEY_RANGE_BUTTON,
GUI_ID_KEY_ZOOM_BUTTON,
@@ -109,7 +114,7 @@ void GUIKeyChangeMenu::removeChildren()
void GUIKeyChangeMenu::regenerateGui(v2u32 screensize)
{
removeChildren();
- v2s32 size(620, 430);
+ v2s32 size(745, 430);
core::rect < s32 > rect(screensize.X / 2 - size.X / 2,
screensize.Y / 2 - size.Y / 2, screensize.X / 2 + size.X / 2,
@@ -146,15 +151,17 @@ void GUIKeyChangeMenu::regenerateGui(v2u32 screensize)
{
core::rect < s32 > rect(0, 0, 100, 30);
- rect += topleft + v2s32(offset.X + 115, offset.Y - 5);
+ rect += topleft + v2s32(offset.X + 120, offset.Y - 5);
const wchar_t *text = wgettext(k->key.name());
k->button = Environment->addButton(rect, this, k->id, text);
delete[] text;
}
- if(i + 1 == KMaxButtonPerColumns)
- offset = v2s32(260, 60);
- else
+ if ((i + 1) % KMaxButtonPerColumns == 0) {
+ offset.X += 230;
+ offset.Y = 60;
+ } else {
offset += v2s32(0, 25);
+ }
}
{
@@ -215,7 +222,7 @@ void GUIKeyChangeMenu::drawMenu()
video::SColor bgcolor(140, 0, 0, 0);
{
- core::rect < s32 > rect(0, 0, 620, 620);
+ core::rect < s32 > rect(0, 0, 745, 620);
rect += AbsoluteRect.UpperLeftCorner;
driver->draw2DRectangle(bgcolor, rect, &AbsoluteClippingRect);
}
@@ -407,6 +414,11 @@ void GUIKeyChangeMenu::init_keys()
this->add_key(GUI_ID_KEY_SNEAK_BUTTON, wgettext("Sneak"), "keymap_sneak");
this->add_key(GUI_ID_KEY_DROP_BUTTON, wgettext("Drop"), "keymap_drop");
this->add_key(GUI_ID_KEY_INVENTORY_BUTTON, wgettext("Inventory"), "keymap_inventory");
+ this->add_key(GUI_ID_KEY_HOTBAR_PREV_BUTTON,wgettext("Prev. item"), "keymap_hotbar_previous");
+ this->add_key(GUI_ID_KEY_HOTBAR_NEXT_BUTTON,wgettext("Next item"), "keymap_hotbar_next");
+ this->add_key(GUI_ID_KEY_MUTE_BUTTON, wgettext("Mute"), "keymap_mute");
+ this->add_key(GUI_ID_KEY_DEC_VOLUME_BUTTON,wgettext("Dec. volume"), "keymap_decrease_volume");
+ this->add_key(GUI_ID_KEY_INC_VOLUME_BUTTON,wgettext("Inc. volume"), "keymap_increase_volume");
this->add_key(GUI_ID_KEY_CHAT_BUTTON, wgettext("Chat"), "keymap_chat");
this->add_key(GUI_ID_KEY_CMD_BUTTON, wgettext("Command"), "keymap_cmd");
this->add_key(GUI_ID_KEY_CMD_LOCAL_BUTTON, wgettext("Local command"), "keymap_cmd_local");
diff --git a/src/guiKeyChangeMenu.h b/src/guiKeyChangeMenu.h
index 19a07620d..1aa400632 100644
--- a/src/guiKeyChangeMenu.h
+++ b/src/guiKeyChangeMenu.h
@@ -30,7 +30,8 @@
#include <string>
#include <vector>
-struct key_setting {
+struct key_setting
+{
int id;
const wchar_t *button_name;
KeyPress key;
@@ -38,12 +39,11 @@ struct key_setting {
gui::IGUIButton *button;
};
-
-class GUIKeyChangeMenu: public GUIModalMenu
+class GUIKeyChangeMenu : public GUIModalMenu
{
public:
- GUIKeyChangeMenu(gui::IGUIEnvironment* env, gui::IGUIElement* parent,
- s32 id, IMenuManager *menumgr);
+ GUIKeyChangeMenu(gui::IGUIEnvironment *env, gui::IGUIElement *parent, s32 id,
+ IMenuManager *menumgr);
~GUIKeyChangeMenu();
void removeChildren();
@@ -56,10 +56,9 @@ public:
bool acceptInput();
- bool OnEvent(const SEvent& event);
+ bool OnEvent(const SEvent &event);
private:
-
void init_keys();
bool resetMenu();
@@ -67,13 +66,12 @@ private:
void add_key(int id, const wchar_t *button_name, const std::string &setting_name);
bool shift_down;
-
+
s32 activeKey;
-
+
std::vector<KeyPress> key_used;
gui::IGUIStaticText *key_used_text;
std::vector<key_setting *> key_settings;
};
#endif
-
diff --git a/src/guiPasswordChange.cpp b/src/guiPasswordChange.cpp
index e2f9994be..c8eb36e5f 100644
--- a/src/guiPasswordChange.cpp
+++ b/src/guiPasswordChange.cpp
@@ -33,6 +33,7 @@ const int ID_newPassword1 = 257;
const int ID_newPassword2 = 258;
const int ID_change = 259;
const int ID_message = 260;
+const int ID_cancel = 261;
GUIPasswordChange::GUIPasswordChange(gui::IGUIEnvironment* env,
gui::IGUIElement* parent, s32 id,
@@ -40,7 +41,10 @@ GUIPasswordChange::GUIPasswordChange(gui::IGUIEnvironment* env,
Client* client
):
GUIModalMenu(env, parent, id, menumgr),
- m_client(client)
+ m_client(client),
+ m_oldpass(L""),
+ m_newpass(L""),
+ m_newpass_confirm(L"")
{
}
@@ -51,31 +55,25 @@ GUIPasswordChange::~GUIPasswordChange()
void GUIPasswordChange::removeChildren()
{
- {
- gui::IGUIElement *e = getElementFromId(ID_oldPassword);
- if(e != NULL)
- e->remove();
- }
- {
- gui::IGUIElement *e = getElementFromId(ID_newPassword1);
- if(e != NULL)
- e->remove();
+ const core::list<gui::IGUIElement *> &children = getChildren();
+ core::list<gui::IGUIElement *> children_copy;
+ for (core::list<gui::IGUIElement *>::ConstIterator i = children.begin();
+ i != children.end(); i++) {
+ children_copy.push_back(*i);
}
- {
- gui::IGUIElement *e = getElementFromId(ID_newPassword2);
- if(e != NULL)
- e->remove();
- }
- {
- gui::IGUIElement *e = getElementFromId(ID_change);
- if(e != NULL)
- e->remove();
+ for (core::list<gui::IGUIElement *>::Iterator i = children_copy.begin();
+ i != children_copy.end(); i++) {
+ (*i)->remove();
}
}
-
void GUIPasswordChange::regenerateGui(v2u32 screensize)
{
/*
+ save current input
+ */
+ acceptInput();
+
+ /*
Remove stuff
*/
removeChildren();
@@ -104,7 +102,7 @@ void GUIPasswordChange::regenerateGui(v2u32 screensize)
s32 ypos = 50;
{
core::rect<s32> rect(0, 0, 150, 20);
- rect += topleft_client + v2s32(25, ypos+6);
+ rect += topleft_client + v2s32(25, ypos + 6);
text = wgettext("Old Password");
Environment->addStaticText(text, rect, false, true, this, -1);
delete[] text;
@@ -112,15 +110,15 @@ void GUIPasswordChange::regenerateGui(v2u32 screensize)
{
core::rect<s32> rect(0, 0, 230, 30);
rect += topleft_client + v2s32(160, ypos);
- gui::IGUIEditBox *e =
- Environment->addEditBox(L"", rect, true, this, ID_oldPassword);
+ gui::IGUIEditBox *e = Environment->addEditBox(
+ m_oldpass.c_str(), rect, true, this, ID_oldPassword);
Environment->setFocus(e);
e->setPasswordBox(true);
}
ypos += 50;
{
core::rect<s32> rect(0, 0, 150, 20);
- rect += topleft_client + v2s32(25, ypos+6);
+ rect += topleft_client + v2s32(25, ypos + 6);
text = wgettext("New Password");
Environment->addStaticText(text, rect, false, true, this, -1);
delete[] text;
@@ -128,14 +126,14 @@ void GUIPasswordChange::regenerateGui(v2u32 screensize)
{
core::rect<s32> rect(0, 0, 230, 30);
rect += topleft_client + v2s32(160, ypos);
- gui::IGUIEditBox *e =
- Environment->addEditBox(L"", rect, true, this, ID_newPassword1);
+ gui::IGUIEditBox *e = Environment->addEditBox(
+ m_newpass.c_str(), rect, true, this, ID_newPassword1);
e->setPasswordBox(true);
}
ypos += 50;
{
core::rect<s32> rect(0, 0, 150, 20);
- rect += topleft_client + v2s32(25, ypos+6);
+ rect += topleft_client + v2s32(25, ypos + 6);
text = wgettext("Confirm Password");
Environment->addStaticText(text, rect, false, true, this, -1);
delete[] text;
@@ -143,19 +141,26 @@ void GUIPasswordChange::regenerateGui(v2u32 screensize)
{
core::rect<s32> rect(0, 0, 230, 30);
rect += topleft_client + v2s32(160, ypos);
- gui::IGUIEditBox *e =
- Environment->addEditBox(L"", rect, true, this, ID_newPassword2);
+ gui::IGUIEditBox *e = Environment->addEditBox(
+ m_newpass_confirm.c_str(), rect, true, this, ID_newPassword2);
e->setPasswordBox(true);
}
ypos += 50;
{
- core::rect<s32> rect(0, 0, 140, 30);
- rect = rect + v2s32(size.X/2-140/2, ypos);
+ core::rect<s32> rect(0, 0, 100, 30);
+ rect = rect + v2s32(size.X / 4 + 56, ypos);
text = wgettext("Change");
Environment->addButton(rect, this, ID_change, text);
delete[] text;
}
+ {
+ core::rect<s32> rect(0, 0, 100, 30);
+ rect = rect + v2s32(size.X / 4 + 185, ypos);
+ text = wgettext("Cancel");
+ Environment->addButton(rect, this, ID_cancel, text);
+ delete[] text;
+ }
ypos += 50;
{
@@ -163,9 +168,8 @@ void GUIPasswordChange::regenerateGui(v2u32 screensize)
rect += topleft_client + v2s32(35, ypos);
text = wgettext("Passwords do not match!");
IGUIElement *e =
- Environment->addStaticText(
- text,
- rect, false, true, this, ID_message);
+ Environment->addStaticText(
+ text, rect, false, true, this, ID_message);
e->setVisible(false);
delete[] text;
}
@@ -173,88 +177,86 @@ void GUIPasswordChange::regenerateGui(v2u32 screensize)
void GUIPasswordChange::drawMenu()
{
- gui::IGUISkin* skin = Environment->getSkin();
+ gui::IGUISkin *skin = Environment->getSkin();
if (!skin)
return;
- video::IVideoDriver* driver = Environment->getVideoDriver();
+ video::IVideoDriver *driver = Environment->getVideoDriver();
- video::SColor bgcolor(140,0,0,0);
+ video::SColor bgcolor(140, 0, 0, 0);
driver->draw2DRectangle(bgcolor, AbsoluteRect, &AbsoluteClippingRect);
gui::IGUIElement::draw();
}
-bool GUIPasswordChange::acceptInput()
+void GUIPasswordChange::acceptInput()
{
- std::wstring oldpass;
- std::wstring newpass;
- gui::IGUIElement *e;
- e = getElementFromId(ID_oldPassword);
- if(e != NULL)
- oldpass = e->getText();
- e = getElementFromId(ID_newPassword1);
- if(e != NULL)
- newpass = e->getText();
- e = getElementFromId(ID_newPassword2);
- if(e != NULL && newpass != e->getText())
- {
- e = getElementFromId(ID_message);
- if(e != NULL)
- e->setVisible(true);
- return false;
- }
- m_client->sendChangePassword(wide_to_utf8(oldpass),
- wide_to_utf8(newpass));
- return true;
+ gui::IGUIElement *e;
+ e = getElementFromId(ID_oldPassword);
+ if (e != NULL)
+ m_oldpass = e->getText();
+ e = getElementFromId(ID_newPassword1);
+ if (e != NULL)
+ m_newpass = e->getText();
+ e = getElementFromId(ID_newPassword2);
+ if (e != NULL)
+ m_newpass_confirm = e->getText();
}
-bool GUIPasswordChange::OnEvent(const SEvent& event)
+bool GUIPasswordChange::processInput()
{
- if(event.EventType==EET_KEY_INPUT_EVENT)
- {
- if(event.KeyInput.Key==KEY_ESCAPE && event.KeyInput.PressedDown)
- {
+ if (m_newpass != m_newpass_confirm) {
+ gui::IGUIElement *e = getElementFromId(ID_message);
+ if (e != NULL)
+ e->setVisible(true);
+ return false;
+ }
+ m_client->sendChangePassword(wide_to_utf8(m_oldpass), wide_to_utf8(m_newpass));
+ return true;
+}
+
+bool GUIPasswordChange::OnEvent(const SEvent &event)
+{
+ if (event.EventType == EET_KEY_INPUT_EVENT) {
+ if (event.KeyInput.Key == KEY_ESCAPE && event.KeyInput.PressedDown) {
quitMenu();
return true;
}
- if(event.KeyInput.Key==KEY_RETURN && event.KeyInput.PressedDown)
- {
- if(acceptInput())
+ if (event.KeyInput.Key == KEY_RETURN && event.KeyInput.PressedDown) {
+ acceptInput();
+ if (processInput())
quitMenu();
return true;
}
}
- if(event.EventType==EET_GUI_EVENT)
- {
- if(event.GUIEvent.EventType==gui::EGET_ELEMENT_FOCUS_LOST
- && isVisible())
- {
- if(!canTakeFocus(event.GUIEvent.Element))
- {
- dstream<<"GUIPasswordChange: Not allowing focus change."
- <<std::endl;
+ if (event.EventType == EET_GUI_EVENT) {
+ if (event.GUIEvent.EventType == gui::EGET_ELEMENT_FOCUS_LOST &&
+ isVisible()) {
+ if (!canTakeFocus(event.GUIEvent.Element)) {
+ dstream << "GUIPasswordChange: Not allowing focus change."
+ << std::endl;
// Returning true disables focus change
return true;
}
}
- if(event.GUIEvent.EventType==gui::EGET_BUTTON_CLICKED)
- {
- switch(event.GUIEvent.Caller->getID())
- {
+ if (event.GUIEvent.EventType == gui::EGET_BUTTON_CLICKED) {
+ switch (event.GUIEvent.Caller->getID()) {
case ID_change:
- if(acceptInput())
+ acceptInput();
+ if (processInput())
quitMenu();
return true;
+ case ID_cancel:
+ quitMenu();
+ return true;
}
}
- if(event.GUIEvent.EventType==gui::EGET_EDITBOX_ENTER)
- {
- switch(event.GUIEvent.Caller->getID())
- {
+ if (event.GUIEvent.EventType == gui::EGET_EDITBOX_ENTER) {
+ switch (event.GUIEvent.Caller->getID()) {
case ID_oldPassword:
case ID_newPassword1:
case ID_newPassword2:
- if(acceptInput())
+ acceptInput();
+ if (processInput())
quitMenu();
return true;
}
@@ -263,4 +265,3 @@ bool GUIPasswordChange::OnEvent(const SEvent& event)
return Parent ? Parent->OnEvent(event) : false;
}
-
diff --git a/src/guiPasswordChange.h b/src/guiPasswordChange.h
index aecc7076f..9680ef13d 100644
--- a/src/guiPasswordChange.h
+++ b/src/guiPasswordChange.h
@@ -27,12 +27,10 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
class GUIPasswordChange : public GUIModalMenu
{
public:
- GUIPasswordChange(gui::IGUIEnvironment* env,
- gui::IGUIElement* parent, s32 id,
- IMenuManager *menumgr,
- Client* client);
+ GUIPasswordChange(gui::IGUIEnvironment *env, gui::IGUIElement *parent, s32 id,
+ IMenuManager *menumgr, Client *client);
~GUIPasswordChange();
-
+
void removeChildren();
/*
Remove and re-add (or reposition) stuff
@@ -41,14 +39,17 @@ public:
void drawMenu();
- bool acceptInput();
+ void acceptInput();
- bool OnEvent(const SEvent& event);
-
-private:
- Client* m_client;
+ bool processInput();
+
+ bool OnEvent(const SEvent &event);
+private:
+ Client *m_client;
+ std::wstring m_oldpass;
+ std::wstring m_newpass;
+ std::wstring m_newpass_confirm;
};
#endif
-
diff --git a/src/guiTable.cpp b/src/guiTable.cpp
index 6b33b8266..d223e3069 100644
--- a/src/guiTable.cpp
+++ b/src/guiTable.cpp
@@ -828,7 +828,7 @@ bool GUITable::OnEvent(const SEvent &event)
}
else if (event.KeyInput.PressedDown && event.KeyInput.Char) {
// change selection based on text as it is typed
- s32 now = getTimeMs();
+ u64 now = porting::getTimeMs();
if (now - m_keynav_time >= 500)
m_keynav_buffer = L"";
m_keynav_time = now;
diff --git a/src/guiTable.h b/src/guiTable.h
index 4d5b39166..02e8af00b 100644
--- a/src/guiTable.h
+++ b/src/guiTable.h
@@ -74,11 +74,10 @@ public:
std::string name;
std::string value;
- Option(const std::string &name_, const std::string &value_)
- {
- name = name_;
- value = value_;
- }
+ Option(const std::string &name_, const std::string &value_) :
+ name(name_),
+ value(value_)
+ {}
};
/*
@@ -197,7 +196,7 @@ protected:
bool m_sel_doubleclick;
// Keyboard navigation stuff
- s32 m_keynav_time;
+ u64 m_keynav_time;
core::stringw m_keynav_buffer;
// Drawing and geometry information
diff --git a/src/guiVolumeChange.cpp b/src/guiVolumeChange.cpp
index 8425bc04f..c7868ad35 100644
--- a/src/guiVolumeChange.cpp
+++ b/src/guiVolumeChange.cpp
@@ -30,10 +30,9 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include "gettext.h"
-const int ID_soundText1 = 263;
-const int ID_soundText2 = 264;
-const int ID_soundExitButton = 265;
-const int ID_soundSlider = 266;
+const int ID_soundText = 263;
+const int ID_soundExitButton = 264;
+const int ID_soundSlider = 265;
GUIVolumeChange::GUIVolumeChange(gui::IGUIEnvironment* env,
gui::IGUIElement* parent, s32 id,
@@ -50,10 +49,7 @@ GUIVolumeChange::~GUIVolumeChange()
void GUIVolumeChange::removeChildren()
{
- if (gui::IGUIElement *e = getElementFromId(ID_soundText1))
- e->remove();
-
- if (gui::IGUIElement *e = getElementFromId(ID_soundText2))
+ if (gui::IGUIElement *e = getElementFromId(ID_soundText))
e->remove();
if (gui::IGUIElement *e = getElementFromId(ID_soundExitButton))
@@ -69,39 +65,35 @@ void GUIVolumeChange::regenerateGui(v2u32 screensize)
Remove stuff
*/
removeChildren();
-
+
/*
Calculate new sizes and positions
*/
- core::rect<s32> rect(
- screensize.X/2 - 380/2,
- screensize.Y/2 - 200/2,
- screensize.X/2 + 380/2,
- screensize.Y/2 + 200/2
+ DesiredRect = core::rect<s32>(
+ screensize.X/2 - 380/2,
+ screensize.Y/2 - 200/2,
+ screensize.X/2 + 380/2,
+ screensize.Y/2 + 200/2
);
-
- DesiredRect = rect;
recalculateAbsolutePosition(false);
- v2s32 size = rect.getSize();
- v2s32 topleft_client(40, 0);
- int volume = (int)(g_settings->getFloat("sound_volume")*100);
+ v2s32 size = DesiredRect.getSize();
+ int volume = (int)(g_settings->getFloat("sound_volume") * 100);
+
/*
Add stuff
*/
{
- core::rect<s32> rect(0, 0, 120, 20);
- rect = rect + v2s32(size.X/2-60, size.Y/2-35);
+ core::rect<s32> rect(0, 0, 160, 20);
+ rect = rect + v2s32(size.X / 2 - 80, size.Y / 2 - 35);
+
const wchar_t *text = wgettext("Sound Volume: ");
- Environment->addStaticText(text, rect, false,
- true, this, ID_soundText1);
- delete[] text;
- }
- {
- core::rect<s32> rect(0, 0, 30, 20);
- rect = rect + v2s32(size.X/2+40, size.Y/2-35);
- Environment->addStaticText(core::stringw(volume).c_str(), rect, false,
- true, this, ID_soundText2);
+ core::stringw volume_text = text;
+ delete [] text;
+
+ volume_text += core::stringw(volume) + core::stringw("%");
+ Environment->addStaticText(volume_text.c_str(), rect, false,
+ true, this, ID_soundText);
}
{
core::rect<s32> rect(0, 0, 80, 30);
@@ -156,10 +148,15 @@ bool GUIVolumeChange::OnEvent(const SEvent& event)
if (event.GUIEvent.EventType == gui::EGET_SCROLL_BAR_CHANGED) {
if (event.GUIEvent.Caller->getID() == ID_soundSlider) {
s32 pos = ((gui::IGUIScrollBar*)event.GUIEvent.Caller)->getPos();
- g_settings->setFloat("sound_volume", (float)pos/100);
+ g_settings->setFloat("sound_volume", (float) pos / 100);
+
+ gui::IGUIElement *e = getElementFromId(ID_soundText);
+ const wchar_t *text = wgettext("Sound Volume: ");
+ core::stringw volume_text = text;
+ delete [] text;
- gui::IGUIElement *e = getElementFromId(ID_soundText2);
- e->setText(core::stringw(pos).c_str());
+ volume_text += core::stringw(pos) + core::stringw("%");
+ e->setText(volume_text.c_str());
return true;
}
}
diff --git a/src/httpfetch.cpp b/src/httpfetch.cpp
index f64c9f717..f7d20100d 100644
--- a/src/httpfetch.cpp
+++ b/src/httpfetch.cpp
@@ -40,16 +40,15 @@ Mutex g_httpfetch_mutex;
std::map<unsigned long, std::queue<HTTPFetchResult> > g_httpfetch_results;
PcgRandom g_callerid_randomness;
-HTTPFetchRequest::HTTPFetchRequest()
+HTTPFetchRequest::HTTPFetchRequest() :
+ url(""),
+ caller(HTTPFETCH_DISCARD),
+ request_id(0),
+ timeout(g_settings->getS32("curl_timeout")),
+ connect_timeout(timeout),
+ multipart(false),
+ useragent(std::string(PROJECT_NAME_C "/") + g_version_hash + " (" + porting::get_sysinfo() + ")")
{
- url = "";
- caller = HTTPFETCH_DISCARD;
- request_id = 0;
- timeout = g_settings->getS32("curl_timeout");
- connect_timeout = timeout;
- multipart = false;
-
- useragent = std::string(PROJECT_NAME_C "/") + g_version_hash + " (" + porting::get_sysinfo() + ")";
}
@@ -207,7 +206,7 @@ public:
class HTTPFetchOngoing
{
public:
- HTTPFetchOngoing(HTTPFetchRequest request, CurlHandlePool *pool);
+ HTTPFetchOngoing(const HTTPFetchRequest &request, CurlHandlePool *pool);
~HTTPFetchOngoing();
CURLcode start(CURLM *multi);
@@ -228,7 +227,8 @@ private:
};
-HTTPFetchOngoing::HTTPFetchOngoing(HTTPFetchRequest request_, CurlHandlePool *pool_):
+HTTPFetchOngoing::HTTPFetchOngoing(const HTTPFetchRequest &request_,
+ CurlHandlePool *pool_):
pool(pool_),
curl(NULL),
multi(NULL),
@@ -248,6 +248,7 @@ HTTPFetchOngoing::HTTPFetchOngoing(HTTPFetchRequest request_, CurlHandlePool *po
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()) {
diff --git a/src/httpfetch.h b/src/httpfetch.h
index f57ed8789..29fb540d0 100644
--- a/src/httpfetch.h
+++ b/src/httpfetch.h
@@ -61,7 +61,7 @@ struct HTTPFetchRequest
// If not empty, should contain entries such as "Accept: text/html"
std::vector<std::string> extra_headers;
- //useragent to use
+ // useragent to use
std::string useragent;
HTTPFetchRequest();
@@ -78,25 +78,16 @@ struct HTTPFetchResult
unsigned long request_id;
HTTPFetchResult()
+ : succeeded(false), timeout(false), response_code(0), data(""),
+ caller(HTTPFETCH_DISCARD), request_id(0)
{
- succeeded = false;
- timeout = false;
- response_code = 0;
- data = "";
- caller = HTTPFETCH_DISCARD;
- request_id = 0;
}
HTTPFetchResult(const HTTPFetchRequest &fetch_request)
+ : succeeded(false), timeout(false), response_code(0), data(""),
+ caller(fetch_request.caller), request_id(fetch_request.request_id)
{
- succeeded = false;
- timeout = false;
- response_code = 0;
- data = "";
- caller = fetch_request.caller;
- request_id = fetch_request.request_id;
}
-
};
// Initializes the httpfetch module
@@ -126,8 +117,6 @@ void httpfetch_caller_free(unsigned long caller);
// Performs a synchronous HTTP request. This blocks and therefore should
// only be used from background threads.
-void httpfetch_sync(const HTTPFetchRequest &fetch_request,
- HTTPFetchResult &fetch_result);
-
+void httpfetch_sync(const HTTPFetchRequest &fetch_request, HTTPFetchResult &fetch_result);
#endif // !HTTPFETCH_HEADER
diff --git a/src/hud.cpp b/src/hud.cpp
index f558acf1e..6895349ef 100644
--- a/src/hud.cpp
+++ b/src/hud.cpp
@@ -87,24 +87,31 @@ Hud::Hud(video::IVideoDriver *driver, scene::ISceneManager* smgr,
m_halo_boxes.clear();
m_selection_pos = v3f(0.0, 0.0, 0.0);
- std::string mode = g_settings->get("node_highlighting");
+ std::string mode_setting = g_settings->get("node_highlighting");
+
+ if (mode_setting == "halo") {
+ m_mode = HIGHLIGHT_HALO;
+ } else if (mode_setting == "none") {
+ m_mode = HIGHLIGHT_NONE;
+ } else {
+ m_mode = HIGHLIGHT_BOX;
+ }
+
m_selection_material.Lighting = false;
if (g_settings->getBool("enable_shaders")) {
IShaderSource *shdrsrc = client->getShaderSource();
u16 shader_id = shdrsrc->getShader(
- mode == "halo" ? "selection_shader" : "default_shader", 1, 1);
+ m_mode == HIGHLIGHT_HALO ? "selection_shader" : "default_shader", 1, 1);
m_selection_material.MaterialType = shdrsrc->getShaderInfo(shader_id).material;
} else {
m_selection_material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
}
- if (mode == "box") {
- m_use_selection_mesh = false;
+ if (m_mode == HIGHLIGHT_BOX) {
m_selection_material.Thickness =
rangelim(g_settings->getS16("selectionbox_width"), 1, 5);
- } else if (mode == "halo") {
- m_use_selection_mesh = true;
+ } else if (m_mode == HIGHLIGHT_HALO) {
m_selection_material.setTexture(0, tsrc->getTextureForMesh("halo.png"));
m_selection_material.setFlag(video::EMF_BACK_FACE_CULLING, true);
} else {
@@ -400,24 +407,32 @@ void Hud::drawStatbar(v2s32 pos, u16 corner, u16 drawdir, std::string texture,
p += offset;
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);
@@ -426,13 +441,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);
}
}
@@ -518,7 +529,7 @@ void Hud::setSelectionPos(const v3f &pos, const v3s16 &camera_offset)
void Hud::drawSelectionMesh()
{
- if (!m_use_selection_mesh) {
+ if (m_mode == HIGHLIGHT_BOX) {
// Draw 3D selection boxes
video::SMaterial oldmaterial = driver->getMaterial2D();
driver->setMaterial(m_selection_material);
@@ -538,7 +549,7 @@ void Hud::drawSelectionMesh()
driver->draw3DBox(box, video::SColor(255, r, g, b));
}
driver->setMaterial(oldmaterial);
- } else if (m_selection_mesh) {
+ } else if (m_mode == HIGHLIGHT_HALO && m_selection_mesh) {
// Draw selection mesh
video::SMaterial oldmaterial = driver->getMaterial2D();
driver->setMaterial(m_selection_material);
@@ -564,7 +575,7 @@ void Hud::drawSelectionMesh()
void Hud::updateSelectionMesh(const v3s16 &camera_offset)
{
m_camera_offset = camera_offset;
- if (!m_use_selection_mesh)
+ if (m_mode != HIGHLIGHT_HALO)
return;
if (m_selection_mesh) {
@@ -619,7 +630,7 @@ void Hud::resizeHotbar() {
}
struct MeshTimeInfo {
- s32 time;
+ u64 time;
scene::IMesh *mesh;
};
@@ -653,9 +664,9 @@ void drawItemStack(video::IVideoDriver *driver,
MeshTimeInfo &ti = rotation_time_infos[rotation_kind];
if (mesh != ti.mesh) {
ti.mesh = mesh;
- ti.time = getTimeMs();
+ ti.time = porting::getTimeMs();
} else {
- delta = porting::getDeltaMs(ti.time, getTimeMs()) % 100000;
+ delta = porting::getDeltaMs(ti.time, porting::getTimeMs()) % 100000;
}
}
core::rect<s32> oldViewPort = driver->getViewPort();
@@ -687,10 +698,14 @@ void drawItemStack(video::IVideoDriver *driver,
assert(buf->getHardwareMappingHint_Vertex() == scene::EHM_NEVER);
video::SColor c = basecolor;
if (imesh->buffer_colors.size() > j) {
- std::pair<bool, video::SColor> p = imesh->buffer_colors[j];
- c = p.first ? p.second : basecolor;
+ ItemPartColor *p = &imesh->buffer_colors[j];
+ if (p->override_base)
+ c = p->color;
}
- colorizeMeshBuffer(buf, &c);
+ if (imesh->needs_shading)
+ colorizeMeshBuffer(buf, &c);
+ else
+ setMeshBufferColor(buf, c);
video::SMaterial &material = buf->getMaterial();
material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
material.Lighting = false;
diff --git a/src/hud.h b/src/hud.h
index efa0c3648..15c115d89 100644
--- a/src/hud.h
+++ b/src/hud.h
@@ -175,7 +175,11 @@ private:
v3f m_selected_face_normal;
video::SMaterial m_selection_material;
- bool m_use_selection_mesh;
+
+ enum {
+ HIGHLIGHT_BOX,
+ HIGHLIGHT_HALO,
+ HIGHLIGHT_NONE } m_mode;
};
enum ItemRotationKind {
diff --git a/src/intlGUIEditBox.cpp b/src/intlGUIEditBox.cpp
index 29f828076..74b9f634c 100644
--- a/src/intlGUIEditBox.cpp
+++ b/src/intlGUIEditBox.cpp
@@ -29,6 +29,7 @@
// This file is part of the "Irrlicht Engine".
// For conditions of distribution and use, see copyright notice in irrlicht.h
+#include <util/numeric.h>
#include "intlGUIEditBox.h"
#if defined(_IRR_COMPILE_WITH_GUI_) && IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 9
@@ -1096,39 +1097,39 @@ s32 intlGUIEditBox::getCursorPos(s32 x, s32 y)
const u32 lineCount = (WordWrap || MultiLine) ? BrokenText.size() : 1;
- core::stringw *txtLine=0;
- s32 startPos=0;
- x+=3;
+ core::stringw *txtLine = NULL;
+ s32 startPos = 0;
+ u32 curr_line_idx = 0;
+ x += 3;
- for (u32 i=0; i < lineCount; ++i)
- {
- setTextRect(i);
- if (i == 0 && y < CurrentTextRect.UpperLeftCorner.Y)
+ for (; curr_line_idx < lineCount; ++curr_line_idx) {
+ setTextRect(curr_line_idx);
+ if (curr_line_idx == 0 && y < CurrentTextRect.UpperLeftCorner.Y)
y = CurrentTextRect.UpperLeftCorner.Y;
- if (i == lineCount - 1 && y > CurrentTextRect.LowerRightCorner.Y )
+ if (curr_line_idx == lineCount - 1 && y > CurrentTextRect.LowerRightCorner.Y)
y = CurrentTextRect.LowerRightCorner.Y;
// is it inside this region?
- if (y >= CurrentTextRect.UpperLeftCorner.Y && y <= CurrentTextRect.LowerRightCorner.Y)
- {
+ if (y >= CurrentTextRect.UpperLeftCorner.Y && y <= CurrentTextRect.LowerRightCorner.Y) {
// we've found the clicked line
- txtLine = (WordWrap || MultiLine) ? &BrokenText[i] : &Text;
- startPos = (WordWrap || MultiLine) ? BrokenTextPositions[i] : 0;
+ txtLine = (WordWrap || MultiLine) ? &BrokenText[curr_line_idx] : &Text;
+ startPos = (WordWrap || MultiLine) ? BrokenTextPositions[curr_line_idx] : 0;
break;
}
}
if (x < CurrentTextRect.UpperLeftCorner.X)
x = CurrentTextRect.UpperLeftCorner.X;
+ else if (x > CurrentTextRect.LowerRightCorner.X)
+ x = CurrentTextRect.LowerRightCorner.X;
- s32 idx = font->getCharacterFromPos(Text.c_str(), x - CurrentTextRect.UpperLeftCorner.X);
+ s32 idx = font->getCharacterFromPos(txtLine->c_str(), x - CurrentTextRect.UpperLeftCorner.X);
+ // Special handling for last line, if we are on limits, add 1 extra shift because idx
+ // will be the last char, not null char of the wstring
+ if (curr_line_idx == lineCount - 1 && x == CurrentTextRect.LowerRightCorner.X)
+ idx++;
- // click was on or left of the line
- if (idx != -1)
- return idx + startPos;
-
- // click was off the right edge of the line, go to end.
- return txtLine->size() + startPos;
+ return rangelim(idx + startPos, 0, S32_MAX);
}
@@ -1417,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
}
diff --git a/src/intlGUIEditBox.h b/src/intlGUIEditBox.h
index e3ee15a30..bb617476c 100644
--- a/src/intlGUIEditBox.h
+++ b/src/intlGUIEditBox.h
@@ -155,7 +155,7 @@ namespace gui
gui::IGUIFont *OverrideFont, *LastBreakFont;
IOSOperator* Operator;
- u32 BlinkStartTime;
+ u64 BlinkStartTime;
s32 CursorPos;
s32 HScrollPos, VScrollPos; // scroll position in characters
u32 Max;
diff --git a/src/inventory.cpp b/src/inventory.cpp
index 6d5b49916..ec8f3db72 100644
--- a/src/inventory.cpp
+++ b/src/inventory.cpp
@@ -46,12 +46,11 @@ static content_t content_translate_from_19_to_internal(content_t c_from)
}
ItemStack::ItemStack(const std::string &name_, u16 count_,
- u16 wear_, IItemDefManager *itemdef)
+ u16 wear_, IItemDefManager *itemdef) :
+ name(itemdef->getAlias(name_)),
+ count(count_),
+ wear(wear_)
{
- name = itemdef->getAlias(name_);
- count = count_;
- wear = wear_;
-
if (name.empty() || count == 0)
clear();
else if (itemdef->get(name).type == ITEM_TOOL)
@@ -370,14 +369,13 @@ ItemStack ItemStack::peekItem(u32 peekcount) const
Inventory
*/
-InventoryList::InventoryList(std::string name, u32 size, IItemDefManager *itemdef)
+InventoryList::InventoryList(const std::string &name, u32 size, IItemDefManager *itemdef):
+ m_name(name),
+ m_size(size),
+ m_width(0),
+ m_itemdef(itemdef)
{
- m_name = name;
- m_size = size;
- m_width = 0;
- m_itemdef = itemdef;
clearItems();
- //m_dirty = false;
}
InventoryList::~InventoryList()
@@ -661,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)
@@ -672,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;
@@ -712,14 +710,6 @@ ItemStack InventoryList::takeItem(u32 i, u32 takecount)
return taken;
}
-ItemStack InventoryList::peekItem(u32 i, u32 peekcount) const
-{
- if(i >= m_items.size())
- return ItemStack();
-
- return m_items[i].peekItem(peekcount);
-}
-
void InventoryList::moveItemSomewhere(u32 i, InventoryList *dest, u32 count)
{
// Take item from source list
diff --git a/src/inventory.h b/src/inventory.h
index fe1639728..51664e8d3 100644
--- a/src/inventory.h
+++ b/src/inventory.h
@@ -173,7 +173,7 @@ struct ItemStack
class InventoryList
{
public:
- InventoryList(std::string name, u32 size, IItemDefManager *itemdef);
+ InventoryList(const std::string &name, u32 size, IItemDefManager *itemdef);
~InventoryList();
void clearItems();
void setSize(u32 newsize);
@@ -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.
@@ -239,9 +240,6 @@ public:
// Returns empty item if couldn't take any.
ItemStack takeItem(u32 i, u32 takecount);
- // Similar to takeItem, but keeps the slot intact.
- ItemStack peekItem(u32 i, u32 peekcount) const;
-
// Move an item to a different list (or a different stack in the same list)
// count is the maximum number of items to move (0 for everything)
// returns number of moved items
@@ -254,8 +252,8 @@ public:
private:
std::vector<ItemStack> m_items;
- u32 m_size, m_width;
std::string m_name;
+ u32 m_size, m_width;
IItemDefManager *m_itemdef;
};
diff --git a/src/inventorymanager.cpp b/src/inventorymanager.cpp
index 6ebc2994b..c976bd037 100644
--- a/src/inventorymanager.cpp
+++ b/src/inventorymanager.cpp
@@ -20,7 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "inventorymanager.h"
#include "log.h"
#include "serverenvironment.h"
-#include "serverscripting.h"
+#include "scripting_server.h"
#include "serverobject.h"
#include "settings.h"
#include "craftdef.h"
diff --git a/src/irr_v3d.h b/src/irr_v3d.h
index fa6af3661..3a8d1fd30 100644
--- a/src/irr_v3d.h
+++ b/src/irr_v3d.h
@@ -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;
diff --git a/src/irrlichttypes_bloated.h b/src/irrlichttypes_bloated.h
index 77aba350c..2caca6fc4 100644
--- a/src/irrlichttypes_bloated.h
+++ b/src/irrlichttypes_bloated.h
@@ -29,4 +29,3 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <SColor.h>
#endif
-
diff --git a/src/irrlichttypes_extrabloated.h b/src/irrlichttypes_extrabloated.h
index cd6cb1d2c..464ee7904 100644
--- a/src/irrlichttypes_extrabloated.h
+++ b/src/irrlichttypes_extrabloated.h
@@ -36,4 +36,3 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#endif
#endif
-
diff --git a/src/itemgroup.h b/src/itemgroup.h
index f91ccc221..2206857fd 100644
--- a/src/itemgroup.h
+++ b/src/itemgroup.h
@@ -25,14 +25,12 @@ with this program; if not, write to the Free Software Foundation, Inc.,
typedef UNORDERED_MAP<std::string, int> ItemGroupList;
-static inline int itemgroup_get(const ItemGroupList &groups,
- const std::string &name)
+static inline int itemgroup_get(const ItemGroupList &groups, const std::string &name)
{
ItemGroupList::const_iterator i = groups.find(name);
- if(i == groups.end())
+ if (i == groups.end())
return 0;
return i->second;
}
#endif
-
diff --git a/src/itemstackmetadata.cpp b/src/itemstackmetadata.cpp
index c3d602245..9847cb6f9 100644
--- a/src/itemstackmetadata.cpp
+++ b/src/itemstackmetadata.cpp
@@ -13,11 +13,11 @@ void ItemStackMetadata::serialize(std::ostream &os) const
{
std::ostringstream os2;
os2 << DESERIALIZE_START;
- for (StringMap::const_iterator
- it = m_stringvars.begin();
- it != m_stringvars.end(); ++it) {
- os2 << it->first << DESERIALIZE_KV_DELIM
- << it->second << DESERIALIZE_PAIR_DELIM;
+ for (StringMap::const_iterator it = m_stringvars.begin(); it != m_stringvars.end();
+ ++it) {
+ if (!(*it).first.empty() || !(*it).second.empty())
+ os2 << (*it).first << DESERIALIZE_KV_DELIM
+ << (*it).second << DESERIALIZE_PAIR_DELIM;
}
os << serializeJsonStringIfNeeded(os2.str());
}
@@ -28,16 +28,18 @@ void ItemStackMetadata::deSerialize(std::istream &is)
m_stringvars.clear();
- if (!in.empty() && in[0] == DESERIALIZE_START) {
- Strfnd fnd(in);
- fnd.to(1);
- while (!fnd.at_end()) {
- std::string name = fnd.next(DESERIALIZE_KV_DELIM_STR);
- std::string var = fnd.next(DESERIALIZE_PAIR_DELIM_STR);
- m_stringvars[name] = var;
+ if (!in.empty()) {
+ if (in[0] == DESERIALIZE_START) {
+ Strfnd fnd(in);
+ fnd.to(1);
+ while (!fnd.at_end()) {
+ std::string name = fnd.next(DESERIALIZE_KV_DELIM_STR);
+ std::string var = fnd.next(DESERIALIZE_PAIR_DELIM_STR);
+ m_stringvars[name] = var;
+ }
+ } else {
+ // BACKWARDS COMPATIBILITY
+ m_stringvars[""] = in;
}
- } else {
- // BACKWARDS COMPATIBILITY
- m_stringvars[""] = in;
}
}
diff --git a/src/keycode.h b/src/keycode.h
index 4d66cf7b5..4cd0b707e 100644
--- a/src/keycode.h
+++ b/src/keycode.h
@@ -34,16 +34,16 @@ public:
KeyPress();
KeyPress(const char *name);
- KeyPress(const irr::SEvent::SKeyInput &in, bool prefer_character=false);
+ KeyPress(const irr::SEvent::SKeyInput &in, bool prefer_character = false);
bool operator==(const KeyPress &o) const
{
- return (Char > 0 && Char == o.Char) ||
- (valid_kcode(Key) && Key == o.Key);
+ return (Char > 0 && Char == o.Char) || (valid_kcode(Key) && Key == o.Key);
}
const char *sym() const;
const char *name() const;
+
protected:
static bool valid_kcode(irr::EKEY_CODE k)
{
@@ -68,4 +68,3 @@ void clearKeyCache();
irr::EKEY_CODE keyname_to_keycode(const char *name);
#endif
-
diff --git a/src/light.h b/src/light.h
index 984e6d7c2..30a647581 100644
--- a/src/light.h
+++ b/src/light.h
@@ -38,30 +38,30 @@ with this program; if not, write to the Free Software Foundation, Inc.,
inline u8 diminish_light(u8 light)
{
- if(light == 0)
+ if (light == 0)
return 0;
- if(light >= LIGHT_MAX)
+ if (light >= LIGHT_MAX)
return LIGHT_MAX - 1;
-
+
return light - 1;
}
inline u8 diminish_light(u8 light, u8 distance)
{
- if(distance >= light)
+ if (distance >= light)
return 0;
- return light - distance;
+ return light - distance;
}
inline u8 undiminish_light(u8 light)
{
// We don't know if light should undiminish from this particular 0.
// Thus, keep it at 0.
- if(light == 0)
+ if (light == 0)
return 0;
- if(light == LIGHT_MAX)
+ if (light == LIGHT_MAX)
return light;
-
+
return light + 1;
}
@@ -85,9 +85,9 @@ extern const u8 *light_decode_table;
// 0 <= return value <= 255
inline u8 decode_light(u8 light)
{
- if(light > LIGHT_MAX)
+ if (light > LIGHT_MAX)
light = LIGHT_MAX;
-
+
return light_decode_table[light];
}
@@ -97,12 +97,12 @@ inline float decode_light_f(float light_f)
{
s32 i = (u32)(light_f * LIGHT_MAX + 0.5);
- if(i <= 0)
+ if (i <= 0)
return (float)light_decode_table[0] / 255.0;
- if(i >= LIGHT_MAX)
+ if (i >= LIGHT_MAX)
return (float)light_decode_table[LIGHT_MAX] / 255.0;
- float v1 = (float)light_decode_table[i-1] / 255.0;
+ float v1 = (float)light_decode_table[i - 1] / 255.0;
float v2 = (float)light_decode_table[i] / 255.0;
float f0 = (float)i - 0.5;
float f = light_f * LIGHT_MAX - f0;
@@ -119,11 +119,10 @@ void set_light_table(float gamma);
inline u8 blend_light(u32 daylight_factor, u8 lightday, u8 lightnight)
{
u32 c = 1000;
- u32 l = ((daylight_factor * lightday + (c-daylight_factor) * lightnight))/c;
- if(l > LIGHT_SUN)
+ u32 l = ((daylight_factor * lightday + (c - daylight_factor) * lightnight)) / c;
+ if (l > LIGHT_SUN)
l = LIGHT_SUN;
return l;
}
#endif
-
diff --git a/src/localplayer.cpp b/src/localplayer.cpp
index ea4347207..05195c91f 100644
--- a/src/localplayer.cpp
+++ b/src/localplayer.cpp
@@ -26,6 +26,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "environment.h"
#include "map.h"
#include "client.h"
+#include "content_cao.h"
/*
LocalPlayer
@@ -33,9 +34,8 @@ 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),
- got_teleported(false),
isAttached(false),
touching_ground(false),
in_liquid(false),
@@ -48,17 +48,18 @@ LocalPlayer::LocalPlayer(Client *client, const char *name):
physics_override_jump(1.0f),
physics_override_gravity(1.0f),
physics_override_sneak(true),
- physics_override_sneak_glitch(true),
+ physics_override_sneak_glitch(false),
physics_override_new_move(true), // Temporary option for old move code
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),
camera_impact(0.f),
+ makes_footstep_sound(true),
last_animation(NO_ANIM),
hotbar_image(""),
hotbar_selected_image(""),
@@ -66,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),
@@ -95,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);
-}
-#define GETNODE(map, p3, v2, y, valid) \
- (map)->getNodeNoEx((p3) + v3s16((v2).X, y, (v2).Y), valid)
+ std::vector<aabb3f>::const_iterator it = nodeboxes.begin();
+ b_max = aabb3f(it->MinEdge, it->MaxEdge);
-// pos is the node the player is standing inside(!)
-static bool detectSneakLadder(Map *map, INodeDefManager *nodemgr, v3s16 pos)
+ ++it;
+ for (; it != nodeboxes.end(); ++it)
+ b_max.addInternalBox(*it);
+
+ return b_max;
+}
+
+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;
+
+ // 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;
- for (u16 i = 0; i < ARRLEN(vecs); i++) {
- const v2s16 vec = vecs[i];
+ // Get position of current standing node
+ const v3s16 current_node = floatToInt(position - v3f(0, position_y_mod, 0), BS);
- // 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)
+ if (current_node != m_sneak_node) {
+ new_sneak_node_exists = false;
+ } else {
+ node = map->getNodeNoEx(current_node, &is_valid_position);
+ if (!is_valid_position || !nodemgr->get(node).walkable)
+ new_sneak_node_exists = false;
+ }
+
+ // Keep old sneak node
+ if (new_sneak_node_exists)
+ return true;
+
+ // Get new sneak node
+ m_sneak_ladder_detected = false;
+ f32 min_distance_f = 100000.0 * BS;
+
+ for (s16 d = 0; d < 9; d++) {
+ const v3s16 p = current_node + dir9_center[d];
+ const v3f pf = intToFloat(p, BS);
+ const v2f diff(position.X - pf.X, position.Z - pf.Z);
+ f32 distance_f = diff.getLength();
+
+ if (distance_f > min_distance_f ||
+ fabs(diff.X) > (.5 + .1) * BS + sneak_max.X ||
+ fabs(diff.Y) > (.5 + .1) * BS + sneak_max.Z)
continue;
- // check one more node above OR below with corresponding walkability
- node = GETNODE(map, pos, vec, -1, &is_valid_position);
- bool ok = is_valid_position && w != nodemgr->get(node).walkable;
- if (!ok) {
- node = GETNODE(map, pos, vec, 2, &is_valid_position);
- ok = is_valid_position && w == nodemgr->get(node).walkable;
+
+ // The node to be sneaked on has to be walkable
+ node = map->getNodeNoEx(p, &is_valid_position);
+ if (!is_valid_position || !nodemgr->get(node).walkable)
+ continue;
+ // And the node(s) above have to be nonwalkable
+ bool ok = true;
+ if (!physics_override_sneak_glitch) {
+ u16 height = ceilf(
+ (m_collisionbox.MaxEdge.Y - m_collisionbox.MinEdge.Y) / BS
+ );
+ for (u16 y = 1; y <= height; y++) {
+ node = map->getNodeNoEx(p + v3s16(0, y, 0), &is_valid_position);
+ if (!is_valid_position || nodemgr->get(node).walkable) {
+ ok = false;
+ break;
+ }
+ }
+ } else {
+ // legacy behaviour: check just one node
+ node = map->getNodeNoEx(p + v3s16(0, 1, 0), &is_valid_position);
+ ok = is_valid_position && !nodemgr->get(node).walkable;
}
+ if (!ok)
+ continue;
- if (ok)
- return true;
+ min_distance_f = distance_f;
+ m_sneak_node = p;
+ new_sneak_node_exists = true;
}
- return false;
-}
-
-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)};
+ if (!new_sneak_node_exists)
+ return false;
- for (u16 i = 0; i < ARRLEN(vecs); i++) {
- const v2s16 vec = vecs[i];
+ // 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);
- node = GETNODE(map, pos, vec, 1, &is_valid_position);
+ if (physics_override_sneak_glitch) {
+ // Detect sneak ladder:
+ // Node two meters above sneak node must be solid
+ node = map->getNodeNoEx(m_sneak_node + v3s16(0, 2, 0),
+ &is_valid_position);
if (is_valid_position && nodemgr->get(node).walkable) {
- // Ledge exists
- node = GETNODE(map, pos, vec, 2, &is_valid_position);
- if (is_valid_position && !nodemgr->get(node).walkable)
- // Space above ledge exists
- return true;
+ // Node three meters above: must be non-solid
+ node = map->getNodeNoEx(m_sneak_node + v3s16(0, 3, 0),
+ &is_valid_position);
+ m_sneak_ladder_detected = is_valid_position &&
+ !nodemgr->get(node).walkable;
}
}
-
- return false;
+ return true;
}
-#undef GETNODE
-
void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d,
std::vector<CollisionInfo> *collision_info)
{
+ if (!collision_info || collision_info->empty()) {
+ // Node below the feet, update each ClientEnvironment::step()
+ m_standing_node = floatToInt(m_position, BS) - v3s16(0, 1, 0);
+ }
+
// Temporary option for old move code
if (!physics_override_new_move) {
old_move(dtime, env, pos_max_d, collision_info);
@@ -192,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;
}
@@ -203,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;
}
@@ -278,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
@@ -290,50 +338,12 @@ 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);
- // 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 (control.sneak && m_sneak_node_exists &&
- !(fly_allowed && g_settings->getBool("free_move")) &&
- !in_liquid && !is_climbing &&
- physics_override_sneak && !got_teleported) {
- v3f sn_f = intToFloat(m_sneak_node, BS);
- const v3f bmin = m_sneak_node_bb_top.MinEdge;
- const v3f bmax = m_sneak_node_bb_top.MaxEdge;
-
- position.X = rangelim(position.X,
- sn_f.X+bmin.X - sneak_max.X, sn_f.X+bmax.X + sneak_max.X);
- position.Z = rangelim(position.Z,
- sn_f.Z+bmin.Z - sneak_max.Z, sn_f.Z+bmax.Z + sneak_max.Z);
-
- // 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, sn_f.Y+bmax.Y);
- } else {
- // legacy behaviour that sometimes causes some weird slow sinking
- m_speed.Y = MYMAX(m_speed.Y, 0);
- }
- }
-
- if (got_teleported)
- got_teleported = false;
-
// TODO: this shouldn't be hardcoded but transmitted from server
- float player_stepheight = touching_ground ? (BS*0.6) : (BS*0.2);
+ float player_stepheight = (touching_ground) ? (BS * 0.6f) : (BS * 0.2f);
#ifdef __ANDROID__
- player_stepheight += (0.6 * BS);
+ if (touching_ground)
+ player_stepheight += (0.6f * BS);
#endif
v3f accel_f = v3f(0,0,0);
@@ -342,6 +352,37 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d,
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.
@@ -350,121 +391,84 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d,
*/
bool touching_ground_was = touching_ground;
touching_ground = result.touching_ground;
+ bool sneak_can_jump = 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;
-
+ // 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;
- // The node to be sneaked on has to be walkable
- node = map->getNodeNoEx(p, &is_valid_position);
- if (!is_valid_position || !nodemgr->get(node).walkable)
- continue;
- // And the node(s) above have to be nonwalkable
- bool ok = true;
- if (!physics_override_sneak_glitch) {
- u16 height = ceilf(
- (m_collisionbox.MaxEdge.Y - m_collisionbox.MinEdge.Y) / BS
- );
- for (u16 y = 1; y <= height; y++) {
- node = map->getNodeNoEx(p + v3s16(0,y,0), &is_valid_position);
- if (!is_valid_position || nodemgr->get(node).walkable) {
- ok = false;
- break;
- }
- }
- } else {
- // legacy behaviour: check just one node
- node = map->getNodeNoEx(p + v3s16(0,1,0), &is_valid_position);
- ok = is_valid_position && !nodemgr->get(node).walkable;
- }
- if (!ok)
- continue;
+ if (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);
+ }
- min_distance_f = distance_f;
- new_sneak_node = p;
+ /*
+ If sneaking, keep on top of last walked node and don't fall off
+ */
+ 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,
+ bmin.Z - sneak_max.Z, bmax.Z + sneak_max.Z);
+
+ if (position.X != old_pos.X)
+ m_speed.X = 0;
+ if (position.Z != old_pos.Z)
+ m_speed.Z = 0;
}
- 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);
+ 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;
+ }
- m_sneak_ladder_detected = physics_override_sneak_glitch &&
- detectSneakLadder(map, nodemgr, floatToInt(position, BS));
- } else {
- m_sneak_ladder_detected = false;
+ // 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;
+ sn_info.node_p = m_sneak_node;
+ sn_info.old_speed = old_speed;
+ sn_info.new_speed = m_speed;
+ collision_info->push_back(sn_info);
}
}
/*
- If 'sneak glitch' enabled detect ledge for old sneak-jump
- behaviour of climbing onto a ledge 2 nodes up.
+ Find the next sneak node if necessary
*/
- if (physics_override_sneak_glitch && control.sneak && control.jump)
- m_ledge_detected = detectLedge(map, nodemgr, floatToInt(position, BS));
+ bool new_sneak_node_exists = false;
+
+ if (could_sneak)
+ new_sneak_node_exists = updateSneakNode(map, position, sneak_max);
/*
- Set new position
+ Set new position but keep sneak node set
*/
setPosition(position);
+ 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);
@@ -485,21 +489,14 @@ 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") &&
@@ -688,17 +685,8 @@ void LocalPlayer::applyControl(float dtime)
*/
v3f speedJ = getSpeed();
if(speedJ.Y >= -0.5 * BS) {
- if (m_ledge_detected) {
- // Limit jump speed to a minimum that allows
- // jumping up onto a ledge 2 nodes up.
- speedJ.Y = movement_speed_jump *
- MYMAX(physics_override_jump, 1.3f);
- setSpeed(speedJ);
- m_ledge_detected = false;
- } else {
- speedJ.Y = movement_speed_jump * physics_override_jump;
- setSpeed(speedJ);
- }
+ speedJ.Y = movement_speed_jump * physics_override_jump;
+ setSpeed(speedJ);
MtEvent *e = new SimpleTriggerEvent("PlayerJump");
m_client->event()->put(e);
@@ -755,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()
@@ -917,7 +905,7 @@ void LocalPlayer::old_move(f32 dtime, Environment *env, f32 pos_max_d,
*/
if (control.sneak && m_sneak_node_exists &&
!(fly_allowed && g_settings->getBool("free_move")) && !in_liquid &&
- physics_override_sneak && !got_teleported) {
+ physics_override_sneak) {
f32 maxd = 0.5 * BS + sneak_max;
v3f lwn_f = intToFloat(m_sneak_node, BS);
position.X = rangelim(position.X, lwn_f.X - maxd, lwn_f.X + maxd);
@@ -938,9 +926,6 @@ void LocalPlayer::old_move(f32 dtime, Environment *env, f32 pos_max_d,
}
}
- if (got_teleported)
- got_teleported = false;
-
// this shouldn't be hardcoded but transmitted from server
float player_stepheight = touching_ground ? (BS * 0.6) : (BS * 0.2);
@@ -1055,9 +1040,11 @@ void LocalPlayer::old_move(f32 dtime, Environment *env, f32 pos_max_d,
}
/*
- Set new position
+ 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;
/*
Report collisions
diff --git a/src/localplayer.h b/src/localplayer.h
index 01e859bf0..7249be74b 100644
--- a/src/localplayer.h
+++ b/src/localplayer.h
@@ -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;
@@ -30,7 +31,13 @@ class GenericCAO;
class ClientActiveObject;
class IGameDef;
-enum LocalPlayerAnimations {NO_ANIM, WALK_ANIM, DIG_ANIM, WD_ANIM}; // no local animation, walking, digging, both
+enum LocalPlayerAnimations
+{
+ NO_ANIM,
+ WALK_ANIM,
+ DIG_ANIM,
+ WD_ANIM
+}; // no local animation, walking, digging, both
class LocalPlayer : public Player
{
@@ -40,8 +47,9 @@ public:
ClientActiveObject *parent;
+ // Initialize hp to 0, so that no hearts will be shown if server
+ // doesn't support health points
u16 hp;
- bool got_teleported;
bool isAttached;
bool touching_ground;
// This oscillates so that the player jumps a bit above the surface
@@ -86,6 +94,8 @@ public:
float camera_impact;
+ bool makes_footstep_sound;
+
int last_animation;
float last_animation_speed;
@@ -97,12 +107,11 @@ public:
float hurt_tilt_timer;
float hurt_tilt_strength;
- GenericCAO* getCAO() const {
- return m_cao;
- }
+ GenericCAO *getCAO() const { return m_cao; }
- void setCAO(GenericCAO* toset) {
- assert( m_cao == NULL ); // Pre-condition
+ void setCAO(GenericCAO *toset)
+ {
+ assert(!m_cao); // Pre-condition
m_cao = toset;
}
@@ -113,55 +122,52 @@ public:
v3s16 getLightPosition() const;
- void setYaw(f32 yaw)
- {
- m_yaw = yaw;
- }
+ void setYaw(f32 yaw) { m_yaw = yaw; }
f32 getYaw() const { return m_yaw; }
- void setPitch(f32 pitch)
- {
- m_pitch = pitch;
- }
+ void setPitch(f32 pitch) { m_pitch = pitch; }
f32 getPitch() const { return m_pitch; }
- void setPosition(const v3f &position)
+ inline void setPosition(const v3f &position)
{
m_position = position;
+ m_sneak_node_exists = false;
}
v3f getPosition() const { return m_position; }
v3f getEyePosition() const { return m_position + getEyeOffset(); }
v3f getEyeOffset() const;
+
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;
@@ -169,9 +175,8 @@ private:
bool camera_barely_in_ceiling;
aabb3f m_collisionbox;
- GenericCAO* m_cao;
+ GenericCAO *m_cao;
Client *m_client;
};
#endif
-
diff --git a/src/log.cpp b/src/log.cpp
index 589cfd909..0dec7dd70 100644
--- a/src/log.cpp
+++ b/src/log.cpp
@@ -347,13 +347,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;
}
}
}
diff --git a/src/main.cpp b/src/main.cpp
index 1ec278981..6a2e89f7a 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -47,6 +47,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#endif
#ifndef SERVER
#include "client/clientlauncher.h"
+
#endif
#ifdef HAVE_TOUCHSCREENGUI
@@ -102,28 +103,10 @@ static bool get_game_from_cmdline(GameParams *game_params, const Settings &cmd_a
static bool determine_subgame(GameParams *game_params);
static bool run_dedicated_server(const GameParams &game_params, const Settings &cmd_args);
-static bool migrate_database(const GameParams &game_params, const Settings &cmd_args);
+static bool migrate_map_database(const GameParams &game_params, const Settings &cmd_args);
/**********************************************************************/
-/*
- gettime.h implementation
-*/
-
-#ifdef SERVER
-
-u32 getTimeMs()
-{
- /* Use imprecise system calls directly (from porting.h) */
- return porting::getTime(PRECISION_MILLI);
-}
-
-u32 getTime(TimePrecision prec)
-{
- return porting::getTime(prec);
-}
-
-#endif
FileLogOutput file_log_output;
@@ -292,6 +275,8 @@ static void set_allowed_options(OptionList *allowed_options)
_("Set gameid (\"--gameid list\" prints available ones)"))));
allowed_options->insert(std::make_pair("migrate", ValueSpec(VALUETYPE_STRING,
_("Migrate from current map backend to another (Only works when using minetestserver or with --server)"))));
+ allowed_options->insert(std::make_pair("migrate-players", ValueSpec(VALUETYPE_STRING,
+ _("Migrate from current players backend to another (Only works when using minetestserver or with --server)"))));
allowed_options->insert(std::make_pair("terminal", ValueSpec(VALUETYPE_FLAG,
_("Feature an interactive terminal (Only works when using minetestserver or with --server)"))));
#ifndef SERVER
@@ -332,7 +317,7 @@ static void print_allowed_options(const OptionList &allowed_options)
if (i->second.type != VALUETYPE_FLAG)
os1 << _(" <value>");
- std::cout << padStringRight(os1.str(), 24);
+ std::cout << padStringRight(os1.str(), 30);
if (i->second.help != NULL)
std::cout << i->second.help;
@@ -828,7 +813,9 @@ static bool run_dedicated_server(const GameParams &game_params, const Settings &
// Database migration
if (cmd_args.exists("migrate"))
- return migrate_database(game_params, cmd_args);
+ return migrate_map_database(game_params, cmd_args);
+ else if (cmd_args.exists("migrate-players"))
+ return ServerEnvironment::migratePlayersDatabase(game_params, cmd_args);
if (cmd_args.exists("terminal")) {
#if USE_CURSES
@@ -912,7 +899,7 @@ static bool run_dedicated_server(const GameParams &game_params, const Settings &
return true;
}
-static bool migrate_database(const GameParams &game_params, const Settings &cmd_args)
+static bool migrate_map_database(const GameParams &game_params, const Settings &cmd_args)
{
std::string migrate_to = cmd_args.get("migrate");
Settings world_mt;
@@ -921,20 +908,23 @@ static bool migrate_database(const GameParams &game_params, const Settings &cmd_
errorstream << "Cannot read world.mt!" << std::endl;
return false;
}
+
if (!world_mt.exists("backend")) {
errorstream << "Please specify your current backend in world.mt:"
<< std::endl
- << " backend = {sqlite3|leveldb|redis|dummy}"
+ << " backend = {sqlite3|leveldb|redis|dummy|postgresql}"
<< std::endl;
return false;
}
+
std::string backend = world_mt.get("backend");
if (backend == migrate_to) {
errorstream << "Cannot migrate: new backend is same"
<< " as the old one" << std::endl;
return false;
}
- Database *old_db = ServerMap::createDatabase(backend, game_params.world_path, world_mt),
+
+ MapDatabase *old_db = ServerMap::createDatabase(backend, game_params.world_path, world_mt),
*new_db = ServerMap::createDatabase(migrate_to, game_params.world_path, world_mt);
u32 count = 0;
@@ -976,4 +966,3 @@ static bool migrate_database(const GameParams &game_params, const Settings &cmd_
return true;
}
-
diff --git a/src/mainmenumanager.h b/src/mainmenumanager.h
index 17133b164..fb715ca9b 100644
--- a/src/mainmenumanager.h
+++ b/src/mainmenumanager.h
@@ -83,7 +83,7 @@ public:
/*core::list<GUIModalMenu*>::Iterator i = m_stack.getLast();
assert(*i == menu);
m_stack.erase(i);*/
-
+
if(!m_stack.empty())
m_stack.back()->setVisible(true);
}
@@ -119,7 +119,7 @@ public:
extern MainMenuManager g_menumgr;
-extern bool noMenuActive();
+extern bool isMenuActive();
class MainGameCallback : public IGameCallback
{
@@ -168,7 +168,7 @@ public:
keyconfig_changed = true;
}
-
+
bool disconnect_requested;
bool changepassword_requested;
bool changevolume_requested;
diff --git a/src/map.cpp b/src/map.cpp
index b690ea3b3..128971442 100644
--- a/src/map.cpp
+++ b/src/map.cpp
@@ -44,6 +44,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "database.h"
#include "database-dummy.h"
#include "database-sqlite3.h"
+#include "script/scripting_server.h"
#include <deque>
#include <queue>
#if USE_LEVELDB
@@ -637,7 +638,8 @@ s32 Map::transforming_liquid_size() {
return m_transforming_liquid.size();
}
-void Map::transformLiquids(std::map<v3s16, MapBlock*> &modified_blocks)
+void Map::transformLiquids(std::map<v3s16, MapBlock*> &modified_blocks,
+ ServerEnvironment *env)
{
DSTACK(FUNCTION_NAME);
//TimeTaker timer("transformLiquids()");
@@ -897,8 +899,16 @@ void Map::transformLiquids(std::map<v3s16, MapBlock*> &modified_blocks)
// set the liquid level and flow bit to 0
n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
}
+
+ // change the node.
n0.setContent(new_node_content);
+ // on_flood() the node
+ if (floodable_node != CONTENT_AIR) {
+ if (env->getScriptIface()->node_on_flood(p0, n00, n0))
+ continue;
+ }
+
// Ignore light (because calling voxalgo::update_lighting_nodes)
n0.setLight(LIGHTBANK_DAY, 0, m_nodedef);
n0.setLight(LIGHTBANK_NIGHT, 0, m_nodedef);
@@ -971,7 +981,7 @@ void Map::transformLiquids(std::map<v3s16, MapBlock*> &modified_blocks)
time_until_purge *= 1000; // seconds -> milliseconds
- u32 curr_time = getTime(PRECISION_MILLI);
+ u64 curr_time = porting::getTimeMs();
u32 prev_unprocessed = m_unprocessed_count;
m_unprocessed_count = m_transforming_liquid.size();
@@ -1226,7 +1236,8 @@ bool Map::isBlockOccluded(MapBlock *block, v3s16 cam_pos_nodes) {
/*
ServerMap
*/
-ServerMap::ServerMap(std::string savedir, IGameDef *gamedef, EmergeManager *emerge):
+ServerMap::ServerMap(const std::string &savedir, IGameDef *gamedef,
+ EmergeManager *emerge):
Map(dout_server, gamedef),
settings_mgr(g_settings, savedir + DIR_DELIM + "map_meta.txt"),
m_emerge(emerge),
@@ -1827,9 +1838,6 @@ MapBlock *ServerMap::getBlockOrEmerge(v3s16 p3d)
return block;
}
-void ServerMap::prepareBlock(MapBlock *block) {
-}
-
// N.B. This requires no synchronization, since data will not be modified unless
// the VoxelManipulator being updated belongs to the same thread.
void ServerMap::updateVManip(v3s16 pos)
@@ -1936,7 +1944,7 @@ std::string ServerMap::getSectorDir(v2s16 pos, int layout)
}
}
-v2s16 ServerMap::getSectorPos(std::string dirname)
+v2s16 ServerMap::getSectorPos(const std::string &dirname)
{
unsigned int x = 0, y = 0;
int r;
@@ -1966,7 +1974,7 @@ v2s16 ServerMap::getSectorPos(std::string dirname)
return pos;
}
-v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
+v3s16 ServerMap::getBlockPos(const std::string &sectordir, const std::string &blockfile)
{
v2s16 p2d = getSectorPos(sectordir);
@@ -2275,13 +2283,13 @@ bool ServerMap::loadSectorFull(v2s16 p2d)
}
#endif
-Database *ServerMap::createDatabase(
+MapDatabase *ServerMap::createDatabase(
const std::string &name,
const std::string &savedir,
Settings &conf)
{
if (name == "sqlite3")
- return new Database_SQLite3(savedir);
+ return new MapDatabaseSQLite3(savedir);
if (name == "dummy")
return new Database_Dummy();
#if USE_LEVELDB
@@ -2293,8 +2301,11 @@ Database *ServerMap::createDatabase(
return new Database_Redis(conf);
#endif
#if USE_POSTGRESQL
- else if (name == "postgresql")
- return new Database_PostgreSQL(conf);
+ else if (name == "postgresql") {
+ std::string connect_string = "";
+ conf.getNoEx("pgsql_connection", connect_string);
+ return new MapDatabasePostgreSQL(connect_string);
+ }
#endif
else
throw BaseException(std::string("Database backend ") + name + " not supported.");
@@ -2315,7 +2326,7 @@ bool ServerMap::saveBlock(MapBlock *block)
return saveBlock(block, dbase);
}
-bool ServerMap::saveBlock(MapBlock *block, Database *db)
+bool ServerMap::saveBlock(MapBlock *block, MapDatabase *db)
{
v3s16 p3d = block->getPos();
@@ -2346,16 +2357,15 @@ bool ServerMap::saveBlock(MapBlock *block, Database *db)
return ret;
}
-void ServerMap::loadBlock(std::string sectordir, std::string blockfile,
+void ServerMap::loadBlock(const std::string &sectordir, const std::string &blockfile,
MapSector *sector, bool save_after_load)
{
DSTACK(FUNCTION_NAME);
std::string fullpath = sectordir + DIR_DELIM + blockfile;
try {
-
std::ifstream is(fullpath.c_str(), std::ios_base::binary);
- if(is.good() == false)
+ if (!is.good())
throw FileNotGoodException("Cannot open block file");
v3s16 p3d = getBlockPos(sectordir, blockfile);
@@ -2591,6 +2601,16 @@ void ServerMap::PrintInfo(std::ostream &out)
out<<"ServerMap: ";
}
+bool ServerMap::repairBlockLight(v3s16 blockpos,
+ std::map<v3s16, MapBlock *> *modified_blocks)
+{
+ MapBlock *block = emergeBlock(blockpos, false);
+ if (!block || !block->isGenerated())
+ return false;
+ voxalgo::repair_block_light(this, block, modified_blocks);
+ return true;
+}
+
MMVManip::MMVManip(Map *map):
VoxelManipulator(),
m_is_dirty(false),
@@ -2712,6 +2732,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;
diff --git a/src/map.h b/src/map.h
index ea8dc76d1..5123a7ec1 100644
--- a/src/map.h
+++ b/src/map.h
@@ -37,7 +37,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "map_settings_manager.h"
class Settings;
-class Database;
+class MapDatabase;
class ClientMap;
class MapSector;
class ServerMapSector;
@@ -266,7 +266,8 @@ public:
// For debug printing. Prints "Map: ", "ServerMap: " or "ClientMap: "
virtual void PrintInfo(std::ostream &out);
- void transformLiquids(std::map<v3s16, MapBlock*> & modified_blocks);
+ void transformLiquids(std::map<v3s16, MapBlock*> & modified_blocks,
+ ServerEnvironment *env);
/*
Node metadata
@@ -342,7 +343,7 @@ protected:
private:
f32 m_transforming_liquid_loop_count_multiplier;
u32 m_unprocessed_count;
- u32 m_inc_trending_up_start_time; // milliseconds
+ u64 m_inc_trending_up_start_time; // milliseconds
bool m_queue_size_timer_started;
DISABLE_CLASS_COPY(Map);
@@ -360,7 +361,7 @@ public:
/*
savedir: directory to which map data should be saved
*/
- ServerMap(std::string savedir, IGameDef *gamedef, EmergeManager *emerge);
+ ServerMap(const std::string &savedir, IGameDef *gamedef, EmergeManager *emerge);
~ServerMap();
s32 mapType() const
@@ -408,9 +409,6 @@ public:
*/
MapBlock *getBlockOrEmerge(v3s16 p3d);
- // Carries out any initialization necessary before block is sent
- void prepareBlock(MapBlock *block);
-
// Helper for placing objects on ground level
s16 findGroundLevel(v2s16 p2d);
@@ -422,16 +420,14 @@ public:
// returns something like "map/sectors/xxxxxxxx"
std::string getSectorDir(v2s16 pos, int layout = 2);
// dirname: final directory name
- v2s16 getSectorPos(std::string dirname);
- v3s16 getBlockPos(std::string sectordir, std::string blockfile);
+ v2s16 getSectorPos(const std::string &dirname);
+ v3s16 getBlockPos(const std::string &sectordir, const std::string &blockfile);
static std::string getBlockFilename(v3s16 p);
/*
Database functions
*/
- static Database *createDatabase(const std::string &name, const std::string &savedir, Settings &conf);
- // Verify we can read/write to the database
- void verifyDatabase();
+ static MapDatabase *createDatabase(const std::string &name, const std::string &savedir, Settings &conf);
// Returns true if the database file does not exist
bool loadFromFolders();
@@ -458,17 +454,11 @@ public:
MapSector* loadSectorMeta(std::string dirname, bool save_after_load);
bool loadSectorMeta(v2s16 p2d);
- // Full load of a sector including all blocks.
- // returns true on success, false on failure.
- bool loadSectorFull(v2s16 p2d);
- // If sector is not found in memory, try to load it from disk.
- // Returns true if sector now resides in memory
- //bool deFlushSector(v2s16 p2d);
-
bool saveBlock(MapBlock *block);
- static bool saveBlock(MapBlock *block, Database *db);
+ static bool saveBlock(MapBlock *block, MapDatabase *db);
// This will generate a sector with getSector if not found.
- void loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load=false);
+ void loadBlock(const std::string &sectordir, const std::string &blockfile,
+ MapSector *sector, bool save_after_load=false);
MapBlock* loadBlock(v3s16 p);
// Database version
void loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load=false);
@@ -485,6 +475,16 @@ public:
u64 getSeed();
s16 getWaterLevel();
+ /*!
+ * Fixes lighting in one map block.
+ * May modify other blocks as well, as light can spread
+ * out of the specified block.
+ * Returns false if the block is not generated (so nothing
+ * changed), true otherwise.
+ */
+ bool repairBlockLight(v3s16 blockpos,
+ std::map<v3s16, MapBlock *> *modified_blocks);
+
MapSettingsManager settings_mgr;
private:
@@ -507,7 +507,7 @@ private:
This is reset to false when written on disk.
*/
bool m_map_metadata_changed;
- Database *dbase;
+ MapDatabase *dbase;
};
diff --git a/src/map_settings_manager.cpp b/src/map_settings_manager.cpp
index 53d17125c..52f88778c 100644
--- a/src/map_settings_manager.cpp
+++ b/src/map_settings_manager.cpp
@@ -25,14 +25,13 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "map_settings_manager.h"
-MapSettingsManager::MapSettingsManager(
- Settings *user_settings, const std::string &map_meta_path)
+MapSettingsManager::MapSettingsManager(Settings *user_settings,
+ const std::string &map_meta_path):
+ mapgen_params(NULL),
+ m_map_meta_path(map_meta_path),
+ m_map_settings(new Settings()),
+ m_user_settings(user_settings)
{
- m_map_meta_path = map_meta_path;
- m_user_settings = user_settings;
- m_map_settings = new Settings;
- mapgen_params = NULL;
-
assert(m_user_settings != NULL);
}
diff --git a/src/mapblock.cpp b/src/mapblock.cpp
index 1a0b01f2b..ec10a49bb 100644
--- a/src/mapblock.cpp
+++ b/src/mapblock.cpp
@@ -611,7 +611,7 @@ void MapBlock::serialize(std::ostream &os, u8 version, bool disk)
Node metadata
*/
std::ostringstream oss(std::ios_base::binary);
- m_node_metadata.serialize(oss);
+ m_node_metadata.serialize(oss, version, disk);
compressZlib(oss.str(), os);
/*
@@ -669,11 +669,10 @@ void MapBlock::deSerialize(std::istream &is, u8 version, bool disk)
u8 flags = readU8(is);
is_underground = (flags & 0x01) ? true : false;
m_day_night_differs = (flags & 0x02) ? true : false;
- if (version < 27) {
+ if (version < 27)
m_lighting_complete = 0xFFFF;
- } else {
+ else
m_lighting_complete = readU16(is);
- }
m_generated = (flags & 0x08) ? false : true;
/*
diff --git a/src/mapblock.h b/src/mapblock.h
index 8816dc817..1ccaccaef 100644
--- a/src/mapblock.h
+++ b/src/mapblock.h
@@ -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
diff --git a/src/mapblock_mesh.cpp b/src/mapblock_mesh.cpp
index 933dfc32a..6781f21af 100644
--- a/src/mapblock_mesh.cpp
+++ b/src/mapblock_mesh.cpp
@@ -323,7 +323,7 @@ void final_color_blend(video::SColor *result,
video::SColorf dayLight;
get_sunlight_color(&dayLight, daynight_ratio);
final_color_blend(result,
- encode_light_and_color(light, video::SColor(0xFFFFFFFF), 0), dayLight);
+ encode_light(light, 0), dayLight);
}
void final_color_blend(video::SColor *result,
@@ -422,12 +422,19 @@ static void getNodeVertexDirs(v3s16 dir, v3s16 *vertex_dirs)
struct FastFace
{
- TileSpec tile;
+ TileLayer layer;
video::S3DVertex vertices[4]; // Precalculated vertices
+ /*!
+ * The face is divided into two triangles. If this is true,
+ * vertices 0 and 2 are connected, othervise vertices 1 and 3
+ * are connected.
+ */
+ bool vertex_0_2_connected;
+ u8 layernum;
};
-static void makeFastFace(TileSpec tile, u16 li0, u16 li1, u16 li2, u16 li3,
- v3f p, v3s16 dir, v3f scale, std::vector<FastFace> &dest)
+static void makeFastFace(const TileSpec &tile, u16 li0, u16 li1, u16 li2, u16 li3,
+ v3f p, v3s16 dir, v3f scale, std::vector<FastFace> &dest)
{
// Position is at the center of the cube.
v3f pos = p * BS;
@@ -577,27 +584,50 @@ static void makeFastFace(TileSpec tile, u16 li0, u16 li1, u16 li2, u16 li3,
v3f normal(dir.X, dir.Y, dir.Z);
- dest.push_back(FastFace());
+ u16 li[4] = { li0, li1, li2, li3 };
+ u16 day[4];
+ u16 night[4];
+
+ for (u8 i = 0; i < 4; i++) {
+ day[i] = li[i] >> 8;
+ night[i] = li[i] & 0xFF;
+ }
- FastFace& face = *dest.rbegin();
+ bool vertex_0_2_connected = abs(day[0] - day[2]) + abs(night[0] - night[2])
+ < abs(day[1] - day[3]) + abs(night[1] - night[3]);
- u16 li[4] = { li0, li1, li2, li3 };
v2f32 f[4] = {
core::vector2d<f32>(x0 + w * abs_scale, y0 + h),
core::vector2d<f32>(x0, y0 + h),
core::vector2d<f32>(x0, y0),
core::vector2d<f32>(x0 + w * abs_scale, y0) };
- for (u8 i = 0; i < 4; i++) {
- video::SColor c = encode_light_and_color(li[i], tile.color,
- tile.emissive_light);
- if (!tile.emissive_light)
- applyFacesShading(c, normal);
+ for (int layernum = 0; layernum < MAX_TILE_LAYERS; layernum++) {
+ const TileLayer *layer = &tile.layers[layernum];
+ if (layer->texture_id == 0)
+ continue;
- face.vertices[i] = video::S3DVertex(vertex_pos[i], normal, c, f[i]);
- }
+ dest.push_back(FastFace());
+ FastFace& face = *dest.rbegin();
+
+ for (u8 i = 0; i < 4; i++) {
+ video::SColor c = encode_light(li[i], tile.emissive_light);
+ if (!tile.emissive_light)
+ applyFacesShading(c, normal);
+
+ face.vertices[i] = video::S3DVertex(vertex_pos[i], normal, c, f[i]);
+ }
+
+ /*
+ Revert triangles for nicer looking gradient if the
+ brightness of vertices 1 and 3 differ less than
+ the brightness of vertices 0 and 2.
+ */
+ face.vertex_0_2_connected = vertex_0_2_connected;
- face.tile = tile;
+ face.layer = *layer;
+ face.layernum = layernum;
+ }
}
/*
@@ -659,23 +689,29 @@ static u8 face_contents(content_t m1, content_t m2, bool *equivalent,
/*
Gets nth node tile (0 <= n <= 5).
*/
-TileSpec getNodeTileN(MapNode mn, v3s16 p, u8 tileindex, MeshMakeData *data)
+void getNodeTileN(MapNode mn, v3s16 p, u8 tileindex, MeshMakeData *data, TileSpec &tile)
{
INodeDefManager *ndef = data->m_client->ndef();
const ContentFeatures &f = ndef->get(mn);
- TileSpec spec = f.tiles[tileindex];
- if (!spec.has_color)
- mn.getColor(f, &spec.color);
+ tile = f.tiles[tileindex];
+ TileLayer *top_layer = NULL;
+ for (int layernum = 0; layernum < MAX_TILE_LAYERS; layernum++) {
+ TileLayer *layer = &tile.layers[layernum];
+ if (layer->texture_id == 0)
+ continue;
+ top_layer = layer;
+ if (!layer->has_color)
+ mn.getColor(f, &(layer->color));
+ }
// Apply temporary crack
if (p == data->m_crack_pos_relative)
- spec.material_flags |= MATERIAL_FLAG_CRACK;
- return spec;
+ top_layer->material_flags |= MATERIAL_FLAG_CRACK;
}
/*
Gets node tile given a face direction.
*/
-TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 dir, MeshMakeData *data)
+void getNodeTile(MapNode mn, v3s16 p, v3s16 dir, MeshMakeData *data, TileSpec &tile)
{
INodeDefManager *ndef = data->m_client->ndef();
@@ -732,10 +768,8 @@ TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 dir, MeshMakeData *data)
};
u16 tile_index=facedir*16 + dir_i;
- TileSpec spec = getNodeTileN(mn, p, dir_to_tile[tile_index], data);
- spec.rotation=dir_to_tile[tile_index + 1];
- spec.texture = data->m_client->tsrc()->getTexture(spec.texture_id);
- return spec;
+ getNodeTileN(mn, p, dir_to_tile[tile_index], data, tile);
+ tile.rotation = dir_to_tile[tile_index + 1];
}
static void getTileInfo(
@@ -755,7 +789,7 @@ static void getTileInfo(
INodeDefManager *ndef = data->m_client->ndef();
v3s16 blockpos_nodes = data->m_blockpos * MAP_BLOCKSIZE;
- MapNode &n0 = vmanip.getNodeRefUnsafe(blockpos_nodes + p);
+ const MapNode &n0 = vmanip.getNodeRefUnsafe(blockpos_nodes + p);
// Don't even try to get n1 if n0 is already CONTENT_IGNORE
if (n0.getContent() == CONTENT_IGNORE) {
@@ -763,8 +797,7 @@ static void getTileInfo(
return;
}
- const MapNode &n1 = vmanip.getNodeRefUnsafeCheckFlags(
- blockpos_nodes + p + face_dir);
+ const MapNode &n1 = vmanip.getNodeRefUnsafeCheckFlags(blockpos_nodes + p + face_dir);
if (n1.getContent() == CONTENT_IGNORE) {
makes_face = false;
@@ -776,8 +809,7 @@ static void getTileInfo(
u8 mf = face_contents(n0.getContent(), n1.getContent(),
&equivalent, ndef);
- if(mf == 0)
- {
+ if (mf == 0) {
makes_face = false;
return;
}
@@ -794,32 +826,31 @@ static void getTileInfo(
p_corrected = p + face_dir;
face_dir_corrected = -face_dir;
}
- tile = getNodeTile(n, p_corrected, face_dir_corrected, data);
+
+ getNodeTile(n, p_corrected, face_dir_corrected, data, tile);
const ContentFeatures &f = ndef->get(n);
tile.emissive_light = f.light_source;
// eg. water and glass
- if (equivalent)
- tile.material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
+ if (equivalent) {
+ for (int layernum = 0; layernum < MAX_TILE_LAYERS; layernum++)
+ tile.layers[layernum].material_flags |=
+ MATERIAL_FLAG_BACKFACE_CULLING;
+ }
- if (data->m_smooth_lighting == false)
- {
+ if (!data->m_smooth_lighting) {
lights[0] = lights[1] = lights[2] = lights[3] =
getFaceLight(n0, n1, face_dir, ndef);
}
- else
- {
+ else {
v3s16 vertex_dirs[4];
getNodeVertexDirs(face_dir_corrected, vertex_dirs);
- for(u16 i=0; i<4; i++)
- {
- lights[i] = getSmoothLight(
- blockpos_nodes + p_corrected,
- vertex_dirs[i], data);
+
+ v3s16 light_p = blockpos_nodes + p_corrected;
+ for (u16 i = 0; i < 4; i++) {
+ lights[i] = getSmoothLight(light_p, vertex_dirs[i], data);
}
}
-
- return;
}
/*
@@ -876,16 +907,8 @@ static void updateFastFaceRow(
if (next_makes_face == makes_face
&& next_p_corrected == p_corrected + translate_dir
&& next_face_dir_corrected == face_dir_corrected
- && next_lights[0] == lights[0]
- && next_lights[1] == lights[1]
- && next_lights[2] == lights[2]
- && next_lights[3] == lights[3]
- && next_tile == tile
- && tile.rotation == 0
- && (tile.material_flags & MATERIAL_FLAG_TILEABLE_HORIZONTAL)
- && (tile.material_flags & MATERIAL_FLAG_TILEABLE_VERTICAL)
- && tile.color == next_tile.color
- && tile.emissive_light == next_tile.emissive_light) {
+ && memcmp(next_lights, lights, ARRLEN(lights) * sizeof(u16)) == 0
+ && next_tile.isTileable(tile)) {
next_is_different = false;
continuous_tiles_count++;
}
@@ -899,7 +922,8 @@ static void updateFastFaceRow(
// Floating point conversion of the position vector
v3f pf(p_corrected.X, p_corrected.Y, p_corrected.Z);
// Center point of face (kind of)
- v3f sp = pf - ((f32)continuous_tiles_count / 2.0 - 0.5) * translate_dir_f;
+ v3f sp = pf -
+ ((f32)continuous_tiles_count / 2.0f - 0.5f) * translate_dir_f;
v3f scale(1,1,1);
if(translate_dir.X != 0) {
@@ -988,7 +1012,6 @@ static void updateAllFastFaceRows(MeshMakeData *data,
*/
MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
- m_mesh(new scene::SMesh()),
m_minimap_mapblock(NULL),
m_client(data->m_client),
m_driver(m_client->tsrc()->getDevice()->getVideoDriver()),
@@ -1000,6 +1023,8 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
m_last_daynight_ratio((u32) -1),
m_daynight_diffs()
{
+ for (int m = 0; m < MAX_TILE_LAYERS; m++)
+ m_mesh[m] = new scene::SMesh();
m_enable_shaders = data->m_use_shaders;
m_use_tangent_vertices = data->m_use_tangent_vertices;
m_enable_vbo = g_settings->getBool("enable_vbo");
@@ -1048,23 +1073,14 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
const u16 indices[] = {0,1,2,2,3,0};
const u16 indices_alternate[] = {0,1,3,2,3,1};
- if(f.tile.texture == NULL)
+ if (f.layer.texture == NULL)
continue;
- const u16 *indices_p = indices;
-
- /*
- Revert triangles for nicer looking gradient if the
- brightness of vertices 1 and 3 differ less than
- the brightness of vertices 0 and 2.
- */
- if (fabs(f.vertices[0].Color.getLuminance()
- - f.vertices[2].Color.getLuminance())
- > fabs(f.vertices[1].Color.getLuminance()
- - f.vertices[3].Color.getLuminance()))
- indices_p = indices_alternate;
+ const u16 *indices_p =
+ f.vertex_0_2_connected ? indices : indices_alternate;
- collector.append(f.tile, f.vertices, 4, indices_p, 6);
+ collector.append(f.layer, f.vertices, 4, indices_p, 6,
+ f.layernum);
}
}
@@ -1081,146 +1097,150 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
generator.generate();
}
+ collector.applyTileColors();
+
/*
Convert MeshCollector to SMesh
*/
- for(u32 i = 0; i < collector.prebuffers.size(); i++)
- {
- PreMeshBuffer &p = collector.prebuffers[i];
-
- // Generate animation data
- // - Cracks
- if(p.tile.material_flags & MATERIAL_FLAG_CRACK)
+ for (int layer = 0; layer < MAX_TILE_LAYERS; layer++) {
+ for(u32 i = 0; i < collector.prebuffers[layer].size(); i++)
{
- // Find the texture name plus ^[crack:N:
- std::ostringstream os(std::ios::binary);
- os<<m_tsrc->getTextureName(p.tile.texture_id)<<"^[crack";
- if(p.tile.material_flags & MATERIAL_FLAG_CRACK_OVERLAY)
- os<<"o"; // use ^[cracko
- os<<":"<<(u32)p.tile.animation_frame_count<<":";
- m_crack_materials.insert(std::make_pair(i, os.str()));
- // Replace tile texture with the cracked one
- p.tile.texture = m_tsrc->getTextureForMesh(
- os.str()+"0",
- &p.tile.texture_id);
- }
- // - Texture animation
- if (p.tile.material_flags & MATERIAL_FLAG_ANIMATION) {
- // Add to MapBlockMesh in order to animate these tiles
- m_animation_tiles[i] = p.tile;
- m_animation_frames[i] = 0;
- if(g_settings->getBool("desynchronize_mapblock_texture_animation")){
- // Get starting position from noise
- m_animation_frame_offsets[i] = 100000 * (2.0 + noise3d(
- data->m_blockpos.X, data->m_blockpos.Y,
- data->m_blockpos.Z, 0));
- } else {
- // Play all synchronized
- m_animation_frame_offsets[i] = 0;
- }
- // Replace tile texture with the first animation frame
- FrameSpec animation_frame = p.tile.frames[0];
- p.tile.texture = animation_frame.texture;
- }
+ PreMeshBuffer &p = collector.prebuffers[layer][i];
- if (!m_enable_shaders) {
- // Extract colors for day-night animation
- // Dummy sunlight to handle non-sunlit areas
- video::SColorf sunlight;
- get_sunlight_color(&sunlight, 0);
- u32 vertex_count =
- m_use_tangent_vertices ?
- p.tangent_vertices.size() : p.vertices.size();
- for (u32 j = 0; j < vertex_count; j++) {
- video::SColor *vc;
- if (m_use_tangent_vertices) {
- vc = &p.tangent_vertices[j].Color;
+ // Generate animation data
+ // - Cracks
+ if(p.layer.material_flags & MATERIAL_FLAG_CRACK)
+ {
+ // Find the texture name plus ^[crack:N:
+ std::ostringstream os(std::ios::binary);
+ os<<m_tsrc->getTextureName(p.layer.texture_id)<<"^[crack";
+ if(p.layer.material_flags & MATERIAL_FLAG_CRACK_OVERLAY)
+ os<<"o"; // use ^[cracko
+ os<<":"<<(u32)p.layer.animation_frame_count<<":";
+ m_crack_materials.insert(std::make_pair(std::pair<u8, u32>(layer, i), os.str()));
+ // Replace tile texture with the cracked one
+ p.layer.texture = m_tsrc->getTextureForMesh(
+ os.str()+"0",
+ &p.layer.texture_id);
+ }
+ // - Texture animation
+ if (p.layer.material_flags & MATERIAL_FLAG_ANIMATION) {
+ // Add to MapBlockMesh in order to animate these tiles
+ m_animation_tiles[std::pair<u8, u32>(layer, i)] = p.layer;
+ m_animation_frames[std::pair<u8, u32>(layer, i)] = 0;
+ if(g_settings->getBool("desynchronize_mapblock_texture_animation")){
+ // Get starting position from noise
+ m_animation_frame_offsets[std::pair<u8, u32>(layer, i)] = 100000 * (2.0 + noise3d(
+ data->m_blockpos.X, data->m_blockpos.Y,
+ data->m_blockpos.Z, 0));
} else {
- vc = &p.vertices[j].Color;
+ // Play all synchronized
+ m_animation_frame_offsets[std::pair<u8, u32>(layer, i)] = 0;
}
- video::SColor copy(*vc);
- if (vc->getAlpha() == 0) // No sunlight - no need to animate
- final_color_blend(vc, copy, sunlight); // Finalize color
- else // Record color to animate
- m_daynight_diffs[i][j] = copy;
-
- // The sunlight ratio has been stored,
- // delete alpha (for the final rendering).
- vc->setAlpha(255);
+ // Replace tile texture with the first animation frame
+ p.layer.texture = p.layer.frames[0].texture;
}
- }
- // Create material
- video::SMaterial material;
- material.setFlag(video::EMF_LIGHTING, false);
- material.setFlag(video::EMF_BACK_FACE_CULLING, true);
- material.setFlag(video::EMF_BILINEAR_FILTER, false);
- material.setFlag(video::EMF_FOG_ENABLE, true);
- material.setTexture(0, p.tile.texture);
+ if (!m_enable_shaders) {
+ // Extract colors for day-night animation
+ // Dummy sunlight to handle non-sunlit areas
+ video::SColorf sunlight;
+ get_sunlight_color(&sunlight, 0);
+ u32 vertex_count =
+ m_use_tangent_vertices ?
+ p.tangent_vertices.size() : p.vertices.size();
+ for (u32 j = 0; j < vertex_count; j++) {
+ video::SColor *vc;
+ if (m_use_tangent_vertices) {
+ vc = &p.tangent_vertices[j].Color;
+ } else {
+ vc = &p.vertices[j].Color;
+ }
+ video::SColor copy(*vc);
+ if (vc->getAlpha() == 0) // No sunlight - no need to animate
+ final_color_blend(vc, copy, sunlight); // Finalize color
+ else // Record color to animate
+ m_daynight_diffs[std::pair<u8, u32>(layer, i)][j] = copy;
+
+ // The sunlight ratio has been stored,
+ // delete alpha (for the final rendering).
+ vc->setAlpha(255);
+ }
+ }
- if (m_enable_shaders) {
- material.MaterialType = m_shdrsrc->getShaderInfo(p.tile.shader_id).material;
- p.tile.applyMaterialOptionsWithShaders(material);
- if (p.tile.normal_texture) {
- material.setTexture(1, p.tile.normal_texture);
+ // Create material
+ video::SMaterial material;
+ material.setFlag(video::EMF_LIGHTING, false);
+ material.setFlag(video::EMF_BACK_FACE_CULLING, true);
+ material.setFlag(video::EMF_BILINEAR_FILTER, false);
+ material.setFlag(video::EMF_FOG_ENABLE, true);
+ material.setTexture(0, p.layer.texture);
+
+ if (m_enable_shaders) {
+ material.MaterialType = m_shdrsrc->getShaderInfo(p.layer.shader_id).material;
+ p.layer.applyMaterialOptionsWithShaders(material);
+ if (p.layer.normal_texture) {
+ material.setTexture(1, p.layer.normal_texture);
+ }
+ material.setTexture(2, p.layer.flags_texture);
+ } else {
+ p.layer.applyMaterialOptions(material);
+ }
+
+ scene::SMesh *mesh = (scene::SMesh *)m_mesh[layer];
+
+ // Create meshbuffer, add to mesh
+ if (m_use_tangent_vertices) {
+ scene::SMeshBufferTangents *buf = new scene::SMeshBufferTangents();
+ // Set material
+ buf->Material = material;
+ // Add to mesh
+ mesh->addMeshBuffer(buf);
+ // Mesh grabbed it
+ buf->drop();
+ buf->append(&p.tangent_vertices[0], p.tangent_vertices.size(),
+ &p.indices[0], p.indices.size());
+ } else {
+ scene::SMeshBuffer *buf = new scene::SMeshBuffer();
+ // Set material
+ buf->Material = material;
+ // Add to mesh
+ mesh->addMeshBuffer(buf);
+ // Mesh grabbed it
+ buf->drop();
+ buf->append(&p.vertices[0], p.vertices.size(),
+ &p.indices[0], p.indices.size());
}
- material.setTexture(2, p.tile.flags_texture);
- } else {
- p.tile.applyMaterialOptions(material);
}
- scene::SMesh *mesh = (scene::SMesh *)m_mesh;
- // Create meshbuffer, add to mesh
+ /*
+ Do some stuff to the mesh
+ */
+ m_camera_offset = camera_offset;
+ translateMesh(m_mesh[layer],
+ intToFloat(data->m_blockpos * MAP_BLOCKSIZE - camera_offset, BS));
+
if (m_use_tangent_vertices) {
- scene::SMeshBufferTangents *buf = new scene::SMeshBufferTangents();
- // Set material
- buf->Material = material;
- // Add to mesh
- mesh->addMeshBuffer(buf);
- // Mesh grabbed it
- buf->drop();
- buf->append(&p.tangent_vertices[0], p.tangent_vertices.size(),
- &p.indices[0], p.indices.size());
- } else {
- scene::SMeshBuffer *buf = new scene::SMeshBuffer();
- // Set material
- buf->Material = material;
- // Add to mesh
- mesh->addMeshBuffer(buf);
- // Mesh grabbed it
- buf->drop();
- buf->append(&p.vertices[0], p.vertices.size(),
- &p.indices[0], p.indices.size());
+ scene::IMeshManipulator* meshmanip =
+ m_client->getSceneManager()->getMeshManipulator();
+ meshmanip->recalculateTangents(m_mesh[layer], true, false, false);
}
- }
-
- /*
- Do some stuff to the mesh
- */
- m_camera_offset = camera_offset;
- translateMesh(m_mesh,
- intToFloat(data->m_blockpos * MAP_BLOCKSIZE - camera_offset, BS));
-
- if (m_use_tangent_vertices) {
- scene::IMeshManipulator* meshmanip =
- m_client->getSceneManager()->getMeshManipulator();
- meshmanip->recalculateTangents(m_mesh, true, false, false);
- }
- if (m_mesh)
- {
+ if (m_mesh[layer])
+ {
#if 0
- // Usually 1-700 faces and 1-7 materials
- std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
- <<"and uses "<<m_mesh->getMeshBufferCount()
- <<" materials (meshbuffers)"<<std::endl;
+ // Usually 1-700 faces and 1-7 materials
+ std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
+ <<"and uses "<<m_mesh[layer]->getMeshBufferCount()
+ <<" materials (meshbuffers)"<<std::endl;
#endif
- // Use VBO for mesh (this just would set this for ever buffer)
- if (m_enable_vbo) {
- m_mesh->setHardwareMappingHint(scene::EHM_STATIC);
+ // Use VBO for mesh (this just would set this for ever buffer)
+ if (m_enable_vbo) {
+ m_mesh[layer]->setHardwareMappingHint(scene::EHM_STATIC);
+ }
}
}
@@ -1235,14 +1255,15 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
MapBlockMesh::~MapBlockMesh()
{
- if (m_enable_vbo && m_mesh) {
- for (u32 i = 0; i < m_mesh->getMeshBufferCount(); i++) {
- scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i);
- m_driver->removeHardwareBuffer(buf);
- }
+ for (int m = 0; m < MAX_TILE_LAYERS; m++) {
+ if (m_enable_vbo && m_mesh[m])
+ for (u32 i = 0; i < m_mesh[m]->getMeshBufferCount(); i++) {
+ scene::IMeshBuffer *buf = m_mesh[m]->getMeshBuffer(i);
+ m_driver->removeHardwareBuffer(buf);
+ }
+ m_mesh[m]->drop();
+ m_mesh[m] = NULL;
}
- m_mesh->drop();
- m_mesh = NULL;
delete m_minimap_mapblock;
}
@@ -1259,9 +1280,10 @@ bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_rat
// Cracks
if(crack != m_last_crack)
{
- for (UNORDERED_MAP<u32, std::string>::iterator i = m_crack_materials.begin();
- i != m_crack_materials.end(); ++i) {
- scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
+ for (std::map<std::pair<u8, u32>, std::string>::iterator i =
+ m_crack_materials.begin(); i != m_crack_materials.end(); ++i) {
+ scene::IMeshBuffer *buf = m_mesh[i->first.first]->
+ getMeshBuffer(i->first.second);
std::string basename = i->second;
// Create new texture name from original
@@ -1274,10 +1296,10 @@ bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_rat
// If the current material is also animated,
// update animation info
- UNORDERED_MAP<u32, TileSpec>::iterator anim_iter =
- m_animation_tiles.find(i->first);
+ std::map<std::pair<u8, u32>, TileLayer>::iterator anim_iter =
+ m_animation_tiles.find(i->first);
if (anim_iter != m_animation_tiles.end()){
- TileSpec &tile = anim_iter->second;
+ TileLayer &tile = anim_iter->second;
tile.texture = new_texture;
tile.texture_id = new_texture_id;
// force animation update
@@ -1289,9 +1311,9 @@ bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_rat
}
// Texture animation
- for (UNORDERED_MAP<u32, TileSpec>::iterator i = m_animation_tiles.begin();
- i != m_animation_tiles.end(); ++i) {
- const TileSpec &tile = i->second;
+ for (std::map<std::pair<u8, u32>, TileLayer>::iterator i =
+ m_animation_tiles.begin(); i != m_animation_tiles.end(); ++i) {
+ const TileLayer &tile = i->second;
// Figure out current frame
int frameoffset = m_animation_frame_offsets[i->first];
int frame = (int)(time * 1000 / tile.animation_frame_length_ms
@@ -1302,9 +1324,10 @@ bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_rat
m_animation_frames[i->first] = frame;
- scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
+ scene::IMeshBuffer *buf = m_mesh[i->first.first]->
+ getMeshBuffer(i->first.second);
- FrameSpec animation_frame = tile.frames[frame];
+ const FrameSpec &animation_frame = tile.frames[frame];
buf->getMaterial().setTexture(0, animation_frame.texture);
if (m_enable_shaders) {
if (animation_frame.normal_texture) {
@@ -1318,22 +1341,24 @@ bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_rat
if(!m_enable_shaders && (daynight_ratio != m_last_daynight_ratio))
{
// Force reload mesh to VBO
- if (m_enable_vbo) {
- m_mesh->setDirty();
- }
+ if (m_enable_vbo)
+ for (int m = 0; m < MAX_TILE_LAYERS; m++)
+ m_mesh[m]->setDirty();
video::SColorf day_color;
get_sunlight_color(&day_color, daynight_ratio);
- for(std::map<u32, std::map<u32, video::SColor > >::iterator
+ for(std::map<std::pair<u8, u32>, std::map<u32, video::SColor > >::iterator
i = m_daynight_diffs.begin();
i != m_daynight_diffs.end(); ++i)
{
- scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
+ scene::IMeshBuffer *buf = m_mesh[i->first.first]->
+ getMeshBuffer(i->first.second);
video::S3DVertex *vertices = (video::S3DVertex *)buf->getVertices();
for(std::map<u32, video::SColor >::iterator
j = i->second.begin();
j != i->second.end(); ++j)
{
- final_color_blend(&(vertices[j->first].Color), j->second, day_color);
+ final_color_blend(&(vertices[j->first].Color),
+ j->second, day_color);
}
}
m_last_daynight_ratio = daynight_ratio;
@@ -1345,9 +1370,12 @@ bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_rat
void MapBlockMesh::updateCameraOffset(v3s16 camera_offset)
{
if (camera_offset != m_camera_offset) {
- translateMesh(m_mesh, intToFloat(m_camera_offset-camera_offset, BS));
- if (m_enable_vbo) {
- m_mesh->setDirty();
+ for (u8 layer = 0; layer < 2; layer++) {
+ translateMesh(m_mesh[layer],
+ intToFloat(m_camera_offset - camera_offset, BS));
+ if (m_enable_vbo) {
+ m_mesh[layer]->setDirty();
+ }
}
m_camera_offset = camera_offset;
}
@@ -1361,15 +1389,29 @@ void MeshCollector::append(const TileSpec &tile,
const video::S3DVertex *vertices, u32 numVertices,
const u16 *indices, u32 numIndices)
{
+ for (int layernum = 0; layernum < MAX_TILE_LAYERS; layernum++) {
+ const TileLayer *layer = &tile.layers[layernum];
+ if (layer->texture_id == 0)
+ continue;
+ append(*layer, vertices, numVertices, indices, numIndices,
+ layernum);
+ }
+}
+
+void MeshCollector::append(const TileLayer &layer,
+ const video::S3DVertex *vertices, u32 numVertices,
+ const u16 *indices, u32 numIndices, u8 layernum)
+{
if (numIndices > 65535) {
dstream<<"FIXME: MeshCollector::append() called with numIndices="<<numIndices<<" (limit 65535)"<<std::endl;
return;
}
+ std::vector<PreMeshBuffer> *buffers = &prebuffers[layernum];
PreMeshBuffer *p = NULL;
- for (u32 i = 0; i < prebuffers.size(); i++) {
- PreMeshBuffer &pp = prebuffers[i];
- if (pp.tile != tile)
+ for (u32 i = 0; i < buffers->size(); i++) {
+ PreMeshBuffer &pp = (*buffers)[i];
+ if (pp.layer != layer)
continue;
if (pp.indices.size() + numIndices > 65535)
continue;
@@ -1380,9 +1422,9 @@ void MeshCollector::append(const TileSpec &tile,
if (p == NULL) {
PreMeshBuffer pp;
- pp.tile = tile;
- prebuffers.push_back(pp);
- p = &prebuffers[prebuffers.size() - 1];
+ pp.layer = layer;
+ buffers->push_back(pp);
+ p = &(*buffers)[buffers->size() - 1];
}
u32 vertex_count;
@@ -1417,15 +1459,30 @@ void MeshCollector::append(const TileSpec &tile,
const u16 *indices, u32 numIndices,
v3f pos, video::SColor c, u8 light_source)
{
+ for (int layernum = 0; layernum < MAX_TILE_LAYERS; layernum++) {
+ const TileLayer *layer = &tile.layers[layernum];
+ if (layer->texture_id == 0)
+ continue;
+ append(*layer, vertices, numVertices, indices, numIndices, pos,
+ c, light_source, layernum);
+ }
+}
+
+void MeshCollector::append(const TileLayer &layer,
+ const video::S3DVertex *vertices, u32 numVertices,
+ const u16 *indices, u32 numIndices,
+ v3f pos, video::SColor c, u8 light_source, u8 layernum)
+{
if (numIndices > 65535) {
dstream<<"FIXME: MeshCollector::append() called with numIndices="<<numIndices<<" (limit 65535)"<<std::endl;
return;
}
+ std::vector<PreMeshBuffer> *buffers = &prebuffers[layernum];
PreMeshBuffer *p = NULL;
- for (u32 i = 0; i < prebuffers.size(); i++) {
- PreMeshBuffer &pp = prebuffers[i];
- if(pp.tile != tile)
+ for (u32 i = 0; i < buffers->size(); i++) {
+ PreMeshBuffer &pp = (*buffers)[i];
+ if(pp.layer != layer)
continue;
if(pp.indices.size() + numIndices > 65535)
continue;
@@ -1436,9 +1493,9 @@ void MeshCollector::append(const TileSpec &tile,
if (p == NULL) {
PreMeshBuffer pp;
- pp.tile = tile;
- prebuffers.push_back(pp);
- p = &prebuffers[prebuffers.size() - 1];
+ pp.layer = layer;
+ buffers->push_back(pp);
+ p = &(*buffers)[buffers->size() - 1];
}
video::SColor original_c = c;
@@ -1473,14 +1530,49 @@ void MeshCollector::append(const TileSpec &tile,
}
}
-video::SColor encode_light_and_color(u16 light, const video::SColor &color,
- u8 emissive_light)
+void MeshCollector::applyTileColors()
+{
+ if (m_use_tangent_vertices)
+ for (int layer = 0; layer < MAX_TILE_LAYERS; layer++) {
+ std::vector<PreMeshBuffer> *p = &prebuffers[layer];
+ for (std::vector<PreMeshBuffer>::iterator it = p->begin();
+ it != p->end(); ++it) {
+ video::SColor tc = it->layer.color;
+ if (tc == video::SColor(0xFFFFFFFF))
+ continue;
+ for (u32 index = 0; index < it->tangent_vertices.size(); index++) {
+ video::SColor *c = &it->tangent_vertices[index].Color;
+ c->set(c->getAlpha(), c->getRed() * tc.getRed() / 255,
+ c->getGreen() * tc.getGreen() / 255,
+ c->getBlue() * tc.getBlue() / 255);
+ }
+ }
+ }
+ else
+ for (int layer = 0; layer < MAX_TILE_LAYERS; layer++) {
+ std::vector<PreMeshBuffer> *p = &prebuffers[layer];
+ for (std::vector<PreMeshBuffer>::iterator it = p->begin();
+ it != p->end(); ++it) {
+ video::SColor tc = it->layer.color;
+ if (tc == video::SColor(0xFFFFFFFF))
+ continue;
+ for (u32 index = 0; index < it->vertices.size(); index++) {
+ video::SColor *c = &it->vertices[index].Color;
+ c->set(c->getAlpha(), c->getRed() * tc.getRed() / 255,
+ c->getGreen() * tc.getGreen() / 255,
+ c->getBlue() * tc.getBlue() / 255);
+ }
+ }
+ }
+}
+
+video::SColor encode_light(u16 light, u8 emissive_light)
{
// Get components
- f32 day = (light & 0xff) / 255.0f;
- f32 night = (light >> 8) / 255.0f;
+ u32 day = (light & 0xff);
+ u32 night = (light >> 8);
// Add emissive light
- night += emissive_light * 0.01f;
+ night += emissive_light * 2.5f;
if (night > 255)
night = 255;
// Since we don't know if the day light is sunlight or
@@ -1490,15 +1582,14 @@ video::SColor encode_light_and_color(u16 light, const video::SColor &color,
day = 0;
else
day = day - night;
- f32 sum = day + night;
+ u32 sum = day + night;
// Ratio of sunlight:
- float r;
+ u32 r;
if (sum > 0)
- r = day / sum;
+ r = day * 255 / sum;
else
r = 0;
// Average light:
float b = (day + night) / 2;
- return video::SColor(r * 255, b * color.getRed(), b * color.getGreen(),
- b * color.getBlue());
+ return video::SColor(r, b, b, b);
}
diff --git a/src/mapblock_mesh.h b/src/mapblock_mesh.h
index 25c699e1c..93d932a7b 100644
--- a/src/mapblock_mesh.h
+++ b/src/mapblock_mesh.h
@@ -108,7 +108,12 @@ public:
scene::IMesh *getMesh()
{
- return m_mesh;
+ return m_mesh[0];
+ }
+
+ scene::IMesh *getMesh(u8 layer)
+ {
+ return m_mesh[layer];
}
MinimapMapblock *moveMinimapMapblock()
@@ -132,7 +137,7 @@ public:
void updateCameraOffset(v3s16 camera_offset);
private:
- scene::IMesh *m_mesh;
+ scene::IMesh *m_mesh[MAX_TILE_LAYERS];
MinimapMapblock *m_minimap_mapblock;
Client *m_client;
video::IVideoDriver *m_driver;
@@ -150,20 +155,23 @@ private:
// Animation info: cracks
// Last crack value passed to animate()
int m_last_crack;
- // Maps mesh buffer (i.e. material) indices to base texture names
- UNORDERED_MAP<u32, std::string> m_crack_materials;
+ // Maps mesh and mesh buffer (i.e. material) indices to base texture names
+ std::map<std::pair<u8, u32>, std::string> m_crack_materials;
// Animation info: texture animationi
- // Maps meshbuffers to TileSpecs
- UNORDERED_MAP<u32, TileSpec> m_animation_tiles;
- UNORDERED_MAP<u32, int> m_animation_frames; // last animation frame
- UNORDERED_MAP<u32, int> m_animation_frame_offsets;
+ // Maps mesh and mesh buffer indices to TileSpecs
+ // Keys are pairs of (mesh index, buffer index in the mesh)
+ std::map<std::pair<u8, u32>, TileLayer> m_animation_tiles;
+ std::map<std::pair<u8, u32>, int> m_animation_frames; // last animation frame
+ std::map<std::pair<u8, u32>, int> m_animation_frame_offsets;
// Animation info: day/night transitions
// Last daynight_ratio value passed to animate()
u32 m_last_daynight_ratio;
- // For each meshbuffer, stores pre-baked colors of sunlit vertices
- std::map<u32, std::map<u32, video::SColor > > m_daynight_diffs;
+ // For each mesh and mesh buffer, stores pre-baked colors
+ // of sunlit vertices
+ // Keys are pairs of (mesh index, buffer index in the mesh)
+ std::map<std::pair<u8, u32>, std::map<u32, video::SColor > > m_daynight_diffs;
// Camera offset info -> do we have to translate the mesh?
v3s16 m_camera_offset;
@@ -176,7 +184,7 @@ private:
*/
struct PreMeshBuffer
{
- TileSpec tile;
+ TileLayer layer;
std::vector<u16> indices;
std::vector<video::S3DVertex> vertices;
std::vector<video::S3DVertexTangents> tangent_vertices;
@@ -184,7 +192,7 @@ struct PreMeshBuffer
struct MeshCollector
{
- std::vector<PreMeshBuffer> prebuffers;
+ std::vector<PreMeshBuffer> prebuffers[MAX_TILE_LAYERS];
bool m_use_tangent_vertices;
MeshCollector(bool use_tangent_vertices):
@@ -193,27 +201,38 @@ struct MeshCollector
}
void append(const TileSpec &material,
+ const video::S3DVertex *vertices, u32 numVertices,
+ const u16 *indices, u32 numIndices);
+ void append(const TileLayer &material,
const video::S3DVertex *vertices, u32 numVertices,
- const u16 *indices, u32 numIndices);
+ const u16 *indices, u32 numIndices, u8 layernum);
void append(const TileSpec &material,
+ const video::S3DVertex *vertices, u32 numVertices,
+ const u16 *indices, u32 numIndices, v3f pos,
+ video::SColor c, u8 light_source);
+ void append(const TileLayer &material,
const video::S3DVertex *vertices, u32 numVertices,
- const u16 *indices, u32 numIndices,
- v3f pos, video::SColor c, u8 light_source);
+ const u16 *indices, u32 numIndices, v3f pos,
+ video::SColor c, u8 light_source, u8 layernum);
+ /*!
+ * Colorizes all vertices in the collector.
+ */
+ void applyTileColors();
};
/*!
- * Encodes light and color of a node.
+ * Encodes light of a node.
* The result is not the final color, but a
* half-baked vertex color.
+ * You have to multiply the resulting color
+ * with the node's color.
*
* \param light the first 8 bits are day light,
* the last 8 bits are night light
- * \param color the node's color
* \param emissive_light amount of light the surface emits,
* from 0 to LIGHT_SUN.
*/
-video::SColor encode_light_and_color(u16 light, const video::SColor &color,
- u8 emissive_light);
+video::SColor encode_light(u16 light, u8 emissive_light);
// Compute light at node
u16 getInteriorLight(MapNode n, s32 increment, INodeDefManager *ndef);
@@ -248,8 +267,10 @@ void final_color_blend(video::SColor *result,
// Retrieves the TileSpec of a face of a node
// Adds MATERIAL_FLAG_CRACK if the node is cracked
-TileSpec getNodeTileN(MapNode mn, v3s16 p, u8 tileindex, MeshMakeData *data);
-TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 dir, MeshMakeData *data);
+// TileSpec should be passed as reference due to the underlying TileFrame and its vector
+// TileFrame vector copy cost very much to client
+void getNodeTileN(MapNode mn, v3s16 p, u8 tileindex, MeshMakeData *data, TileSpec &tile);
+void getNodeTile(MapNode mn, v3s16 p, v3s16 dir, MeshMakeData *data, TileSpec &tile);
#endif
diff --git a/src/mapgen.cpp b/src/mapgen.cpp
index bd0e94ac7..6fe2906bb 100644
--- a/src/mapgen.cpp
+++ b/src/mapgen.cpp
@@ -1,7 +1,8 @@
/*
Minetest
-Copyright (C) 2010-2015 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
Copyright (C) 2010-2015 celeron55, Perttu Ahola <celeron55@gmail.com>
+Copyright (C) 2013-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
+Copyright (C) 2015-2017 paramat
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
@@ -84,7 +85,7 @@ static MapgenDesc g_reg_mapgens[] = {
{"flat", true},
{"fractal", true},
{"valleys", true},
- {"singlenode", false},
+ {"singlenode", true},
};
STATIC_ASSERT(
@@ -316,7 +317,6 @@ void Mapgen::updateHeightmap(v3s16 nmin, v3s16 nmax)
heightmap[index] = y;
}
}
- //printf("updateHeightmap: %dus\n", t.stop());
}
inline bool Mapgen::isLiquidHorizontallyFlowable(u32 vi, v3s16 em)
@@ -809,7 +809,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);
}
@@ -973,8 +982,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;
@@ -986,9 +994,12 @@ void GenerateNotifier::getEvents(
event_map[name].push_back(gn.pos);
}
+}
+
- if (!peek_events)
- m_notify_events.clear();
+void GenerateNotifier::clearEvents()
+{
+ m_notify_events.clear();
}
@@ -1048,3 +1059,45 @@ void MapgenParams::writeParams(Settings *settings) const
if (bparams)
bparams->writeParams(settings);
}
+
+
+// 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;
+ // Fullminp/fullmaxp of central chunk, in nodes
+ s16 ccfmin = ccmin - MAP_BLOCKSIZE;
+ s16 ccfmax = ccmax + MAP_BLOCKSIZE;
+ // Effective mapgen limit, in blocks
+ // Uses same calculation as ServerMap::blockpos_over_mapgen_limit(v3s16 p)
+ s16 mapgen_limit_b = rangelim(mapgen_limit,
+ 0, MAX_MAP_GENERATION_LIMIT) / MAP_BLOCKSIZE;
+ // Effective mapgen limits, in nodes
+ s16 mapgen_limit_min = -mapgen_limit_b * MAP_BLOCKSIZE;
+ s16 mapgen_limit_max = (mapgen_limit_b + 1) * MAP_BLOCKSIZE - 1;
+ // Number of complete chunks from central chunk fullminp/fullmaxp
+ // to effective mapgen limits.
+ s16 numcmin = MYMAX((ccfmin - mapgen_limit_min) / csize_n, 0);
+ s16 numcmax = MYMAX((mapgen_limit_max - ccfmax) / csize_n, 0);
+ // Mapgen edges, in nodes
+ mapgen_edge_min = ccmin - numcmin * csize_n;
+ mapgen_edge_max = ccmax + numcmax * csize_n;
+
+ m_mapgen_edges_calculated = true;
+}
+
+
+s32 MapgenParams::getSpawnRangeMax()
+{
+ if (!m_mapgen_edges_calculated)
+ calcMapgenEdges();
+
+ return MYMIN(-mapgen_edge_min, mapgen_edge_max);
+}
diff --git a/src/mapgen.h b/src/mapgen.h
index 653b79ed8..4e0d75b39 100644
--- a/src/mapgen.h
+++ b/src/mapgen.h
@@ -1,6 +1,8 @@
/*
Minetest
-Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
+Copyright (C) 2010-2015 celeron55, Perttu Ahola <celeron55@gmail.com>
+Copyright (C) 2013-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
+Copyright (C) 2015-2017 paramat
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
@@ -99,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;
@@ -129,6 +131,9 @@ struct MapgenParams {
BiomeParams *bparams;
+ s16 mapgen_edge_min;
+ s16 mapgen_edge_max;
+
MapgenParams() :
mgtype(MAPGEN_DEFAULT),
chunksize(5),
@@ -136,7 +141,11 @@ struct MapgenParams {
water_level(1),
mapgen_limit(MAX_MAP_GENERATION_LIMIT),
flags(MG_CAVES | MG_LIGHT | MG_DECORATIONS),
- bparams(NULL)
+ bparams(NULL),
+
+ mapgen_edge_min(-MAX_MAP_GENERATION_LIMIT),
+ mapgen_edge_max(MAX_MAP_GENERATION_LIMIT),
+ m_mapgen_edges_calculated(false)
{
}
@@ -144,6 +153,13 @@ struct MapgenParams {
virtual void readParams(const Settings *settings);
virtual void writeParams(Settings *settings) const;
+
+ s32 getSpawnRangeMax();
+
+private:
+ void calcMapgenEdges();
+
+ bool m_mapgen_edges_calculated;
};
diff --git a/src/mapgen_flat.cpp b/src/mapgen_flat.cpp
index 3c6a112e2..604c79dd0 100644
--- a/src/mapgen_flat.cpp
+++ b/src/mapgen_flat.cpp
@@ -1,7 +1,7 @@
/*
Minetest
-Copyright (C) 2010-2015 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
-Copyright (C) 2010-2015 paramat, Matt Gregory
+Copyright (C) 2015-2017 paramat
+Copyright (C) 2015-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
@@ -60,10 +60,12 @@ MapgenFlat::MapgenFlat(int mapgenid, MapgenFlatParams *params, EmergeManager *em
this->hill_threshold = params->hill_threshold;
this->hill_steepness = params->hill_steepness;
- //// 2D noise
- noise_terrain = new Noise(&params->np_terrain, seed, csize.X, csize.Z);
+ // 2D noise
noise_filler_depth = new Noise(&params->np_filler_depth, seed, csize.X, csize.Z);
+ if ((spflags & MGFLAT_LAKES) || (spflags & MGFLAT_HILLS))
+ noise_terrain = new Noise(&params->np_terrain, seed, csize.X, csize.Z);
+ // 3D noise
MapgenBasic::np_cave1 = params->np_cave1;
MapgenBasic::np_cave2 = params->np_cave2;
}
@@ -71,8 +73,10 @@ MapgenFlat::MapgenFlat(int mapgenid, MapgenFlatParams *params, EmergeManager *em
MapgenFlat::~MapgenFlat()
{
- delete noise_terrain;
delete noise_filler_depth;
+
+ if ((spflags & MGFLAT_LAKES) || (spflags & MGFLAT_HILLS))
+ delete noise_terrain;
}
@@ -136,7 +140,9 @@ void MapgenFlatParams::writeParams(Settings *settings) const
int MapgenFlat::getSpawnLevelAtPoint(v2s16 p)
{
s16 level_at_point = ground_level;
- float n_terrain = NoisePerlin2D(&noise_terrain->np, p.X, p.Y, seed);
+ float n_terrain = 0.0f;
+ if ((spflags & MGFLAT_LAKES) || (spflags & MGFLAT_HILLS))
+ n_terrain = NoisePerlin2D(&noise_terrain->np, p.X, p.Y, seed);
if ((spflags & MGFLAT_LAKES) && n_terrain < lake_threshold) {
level_at_point = ground_level -
diff --git a/src/mapgen_flat.h b/src/mapgen_flat.h
index 8b3de2bcf..18b84de76 100644
--- a/src/mapgen_flat.h
+++ b/src/mapgen_flat.h
@@ -1,7 +1,7 @@
/*
Minetest
-Copyright (C) 2010-2015 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
-Copyright (C) 2010-2015 paramat, Matt Gregory
+Copyright (C) 2015-2017 paramat
+Copyright (C) 2015-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
@@ -31,8 +31,8 @@ class BiomeManager;
extern FlagDesc flagdesc_mapgen_flat[];
-
-struct MapgenFlatParams : public MapgenParams {
+struct MapgenFlatParams : public MapgenParams
+{
u32 spflags;
s16 ground_level;
s16 large_cave_depth;
@@ -53,7 +53,8 @@ struct MapgenFlatParams : public MapgenParams {
void writeParams(Settings *settings) const;
};
-class MapgenFlat : public MapgenBasic {
+class MapgenFlat : public MapgenBasic
+{
public:
MapgenFlat(int mapgenid, MapgenFlatParams *params, EmergeManager *emerge);
~MapgenFlat();
diff --git a/src/mapgen_fractal.cpp b/src/mapgen_fractal.cpp
index d48d38b65..faac9e1c1 100644
--- a/src/mapgen_fractal.cpp
+++ b/src/mapgen_fractal.cpp
@@ -1,7 +1,7 @@
/*
Minetest
-Copyright (C) 2010-2015 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
-Copyright (C) 2010-2015 paramat, Matt Gregory
+Copyright (C) 2015-2017 paramat
+Copyright (C) 2015-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/src/mapgen_fractal.h b/src/mapgen_fractal.h
index 3331848bc..a5a09ccb9 100644
--- a/src/mapgen_fractal.h
+++ b/src/mapgen_fractal.h
@@ -1,7 +1,7 @@
/*
Minetest
-Copyright (C) 2010-2015 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
-Copyright (C) 2010-2015 paramat, Matt Gregory
+Copyright (C) 2015-2017 paramat
+Copyright (C) 2015-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
Fractal formulas from http://www.bugman123.com/Hypercomplex/index.html
by Paul Nylander, and from http://www.fractalforums.com, thank you.
@@ -32,8 +32,8 @@ class BiomeManager;
extern FlagDesc flagdesc_mapgen_fractal[];
-
-struct MapgenFractalParams : public MapgenParams {
+struct MapgenFractalParams : public MapgenParams
+{
u32 spflags;
float cave_width;
u16 fractal;
@@ -57,7 +57,8 @@ struct MapgenFractalParams : public MapgenParams {
void writeParams(Settings *settings) const;
};
-class MapgenFractal : public MapgenBasic {
+class MapgenFractal : public MapgenBasic
+{
public:
MapgenFractal(int mapgenid, MapgenFractalParams *params, EmergeManager *emerge);
~MapgenFractal();
diff --git a/src/mapgen_singlenode.cpp b/src/mapgen_singlenode.cpp
index ff985dd34..f49059f7f 100644
--- a/src/mapgen_singlenode.cpp
+++ b/src/mapgen_singlenode.cpp
@@ -1,6 +1,8 @@
/*
Minetest
-Copyright (C) 2010-2015 celeron55, Perttu Ahola <celeron55@gmail.com>
+Copyright (C) 2013-2015 celeron55, Perttu Ahola <celeron55@gmail.com>
+Copyright (C) 2013-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
+Copyright (C) 2015-2017 paramat
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/src/mapgen_singlenode.h b/src/mapgen_singlenode.h
index 07520134d..5171bfbca 100644
--- a/src/mapgen_singlenode.h
+++ b/src/mapgen_singlenode.h
@@ -1,6 +1,8 @@
/*
Minetest
-Copyright (C) 2010-2015 celeron55, Perttu Ahola <celeron55@gmail.com>
+Copyright (C) 2013-2015 celeron55, Perttu Ahola <celeron55@gmail.com>
+Copyright (C) 2013-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
+Copyright (C) 2015-2017 paramat
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
@@ -22,7 +24,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "mapgen.h"
-struct MapgenSinglenodeParams : public MapgenParams {
+struct MapgenSinglenodeParams : public MapgenParams
+{
MapgenSinglenodeParams() {}
~MapgenSinglenodeParams() {}
@@ -30,7 +33,8 @@ struct MapgenSinglenodeParams : public MapgenParams {
void writeParams(Settings *settings) const {}
};
-class MapgenSinglenode : public Mapgen {
+class MapgenSinglenode : public Mapgen
+{
public:
u32 flags;
content_t c_node;
diff --git a/src/mapgen_v5.cpp b/src/mapgen_v5.cpp
index c7079d229..932677e2a 100644
--- a/src/mapgen_v5.cpp
+++ b/src/mapgen_v5.cpp
@@ -1,7 +1,7 @@
/*
Minetest
-Copyright (C) 2010-2015 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
-Copyright (C) 2010-2015 paramat, Matt Gregory
+Copyright (C) 2014-2017 paramat
+Copyright (C) 2014-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
@@ -204,12 +204,12 @@ void MapgenV5::makeChunk(BlockMakeData *data)
// Generate caverns, tunnels and classic caves
if (flags & MG_CAVES) {
- bool has_cavern = false;
+ bool near_cavern = false;
// Generate caverns
if (spflags & MGV5_CAVERNS)
- has_cavern = generateCaverns(stone_surface_max_y);
+ near_cavern = generateCaverns(stone_surface_max_y);
// Generate tunnels and classic caves
- if (has_cavern)
+ if (near_cavern)
// Disable classic caves in this mapchunk by setting
// 'large cave depth' to world base. Avoids excessive liquid in
// large caverns and floating blobs of overgenerated liquid.
diff --git a/src/mapgen_v5.h b/src/mapgen_v5.h
index 034d53560..b742638cd 100644
--- a/src/mapgen_v5.h
+++ b/src/mapgen_v5.h
@@ -1,7 +1,7 @@
/*
Minetest
-Copyright (C) 2010-2015 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
-Copyright (C) 2010-2015 paramat, Matt Gregory
+Copyright (C) 2014-2017 paramat
+Copyright (C) 2014-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
@@ -32,8 +32,8 @@ class BiomeManager;
extern FlagDesc flagdesc_mapgen_v5[];
-
-struct MapgenV5Params : public MapgenParams {
+struct MapgenV5Params : public MapgenParams
+{
u32 spflags;
float cave_width;
s16 cavern_limit;
@@ -55,8 +55,8 @@ struct MapgenV5Params : public MapgenParams {
void writeParams(Settings *settings) const;
};
-
-class MapgenV5 : public MapgenBasic {
+class MapgenV5 : public MapgenBasic
+{
public:
MapgenV5(int mapgenid, MapgenV5Params *params, EmergeManager *emerge);
~MapgenV5();
diff --git a/src/mapgen_v6.cpp b/src/mapgen_v6.cpp
index f3e893f58..fe2b0b36f 100644
--- a/src/mapgen_v6.cpp
+++ b/src/mapgen_v6.cpp
@@ -1,6 +1,8 @@
/*
Minetest
Copyright (C) 2010-2015 celeron55, Perttu Ahola <celeron55@gmail.com>
+Copyright (C) 2013-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
+Copyright (C) 2014-2017 paramat
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
@@ -836,13 +838,17 @@ void MapgenV6::flowMud(s16 &mudflow_minpos, s16 &mudflow_maxpos)
v3s16(-1, 0, 0), // left
};
- // Check that upper is air or doesn't exist.
- // Cancel dropping if upper keeps it in place
+ // Check that upper is walkable. Cancel
+ // dropping if upper keeps it in place.
u32 i3 = i;
vm->m_area.add_y(em, i3, 1);
- if (vm->m_area.contains(i3) == true &&
- ndef->get(vm->m_data[i3]).walkable)
- continue;
+ MapNode *n3 = NULL;
+
+ if (vm->m_area.contains(i3)) {
+ n3 = &vm->m_data[i3];
+ if (ndef->get(*n3).walkable)
+ continue;
+ }
// Drop mud on side
for (u32 di = 0; di < 4; di++) {
@@ -885,10 +891,18 @@ void MapgenV6::flowMud(s16 &mudflow_minpos, s16 &mudflow_maxpos)
if (!dropped_to_unknown) {
*n2 = *n;
// Set old place to be air (or water)
- if (old_is_water)
+ if (old_is_water) {
*n = MapNode(c_water_source);
- else
+ } else {
*n = MapNode(CONTENT_AIR);
+ // Upper (n3) is not walkable or is NULL. If it is
+ // not NULL and not air and not water it is a
+ // decoration that needs removing, to avoid
+ // unsupported decorations.
+ if (n3 && n3->getContent() != CONTENT_AIR &&
+ n3->getContent() != c_water_source)
+ *n3 = MapNode(CONTENT_AIR);
+ }
}
// Done
diff --git a/src/mapgen_v6.h b/src/mapgen_v6.h
index 44591e3dc..2b3b4444e 100644
--- a/src/mapgen_v6.h
+++ b/src/mapgen_v6.h
@@ -1,6 +1,8 @@
/*
Minetest
Copyright (C) 2010-2015 celeron55, Perttu Ahola <celeron55@gmail.com>
+Copyright (C) 2013-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
+Copyright (C) 2014-2017 paramat
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/src/mapgen_v7.cpp b/src/mapgen_v7.cpp
index c9b6b48e0..44a42948b 100644
--- a/src/mapgen_v7.cpp
+++ b/src/mapgen_v7.cpp
@@ -1,7 +1,7 @@
/*
Minetest
-Copyright (C) 2010-2015 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
-Copyright (C) 2010-2015 paramat, Matt Gregory
+Copyright (C) 2013-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
+Copyright (C) 2014-2017 paramat
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
@@ -57,29 +57,41 @@ 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;
- //// Terrain noise
- noise_terrain_base = new Noise(&params->np_terrain_base, seed, csize.X, csize.Z);
- noise_terrain_alt = new Noise(&params->np_terrain_alt, seed, csize.X, csize.Z);
- noise_terrain_persist = new Noise(&params->np_terrain_persist, seed, csize.X, csize.Z);
- noise_height_select = new Noise(&params->np_height_select, seed, csize.X, csize.Z);
- noise_filler_depth = new Noise(&params->np_filler_depth, seed, csize.X, csize.Z);
- noise_mount_height = new Noise(&params->np_mount_height, seed, csize.X, csize.Z);
- noise_ridge_uwater = new Noise(&params->np_ridge_uwater, seed, csize.X, csize.Z);
- noise_floatland_base = new Noise(&params->np_floatland_base, seed, csize.X, csize.Z);
- noise_float_base_height = new Noise(&params->np_float_base_height, seed, csize.X, csize.Z);
-
- //// 3d terrain noise
- // 1-up 1-down overgeneration
- noise_mountain = new Noise(&params->np_mountain, seed, csize.X, csize.Y + 2, csize.Z);
- noise_ridge = new Noise(&params->np_ridge, seed, csize.X, csize.Y + 2, csize.Z);
- // 1 down overgeneration
+ // This is to avoid a divide-by-zero.
+ // Parameter will be saved to map_meta.txt in limited form.
+ params->float_mount_height = MYMAX(params->float_mount_height, 1.0f);
+ this->float_mount_height = params->float_mount_height;
+
+ // 2D noise
+ noise_terrain_base = new Noise(&params->np_terrain_base, seed, csize.X, csize.Z);
+ noise_terrain_alt = new Noise(&params->np_terrain_alt, seed, csize.X, csize.Z);
+ noise_terrain_persist = new Noise(&params->np_terrain_persist, seed, csize.X, csize.Z);
+ noise_height_select = new Noise(&params->np_height_select, seed, csize.X, csize.Z);
+ noise_filler_depth = new Noise(&params->np_filler_depth, seed, csize.X, csize.Z);
+
+ if (spflags & MGV7_MOUNTAINS)
+ noise_mount_height = new Noise(&params->np_mount_height, seed, csize.X, csize.Z);
+
+ if (spflags & MGV7_FLOATLANDS) {
+ noise_floatland_base = new Noise(&params->np_floatland_base, seed, csize.X, csize.Z);
+ noise_float_base_height = new Noise(&params->np_float_base_height, seed, csize.X, csize.Z);
+ }
+
+ if (spflags & MGV7_RIDGES) {
+ noise_ridge_uwater = new Noise(&params->np_ridge_uwater, seed, csize.X, csize.Z);
+ // 3D noise, 1-up 1-down overgeneration
+ noise_ridge = new Noise(&params->np_ridge, seed, csize.X, csize.Y + 2, csize.Z);
+ }
+ // 3D noise, 1 up, 1 down overgeneration
+ if ((spflags & MGV7_MOUNTAINS) || (spflags & MGV7_FLOATLANDS))
+ noise_mountain = new Noise(&params->np_mountain, seed, csize.X, csize.Y + 2, csize.Z);
+ // 3D noise, 1 down overgeneration
MapgenBasic::np_cave1 = params->np_cave1;
MapgenBasic::np_cave2 = params->np_cave2;
MapgenBasic::np_cavern = params->np_cavern;
@@ -89,16 +101,26 @@ MapgenV7::MapgenV7(int mapgenid, MapgenV7Params *params, EmergeManager *emerge)
MapgenV7::~MapgenV7()
{
delete noise_terrain_base;
+ delete noise_terrain_alt;
delete noise_terrain_persist;
delete noise_height_select;
- delete noise_terrain_alt;
delete noise_filler_depth;
- delete noise_mount_height;
- delete noise_ridge_uwater;
- delete noise_floatland_base;
- delete noise_float_base_height;
- delete noise_mountain;
- delete noise_ridge;
+
+ if (spflags & MGV7_MOUNTAINS)
+ delete noise_mount_height;
+
+ if (spflags & MGV7_FLOATLANDS) {
+ delete noise_floatland_base;
+ delete noise_float_base_height;
+ }
+
+ if (spflags & MGV7_RIDGES) {
+ delete noise_ridge_uwater;
+ delete noise_ridge;
+ }
+
+ if ((spflags & MGV7_MOUNTAINS) || (spflags & MGV7_FLOATLANDS))
+ delete noise_mountain;
}
@@ -274,12 +296,12 @@ void MapgenV7::makeChunk(BlockMakeData *data)
// Generate caverns, tunnels and classic caves
if (flags & MG_CAVES) {
- bool has_cavern = false;
+ bool near_cavern = false;
// Generate caverns
if (spflags & MGV7_CAVERNS)
- has_cavern = generateCaverns(stone_surface_max_y);
+ near_cavern = generateCaverns(stone_surface_max_y);
// Generate tunnels and classic caves
- if (has_cavern)
+ if (near_cavern)
// Disable classic caves in this mapchunk by setting
// 'large cave depth' to world base. Avoids excessive liquid in
// large caverns and floating blobs of overgenerated liquid.
@@ -358,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);
@@ -368,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];
@@ -397,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;
diff --git a/src/mapgen_v7.h b/src/mapgen_v7.h
index 71a341afe..a69170057 100644
--- a/src/mapgen_v7.h
+++ b/src/mapgen_v7.h
@@ -1,7 +1,7 @@
/*
Minetest
-Copyright (C) 2010-2015 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
-Copyright (C) 2010-2015 paramat, Matt Gregory
+Copyright (C) 2013-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
+Copyright (C) 2014-2017 paramat
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/src/mapgen_valleys.cpp b/src/mapgen_valleys.cpp
index 32a32eb88..df318291c 100644
--- a/src/mapgen_valleys.cpp
+++ b/src/mapgen_valleys.cpp
@@ -1,8 +1,7 @@
/*
Minetest Valleys C
-Copyright (C) 2010-2015 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
-Copyright (C) 2010-2015 paramat, Matt Gregory
-Copyright (C) 2016 Duane Robertson <duane@duanerobertson.com>
+Copyright (C) 2016-2017 Duane Robertson <duane@duanerobertson.com>
+Copyright (C) 2016-2017 paramat
Based on Valleys Mapgen by Gael de Sailly
(https://forum.minetest.net/viewtopic.php?f=9&t=11430)
diff --git a/src/mapgen_valleys.h b/src/mapgen_valleys.h
index 0c67c3232..8a32a5a82 100644
--- a/src/mapgen_valleys.h
+++ b/src/mapgen_valleys.h
@@ -1,8 +1,7 @@
/*
Minetest Valleys C
-Copyright (C) 2010-2015 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
-Copyright (C) 2010-2015 paramat, Matt Gregory
-Copyright (C) 2016 Duane Robertson <duane@duanerobertson.com>
+Copyright (C) 2016-2017 Duane Robertson <duane@duanerobertson.com>
+Copyright (C) 2016-2017 paramat
Based on Valleys Mapgen by Gael de Sailly
(https://forum.minetest.net/viewtopic.php?f=9&t=11430)
diff --git a/src/mapnode.cpp b/src/mapnode.cpp
index d835daba2..aaea3831e 100644
--- a/src/mapnode.cpp
+++ b/src/mapnode.cpp
@@ -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)
{
diff --git a/src/mapnode.h b/src/mapnode.h
index 9c56a7e17..23248c45d 100644
--- a/src/mapnode.h
+++ b/src/mapnode.h
@@ -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
diff --git a/src/mesh.cpp b/src/mesh.cpp
index a79264ef0..3ab67510a 100644
--- a/src/mesh.cpp
+++ b/src/mesh.cpp
@@ -175,6 +175,14 @@ void translateMesh(scene::IMesh *mesh, v3f vec)
mesh->setBoundingBox(bbox);
}
+void setMeshBufferColor(scene::IMeshBuffer *buf, const video::SColor &color)
+{
+ const u32 stride = getVertexPitchFromType(buf->getVertexType());
+ u32 vertex_count = buf->getVertexCount();
+ u8 *vertices = (u8 *) buf->getVertices();
+ for (u32 i = 0; i < vertex_count; i++)
+ ((video::S3DVertex *) (vertices + i * stride))->Color = color;
+}
void setMeshColor(scene::IMesh *mesh, const video::SColor &color)
{
@@ -182,14 +190,8 @@ void setMeshColor(scene::IMesh *mesh, const video::SColor &color)
return;
u32 mc = mesh->getMeshBufferCount();
- for (u32 j = 0; j < mc; j++) {
- scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
- const u32 stride = getVertexPitchFromType(buf->getVertexType());
- u32 vertex_count = buf->getVertexCount();
- u8 *vertices = (u8 *)buf->getVertices();
- for (u32 i = 0; i < vertex_count; i++)
- ((video::S3DVertex *)(vertices + i * stride))->Color = color;
- }
+ for (u32 j = 0; j < mc; j++)
+ setMeshBufferColor(mesh->getMeshBuffer(j), color);
}
void colorizeMeshBuffer(scene::IMeshBuffer *buf, const video::SColor *buffercolor)
@@ -385,48 +387,52 @@ void recalculateBoundingBox(scene::IMesh *src_mesh)
src_mesh->setBoundingBox(bbox);
}
-scene::IMesh* cloneMesh(scene::IMesh *src_mesh)
+scene::IMeshBuffer* cloneMeshBuffer(scene::IMeshBuffer *mesh_buffer)
+{
+ switch (mesh_buffer->getVertexType()) {
+ case video::EVT_STANDARD: {
+ video::S3DVertex *v = (video::S3DVertex *) mesh_buffer->getVertices();
+ u16 *indices = mesh_buffer->getIndices();
+ scene::SMeshBuffer *cloned_buffer = new scene::SMeshBuffer();
+ cloned_buffer->append(v, mesh_buffer->getVertexCount(), indices,
+ mesh_buffer->getIndexCount());
+ return cloned_buffer;
+ }
+ case video::EVT_2TCOORDS: {
+ video::S3DVertex2TCoords *v =
+ (video::S3DVertex2TCoords *) mesh_buffer->getVertices();
+ u16 *indices = mesh_buffer->getIndices();
+ scene::SMeshBufferTangents *cloned_buffer =
+ new scene::SMeshBufferTangents();
+ cloned_buffer->append(v, mesh_buffer->getVertexCount(), indices,
+ mesh_buffer->getIndexCount());
+ return cloned_buffer;
+ }
+ case video::EVT_TANGENTS: {
+ video::S3DVertexTangents *v =
+ (video::S3DVertexTangents *) mesh_buffer->getVertices();
+ u16 *indices = mesh_buffer->getIndices();
+ scene::SMeshBufferTangents *cloned_buffer =
+ new scene::SMeshBufferTangents();
+ cloned_buffer->append(v, mesh_buffer->getVertexCount(), indices,
+ mesh_buffer->getIndexCount());
+ return cloned_buffer;
+ }
+ }
+ // This should not happen.
+ sanity_check(false);
+ return NULL;
+}
+
+scene::SMesh* cloneMesh(scene::IMesh *src_mesh)
{
scene::SMesh* dst_mesh = new scene::SMesh();
for (u16 j = 0; j < src_mesh->getMeshBufferCount(); j++) {
- scene::IMeshBuffer *buf = src_mesh->getMeshBuffer(j);
- switch (buf->getVertexType()) {
- case video::EVT_STANDARD: {
- video::S3DVertex *v =
- (video::S3DVertex *) buf->getVertices();
- u16 *indices = (u16*)buf->getIndices();
- scene::SMeshBuffer *temp_buf = new scene::SMeshBuffer();
- temp_buf->append(v, buf->getVertexCount(),
- indices, buf->getIndexCount());
- dst_mesh->addMeshBuffer(temp_buf);
- temp_buf->drop();
- break;
- }
- case video::EVT_2TCOORDS: {
- video::S3DVertex2TCoords *v =
- (video::S3DVertex2TCoords *) buf->getVertices();
- u16 *indices = (u16*)buf->getIndices();
- scene::SMeshBufferTangents *temp_buf =
- new scene::SMeshBufferTangents();
- temp_buf->append(v, buf->getVertexCount(),
- indices, buf->getIndexCount());
- dst_mesh->addMeshBuffer(temp_buf);
- temp_buf->drop();
- break;
- }
- case video::EVT_TANGENTS: {
- video::S3DVertexTangents *v =
- (video::S3DVertexTangents *) buf->getVertices();
- u16 *indices = (u16*)buf->getIndices();
- scene::SMeshBufferTangents *temp_buf =
- new scene::SMeshBufferTangents();
- temp_buf->append(v, buf->getVertexCount(),
- indices, buf->getIndexCount());
- dst_mesh->addMeshBuffer(temp_buf);
- temp_buf->drop();
- break;
- }
- }
+ scene::IMeshBuffer *temp_buf = cloneMeshBuffer(
+ src_mesh->getMeshBuffer(j));
+ dst_mesh->addMeshBuffer(temp_buf);
+ temp_buf->drop();
+
}
return dst_mesh;
}
@@ -445,7 +451,7 @@ scene::IMesh* convertNodeboxesToMesh(const std::vector<aabb3f> &boxes,
buf->drop();
}
- video::SColor c(255,255,255,255);
+ video::SColor c(255,255,255,255);
for (std::vector<aabb3f>::const_iterator
i = boxes.begin();
@@ -532,7 +538,7 @@ scene::IMesh* convertNodeboxesToMesh(const std::vector<aabb3f> &boxes,
buf->append(vertices + j, 4, indices, 6);
}
}
- return dst_mesh;
+ return dst_mesh;
}
struct vcache
diff --git a/src/mesh.h b/src/mesh.h
index bcf0d771c..423e43aee 100644
--- a/src/mesh.h
+++ b/src/mesh.h
@@ -49,11 +49,20 @@ void scaleMesh(scene::IMesh *mesh, v3f scale);
*/
void translateMesh(scene::IMesh *mesh, v3f vec);
+/*!
+ * Sets a constant color for all vertices in the mesh buffer.
+ */
+void setMeshBufferColor(scene::IMeshBuffer *buf, const video::SColor &color);
+
/*
Set a constant color for all vertices in the mesh
*/
void setMeshColor(scene::IMesh *mesh, const video::SColor &color);
+/*!
+ * Overwrites the color of a mesh buffer.
+ * The color is darkened based on the normal vector of the vertices.
+ */
void colorizeMeshBuffer(scene::IMeshBuffer *buf, const video::SColor *buffercolor);
/*
@@ -82,11 +91,17 @@ void rotateMeshBy6dFacedir(scene::IMesh *mesh, int facedir);
void rotateMeshXYby (scene::IMesh *mesh, f64 degrees);
void rotateMeshXZby (scene::IMesh *mesh, f64 degrees);
void rotateMeshYZby (scene::IMesh *mesh, f64 degrees);
+
+/*
+ * Clone the mesh buffer.
+ * The returned pointer should be dropped.
+ */
+scene::IMeshBuffer* cloneMeshBuffer(scene::IMeshBuffer *mesh_buffer);
/*
Clone the mesh.
*/
-scene::IMesh* cloneMesh(scene::IMesh *src_mesh);
+scene::SMesh* cloneMesh(scene::IMesh *src_mesh);
/*
Convert nodeboxes to mesh. Each tile goes into a different buffer.
diff --git a/src/mesh_generator_thread.cpp b/src/mesh_generator_thread.cpp
index 126bf6327..dce788a7c 100644
--- a/src/mesh_generator_thread.cpp
+++ b/src/mesh_generator_thread.cpp
@@ -83,6 +83,11 @@ MeshUpdateQueue::~MeshUpdateQueue()
{
MutexAutoLock lock(m_mutex);
+ for (std::map<v3s16, CachedMapBlockData *>::iterator i = m_cache.begin();
+ i != m_cache.end(); ++i) {
+ delete i->second;
+ }
+
for (std::vector<QueuedMeshUpdate*>::iterator i = m_queue.begin();
i != m_queue.end(); ++i) {
QueuedMeshUpdate *q = *i;
@@ -281,6 +286,7 @@ void MeshUpdateQueue::cleanupCache()
if (cached_block->refcount_from_queue == 0 &&
cached_block->last_used_timestamp < t_now - cache_seconds) {
m_cache.erase(it++);
+ delete cached_block;
} else {
++it;
}
diff --git a/src/mesh_generator_thread.h b/src/mesh_generator_thread.h
index e74861862..6edb6906d 100644
--- a/src/mesh_generator_thread.h
+++ b/src/mesh_generator_thread.h
@@ -53,10 +53,12 @@ struct QueuedMeshUpdate
*/
class MeshUpdateQueue
{
- enum UpdateMode {
+ enum UpdateMode
+ {
FORCE_UPDATE,
SKIP_UPDATE_IF_ALREADY_CACHED,
};
+
public:
MeshUpdateQueue(Client *client);
@@ -68,7 +70,7 @@ public:
// Returned pointer must be deleted
// Returns NULL if queue is empty
- QueuedMeshUpdate * pop();
+ QueuedMeshUpdate *pop();
u32 size()
{
@@ -78,9 +80,9 @@ public:
private:
Client *m_client;
- std::vector<QueuedMeshUpdate*> m_queue;
+ std::vector<QueuedMeshUpdate *> m_queue;
std::set<v3s16> m_urgents;
- std::map<v3s16, CachedMapBlockData*> m_cache;
+ std::map<v3s16, CachedMapBlockData *> m_cache;
Mutex m_mutex;
// TODO: Add callback to update these when g_settings changes
@@ -89,9 +91,9 @@ private:
bool m_cache_smooth_lighting;
int m_meshgen_block_cache_size;
- CachedMapBlockData* cacheBlock(Map *map, v3s16 p, UpdateMode mode,
- size_t *cache_hit_counter=NULL);
- CachedMapBlockData* getCachedBlock(const v3s16 &p);
+ CachedMapBlockData *cacheBlock(Map *map, v3s16 p, UpdateMode mode,
+ size_t *cache_hit_counter = NULL);
+ CachedMapBlockData *getCachedBlock(const v3s16 &p);
void fillDataFromMapBlockCache(QueuedMeshUpdate *q);
void cleanupCache();
};
@@ -102,10 +104,8 @@ struct MeshUpdateResult
MapBlockMesh *mesh;
bool ack_block_to_server;
- MeshUpdateResult():
- p(-1338,-1338,-1338),
- mesh(NULL),
- ack_block_to_server(false)
+ MeshUpdateResult()
+ : p(-1338, -1338, -1338), mesh(NULL), ack_block_to_server(false)
{
}
};
diff --git a/src/metadata.cpp b/src/metadata.cpp
index 2ce9af5af..833735464 100644
--- a/src/metadata.cpp
+++ b/src/metadata.cpp
@@ -64,8 +64,7 @@ bool Metadata::operator==(const Metadata &other) const
return true;
}
-const std::string &Metadata::getString(const std::string &name,
- u16 recursion) const
+const std::string &Metadata::getString(const std::string &name, u16 recursion) const
{
StringMap::const_iterator it = m_stringvars.find(name);
if (it == m_stringvars.end()) {
@@ -99,11 +98,9 @@ bool Metadata::setString(const std::string &name, const std::string &var)
return true;
}
-const std::string &Metadata::resolveString(const std::string &str,
- u16 recursion) const
+const std::string &Metadata::resolveString(const std::string &str, u16 recursion) const
{
- if (recursion <= 1 &&
- str.substr(0, 2) == "${" && str[str.length() - 1] == '}') {
+ if (recursion <= 1 && str.substr(0, 2) == "${" && str[str.length() - 1] == '}') {
return getString(str.substr(2, str.length() - 3), recursion + 1);
} else {
return str;
diff --git a/src/mg_biome.cpp b/src/mg_biome.cpp
index ef7e52685..2ef2ed16a 100644
--- a/src/mg_biome.cpp
+++ b/src/mg_biome.cpp
@@ -1,6 +1,7 @@
/*
Minetest
-Copyright (C) 2010-2013 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
+Copyright (C) 2014-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
+Copyright (C) 2014-2017 paramat
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/src/mg_biome.h b/src/mg_biome.h
index 15088f7dd..2e07fd9cf 100644
--- a/src/mg_biome.h
+++ b/src/mg_biome.h
@@ -1,6 +1,7 @@
/*
Minetest
-Copyright (C) 2010-2013 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
+Copyright (C) 2014-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
+Copyright (C) 2014-2017 paramat
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/src/mg_decoration.cpp b/src/mg_decoration.cpp
index 51e4fbbcc..b0566e830 100644
--- a/src/mg_decoration.cpp
+++ b/src/mg_decoration.cpp
@@ -1,6 +1,7 @@
/*
Minetest
-Copyright (C) 2010-2014 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
+Copyright (C) 2014-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
+Copyright (C) 2015-2017 paramat
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
@@ -24,6 +25,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "map.h"
#include "log.h"
#include "util/numeric.h"
+#include <algorithm>
+
FlagDesc flagdesc_deco[] = {
{"place_center_x", DECO_PLACE_CENTER_X},
diff --git a/src/mg_decoration.h b/src/mg_decoration.h
index 986328ec3..950800ec8 100644
--- a/src/mg_decoration.h
+++ b/src/mg_decoration.h
@@ -1,6 +1,7 @@
/*
Minetest
-Copyright (C) 2010-2013 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
+Copyright (C) 2014-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
+Copyright (C) 2015-2017 paramat
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/src/mg_ore.cpp b/src/mg_ore.cpp
index d840d745a..89319238e 100644
--- a/src/mg_ore.cpp
+++ b/src/mg_ore.cpp
@@ -1,6 +1,7 @@
/*
Minetest
-Copyright (C) 2010-2014 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
+Copyright (C) 2014-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
+Copyright (C) 2015-2017 paramat
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
@@ -20,9 +21,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "mg_ore.h"
#include "mapgen.h"
#include "noise.h"
-#include "util/numeric.h"
#include "map.h"
#include "log.h"
+#include <algorithm>
+
FlagDesc flagdesc_ore[] = {
{"absheight", OREFLAG_ABSHEIGHT},
diff --git a/src/mg_ore.h b/src/mg_ore.h
index e95fdd330..0503a6ca0 100644
--- a/src/mg_ore.h
+++ b/src/mg_ore.h
@@ -1,6 +1,7 @@
/*
Minetest
-Copyright (C) 2010-2013 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
+Copyright (C) 2014-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
+Copyright (C) 2015-2017 paramat
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/src/mg_schematic.cpp b/src/mg_schematic.cpp
index 92e138df4..67931497f 100644
--- a/src/mg_schematic.cpp
+++ b/src/mg_schematic.cpp
@@ -1,6 +1,7 @@
/*
Minetest
-Copyright (C) 2010-2014 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
+Copyright (C) 2014-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
+Copyright (C) 2015-2017 paramat
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/src/mg_schematic.h b/src/mg_schematic.h
index 2f60c843b..db040343c 100644
--- a/src/mg_schematic.h
+++ b/src/mg_schematic.h
@@ -1,6 +1,7 @@
/*
Minetest
-Copyright (C) 2010-2013 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
+Copyright (C) 2014-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
+Copyright (C) 2015-2017 paramat
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/src/modalMenu.h b/src/modalMenu.h
index 43bb8e1b8..38a26535e 100644
--- a/src/modalMenu.h
+++ b/src/modalMenu.h
@@ -43,14 +43,13 @@ public:
class GUIModalMenu : public gui::IGUIElement
{
public:
- GUIModalMenu(gui::IGUIEnvironment* env,
- gui::IGUIElement* parent, s32 id,
+ GUIModalMenu(gui::IGUIEnvironment* env, gui::IGUIElement* parent, s32 id,
IMenuManager *menumgr):
IGUIElement(gui::EGUIET_ELEMENT, env, parent, id,
core::rect<s32>(0,0,100,100))
{
//m_force_regenerate_gui = false;
-
+
m_menumgr = menumgr;
m_allow_focus_removal = false;
m_screensize_old = v2u32(0,0);
@@ -59,6 +58,7 @@ public:
Environment->setFocus(this);
m_menumgr->createdMenu(this);
}
+
virtual ~GUIModalMenu()
{
m_menumgr->deletingMenu(this);
@@ -78,7 +78,7 @@ public:
{
if(!IsVisible)
return;
-
+
video::IVideoDriver* driver = Environment->getVideoDriver();
v2u32 screensize = driver->getScreenSize();
if(screensize != m_screensize_old /*|| m_force_regenerate_gui*/)
@@ -90,7 +90,7 @@ public:
drawMenu();
}
-
+
/*
This should be called when the menu wants to quit.
diff --git a/src/modifiedstate.h b/src/modifiedstate.h
index 75518f2f5..576c3c576 100644
--- a/src/modifiedstate.h
+++ b/src/modifiedstate.h
@@ -34,4 +34,3 @@ enum ModifiedState
};
#endif
-
diff --git a/src/mods.cpp b/src/mods.cpp
index 5a7dc6dca..0e583b2db 100644
--- a/src/mods.cpp
+++ b/src/mods.cpp
@@ -214,6 +214,55 @@ void ModConfiguration::addMods(const std::vector<ModSpec> &new_mods)
}
}
+void ModConfiguration::addModsFormConfig(const std::string &settings_path, const std::set<std::string> &mods)
+{
+ Settings conf;
+ std::set<std::string> load_mod_names;
+
+ conf.readConfigFile(settings_path.c_str());
+ std::vector<std::string> names = conf.getNames();
+ for (std::vector<std::string>::iterator it = names.begin();
+ it != names.end(); ++it) {
+ std::string name = *it;
+ if (name.compare(0,9,"load_mod_")==0 && conf.getBool(name))
+ load_mod_names.insert(name.substr(9));
+ }
+
+ std::vector<ModSpec> addon_mods;
+ for (std::set<std::string>::const_iterator i = mods.begin();
+ i != mods.end(); ++i) {
+ std::vector<ModSpec> addon_mods_in_path = flattenMods(getModsInPath(*i));
+ for (std::vector<ModSpec>::const_iterator it = addon_mods_in_path.begin();
+ it != addon_mods_in_path.end(); ++it) {
+ const ModSpec& mod = *it;
+ if (load_mod_names.count(mod.name) != 0)
+ addon_mods.push_back(mod);
+ else
+ conf.setBool("load_mod_" + mod.name, false);
+ }
+ }
+ conf.updateConfigFile(settings_path.c_str());
+
+ addMods(addon_mods);
+ checkConflictsAndDeps();
+
+ // complain about mods declared to be loaded, but not found
+ for (std::vector<ModSpec>::iterator it = addon_mods.begin();
+ it != addon_mods.end(); ++it)
+ load_mod_names.erase((*it).name);
+ std::vector<ModSpec> UnsatisfiedMods = getUnsatisfiedMods();
+ for (std::vector<ModSpec>::iterator it = UnsatisfiedMods.begin();
+ it != UnsatisfiedMods.end(); ++it)
+ load_mod_names.erase((*it).name);
+ if (!load_mod_names.empty()) {
+ errorstream << "The following mods could not be found:";
+ for (std::set<std::string>::iterator it = load_mod_names.begin();
+ it != load_mod_names.end(); ++it)
+ errorstream << " \"" << (*it) << "\"";
+ errorstream << std::endl;
+ }
+}
+
void ModConfiguration::checkConflictsAndDeps()
{
// report on name conflicts
@@ -296,58 +345,27 @@ ServerModConfiguration::ServerModConfiguration(const std::string &worldpath):
addModsInPath(gamespec.gamemods_path);
addModsInPath(worldpath + DIR_DELIM + "worldmods");
- // check world.mt file for mods explicitely declared to be
- // loaded or not by a load_mod_<modname> = ... line.
- std::string worldmt = worldpath+DIR_DELIM+"world.mt";
- Settings worldmt_settings;
- worldmt_settings.readConfigFile(worldmt.c_str());
- std::vector<std::string> names = worldmt_settings.getNames();
- std::set<std::string> include_mod_names;
- for (std::vector<std::string>::const_iterator it = names.begin();
- it != names.end(); ++it) {
- std::string name = *it;
- // for backwards compatibility: exclude only mods which are
- // explicitely excluded. if mod is not mentioned at all, it is
- // enabled. So by default, all installed mods are enabled.
- if (name.compare(0,9,"load_mod_") == 0 &&
- worldmt_settings.getBool(name)) {
- include_mod_names.insert(name.substr(9));
- }
- }
-
- // Collect all mods that are also in include_mod_names
- std::vector<ModSpec> addon_mods;
- for (std::set<std::string>::const_iterator it_path = gamespec.addon_mods_paths.begin();
- it_path != gamespec.addon_mods_paths.end(); ++it_path) {
- std::vector<ModSpec> addon_mods_in_path = flattenMods(getModsInPath(*it_path));
- for (std::vector<ModSpec>::const_iterator it = addon_mods_in_path.begin();
- it != addon_mods_in_path.end(); ++it) {
- const ModSpec& mod = *it;
- if (include_mod_names.count(mod.name) != 0)
- addon_mods.push_back(mod);
- else
- worldmt_settings.setBool("load_mod_" + mod.name, false);
- }
- }
- worldmt_settings.updateConfigFile(worldmt.c_str());
-
- addMods(addon_mods);
-
- checkConflictsAndDeps();
+ // Load normal mods
+ std::string worldmt = worldpath + DIR_DELIM + "world.mt";
+ addModsFormConfig(worldmt, gamespec.addon_mods_paths);
}
#ifndef SERVER
ClientModConfiguration::ClientModConfiguration(const std::string &path):
ModConfiguration(path)
{
- addModsInPath(path);
- addModsInPath(porting::path_user + DIR_DELIM + "clientmods");
- checkConflictsAndDeps();
+ std::set<std::string> paths;
+ std::string path_user = porting::path_user + DIR_DELIM + "clientmods";
+ paths.insert(path);
+ paths.insert(path_user);
+
+ std::string settings_path = path_user + DIR_DELIM + "mods.conf";
+ addModsFormConfig(settings_path, paths);
}
#endif
#if USE_CURL
-Json::Value getModstoreUrl(std::string url)
+Json::Value getModstoreUrl(const std::string &url)
{
std::vector<std::string> extra_headers;
diff --git a/src/mods.h b/src/mods.h
index c9bd51d99..7455a51ea 100644
--- a/src/mods.h
+++ b/src/mods.h
@@ -99,6 +99,8 @@ protected:
// adds all mods in the set.
void addMods(const std::vector<ModSpec> &new_mods);
+ void addModsFormConfig(const std::string &settings_path, const std::set<std::string> &mods);
+
void checkConflictsAndDeps();
private:
// move mods from m_unsatisfied_mods to m_sorted_mods
@@ -146,9 +148,10 @@ public:
#endif
#if USE_CURL
-Json::Value getModstoreUrl(std::string url);
+Json::Value getModstoreUrl(const std::string &url);
#else
-inline Json::Value getModstoreUrl(std::string url) {
+inline Json::Value getModstoreUrl(const std::string &url)
+{
return Json::Value();
}
#endif
diff --git a/src/nameidmapping.cpp b/src/nameidmapping.cpp
index 2af8befff..d031f0808 100644
--- a/src/nameidmapping.cpp
+++ b/src/nameidmapping.cpp
@@ -25,27 +25,25 @@ void NameIdMapping::serialize(std::ostream &os) const
{
writeU8(os, 0); // version
writeU16(os, m_id_to_name.size());
- for(UNORDERED_MAP<u16, std::string>::const_iterator
- i = m_id_to_name.begin();
- i != m_id_to_name.end(); ++i){
+ for (UNORDERED_MAP<u16, std::string>::const_iterator i = m_id_to_name.begin();
+ i != m_id_to_name.end(); ++i) {
writeU16(os, i->first);
- os<<serializeString(i->second);
+ os << serializeString(i->second);
}
}
void NameIdMapping::deSerialize(std::istream &is)
{
int version = readU8(is);
- if(version != 0)
+ if (version != 0)
throw SerializationError("unsupported NameIdMapping version");
u32 count = readU16(is);
m_id_to_name.clear();
m_name_to_id.clear();
- for(u32 i=0; i<count; i++){
+ for (u32 i = 0; i < count; i++) {
u16 id = readU16(is);
std::string name = deSerializeString(is);
m_id_to_name[id] = name;
m_name_to_id[name] = id;
}
}
-
diff --git a/src/nameidmapping.h b/src/nameidmapping.h
index a0336864b..a2f3a3062 100644
--- a/src/nameidmapping.h
+++ b/src/nameidmapping.h
@@ -38,47 +38,51 @@ public:
m_name_to_id.clear();
}
- void set(u16 id, const std::string &name){
+ void set(u16 id, const std::string &name)
+ {
m_id_to_name[id] = name;
m_name_to_id[name] = id;
}
- void removeId(u16 id){
+ void removeId(u16 id)
+ {
std::string name;
bool found = getName(id, name);
- if(!found) return;
+ if (!found)
+ return;
m_id_to_name.erase(id);
m_name_to_id.erase(name);
}
- void eraseName(const std::string &name){
+ void eraseName(const std::string &name)
+ {
u16 id;
bool found = getId(name, id);
- if(!found) return;
+ if (!found)
+ return;
m_id_to_name.erase(id);
m_name_to_id.erase(name);
}
- bool getName(u16 id, std::string &result) const{
+ bool getName(u16 id, std::string &result) const
+ {
UNORDERED_MAP<u16, std::string>::const_iterator i;
i = m_id_to_name.find(id);
- if(i == m_id_to_name.end())
+ if (i == m_id_to_name.end())
return false;
result = i->second;
return true;
}
- bool getId(const std::string &name, u16 &result) const{
+ bool getId(const std::string &name, u16 &result) const
+ {
UNORDERED_MAP<std::string, u16>::const_iterator i;
i = m_name_to_id.find(name);
- if(i == m_name_to_id.end())
+ if (i == m_name_to_id.end())
return false;
result = i->second;
return true;
}
- u16 size() const{
- return m_id_to_name.size();
- }
+ u16 size() const { return m_id_to_name.size(); }
private:
UNORDERED_MAP<u16, std::string> m_id_to_name;
UNORDERED_MAP<std::string, u16> m_name_to_id;
};
#endif
-
diff --git a/src/network/clientopcodes.cpp b/src/network/clientopcodes.cpp
index cee402acd..bdcb1dfce 100644
--- a/src/network/clientopcodes.cpp
+++ b/src/network/clientopcodes.cpp
@@ -78,12 +78,12 @@ const ToClientCommandHandler toClientCommandTable[TOCLIENT_NUM_MSG_TYPES] =
{ "TOCLIENT_HP", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_HP }, // 0x33
{ "TOCLIENT_MOVE_PLAYER", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_MovePlayer }, // 0x34
{ "TOCLIENT_ACCESS_DENIED_LEGACY", TOCLIENT_STATE_NOT_CONNECTED, &Client::handleCommand_AccessDenied }, // 0x35
- { "TOCLIENT_PLAYERITEM", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_PlayerItem }, // 0x36
+ null_command_handler,
{ "TOCLIENT_DEATHSCREEN", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_DeathScreen }, // 0x37
{ "TOCLIENT_MEDIA", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_Media }, // 0x38
- { "TOCLIENT_TOOLDEF", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_ToolDef }, // 0x39
+ null_command_handler,
{ "TOCLIENT_NODEDEF", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_NodeDef }, // 0x3a
- { "TOCLIENT_CRAFTITEMDEF", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_CraftItemDef }, // 0x3b
+ null_command_handler,
{ "TOCLIENT_ANNOUNCE_MEDIA", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_AnnounceMedia }, // 0x3c
{ "TOCLIENT_ITEMDEF", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_ItemDef }, // 0x3d
null_command_handler,
@@ -108,8 +108,8 @@ const ToClientCommandHandler toClientCommandTable[TOCLIENT_NUM_MSG_TYPES] =
{ "TOCLIENT_LOCAL_PLAYER_ANIMATIONS", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_LocalPlayerAnimations }, // 0x51
{ "TOCLIENT_EYE_OFFSET", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_EyeOffset }, // 0x52
{ "TOCLIENT_DELETE_PARTICLESPAWNER", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_DeleteParticleSpawner }, // 0x53
- null_command_handler,
- null_command_handler,
+ { "TOCLIENT_CLOUD_PARAMS", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_CloudParams }, // 0x54
+ { "TOCLIENT_FADE_SOUND", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_FadeSound }, // 0x55
null_command_handler,
null_command_handler,
null_command_handler,
diff --git a/src/network/clientpackethandler.cpp b/src/network/clientpackethandler.cpp
index 19f8bbf58..0d4b792de 100644
--- a/src/network/clientpackethandler.cpp
+++ b/src/network/clientpackethandler.cpp
@@ -30,7 +30,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "server.h"
#include "util/strfnd.h"
#include "network/clientopcodes.h"
-#include "script/clientscripting.h"
+#include "script/scripting_client.h"
#include "util/serialize.h"
#include "util/srp.h"
#include "tileanimation.h"
@@ -561,7 +561,6 @@ void Client::handleCommand_MovePlayer(NetworkPacket* pkt)
*pkt >> pos >> pitch >> yaw;
- player->got_teleported = true;
player->setPosition(pos);
infostream << "Client got TOCLIENT_MOVE_PLAYER"
@@ -581,15 +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_PlayerItem(NetworkPacket* pkt)
-{
- warningstream << "Client: Ignoring TOCLIENT_PLAYERITEM" << std::endl;
}
void Client::handleCommand_DeathScreen(NetworkPacket* pkt)
@@ -718,11 +708,6 @@ void Client::handleCommand_Media(NetworkPacket* pkt)
}
}
-void Client::handleCommand_ToolDef(NetworkPacket* pkt)
-{
- warningstream << "Client: Ignoring TOCLIENT_TOOLDEF" << std::endl;
-}
-
void Client::handleCommand_NodeDef(NetworkPacket* pkt)
{
infostream << "Client: Received node definitions: packet size: "
@@ -743,11 +728,6 @@ void Client::handleCommand_NodeDef(NetworkPacket* pkt)
m_nodedef_received = true;
}
-void Client::handleCommand_CraftItemDef(NetworkPacket* pkt)
-{
- warningstream << "Client: Ignoring TOCLIENT_CRAFTITEMDEF" << std::endl;
-}
-
void Client::handleCommand_ItemDef(NetworkPacket* pkt)
{
infostream << "Client: Received item definitions: packet size: "
@@ -770,21 +750,39 @@ void Client::handleCommand_ItemDef(NetworkPacket* pkt)
void Client::handleCommand_PlaySound(NetworkPacket* pkt)
{
+ /*
+ [0] u32 server_id
+ [4] u16 name length
+ [6] char name[len]
+ [ 6 + len] f32 gain
+ [10 + len] u8 type
+ [11 + len] (f32 * 3) pos
+ [23 + len] u16 object_id
+ [25 + len] bool loop
+ [26 + len] f32 fade
+ */
+
s32 server_id;
std::string name;
+
float gain;
u8 type; // 0=local, 1=positional, 2=object
v3f pos;
u16 object_id;
bool loop;
+ float fade = 0;
*pkt >> server_id >> name >> gain >> type >> pos >> object_id >> loop;
+ try {
+ *pkt >> fade;
+ } catch (PacketError &e) {};
+
// Start playing
int client_id = -1;
switch(type) {
case 0: // local
- client_id = m_sound->playSound(name, loop, gain);
+ client_id = m_sound->playSound(name, loop, gain, fade);
break;
case 1: // positional
client_id = m_sound->playSoundAt(name, loop, gain, pos);
@@ -823,6 +821,21 @@ void Client::handleCommand_StopSound(NetworkPacket* pkt)
}
}
+void Client::handleCommand_FadeSound(NetworkPacket *pkt)
+{
+ s32 sound_id;
+ float step;
+ float gain;
+
+ *pkt >> sound_id >> step >> gain;
+
+ UNORDERED_MAP<s32, int>::iterator i =
+ m_sounds_server_to_client.find(sound_id);
+
+ if (i != m_sounds_server_to_client.end())
+ m_sound->fadeSound(i->second, step, gain);
+}
+
void Client::handleCommand_Privileges(NetworkPacket* pkt)
{
m_privileges.clear();
@@ -1133,7 +1146,7 @@ void Client::handleCommand_HudSetFlags(NetworkPacket* pkt)
m_minimap_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE);
// Hide minimap if it has been disabled by the server
- if (m_minimap_disabled_by_server && was_minimap_visible) {
+ if (m_minimap && m_minimap_disabled_by_server && was_minimap_visible) {
// defers a minimap update, therefore only call it if really
// needed, by checking that minimap was visible before
m_minimap->setMinimapMode(MINIMAP_MODE_OFF);
@@ -1155,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;
}
}
@@ -1175,11 +1200,45 @@ void Client::handleCommand_HudSetSky(NetworkPacket* pkt)
for (size_t i = 0; i < count; i++)
params->push_back(deSerializeString(is));
+ bool clouds = true;
+ try {
+ clouds = readU8(is);
+ } catch (...) {}
+
ClientEvent event;
event.type = CE_SET_SKY;
event.set_sky.bgcolor = bgcolor;
event.set_sky.type = type;
event.set_sky.params = params;
+ event.set_sky.clouds = clouds;
+ m_client_event_queue.push(event);
+}
+
+void Client::handleCommand_CloudParams(NetworkPacket* pkt)
+{
+ f32 density;
+ video::SColor color_bright;
+ video::SColor color_ambient;
+ f32 height;
+ f32 thickness;
+ v2f speed;
+
+ *pkt >> density >> color_bright >> color_ambient
+ >> height >> thickness >> speed;
+
+ ClientEvent event;
+ event.type = CE_CLOUD_PARAMS;
+ event.cloud_params.density = density;
+ // use the underlying u32 representation, because we can't
+ // use struct members with constructors here, and this way
+ // we avoid using new() and delete() for no good reason
+ event.cloud_params.color_bright = color_bright.color;
+ event.cloud_params.color_ambient = color_ambient.color;
+ event.cloud_params.height = height;
+ event.cloud_params.thickness = thickness;
+ // same here: deconstruct to skip constructor
+ event.cloud_params.speed_x = speed.X;
+ event.cloud_params.speed_y = speed.Y;
m_client_event_queue.push(event);
}
diff --git a/src/network/connection.cpp b/src/network/connection.cpp
index e11b4a953..a504bbc08 100644
--- a/src/network/connection.cpp
+++ b/src/network/connection.cpp
@@ -54,7 +54,8 @@ Mutex log_message_mutex;
#endif
-static inline float CALC_DTIME(unsigned int lasttime, unsigned int curtime) {
+static inline float CALC_DTIME(u64 lasttime, u64 curtime)
+{
float value = ( curtime - lasttime) / 1000.0;
return MYMAX(MYMIN(value,0.1),0.0);
}
@@ -930,7 +931,7 @@ void Peer::DecUseCount()
delete this;
}
-void Peer::RTTStatistics(float rtt, std::string profiler_id,
+void Peer::RTTStatistics(float rtt, const std::string &profiler_id,
unsigned int num_samples) {
if (m_last_rtt > 0) {
@@ -969,8 +970,7 @@ void Peer::RTTStatistics(float rtt, std::string profiler_id,
m_rtt.jitter_avg = m_rtt.jitter_avg * (num_samples/(num_samples-1)) +
jitter * (1/num_samples);
- if (profiler_id != "")
- {
+ if (profiler_id != "") {
g_profiler->graphAdd(profiler_id + "_rtt", rtt);
g_profiler->graphAdd(profiler_id + "_jitter", jitter);
}
@@ -982,7 +982,7 @@ void Peer::RTTStatistics(float rtt, std::string profiler_id,
bool Peer::isTimedOut(float timeout)
{
MutexAutoLock lock(m_exclusive_access_mutex);
- u32 current_time = porting::getTimeMs();
+ u64 current_time = porting::getTimeMs();
float dtime = CALC_DTIME(m_last_timeout_check,current_time);
m_last_timeout_check = current_time;
@@ -1277,8 +1277,8 @@ void * ConnectionSendThread::run()
LOG(dout_con<<m_connection->getDesc()
<<"ConnectionSend thread started"<<std::endl);
- u32 curtime = porting::getTimeMs();
- u32 lasttime = curtime;
+ u64 curtime = porting::getTimeMs();
+ u64 lasttime = curtime;
PROFILE(std::stringstream ThreadIdentifier);
PROFILE(ThreadIdentifier << "ConnectionSend: [" << m_connection->getDesc() << "]");
@@ -2047,8 +2047,8 @@ void * ConnectionReceiveThread::run()
PROFILE(ThreadIdentifier << "ConnectionReceive: [" << m_connection->getDesc() << "]");
#ifdef DEBUG_CONNECTION_KBPS
- u32 curtime = porting::getTimeMs();
- u32 lasttime = curtime;
+ u64 curtime = porting::getTimeMs();
+ u64 lasttime = curtime;
float debug_print_timer = 0.0;
#endif
@@ -2391,7 +2391,7 @@ SharedBuffer<u8> ConnectionReceiveThread::processPacket(Channel *channel,
// only calculate rtt from straight sent packets
if (p.resend_count == 0) {
// Get round trip time
- unsigned int current_time = porting::getTimeMs();
+ u64 current_time = porting::getTimeMs();
// a overflow is quite unlikely but as it'd result in major
// rtt miscalculation we handle it here
@@ -2884,16 +2884,21 @@ void Connection::Disconnect()
putCommand(c);
}
-void Connection::Receive(NetworkPacket* pkt)
+bool Connection::Receive(NetworkPacket *pkt, u32 timeout)
{
+ /*
+ Note that this function can potentially wait infinitely if non-data
+ events keep happening before the timeout expires.
+ This is not considered to be a problem (is it?)
+ */
for(;;) {
- ConnectionEvent e = waitEvent(m_bc_receive_timeout);
+ ConnectionEvent e = waitEvent(timeout);
if (e.type != CONNEVENT_NONE)
LOG(dout_con << getDesc() << ": Receive: got event: "
<< e.describe() << std::endl);
switch(e.type) {
case CONNEVENT_NONE:
- throw NoIncomingDataException("No incoming data");
+ return false;
case CONNEVENT_DATA_RECEIVED:
// Data size is lesser than command size, ignoring packet
if (e.data.getSize() < 2) {
@@ -2901,7 +2906,7 @@ void Connection::Receive(NetworkPacket* pkt)
}
pkt->putRawPacket(*e.data, e.data.getSize(), e.peer_id);
- return;
+ return true;
case CONNEVENT_PEER_ADDED: {
UDPPeer tmp(e.peer_id, e.address, this);
if (m_bc_peerhandler)
@@ -2919,7 +2924,19 @@ void Connection::Receive(NetworkPacket* pkt)
"(port already in use?)");
}
}
- throw NoIncomingDataException("No incoming data");
+ return false;
+}
+
+void Connection::Receive(NetworkPacket *pkt)
+{
+ bool any = Receive(pkt, m_bc_receive_timeout);
+ if (!any)
+ throw NoIncomingDataException("No incoming data");
+}
+
+bool Connection::TryReceive(NetworkPacket *pkt)
+{
+ return Receive(pkt, 0);
}
void Connection::Send(u16 peer_id, u8 channelnum,
diff --git a/src/network/connection.h b/src/network/connection.h
index 5ee53b9d4..a202fa2f5 100644
--- a/src/network/connection.h
+++ b/src/network/connection.h
@@ -175,7 +175,7 @@ struct BufferedPacket
Buffer<u8> data; // Data of the packet, including headers
float time; // Seconds from buffering the packet or re-sending
float totaltime; // Seconds from buffering the packet
- unsigned int absolute_send_time;
+ u64 absolute_send_time;
Address address; // Sender or destination
unsigned int resend_count;
};
@@ -383,7 +383,7 @@ struct OutgoingPacket
bool reliable;
bool ack;
- OutgoingPacket(u16 peer_id_, u8 channelnum_, SharedBuffer<u8> data_,
+ OutgoingPacket(u16 peer_id_, u8 channelnum_, const SharedBuffer<u8> &data_,
bool reliable_,bool ack_=false):
peer_id(peer_id_),
channelnum(channelnum_),
@@ -448,7 +448,7 @@ struct ConnectionCommand
reliable = reliable_;
}
- void ack(u16 peer_id_, u8 channelnum_, SharedBuffer<u8> data_)
+ void ack(u16 peer_id_, u8 channelnum_, const SharedBuffer<u8> &data_)
{
type = CONCMD_ACK;
peer_id = peer_id_;
@@ -457,7 +457,7 @@ struct ConnectionCommand
reliable = false;
}
- void createPeer(u16 peer_id_, SharedBuffer<u8> data_)
+ void createPeer(u16 peer_id_, const SharedBuffer<u8> &data_)
{
type = CONCMD_CREATE_PEER;
peer_id = peer_id_;
@@ -467,7 +467,7 @@ struct ConnectionCommand
raw = true;
}
- void disableLegacy(u16 peer_id_, SharedBuffer<u8> data_)
+ void disableLegacy(u16 peer_id_, const SharedBuffer<u8> &data_)
{
type = CONCMD_DISABLE_LEGACY;
peer_id = peer_id_;
@@ -732,8 +732,8 @@ class Peer {
virtual void reportRTT(float rtt) {};
void RTTStatistics(float rtt,
- std::string profiler_id="",
- unsigned int num_samples=1000);
+ const std::string &profiler_id = "",
+ unsigned int num_samples = 1000);
bool IncUseCount();
void DecUseCount();
@@ -769,7 +769,7 @@ class Peer {
// Seconds from last receive
float m_timeout_counter;
- u32 m_last_timeout_check;
+ u64 m_last_timeout_check;
};
class UDPPeer : public Peer
@@ -874,7 +874,7 @@ struct ConnectionEvent
return "Invalid ConnectionEvent";
}
- void dataReceived(u16 peer_id_, SharedBuffer<u8> data_)
+ void dataReceived(u16 peer_id_, const SharedBuffer<u8> &data_)
{
type = CONNEVENT_DATA_RECEIVED;
peer_id = peer_id_;
@@ -1016,6 +1016,7 @@ public:
bool Connected();
void Disconnect();
void Receive(NetworkPacket* pkt);
+ bool TryReceive(NetworkPacket* pkt);
void Send(u16 peer_id, u8 channelnum, NetworkPacket* pkt, bool reliable);
u16 GetPeerID() { return m_peer_id; }
Address GetPeerAddress(u16 peer_id);
@@ -1050,6 +1051,8 @@ protected:
UDPSocket m_udpSocket;
MutexedQueue<ConnectionCommand> m_command_queue;
+ bool Receive(NetworkPacket *pkt, u32 timeout);
+
void putEvent(ConnectionEvent &e);
void TriggerSend()
@@ -1074,7 +1077,7 @@ private:
// Backwards compatibility
PeerHandler *m_bc_peerhandler;
int m_bc_receive_timeout;
-
+
bool m_shutting_down;
u16 m_next_remote_peer_id;
diff --git a/src/network/networkpacket.cpp b/src/network/networkpacket.cpp
index f7a6499dd..7c2c7d0f9 100644
--- a/src/network/networkpacket.cpp
+++ b/src/network/networkpacket.cpp
@@ -58,9 +58,20 @@ 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);
+}
+
+void NetworkPacket::clear()
+{
+ m_data.clear();
+ m_datasize = 0;
+ m_read_offset = 0;
+ m_command = 0;
+ m_peer_id = 0;
}
const char* NetworkPacket::getString(u32 from_offset)
diff --git a/src/network/networkpacket.h b/src/network/networkpacket.h
index 83dc33f6f..fc14d61d8 100644
--- a/src/network/networkpacket.h
+++ b/src/network/networkpacket.h
@@ -35,7 +35,7 @@ public:
~NetworkPacket();
void putRawPacket(u8 *data, u32 datasize, u16 peer_id);
-
+ void clear();
// Getters
u32 getSize() { return m_datasize; }
u16 getPeerId() { return m_peer_id; }
diff --git a/src/network/networkprotocol.h b/src/network/networkprotocol.h
index ea532d9e0..7126c237b 100644
--- a/src/network/networkprotocol.h
+++ b/src/network/networkprotocol.h
@@ -50,6 +50,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
ContentFeatures and NodeDefManager use a different serialization
format; better for future version cross-compatibility
Many things
+ Obsolete TOCLIENT_PLAYERITEM
PROTOCOL_VERSION 10:
TOCLIENT_PRIVILEGES
Version raised to force 'fly' and 'fast' privileges into effect.
@@ -104,7 +105,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
PROTOCOL_VERSION 22:
add swap_node
PROTOCOL_VERSION 23:
- TOSERVER_CLIENT_READY
+ Obsolete TOSERVER_RECEIVED_MEDIA
+ Server: Stop using TOSERVER_CLIENT_READY
PROTOCOL_VERSION 24:
ContentFeatures version 7
ContentFeatures: change number of special tiles to 6 (CF_SPECIAL_COUNT)
@@ -148,9 +150,14 @@ with this program; if not, write to the Free Software Foundation, Inc.,
Add node and tile color and palette
Fix plantlike visual_scale being applied squared and add compatibility
with pre-30 clients by sending sqrt(visual_scale)
+ PROTOCOL VERSION 31:
+ Add tile overlay
+ Stop sending TOSERVER_CLIENT_READY
+ PROTOCOL VERSION 32:
+ Add fading sounds
*/
-#define LATEST_PROTOCOL_VERSION 30
+#define LATEST_PROTOCOL_VERSION 32
// Server's supported network protocol range
#define SERVER_PROTOCOL_VERSION_MIN 24
@@ -577,6 +584,7 @@ enum ToClientCommand
foreach count:
u8 len
u8[len] param
+ u8 clouds (boolean)
*/
TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO = 0x50,
@@ -605,6 +613,23 @@ enum ToClientCommand
u32 id
*/
+ TOCLIENT_CLOUD_PARAMS = 0x54,
+ /*
+ f1000 density
+ u8[4] color_diffuse (ARGB)
+ u8[4] color_ambient (ARGB)
+ f1000 height
+ f1000 thickness
+ v2f1000 speed
+ */
+
+ TOCLIENT_FADE_SOUND = 0x55,
+ /*
+ s32 sound_id
+ float step
+ float gain
+ */
+
TOCLIENT_SRP_BYTES_S_B = 0x60,
/*
Belonging to AUTH_MECHANISM_LEGACY_PASSWORD and AUTH_MECHANISM_SRP.
diff --git a/src/network/serveropcodes.cpp b/src/network/serveropcodes.cpp
index 642dd376a..19978a2b6 100644
--- a/src/network/serveropcodes.cpp
+++ b/src/network/serveropcodes.cpp
@@ -89,7 +89,7 @@ const ToServerCommandHandler toServerCommandTable[TOSERVER_NUM_MSG_TYPES] =
null_command_handler, // 0x3e
null_command_handler, // 0x3f
{ "TOSERVER_REQUEST_MEDIA", TOSERVER_STATE_STARTUP, &Server::handleCommand_RequestMedia }, // 0x40
- { "TOSERVER_RECEIVED_MEDIA", TOSERVER_STATE_STARTUP, &Server::handleCommand_ReceivedMedia }, // 0x41
+ { "TOSERVER_RECEIVED_MEDIA", TOSERVER_STATE_STARTUP, &Server::handleCommand_Deprecated }, // 0x41 not used by the server since protocol version 23
{ "TOSERVER_BREATH", TOSERVER_STATE_INGAME, &Server::handleCommand_Deprecated }, // 0x42 Old breath model which is now deprecated for anticheating
{ "TOSERVER_CLIENT_READY", TOSERVER_STATE_STARTUP, &Server::handleCommand_ClientReady }, // 0x43
null_command_handler, // 0x44
@@ -197,8 +197,8 @@ const ClientCommandFactory clientCommandFactoryTable[TOCLIENT_NUM_MSG_TYPES] =
{ "TOCLIENT_LOCAL_PLAYER_ANIMATIONS", 0, true }, // 0x51
{ "TOCLIENT_EYE_OFFSET", 0, true }, // 0x52
{ "TOCLIENT_DELETE_PARTICLESPAWNER", 0, true }, // 0x53
- null_command_factory,
- null_command_factory,
+ { "TOCLIENT_CLOUD_PARAMS", 0, true }, // 0x54
+ { "TOCLIENT_FADE_SOUND", 0, true }, // 0x55
null_command_factory,
null_command_factory,
null_command_factory,
diff --git a/src/network/serverpackethandler.cpp b/src/network/serverpackethandler.cpp
index 174b827f0..fc64c142e 100644
--- a/src/network/serverpackethandler.cpp
+++ b/src/network/serverpackethandler.cpp
@@ -27,7 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "nodedef.h"
#include "player.h"
#include "rollback_interface.h"
-#include "serverscripting.h"
+#include "scripting_server.h"
#include "settings.h"
#include "tool.h"
#include "version.h"
@@ -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") &&
@@ -674,10 +674,6 @@ void Server::handleCommand_RequestMedia(NetworkPacket* pkt)
sendRequestedMedia(pkt->getPeerId(), tosend);
}
-void Server::handleCommand_ReceivedMedia(NetworkPacket* pkt)
-{
-}
-
void Server::handleCommand_ClientReady(NetworkPacket* pkt)
{
u16 peer_id = pkt->getPeerId();
@@ -947,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
*/
@@ -1261,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)
{
/*
@@ -1384,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;
}
}
@@ -1445,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 "
@@ -1604,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 "
diff --git a/src/nodedef.cpp b/src/nodedef.cpp
index ce3e378a0..17162b344 100644
--- a/src/nodedef.cpp
+++ b/src/nodedef.cpp
@@ -378,13 +378,13 @@ void ContentFeatures::reset()
void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const
{
- if (protocol_version < 30) {
+ if (protocol_version < 31) {
serializeOld(os, protocol_version);
return;
}
// version
- writeU8(os, 9);
+ writeU8(os, 10);
// general
os << serializeString(name);
@@ -404,6 +404,8 @@ void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const
writeU8(os, 6);
for (u32 i = 0; i < 6; i++)
tiledef[i].serialize(os, protocol_version);
+ for (u32 i = 0; i < 6; i++)
+ tiledef_overlay[i].serialize(os, protocol_version);
writeU8(os, CF_SPECIAL_COUNT);
for (u32 i = 0; i < CF_SPECIAL_COUNT; i++) {
tiledef_special[i].serialize(os, protocol_version);
@@ -467,21 +469,18 @@ void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const
writeU8(os, legacy_wallmounted);
}
-void ContentFeatures::correctAlpha()
+void ContentFeatures::correctAlpha(TileDef *tiles, int length)
{
+ // alpha == 0 means that the node is using texture alpha
if (alpha == 0 || alpha == 255)
return;
- for (u32 i = 0; i < 6; i++) {
- std::stringstream s;
- s << tiledef[i].name << "^[noalpha^[opacity:" << ((int)alpha);
- tiledef[i].name = s.str();
- }
-
- for (u32 i = 0; i < CF_SPECIAL_COUNT; i++) {
+ for (int i = 0; i < length; i++) {
+ if (tiles[i].name == "")
+ continue;
std::stringstream s;
- s << tiledef_special[i].name << "^[noalpha^[opacity:" << ((int)alpha);
- tiledef_special[i].name = s.str();
+ s << tiles[i].name << "^[noalpha^[opacity:" << ((int)alpha);
+ tiles[i].name = s.str();
}
}
@@ -492,7 +491,7 @@ void ContentFeatures::deSerialize(std::istream &is)
if (version < 9) {
deSerializeOld(is, version);
return;
- } else if (version > 9) {
+ } else if (version > 10) {
throw SerializationError("unsupported ContentFeatures version");
}
@@ -516,6 +515,9 @@ void ContentFeatures::deSerialize(std::istream &is)
throw SerializationError("unsupported tile count");
for (u32 i = 0; i < 6; i++)
tiledef[i].deSerialize(is, version, drawtype);
+ if (version >= 10)
+ for (u32 i = 0; i < 6; i++)
+ tiledef_overlay[i].deSerialize(is, version, drawtype);
if (readU8(is) != CF_SPECIAL_COUNT)
throw SerializationError("unsupported CF_SPECIAL_COUNT");
for (u32 i = 0; i < CF_SPECIAL_COUNT; i++)
@@ -581,7 +583,7 @@ void ContentFeatures::deSerialize(std::istream &is)
}
#ifndef SERVER
-void ContentFeatures::fillTileAttribs(ITextureSource *tsrc, TileSpec *tile,
+void ContentFeatures::fillTileAttribs(ITextureSource *tsrc, TileLayer *tile,
TileDef *tiledef, u32 shader_id, bool use_normal_texture,
bool backface_culling, u8 material_type)
{
@@ -663,9 +665,16 @@ void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc
if (tdef[j].name == "")
tdef[j].name = "unknown_node.png";
}
+ // also the overlay tiles
+ TileDef tdef_overlay[6];
+ for (u32 j = 0; j < 6; j++)
+ tdef_overlay[j] = tiledef_overlay[j];
+ // also the special tiles
+ TileDef tdef_spec[6];
+ for (u32 j = 0; j < CF_SPECIAL_COUNT; j++)
+ tdef_spec[j] = tiledef_special[j];
bool is_liquid = false;
- bool is_water_surface = false;
u8 material_type = (alpha == 255) ?
TILE_MATERIAL_BASIC : TILE_MATERIAL_ALPHA;
@@ -673,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:
@@ -716,8 +727,8 @@ void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc
visual_solidness = 1;
} else if (tsettings.leaves_style == LEAVES_SIMPLE) {
for (u32 j = 0; j < 6; j++) {
- if (tiledef_special[j].name != "")
- tdef[j].name = tiledef_special[j].name;
+ if (tdef_spec[j].name != "")
+ tdef[j].name = tdef_spec[j].name;
}
drawtype = NDT_GLASSLIKE;
solidness = 0;
@@ -728,62 +739,74 @@ void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc
for (u32 i = 0; i < 6; i++)
tdef[i].name += std::string("^[noalpha");
}
- if (waving == 1)
+ if (waving >= 1)
material_type = TILE_MATERIAL_WAVING_LEAVES;
break;
case NDT_PLANTLIKE:
solidness = 0;
- if (waving == 1)
+ if (waving >= 1)
material_type = TILE_MATERIAL_WAVING_PLANTS;
break;
case NDT_FIRELIKE:
solidness = 0;
break;
case NDT_MESH:
+ case NDT_NODEBOX:
solidness = 0;
+ if (waving == 1)
+ material_type = TILE_MATERIAL_WAVING_PLANTS;
+ else if (waving == 2)
+ material_type = TILE_MATERIAL_WAVING_LEAVES;
break;
case NDT_TORCHLIKE:
case NDT_SIGNLIKE:
case NDT_FENCELIKE:
case NDT_RAILLIKE:
- case NDT_NODEBOX:
solidness = 0;
break;
}
if (is_liquid) {
+ // Vertex alpha is no longer supported, correct if necessary.
+ correctAlpha(tdef, 6);
+ correctAlpha(tdef_overlay, 6);
+ correctAlpha(tdef_spec, CF_SPECIAL_COUNT);
material_type = (alpha == 255) ?
TILE_MATERIAL_LIQUID_OPAQUE : TILE_MATERIAL_LIQUID_TRANSPARENT;
- if (name == "default:water_source")
- is_water_surface = true;
}
- // Vertex alpha is no longer supported, correct if necessary.
- correctAlpha();
-
u32 tile_shader[6];
for (u16 j = 0; j < 6; j++) {
tile_shader[j] = shdsrc->getShader("nodes_shader",
material_type, drawtype);
}
-
- if (is_water_surface) {
- tile_shader[0] = shdsrc->getShader("water_surface_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++) {
- fillTileAttribs(tsrc, &tiles[j], &tdef[j], tile_shader[j],
+ fillTileAttribs(tsrc, &tiles[j].layers[0], &tdef[j], tile_shader[j],
tsettings.use_normal_texture,
- tiledef[j].backface_culling, material_type);
+ tdef[j].backface_culling, material_type);
+ if (tdef_overlay[j].name != "")
+ fillTileAttribs(tsrc, &tiles[j].layers[1], &tdef_overlay[j],
+ overlay_shader[j], tsettings.use_normal_texture,
+ tdef[j].backface_culling, overlay_material);
}
// Special tiles (fill in f->special_tiles[])
for (u16 j = 0; j < CF_SPECIAL_COUNT; j++) {
- fillTileAttribs(tsrc, &special_tiles[j], &tiledef_special[j],
+ fillTileAttribs(tsrc, &special_tiles[j].layers[0], &tdef_spec[j],
tile_shader[j], tsettings.use_normal_texture,
- tiledef_special[j].backface_culling, material_type);
+ tdef_spec[j].backface_culling, material_type);
}
if (param_type_2 == CPT2_COLOR ||
@@ -870,7 +893,6 @@ public:
void serialize(std::ostream &os, u16 protocol_version) const;
void deSerialize(std::istream &is);
- inline virtual bool getNodeRegistrationStatus() const;
inline virtual void setNodeRegistrationStatus(bool completed);
virtual void pendNodeResolve(NodeResolver *nr);
@@ -1309,22 +1331,21 @@ void CNodeDefManager::removeNode(const std::string &name)
// Erase node content from all groups it belongs to
for (UNORDERED_MAP<std::string, GroupItems>::iterator iter_groups =
- m_group_to_items.begin();
- iter_groups != m_group_to_items.end();) {
+ m_group_to_items.begin(); iter_groups != m_group_to_items.end();) {
GroupItems &items = iter_groups->second;
for (GroupItems::iterator iter_groupitems = items.begin();
iter_groupitems != items.end();) {
if (iter_groupitems->first == id)
items.erase(iter_groupitems++);
else
- iter_groupitems++;
+ ++iter_groupitems;
}
// Check if group is empty
if (items.size() == 0)
m_group_to_items.erase(iter_groups++);
else
- iter_groups++;
+ ++iter_groups;
}
}
@@ -1538,8 +1559,19 @@ void ContentFeatures::serializeOld(std::ostream &os, u16 protocol_version) const
if (protocol_version < 30 && drawtype == NDT_PLANTLIKE)
compatible_visual_scale = sqrt(visual_scale);
+ TileDef compatible_tiles[6];
+ for (u8 i = 0; i < 6; i++) {
+ compatible_tiles[i] = tiledef[i];
+ if (tiledef_overlay[i].name != "") {
+ std::stringstream s;
+ s << "(" << tiledef[i].name << ")^(" << tiledef_overlay[i].name
+ << ")";
+ compatible_tiles[i].name = s.str();
+ }
+ }
+
// Protocol >= 24
- if (protocol_version < 30) {
+ if (protocol_version < 31) {
writeU8(os, protocol_version < 27 ? 7 : 8);
os << serializeString(name);
@@ -1553,7 +1585,7 @@ void ContentFeatures::serializeOld(std::ostream &os, u16 protocol_version) const
writeF1000(os, compatible_visual_scale);
writeU8(os, 6);
for (u32 i = 0; i < 6; i++)
- tiledef[i].serialize(os, protocol_version);
+ compatible_tiles[i].serialize(os, protocol_version);
writeU8(os, CF_SPECIAL_COUNT);
for (u32 i = 0; i < CF_SPECIAL_COUNT; i++)
tiledef_special[i].serialize(os, protocol_version);
@@ -1784,13 +1816,6 @@ void ContentFeatures::deSerializeOld(std::istream &is, int version)
}
}
-
-inline bool CNodeDefManager::getNodeRegistrationStatus() const
-{
- return m_node_registration_complete;
-}
-
-
inline void CNodeDefManager::setNodeRegistrationStatus(bool completed)
{
m_node_registration_complete = completed;
diff --git a/src/nodedef.h b/src/nodedef.h
index da3345d80..4669df7f0 100644
--- a/src/nodedef.h
+++ b/src/nodedef.h
@@ -218,14 +218,14 @@ struct TileDef
struct TileAnimationParams animation;
- TileDef()
+ TileDef() :
+ name(""),
+ backface_culling(true),
+ tileable_horizontal(true),
+ tileable_vertical(true),
+ has_color(false),
+ color(video::SColor(0xFFFFFFFF))
{
- name = "";
- backface_culling = true;
- tileable_horizontal = true;
- tileable_vertical = true;
- has_color = false;
- color = video::SColor(0xFFFFFFFF);
animation.type = TAT_NONE;
}
@@ -280,6 +280,8 @@ struct ContentFeatures
#endif
float visual_scale; // Misc. scale parameter
TileDef tiledef[6];
+ // These will be drawn over the base tiles.
+ TileDef tiledef_overlay[6];
TileDef tiledef_special[CF_SPECIAL_COUNT]; // eg. flowing liquid
// If 255, the node is opaque.
// Otherwise it uses texture alpha.
@@ -380,13 +382,13 @@ struct ContentFeatures
void serializeOld(std::ostream &os, u16 protocol_version) const;
void deSerializeOld(std::istream &is, int version);
/*!
- * Since vertex alpha is no lnger supported, this method
- * adds instructions to the texture names to blend alpha there.
+ * Since vertex alpha is no longer supported, this method
+ * adds opacity directly to the texture pixels.
*
- * tiledef, tiledef_special and alpha must be initialized
- * before calling this.
+ * \param tiles array of the tile definitions.
+ * \param length length of tiles
*/
- void correctAlpha();
+ void correctAlpha(TileDef *tiles, int length);
/*
Some handy methods
@@ -405,7 +407,7 @@ struct ContentFeatures
}
#ifndef SERVER
- void fillTileAttribs(ITextureSource *tsrc, TileSpec *tile, TileDef *tiledef,
+ void fillTileAttribs(ITextureSource *tsrc, TileLayer *tile, TileDef *tiledef,
u32 shader_id, bool use_normal_texture, bool backface_culling,
u8 material_type);
void updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc,
@@ -430,8 +432,6 @@ public:
virtual void serialize(std::ostream &os, u16 protocol_version) const=0;
- virtual bool getNodeRegistrationStatus() const=0;
-
virtual void pendNodeResolve(NodeResolver *nr)=0;
virtual bool cancelNodeResolveCallback(NodeResolver *nr)=0;
virtual bool nodeboxConnects(const MapNode from, const MapNode to, u8 connect_face)=0;
@@ -489,7 +489,6 @@ public:
virtual void serialize(std::ostream &os, u16 protocol_version) const=0;
virtual void deSerialize(std::istream &is)=0;
- virtual bool getNodeRegistrationStatus() const=0;
virtual void setNodeRegistrationStatus(bool completed)=0;
virtual void pendNodeResolve(NodeResolver *nr)=0;
diff --git a/src/nodemetadata.cpp b/src/nodemetadata.cpp
index 9b60cf33e..0e8195c34 100644
--- a/src/nodemetadata.cpp
+++ b/src/nodemetadata.cpp
@@ -23,6 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "inventory.h"
#include "log.h"
#include "util/serialize.h"
+#include "util/basic_macros.h"
#include "constants.h" // MAP_BLOCKSIZE
#include <sstream>
@@ -39,28 +40,38 @@ NodeMetadata::~NodeMetadata()
delete m_inventory;
}
-void NodeMetadata::serialize(std::ostream &os) const
+void NodeMetadata::serialize(std::ostream &os, u8 version, bool disk) const
{
- int num_vars = m_stringvars.size();
+ int num_vars = disk ? m_stringvars.size() : countNonPrivate();
writeU32(os, num_vars);
for (StringMap::const_iterator
it = m_stringvars.begin();
it != m_stringvars.end(); ++it) {
+ bool priv = isPrivate(it->first);
+ if (!disk && priv)
+ continue;
+
os << serializeString(it->first);
os << serializeLongString(it->second);
+ if (version >= 2)
+ writeU8(os, (priv) ? 1 : 0);
}
m_inventory->serialize(os);
}
-void NodeMetadata::deSerialize(std::istream &is)
+void NodeMetadata::deSerialize(std::istream &is, u8 version)
{
- m_stringvars.clear();
+ clear();
int num_vars = readU32(is);
for(int i=0; i<num_vars; i++){
std::string name = deSerializeString(is);
std::string var = deSerializeLongString(is);
m_stringvars[name] = var;
+ if (version >= 2) {
+ if (readU8(is) == 1)
+ markPrivate(name, true);
+ }
}
m_inventory->deSerialize(is);
@@ -69,6 +80,7 @@ void NodeMetadata::deSerialize(std::istream &is)
void NodeMetadata::clear()
{
Metadata::clear();
+ m_privatevars.clear();
m_inventory->clear();
}
@@ -77,11 +89,34 @@ bool NodeMetadata::empty() const
return Metadata::empty() && m_inventory->getLists().size() == 0;
}
+
+void NodeMetadata::markPrivate(const std::string &name, bool set)
+{
+ if (set)
+ m_privatevars.insert(name);
+ else
+ m_privatevars.erase(name);
+}
+
+int NodeMetadata::countNonPrivate() const
+{
+ // m_privatevars can contain names not actually present
+ // DON'T: return m_stringvars.size() - m_privatevars.size();
+ int n = 0;
+ for (StringMap::const_iterator
+ it = m_stringvars.begin();
+ it != m_stringvars.end(); ++it) {
+ if (isPrivate(it->first) == false)
+ n++;
+ }
+ return n;
+}
+
/*
NodeMetadataList
*/
-void NodeMetadataList::serialize(std::ostream &os) const
+void NodeMetadataList::serialize(std::ostream &os, u8 blockver, bool disk) const
{
/*
Version 0 is a placeholder for "nothing to see here; go away."
@@ -93,7 +128,8 @@ void NodeMetadataList::serialize(std::ostream &os) const
return;
}
- writeU8(os, 1); // version
+ u8 version = (blockver > 27) ? 2 : 1;
+ writeU8(os, version);
writeU16(os, count);
for(std::map<v3s16, NodeMetadata*>::const_iterator
@@ -108,7 +144,7 @@ void NodeMetadataList::serialize(std::ostream &os) const
u16 p16 = p.Z * MAP_BLOCKSIZE * MAP_BLOCKSIZE + p.Y * MAP_BLOCKSIZE + p.X;
writeU16(os, p16);
- data->serialize(os);
+ data->serialize(os, version, disk);
}
}
@@ -123,7 +159,7 @@ void NodeMetadataList::deSerialize(std::istream &is, IItemDefManager *item_def_m
return;
}
- if (version != 1) {
+ if (version > 2) {
std::string err_str = std::string(FUNCTION_NAME)
+ ": version " + itos(version) + " not supported";
infostream << err_str << std::endl;
@@ -132,7 +168,7 @@ void NodeMetadataList::deSerialize(std::istream &is, IItemDefManager *item_def_m
u16 count = readU16(is);
- for (u16 i=0; i < count; i++) {
+ for (u16 i = 0; i < count; i++) {
u16 p16 = readU16(is);
v3s16 p;
@@ -143,15 +179,14 @@ void NodeMetadataList::deSerialize(std::istream &is, IItemDefManager *item_def_m
p.X = p16;
if (m_data.find(p) != m_data.end()) {
- warningstream<<"NodeMetadataList::deSerialize(): "
- <<"already set data at position"
- <<"("<<p.X<<","<<p.Y<<","<<p.Z<<"): Ignoring."
- <<std::endl;
+ warningstream << "NodeMetadataList::deSerialize(): "
+ << "already set data at position " << PP(p)
+ << ": Ignoring." << std::endl;
continue;
}
NodeMetadata *data = new NodeMetadata(item_def_mgr);
- data->deSerialize(is);
+ data->deSerialize(is, version);
m_data[p] = data;
}
}
diff --git a/src/nodemetadata.h b/src/nodemetadata.h
index f46c0fe91..0d72485bc 100644
--- a/src/nodemetadata.h
+++ b/src/nodemetadata.h
@@ -21,6 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#define NODEMETADATA_HEADER
#include "metadata.h"
+#include "util/cpp11_container.h"
/*
NodeMetadata stores arbitary amounts of data for special blocks.
@@ -40,8 +41,8 @@ public:
NodeMetadata(IItemDefManager *item_def_mgr);
~NodeMetadata();
- void serialize(std::ostream &os) const;
- void deSerialize(std::istream &is);
+ void serialize(std::ostream &os, u8 version, bool disk=true) const;
+ void deSerialize(std::istream &is, u8 version);
void clear();
bool empty() const;
@@ -52,8 +53,17 @@ public:
return m_inventory;
}
+ inline bool isPrivate(const std::string &name) const
+ {
+ return m_privatevars.count(name) != 0;
+ }
+ void markPrivate(const std::string &name, bool set);
+
private:
+ int countNonPrivate() const;
+
Inventory *m_inventory;
+ UNORDERED_SET<std::string> m_privatevars;
};
@@ -66,7 +76,7 @@ class NodeMetadataList
public:
~NodeMetadataList();
- void serialize(std::ostream &os) const;
+ void serialize(std::ostream &os, u8 blockver, bool disk=true) const;
void deSerialize(std::istream &is, IItemDefManager *item_def_mgr);
// Add all keys in this list to the vector keys
diff --git a/src/noise.cpp b/src/noise.cpp
index b918c9936..abd29f3ec 100644
--- a/src/noise.cpp
+++ b/src/noise.cpp
@@ -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;
}
diff --git a/src/object_properties.cpp b/src/object_properties.cpp
index a77368151..543c0e876 100644
--- a/src/object_properties.cpp
+++ b/src/object_properties.cpp
@@ -72,6 +72,7 @@ std::string ObjectProperties::dump()
}
os<<"]";
os<<", spritediv="<<PP2(spritediv);
+ os << ", static_save=" << static_save;
os<<", initial_sprite_basepos="<<PP2(initial_sprite_basepos);
os<<", is_visible="<<is_visible;
os<<", makes_footstep_sound="<<makes_footstep_sound;
diff --git a/src/object_properties.h b/src/object_properties.h
index 908757a64..9bfe7ea7b 100644
--- a/src/object_properties.h
+++ b/src/object_properties.h
@@ -54,6 +54,7 @@ struct ObjectProperties
std::string infotext;
//! For dropped items, this contains item information.
std::string wield_item;
+ bool static_save = true;
ObjectProperties();
std::string dump();
diff --git a/src/particles.cpp b/src/particles.cpp
index e1f292fb6..69bc31358 100644
--- a/src/particles.cpp
+++ b/src/particles.cpp
@@ -27,6 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "clientmap.h"
#include "mapnode.h"
#include "client.h"
+#include "settings.h"
/*
Utility
@@ -289,10 +290,66 @@ 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;
+ static const float radius =
+ g_settings->getS16("max_block_send_distance") * MAP_BLOCKSIZE;
+
bool unloaded = false;
bool is_attached = false;
v3f attached_pos = v3f(0,0,0);
@@ -307,114 +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.
- if (!unloaded) {
- v3f pos = random_v3f(m_minpos, m_maxpos);
- 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);
- }
+ // Pretend to, but don't actually spawn a particle if it is
+ // attached to an unloaded object or distant from player.
+ if (!unloaded)
+ spawnParticle(env, radius, is_attached, attached_pos, attached_yaw);
+
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)
- {
- v3f pos = random_v3f(m_minpos, m_maxpos);
- 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);
}
}
}
@@ -620,7 +596,7 @@ void ParticleManager::addNodeParticle(IGameDef* gamedef,
{
// Texture
u8 texid = myrand_range(0, 5);
- const TileSpec &tile = f.tiles[texid];
+ const TileLayer &tile = f.tiles[texid].layers[0];
video::ITexture *texture;
struct TileAnimationParams anim;
anim.type = TAT_NONE;
diff --git a/src/particles.h b/src/particles.h
index 7ffb1c728..cb16d1c07 100644
--- a/src/particles.h
+++ b/src/particles.h
@@ -20,8 +20,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#ifndef PARTICLES_HEADER
#define PARTICLES_HEADER
-#define DIGGING_PARTICLES_AMOUNT 10
-
#include <iostream>
#include "irrlichttypes_extrabloated.h"
#include "client/tile.h"
@@ -119,7 +117,7 @@ private:
class ParticleSpawner
{
- public:
+public:
ParticleSpawner(IGameDef* gamedef,
scene::ISceneManager *smgr,
LocalPlayer *player,
@@ -146,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;
diff --git a/src/pathfinder.cpp b/src/pathfinder.cpp
index ee06db630..16c5678ee 100644
--- a/src/pathfinder.cpp
+++ b/src/pathfinder.cpp
@@ -111,7 +111,7 @@ public:
* @param dir direction to set cost for
* @cost cost to set
*/
- void setCost(v3s16 dir, PathCost cost);
+ void setCost(v3s16 dir, const PathCost &cost);
bool valid; /**< node is on surface */
bool target; /**< node is target position */
@@ -496,7 +496,7 @@ PathCost PathGridnode::getCost(v3s16 dir)
}
/******************************************************************************/
-void PathGridnode::setCost(v3s16 dir, PathCost cost)
+void PathGridnode::setCost(v3s16 dir, const PathCost &cost)
{
if (dir.X > 0) {
directions[DIR_XP] = cost;
diff --git a/src/porting.cpp b/src/porting.cpp
index 9d859da7d..0a9de2a59 100644
--- a/src/porting.cpp
+++ b/src/porting.cpp
@@ -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>
@@ -181,20 +182,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 << " ";
+ 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
@@ -205,6 +212,9 @@ std::string get_sysinfo()
oss << "x86";
#endif
+ delete[] lpVersionInfo;
+ delete[] filePath;
+
return oss.str();
#else
struct utsname osinfo;
@@ -408,7 +418,7 @@ bool setSystemPaths()
#endif
for (std::list<std::string>::const_iterator
- i = trylist.begin(); i != trylist.end(); i++) {
+ i = trylist.begin(); i != trylist.end(); ++i) {
const std::string &trypath = *i;
if (!fs::PathExists(trypath) ||
!fs::PathExists(trypath + DIR_DELIM + "builtin")) {
@@ -942,5 +952,18 @@ void attachOrCreateConsole(void)
#endif
}
+// Load performance counter frequency only once at startup
+#ifdef _WIN32
+
+inline double get_perf_freq()
+{
+ LARGE_INTEGER freq;
+ QueryPerformanceFrequency(&freq);
+ return freq.QuadPart;
+}
+
+double perf_freq = get_perf_freq();
+
+#endif
} //namespace porting
diff --git a/src/porting.h b/src/porting.h
index aa389d02c..7034d956b 100644
--- a/src/porting.h
+++ b/src/porting.h
@@ -181,124 +181,99 @@ std::string get_sysinfo();
void initIrrlicht(irr::IrrlichtDevice * );
-/*
- Resolution is 10-20ms.
- Remember to check for overflows.
- Overflow can occur at any value higher than 10000000.
-*/
-#ifdef _WIN32 // Windows
- inline u32 getTimeS()
- {
- return GetTickCount() / 1000;
- }
+// Monotonic counter getters.
- inline u32 getTimeMs()
- {
- return GetTickCount();
- }
+#ifdef _WIN32 // Windows
- inline u32 getTimeUs()
- {
- LARGE_INTEGER freq, t;
- QueryPerformanceFrequency(&freq);
- QueryPerformanceCounter(&t);
- return (double)(t.QuadPart) / ((double)(freq.QuadPart) / 1000000.0);
- }
+extern double perf_freq;
- inline u32 getTimeNs()
- {
- LARGE_INTEGER freq, t;
- QueryPerformanceFrequency(&freq);
- QueryPerformanceCounter(&t);
- return (double)(t.QuadPart) / ((double)(freq.QuadPart) / 1000000000.0);
- }
+inline u64 os_get_time(double mult)
+{
+ LARGE_INTEGER t;
+ QueryPerformanceCounter(&t);
+ return static_cast<double>(t.QuadPart) / (perf_freq / mult);
+}
+
+// Resolution is <1us.
+inline u64 getTimeS() { return os_get_time(1); }
+inline u64 getTimeMs() { return os_get_time(1000); }
+inline u64 getTimeUs() { return os_get_time(1000*1000); }
+inline u64 getTimeNs() { return os_get_time(1000*1000*1000); }
#else // Posix
- inline void _os_get_clock(struct timespec *ts)
- {
+
+inline void os_get_clock(struct timespec *ts)
+{
#if defined(__MACH__) && defined(__APPLE__)
- // from http://stackoverflow.com/questions/5167269/clock-gettime-alternative-in-mac-os-x
- // OS X does not have clock_gettime, use clock_get_time
- clock_serv_t cclock;
- mach_timespec_t mts;
- host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock);
- clock_get_time(cclock, &mts);
- mach_port_deallocate(mach_task_self(), cclock);
- ts->tv_sec = mts.tv_sec;
- ts->tv_nsec = mts.tv_nsec;
+// From http://stackoverflow.com/questions/5167269/clock-gettime-alternative-in-mac-os-x
+// OS X does not have clock_gettime, use clock_get_time
+ clock_serv_t cclock;
+ mach_timespec_t mts;
+ host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock);
+ clock_get_time(cclock, &mts);
+ mach_port_deallocate(mach_task_self(), cclock);
+ ts->tv_sec = mts.tv_sec;
+ ts->tv_nsec = mts.tv_nsec;
#elif defined(CLOCK_MONOTONIC_RAW)
- clock_gettime(CLOCK_MONOTONIC_RAW, ts);
+ clock_gettime(CLOCK_MONOTONIC_RAW, ts);
#elif defined(_POSIX_MONOTONIC_CLOCK)
- clock_gettime(CLOCK_MONOTONIC, ts);
+ clock_gettime(CLOCK_MONOTONIC, ts);
#else
- struct timeval tv;
- gettimeofday(&tv, NULL);
- TIMEVAL_TO_TIMESPEC(&tv, ts);
-#endif // defined(__MACH__) && defined(__APPLE__)
- }
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ TIMEVAL_TO_TIMESPEC(&tv, ts);
+#endif
+}
- // Note: these clock functions do not return wall time, but
- // generally a clock that starts at 0 when the process starts.
- inline u32 getTimeS()
- {
- struct timespec ts;
- _os_get_clock(&ts);
- return ts.tv_sec;
- }
+inline u64 getTimeS()
+{
+ struct timespec ts;
+ os_get_clock(&ts);
+ return ts.tv_sec;
+}
- inline u32 getTimeMs()
- {
- struct timespec ts;
- _os_get_clock(&ts);
- return ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
- }
+inline u64 getTimeMs()
+{
+ struct timespec ts;
+ os_get_clock(&ts);
+ return ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
+}
- inline u32 getTimeUs()
- {
- struct timespec ts;
- _os_get_clock(&ts);
- return ts.tv_sec * 1000000 + ts.tv_nsec / 1000;
- }
+inline u64 getTimeUs()
+{
+ struct timespec ts;
+ os_get_clock(&ts);
+ return ts.tv_sec * 1000000 + ts.tv_nsec / 1000;
+}
- inline u32 getTimeNs()
- {
- struct timespec ts;
- _os_get_clock(&ts);
- return ts.tv_sec * 1000000000 + ts.tv_nsec;
- }
+inline u64 getTimeNs()
+{
+ struct timespec ts;
+ os_get_clock(&ts);
+ return ts.tv_sec * 1000000000 + ts.tv_nsec;
+}
- /*#include <sys/timeb.h>
- inline u32 getTimeMs()
- {
- struct timeb tb;
- ftime(&tb);
- return tb.time * 1000 + tb.millitm;
- }*/
#endif
-inline u32 getTime(TimePrecision prec)
+inline u64 getTime(TimePrecision prec)
{
switch (prec) {
- case PRECISION_SECONDS:
- return getTimeS();
- case PRECISION_MILLI:
- return getTimeMs();
- case PRECISION_MICRO:
- return getTimeUs();
- case PRECISION_NANO:
- return getTimeNs();
+ case PRECISION_SECONDS: return getTimeS();
+ case PRECISION_MILLI: return getTimeMs();
+ case PRECISION_MICRO: return getTimeUs();
+ case PRECISION_NANO: return getTimeNs();
}
- return 0;
+ FATAL_ERROR("Called getTime with invalid time precision");
}
/**
- * Delta calculation function taking two 32bit arguments.
- * @param old_time_ms old time for delta calculation (order is relevant!)
- * @param new_time_ms new time for delta calculation (order is relevant!)
- * @return positive 32bit delta value
+ * Delta calculation function arguments.
+ * @param old_time_ms old time for delta calculation
+ * @param new_time_ms new time for delta calculation
+ * @return positive delta value
*/
-inline u32 getDeltaMs(u32 old_time_ms, u32 new_time_ms)
+inline u64 getDeltaMs(u64 old_time_ms, u64 new_time_ms)
{
if (new_time_ms >= old_time_ms) {
return (new_time_ms - old_time_ms);
diff --git a/src/profiler.cpp b/src/profiler.cpp
index 197e094f6..8e997442c 100644
--- a/src/profiler.cpp
+++ b/src/profiler.cpp
@@ -21,3 +21,33 @@ with this program; if not, write to the Free Software Foundation, Inc.,
static Profiler main_profiler;
Profiler *g_profiler = &main_profiler;
+ScopeProfiler::ScopeProfiler(
+ Profiler *profiler, const std::string &name, ScopeProfilerType type)
+ : m_profiler(profiler), m_name(name), m_timer(NULL), m_type(type)
+{
+ if (m_profiler)
+ m_timer = new TimeTaker(m_name);
+}
+
+ScopeProfiler::~ScopeProfiler()
+{
+ if (!m_timer)
+ return;
+
+ float duration_ms = m_timer->stop(true);
+ float duration = duration_ms / 1000.0;
+ if (m_profiler) {
+ switch (m_type) {
+ case SPT_ADD:
+ m_profiler->add(m_name, duration);
+ break;
+ case SPT_AVG:
+ m_profiler->avg(m_name, duration);
+ break;
+ case SPT_GRAPH_ADD:
+ m_profiler->graphAdd(m_name, duration);
+ break;
+ }
+ }
+ delete m_timer;
+}
diff --git a/src/profiler.h b/src/profiler.h
index 6da115972..ce60c6262 100644
--- a/src/profiler.h
+++ b/src/profiler.h
@@ -193,48 +193,8 @@ class ScopeProfiler
{
public:
ScopeProfiler(Profiler *profiler, const std::string &name,
- enum ScopeProfilerType type = SPT_ADD):
- m_profiler(profiler),
- m_name(name),
- m_timer(NULL),
- m_type(type)
- {
- if(m_profiler)
- m_timer = new TimeTaker(m_name.c_str());
- }
- // name is copied
- ScopeProfiler(Profiler *profiler, const char *name,
- enum ScopeProfilerType type = SPT_ADD):
- m_profiler(profiler),
- m_name(name),
- m_timer(NULL),
- m_type(type)
- {
- if(m_profiler)
- m_timer = new TimeTaker(m_name.c_str());
- }
- ~ScopeProfiler()
- {
- if(m_timer)
- {
- float duration_ms = m_timer->stop(true);
- float duration = duration_ms / 1000.0;
- if(m_profiler){
- switch(m_type){
- case SPT_ADD:
- m_profiler->add(m_name, duration);
- break;
- case SPT_AVG:
- m_profiler->avg(m_name, duration);
- break;
- case SPT_GRAPH_ADD:
- m_profiler->graphAdd(m_name, duration);
- break;
- }
- }
- delete m_timer;
- }
- }
+ ScopeProfilerType type = SPT_ADD);
+ ~ScopeProfiler();
private:
Profiler *m_profiler;
std::string m_name;
diff --git a/src/reflowscan.cpp b/src/reflowscan.cpp
index eec371022..ba7898d52 100644
--- a/src/reflowscan.cpp
+++ b/src/reflowscan.cpp
@@ -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;
diff --git a/src/remoteplayer.cpp b/src/remoteplayer.cpp
index c8e5b9132..df9542630 100644
--- a/src/remoteplayer.cpp
+++ b/src/remoteplayer.cpp
@@ -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("")
{
@@ -65,54 +66,14 @@ RemotePlayer::RemotePlayer(const char *name, IItemDefManager *idef):
movement_liquid_fluidity_smooth = g_settings->getFloat("movement_liquid_fluidity_smooth") * BS;
movement_liquid_sink = g_settings->getFloat("movement_liquid_sink") * BS;
movement_gravity = g_settings->getFloat("movement_gravity") * BS;
-}
-
-void RemotePlayer::save(std::string savedir, IGameDef *gamedef)
-{
- /*
- * We have to open all possible player files in the players directory
- * and check their player names because some file systems are not
- * case-sensitive and player names are case-sensitive.
- */
-
- // A player to deserialize files into to check their names
- RemotePlayer testplayer("", gamedef->idef());
-
- savedir += DIR_DELIM;
- std::string path = savedir + m_name;
- for (u32 i = 0; i < PLAYER_FILE_ALTERNATE_TRIES; i++) {
- if (!fs::PathExists(path)) {
- // Open file and serialize
- std::ostringstream ss(std::ios_base::binary);
- serialize(ss);
- if (!fs::safeWriteToFile(path, ss.str())) {
- infostream << "Failed to write " << path << std::endl;
- }
- setModified(false);
- return;
- }
- // Open file and deserialize
- std::ifstream is(path.c_str(), std::ios_base::binary);
- if (!is.good()) {
- infostream << "Failed to open " << path << std::endl;
- return;
- }
- testplayer.deSerialize(is, path, NULL);
- is.close();
- if (strcmp(testplayer.getName(), m_name) == 0) {
- // Open file and serialize
- std::ostringstream ss(std::ios_base::binary);
- serialize(ss);
- if (!fs::safeWriteToFile(path, ss.str())) {
- infostream << "Failed to write " << path << std::endl;
- }
- setModified(false);
- return;
- }
- path = savedir + m_name + itos(i);
- }
- infostream << "Didn't find free file for player " << m_name << std::endl;
+ // copy defaults
+ m_cloud_params.density = 0.4f;
+ m_cloud_params.color_bright = video::SColor(229, 240, 240, 255);
+ m_cloud_params.color_ambient = video::SColor(255, 0, 0, 0);
+ m_cloud_params.height = 120.0f;
+ m_cloud_params.thickness = 16.0f;
+ m_cloud_params.speed = v2f(0.0f, -2.0f);
}
void RemotePlayer::serializeExtraAttributes(std::string &output)
diff --git a/src/remoteplayer.h b/src/remoteplayer.h
index 9d123393f..ee0d625b6 100644
--- a/src/remoteplayer.h
+++ b/src/remoteplayer.h
@@ -22,6 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#define REMOTEPLAYER_HEADER
#include "player.h"
+#include "cloudparams.h"
class PlayerSAO;
@@ -37,11 +38,12 @@ enum RemotePlayerChatResult
*/
class RemotePlayer : public Player
{
+ friend class PlayerDatabaseFiles;
+
public:
RemotePlayer(const char *name, IItemDefManager *idef);
virtual ~RemotePlayer() {}
- void save(std::string savedir, IGameDef *gamedef);
void deSerialize(std::istream &is, const std::string &playername, PlayerSAO *sao);
PlayerSAO *getPlayerSAO() { return m_sao; }
@@ -83,21 +85,30 @@ public:
}
void setSky(const video::SColor &bgcolor, const std::string &type,
- const std::vector<std::string> &params)
+ const std::vector<std::string> &params, bool &clouds)
{
m_sky_bgcolor = bgcolor;
m_sky_type = type;
m_sky_params = params;
+ m_sky_clouds = clouds;
}
void getSky(video::SColor *bgcolor, std::string *type,
- std::vector<std::string> *params)
+ std::vector<std::string> *params, bool *clouds)
{
*bgcolor = m_sky_bgcolor;
*type = m_sky_type;
*params = m_sky_params;
+ *clouds = m_sky_clouds;
+ }
+
+ void setCloudParams(const CloudParams &cloud_params)
+ {
+ m_cloud_params = cloud_params;
}
+ const CloudParams &getCloudParams() const { return m_cloud_params; }
+
bool checkModified() const { return m_dirty || inventory.checkModified(); }
void setModified(const bool x)
@@ -153,6 +164,9 @@ private:
std::string m_sky_type;
video::SColor m_sky_bgcolor;
std::vector<std::string> m_sky_params;
+ bool m_sky_clouds;
+
+ CloudParams m_cloud_params;
};
#endif
diff --git a/src/rollback_interface.cpp b/src/rollback_interface.cpp
index 40a33a51d..d02d1cb3e 100644
--- a/src/rollback_interface.cpp
+++ b/src/rollback_interface.cpp
@@ -44,7 +44,7 @@ RollbackNode::RollbackNode(Map *map, v3s16 p, IGameDef *gamedef)
NodeMetadata *metap = map->getNodeMetadata(p);
if (metap) {
std::ostringstream os(std::ios::binary);
- metap->serialize(os);
+ metap->serialize(os, 1); // FIXME: version bump??
meta = os.str();
}
}
@@ -165,7 +165,7 @@ bool RollbackAction::applyRevert(Map *map, InventoryManager *imgr, IGameDef *gam
}
}
std::istringstream is(n_old.meta, std::ios::binary);
- meta->deSerialize(is);
+ meta->deSerialize(is, 1); // FIXME: version bump??
}
// Inform other things that the meta data has changed
v3s16 blockpos = getContainerPos(p, MAP_BLOCKSIZE);
diff --git a/src/script/CMakeLists.txt b/src/script/CMakeLists.txt
index c96ccc816..bebe2f037 100644
--- a/src/script/CMakeLists.txt
+++ b/src/script/CMakeLists.txt
@@ -4,7 +4,7 @@ add_subdirectory(lua_api)
# Used by server and client
set(common_SCRIPT_SRCS
- ${CMAKE_CURRENT_SOURCE_DIR}/serverscripting.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/scripting_server.cpp
${common_SCRIPT_COMMON_SRCS}
${common_SCRIPT_CPP_API_SRCS}
${common_SCRIPT_LUA_API_SRCS}
@@ -13,7 +13,7 @@ set(common_SCRIPT_SRCS
# Used by client only
set(client_SCRIPT_SRCS
${CMAKE_CURRENT_SOURCE_DIR}/scripting_mainmenu.cpp
- ${CMAKE_CURRENT_SOURCE_DIR}/clientscripting.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/scripting_client.cpp
${client_SCRIPT_COMMON_SRCS}
${client_SCRIPT_CPP_API_SRCS}
${client_SCRIPT_LUA_API_SRCS}
diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp
index bcae874b9..24b84e0d0 100644
--- a/src/script/common/c_content.cpp
+++ b/src/script/common/c_content.cpp
@@ -32,6 +32,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "porting.h"
#include "mg_schematic.h"
#include "noise.h"
+#include "util/pointedthing.h"
+#include "debug.h" // For FATAL_ERROR
#include <json/json.h>
struct EnumString es_TileAnimationType[] =
@@ -43,15 +45,12 @@ struct EnumString es_TileAnimationType[] =
};
/******************************************************************************/
-ItemDefinition read_item_definition(lua_State* L,int index,
- ItemDefinition default_def)
+void read_item_definition(lua_State* L, int index,
+ const ItemDefinition &default_def, ItemDefinition &def)
{
- if(index < 0)
+ if (index < 0)
index = lua_gettop(L) + 1 + index;
- // Read the item definition
- ItemDefinition def = default_def;
-
def.type = (ItemType)getenumfield(L, index, "type",
es_ItemType, ITEM_NONE);
getstringfield(L, index, "name", def.name);
@@ -118,8 +117,61 @@ ItemDefinition read_item_definition(lua_State* L,int index,
// "" = no prediction
getstringfield(L, index, "node_placement_prediction",
def.node_placement_prediction);
+}
+
+/******************************************************************************/
+void push_item_definition(lua_State *L, const ItemDefinition &i)
+{
+ lua_newtable(L);
+ lua_pushstring(L, i.name.c_str());
+ lua_setfield(L, -2, "name");
+ lua_pushstring(L, i.description.c_str());
+ lua_setfield(L, -2, "description");
+}
- return def;
+void push_item_definition_full(lua_State *L, const ItemDefinition &i)
+{
+ std::string type(es_ItemType[(int)i.type].str);
+
+ lua_newtable(L);
+ lua_pushstring(L, i.name.c_str());
+ lua_setfield(L, -2, "name");
+ lua_pushstring(L, i.description.c_str());
+ lua_setfield(L, -2, "description");
+ lua_pushstring(L, type.c_str());
+ lua_setfield(L, -2, "type");
+ lua_pushstring(L, i.inventory_image.c_str());
+ lua_setfield(L, -2, "inventory_image");
+ lua_pushstring(L, i.wield_image.c_str());
+ lua_setfield(L, -2, "wield_image");
+ lua_pushstring(L, i.palette_image.c_str());
+ lua_setfield(L, -2, "palette_image");
+ push_ARGB8(L, i.color);
+ lua_setfield(L, -2, "color");
+ push_v3f(L, i.wield_scale);
+ lua_setfield(L, -2, "wield_scale");
+ lua_pushinteger(L, i.stack_max);
+ lua_setfield(L, -2, "stack_max");
+ lua_pushboolean(L, i.usable);
+ lua_setfield(L, -2, "usable");
+ lua_pushboolean(L, i.liquids_pointable);
+ lua_setfield(L, -2, "liquids_pointable");
+ if (i.type == ITEM_TOOL) {
+ push_tool_capabilities(L, ToolCapabilities(
+ i.tool_capabilities->full_punch_interval,
+ i.tool_capabilities->max_drop_level,
+ i.tool_capabilities->groupcaps,
+ i.tool_capabilities->damageGroups));
+ lua_setfield(L, -2, "tool_capabilities");
+ }
+ push_groups(L, i.groups);
+ lua_setfield(L, -2, "groups");
+ push_soundspec(L, i.sound_place);
+ lua_setfield(L, -2, "sound_place");
+ push_soundspec(L, i.sound_place_failed);
+ lua_setfield(L, -2, "sound_place_failed");
+ lua_pushstring(L, i.node_placement_prediction.c_str());
+ lua_setfield(L, -2, "node_placement_prediction");
}
/******************************************************************************/
@@ -221,7 +273,10 @@ void read_object_properties(lua_State *L, int index,
prop->automatic_face_movement_max_rotation_per_sec = luaL_checknumber(L, -1);
}
lua_pop(L, 1);
+
getstringfield(L, -1, "infotext", prop->infotext);
+ getboolfield(L, -1, "static_save", prop->static_save);
+
lua_getfield(L, -1, "wield_item");
if (!lua_isnil(L, -1))
prop->wield_item = read_item(L, -1, idef).getItemString();
@@ -294,6 +349,8 @@ void push_object_properties(lua_State *L, ObjectProperties *prop)
lua_setfield(L, -2, "automatic_face_movement_max_rotation_per_sec");
lua_pushlstring(L, prop->infotext.c_str(), prop->infotext.size());
lua_setfield(L, -2, "infotext");
+ lua_pushboolean(L, prop->static_save);
+ lua_setfield(L, -2, "static_save");
lua_pushlstring(L, prop->wield_item.c_str(), prop->wield_item.size());
lua_setfield(L, -2, "wield_item");
}
@@ -431,6 +488,34 @@ ContentFeatures read_content_features(lua_State *L, int index)
}
lua_pop(L, 1);
+ // overlay_tiles = {}
+ lua_getfield(L, index, "overlay_tiles");
+ if (lua_istable(L, -1)) {
+ int table = lua_gettop(L);
+ lua_pushnil(L);
+ int i = 0;
+ while (lua_next(L, table) != 0) {
+ // Read tiledef from value
+ f.tiledef_overlay[i] = read_tiledef(L, -1, f.drawtype);
+ // removes value, keeps key for next iteration
+ lua_pop(L, 1);
+ i++;
+ if (i == 6) {
+ lua_pop(L, 1);
+ break;
+ }
+ }
+ // Copy last value to all remaining textures
+ if (i >= 1) {
+ TileDef lasttile = f.tiledef_overlay[i - 1];
+ while (i < 6) {
+ f.tiledef_overlay[i] = lasttile;
+ i++;
+ }
+ }
+ }
+ lua_pop(L, 1);
+
// special_tiles = {}
lua_getfield(L, index, "special_tiles");
// If nil, try the deprecated name "special_materials" instead
@@ -635,6 +720,204 @@ ContentFeatures read_content_features(lua_State *L, int index)
return f;
}
+void push_content_features(lua_State *L, const ContentFeatures &c)
+{
+ std::string paramtype(ScriptApiNode::es_ContentParamType[(int)c.param_type].str);
+ std::string paramtype2(ScriptApiNode::es_ContentParamType2[(int)c.param_type_2].str);
+ 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);
+ 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, "has_on_destruct");
+ lua_pushboolean(L, c.has_after_destruct);
+ lua_setfield(L, -2, "has_after_destruct");
+ lua_pushstring(L, c.name.c_str());
+ lua_setfield(L, -2, "name");
+ push_groups(L, c.groups);
+ lua_setfield(L, -2, "groups");
+ lua_pushstring(L, paramtype.c_str());
+ lua_setfield(L, -2, "paramtype");
+ lua_pushstring(L, paramtype2.c_str());
+ lua_setfield(L, -2, "paramtype2");
+ lua_pushstring(L, drawtype.c_str());
+ lua_setfield(L, -2, "drawtype");
+ if (!c.mesh.empty()) {
+ lua_pushstring(L, c.mesh.c_str());
+ lua_setfield(L, -2, "mesh");
+ }
+#ifndef SERVER
+ push_ARGB8(L, c.minimap_color); // I know this is not set-able w/ register_node,
+ lua_setfield(L, -2, "minimap_color"); // but the people need to know!
+#endif
+ lua_pushnumber(L, c.visual_scale);
+ lua_setfield(L, -2, "visual_scale");
+ lua_pushnumber(L, c.alpha);
+ lua_setfield(L, -2, "alpha");
+ if (!c.palette_name.empty()) {
+ push_ARGB8(L, c.color);
+ lua_setfield(L, -2, "color");
+
+ lua_pushstring(L, c.palette_name.c_str());
+ lua_setfield(L, -2, "palette_name");
+
+ push_palette(L, c.palette);
+ lua_setfield(L, -2, "palette");
+ }
+ lua_pushnumber(L, c.waving);
+ lua_setfield(L, -2, "waving");
+ lua_pushnumber(L, c.connect_sides);
+ lua_setfield(L, -2, "connect_sides");
+
+ lua_newtable(L);
+ u16 i = 1;
+ for (std::vector<std::string>::const_iterator it = c.connects_to.begin();
+ it != c.connects_to.end(); ++it) {
+ lua_pushlstring(L, it->c_str(), it->size());
+ lua_rawseti(L, -2, i);
+ }
+ lua_setfield(L, -2, "connects_to");
+
+ push_ARGB8(L, c.post_effect_color);
+ lua_setfield(L, -2, "post_effect_color");
+ lua_pushnumber(L, c.leveled);
+ lua_setfield(L, -2, "leveled");
+ lua_pushboolean(L, c.sunlight_propagates);
+ lua_setfield(L, -2, "sunlight_propagates");
+ lua_pushnumber(L, c.light_source);
+ lua_setfield(L, -2, "light_source");
+ lua_pushboolean(L, c.is_ground_content);
+ lua_setfield(L, -2, "is_ground_content");
+ lua_pushboolean(L, c.walkable);
+ lua_setfield(L, -2, "walkable");
+ lua_pushboolean(L, c.pointable);
+ lua_setfield(L, -2, "pointable");
+ lua_pushboolean(L, c.diggable);
+ lua_setfield(L, -2, "diggable");
+ lua_pushboolean(L, c.climbable);
+ lua_setfield(L, -2, "climbable");
+ lua_pushboolean(L, c.buildable_to);
+ lua_setfield(L, -2, "buildable_to");
+ lua_pushboolean(L, c.rightclickable);
+ lua_setfield(L, -2, "rightclickable");
+ lua_pushnumber(L, c.damage_per_second);
+ lua_setfield(L, -2, "damage_per_second");
+ if (c.isLiquid()) {
+ lua_pushstring(L, liquid_type.c_str());
+ lua_setfield(L, -2, "liquid_type");
+ lua_pushstring(L, c.liquid_alternative_flowing.c_str());
+ lua_setfield(L, -2, "liquid_alternative_flowing");
+ lua_pushstring(L, c.liquid_alternative_source.c_str());
+ lua_setfield(L, -2, "liquid_alternative_source");
+ lua_pushnumber(L, c.liquid_viscosity);
+ lua_setfield(L, -2, "liquid_viscosity");
+ lua_pushboolean(L, c.liquid_renewable);
+ lua_setfield(L, -2, "liquid_renewable");
+ lua_pushnumber(L, c.liquid_range);
+ lua_setfield(L, -2, "liquid_range");
+ }
+ lua_pushnumber(L, c.drowning);
+ lua_setfield(L, -2, "drowning");
+ lua_pushboolean(L, c.floodable);
+ lua_setfield(L, -2, "floodable");
+ push_nodebox(L, c.node_box);
+ lua_setfield(L, -2, "node_box");
+ push_nodebox(L, c.selection_box);
+ lua_setfield(L, -2, "selection_box");
+ push_nodebox(L, c.collision_box);
+ lua_setfield(L, -2, "collision_box");
+ lua_newtable(L);
+ push_soundspec(L, c.sound_footstep);
+ lua_setfield(L, -2, "sound_footstep");
+ push_soundspec(L, c.sound_dig);
+ lua_setfield(L, -2, "sound_dig");
+ push_soundspec(L, c.sound_dug);
+ lua_setfield(L, -2, "sound_dug");
+ lua_setfield(L, -2, "sounds");
+ lua_pushboolean(L, c.legacy_facedir_simple);
+ lua_setfield(L, -2, "legacy_facedir_simple");
+ lua_pushboolean(L, c.legacy_wallmounted);
+ lua_setfield(L, -2, "legacy_wallmounted");
+}
+
+/******************************************************************************/
+void push_nodebox(lua_State *L, const NodeBox &box)
+{
+ lua_newtable(L);
+ switch (box.type)
+ {
+ case NODEBOX_REGULAR:
+ lua_pushstring(L, "regular");
+ lua_setfield(L, -2, "type");
+ break;
+ case NODEBOX_LEVELED:
+ case NODEBOX_FIXED:
+ lua_pushstring(L, "fixed");
+ lua_setfield(L, -2, "type");
+ push_box(L, box.fixed);
+ lua_setfield(L, -2, "fixed");
+ break;
+ case NODEBOX_WALLMOUNTED:
+ lua_pushstring(L, "wallmounted");
+ lua_setfield(L, -2, "type");
+ push_aabb3f(L, box.wall_top);
+ lua_setfield(L, -2, "wall_top");
+ push_aabb3f(L, box.wall_bottom);
+ lua_setfield(L, -2, "wall_bottom");
+ push_aabb3f(L, box.wall_side);
+ lua_setfield(L, -2, "wall_side");
+ break;
+ case NODEBOX_CONNECTED:
+ lua_pushstring(L, "connected");
+ lua_setfield(L, -2, "type");
+ push_box(L, box.connect_top);
+ lua_setfield(L, -2, "connect_top");
+ push_box(L, box.connect_bottom);
+ lua_setfield(L, -2, "connect_bottom");
+ push_box(L, box.connect_front);
+ lua_setfield(L, -2, "connect_front");
+ push_box(L, box.connect_back);
+ lua_setfield(L, -2, "connect_back");
+ push_box(L, box.connect_left);
+ lua_setfield(L, -2, "connect_left");
+ push_box(L, box.connect_right);
+ lua_setfield(L, -2, "connect_right");
+ break;
+ default:
+ FATAL_ERROR("Invalid box.type");
+ break;
+ }
+}
+
+void push_box(lua_State *L, const std::vector<aabb3f> &box)
+{
+ lua_newtable(L);
+ u8 i = 1;
+ for (std::vector<aabb3f>::const_iterator it = box.begin();
+ it != box.end(); ++it) {
+ push_aabb3f(L, (*it));
+ lua_rawseti(L, -2, i);
+ }
+}
+
+/******************************************************************************/
+void push_palette(lua_State *L, const std::vector<video::SColor> *palette)
+{
+ lua_createtable(L, palette->size(), 0);
+ int newTable = lua_gettop(L);
+ int index = 1;
+ std::vector<video::SColor>::const_iterator iter;
+ for (iter = palette->begin(); iter != palette->end(); ++iter) {
+ push_ARGB8(L, (*iter));
+ lua_rawseti(L, newTable, index);
+ index++;
+ }
+}
+
/******************************************************************************/
void read_server_sound_params(lua_State *L, int index,
ServerSoundParams &params)
@@ -646,6 +929,7 @@ void read_server_sound_params(lua_State *L, int index,
if(lua_istable(L, index)){
getfloatfield(L, index, "gain", params.gain);
getstringfield(L, index, "to_player", params.to_player);
+ getfloatfield(L, index, "fade", params.fade);
lua_getfield(L, index, "pos");
if(!lua_isnil(L, -1)){
v3f p = read_v3f(L, -1)*BS;
@@ -678,11 +962,23 @@ void read_soundspec(lua_State *L, int index, SimpleSoundSpec &spec)
} else if(lua_istable(L, index)){
getstringfield(L, index, "name", spec.name);
getfloatfield(L, index, "gain", spec.gain);
+ getfloatfield(L, index, "fade", spec.fade);
} else if(lua_isstring(L, index)){
spec.name = lua_tostring(L, index);
}
}
+void push_soundspec(lua_State *L, const SimpleSoundSpec &spec)
+{
+ lua_newtable(L);
+ lua_pushstring(L, spec.name.c_str());
+ lua_setfield(L, -2, "name");
+ lua_pushnumber(L, spec.gain);
+ lua_setfield(L, -2, "gain");
+ lua_pushnumber(L, spec.fade);
+ lua_setfield(L, -2, "fade");
+}
+
/******************************************************************************/
NodeBox read_nodebox(lua_State *L, int index)
{
@@ -873,7 +1169,7 @@ void push_tool_capabilities(lua_State *L,
lua_newtable(L);
// For each groupcap
for (ToolGCMap::const_iterator i = toolcap.groupcaps.begin();
- i != toolcap.groupcaps.end(); i++) {
+ i != toolcap.groupcaps.end(); ++i) {
// Create groupcap table
lua_newtable(L);
const std::string &name = i->first;
@@ -881,7 +1177,7 @@ void push_tool_capabilities(lua_State *L,
// Create subtable "times"
lua_newtable(L);
for (UNORDERED_MAP<int, float>::const_iterator
- i = groupcap.times.begin(); i != groupcap.times.end(); i++) {
+ i = groupcap.times.begin(); i != groupcap.times.end(); ++i) {
lua_pushinteger(L, i->first);
lua_pushnumber(L, i->second);
lua_settable(L, -3);
@@ -900,7 +1196,7 @@ void push_tool_capabilities(lua_State *L,
lua_newtable(L);
// For each damage group
for (DamageGroup::const_iterator i = toolcap.damageGroups.begin();
- i != toolcap.damageGroups.end(); i++) {
+ i != toolcap.damageGroups.end(); ++i) {
// Create damage group table
lua_pushinteger(L, i->second);
lua_setfield(L, -2, i->first.c_str());
@@ -939,7 +1235,7 @@ void read_inventory_list(lua_State *L, int tableindex,
InventoryList *invlist = inv->addList(name, listsize);
int index = 0;
for(std::vector<ItemStack>::const_iterator
- i = items.begin(); i != items.end(); i++){
+ i = items.begin(); i != items.end(); ++i){
if(forcesize != -1 && index == forcesize)
break;
invlist->changeItem(index, *i);
@@ -1404,3 +1700,42 @@ void read_json_value(lua_State *L, Json::Value &root, int index, u8 recursion)
}
lua_pop(L, 1); // Pop value
}
+
+void push_pointed_thing(lua_State *L, const PointedThing &pointed, bool csm)
+{
+ lua_newtable(L);
+ if (pointed.type == POINTEDTHING_NODE) {
+ lua_pushstring(L, "node");
+ lua_setfield(L, -2, "type");
+ push_v3s16(L, pointed.node_undersurface);
+ lua_setfield(L, -2, "under");
+ push_v3s16(L, pointed.node_abovesurface);
+ lua_setfield(L, -2, "above");
+ } 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");
+ }
+ } else {
+ lua_pushstring(L, "nothing");
+ lua_setfield(L, -2, "type");
+ }
+}
+
+void push_objectRef(lua_State *L, const u16 id)
+{
+ // Get core.object_refs[i]
+ lua_getglobal(L, "core");
+ lua_getfield(L, -1, "object_refs");
+ luaL_checktype(L, -1, LUA_TTABLE);
+ lua_pushnumber(L, id);
+ lua_gettable(L, -2);
+ lua_remove(L, -2); // object_refs
+ lua_remove(L, -2); // core
+}
diff --git a/src/script/common/c_content.h b/src/script/common/c_content.h
index 949b136eb..9b8796297 100644
--- a/src/script/common/c_content.h
+++ b/src/script/common/c_content.h
@@ -39,6 +39,7 @@ extern "C" {
#include "util/string.h"
#include "itemgroup.h"
#include "itemdef.h"
+#include "c_types.h"
namespace Json { class Value; }
@@ -64,8 +65,20 @@ class Schematic;
ContentFeatures read_content_features (lua_State *L, int index);
+void push_content_features (lua_State *L,
+ const ContentFeatures &c);
+
+void push_nodebox (lua_State *L,
+ const NodeBox &box);
+void push_box (lua_State *L,
+ const std::vector<aabb3f> &box);
+
+void push_palette (lua_State *L,
+ const std::vector<video::SColor> *palette);
+
TileDef read_tiledef (lua_State *L, int index,
u8 drawtype);
+
void read_soundspec (lua_State *L, int index,
SimpleSoundSpec &spec);
NodeBox read_nodebox (lua_State *L, int index);
@@ -86,8 +99,13 @@ ToolCapabilities read_tool_capabilities (lua_State *L, int table);
void push_tool_capabilities (lua_State *L,
const ToolCapabilities &prop);
-ItemDefinition read_item_definition (lua_State *L, int index,
- ItemDefinition default_def);
+void read_item_definition (lua_State *L, int index, const ItemDefinition &default_def,
+ ItemDefinition &def);
+void push_item_definition (lua_State *L,
+ const ItemDefinition &i);
+void push_item_definition_full (lua_State *L,
+ const ItemDefinition &i);
+
void read_object_properties (lua_State *L, int index,
ObjectProperties *prop,
IItemDefManager *idef);
@@ -145,6 +163,8 @@ std::vector<ItemStack> read_items (lua_State *L,
void read_soundspec (lua_State *L,
int index,
SimpleSoundSpec &spec);
+void push_soundspec (lua_State *L,
+ const SimpleSoundSpec &spec);
bool string_to_enum (const EnumString *spec,
int &result,
@@ -162,6 +182,10 @@ bool push_json_value (lua_State *L,
void read_json_value (lua_State *L, Json::Value &root,
int index, u8 recursion = 0);
+void push_pointed_thing (lua_State *L, const PointedThing &pointed, bool csm = false);
+
+void push_objectRef (lua_State *L, const u16 id);
+
extern struct EnumString es_TileAnimationType[];
#endif /* C_CONTENT_H_ */
diff --git a/src/script/common/c_converter.cpp b/src/script/common/c_converter.cpp
index fc516d56a..d9b926e3e 100644
--- a/src/script/common/c_converter.cpp
+++ b/src/script/common/c_converter.cpp
@@ -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)
diff --git a/src/script/common/c_converter.h b/src/script/common/c_converter.h
index a5fbee765..b0f61a8ca 100644
--- a/src/script/common/c_converter.h
+++ b/src/script/common/c_converter.h
@@ -77,6 +77,8 @@ void setfloatfield(lua_State *L, int table,
const char *fieldname, float value);
void setboolfield(lua_State *L, int table,
const char *fieldname, bool value);
+void setstringfield(lua_State *L, int table,
+ const char *fieldname, const char *value);
v3f checkFloatPos (lua_State *L, int index);
v2f check_v2f (lua_State *L, int index);
diff --git a/src/script/common/c_internal.cpp b/src/script/common/c_internal.cpp
index b349f9dd1..3e28d9cf6 100644
--- a/src/script/common/c_internal.cpp
+++ b/src/script/common/c_internal.cpp
@@ -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);
diff --git a/src/script/cpp_api/s_async.cpp b/src/script/cpp_api/s_async.cpp
index 1fb84fab6..722359066 100644
--- a/src/script/cpp_api/s_async.cpp
+++ b/src/script/cpp_api/s_async.cpp
@@ -46,26 +46,26 @@ AsyncEngine::~AsyncEngine()
// Request all threads to stop
for (std::vector<AsyncWorkerThread *>::iterator it = workerThreads.begin();
- it != workerThreads.end(); it++) {
+ it != workerThreads.end(); ++it) {
(*it)->stop();
}
// Wake up all threads
for (std::vector<AsyncWorkerThread *>::iterator it = workerThreads.begin();
- it != workerThreads.end(); it++) {
+ it != workerThreads.end(); ++it) {
jobQueueCounter.post();
}
// Wait for threads to finish
for (std::vector<AsyncWorkerThread *>::iterator it = workerThreads.begin();
- it != workerThreads.end(); it++) {
+ it != workerThreads.end(); ++it) {
(*it)->wait();
}
// Force kill all threads
for (std::vector<AsyncWorkerThread *>::iterator it = workerThreads.begin();
- it != workerThreads.end(); it++) {
+ it != workerThreads.end(); ++it) {
delete *it;
}
@@ -76,14 +76,9 @@ AsyncEngine::~AsyncEngine()
}
/******************************************************************************/
-bool AsyncEngine::registerFunction(const char* name, lua_CFunction func)
+void AsyncEngine::registerStateInitializer(StateInitializer func)
{
- if (initDone) {
- return false;
- }
-
- functionList[name] = func;
- return true;
+ stateInitializers.push_back(func);
}
/******************************************************************************/
@@ -100,7 +95,8 @@ void AsyncEngine::initialize(unsigned int numEngines)
}
/******************************************************************************/
-unsigned int AsyncEngine::queueAsyncJob(std::string func, std::string params)
+unsigned int AsyncEngine::queueAsyncJob(const std::string &func,
+ const std::string &params)
{
jobQueueMutex.lock();
LuaJobInfo toAdd;
@@ -124,7 +120,6 @@ LuaJobInfo AsyncEngine::getJob()
jobQueueMutex.lock();
LuaJobInfo retval;
- retval.valid = false;
if (!jobQueue.empty()) {
retval = jobQueue.front();
@@ -137,7 +132,7 @@ LuaJobInfo AsyncEngine::getJob()
}
/******************************************************************************/
-void AsyncEngine::putJobResult(LuaJobInfo result)
+void AsyncEngine::putJobResult(const LuaJobInfo &result)
{
resultQueueMutex.lock();
resultQueue.push_back(result);
@@ -204,11 +199,9 @@ void AsyncEngine::pushFinishedJobs(lua_State* L) {
/******************************************************************************/
void AsyncEngine::prepareEnvironment(lua_State* L, int top)
{
- for (UNORDERED_MAP<std::string, lua_CFunction>::iterator it = functionList.begin();
- it != functionList.end(); it++) {
- lua_pushstring(L, it->first.c_str());
- lua_pushcfunction(L, it->second);
- lua_settable(L, top);
+ for (std::vector<StateInitializer>::iterator it = stateInitializers.begin();
+ it != stateInitializers.end(); it++) {
+ (*it)(L, top);
}
}
@@ -264,7 +257,7 @@ void* AsyncWorkerThread::run()
// Wait for job
LuaJobInfo toProcess = jobDispatcher->getJob();
- if (toProcess.valid == false || stopRequested()) {
+ if (!toProcess.valid || stopRequested()) {
continue;
}
diff --git a/src/script/cpp_api/s_async.h b/src/script/cpp_api/s_async.h
index 016381e5f..dbe0654e2 100644
--- a/src/script/cpp_api/s_async.h
+++ b/src/script/cpp_api/s_async.h
@@ -38,7 +38,16 @@ class AsyncEngine;
// Declarations
// Data required to queue a job
-struct LuaJobInfo {
+struct LuaJobInfo
+{
+ LuaJobInfo() :
+ serializedFunction(""),
+ serializedParams(""),
+ serializedResult(""),
+ id(0),
+ valid(false)
+ {}
+
// Function to be called in async environment
std::string serializedFunction;
// Parameter to be passed to function
@@ -66,16 +75,16 @@ private:
// Asynchornous thread and job management
class AsyncEngine {
friend class AsyncWorkerThread;
+ typedef void (*StateInitializer)(lua_State *L, int top);
public:
AsyncEngine();
~AsyncEngine();
/**
- * Register function to be used within engine
- * @param name Function name to be used within Lua environment
+ * Register function to be called on new states
* @param func C function to be called
*/
- bool registerFunction(const char* name, lua_CFunction func);
+ void registerStateInitializer(StateInitializer func);
/**
* Create async engine tasks and lock function registration
@@ -89,7 +98,7 @@ public:
* @param params Serialized parameters
* @return jobid The job is queued
*/
- unsigned int queueAsyncJob(std::string func, std::string params);
+ unsigned int queueAsyncJob(const std::string &func, const std::string &params);
/**
* Engine step to process finished jobs
@@ -116,7 +125,7 @@ protected:
* Put a Job result back to result queue
* @param result result of completed job
*/
- void putJobResult(LuaJobInfo result);
+ void putJobResult(const LuaJobInfo &result);
/**
* Initialize environment with current registred functions
@@ -131,8 +140,8 @@ private:
// Variable locking the engine against further modification
bool initDone;
- // Internal store for registred functions
- UNORDERED_MAP<std::string, lua_CFunction> functionList;
+ // Internal store for registred state initializers
+ std::vector<StateInitializer> stateInitializers;
// Internal counter to create job IDs
unsigned int jobIdCounter;
diff --git a/src/script/cpp_api/s_base.cpp b/src/script/cpp_api/s_base.cpp
index 6a843810f..c75b1c2f8 100644
--- a/src/script/cpp_api/s_base.cpp
+++ b/src/script/cpp_api/s_base.cpp
@@ -42,6 +42,7 @@ extern "C" {
#include <stdio.h>
#include <cstdarg>
+#include "script/common/c_content.h"
#include <sstream>
@@ -118,6 +119,9 @@ ScriptApiBase::ScriptApiBase() :
m_environment = NULL;
m_guiengine = NULL;
+
+ // Make sure Lua uses the right locale
+ setlocale(LC_NUMERIC, "C");
}
ScriptApiBase::~ScriptApiBase()
@@ -239,7 +243,7 @@ void ScriptApiBase::stackDump(std::ostream &o)
break;
case LUA_TNUMBER: /* numbers */ {
char buf[10];
- snprintf(buf, 10, "%g", lua_tonumber(m_luastack, i));
+ snprintf(buf, 10, "%lf", lua_tonumber(m_luastack, i));
o << buf;
break;
}
@@ -320,22 +324,14 @@ void ScriptApiBase::objectrefGetOrCreate(lua_State *L,
if (cobj == NULL || cobj->getId() == 0) {
ObjectRef::create(L, cobj);
} else {
- objectrefGet(L, cobj->getId());
+ push_objectRef(L, cobj->getId());
+ if (cobj->isGone())
+ warningstream << "ScriptApiBase::objectrefGetOrCreate(): "
+ << "Pushing ObjectRef to removed/deactivated object"
+ << ", this is probably a bug." << std::endl;
}
}
-void ScriptApiBase::objectrefGet(lua_State *L, u16 id)
-{
- // Get core.object_refs[i]
- lua_getglobal(L, "core");
- lua_getfield(L, -1, "object_refs");
- luaL_checktype(L, -1, LUA_TTABLE);
- lua_pushnumber(L, id);
- lua_gettable(L, -2);
- lua_remove(L, -2); // object_refs
- lua_remove(L, -2); // core
-}
-
Server* ScriptApiBase::getServer()
{
return dynamic_cast<Server *>(m_gamedef);
diff --git a/src/script/cpp_api/s_base.h b/src/script/cpp_api/s_base.h
index 19d71df65..5b047a081 100644
--- a/src/script/cpp_api/s_base.h
+++ b/src/script/cpp_api/s_base.h
@@ -115,7 +115,6 @@ protected:
void setGuiEngine(GUIEngine* guiengine) { m_guiengine = guiengine; }
void objectrefGetOrCreate(lua_State *L, ServerActiveObject *cobj);
- void objectrefGet(lua_State *L, u16 id);
RecursiveMutex m_luastackmutex;
std::string m_last_run_mod;
diff --git a/src/script/cpp_api/s_client.cpp b/src/script/cpp_api/s_client.cpp
index a8a7d5e26..55d309fda 100644
--- a/src/script/cpp_api/s_client.cpp
+++ b/src/script/cpp_api/s_client.cpp
@@ -23,6 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "client.h"
#include "common/c_converter.h"
#include "common/c_content.h"
+#include "s_item.h"
void ScriptApiClient::on_shutdown()
{
@@ -189,6 +190,40 @@ bool ScriptApiClient::on_punchnode(v3s16 p, MapNode node)
return blocked;
}
+bool ScriptApiClient::on_placenode(const PointedThing &pointed, const ItemDefinition &item)
+{
+ SCRIPTAPI_PRECHECKHEADER
+
+ // Get core.registered_on_placenode
+ lua_getglobal(L, "core");
+ lua_getfield(L, -1, "registered_on_placenode");
+
+ // Push data
+ push_pointed_thing(L, pointed, true);
+ push_item_definition(L, item);
+
+ // Call functions
+ runCallbacks(2, RUN_CALLBACKS_MODE_OR);
+ return lua_toboolean(L, -1);
+}
+
+bool ScriptApiClient::on_item_use(const ItemStack &item, const PointedThing &pointed)
+{
+ SCRIPTAPI_PRECHECKHEADER
+
+ // Get core.registered_on_item_use
+ lua_getglobal(L, "core");
+ lua_getfield(L, -1, "registered_on_item_use");
+
+ // Push data
+ LuaItemStack::create(L, item);
+ push_pointed_thing(L, pointed, true);
+
+ // Call functions
+ runCallbacks(2, RUN_CALLBACKS_MODE_OR);
+ return lua_toboolean(L, -1);
+}
+
void ScriptApiClient::setEnv(ClientEnvironment *env)
{
ScriptApiBase::setEnv(env);
diff --git a/src/script/cpp_api/s_client.h b/src/script/cpp_api/s_client.h
index 94a597b2c..9133637a6 100644
--- a/src/script/cpp_api/s_client.h
+++ b/src/script/cpp_api/s_client.h
@@ -21,9 +21,13 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#ifndef S_CLIENT_H_
#define S_CLIENT_H_
+#include "util/pointedthing.h"
#include "cpp_api/s_base.h"
#include "mapnode.h"
+#include "itemdef.h"
#include "util/string.h"
+#include "util/pointedthing.h"
+#include "lua_api/l_item.h"
#ifdef _CRT_MSVCP_CURRENT
#include <cstdint>
@@ -51,6 +55,8 @@ public:
bool on_dignode(v3s16 p, MapNode node);
bool on_punchnode(v3s16 p, MapNode node);
+ bool on_placenode(const PointedThing &pointed, const ItemDefinition &item);
+ bool on_item_use(const ItemStack &item, const PointedThing &pointed);
void setEnv(ClientEnvironment *env);
};
diff --git a/src/script/cpp_api/s_entity.cpp b/src/script/cpp_api/s_entity.cpp
index 2e1d277e4..4c1e296d4 100644
--- a/src/script/cpp_api/s_entity.cpp
+++ b/src/script/cpp_api/s_entity.cpp
@@ -57,7 +57,7 @@ bool ScriptApiEntity::luaentity_Add(u16 id, const char *name)
// Add object reference
// This should be userdata with metatable ObjectRef
- objectrefGet(L, id);
+ push_objectRef(L, id);
luaL_checktype(L, -1, LUA_TUSERDATA);
if (!luaL_checkudata(L, -1, "ObjectRef"))
luaL_typerror(L, -1, "ObjectRef");
diff --git a/src/script/cpp_api/s_item.cpp b/src/script/cpp_api/s_item.cpp
index cbb833807..0654ca3dc 100644
--- a/src/script/cpp_api/s_item.cpp
+++ b/src/script/cpp_api/s_item.cpp
@@ -69,7 +69,12 @@ bool ScriptApiItem::item_OnPlace(ItemStack &item,
// Call function
LuaItemStack::create(L, item);
- objectrefGetOrCreate(L, placer);
+
+ if (!placer)
+ lua_pushnil(L);
+ else
+ objectrefGetOrCreate(L, placer);
+
pushPointedThing(pointed);
PCALL_RES(lua_pcall(L, 3, 1, error_handler));
if (!lua_isnil(L, -1)) {
@@ -206,7 +211,8 @@ bool ScriptApiItem::item_CraftPredict(ItemStack &item, ServerActiveObject *user,
// function onto the stack
// If core.registered_items[name] doesn't exist, core.nodedef_default
// is tried instead so unknown items can still be manipulated to some degree
-bool ScriptApiItem::getItemCallback(const char *name, const char *callbackname)
+bool ScriptApiItem::getItemCallback(const char *name, const char *callbackname,
+ const v3s16 *p)
{
lua_State* L = getStack();
@@ -217,10 +223,12 @@ bool ScriptApiItem::getItemCallback(const char *name, const char *callbackname)
lua_getfield(L, -1, name);
lua_remove(L, -2); // Remove registered_items
// Should be a table
- if(lua_type(L, -1) != LUA_TTABLE)
- {
+ if (lua_type(L, -1) != LUA_TTABLE) {
// Report error and clean up
- errorstream << "Item \"" << name << "\" not defined" << std::endl;
+ errorstream << "Item \"" << name << "\" not defined";
+ if (p)
+ errorstream << " at position " << PP(*p);
+ errorstream << std::endl;
lua_pop(L, 1);
// Try core.nodedef_default instead
@@ -249,27 +257,6 @@ void ScriptApiItem::pushPointedThing(const PointedThing& pointed)
{
lua_State* L = getStack();
- lua_newtable(L);
- if(pointed.type == POINTEDTHING_NODE)
- {
- lua_pushstring(L, "node");
- lua_setfield(L, -2, "type");
- push_v3s16(L, pointed.node_undersurface);
- lua_setfield(L, -2, "under");
- push_v3s16(L, pointed.node_abovesurface);
- lua_setfield(L, -2, "above");
- }
- else if(pointed.type == POINTEDTHING_OBJECT)
- {
- lua_pushstring(L, "object");
- lua_setfield(L, -2, "type");
- objectrefGet(L, pointed.object_id);
- lua_setfield(L, -2, "ref");
- }
- else
- {
- lua_pushstring(L, "nothing");
- lua_setfield(L, -2, "type");
- }
+ push_pointed_thing(L, pointed);
}
diff --git a/src/script/cpp_api/s_item.h b/src/script/cpp_api/s_item.h
index 7350a71c5..daff15bf1 100644
--- a/src/script/cpp_api/s_item.h
+++ b/src/script/cpp_api/s_item.h
@@ -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);
};
diff --git a/src/script/cpp_api/s_mainmenu.cpp b/src/script/cpp_api/s_mainmenu.cpp
index e9a7a13b9..1e9ba3a41 100644
--- a/src/script/cpp_api/s_mainmenu.cpp
+++ b/src/script/cpp_api/s_mainmenu.cpp
@@ -34,8 +34,7 @@ void ScriptApiMainMenu::setMainMenuData(MainMenuDataForScript *data)
lua_pushnil(L);
}
lua_settable(L, gamedata_idx);
- setboolfield(L, gamedata_idx, "reconnect_requested",
- data->reconnect_requested);
+ setboolfield(L, gamedata_idx, "reconnect_requested", data->reconnect_requested);
lua_pop(L, 1);
}
@@ -58,7 +57,7 @@ void ScriptApiMainMenu::handleMainMenuEvent(std::string text)
// Call it
lua_pushstring(L, text.c_str());
PCALL_RES(lua_pcall(L, 1, 0, error_handler));
- lua_pop(L, 1); // Pop error handler
+ lua_pop(L, 1); // Pop error handler
}
void ScriptApiMainMenu::handleMainMenuButtons(const StringMap &fields)
@@ -90,6 +89,5 @@ void ScriptApiMainMenu::handleMainMenuButtons(const StringMap &fields)
// Call it
PCALL_RES(lua_pcall(L, 1, 0, error_handler));
- lua_pop(L, 1); // Pop error handler
+ lua_pop(L, 1); // Pop error handler
}
-
diff --git a/src/script/cpp_api/s_node.cpp b/src/script/cpp_api/s_node.cpp
index adad01e45..91e153c18 100644
--- a/src/script/cpp_api/s_node.cpp
+++ b/src/script/cpp_api/s_node.cpp
@@ -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
@@ -178,6 +178,27 @@ void ScriptApiNode::node_on_destruct(v3s16 p, MapNode node)
lua_pop(L, 1); // Pop error handler
}
+bool ScriptApiNode::node_on_flood(v3s16 p, MapNode node, MapNode newnode)
+{
+ SCRIPTAPI_PRECHECKHEADER
+
+ int error_handler = PUSH_ERROR_HANDLER(L);
+
+ INodeDefManager *ndef = getServer()->ndef();
+
+ // Push callback function on stack
+ if (!getItemCallback(ndef->get(node).name.c_str(), "on_flood", &p))
+ return false;
+
+ // Call function
+ push_v3s16(L, p);
+ pushnode(L, node, ndef);
+ pushnode(L, newnode, ndef);
+ PCALL_RES(lua_pcall(L, 3, 1, error_handler));
+ lua_remove(L, error_handler);
+ return (bool) lua_isboolean(L, -1) && (bool) lua_toboolean(L, -1) == true;
+}
+
void ScriptApiNode::node_after_destruct(v3s16 p, MapNode node)
{
SCRIPTAPI_PRECHECKHEADER
@@ -187,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
@@ -206,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
@@ -234,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
@@ -242,7 +263,7 @@ void ScriptApiNode::node_on_receive_fields(v3s16 p,
lua_pushstring(L, formname.c_str()); // formname
lua_newtable(L); // fields
StringMap::const_iterator it;
- for (it = fields.begin(); it != fields.end(); it++) {
+ for (it = fields.begin(); it != fields.end(); ++it) {
const std::string &name = it->first;
const std::string &value = it->second;
lua_pushstring(L, name.c_str());
diff --git a/src/script/cpp_api/s_node.h b/src/script/cpp_api/s_node.h
index fe1180cb3..eb127909d 100644
--- a/src/script/cpp_api/s_node.h
+++ b/src/script/cpp_api/s_node.h
@@ -42,6 +42,7 @@ public:
ServerActiveObject *digger);
void node_on_construct(v3s16 p, MapNode node);
void node_on_destruct(v3s16 p, MapNode node);
+ bool node_on_flood(v3s16 p, MapNode node, MapNode newnode);
void node_after_destruct(v3s16 p, MapNode node);
bool node_on_timer(v3s16 p, MapNode node, f32 dtime);
void node_on_receive_fields(v3s16 p,
diff --git a/src/script/cpp_api/s_nodemeta.cpp b/src/script/cpp_api/s_nodemeta.cpp
index d050c0bc9..2f726dfa2 100644
--- a/src/script/cpp_api/s_nodemeta.cpp
+++ b/src/script/cpp_api/s_nodemeta.cpp
@@ -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)
diff --git a/src/script/cpp_api/s_security.cpp b/src/script/cpp_api/s_security.cpp
index 5ad7947d5..66a761f4c 100644
--- a/src/script/cpp_api/s_security.cpp
+++ b/src/script/cpp_api/s_security.cpp
@@ -249,9 +249,8 @@ void ScriptApiSecurity::initializeSecurityClient()
static const char *os_whitelist[] = {
"clock",
"date",
- "difftime",
- "time",
- "setlocale",
+ "difftime",
+ "time"
};
static const char *debug_whitelist[] = {
"getinfo",
diff --git a/src/script/lua_api/CMakeLists.txt b/src/script/lua_api/CMakeLists.txt
index b03e94a09..1a78580e6 100644
--- a/src/script/lua_api/CMakeLists.txt
+++ b/src/script/lua_api/CMakeLists.txt
@@ -29,4 +29,5 @@ set(client_SCRIPT_LUA_API_SRCS
${CMAKE_CURRENT_SOURCE_DIR}/l_storage.cpp
${CMAKE_CURRENT_SOURCE_DIR}/l_sound.cpp
${CMAKE_CURRENT_SOURCE_DIR}/l_localplayer.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/l_camera.cpp
PARENT_SCOPE)
diff --git a/src/script/lua_api/l_base.cpp b/src/script/lua_api/l_base.cpp
index dfe743b72..5d7ba9640 100644
--- a/src/script/lua_api/l_base.cpp
+++ b/src/script/lua_api/l_base.cpp
@@ -74,17 +74,13 @@ std::string ModApiBase::getCurrentModPath(lua_State *L)
}
-bool ModApiBase::registerFunction(
- lua_State *L,
- const char *name,
- lua_CFunction fct,
- int top)
+bool ModApiBase::registerFunction(lua_State *L, const char *name,
+ lua_CFunction func, int top)
{
- //TODO check presence first!
+ // TODO: Check presence first!
- lua_pushstring(L,name);
- lua_pushcfunction(L,fct);
- lua_settable(L, top);
+ lua_pushcfunction(L, func);
+ lua_setfield(L, top, name);
return true;
}
diff --git a/src/script/lua_api/l_base.h b/src/script/lua_api/l_base.h
index cd382629d..af89afd93 100644
--- a/src/script/lua_api/l_base.h
+++ b/src/script/lua_api/l_base.h
@@ -68,9 +68,8 @@ public:
static bool registerFunction(lua_State *L,
const char* name,
- lua_CFunction fct,
- int top
- );
+ lua_CFunction func,
+ int top);
};
#endif /* L_BASE_H_ */
diff --git a/src/script/lua_api/l_camera.cpp b/src/script/lua_api/l_camera.cpp
new file mode 100644
index 000000000..862384198
--- /dev/null
+++ b/src/script/lua_api/l_camera.cpp
@@ -0,0 +1,202 @@
+#include "script/common/c_converter.h"
+#include "l_camera.h"
+#include "l_internal.h"
+#include "content_cao.h"
+#include "camera.h"
+
+LuaCamera::LuaCamera(Camera *m)
+{
+ m_camera = m;
+}
+
+void LuaCamera::create(lua_State *L, Camera *m)
+{
+ LuaCamera *o = new LuaCamera(m);
+ *(void **)(lua_newuserdata(L, sizeof(void *))) = o;
+ luaL_getmetatable(L, className);
+ lua_setmetatable(L, -2);
+
+ int camera_object = lua_gettop(L);
+
+ lua_getglobal(L, "core");
+ luaL_checktype(L, -1, LUA_TTABLE);
+ int coretable = lua_gettop(L);
+
+ lua_pushvalue(L, camera_object);
+ lua_setfield(L, coretable, "camera");
+}
+
+int LuaCamera::l_set_camera_mode(lua_State *L)
+{
+ Camera *camera = getobject(L, 1);
+ GenericCAO *playercao = getClient(L)->getEnv().getLocalPlayer()->getCAO();
+ if (!camera)
+ return 0;
+ sanity_check(playercao);
+ if (!lua_isnumber(L, 2))
+ return 0;
+
+ camera->setCameraMode((CameraMode)((int)lua_tonumber(L, 2)));
+ playercao->setVisible(camera->getCameraMode() > CAMERA_MODE_FIRST);
+ playercao->setChildrenVisible(camera->getCameraMode() > CAMERA_MODE_FIRST);
+ return 0;
+}
+
+int LuaCamera::l_get_camera_mode(lua_State *L)
+{
+ Camera *camera = getobject(L, 1);
+ if (!camera)
+ return 0;
+
+ lua_pushnumber(L, (int)camera->getCameraMode());
+
+ return 1;
+}
+
+int LuaCamera::l_get_fov(lua_State *L)
+{
+ Camera *camera = getobject(L, 1);
+ if (!camera)
+ return 0;
+
+ lua_newtable(L);
+ lua_pushnumber(L, camera->getFovX() * core::DEGTORAD);
+ lua_setfield(L, -2, "x");
+ lua_pushnumber(L, camera->getFovY() * core::DEGTORAD);
+ lua_setfield(L, -2, "y");
+ lua_pushnumber(L, camera->getCameraNode()->getFOV() * core::RADTODEG);
+ lua_setfield(L, -2, "actual");
+ lua_pushnumber(L, camera->getFovMax() * core::RADTODEG);
+ lua_setfield(L, -2, "max");
+ return 1;
+}
+
+int LuaCamera::l_get_pos(lua_State *L)
+{
+ Camera *camera = getobject(L, 1);
+ if (!camera)
+ return 0;
+
+ push_v3f(L, camera->getPosition());
+ return 1;
+}
+
+int LuaCamera::l_get_offset(lua_State *L)
+{
+ Camera *camera = getobject(L, 1);
+ if (!camera)
+ return 0;
+
+ push_v3s16(L, camera->getOffset());
+ return 1;
+}
+
+int LuaCamera::l_get_look_dir(lua_State *L)
+{
+ LocalPlayer *player = getClient(L)->getEnv().getLocalPlayer();
+ sanity_check(player);
+
+ float pitch = -1.0 * player->getPitch() * core::DEGTORAD;
+ float yaw = (player->getYaw() + 90.) * core::DEGTORAD;
+ v3f v(cos(pitch) * cos(yaw), sin(pitch), cos(pitch) * sin(yaw));
+
+ push_v3f(L, v);
+ return 1;
+}
+
+int LuaCamera::l_get_look_horizontal(lua_State *L)
+{
+ LocalPlayer *player = getClient(L)->getEnv().getLocalPlayer();
+ sanity_check(player);
+
+ lua_pushnumber(L, (player->getYaw() + 90.) * core::DEGTORAD);
+ return 1;
+}
+
+int LuaCamera::l_get_look_vertical(lua_State *L)
+{
+ LocalPlayer *player = getClient(L)->getEnv().getLocalPlayer();
+ sanity_check(player);
+
+ lua_pushnumber(L, -1.0 * player->getPitch() * core::DEGTORAD);
+ return 1;
+}
+
+int LuaCamera::l_get_aspect_ratio(lua_State *L)
+{
+ Camera *camera = getobject(L, 1);
+ if (!camera)
+ return 0;
+
+ lua_pushnumber(L, camera->getCameraNode()->getAspectRatio());
+ return 1;
+}
+
+LuaCamera *LuaCamera::checkobject(lua_State *L, int narg)
+{
+ luaL_checktype(L, narg, LUA_TUSERDATA);
+
+ void *ud = luaL_checkudata(L, narg, className);
+ if (!ud)
+ luaL_typerror(L, narg, className);
+
+ return *(LuaCamera **)ud;
+}
+
+Camera *LuaCamera::getobject(LuaCamera *ref)
+{
+ return ref->m_camera;
+}
+
+Camera *LuaCamera::getobject(lua_State *L, int narg)
+{
+ LuaCamera *ref = checkobject(L, narg);
+ assert(ref);
+ Camera *camera = getobject(ref);
+ if (!camera)
+ return NULL;
+ return camera;
+}
+
+int LuaCamera::gc_object(lua_State *L)
+{
+ LuaCamera *o = *(LuaCamera **)(lua_touserdata(L, 1));
+ delete o;
+ return 0;
+}
+
+void LuaCamera::Register(lua_State *L)
+{
+ lua_newtable(L);
+ int methodtable = lua_gettop(L);
+ luaL_newmetatable(L, className);
+ int metatable = lua_gettop(L);
+
+ lua_pushliteral(L, "__metatable");
+ lua_pushvalue(L, methodtable);
+ lua_settable(L, metatable);
+
+ lua_pushliteral(L, "__index");
+ lua_pushvalue(L, methodtable);
+ lua_settable(L, metatable);
+
+ lua_pushliteral(L, "__gc");
+ lua_pushcfunction(L, gc_object);
+ lua_settable(L, metatable);
+
+ lua_pop(L, 1);
+
+ luaL_openlib(L, 0, methods, 0);
+ lua_pop(L, 1);
+}
+
+const char LuaCamera::className[] = "Camera";
+const luaL_Reg LuaCamera::methods[] = {luamethod(LuaCamera, set_camera_mode),
+ luamethod(LuaCamera, get_camera_mode), luamethod(LuaCamera, get_fov),
+ luamethod(LuaCamera, get_pos), luamethod(LuaCamera, get_offset),
+ luamethod(LuaCamera, get_look_dir),
+ luamethod(LuaCamera, get_look_vertical),
+ luamethod(LuaCamera, get_look_horizontal),
+ luamethod(LuaCamera, get_aspect_ratio),
+
+ {0, 0}};
diff --git a/src/script/lua_api/l_camera.h b/src/script/lua_api/l_camera.h
new file mode 100644
index 000000000..04921ad03
--- /dev/null
+++ b/src/script/lua_api/l_camera.h
@@ -0,0 +1,44 @@
+#ifndef L_CAMERA_H
+#define L_CAMERA_H
+
+#include "l_base.h"
+
+class Camera;
+
+class LuaCamera : public ModApiBase
+{
+private:
+ static const char className[];
+ static const luaL_Reg methods[];
+
+ // garbage collector
+ static int gc_object(lua_State *L);
+
+ static int l_set_camera_mode(lua_State *L);
+ static int l_get_camera_mode(lua_State *L);
+
+ static int l_get_fov(lua_State *L);
+
+ static int l_get_pos(lua_State *L);
+ static int l_get_offset(lua_State *L);
+ static int l_get_look_dir(lua_State *L);
+ static int l_get_look_vertical(lua_State *L);
+ static int l_get_look_horizontal(lua_State *L);
+ static int l_get_aspect_ratio(lua_State *L);
+
+ Camera *m_camera;
+
+public:
+ LuaCamera(Camera *m);
+ ~LuaCamera() {}
+
+ static void create(lua_State *L, Camera *m);
+
+ static LuaCamera *checkobject(lua_State *L, int narg);
+ static Camera *getobject(LuaCamera *ref);
+ static Camera *getobject(lua_State *L, int narg);
+
+ static void Register(lua_State *L);
+};
+
+#endif // L_CAMERA_H
diff --git a/src/script/lua_api/l_client.cpp b/src/script/lua_api/l_client.cpp
index be3a749de..3c2955bcd 100644
--- a/src/script/lua_api/l_client.cpp
+++ b/src/script/lua_api/l_client.cpp
@@ -30,6 +30,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "mainmenumanager.h"
#include "map.h"
#include "util/string.h"
+#include "nodedef.h"
extern MainGameCallback *g_gamecallback;
@@ -63,6 +64,15 @@ int ModApiClient::l_set_last_run_mod(lua_State *L)
return 1;
}
+// print(text)
+int ModApiClient::l_print(lua_State *L)
+{
+ NO_MAP_LOCK_REQUIRED;
+ std::string text = luaL_checkstring(L, 1);
+ rawstream << text << std::endl;
+ return 0;
+}
+
// display_chat_message(message)
int ModApiClient::l_display_chat_message(lua_State *L)
{
@@ -75,6 +85,23 @@ int ModApiClient::l_display_chat_message(lua_State *L)
return 1;
}
+// send_chat_message(message)
+int ModApiClient::l_send_chat_message(lua_State *L)
+{
+ if (!lua_isstring(L, 1))
+ return 0;
+ std::string message = luaL_checkstring(L, 1);
+ getClient(L)->sendChatMessage(utf8_to_wide(message));
+ return 0;
+}
+
+// clear_out_chat_queue()
+int ModApiClient::l_clear_out_chat_queue(lua_State *L)
+{
+ getClient(L)->clearOutChatQueue();
+ return 0;
+}
+
// get_player_names()
int ModApiClient::l_get_player_names(lua_State *L)
{
@@ -83,7 +110,7 @@ int ModApiClient::l_get_player_names(lua_State *L)
int newTable = lua_gettop(L);
int index = 1;
std::list<std::string>::const_iterator iter;
- for (iter = plist.begin(); iter != plist.end(); iter++) {
+ for (iter = plist.begin(); iter != plist.end(); ++iter) {
lua_pushstring(L, (*iter).c_str());
lua_rawseti(L, newTable, index);
index++;
@@ -234,17 +261,81 @@ int ModApiClient::l_sound_stop(lua_State *L)
return 0;
}
-// get_protocol_version()
-int ModApiClient::l_get_protocol_version(lua_State *L)
+// get_server_info()
+int ModApiClient::l_get_server_info(lua_State *L)
{
- lua_pushinteger(L, getClient(L)->getProtoVersion());
+ Client *client = getClient(L);
+ Address serverAddress = client->getServerAddress();
+ lua_newtable(L);
+ lua_pushstring(L, client->getAddressName().c_str());
+ lua_setfield(L, -2, "address");
+ lua_pushstring(L, serverAddress.serializeString().c_str());
+ lua_setfield(L, -2, "ip");
+ lua_pushinteger(L, serverAddress.getPort());
+ lua_setfield(L, -2, "port");
+ lua_pushinteger(L, client->getProtoVersion());
+ lua_setfield(L, -2, "protocol_version");
return 1;
}
+// get_item_def(itemstring)
+int ModApiClient::l_get_item_def(lua_State *L)
+{
+ IGameDef *gdef = getGameDef(L);
+ assert(gdef);
+
+ IItemDefManager *idef = gdef->idef();
+ assert(idef);
+
+ if (!lua_isstring(L, 1))
+ return 0;
+
+ const std::string &name(lua_tostring(L, 1));
+ if (!idef->isKnown(name))
+ return 0;
+ const ItemDefinition &def = idef->get(name);
+
+ push_item_definition_full(L, def);
+
+ return 1;
+}
+
+// get_node_def(nodename)
+int ModApiClient::l_get_node_def(lua_State *L)
+{
+ IGameDef *gdef = getGameDef(L);
+ assert(gdef);
+
+ INodeDefManager *ndef = gdef->ndef();
+ assert(ndef);
+
+ if (!lua_isstring(L, 1))
+ return 0;
+
+ const std::string &name = lua_tostring(L, 1);
+ const ContentFeatures &cf = ndef->get(ndef->getId(name));
+ if (cf.name != name) // Unknown node. | name = <whatever>, cf.name = ignore
+ return 0;
+
+ push_content_features(L, cf);
+
+ return 1;
+}
+
+int ModApiClient::l_take_screenshot(lua_State *L)
+{
+ Client *client = getClient(L);
+ client->makeScreenshot(client->getDevice());
+ return 0;
+}
+
void ModApiClient::Initialize(lua_State *L, int top)
{
API_FCT(get_current_modname);
+ API_FCT(print);
API_FCT(display_chat_message);
+ API_FCT(send_chat_message);
+ API_FCT(clear_out_chat_queue);
API_FCT(get_player_names);
API_FCT(set_last_run_mod);
API_FCT(get_last_run_mod);
@@ -258,5 +349,8 @@ void ModApiClient::Initialize(lua_State *L, int top)
API_FCT(get_meta);
API_FCT(sound_play);
API_FCT(sound_stop);
- API_FCT(get_protocol_version);
+ API_FCT(get_server_info);
+ API_FCT(get_item_def);
+ API_FCT(get_node_def);
+ API_FCT(take_screenshot);
}
diff --git a/src/script/lua_api/l_client.h b/src/script/lua_api/l_client.h
index 478b8ed6c..fe5780fb1 100644
--- a/src/script/lua_api/l_client.h
+++ b/src/script/lua_api/l_client.h
@@ -22,6 +22,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#define L_CLIENT_H_
#include "lua_api/l_base.h"
+#include "itemdef.h"
+#include "tool.h"
class ModApiClient : public ModApiBase
{
@@ -29,13 +31,22 @@ private:
// get_current_modname()
static int l_get_current_modname(lua_State *L);
+ // print(text)
+ static int l_print(lua_State *L);
+
// display_chat_message(message)
static int l_display_chat_message(lua_State *L);
+ // send_chat_message(message)
+ static int l_send_chat_message(lua_State *L);
+
+ // clear_out_chat_queue()
+ static int l_clear_out_chat_queue(lua_State *L);
+
// get_player_names()
static int l_get_player_names(lua_State *L);
- // show_formspec(name, fornspec)
+ // show_formspec(name, formspec)
static int l_show_formspec(lua_State *L);
// send_respawn()
@@ -69,8 +80,16 @@ private:
static int l_sound_stop(lua_State *L);
- // get_protocol_version()
- static int l_get_protocol_version(lua_State *L);
+ // get_server_info()
+ static int l_get_server_info(lua_State *L);
+
+ // get_item_def(itemstring)
+ static int l_get_item_def(lua_State *L);
+
+ // get_node_def(nodename)
+ static int l_get_node_def(lua_State *L);
+
+ static int l_take_screenshot(lua_State *L);
public:
static void Initialize(lua_State *L, int top);
diff --git a/src/script/lua_api/l_craft.cpp b/src/script/lua_api/l_craft.cpp
index 2236566de..315391856 100644
--- a/src/script/lua_api/l_craft.cpp
+++ b/src/script/lua_api/l_craft.cpp
@@ -414,7 +414,7 @@ static void push_craft_recipe(lua_State *L, IGameDef *gdef,
lua_newtable(L); // items
std::vector<ItemStack>::const_iterator iter = input.items.begin();
- for (u16 j = 1; iter != input.items.end(); iter++, j++) {
+ for (u16 j = 1; iter != input.items.end(); ++iter, j++) {
if (iter->empty())
continue;
lua_pushstring(L, iter->name.c_str());
diff --git a/src/script/lua_api/l_env.cpp b/src/script/lua_api/l_env.cpp
index 4fad7b37c..630f6cc64 100644
--- a/src/script/lua_api/l_env.cpp
+++ b/src/script/lua_api/l_env.cpp
@@ -25,7 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "lua_api/l_vmanip.h"
#include "common/c_converter.h"
#include "common/c_content.h"
-#include "serverscripting.h"
+#include "scripting_server.h"
#include "environment.h"
#include "server.h"
#include "nodedef.h"
@@ -35,6 +35,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "treegen.h"
#include "emerge.h"
#include "pathfinder.h"
+#include "face_position_cache.h"
struct EnumString ModApiEnvMod::es_ClearObjectsMode[] =
{
@@ -290,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;
@@ -534,11 +534,13 @@ int ModApiEnvMod::l_get_objects_inside_radius(lua_State *L)
ScriptApiBase *script = getScriptApiBase(L);
lua_createtable(L, ids.size(), 0);
std::vector<u16>::const_iterator iter = ids.begin();
- for(u32 i = 0; iter != ids.end(); iter++) {
+ for(u32 i = 0; iter != ids.end(); ++iter) {
ServerActiveObject *obj = env->getActiveObject(*iter);
- // Insert object reference into table
- script->objectrefGetOrCreate(L, obj);
- lua_rawseti(L, -2, ++i);
+ if (!obj->isGone()) {
+ // Insert object reference into table
+ script->objectrefGetOrCreate(L, obj);
+ lua_rawseti(L, -2, ++i);
+ }
}
return 1;
}
@@ -599,7 +601,7 @@ int ModApiEnvMod::l_get_gametime(lua_State *L)
}
-// find_node_near(pos, radius, nodenames) -> pos or nil
+// find_node_near(pos, radius, nodenames, search_center) -> pos or nil
// nodenames: eg. {"ignore", "group:tree"} or "default:dirt"
int ModApiEnvMod::l_find_node_near(lua_State *L)
{
@@ -612,27 +614,27 @@ int ModApiEnvMod::l_find_node_near(lua_State *L)
v3s16 pos = read_v3s16(L, 1);
int radius = luaL_checkinteger(L, 2);
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);
}
- for (int d=1; d<=radius; d++){
+ int start_radius = (lua_toboolean(L, 4)) ? 0 : 1;
+ for (int d = start_radius; d <= radius; d++) {
std::vector<v3s16> list = FacePositionCache::getFacePositions(d);
for (std::vector<v3s16>::iterator i = list.begin();
- i != list.end(); ++i){
+ i != list.end(); ++i) {
v3s16 p = pos + (*i);
content_t c = env->getMap().getNodeNoEx(p).getContent();
- if (filter.count(c) != 0){
+ if (filter.count(c) != 0) {
push_v3s16(L, p);
return 1;
}
@@ -650,38 +652,47 @@ int ModApiEnvMod::l_find_nodes_in_area(lua_State *L)
INodeDefManager *ndef = getServer(L)->ndef();
v3s16 minp = read_v3s16(L, 1);
v3s16 maxp = read_v3s16(L, 2);
+ sortBoxVerticies(minp, maxp);
+
+ v3s16 cube = maxp - minp + 1;
+ // Volume limit equal to 8 default mapchunks, (80 * 2) ^ 3 = 4,096,000
+ if ((u64)cube.X * (u64)cube.Y * (u64)cube.Z > 4096000) {
+ luaL_error(L, "find_nodes_in_area(): area volume"
+ " exceeds allowed value of 4096000");
+ return 0;
+ }
+
std::set<content_t> filter;
- if(lua_istable(L, 3)) {
- int table = 3;
+ if (lua_istable(L, 3)) {
lua_pushnil(L);
- while(lua_next(L, table) != 0) {
+ while (lua_next(L, 3) != 0) {
// key at index -2 and value at index -1
luaL_checktype(L, -1, LUA_TSTRING);
ndef->getIds(lua_tostring(L, -1), filter);
// removes value, keeps key for next iteration
lua_pop(L, 1);
}
- } else if(lua_isstring(L, 3)) {
+ } else if (lua_isstring(L, 3)) {
ndef->getIds(lua_tostring(L, 3), filter);
}
- std::map<content_t, u16> individual_count;
+ UNORDERED_MAP<content_t, u32> individual_count;
lua_newtable(L);
u64 i = 0;
for (s16 x = minp.X; x <= maxp.X; x++)
- for (s16 y = minp.Y; y <= maxp.Y; y++)
- for (s16 z = minp.Z; z <= maxp.Z; z++) {
- v3s16 p(x, y, z);
- content_t c = env->getMap().getNodeNoEx(p).getContent();
- if (filter.count(c) != 0) {
- push_v3s16(L, p);
- lua_rawseti(L, -2, ++i);
- individual_count[c]++;
- }
+ for (s16 y = minp.Y; y <= maxp.Y; y++)
+ for (s16 z = minp.Z; z <= maxp.Z; z++) {
+ v3s16 p(x, y, z);
+ content_t c = env->getMap().getNodeNoEx(p).getContent();
+ if (filter.count(c) != 0) {
+ push_v3s16(L, p);
+ lua_rawseti(L, -2, ++i);
+ individual_count[c]++;
+ }
}
lua_newtable(L);
- for (std::set<content_t>::iterator it = filter.begin();
+ for (std::set<content_t>::const_iterator it = filter.begin();
it != filter.end(); ++it) {
lua_pushnumber(L, individual_count[*it]);
lua_setfield(L, -2, ndef->get(*it).name.c_str());
@@ -705,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);
@@ -731,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);
@@ -847,6 +867,36 @@ int ModApiEnvMod::l_line_of_sight(lua_State *L)
return 1;
}
+// fix_light(p1, p2)
+int ModApiEnvMod::l_fix_light(lua_State *L)
+{
+ GET_ENV_PTR;
+
+ v3s16 blockpos1 = getContainerPos(read_v3s16(L, 1), MAP_BLOCKSIZE);
+ v3s16 blockpos2 = getContainerPos(read_v3s16(L, 2), MAP_BLOCKSIZE);
+ ServerMap &map = env->getServerMap();
+ std::map<v3s16, MapBlock *> modified_blocks;
+ bool success = true;
+ v3s16 blockpos;
+ for (blockpos.X = blockpos1.X; blockpos.X <= blockpos2.X; blockpos.X++)
+ for (blockpos.Y = blockpos1.Y; blockpos.Y <= blockpos2.Y; blockpos.Y++)
+ for (blockpos.Z = blockpos1.Z; blockpos.Z <= blockpos2.Z; blockpos.Z++) {
+ success = success & map.repairBlockLight(blockpos, &modified_blocks);
+ }
+ if (modified_blocks.size() > 0) {
+ MapEditEvent event;
+ event.type = MEET_OTHER;
+ for (std::map<v3s16, MapBlock *>::iterator it = modified_blocks.begin();
+ it != modified_blocks.end(); ++it)
+ event.modified_blocks.insert(it->first);
+
+ map.dispatchEvent(&event);
+ }
+ lua_pushboolean(L, success);
+
+ return 1;
+}
+
// emerge_area(p1, p2, [callback, context])
// emerge mapblocks in area p1..p2, calls callback with context upon completion
int ModApiEnvMod::l_emerge_area(lua_State *L)
@@ -955,8 +1005,7 @@ int ModApiEnvMod::l_find_path(lua_State *L)
lua_newtable(L);
int top = lua_gettop(L);
unsigned int index = 1;
- for (std::vector<v3s16>::iterator i = path.begin(); i != path.end();i++)
- {
+ for (std::vector<v3s16>::iterator i = path.begin(); i != path.end(); ++i) {
lua_pushnumber(L,index);
push_v3s16(L, *i);
lua_settable(L, top);
@@ -1089,6 +1138,7 @@ void ModApiEnvMod::Initialize(lua_State *L, int top)
API_FCT(find_node_near);
API_FCT(find_nodes_in_area);
API_FCT(find_nodes_in_area_under_air);
+ API_FCT(fix_light);
API_FCT(emerge_area);
API_FCT(delete_area);
API_FCT(get_perlin);
diff --git a/src/script/lua_api/l_env.h b/src/script/lua_api/l_env.h
index 38b2282d7..7ce19b085 100644
--- a/src/script/lua_api/l_env.h
+++ b/src/script/lua_api/l_env.h
@@ -116,7 +116,7 @@ private:
// get_day_count() -> int
static int l_get_day_count(lua_State *L);
- // find_node_near(pos, radius, nodenames) -> pos or nil
+ // find_node_near(pos, radius, nodenames, search_center) -> pos or nil
// nodenames: eg. {"ignore", "group:tree"} or "default:dirt"
static int l_find_node_near(lua_State *L);
@@ -128,6 +128,9 @@ private:
// nodenames: eg. {"ignore", "group:tree"} or "default:dirt"
static int l_find_nodes_in_area_under_air(lua_State *L);
+ // fix_light(p1, p2) -> true/false
+ static int l_fix_light(lua_State *L);
+
// emerge_area(p1, p2)
static int l_emerge_area(lua_State *L);
@@ -200,11 +203,11 @@ public:
m_simple_catch_up(simple_catch_up)
{
}
- virtual std::set<std::string> getTriggerContents()
+ virtual const std::set<std::string> &getTriggerContents() const
{
return m_trigger_contents;
}
- virtual std::set<std::string> getRequiredNeighbors()
+ virtual const std::set<std::string> &getRequiredNeighbors() const
{
return m_required_neighbors;
}
diff --git a/src/script/lua_api/l_internal.h b/src/script/lua_api/l_internal.h
index b7627619e..e9b689931 100644
--- a/src/script/lua_api/l_internal.h
+++ b/src/script/lua_api/l_internal.h
@@ -31,8 +31,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#define luamethod(class, name) {#name, class::l_##name}
#define luamethod_aliased(class, name, alias) {#name, class::l_##name}, {#alias, class::l_##name}
-#define API_FCT(name) registerFunction(L, #name, l_##name,top)
-#define ASYNC_API_FCT(name) engine.registerFunction(#name, l_##name)
+#define API_FCT(name) registerFunction(L, #name, l_##name, top)
#define MAP_LOCK_REQUIRED
#define NO_MAP_LOCK_REQUIRED
diff --git a/src/script/lua_api/l_inventory.cpp b/src/script/lua_api/l_inventory.cpp
index f5e76a7b6..e92197c14 100644
--- a/src/script/lua_api/l_inventory.cpp
+++ b/src/script/lua_api/l_inventory.cpp
@@ -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);
}
diff --git a/src/script/lua_api/l_inventory.h b/src/script/lua_api/l_inventory.h
index 91d41c0d0..502827a11 100644
--- a/src/script/lua_api/l_inventory.h
+++ b/src/script/lua_api/l_inventory.h
@@ -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);
diff --git a/src/script/lua_api/l_item.cpp b/src/script/lua_api/l_item.cpp
index 2d32093a7..0e4fc4ef0 100644
--- a/src/script/lua_api/l_item.cpp
+++ b/src/script/lua_api/l_item.cpp
@@ -526,7 +526,7 @@ int ModApiItemMod::l_register_item_raw(lua_State *L)
def.node_placement_prediction = "__default";
// Read the item definition
- def = read_item_definition(L, table, def);
+ read_item_definition(L, table, def, def);
// Default to having client-side placement prediction for nodes
// ("" in item definition sets it off)
diff --git a/src/script/lua_api/l_localplayer.cpp b/src/script/lua_api/l_localplayer.cpp
index 4b44bb709..7ec4eaa62 100644
--- a/src/script/lua_api/l_localplayer.cpp
+++ b/src/script/lua_api/l_localplayer.cpp
@@ -68,14 +68,6 @@ int LuaLocalPlayer::l_get_name(lua_State *L)
return 1;
}
-int LuaLocalPlayer::l_is_teleported(lua_State *L)
-{
- LocalPlayer *player = getobject(L, 1);
-
- lua_pushboolean(L, player->got_teleported);
- return 1;
-}
-
int LuaLocalPlayer::l_is_attached(lua_State *L)
{
LocalPlayer *player = getobject(L, 1);
@@ -211,34 +203,6 @@ int LuaLocalPlayer::l_get_breath(lua_State *L)
return 1;
}
-int LuaLocalPlayer::l_get_look_dir(lua_State *L)
-{
- LocalPlayer *player = getobject(L, 1);
-
- float pitch = -1.0 * player->getPitch() * core::DEGTORAD;
- float yaw = (player->getYaw() + 90.) * core::DEGTORAD;
- v3f v(cos(pitch) * cos(yaw), sin(pitch), cos(pitch) * sin(yaw));
-
- push_v3f(L, v);
- return 1;
-}
-
-int LuaLocalPlayer::l_get_look_horizontal(lua_State *L)
-{
- LocalPlayer *player = getobject(L, 1);
-
- lua_pushnumber(L, (player->getYaw() + 90.) * core::DEGTORAD);
- return 1;
-}
-
-int LuaLocalPlayer::l_get_look_vertical(lua_State *L)
-{
- LocalPlayer *player = getobject(L, 1);
-
- lua_pushnumber(L, -1.0 * player->getPitch() * core::DEGTORAD);
- return 1;
-}
-
int LuaLocalPlayer::l_get_pos(lua_State *L)
{
LocalPlayer *player = getobject(L, 1);
@@ -247,22 +211,6 @@ int LuaLocalPlayer::l_get_pos(lua_State *L)
return 1;
}
-int LuaLocalPlayer::l_get_eye_pos(lua_State *L)
-{
- LocalPlayer *player = getobject(L, 1);
-
- push_v3f(L, player->getEyePosition());
- return 1;
-}
-
-int LuaLocalPlayer::l_get_eye_offset(lua_State *L)
-{
- LocalPlayer *player = getobject(L, 1);
-
- push_v3f(L, player->getEyeOffset());
- return 1;
-}
-
int LuaLocalPlayer::l_get_movement_acceleration(lua_State *L)
{
LocalPlayer *player = getobject(L, 1);
@@ -386,7 +334,6 @@ const luaL_Reg LuaLocalPlayer::methods[] = {
luamethod(LuaLocalPlayer, get_velocity),
luamethod(LuaLocalPlayer, get_hp),
luamethod(LuaLocalPlayer, get_name),
- luamethod(LuaLocalPlayer, is_teleported),
luamethod(LuaLocalPlayer, is_attached),
luamethod(LuaLocalPlayer, is_touching_ground),
luamethod(LuaLocalPlayer, is_in_liquid),
@@ -402,12 +349,7 @@ const luaL_Reg LuaLocalPlayer::methods[] = {
luamethod(LuaLocalPlayer, get_last_look_vertical),
luamethod(LuaLocalPlayer, get_key_pressed),
luamethod(LuaLocalPlayer, get_breath),
- luamethod(LuaLocalPlayer, get_look_dir),
- luamethod(LuaLocalPlayer, get_look_horizontal),
- luamethod(LuaLocalPlayer, get_look_vertical),
luamethod(LuaLocalPlayer, get_pos),
- luamethod(LuaLocalPlayer, get_eye_pos),
- luamethod(LuaLocalPlayer, get_eye_offset),
luamethod(LuaLocalPlayer, get_movement_acceleration),
luamethod(LuaLocalPlayer, get_movement_speed),
luamethod(LuaLocalPlayer, get_movement),
diff --git a/src/script/lua_api/l_localplayer.h b/src/script/lua_api/l_localplayer.h
index 1070857c5..e56ec808f 100644
--- a/src/script/lua_api/l_localplayer.h
+++ b/src/script/lua_api/l_localplayer.h
@@ -39,7 +39,6 @@ private:
static int l_get_name(lua_State *L);
- static int l_is_teleported(lua_State *L);
static int l_is_attached(lua_State *L);
static int l_is_touching_ground(lua_State *L);
static int l_is_in_liquid(lua_State *L);
@@ -60,15 +59,8 @@ private:
static int l_get_breath(lua_State *L);
- static int l_get_look_dir(lua_State *L);
- static int l_get_look_horizontal(lua_State *L);
- static int l_get_look_vertical(lua_State *L);
-
static int l_get_pos(lua_State *L);
- static int l_get_eye_pos(lua_State *L);
- static int l_get_eye_offset(lua_State *L);
-
static int l_get_movement_acceleration(lua_State *L);
static int l_get_movement_speed(lua_State *L);
diff --git a/src/script/lua_api/l_mainmenu.cpp b/src/script/lua_api/l_mainmenu.cpp
index 3d204db98..dc8654960 100644
--- a/src/script/lua_api/l_mainmenu.cpp
+++ b/src/script/lua_api/l_mainmenu.cpp
@@ -38,6 +38,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <IFileArchive.h>
#include <IFileSystem.h>
+
/******************************************************************************/
std::string ModApiMainMenu::getTextData(lua_State *L, std::string name)
{
@@ -297,7 +298,7 @@ int ModApiMainMenu::l_get_games(lua_State *L)
int table2 = lua_gettop(L);
int internal_index=1;
for (std::set<std::string>::iterator iter = games[i].addon_mods_paths.begin();
- iter != games[i].addon_mods_paths.end(); iter++) {
+ iter != games[i].addon_mods_paths.end(); ++iter) {
lua_pushnumber(L,internal_index);
lua_pushstring(L,(*iter).c_str());
lua_settable(L, table2);
@@ -1141,23 +1142,24 @@ void ModApiMainMenu::Initialize(lua_State *L, int top)
}
/******************************************************************************/
-void ModApiMainMenu::InitializeAsync(AsyncEngine& engine)
+void ModApiMainMenu::InitializeAsync(lua_State *L, int top)
{
- ASYNC_API_FCT(get_worlds);
- ASYNC_API_FCT(get_games);
- ASYNC_API_FCT(get_favorites);
- ASYNC_API_FCT(get_mapgen_names);
- ASYNC_API_FCT(get_modpath);
- ASYNC_API_FCT(get_gamepath);
- ASYNC_API_FCT(get_texturepath);
- ASYNC_API_FCT(get_texturepath_share);
- ASYNC_API_FCT(create_dir);
- ASYNC_API_FCT(delete_dir);
- ASYNC_API_FCT(copy_dir);
- //ASYNC_API_FCT(extract_zip); //TODO remove dependency to GuiEngine
- ASYNC_API_FCT(download_file);
- ASYNC_API_FCT(get_modstore_details);
- ASYNC_API_FCT(get_modstore_list);
- //ASYNC_API_FCT(gettext); (gettext lib isn't threadsafe)
+ API_FCT(get_worlds);
+ API_FCT(get_games);
+ API_FCT(get_favorites);
+ API_FCT(get_mapgen_names);
+ API_FCT(get_modpath);
+ API_FCT(get_gamepath);
+ API_FCT(get_texturepath);
+ API_FCT(get_texturepath_share);
+ API_FCT(create_dir);
+ API_FCT(delete_dir);
+ API_FCT(copy_dir);
+ //API_FCT(extract_zip); //TODO remove dependency to GuiEngine
+ API_FCT(download_file);
+ API_FCT(get_modstore_details);
+ API_FCT(get_modstore_list);
+ //API_FCT(gettext); (gettext lib isn't threadsafe)
}
+
diff --git a/src/script/lua_api/l_mainmenu.h b/src/script/lua_api/l_mainmenu.h
index e31ac0112..d4946bab1 100644
--- a/src/script/lua_api/l_mainmenu.h
+++ b/src/script/lua_api/l_mainmenu.h
@@ -142,6 +142,7 @@ private:
static int l_do_async_callback(lua_State *L);
public:
+
/**
* initialize this API module
* @param L lua stack to initialize
@@ -149,7 +150,7 @@ public:
*/
static void Initialize(lua_State *L, int top);
- static void InitializeAsync(AsyncEngine& engine);
+ static void InitializeAsync(lua_State *L, int top);
};
diff --git a/src/script/lua_api/l_mapgen.cpp b/src/script/lua_api/l_mapgen.cpp
index 6d3171df9..32eb7af84 100644
--- a/src/script/lua_api/l_mapgen.cpp
+++ b/src/script/lua_api/l_mapgen.cpp
@@ -325,14 +325,22 @@ void read_schematic_replacements(lua_State *L, int index, StringMap *replace_nam
if (lua_istable(L, -1)) { // Old {{"x", "y"}, ...} format
lua_rawgeti(L, -1, 1);
+ if (!lua_isstring(L, -1))
+ throw LuaError("schematics: replace_from field is not a string");
replace_from = lua_tostring(L, -1);
lua_pop(L, 1);
lua_rawgeti(L, -1, 2);
+ if (!lua_isstring(L, -1))
+ throw LuaError("schematics: replace_to field is not a string");
replace_to = lua_tostring(L, -1);
lua_pop(L, 1);
} else { // New {x = "y", ...} format
+ if (!lua_isstring(L, -2))
+ throw LuaError("schematics: replace_from field is not a string");
replace_from = lua_tostring(L, -2);
+ if (!lua_isstring(L, -1))
+ throw LuaError("schematics: replace_to field is not a string");
replace_to = lua_tostring(L, -1);
}
diff --git a/src/script/lua_api/l_metadata.cpp b/src/script/lua_api/l_metadata.cpp
index 5f4e984cb..16cbcc05e 100644
--- a/src/script/lua_api/l_metadata.cpp
+++ b/src/script/lua_api/l_metadata.cpp
@@ -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);
diff --git a/src/script/lua_api/l_nodemeta.cpp b/src/script/lua_api/l_nodemeta.cpp
index c65d56f14..0af5eea54 100644
--- a/src/script/lua_api/l_nodemeta.cpp
+++ b/src/script/lua_api/l_nodemeta.cpp
@@ -94,6 +94,32 @@ int NodeMetaRef::l_get_inventory(lua_State *L)
return 1;
}
+// mark_as_private(self, <string> or {<string>, <string>, ...})
+int NodeMetaRef::l_mark_as_private(lua_State *L)
+{
+ MAP_LOCK_REQUIRED;
+
+ NodeMetaRef *ref = checkobject(L, 1);
+ NodeMetadata *meta = dynamic_cast<NodeMetadata*>(ref->getmeta(true));
+ assert(meta);
+
+ if (lua_istable(L, 2)) {
+ lua_pushnil(L);
+ while (lua_next(L, 2) != 0) {
+ // key at index -2 and value at index -1
+ luaL_checktype(L, -1, LUA_TSTRING);
+ meta->markPrivate(lua_tostring(L, -1), true);
+ // removes value, keeps key for next iteration
+ lua_pop(L, 1);
+ }
+ } else if (lua_isstring(L, 2)) {
+ meta->markPrivate(lua_tostring(L, 2), true);
+ }
+ ref->reportMetadataChange();
+
+ return 0;
+}
+
void NodeMetaRef::handleToTable(lua_State *L, Metadata *_meta)
{
// fields
@@ -107,7 +133,7 @@ void NodeMetaRef::handleToTable(lua_State *L, Metadata *_meta)
if (inv) {
std::vector<const InventoryList *> lists = inv->getLists();
for(std::vector<const InventoryList *>::const_iterator
- i = lists.begin(); i != lists.end(); i++) {
+ i = lists.begin(); i != lists.end(); ++i) {
push_inventory_list(L, inv, (*i)->getName().c_str());
lua_setfield(L, -2, (*i)->getName().c_str());
}
@@ -132,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
}
@@ -229,6 +255,7 @@ const luaL_Reg NodeMetaRef::methodsServer[] = {
luamethod(MetaDataRef, to_table),
luamethod(MetaDataRef, from_table),
luamethod(NodeMetaRef, get_inventory),
+ luamethod(NodeMetaRef, mark_as_private),
luamethod(MetaDataRef, equals),
{0,0}
};
diff --git a/src/script/lua_api/l_nodemeta.h b/src/script/lua_api/l_nodemeta.h
index 2ac028079..dd4260ff9 100644
--- a/src/script/lua_api/l_nodemeta.h
+++ b/src/script/lua_api/l_nodemeta.h
@@ -73,6 +73,9 @@ private:
// get_inventory(self)
static int l_get_inventory(lua_State *L);
+ // mark_as_private(self, <string> or {<string>, <string>, ...})
+ static int l_mark_as_private(lua_State *L);
+
public:
NodeMetaRef(v3s16 p, ServerEnvironment *env);
NodeMetaRef(Metadata *meta);
diff --git a/src/script/lua_api/l_noise.cpp b/src/script/lua_api/l_noise.cpp
index e3e76191f..90b8864d2 100644
--- a/src/script/lua_api/l_noise.cpp
+++ b/src/script/lua_api/l_noise.cpp
@@ -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);
diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp
index 95e977f9e..8905f2d0c 100644
--- a/src/script/lua_api/l_object.cpp
+++ b/src/script/lua_api/l_object.cpp
@@ -23,13 +23,14 @@ 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"
#include "content_sao.h"
#include "server.h"
#include "hud.h"
-#include "serverscripting.h"
+#include "scripting_server.h"
struct EnumString es_HudElementType[] =
{
@@ -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;
}
@@ -726,11 +727,13 @@ int ObjectRef::l_set_detach(lua_State *L)
v3f rotation;
co->getAttachment(&parent_id, &bone, &position, &rotation);
ServerActiveObject *parent = NULL;
- if (parent_id)
+ if (parent_id) {
parent = env->getActiveObject(parent_id);
-
+ co->setAttachment(0, "", position, rotation);
+ } else {
+ co->setAttachment(0, "", v3f(0, 0, 0), v3f(0, 0, 0));
+ }
// Do it
- co->setAttachment(0, "", v3f(0,0,0), v3f(0,0,0));
if (parent != NULL)
parent->removeAttachmentChild(co->getId());
return 0;
@@ -1200,9 +1203,10 @@ int ObjectRef::l_set_attribute(lua_State *L)
}
std::string attr = luaL_checkstring(L, 2);
- std::string value = luaL_checkstring(L, 3);
-
- if (co->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
+ if (lua_isnil(L, 3)) {
+ co->removeExtendedAttribute(attr);
+ } else {
+ std::string value = luaL_checkstring(L, 3);
co->setExtendedAttribute(attr, value);
}
return 1;
@@ -1660,7 +1664,7 @@ int ObjectRef::l_hud_get_hotbar_selected_image(lua_State *L)
return 1;
}
-// set_sky(self, bgcolor, type, list)
+// set_sky(self, bgcolor, type, list, clouds = true)
int ObjectRef::l_set_sky(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
@@ -1676,9 +1680,8 @@ int ObjectRef::l_set_sky(lua_State *L)
std::vector<std::string> params;
if (lua_istable(L, 4)) {
- int table = lua_gettop(L);
lua_pushnil(L);
- while (lua_next(L, table) != 0) {
+ while (lua_next(L, 4) != 0) {
// key at index -2 and value at index -1
if (lua_isstring(L, -1))
params.push_back(lua_tostring(L, -1));
@@ -1692,7 +1695,11 @@ int ObjectRef::l_set_sky(lua_State *L)
if (type == "skybox" && params.size() != 6)
throw LuaError("skybox expects 6 textures");
- if (!getServer(L)->setSky(player, bgcolor, type, params))
+ bool clouds = true;
+ if (lua_isboolean(L, 5))
+ clouds = lua_toboolean(L, 5);
+
+ if (!getServer(L)->setSky(player, bgcolor, type, params, clouds))
return 0;
lua_pushboolean(L, true);
@@ -1710,8 +1717,9 @@ int ObjectRef::l_get_sky(lua_State *L)
video::SColor bgcolor(255, 255, 255, 255);
std::string type;
std::vector<std::string> params;
+ bool clouds;
- player->getSky(&bgcolor, &type, &params);
+ player->getSky(&bgcolor, &type, &params, &clouds);
type = type == "" ? "regular" : type;
push_ARGB8(L, bgcolor);
@@ -1724,9 +1732,89 @@ int ObjectRef::l_get_sky(lua_State *L)
lua_rawseti(L, -2, i);
i++;
}
- return 3;
+ lua_pushboolean(L, clouds);
+ return 4;
+}
+
+// set_clouds(self, {density=, color=, ambient=, height=, thickness=, speed=})
+int ObjectRef::l_set_clouds(lua_State *L)
+{
+ NO_MAP_LOCK_REQUIRED;
+ ObjectRef *ref = checkobject(L, 1);
+ RemotePlayer *player = getplayer(ref);
+ if (!player)
+ return 0;
+ if (!lua_istable(L, 2))
+ return 0;
+
+ CloudParams cloud_params = player->getCloudParams();
+
+ cloud_params.density = getfloatfield_default(L, 2, "density", cloud_params.density);
+
+ lua_getfield(L, 2, "color");
+ if (!lua_isnil(L, -1))
+ read_color(L, -1, &cloud_params.color_bright);
+ lua_pop(L, 1);
+ lua_getfield(L, 2, "ambient");
+ if (!lua_isnil(L, -1))
+ read_color(L, -1, &cloud_params.color_ambient);
+ lua_pop(L, 1);
+
+ cloud_params.height = getfloatfield_default(L, 2, "height", cloud_params.height );
+ cloud_params.thickness = getfloatfield_default(L, 2, "thickness", cloud_params.thickness);
+
+ lua_getfield(L, 2, "speed");
+ if (lua_istable(L, -1)) {
+ v2f new_speed;
+ new_speed.X = getfloatfield_default(L, -1, "x", 0);
+ new_speed.Y = getfloatfield_default(L, -1, "y", 0);
+ cloud_params.speed = new_speed;
+ }
+ lua_pop(L, 1);
+
+ if (!getServer(L)->setClouds(player, cloud_params.density,
+ cloud_params.color_bright, cloud_params.color_ambient,
+ cloud_params.height, cloud_params.thickness,
+ cloud_params.speed))
+ return 0;
+
+ player->setCloudParams(cloud_params);
+
+ lua_pushboolean(L, true);
+ return 1;
}
+int ObjectRef::l_get_clouds(lua_State *L)
+{
+ NO_MAP_LOCK_REQUIRED;
+ ObjectRef *ref = checkobject(L, 1);
+ RemotePlayer *player = getplayer(ref);
+ if (!player)
+ return 0;
+ const CloudParams &cloud_params = player->getCloudParams();
+
+ lua_newtable(L);
+ lua_pushnumber(L, cloud_params.density);
+ lua_setfield(L, -2, "density");
+ push_ARGB8(L, cloud_params.color_bright);
+ lua_setfield(L, -2, "color");
+ push_ARGB8(L, cloud_params.color_ambient);
+ lua_setfield(L, -2, "ambient");
+ lua_pushnumber(L, cloud_params.height);
+ lua_setfield(L, -2, "height");
+ lua_pushnumber(L, cloud_params.thickness);
+ lua_setfield(L, -2, "thickness");
+ lua_newtable(L);
+ lua_pushnumber(L, cloud_params.speed.X);
+ lua_setfield(L, -2, "x");
+ lua_pushnumber(L, cloud_params.speed.Y);
+ lua_setfield(L, -2, "y");
+ lua_setfield(L, -2, "speed");
+
+ return 1;
+}
+
+
// override_day_night_ratio(self, brightness=0...1)
int ObjectRef::l_override_day_night_ratio(lua_State *L)
{
@@ -1909,6 +1997,8 @@ const luaL_Reg ObjectRef::methods[] = {
luamethod(ObjectRef, hud_get_hotbar_selected_image),
luamethod(ObjectRef, set_sky),
luamethod(ObjectRef, get_sky),
+ luamethod(ObjectRef, set_clouds),
+ luamethod(ObjectRef, get_clouds),
luamethod(ObjectRef, override_day_night_ratio),
luamethod(ObjectRef, get_day_night_ratio),
luamethod(ObjectRef, set_local_animation),
diff --git a/src/script/lua_api/l_object.h b/src/script/lua_api/l_object.h
index 98f5c2b11..9801ce02b 100644
--- a/src/script/lua_api/l_object.h
+++ b/src/script/lua_api/l_object.h
@@ -283,12 +283,18 @@ private:
// hud_get_hotbar_selected_image(self)
static int l_hud_get_hotbar_selected_image(lua_State *L);
- // set_sky(self, type, list)
+ // set_sky(self, bgcolor, type, list, clouds = true)
static int l_set_sky(lua_State *L);
- // get_sky(self, type, list)
+ // get_sky(self)
static int l_get_sky(lua_State *L);
+ // set_clouds(self, {density=, color=, ambient=, height=, thickness=, speed=})
+ static int l_set_clouds(lua_State *L);
+
+ // get_clouds(self)
+ static int l_get_clouds(lua_State *L);
+
// override_day_night_ratio(self, type)
static int l_override_day_night_ratio(lua_State *L);
diff --git a/src/script/lua_api/l_server.cpp b/src/script/lua_api/l_server.cpp
index 343d11b7e..bb1d75221 100644
--- a/src/script/lua_api/l_server.cpp
+++ b/src/script/lua_api/l_server.cpp
@@ -26,6 +26,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "environment.h"
#include "player.h"
#include "log.h"
+#include <algorithm>
// request_shutdown()
int ModApiServer::l_request_shutdown(lua_State *L)
@@ -73,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;
}
@@ -87,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;
}
@@ -103,7 +104,7 @@ int ModApiServer::l_get_player_privs(lua_State *L)
int table = lua_gettop(L);
std::set<std::string> privs_s = server->getPlayerEffectivePrivs(name);
for(std::set<std::string>::const_iterator
- i = privs_s.begin(); i != privs_s.end(); i++){
+ i = privs_s.begin(); i != privs_s.end(); ++i){
lua_pushboolean(L, true);
lua_setfield(L, table, i->c_str());
}
@@ -137,7 +138,7 @@ int ModApiServer::l_get_player_ip(lua_State *L)
}
}
-// get_player_information()
+// get_player_information(name)
int ModApiServer::l_get_player_information(lua_State *L)
{
@@ -231,15 +232,15 @@ int ModApiServer::l_get_player_information(lua_State *L)
lua_pushnumber(L, uptime);
lua_settable(L, table);
+ lua_pushstring(L,"protocol_version");
+ lua_pushnumber(L, prot_vers);
+ lua_settable(L, table);
+
#ifndef NDEBUG
lua_pushstring(L,"serialization_version");
lua_pushnumber(L, ser_vers);
lua_settable(L, table);
- lua_pushstring(L,"protocol_version");
- lua_pushnumber(L, prot_vers);
- lua_settable(L, table);
-
lua_pushstring(L,"major");
lua_pushnumber(L, major);
lua_settable(L, table);
@@ -334,6 +335,22 @@ int ModApiServer::l_kick_player(lua_State *L)
return 1;
}
+int ModApiServer::l_remove_player(lua_State *L)
+{
+ NO_MAP_LOCK_REQUIRED;
+ std::string name = luaL_checkstring(L, 1);
+ ServerEnvironment *s_env = dynamic_cast<ServerEnvironment *>(getEnv(L));
+ assert(s_env);
+
+ RemotePlayer *player = s_env->getPlayer(name.c_str());
+ if (!player)
+ lua_pushinteger(L, s_env->removePlayerFromDatabase(name) ? 0 : 1);
+ else
+ lua_pushinteger(L, 2);
+
+ return 1;
+}
+
// unban_player_or_ip()
int ModApiServer::l_unban_player_or_ip(lua_State *L)
{
@@ -401,7 +418,7 @@ int ModApiServer::l_get_modnames(lua_State *L)
// Package them up for Lua
lua_createtable(L, modlist.size(), 0);
std::vector<std::string>::iterator iter = modlist.begin();
- for (u16 i = 0; iter != modlist.end(); iter++) {
+ for (u16 i = 0; iter != modlist.end(); ++iter) {
lua_pushstring(L, iter->c_str());
lua_rawseti(L, -2, ++i);
}
@@ -439,6 +456,16 @@ int ModApiServer::l_sound_stop(lua_State *L)
return 0;
}
+int ModApiServer::l_sound_fade(lua_State *L)
+{
+ NO_MAP_LOCK_REQUIRED;
+ s32 handle = luaL_checkinteger(L, 1);
+ float step = luaL_checknumber(L, 2);
+ float gain = luaL_checknumber(L, 3);
+ getServer(L)->fadeSound(handle, step, gain);
+ return 0;
+}
+
// is_singleplayer()
int ModApiServer::l_is_singleplayer(lua_State *L)
{
@@ -483,36 +510,6 @@ int ModApiServer::l_set_last_run_mod(lua_State *L)
return 0;
}
-#ifndef NDEBUG
-// cause_error(type_of_error)
-int ModApiServer::l_cause_error(lua_State *L)
-{
- NO_MAP_LOCK_REQUIRED;
- std::string type_of_error = "none";
- if(lua_isstring(L, 1))
- type_of_error = lua_tostring(L, 1);
-
- errorstream << "Error handler test called, errortype=" << type_of_error << std::endl;
-
- if(type_of_error == "segv") {
- volatile int* some_pointer = 0;
- errorstream << "Cause a sigsegv now: " << (*some_pointer) << std::endl;
-
- } else if (type_of_error == "zerodivision") {
-
- unsigned int some_number = porting::getTimeS();
- unsigned int zerovalue = 0;
- unsigned int result = some_number / zerovalue;
- errorstream << "Well this shouldn't ever be shown: " << result << std::endl;
-
- } else if (type_of_error == "exception") {
- throw BaseException("Errorhandler test fct called");
- }
-
- return 0;
-}
-#endif
-
void ModApiServer::Initialize(lua_State *L, int top)
{
API_FCT(request_shutdown);
@@ -532,6 +529,7 @@ void ModApiServer::Initialize(lua_State *L, int top)
API_FCT(show_formspec);
API_FCT(sound_play);
API_FCT(sound_stop);
+ API_FCT(sound_fade);
API_FCT(get_player_information);
API_FCT(get_player_privs);
@@ -540,12 +538,10 @@ void ModApiServer::Initialize(lua_State *L, int top)
API_FCT(get_ban_description);
API_FCT(ban_player);
API_FCT(kick_player);
+ API_FCT(remove_player);
API_FCT(unban_player_or_ip);
API_FCT(notify_authentication_modified);
API_FCT(get_last_run_mod);
API_FCT(set_last_run_mod);
-#ifndef NDEBUG
- API_FCT(cause_error);
-#endif
}
diff --git a/src/script/lua_api/l_server.h b/src/script/lua_api/l_server.h
index ca5e7b80f..251a0ce89 100644
--- a/src/script/lua_api/l_server.h
+++ b/src/script/lua_api/l_server.h
@@ -68,13 +68,16 @@ private:
// sound_stop(handle)
static int l_sound_stop(lua_State *L);
+ // sound_fade(handle, step, gain)
+ static int l_sound_fade(lua_State *L);
+
// get_player_privs(name, text)
static int l_get_player_privs(lua_State *L);
// get_player_ip()
static int l_get_player_ip(lua_State *L);
- // get_player_information()
+ // get_player_information(name)
static int l_get_player_information(lua_State *L);
// get_ban_list()
@@ -92,6 +95,9 @@ private:
// kick_player(name, [message]) -> success
static int l_kick_player(lua_State *L);
+ // remove_player(name)
+ static int l_remove_player(lua_State *L);
+
// notify_authentication_modified(name)
static int l_notify_authentication_modified(lua_State *L);
@@ -101,11 +107,6 @@ private:
// set_last_run_mod(modname)
static int l_set_last_run_mod(lua_State *L);
-#ifndef NDEBUG
- // cause_error(type_of_error)
- static int l_cause_error(lua_State *L);
-#endif
-
public:
static void Initialize(lua_State *L, int top);
};
diff --git a/src/script/lua_api/l_settings.cpp b/src/script/lua_api/l_settings.cpp
index 809f7d115..70807f3d2 100644
--- a/src/script/lua_api/l_settings.cpp
+++ b/src/script/lua_api/l_settings.cpp
@@ -23,6 +23,47 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "settings.h"
#include "log.h"
+
+#define SET_SECURITY_CHECK(L, name) \
+ if (o->m_settings == g_settings && ScriptApiSecurity::isSecure(L) && \
+ name.compare(0, 7, "secure.") == 0) { \
+ throw LuaError("Attempt to set secure setting."); \
+ }
+
+LuaSettings::LuaSettings(Settings *settings, const std::string &filename) :
+ m_settings(settings),
+ m_filename(filename),
+ m_is_own_settings(false),
+ m_write_allowed(true)
+{
+}
+
+LuaSettings::LuaSettings(const std::string &filename, bool write_allowed) :
+ m_filename(filename),
+ m_is_own_settings(true),
+ m_write_allowed(write_allowed)
+{
+ m_settings = new Settings();
+ m_settings->readConfigFile(filename.c_str());
+}
+
+LuaSettings::~LuaSettings()
+{
+ if (m_is_own_settings)
+ delete m_settings;
+}
+
+
+void LuaSettings::create(lua_State *L, Settings *settings,
+ const std::string &filename)
+{
+ LuaSettings *o = new LuaSettings(settings, filename);
+ *(void **)(lua_newuserdata(L, sizeof(void *))) = o;
+ luaL_getmetatable(L, className);
+ lua_setmetatable(L, -2);
+}
+
+
// garbage collector
int LuaSettings::gc_object(lua_State* L)
{
@@ -31,6 +72,7 @@ int LuaSettings::gc_object(lua_State* L)
return 0;
}
+
// get(self, key) -> value
int LuaSettings::l_get(lua_State* L)
{
@@ -74,12 +116,30 @@ int LuaSettings::l_set(lua_State* L)
std::string key = std::string(luaL_checkstring(L, 2));
const char* value = luaL_checkstring(L, 3);
+ SET_SECURITY_CHECK(L, key);
+
if (!o->m_settings->set(key, value))
throw LuaError("Invalid sequence found in setting parameters");
return 0;
}
+// set_bool(self, key, value)
+int LuaSettings::l_set_bool(lua_State* L)
+{
+ NO_MAP_LOCK_REQUIRED;
+ LuaSettings* o = checkobject(L, 1);
+
+ std::string key = std::string(luaL_checkstring(L, 2));
+ bool value = lua_toboolean(L, 3);
+
+ SET_SECURITY_CHECK(L, key);
+
+ o->m_settings->setBool(key, value);
+
+ return 1;
+}
+
// remove(self, key) -> success
int LuaSettings::l_remove(lua_State* L)
{
@@ -88,6 +148,8 @@ int LuaSettings::l_remove(lua_State* L)
std::string key = std::string(luaL_checkstring(L, 2));
+ SET_SECURITY_CHECK(L, key);
+
bool success = o->m_settings->remove(key);
lua_pushboolean(L, success);
@@ -147,19 +209,6 @@ int LuaSettings::l_to_table(lua_State* L)
return 1;
}
-LuaSettings::LuaSettings(const char* filename, bool write_allowed)
-{
- m_write_allowed = write_allowed;
- m_filename = std::string(filename);
-
- m_settings = new Settings();
- m_settings->readConfigFile(m_filename.c_str());
-}
-
-LuaSettings::~LuaSettings()
-{
- delete m_settings;
-}
void LuaSettings::Register(lua_State* L)
{
@@ -190,7 +239,7 @@ void LuaSettings::Register(lua_State* L)
}
// LuaSettings(filename)
-// Creates an LuaSettings and leaves it on top of stack
+// Creates a LuaSettings and leaves it on top of the stack
int LuaSettings::create_object(lua_State* L)
{
NO_MAP_LOCK_REQUIRED;
@@ -209,8 +258,9 @@ LuaSettings* LuaSettings::checkobject(lua_State* L, int narg)
NO_MAP_LOCK_REQUIRED;
luaL_checktype(L, narg, LUA_TUSERDATA);
void *ud = luaL_checkudata(L, narg, className);
- if(!ud) luaL_typerror(L, narg, className);
- return *(LuaSettings**)ud; // unbox pointer
+ if (!ud)
+ luaL_typerror(L, narg, className);
+ return *(LuaSettings**) ud; // unbox pointer
}
const char LuaSettings::className[] = "Settings";
@@ -218,6 +268,7 @@ const luaL_Reg LuaSettings::methods[] = {
luamethod(LuaSettings, get),
luamethod(LuaSettings, get_bool),
luamethod(LuaSettings, set),
+ luamethod(LuaSettings, set_bool),
luamethod(LuaSettings, remove),
luamethod(LuaSettings, get_names),
luamethod(LuaSettings, write),
diff --git a/src/script/lua_api/l_settings.h b/src/script/lua_api/l_settings.h
index b90f0a8f2..54b003ab3 100644
--- a/src/script/lua_api/l_settings.h
+++ b/src/script/lua_api/l_settings.h
@@ -42,6 +42,9 @@ private:
// set(self, key, value)
static int l_set(lua_State *L);
+ // set_bool(self, key, value)
+ static int l_set_bool(lua_State *L);
+
// remove(self, key) -> success
static int l_remove(lua_State *L);
@@ -54,16 +57,20 @@ private:
// to_table(self) -> {[key1]=value1,...}
static int l_to_table(lua_State *L);
- bool m_write_allowed;
Settings *m_settings;
std::string m_filename;
+ bool m_is_own_settings;
+ bool m_write_allowed;
public:
- LuaSettings(const char *filename, bool write_allowed);
+ LuaSettings(Settings *settings, const std::string &filename);
+ LuaSettings(const std::string &filename, bool write_allowed);
~LuaSettings();
+ static void create(lua_State *L, Settings *settings, const std::string &filename);
+
// LuaSettings(filename)
- // Creates an LuaSettings and leaves it on top of stack
+ // Creates a LuaSettings and leaves it on top of the stack
static int create_object(lua_State *L);
static LuaSettings *checkobject(lua_State *L, int narg);
diff --git a/src/script/lua_api/l_util.cpp b/src/script/lua_api/l_util.cpp
index 809a2eb68..aaccf17ee 100644
--- a/src/script/lua_api/l_util.cpp
+++ b/src/script/lua_api/l_util.cpp
@@ -19,6 +19,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "lua_api/l_util.h"
#include "lua_api/l_internal.h"
+#include "lua_api/l_settings.h"
#include "common/c_converter.h"
#include "common/c_content.h"
#include "cpp_api/s_async.h"
@@ -35,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>
@@ -77,71 +80,6 @@ int ModApiUtil::l_get_us_time(lua_State *L)
return 1;
}
-#define CHECK_SECURE_SETTING(L, name) \
- if (ScriptApiSecurity::isSecure(L) && \
- name.compare(0, 7, "secure.") == 0) { \
- throw LuaError("Attempt to set secure setting."); \
- }
-
-// setting_set(name, value)
-int ModApiUtil::l_setting_set(lua_State *L)
-{
- NO_MAP_LOCK_REQUIRED;
- std::string name = luaL_checkstring(L, 1);
- std::string value = luaL_checkstring(L, 2);
- CHECK_SECURE_SETTING(L, name);
- g_settings->set(name, value);
- return 0;
-}
-
-// setting_get(name)
-int ModApiUtil::l_setting_get(lua_State *L)
-{
- NO_MAP_LOCK_REQUIRED;
- const char *name = luaL_checkstring(L, 1);
- try{
- std::string value = g_settings->get(name);
- lua_pushstring(L, value.c_str());
- } catch(SettingNotFoundException &e){
- lua_pushnil(L);
- }
- return 1;
-}
-
-// setting_setbool(name)
-int ModApiUtil::l_setting_setbool(lua_State *L)
-{
- NO_MAP_LOCK_REQUIRED;
- std::string name = luaL_checkstring(L, 1);
- bool value = lua_toboolean(L, 2);
- CHECK_SECURE_SETTING(L, name);
- g_settings->setBool(name, value);
- return 0;
-}
-
-// setting_getbool(name)
-int ModApiUtil::l_setting_getbool(lua_State *L)
-{
- NO_MAP_LOCK_REQUIRED;
- const char *name = luaL_checkstring(L, 1);
- try{
- bool value = g_settings->getBool(name);
- lua_pushboolean(L, value);
- } catch(SettingNotFoundException &e){
- lua_pushnil(L);
- }
- return 1;
-}
-
-// setting_save()
-int ModApiUtil::l_setting_save(lua_State *L)
-{
- NO_MAP_LOCK_REQUIRED;
- if(g_settings_path != "")
- g_settings->updateConfigFile(g_settings_path.c_str());
- return 0;
-}
-
// parse_json(str[, nullvalue])
int ModApiUtil::l_parse_json(lua_State *L)
{
@@ -418,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;
@@ -486,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)
{
@@ -493,12 +474,6 @@ void ModApiUtil::Initialize(lua_State *L, int top)
API_FCT(get_us_time);
- API_FCT(setting_set);
- API_FCT(setting_get);
- API_FCT(setting_setbool);
- API_FCT(setting_getbool);
- API_FCT(setting_save);
-
API_FCT(parse_json);
API_FCT(write_json);
@@ -517,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);
@@ -524,6 +500,10 @@ 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");
}
void ModApiUtil::InitializeClient(lua_State *L, int top)
@@ -546,36 +526,35 @@ void ModApiUtil::InitializeClient(lua_State *L, int top)
API_FCT(decode_base64);
API_FCT(get_version);
+ API_FCT(sha1);
}
-void ModApiUtil::InitializeAsync(AsyncEngine& engine)
+void ModApiUtil::InitializeAsync(lua_State *L, int top)
{
- ASYNC_API_FCT(log);
+ API_FCT(log);
- ASYNC_API_FCT(get_us_time);
+ API_FCT(get_us_time);
- //ASYNC_API_FCT(setting_set);
- ASYNC_API_FCT(setting_get);
- //ASYNC_API_FCT(setting_setbool);
- ASYNC_API_FCT(setting_getbool);
- //ASYNC_API_FCT(setting_save);
+ API_FCT(parse_json);
+ API_FCT(write_json);
- ASYNC_API_FCT(parse_json);
- ASYNC_API_FCT(write_json);
+ API_FCT(is_yes);
- ASYNC_API_FCT(is_yes);
+ API_FCT(get_builtin_path);
- ASYNC_API_FCT(get_builtin_path);
+ API_FCT(compress);
+ API_FCT(decompress);
- ASYNC_API_FCT(compress);
- ASYNC_API_FCT(decompress);
+ API_FCT(mkdir);
+ API_FCT(get_dir_list);
- ASYNC_API_FCT(mkdir);
- ASYNC_API_FCT(get_dir_list);
+ API_FCT(encode_base64);
+ API_FCT(decode_base64);
- ASYNC_API_FCT(encode_base64);
- ASYNC_API_FCT(decode_base64);
+ API_FCT(get_version);
+ API_FCT(sha1);
- ASYNC_API_FCT(get_version);
+ LuaSettings::create(L, g_settings, g_settings_path);
+ lua_setfield(L, top, "settings");
}
diff --git a/src/script/lua_api/l_util.h b/src/script/lua_api/l_util.h
index 7325a841a..872e43625 100644
--- a/src/script/lua_api/l_util.h
+++ b/src/script/lua_api/l_util.h
@@ -45,21 +45,6 @@ private:
// get us precision time
static int l_get_us_time(lua_State *L);
- // setting_set(name, value)
- static int l_setting_set(lua_State *L);
-
- // setting_get(name)
- static int l_setting_get(lua_State *L);
-
- // setting_setbool(name, value)
- static int l_setting_setbool(lua_State *L);
-
- // setting_getbool(name)
- static int l_setting_getbool(lua_State *L);
-
- // setting_save()
- static int l_setting_save(lua_State *L);
-
// parse_json(str[, nullvalue])
static int l_parse_json(lua_State *L);
@@ -96,6 +81,9 @@ private:
// get_dir_list(path, is_dir)
static int l_get_dir_list(lua_State *L);
+ // safe_file_write(path, content)
+ static int l_safe_file_write(lua_State *L);
+
// request_insecure_environment()
static int l_request_insecure_environment(lua_State *L);
@@ -108,9 +96,12 @@ private:
// get_version()
static int l_get_version(lua_State *L);
+ // sha1(string, raw)
+ static int l_sha1(lua_State *L);
+
public:
static void Initialize(lua_State *L, int top);
-
+ static void InitializeAsync(lua_State *L, int top);
static void InitializeClient(lua_State *L, int top);
static void InitializeAsync(AsyncEngine &engine);
diff --git a/src/script/lua_api/l_vmanip.cpp b/src/script/lua_api/l_vmanip.cpp
index 7316fb200..254a7e5a6 100644
--- a/src/script/lua_api/l_vmanip.cpp
+++ b/src/script/lua_api/l_vmanip.cpp
@@ -110,9 +110,10 @@ int LuaVoxelManip::l_write_to_map(lua_State *L)
MAP_LOCK_REQUIRED;
LuaVoxelManip *o = checkobject(L, 1);
+ bool update_light = lua_isboolean(L, 2) ? lua_toboolean(L, 2) : true;
GET_ENV_PTR;
ServerMap *map = &(env->getServerMap());
- if (o->is_mapgen_vm) {
+ if (o->is_mapgen_vm || !update_light) {
o->vm->blitBackAll(&(o->modified_blocks));
} else {
voxalgo::blit_back_with_light(map, o->vm,
diff --git a/src/script/clientscripting.cpp b/src/script/scripting_client.cpp
index ba3f910c0..da289e564 100644
--- a/src/script/clientscripting.cpp
+++ b/src/script/scripting_client.cpp
@@ -18,7 +18,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#include "clientscripting.h"
+#include "scripting_client.h"
#include "client.h"
#include "cpp_api/s_internal.h"
#include "lua_api/l_client.h"
@@ -30,6 +30,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "lua_api/l_item.h"
#include "lua_api/l_nodemeta.h"
#include "lua_api/l_localplayer.h"
+#include "lua_api/l_camera.h"
ClientScripting::ClientScripting(Client *client):
ScriptApiBase()
@@ -50,7 +51,8 @@ ClientScripting::ClientScripting(Client *client):
InitializeModApi(L, top);
lua_pop(L, 1);
- LuaMinimap::create(L, client->getMinimap());
+ if (client->getMinimap())
+ LuaMinimap::create(L, client->getMinimap());
// Push builtin initialization type
lua_pushstring(L, "client");
@@ -61,16 +63,17 @@ ClientScripting::ClientScripting(Client *client):
void ClientScripting::InitializeModApi(lua_State *L, int top)
{
- ModApiUtil::InitializeClient(L, top);
- ModApiClient::Initialize(L, top);
- ModApiStorage::Initialize(L, top);
- ModApiEnvMod::InitializeClient(L, top);
-
LuaItemStack::Register(L);
StorageRef::Register(L);
LuaMinimap::Register(L);
NodeMetaRef::RegisterClient(L);
LuaLocalPlayer::Register(L);
+ LuaCamera::Register(L);
+
+ ModApiUtil::InitializeClient(L, top);
+ ModApiClient::Initialize(L, top);
+ ModApiStorage::Initialize(L, top);
+ ModApiEnvMod::InitializeClient(L, top);
}
void ClientScripting::on_client_ready(LocalPlayer *localplayer)
@@ -78,3 +81,8 @@ void ClientScripting::on_client_ready(LocalPlayer *localplayer)
lua_State *L = getStack();
LuaLocalPlayer::create(L, localplayer);
}
+
+void ClientScripting::on_camera_ready(Camera *camera)
+{
+ LuaCamera::create(getStack(), camera);
+}
diff --git a/src/script/clientscripting.h b/src/script/scripting_client.h
index df94e8b71..c13fde607 100644
--- a/src/script/clientscripting.h
+++ b/src/script/scripting_client.h
@@ -24,9 +24,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "cpp_api/s_base.h"
#include "cpp_api/s_client.h"
#include "cpp_api/s_security.h"
+#include "util/basic_macros.h"
class Client;
class LocalPlayer;
+class Camera;
class ClientScripting:
virtual public ScriptApiBase,
public ScriptApiSecurity,
@@ -35,8 +37,10 @@ class ClientScripting:
public:
ClientScripting(Client *client);
void on_client_ready(LocalPlayer *localplayer);
+ void on_camera_ready(Camera *camera);
private:
virtual void InitializeModApi(lua_State *L, int top);
+ DISABLE_CLASS_COPY(ClientScripting);
};
#endif
diff --git a/src/script/scripting_mainmenu.cpp b/src/script/scripting_mainmenu.cpp
index 61318735d..48957b472 100644
--- a/src/script/scripting_mainmenu.cpp
+++ b/src/script/scripting_mainmenu.cpp
@@ -59,17 +59,16 @@ MainMenuScripting::MainMenuScripting(GUIEngine* guiengine)
/******************************************************************************/
void MainMenuScripting::initializeModApi(lua_State *L, int top)
{
+ registerLuaClasses(L, top);
+
// Initialize mod API modules
ModApiMainMenu::Initialize(L, top);
ModApiUtil::Initialize(L, top);
ModApiSound::Initialize(L, top);
- // Register reference classes (userdata)
- LuaSettings::Register(L);
-
- // Register functions to async environment
- ModApiMainMenu::InitializeAsync(asyncEngine);
- ModApiUtil::InitializeAsync(asyncEngine);
+ asyncEngine.registerStateInitializer(registerLuaClasses);
+ asyncEngine.registerStateInitializer(ModApiMainMenu::InitializeAsync);
+ asyncEngine.registerStateInitializer(ModApiUtil::InitializeAsync);
// Initialize async environment
//TODO possibly make number of async threads configurable
@@ -77,13 +76,21 @@ void MainMenuScripting::initializeModApi(lua_State *L, int top)
}
/******************************************************************************/
-void MainMenuScripting::step() {
+void MainMenuScripting::registerLuaClasses(lua_State *L, int top)
+{
+ LuaSettings::Register(L);
+}
+
+/******************************************************************************/
+void MainMenuScripting::step()
+{
asyncEngine.step(getStack());
}
/******************************************************************************/
-unsigned int MainMenuScripting::queueAsync(std::string serialized_func,
- std::string serialized_param) {
+unsigned int MainMenuScripting::queueAsync(const std::string &serialized_func,
+ const std::string &serialized_param)
+{
return asyncEngine.queueAsyncJob(serialized_func, serialized_param);
}
diff --git a/src/script/scripting_mainmenu.h b/src/script/scripting_mainmenu.h
index 3a0795df4..7b3a6eba8 100644
--- a/src/script/scripting_mainmenu.h
+++ b/src/script/scripting_mainmenu.h
@@ -23,6 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "cpp_api/s_base.h"
#include "cpp_api/s_mainmenu.h"
#include "cpp_api/s_async.h"
+#include "util/basic_macros.h"
/*****************************************************************************/
/* Scripting <-> Main Menu Interface */
@@ -39,12 +40,14 @@ public:
void step();
// Pass async events from engine to async threads
- unsigned int queueAsync(std::string serialized_func,
- std::string serialized_params);
+ unsigned int queueAsync(const std::string &serialized_func,
+ const std::string &serialized_params);
private:
void initializeModApi(lua_State *L, int top);
+ static void registerLuaClasses(lua_State *L, int top);
AsyncEngine asyncEngine;
+ DISABLE_CLASS_COPY(MainMenuScripting);
};
diff --git a/src/script/serverscripting.cpp b/src/script/scripting_server.cpp
index 215b2cfd7..cd01b0773 100644
--- a/src/script/serverscripting.cpp
+++ b/src/script/scripting_server.cpp
@@ -17,7 +17,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#include "serverscripting.h"
+#include "scripting_server.h"
#include "server.h"
#include "log.h"
#include "settings.h"
@@ -82,19 +82,6 @@ ServerScripting::ServerScripting(Server* server)
void ServerScripting::InitializeModApi(lua_State *L, int top)
{
- // Initialize mod api modules
- ModApiCraft::Initialize(L, top);
- ModApiEnvMod::Initialize(L, top);
- ModApiInventory::Initialize(L, top);
- ModApiItemMod::Initialize(L, top);
- ModApiMapgen::Initialize(L, top);
- ModApiParticles::Initialize(L, top);
- ModApiRollback::Initialize(L, top);
- ModApiServer::Initialize(L, top);
- ModApiUtil::Initialize(L, top);
- ModApiHttp::Initialize(L, top);
- ModApiStorage::Initialize(L, top);
-
// Register reference classes (userdata)
InvRef::Register(L);
ItemStackMetaRef::Register(L);
@@ -111,6 +98,19 @@ void ServerScripting::InitializeModApi(lua_State *L, int top)
ObjectRef::Register(L);
LuaSettings::Register(L);
StorageRef::Register(L);
+
+ // Initialize mod api modules
+ ModApiCraft::Initialize(L, top);
+ ModApiEnvMod::Initialize(L, top);
+ ModApiInventory::Initialize(L, top);
+ ModApiItemMod::Initialize(L, top);
+ ModApiMapgen::Initialize(L, top);
+ ModApiParticles::Initialize(L, top);
+ ModApiRollback::Initialize(L, top);
+ ModApiServer::Initialize(L, top);
+ ModApiUtil::Initialize(L, top);
+ ModApiHttp::Initialize(L, top);
+ ModApiStorage::Initialize(L, top);
}
void log_deprecated(const std::string &message)
diff --git a/src/script/serverscripting.h b/src/script/scripting_server.h
index fd97ea40b..1b335406e 100644
--- a/src/script/serverscripting.h
+++ b/src/script/scripting_server.h
@@ -28,6 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "cpp_api/s_player.h"
#include "cpp_api/s_server.h"
#include "cpp_api/s_security.h"
+#include "util/basic_macros.h"
/*****************************************************************************/
/* Scripting <-> Server Game Interface */
@@ -50,6 +51,7 @@ public:
private:
void InitializeModApi(lua_State *L, int top);
+ DISABLE_CLASS_COPY(ServerScripting);
};
void log_deprecated(const std::string &message);
diff --git a/src/serialization.h b/src/serialization.h
index 52c63098e..c91c3241f 100644
--- a/src/serialization.h
+++ b/src/serialization.h
@@ -63,13 +63,14 @@ with this program; if not, write to the Free Software Foundation, Inc.,
25: Improved node timer format
26: Never written; read the same as 25
27: Added light spreading flags to blocks
+ 28: Added "private" flag to NodeMetadata
*/
// This represents an uninitialized or invalid format
#define SER_FMT_VER_INVALID 255
// Highest supported serialization version
-#define SER_FMT_VER_HIGHEST_READ 27
+#define SER_FMT_VER_HIGHEST_READ 28
// Saved on disk version
-#define SER_FMT_VER_HIGHEST_WRITE 27
+#define SER_FMT_VER_HIGHEST_WRITE 28
// Lowest supported serialization version
#define SER_FMT_VER_LOWEST_READ 0
// Lowest serialization version for writing
diff --git a/src/server.cpp b/src/server.cpp
index a9e5c3d08..4b54c2398 100644
--- a/src/server.cpp
+++ b/src/server.cpp
@@ -38,7 +38,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "settings.h"
#include "profiler.h"
#include "log.h"
-#include "serverscripting.h"
+#include "scripting_server.h"
#include "nodedef.h"
#include "itemdef.h"
#include "craftdef.h"
@@ -60,6 +60,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "util/base64.h"
#include "util/sha1.h"
#include "util/hex.h"
+#include "database.h"
class ClientNotFoundException : public BaseException
{
@@ -89,6 +90,15 @@ void *ServerThread::run()
DSTACK(FUNCTION_NAME);
BEGIN_DEBUG_EXCEPTION_HANDLER
+ /*
+ * The real business of the server happens on the ServerThread.
+ * How this works:
+ * AsyncRunStep() runs an actual server step as soon as enough time has
+ * passed (dedicated_server_loop keeps track of that).
+ * Receive() blocks at least(!) 30ms waiting for a packet (so this loop
+ * doesn't busy wait) and will process any remaining packets.
+ */
+
m_server->AsyncRunStep(true);
while (!stopRequested()) {
@@ -99,7 +109,6 @@ void *ServerThread::run()
m_server->Receive();
- } catch (con::NoIncomingDataException &e) {
} catch (con::PeerNotFoundException &e) {
infostream<<"Server: PeerNotFoundException"<<std::endl;
} catch (ClientNotFoundException &e) {
@@ -229,32 +238,6 @@ Server::Server(
modconf.printUnsatisfiedModsError();
}
- Settings worldmt_settings;
- std::string worldmt = m_path_world + DIR_DELIM + "world.mt";
- worldmt_settings.readConfigFile(worldmt.c_str());
- std::vector<std::string> names = worldmt_settings.getNames();
- std::set<std::string> load_mod_names;
- for(std::vector<std::string>::iterator it = names.begin();
- it != names.end(); ++it) {
- std::string name = *it;
- if(name.compare(0,9,"load_mod_")==0 && worldmt_settings.getBool(name))
- load_mod_names.insert(name.substr(9));
- }
- // complain about mods declared to be loaded, but not found
- for(std::vector<ModSpec>::iterator it = m_mods.begin();
- it != m_mods.end(); ++it)
- load_mod_names.erase((*it).name);
- for(std::vector<ModSpec>::iterator it = unsatisfied_mods.begin();
- it != unsatisfied_mods.end(); ++it)
- load_mod_names.erase((*it).name);
- if(!load_mod_names.empty()) {
- errorstream << "The following mods could not be found:";
- for(std::set<std::string>::iterator it = load_mod_names.begin();
- it != load_mod_names.end(); ++it)
- errorstream << " \"" << (*it) << "\"";
- errorstream << std::endl;
- }
-
//lock environment
MutexAutoLock envlock(m_env_mutex);
@@ -282,7 +265,7 @@ Server::Server(
if (!string_allowed(mod.name, MODNAME_ALLOWED_CHARS)) {
throw ModError("Error loading mod \"" + mod.name +
"\": Mod name does not follow naming conventions: "
- "Only chararacters [a-z0-9_] are allowed.");
+ "Only characters [a-z0-9_] are allowed.");
}
std::string script_path = mod.path + DIR_DELIM + "init.lua";
infostream << " [" << padStringRight(mod.name, 12) << "] [\""
@@ -352,17 +335,14 @@ Server::Server(
Server::~Server()
{
- infostream<<"Server destructing"<<std::endl;
+ infostream << "Server destructing" << std::endl;
// Send shutdown message
SendChatMessage(PEER_ID_INEXISTENT, L"*** Server shutting down");
{
MutexAutoLock envlock(m_env_mutex);
-
- // Execute script shutdown hooks
- m_script->on_shutdown();
-
+
infostream << "Server: Saving players" << std::endl;
m_env->saveLoadedPlayers();
@@ -378,6 +358,20 @@ Server::~Server()
}
m_env->kickAllPlayers(SERVER_ACCESSDENIED_SHUTDOWN,
kick_msg, reconnect);
+ }
+
+ // Do this before stopping the server in case mapgen callbacks need to access
+ // server-controlled resources (like ModStorages). Also do them before
+ // shutdown callbacks since they may modify state that is finalized in a
+ // callback.
+ m_emerge->stopThreads();
+
+ {
+ MutexAutoLock envlock(m_env_mutex);
+
+ // Execute script shutdown hooks
+ infostream << "Executing shutdown hooks" << std::endl;
+ m_script->on_shutdown();
infostream << "Server: Saving environment metadata" << std::endl;
m_env->saveMeta();
@@ -387,10 +381,6 @@ Server::~Server()
stop();
delete m_thread;
- // stop all emerge threads before deleting players that may have
- // requested blocks to be emerged
- m_emerge->stopThreads();
-
// Delete things in the reverse order of creation
delete m_emerge;
delete m_env;
@@ -402,7 +392,7 @@ Server::~Server()
delete m_craftdef;
// Deinitialize scripting
- infostream<<"Server: Deinitializing scripting"<<std::endl;
+ infostream << "Server: Deinitializing scripting" << std::endl;
delete m_script;
// Delete detached inventories
@@ -433,7 +423,7 @@ void Server::start(Address bind_addr)
m_thread->start();
// ASCII art for the win!
- actionstream
+ rawstream
<<" .__ __ __ "<<std::endl
<<" _____ |__| ____ _____/ |_ ____ _______/ |_ "<<std::endl
<<" / \\| |/ \\_/ __ \\ __\\/ __ \\ / ___/\\ __\\"<<std::endl
@@ -599,7 +589,7 @@ void Server::AsyncRunStep(bool initial_step)
ScopeProfiler sp(g_profiler, "Server: liquid transform");
std::map<v3s16, MapBlock*> modified_blocks;
- m_env->getMap().transformLiquids(modified_blocks);
+ m_env->getMap().transformLiquids(modified_blocks, m_env);
#if 0
/*
Update lighting
@@ -1070,30 +1060,45 @@ void Server::Receive()
DSTACK(FUNCTION_NAME);
SharedBuffer<u8> data;
u16 peer_id;
- try {
- NetworkPacket pkt;
- m_con.Receive(&pkt);
- peer_id = pkt.getPeerId();
- ProcessData(&pkt);
- }
- catch(con::InvalidIncomingDataException &e) {
- infostream<<"Server::Receive(): "
- "InvalidIncomingDataException: what()="
- <<e.what()<<std::endl;
- }
- catch(SerializationError &e) {
- infostream<<"Server::Receive(): "
- "SerializationError: what()="
- <<e.what()<<std::endl;
- }
- catch(ClientStateError &e) {
- errorstream << "ProcessData: peer=" << peer_id << e.what() << std::endl;
- DenyAccess_Legacy(peer_id, L"Your client sent something server didn't expect."
- L"Try reconnecting or updating your client");
- }
- catch(con::PeerNotFoundException &e) {
- // Do nothing
- }
+ bool first = true;
+ NetworkPacket pkt;
+ for (;;) {
+ pkt.clear();
+ peer_id = 0;
+ try {
+ /*
+ In the first iteration *wait* for a packet, afterwards process
+ all packets that are immediately available (no waiting).
+ */
+ if (first) {
+ m_con.Receive(&pkt);
+ first = false;
+ } else {
+ if (!m_con.TryReceive(&pkt))
+ return;
+ }
+
+ peer_id = pkt.getPeerId();
+ ProcessData(&pkt);
+
+ } catch (const con::InvalidIncomingDataException &e) {
+ infostream << "Server::Receive(): InvalidIncomingDataException: what()="
+ << e.what() << std::endl;
+ } catch (const SerializationError &e) {
+ infostream << "Server::Receive(): SerializationError: what()="
+ << e.what() << std::endl;
+ } catch (const ClientStateError &e) {
+ errorstream << "ProcessData: peer=" << peer_id << " what()="
+ << e.what() << std::endl;
+ DenyAccess_Legacy(peer_id, L"Your client sent something server didn't expect."
+ L"Try reconnecting or updating your client");
+ } catch (const con::PeerNotFoundException &e) {
+ // Do nothing
+ } catch (const con::NoIncomingDataException &e) {
+ return;
+ }
+ }
+
}
PlayerSAO* Server::StageTwoClientInit(u16 peer_id)
@@ -1668,15 +1673,20 @@ void Server::SendInventory(PlayerSAO* playerSAO)
void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
{
DSTACK(FUNCTION_NAME);
+ if (peer_id != PEER_ID_INEXISTENT) {
+ NetworkPacket pkt(TOCLIENT_CHAT_MESSAGE, 0, peer_id);
- NetworkPacket pkt(TOCLIENT_CHAT_MESSAGE, 0, peer_id);
- pkt << message;
+ if (m_clients.getProtocolVersion(peer_id) < 27)
+ pkt << unescape_enriched(message);
+ else
+ pkt << message;
- if (peer_id != PEER_ID_INEXISTENT) {
Send(&pkt);
- }
- else {
- m_clients.sendToAll(&pkt);
+ } else {
+ std::vector<u16> clients = m_clients.getClientIDs();
+ for (std::vector<u16>::iterator it = clients.begin();
+ it != clients.end(); ++it)
+ SendChatMessage(*it, message);
}
}
@@ -1706,13 +1716,25 @@ void Server::SendSpawnParticle(u16 peer_id, u16 protocol_version,
const struct TileAnimationParams &animation, u8 glow)
{
DSTACK(FUNCTION_NAME);
+ static const float radius =
+ g_settings->getS16("max_block_send_distance") * MAP_BLOCKSIZE * BS;
+
if (peer_id == PEER_ID_INEXISTENT) {
- // This sucks and should be replaced by a better solution in a refactor:
std::vector<u16> clients = m_clients.getClientIDs();
+
for (std::vector<u16>::iterator i = clients.begin(); i != clients.end(); ++i) {
RemotePlayer *player = m_env->getPlayer(*i);
if (!player)
continue;
+
+ PlayerSAO *sao = player->getPlayerSAO();
+ if (!sao)
+ continue;
+
+ // Do not send to distant clients
+ if (sao->getBasePosition().getDistanceFrom(pos * BS) > radius)
+ continue;
+
SendSpawnParticle(*i, player->protocol_version,
pos, velocity, acceleration,
expirationtime, size, collisiondetection,
@@ -1870,7 +1892,8 @@ void Server::SendHUDSetParam(u16 peer_id, u16 param, const std::string &value)
}
void Server::SendSetSky(u16 peer_id, const video::SColor &bgcolor,
- const std::string &type, const std::vector<std::string> &params)
+ const std::string &type, const std::vector<std::string> &params,
+ bool &clouds)
{
NetworkPacket pkt(TOCLIENT_SET_SKY, 0, peer_id);
pkt << bgcolor << type << (u16) params.size();
@@ -1878,6 +1901,22 @@ void Server::SendSetSky(u16 peer_id, const video::SColor &bgcolor,
for(size_t i=0; i<params.size(); i++)
pkt << params[i];
+ pkt << clouds;
+
+ Send(&pkt);
+}
+
+void Server::SendCloudParams(u16 peer_id, float density,
+ const video::SColor &color_bright,
+ const video::SColor &color_ambient,
+ float height,
+ float thickness,
+ const v2f &speed)
+{
+ NetworkPacket pkt(TOCLIENT_CLOUD_PARAMS, 0, peer_id);
+ pkt << density << color_bright << color_ambient
+ << height << thickness << speed;
+
Send(&pkt);
}
@@ -2085,15 +2124,23 @@ s32 Server::playSound(const SimpleSoundSpec &spec,
m_playing_sounds[id] = ServerPlayingSound();
ServerPlayingSound &psound = m_playing_sounds[id];
psound.params = params;
+ psound.spec = spec;
+ float gain = params.gain * spec.gain;
NetworkPacket pkt(TOCLIENT_PLAY_SOUND, 0);
- pkt << id << spec.name << (float) (spec.gain * params.gain)
- << (u8) params.type << pos << params.object << params.loop;
+ pkt << id << spec.name << gain
+ << (u8) params.type << pos << params.object
+ << params.loop << params.fade;
+
+ // Backwards compability
+ bool play_sound = gain > 0;
- for(std::vector<u16>::iterator i = dst_clients.begin();
+ for (std::vector<u16>::iterator i = dst_clients.begin();
i != dst_clients.end(); ++i) {
- psound.clients.insert(*i);
- m_clients.send(*i, 0, &pkt, true);
+ if (play_sound || m_clients.getProtocolVersion(*i) >= 32) {
+ psound.clients.insert(*i);
+ m_clients.send(*i, 0, &pkt, true);
+ }
}
return id;
}
@@ -2117,6 +2164,52 @@ void Server::stopSound(s32 handle)
m_playing_sounds.erase(i);
}
+void Server::fadeSound(s32 handle, float step, float gain)
+{
+ // Get sound reference
+ UNORDERED_MAP<s32, ServerPlayingSound>::iterator i =
+ m_playing_sounds.find(handle);
+ if (i == m_playing_sounds.end())
+ return;
+
+ ServerPlayingSound &psound = i->second;
+ psound.params.gain = gain;
+
+ NetworkPacket pkt(TOCLIENT_FADE_SOUND, 4);
+ pkt << handle << step << gain;
+
+ // Backwards compability
+ bool play_sound = gain > 0;
+ ServerPlayingSound compat_psound = psound;
+ compat_psound.clients.clear();
+
+ NetworkPacket compat_pkt(TOCLIENT_STOP_SOUND, 4);
+ compat_pkt << handle;
+
+ for (UNORDERED_SET<u16>::iterator it = psound.clients.begin();
+ it != psound.clients.end();) {
+ if (m_clients.getProtocolVersion(*it) >= 32) {
+ // Send as reliable
+ m_clients.send(*it, 0, &pkt, true);
+ ++it;
+ } else {
+ compat_psound.clients.insert(*it);
+ // Stop old sound
+ m_clients.send(*it, 0, &compat_pkt, true);
+ psound.clients.erase(it++);
+ }
+ }
+
+ // Remove sound reference
+ if (!play_sound || psound.clients.size() == 0)
+ m_playing_sounds.erase(i);
+
+ if (play_sound && compat_psound.clients.size() > 0) {
+ // Play new sound volume on older clients
+ playSound(compat_psound.spec, compat_psound.params);
+ }
+}
+
void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
std::vector<u16> *far_players, float far_d_nodes)
{
@@ -2618,9 +2711,8 @@ void Server::RespawnPlayer(u16 peer_id)
bool repositioned = m_script->on_respawnplayer(playersao);
if (!repositioned) {
- v3f pos = findSpawnPos();
// setPos will send the new position to client
- playersao->setPos(pos);
+ playersao->setPos(findSpawnPos());
}
SendPlayerHP(peer_id);
@@ -2813,12 +2905,15 @@ void Server::handleChatInterfaceEvent(ChatEvent *evt)
}
std::wstring Server::handleChat(const std::string &name, const std::wstring &wname,
- const std::wstring &wmessage, bool check_shout_priv, RemotePlayer *player)
+ std::wstring wmessage, bool check_shout_priv, RemotePlayer *player)
{
// If something goes wrong, this player is to blame
RollbackScopeActor rollback_scope(m_rollback,
std::string("player:") + name);
+ if (g_settings->getBool("strip_color_codes"))
+ wmessage = unescape_enriched(wmessage);
+
if (player) {
switch (player->canSendChatMessage()) {
case RPLAYER_CHATRESULT_FLOODING: {
@@ -2873,7 +2968,7 @@ std::wstring Server::handleChat(const std::string &name, const std::wstring &wna
/*
Send the message to others
*/
- actionstream << "CHAT: " << wide_to_narrow(line) << std::endl;
+ actionstream << "CHAT: " << wide_to_narrow(unescape_enriched(line)) << std::endl;
std::vector<u16> clients = m_clients.getClientIDs();
@@ -3186,13 +3281,30 @@ bool Server::setPlayerEyeOffset(RemotePlayer *player, v3f first, v3f third)
}
bool Server::setSky(RemotePlayer *player, const video::SColor &bgcolor,
- const std::string &type, const std::vector<std::string> &params)
+ const std::string &type, const std::vector<std::string> &params,
+ bool &clouds)
{
if (!player)
return false;
- player->setSky(bgcolor, type, params);
- SendSetSky(player->peer_id, bgcolor, type, params);
+ player->setSky(bgcolor, type, params, clouds);
+ SendSetSky(player->peer_id, bgcolor, type, params, clouds);
+ return true;
+}
+
+bool Server::setClouds(RemotePlayer *player, float density,
+ const video::SColor &color_bright,
+ const video::SColor &color_ambient,
+ float height,
+ float thickness,
+ const v2f &speed)
+{
+ if (!player)
+ return false;
+
+ SendCloudParams(player->peer_id, density,
+ color_bright, color_ambient, height,
+ thickness, speed);
return true;
}
@@ -3436,14 +3548,16 @@ v3f Server::findSpawnPos()
}
bool is_good = false;
+ // Limit spawn range to mapgen edges (determined by 'mapgen_limit')
+ s32 range_max = map.getMapgenParams()->getSpawnRangeMax();
// Try to find a good place a few times
for(s32 i = 0; i < 4000 && !is_good; i++) {
- s32 range = 1 + i;
+ s32 range = MYMIN(1 + i, range_max);
// We're going to try to throw the player to this position
v2s16 nodepos2d = v2s16(
- -range + (myrand() % (range * 2)),
- -range + (myrand() % (range * 2)));
+ -range + (myrand() % (range * 2)),
+ -range + (myrand() % (range * 2)));
// Get spawn level at point
s16 spawn_level = m_emerge->getSpawnLevelAtPoint(nodepos2d);
@@ -3479,9 +3593,16 @@ v3f Server::findSpawnPos()
void Server::requestShutdown(const std::string &msg, bool reconnect, float delay)
{
+ m_shutdown_timer = delay;
+ m_shutdown_msg = msg;
+ m_shutdown_ask_reconnect = reconnect;
+
if (delay == 0.0f) {
// No delay, shutdown immediately
m_shutdown_requested = true;
+ // only print to the infostream, a chat message saying
+ // "Server Shutting Down" is sent when the server destructs.
+ infostream << "*** Immediate Server shutdown requested." << std::endl;
} else if (delay < 0.0f && m_shutdown_timer > 0.0f) {
// Negative delay, cancel shutdown if requested
m_shutdown_timer = 0.0f;
@@ -3495,10 +3616,7 @@ void Server::requestShutdown(const std::string &msg, bool reconnect, float delay
infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
SendChatMessage(PEER_ID_INEXISTENT, ws.str());
} else if (delay > 0.0f) {
- // Positive delay, delay the shutdown
- m_shutdown_timer = delay;
- m_shutdown_msg = msg;
- m_shutdown_ask_reconnect = reconnect;
+ // Positive delay, tell the clients when the server will shut down
std::wstringstream ws;
ws << L"*** Server shutting down in "
@@ -3512,8 +3630,6 @@ void Server::requestShutdown(const std::string &msg, bool reconnect, float delay
PlayerSAO* Server::emergePlayer(const char *name, u16 peer_id, u16 proto_version)
{
- bool newplayer = false;
-
/*
Try to get an existing player
*/
@@ -3534,44 +3650,18 @@ PlayerSAO* Server::emergePlayer(const char *name, u16 peer_id, u16 proto_version
return NULL;
}
- // Create a new player active object
- PlayerSAO *playersao = new PlayerSAO(m_env, peer_id, isSingleplayer());
- player = m_env->loadPlayer(name, playersao);
-
- // Create player if it doesn't exist
if (!player) {
- newplayer = true;
- player = new RemotePlayer(name, this->idef());
- // Set player position
- infostream<<"Server: Finding spawn place for player \""
- <<name<<"\""<<std::endl;
- playersao->setBasePosition(findSpawnPos());
-
- // Make sure the player is saved
- player->setModified(true);
-
- // Add player to environment
- m_env->addPlayer(player);
- } else {
- // If the player exists, ensure that they respawn inside legal bounds
- // This fixes an assert crash when the player can't be added
- // to the environment
- if (objectpos_over_limit(playersao->getBasePosition())) {
- actionstream << "Respawn position for player \""
- << name << "\" outside limits, resetting" << std::endl;
- playersao->setBasePosition(findSpawnPos());
- }
+ player = new RemotePlayer(name, idef());
}
- playersao->initialize(player, getPlayerEffectivePrivs(player->getName()));
-
- player->protocol_version = proto_version;
+ bool newplayer = false;
- /* Clean up old HUD elements from previous sessions */
- player->clearHud();
+ // Load player
+ PlayerSAO *playersao = m_env->loadPlayer(player, &newplayer, peer_id, isSingleplayer());
- /* Add object to environment */
- m_env->addActiveObject(playersao);
+ // Complete init with server parts
+ playersao->finalize(player, getPlayerEffectivePrivs(player->getName()));
+ player->protocol_version = proto_version;
/* Run scripts */
if (newplayer) {
@@ -3615,6 +3705,11 @@ void dedicated_server_loop(Server &server, bool &kill)
static const float profiler_print_interval =
g_settings->getFloat("profiler_print_interval");
+ /*
+ * The dedicated server loop only does time-keeping (in Server::step) and
+ * provides a way to main.cpp to kill the server externally (bool &kill).
+ */
+
for(;;) {
// This is kind of a hack but can be done like this
// because server.step() is very light
diff --git a/src/server.h b/src/server.h
index e2445f833..398b6275f 100644
--- a/src/server.h
+++ b/src/server.h
@@ -115,6 +115,7 @@ struct ServerSoundParams
u16 object;
float max_hear_distance;
bool loop;
+ float fade;
ServerSoundParams():
gain(1.0),
@@ -123,7 +124,8 @@ struct ServerSoundParams
pos(0,0,0),
object(0),
max_hear_distance(32*BS),
- loop(false)
+ loop(false),
+ fade(0)
{}
v3f getPos(ServerEnvironment *env, bool *pos_exists) const;
@@ -132,6 +134,7 @@ struct ServerSoundParams
struct ServerPlayingSound
{
ServerSoundParams params;
+ SimpleSoundSpec spec;
UNORDERED_SET<u16> clients; // peer ids
};
@@ -174,7 +177,6 @@ public:
void handleCommand_Init_Legacy(NetworkPacket* pkt);
void handleCommand_Init2(NetworkPacket* pkt);
void handleCommand_RequestMedia(NetworkPacket* pkt);
- void handleCommand_ReceivedMedia(NetworkPacket* pkt);
void handleCommand_ClientReady(NetworkPacket* pkt);
void handleCommand_GotBlocks(NetworkPacket* pkt);
void handleCommand_PlayerPos(NetworkPacket* pkt);
@@ -232,6 +234,7 @@ public:
// Envlock
s32 playSound(const SimpleSoundSpec &spec, const ServerSoundParams &params);
void stopSound(s32 handle);
+ void fadeSound(s32 handle, float step, float gain);
// Envlock
std::set<std::string> getPlayerEffectivePrivs(const std::string &name);
@@ -307,6 +310,7 @@ public:
bool showFormspec(const char *name, const std::string &formspec, const std::string &formname);
Map & getMap() { return m_env->getMap(); }
ServerEnvironment & getEnv() { return *m_env; }
+ v3f findSpawnPos();
u32 hudAdd(RemotePlayer *player, HudElement *element);
bool hudRemove(RemotePlayer *player, u32 id);
@@ -331,7 +335,14 @@ public:
bool setPlayerEyeOffset(RemotePlayer *player, v3f first, v3f third);
bool setSky(RemotePlayer *player, const video::SColor &bgcolor,
- const std::string &type, const std::vector<std::string> &params);
+ const std::string &type, const std::vector<std::string> &params,
+ bool &clouds);
+ bool setClouds(RemotePlayer *player, float density,
+ const video::SColor &color_bright,
+ const video::SColor &color_ambient,
+ float height,
+ float thickness,
+ const v2f &speed);
bool overrideDayNightRatio(RemotePlayer *player, bool do_override, float brightness);
@@ -400,7 +411,14 @@ private:
void SendHUDSetFlags(u16 peer_id, u32 flags, u32 mask);
void SendHUDSetParam(u16 peer_id, u16 param, const std::string &value);
void SendSetSky(u16 peer_id, const video::SColor &bgcolor,
- const std::string &type, const std::vector<std::string> &params);
+ const std::string &type, const std::vector<std::string> &params,
+ bool &clouds);
+ void SendCloudParams(u16 peer_id, float density,
+ const video::SColor &color_bright,
+ const video::SColor &color_ambient,
+ float height,
+ float thickness,
+ const v2f &speed);
void SendOverrideDayNightRatio(u16 peer_id, bool do_override, float ratio);
/*
@@ -463,18 +481,17 @@ private:
void RespawnPlayer(u16 peer_id);
void DeleteClient(u16 peer_id, ClientDeletionReason reason);
void UpdateCrafting(RemotePlayer *player);
+ bool checkInteractDistance(RemotePlayer *player, const f32 d, const std::string what);
void handleChatInterfaceEvent(ChatEvent *evt);
// This returns the answer to the sender of wmessage, or "" if there is none
std::wstring handleChat(const std::string &name, const std::wstring &wname,
- const std::wstring &wmessage,
+ std::wstring wmessage_input,
bool check_shout_priv = false,
RemotePlayer *player = NULL);
void handleAdminChat(const ChatEventChat *evt);
- v3f findSpawnPos();
-
// When called, connection mutex should be locked
RemoteClient* getClient(u16 peer_id,ClientState state_min=CS_Active);
RemoteClient* getClientNoEx(u16 peer_id,ClientState state_min=CS_Active);
diff --git a/src/serverenvironment.cpp b/src/serverenvironment.cpp
index e09c7da16..8c3c34854 100644
--- a/src/serverenvironment.cpp
+++ b/src/serverenvironment.cpp
@@ -28,7 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "profiler.h"
#include "raycast.h"
#include "remoteplayer.h"
-#include "serverscripting.h"
+#include "scripting_server.h"
#include "server.h"
#include "voxelalgorithms.h"
#include "util/serialize.h"
@@ -36,6 +36,13 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "util/pointedthing.h"
#include "threading/mutex_auto_lock.h"
#include "filesys.h"
+#include "gameparams.h"
+#include "database-dummy.h"
+#include "database-files.h"
+#include "database-sqlite3.h"
+#if USE_POSTGRESQL
+#include "database-postgresql.h"
+#endif
#define LBM_NAME_ALLOWED_CHARS "abcdefghijklmnopqrstuvwxyz0123456789_:"
@@ -134,7 +141,7 @@ void LBMManager::addLBMDef(LoadingBlockModifierDef *lbm_def)
if (!string_allowed(lbm_def->name, LBM_NAME_ALLOWED_CHARS)) {
throw ModError("Error adding LBM \"" + lbm_def->name +
"\": Does not follow naming conventions: "
- "Only chararacters [a-z0-9_:] are allowed.");
+ "Only characters [a-z0-9_:] are allowed.");
}
m_lbm_defs[lbm_def->name] = lbm_def;
@@ -255,16 +262,25 @@ void LBMManager::applyLBMs(ServerEnvironment *env, MapBlock *block, u32 stamp)
MapNode n;
content_t c;
lbm_lookup_map::const_iterator it = getLBMsIntroducedAfter(stamp);
- for (pos.X = 0; pos.X < MAP_BLOCKSIZE; pos.X++)
- for (pos.Y = 0; pos.Y < MAP_BLOCKSIZE; pos.Y++)
- for (pos.Z = 0; pos.Z < MAP_BLOCKSIZE; pos.Z++)
- {
- n = block->getNodeNoEx(pos);
- c = n.getContent();
- for (LBMManager::lbm_lookup_map::const_iterator iit = it;
- iit != m_lbm_lookup.end(); ++iit) {
- const std::vector<LoadingBlockModifierDef *> *lbm_list =
- iit->second.lookup(c);
+ for (; it != m_lbm_lookup.end(); ++it) {
+ // Cache previous version to speedup lookup which has a very high performance
+ // penalty on each call
+ content_t previous_c = CONTENT_IGNORE;
+ std::vector<LoadingBlockModifierDef *> *lbm_list = NULL;
+
+ for (pos.X = 0; pos.X < MAP_BLOCKSIZE; pos.X++)
+ for (pos.Y = 0; pos.Y < MAP_BLOCKSIZE; pos.Y++)
+ for (pos.Z = 0; pos.Z < MAP_BLOCKSIZE; pos.Z++) {
+ n = block->getNodeNoEx(pos);
+ c = n.getContent();
+
+ // If content_t are not matching perform an LBM lookup
+ if (previous_c != c) {
+ lbm_list = (std::vector<LoadingBlockModifierDef *> *)
+ it->second.lookup(c);
+ previous_c = c;
+ }
+
if (!lbm_list)
continue;
for (std::vector<LoadingBlockModifierDef *>::const_iterator iit =
@@ -272,7 +288,7 @@ void LBMManager::applyLBMs(ServerEnvironment *env, MapBlock *block, u32 stamp)
(*iit)->trigger(env, pos + pos_of_block, n);
}
}
- }
+ }
}
/*
@@ -365,8 +381,30 @@ ServerEnvironment::ServerEnvironment(ServerMap *map,
m_game_time_fraction_counter(0),
m_last_clear_objects_time(0),
m_recommended_send_interval(0.1),
- m_max_lag_estimate(0.1)
+ m_max_lag_estimate(0.1),
+ m_player_database(NULL)
{
+ // Determine which database backend to use
+ std::string conf_path = path_world + DIR_DELIM + "world.mt";
+ Settings conf;
+ bool succeeded = conf.readConfigFile(conf_path.c_str());
+ if (!succeeded || !conf.exists("player_backend")) {
+ // fall back to files
+ conf.set("player_backend", "files");
+ warningstream << "/!\\ You are using old player file backend. "
+ << "This backend is deprecated and will be removed in next release /!\\"
+ << std::endl << "Switching to SQLite3 or PostgreSQL is advised, "
+ << "please read http://wiki.minetest.net/Database_backends." << std::endl;
+
+ if (!conf.updateConfigFile(conf_path.c_str())) {
+ errorstream << "ServerEnvironment::ServerEnvironment(): "
+ << "Failed to update world.mt!" << std::endl;
+ }
+ }
+
+ std::string name = "";
+ conf.getNoEx("player_backend", name);
+ m_player_database = openPlayerDatabase(name, path_world, conf);
}
ServerEnvironment::~ServerEnvironment()
@@ -392,6 +430,8 @@ ServerEnvironment::~ServerEnvironment()
i != m_players.end(); ++i) {
delete (*i);
}
+
+ delete m_player_database;
}
Map & ServerEnvironment::getMap()
@@ -455,6 +495,11 @@ void ServerEnvironment::removePlayer(RemotePlayer *player)
}
}
+bool ServerEnvironment::removePlayerFromDatabase(const std::string &name)
+{
+ return m_player_database->removePlayer(name);
+}
+
bool ServerEnvironment::line_of_sight(v3f pos1, v3f pos2, float stepsize, v3s16 *p)
{
float distance = pos1.getDistanceFrom(pos2);
@@ -495,7 +540,7 @@ void ServerEnvironment::kickAllPlayers(AccessDeniedCode reason,
void ServerEnvironment::saveLoadedPlayers()
{
- std::string players_path = m_path_world + DIR_DELIM "players";
+ std::string players_path = m_path_world + DIR_DELIM + "players";
fs::CreateDir(players_path);
for (std::vector<RemotePlayer *>::iterator it = m_players.begin();
@@ -503,63 +548,63 @@ void ServerEnvironment::saveLoadedPlayers()
++it) {
if ((*it)->checkModified() ||
((*it)->getPlayerSAO() && (*it)->getPlayerSAO()->extendedAttributesModified())) {
- (*it)->save(players_path, m_server);
+ try {
+ m_player_database->savePlayer(*it);
+ } catch (DatabaseException &e) {
+ errorstream << "Failed to save player " << (*it)->getName() << " exception: "
+ << e.what() << std::endl;
+ throw;
+ }
}
}
}
void ServerEnvironment::savePlayer(RemotePlayer *player)
{
- std::string players_path = m_path_world + DIR_DELIM "players";
- fs::CreateDir(players_path);
-
- player->save(players_path, m_server);
+ try {
+ m_player_database->savePlayer(player);
+ } catch (DatabaseException &e) {
+ errorstream << "Failed to save player " << player->getName() << " exception: "
+ << e.what() << std::endl;
+ throw;
+ }
}
-RemotePlayer *ServerEnvironment::loadPlayer(const std::string &playername, PlayerSAO *sao)
+PlayerSAO *ServerEnvironment::loadPlayer(RemotePlayer *player, bool *new_player,
+ u16 peer_id, bool is_singleplayer)
{
- bool newplayer = false;
- bool found = false;
- std::string players_path = m_path_world + DIR_DELIM "players" DIR_DELIM;
- std::string path = players_path + playername;
-
- RemotePlayer *player = getPlayer(playername.c_str());
- if (!player) {
- player = new RemotePlayer("", m_server->idef());
- newplayer = true;
- }
-
- for (u32 i = 0; i < PLAYER_FILE_ALTERNATE_TRIES; i++) {
- //// Open file and deserialize
- std::ifstream is(path.c_str(), std::ios_base::binary);
- if (!is.good())
- continue;
-
- player->deSerialize(is, path, sao);
- is.close();
-
- if (player->getName() == playername) {
- found = true;
- break;
+ PlayerSAO *playersao = new PlayerSAO(this, player, peer_id, is_singleplayer);
+ // Create player if it doesn't exist
+ if (!m_player_database->loadPlayer(player, playersao)) {
+ *new_player = true;
+ // Set player position
+ infostream << "Server: Finding spawn place for player \""
+ << player->getName() << "\"" << std::endl;
+ playersao->setBasePosition(m_server->findSpawnPos());
+
+ // Make sure the player is saved
+ player->setModified(true);
+ } else {
+ // If the player exists, ensure that they respawn inside legal bounds
+ // This fixes an assert crash when the player can't be added
+ // to the environment
+ if (objectpos_over_limit(playersao->getBasePosition())) {
+ actionstream << "Respawn position for player \""
+ << player->getName() << "\" outside limits, resetting" << std::endl;
+ playersao->setBasePosition(m_server->findSpawnPos());
}
-
- path = players_path + playername + itos(i);
}
- if (!found) {
- infostream << "Player file for player " << playername
- << " not found" << std::endl;
- if (newplayer)
- delete player;
+ // Add player to environment
+ addPlayer(player);
- return NULL;
- }
+ /* Clean up old HUD elements from previous sessions */
+ player->clearHud();
- if (newplayer) {
- addPlayer(player);
- }
- player->setModified(false);
- return player;
+ /* Add object to environment */
+ addActiveObject(playersao);
+
+ return playersao;
}
void ServerEnvironment::saveMeta()
@@ -685,7 +730,7 @@ public:
chance = 1;
ActiveABM aabm;
aabm.abm = abm;
- if(abm->getSimpleCatchUp()) {
+ if (abm->getSimpleCatchUp()) {
float intervals = actual_interval / trigger_interval;
if(intervals == 0)
continue;
@@ -695,25 +740,23 @@ public:
} else {
aabm.chance = chance;
}
+
// Trigger neighbors
- std::set<std::string> required_neighbors_s
- = abm->getRequiredNeighbors();
- for(std::set<std::string>::iterator
- i = required_neighbors_s.begin();
- i != required_neighbors_s.end(); ++i)
- {
- ndef->getIds(*i, aabm.required_neighbors);
+ const std::set<std::string> &required_neighbors_s =
+ abm->getRequiredNeighbors();
+ for (std::set<std::string>::iterator rn = required_neighbors_s.begin();
+ rn != required_neighbors_s.end(); ++rn) {
+ ndef->getIds(*rn, aabm.required_neighbors);
}
+
// Trigger contents
- std::set<std::string> contents_s = abm->getTriggerContents();
- for(std::set<std::string>::iterator
- i = contents_s.begin(); i != contents_s.end(); ++i)
- {
+ const std::set<std::string> &contents_s = abm->getTriggerContents();
+ for (std::set<std::string>::iterator cs = contents_s.begin();
+ cs != contents_s.end(); ++cs) {
std::set<content_t> ids;
- ndef->getIds(*i, ids);
- for(std::set<content_t>::const_iterator k = ids.begin();
- k != ids.end(); ++k)
- {
+ ndef->getIds(*cs, ids);
+ for (std::set<content_t>::const_iterator k = ids.begin();
+ k != ids.end(); ++k) {
content_t c = *k;
if (c >= m_aabms.size())
m_aabms.resize(c + 256, NULL);
@@ -983,26 +1026,19 @@ void ServerEnvironment::clearObjects(ClearObjectsMode mode)
infostream << "ServerEnvironment::clearObjects(): "
<< "Removing all active objects" << std::endl;
std::vector<u16> objects_to_remove;
- for (ActiveObjectMap::iterator i = m_active_objects.begin();
- i != m_active_objects.end(); ++i) {
- ServerActiveObject* obj = i->second;
+ for (ActiveObjectMap::iterator it = m_active_objects.begin();
+ it != m_active_objects.end(); ++it) {
+ u16 id = it->first;
+ ServerActiveObject* obj = it->second;
if (obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
continue;
- u16 id = i->first;
+
// Delete static object if block is loaded
- if (obj->m_static_exists) {
- MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
- if (block) {
- block->m_static_objects.remove(id);
- block->raiseModified(MOD_STATE_WRITE_NEEDED,
- MOD_REASON_CLEAR_ALL_OBJECTS);
- obj->m_static_exists = false;
- }
- }
+ deleteStaticFromBlock(obj, id, MOD_REASON_CLEAR_ALL_OBJECTS, true);
+
// If known by some client, don't delete immediately
if (obj->m_known_by_count > 0) {
- obj->m_pending_deactivation = true;
- obj->m_removed = true;
+ obj->m_pending_removal = true;
continue;
}
@@ -1019,9 +1055,9 @@ void ServerEnvironment::clearObjects(ClearObjectsMode mode)
}
// Remove references from m_active_objects
- for (std::vector<u16>::iterator i = objects_to_remove.begin();
- i != objects_to_remove.end(); ++i) {
- m_active_objects.erase(*i);
+ for (std::vector<u16>::iterator it = objects_to_remove.begin();
+ it != objects_to_remove.end(); ++it) {
+ m_active_objects.erase(*it);
}
// Get list of loaded blocks
@@ -1364,16 +1400,14 @@ void ServerEnvironment::step(float dtime)
for(ActiveObjectMap::iterator i = m_active_objects.begin();
i != m_active_objects.end(); ++i) {
ServerActiveObject* obj = i->second;
- // Don't step if is to be removed or stored statically
- if(obj->m_removed || obj->m_pending_deactivation)
+ if (obj->isGone())
continue;
+
// Step object
obj->step(dtime, send_recommended);
// Read messages from object
- while(!obj->m_messages_out.empty())
- {
- m_active_object_messages.push(
- obj->m_messages_out.front());
+ while (!obj->m_messages_out.empty()) {
+ m_active_object_messages.push(obj->m_messages_out.front());
obj->m_messages_out.pop();
}
}
@@ -1385,9 +1419,6 @@ void ServerEnvironment::step(float dtime)
if(m_object_management_interval.step(dtime, 0.5))
{
ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
- /*
- Remove objects that satisfy (m_removed && m_known_by_count==0)
- */
removeRemovedObjects();
}
@@ -1507,7 +1538,7 @@ void ServerEnvironment::getAddedActiveObjects(PlayerSAO *playersao, s16 radius,
player_radius_f = 0;
/*
Go through the object list,
- - discard m_removed objects,
+ - discard removed/deactivated objects,
- discard objects that are too far away,
- discard objects that are found in current_objects.
- add remaining objects to added_objects
@@ -1521,8 +1552,7 @@ void ServerEnvironment::getAddedActiveObjects(PlayerSAO *playersao, s16 radius,
if (object == NULL)
continue;
- // Discard if removed or deactivating
- if(object->m_removed || object->m_pending_deactivation)
+ if (object->isGone())
continue;
f32 distance_f = object->getBasePosition().
@@ -1580,7 +1610,7 @@ void ServerEnvironment::getRemovedActiveObjects(PlayerSAO *playersao, s16 radius
continue;
}
- if (object->m_removed || object->m_pending_deactivation) {
+ if (object->isGone()) {
removed_objects.push(id);
continue;
}
@@ -1722,7 +1752,7 @@ u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
}
/*
- Remove objects that satisfy (m_removed && m_known_by_count==0)
+ Remove objects that satisfy (isGone() && m_known_by_count==0)
*/
void ServerEnvironment::removeRemovedObjects()
{
@@ -1731,62 +1761,54 @@ void ServerEnvironment::removeRemovedObjects()
i != m_active_objects.end(); ++i) {
u16 id = i->first;
ServerActiveObject* obj = i->second;
+
// This shouldn't happen but check it
- if(obj == NULL)
- {
- infostream<<"NULL object found in ServerEnvironment"
- <<" while finding removed objects. id="<<id<<std::endl;
- // Id to be removed from m_active_objects
+ if (!obj) {
+ errorstream << "ServerEnvironment::removeRemovedObjects(): "
+ << "NULL object found. id=" << id << std::endl;
objects_to_remove.push_back(id);
continue;
}
/*
- We will delete objects that are marked as removed or thatare
- waiting for deletion after deactivation
+ We will handle objects marked for removal or deactivation
*/
- if (!obj->m_removed && !obj->m_pending_deactivation)
+ if (!obj->isGone())
continue;
/*
- Delete static data from block if is marked as removed
+ Delete static data from block if removed
*/
- if(obj->m_static_exists && obj->m_removed)
- {
- MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
- if (block) {
- block->m_static_objects.remove(id);
- block->raiseModified(MOD_STATE_WRITE_NEEDED,
- MOD_REASON_REMOVE_OBJECTS_REMOVE);
- obj->m_static_exists = false;
- } else {
- infostream<<"Failed to emerge block from which an object to "
- <<"be removed was loaded from. id="<<id<<std::endl;
- }
- }
+ if (obj->m_pending_removal)
+ deleteStaticFromBlock(obj, id, MOD_REASON_REMOVE_OBJECTS_REMOVE, false);
- // If m_known_by_count > 0, don't actually remove. On some future
+ // If still known by clients, don't actually remove. On some future
// invocation this will be 0, which is when removal will continue.
if(obj->m_known_by_count > 0)
continue;
/*
- Move static data from active to stored if not marked as removed
+ Move static data from active to stored if deactivated
*/
- if(obj->m_static_exists && !obj->m_removed){
+ if (!obj->m_pending_removal && obj->m_static_exists) {
MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
if (block) {
std::map<u16, StaticObject>::iterator i =
block->m_static_objects.m_active.find(id);
- if(i != block->m_static_objects.m_active.end()){
+ if (i != block->m_static_objects.m_active.end()) {
block->m_static_objects.m_stored.push_back(i->second);
block->m_static_objects.m_active.erase(id);
block->raiseModified(MOD_STATE_WRITE_NEEDED,
MOD_REASON_REMOVE_OBJECTS_DEACTIVATE);
+ } else {
+ warningstream << "ServerEnvironment::removeRemovedObjects(): "
+ << "id=" << id << " m_static_exists=true but "
+ << "static data doesn't actually exist in "
+ << PP(obj->m_static_block) << std::endl;
}
} else {
- infostream<<"Failed to emerge block from which an object to "
- <<"be deactivated was loaded from. id="<<id<<std::endl;
+ infostream << "Failed to emerge block from which an object to "
+ << "be deactivated was loaded from. id=" << id << std::endl;
}
}
@@ -1799,13 +1821,12 @@ void ServerEnvironment::removeRemovedObjects()
if(obj->environmentDeletes())
delete obj;
- // Id to be removed from m_active_objects
objects_to_remove.push_back(id);
}
// Remove references from m_active_objects
- for(std::vector<u16>::iterator i = objects_to_remove.begin();
- i != objects_to_remove.end(); ++i) {
- m_active_objects.erase(*i);
+ for (std::vector<u16>::iterator it = objects_to_remove.begin();
+ it != objects_to_remove.end(); ++it) {
+ m_active_objects.erase(*it);
}
}
@@ -1899,6 +1920,7 @@ void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
// This will also add the object to the active static list
addActiveObjectRaw(obj, false, dtime_s);
}
+
// Clear stored list
block->m_static_objects.m_stored.clear();
// Add leftover failed stuff to stored list
@@ -1909,18 +1931,6 @@ void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
block->m_static_objects.m_stored.push_back(s_obj);
}
- // Turn the active counterparts of activated objects not pending for
- // deactivation
- for(std::map<u16, StaticObject>::iterator
- i = block->m_static_objects.m_active.begin();
- i != block->m_static_objects.m_active.end(); ++i)
- {
- u16 id = i->first;
- ServerActiveObject *object = getActiveObject(id);
- assert(object);
- object->m_pending_deactivation = false;
- }
-
/*
Note: Block hasn't really been modified here.
The objects have just been activated and moved from the stored
@@ -1957,8 +1967,8 @@ void ServerEnvironment::deactivateFarObjects(bool _force_delete)
if(!force_delete && !obj->isStaticAllowed())
continue;
- // If pending deactivation, let removeRemovedObjects() do it
- if(!force_delete && obj->m_pending_deactivation)
+ // removeRemovedObjects() is responsible for these
+ if(!force_delete && obj->isGone())
continue;
u16 id = i->first;
@@ -1975,47 +1985,25 @@ void ServerEnvironment::deactivateFarObjects(bool _force_delete)
!m_active_blocks.contains(obj->m_static_block) &&
m_active_blocks.contains(blockpos_o))
{
- v3s16 old_static_block = obj->m_static_block;
+ // Delete from block where object was located
+ deleteStaticFromBlock(obj, id, MOD_REASON_STATIC_DATA_REMOVED, false);
- // Save to block where object is located
- MapBlock *block = m_map->emergeBlock(blockpos_o, false);
- if(!block){
- errorstream<<"ServerEnvironment::deactivateFarObjects(): "
- <<"Could not save object id="<<id
- <<" to it's current block "<<PP(blockpos_o)
- <<std::endl;
- continue;
- }
std::string staticdata_new = "";
obj->getStaticData(&staticdata_new);
StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
- block->m_static_objects.insert(id, s_obj);
- obj->m_static_block = blockpos_o;
- block->raiseModified(MOD_STATE_WRITE_NEEDED,
- MOD_REASON_STATIC_DATA_ADDED);
+ // Save to block where object is located
+ saveStaticToBlock(blockpos_o, id, obj, s_obj, MOD_REASON_STATIC_DATA_ADDED);
- // Delete from block where object was located
- block = m_map->emergeBlock(old_static_block, false);
- if(!block){
- errorstream<<"ServerEnvironment::deactivateFarObjects(): "
- <<"Could not delete object id="<<id
- <<" from it's previous block "<<PP(old_static_block)
- <<std::endl;
- continue;
- }
- block->m_static_objects.remove(id);
- block->raiseModified(MOD_STATE_WRITE_NEEDED,
- MOD_REASON_STATIC_DATA_REMOVED);
continue;
}
- // If block is active, don't remove
+ // If block is still active, don't remove
if(!force_delete && m_active_blocks.contains(blockpos_o))
continue;
- verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
- <<"deactivating object id="<<id<<" on inactive block "
- <<PP(blockpos_o)<<std::endl;
+ verbosestream << "ServerEnvironment::deactivateFarObjects(): "
+ << "deactivating object id=" << id << " on inactive block "
+ << PP(blockpos_o) << std::endl;
// If known by some client, don't immediately delete.
bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
@@ -2023,7 +2011,6 @@ void ServerEnvironment::deactivateFarObjects(bool _force_delete)
/*
Update the static data
*/
-
if(obj->isStaticAllowed())
{
// Create new static object
@@ -2034,6 +2021,7 @@ void ServerEnvironment::deactivateFarObjects(bool _force_delete)
bool stays_in_same_block = false;
bool data_changed = true;
+ // Check if static data has changed considerably
if (obj->m_static_exists) {
if (obj->m_static_block == blockpos_o)
stays_in_same_block = true;
@@ -2052,88 +2040,29 @@ void ServerEnvironment::deactivateFarObjects(bool _force_delete)
(static_old.pos - objectpos).getLength() < save_movem)
data_changed = false;
} else {
- errorstream<<"ServerEnvironment::deactivateFarObjects(): "
- <<"id="<<id<<" m_static_exists=true but "
- <<"static data doesn't actually exist in "
- <<PP(obj->m_static_block)<<std::endl;
+ warningstream << "ServerEnvironment::deactivateFarObjects(): "
+ << "id=" << id << " m_static_exists=true but "
+ << "static data doesn't actually exist in "
+ << PP(obj->m_static_block) << std::endl;
}
}
}
+ /*
+ While changes are always saved, blocks are only marked as modified
+ if the object has moved or different staticdata. (see above)
+ */
bool shall_be_written = (!stays_in_same_block || data_changed);
+ u32 reason = shall_be_written ? MOD_REASON_STATIC_DATA_CHANGED : MOD_REASON_UNKNOWN;
// Delete old static object
- if(obj->m_static_exists)
- {
- MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
- if(block)
- {
- block->m_static_objects.remove(id);
- obj->m_static_exists = false;
- // Only mark block as modified if data changed considerably
- if(shall_be_written)
- block->raiseModified(MOD_STATE_WRITE_NEEDED,
- MOD_REASON_STATIC_DATA_CHANGED);
- }
- }
+ deleteStaticFromBlock(obj, id, reason, false);
// Add to the block where the object is located in
v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
- // Get or generate the block
- MapBlock *block = NULL;
- try{
- block = m_map->emergeBlock(blockpos);
- } catch(InvalidPositionException &e){
- // Handled via NULL pointer
- // NOTE: emergeBlock's failure is usually determined by it
- // actually returning NULL
- }
-
- if(block)
- {
- if (block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")) {
- warningstream << "ServerEnv: Trying to store id = " << obj->getId()
- << " statically but block " << PP(blockpos)
- << " already contains "
- << block->m_static_objects.m_stored.size()
- << " objects."
- << " Forcing delete." << std::endl;
- force_delete = true;
- } else {
- // If static counterpart already exists in target block,
- // remove it first.
- // This shouldn't happen because the object is removed from
- // the previous block before this according to
- // obj->m_static_block, but happens rarely for some unknown
- // reason. Unsuccessful attempts have been made to find
- // said reason.
- if(id && block->m_static_objects.m_active.find(id) != block->m_static_objects.m_active.end()){
- warningstream<<"ServerEnv: Performing hack #83274"
- <<std::endl;
- block->m_static_objects.remove(id);
- }
- // Store static data
- u16 store_id = pending_delete ? id : 0;
- block->m_static_objects.insert(store_id, s_obj);
-
- // Only mark block as modified if data changed considerably
- if(shall_be_written)
- block->raiseModified(MOD_STATE_WRITE_NEEDED,
- MOD_REASON_STATIC_DATA_CHANGED);
-
- obj->m_static_exists = true;
- obj->m_static_block = block->getPos();
- }
- }
- else{
- if(!force_delete){
- v3s16 p = floatToInt(objectpos, BS);
- errorstream<<"ServerEnv: Could not find or generate "
- <<"a block for storing id="<<obj->getId()
- <<" statically (pos="<<PP(p)<<")"<<std::endl;
- continue;
- }
- }
+ u16 store_id = pending_delete ? id : 0;
+ if (!saveStaticToBlock(blockpos, store_id, obj, s_obj, reason))
+ force_delete = true;
}
/*
@@ -2143,17 +2072,16 @@ void ServerEnvironment::deactivateFarObjects(bool _force_delete)
if(pending_delete && !force_delete)
{
- verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
- <<"object id="<<id<<" is known by clients"
- <<"; not deleting yet"<<std::endl;
+ verbosestream << "ServerEnvironment::deactivateFarObjects(): "
+ << "object id=" << id << " is known by clients"
+ << "; not deleting yet" << std::endl;
obj->m_pending_deactivation = true;
continue;
}
-
- verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
- <<"object id="<<id<<" is not known by clients"
- <<"; deleting"<<std::endl;
+ verbosestream << "ServerEnvironment::deactivateFarObjects(): "
+ << "object id=" << id << " is not known by clients"
+ << "; deleting" << std::endl;
// Tell the object about removal
obj->removingFromEnvironment();
@@ -2168,8 +2096,179 @@ void ServerEnvironment::deactivateFarObjects(bool _force_delete)
}
// Remove references from m_active_objects
- for(std::vector<u16>::iterator i = objects_to_remove.begin();
- i != objects_to_remove.end(); ++i) {
- m_active_objects.erase(*i);
+ for (std::vector<u16>::iterator it = objects_to_remove.begin();
+ it != objects_to_remove.end(); ++it) {
+ m_active_objects.erase(*it);
+ }
+}
+
+void ServerEnvironment::deleteStaticFromBlock(
+ ServerActiveObject *obj, u16 id, u32 mod_reason, bool no_emerge)
+{
+ if (!obj->m_static_exists)
+ return;
+
+ MapBlock *block;
+ if (no_emerge)
+ block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
+ else
+ block = m_map->emergeBlock(obj->m_static_block, false);
+ if (!block) {
+ if (!no_emerge)
+ errorstream << "ServerEnv: Failed to emerge block " << PP(obj->m_static_block)
+ << " when deleting static data of object from it. id=" << id << std::endl;
+ return;
+ }
+
+ block->m_static_objects.remove(id);
+ if (mod_reason != MOD_REASON_UNKNOWN) // Do not mark as modified if requested
+ block->raiseModified(MOD_STATE_WRITE_NEEDED, mod_reason);
+
+ obj->m_static_exists = false;
+}
+
+bool ServerEnvironment::saveStaticToBlock(
+ v3s16 blockpos, u16 store_id,
+ ServerActiveObject *obj, const StaticObject &s_obj,
+ u32 mod_reason)
+{
+ MapBlock *block = NULL;
+ try {
+ block = m_map->emergeBlock(blockpos);
+ } catch (InvalidPositionException &e) {
+ // Handled via NULL pointer
+ // NOTE: emergeBlock's failure is usually determined by it
+ // actually returning NULL
+ }
+
+ if (!block) {
+ errorstream << "ServerEnv: Failed to emerge block " << PP(obj->m_static_block)
+ << " when saving static data of object to it. id=" << store_id << std::endl;
+ return false;
}
+ if (block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")) {
+ warningstream << "ServerEnv: Trying to store id = " << store_id
+ << " statically but block " << PP(blockpos)
+ << " already contains "
+ << block->m_static_objects.m_stored.size()
+ << " objects." << std::endl;
+ return false;
+ }
+
+ block->m_static_objects.insert(store_id, s_obj);
+ if (mod_reason != MOD_REASON_UNKNOWN) // Do not mark as modified if requested
+ block->raiseModified(MOD_STATE_WRITE_NEEDED, mod_reason);
+
+ obj->m_static_exists = true;
+ obj->m_static_block = blockpos;
+
+ return true;
+}
+
+PlayerDatabase *ServerEnvironment::openPlayerDatabase(const std::string &name,
+ const std::string &savedir, const Settings &conf)
+{
+
+ if (name == "sqlite3")
+ return new PlayerDatabaseSQLite3(savedir);
+ else if (name == "dummy")
+ return new Database_Dummy();
+#if USE_POSTGRESQL
+ else if (name == "postgresql") {
+ std::string connect_string = "";
+ conf.getNoEx("pgsql_player_connection", connect_string);
+ return new PlayerDatabasePostgreSQL(connect_string);
+ }
+#endif
+ else if (name == "files")
+ return new PlayerDatabaseFiles(savedir + DIR_DELIM + "players");
+ else
+ throw BaseException(std::string("Database backend ") + name + " not supported.");
+}
+
+bool ServerEnvironment::migratePlayersDatabase(const GameParams &game_params,
+ const Settings &cmd_args)
+{
+ std::string migrate_to = cmd_args.get("migrate-players");
+ Settings world_mt;
+ std::string world_mt_path = game_params.world_path + DIR_DELIM + "world.mt";
+ if (!world_mt.readConfigFile(world_mt_path.c_str())) {
+ errorstream << "Cannot read world.mt!" << std::endl;
+ return false;
+ }
+
+ if (!world_mt.exists("player_backend")) {
+ errorstream << "Please specify your current backend in world.mt:"
+ << std::endl
+ << " player_backend = {files|sqlite3|postgresql}"
+ << std::endl;
+ return false;
+ }
+
+ std::string backend = world_mt.get("player_backend");
+ if (backend == migrate_to) {
+ errorstream << "Cannot migrate: new backend is same"
+ << " as the old one" << std::endl;
+ return false;
+ }
+
+ const std::string players_backup_path = game_params.world_path + DIR_DELIM
+ + "players.bak";
+
+ if (backend == "files") {
+ // Create backup directory
+ fs::CreateDir(players_backup_path);
+ }
+
+ try {
+ PlayerDatabase *srcdb = ServerEnvironment::openPlayerDatabase(backend,
+ game_params.world_path, world_mt);
+ PlayerDatabase *dstdb = ServerEnvironment::openPlayerDatabase(migrate_to,
+ game_params.world_path, world_mt);
+
+ std::vector<std::string> player_list;
+ srcdb->listPlayers(player_list);
+ for (std::vector<std::string>::const_iterator it = player_list.begin();
+ it != player_list.end(); ++it) {
+ actionstream << "Migrating player " << it->c_str() << std::endl;
+ RemotePlayer player(it->c_str(), NULL);
+ PlayerSAO playerSAO(NULL, &player, 15000, false);
+
+ srcdb->loadPlayer(&player, &playerSAO);
+
+ playerSAO.finalize(&player, std::set<std::string>());
+ player.setPlayerSAO(&playerSAO);
+
+ dstdb->savePlayer(&player);
+
+ // For files source, move player files to backup dir
+ if (backend == "files") {
+ fs::Rename(
+ game_params.world_path + DIR_DELIM + "players" + DIR_DELIM + (*it),
+ players_backup_path + DIR_DELIM + (*it));
+ }
+ }
+
+ actionstream << "Successfully migrated " << player_list.size() << " players"
+ << std::endl;
+ world_mt.set("player_backend", migrate_to);
+ if (!world_mt.updateConfigFile(world_mt_path.c_str()))
+ errorstream << "Failed to update world.mt!" << std::endl;
+ else
+ actionstream << "world.mt updated" << std::endl;
+
+ // When migration is finished from file backend, remove players directory if empty
+ if (backend == "files") {
+ fs::DeleteSingleFileOrEmptyDirectory(game_params.world_path + DIR_DELIM
+ + "players");
+ }
+
+ delete srcdb;
+ delete dstdb;
+
+ } catch (BaseException &e) {
+ errorstream << "An error occured during migration: " << e.what() << std::endl;
+ return false;
+ }
+ return true;
}
diff --git a/src/serverenvironment.h b/src/serverenvironment.h
index 99110542a..f6c2f17fc 100644
--- a/src/serverenvironment.h
+++ b/src/serverenvironment.h
@@ -27,10 +27,13 @@ with this program; if not, write to the Free Software Foundation, Inc.,
class IGameDef;
class ServerMap;
+struct GameParams;
class RemotePlayer;
+class PlayerDatabase;
class PlayerSAO;
class ServerEnvironment;
class ActiveBlockModifier;
+struct StaticObject;
class ServerActiveObject;
class Server;
class ServerScripting;
@@ -49,11 +52,10 @@ public:
virtual ~ActiveBlockModifier(){};
// Set of contents to trigger on
- virtual std::set<std::string> getTriggerContents()=0;
+ virtual const std::set<std::string> &getTriggerContents() const = 0;
// Set of required neighbors (trigger doesn't happen if none are found)
// Empty = do not check neighbors
- virtual std::set<std::string> getRequiredNeighbors()
- { return std::set<std::string>(); }
+ virtual const std::set<std::string> &getRequiredNeighbors() const = 0;
// Trigger interval in seconds
virtual float getTriggerInterval() = 0;
// Random chance of (1 / return value), 0 is disallowed
@@ -217,9 +219,11 @@ public:
// Save players
void saveLoadedPlayers();
void savePlayer(RemotePlayer *player);
- RemotePlayer *loadPlayer(const std::string &playername, PlayerSAO *sao);
+ PlayerSAO *loadPlayer(RemotePlayer *player, bool *new_player, u16 peer_id,
+ bool is_singleplayer);
void addPlayer(RemotePlayer *player);
void removePlayer(RemotePlayer *player);
+ bool removePlayerFromDatabase(const std::string &name);
/*
Save and load time of day and game timer
@@ -334,8 +338,13 @@ public:
RemotePlayer *getPlayer(const u16 peer_id);
RemotePlayer *getPlayer(const char* name);
+
+ static bool migratePlayersDatabase(const GameParams &game_params,
+ const Settings &cmd_args);
private:
+ static PlayerDatabase *openPlayerDatabase(const std::string &name,
+ const std::string &savedir, const Settings &conf);
/*
Internal ActiveObject interface
-------------------------------------------
@@ -354,7 +363,7 @@ private:
u16 addActiveObjectRaw(ServerActiveObject *object, bool set_changed, u32 dtime_s);
/*
- Remove all objects that satisfy (m_removed && m_known_by_count==0)
+ Remove all objects that satisfy (isGone() && m_known_by_count==0)
*/
void removeRemovedObjects();
@@ -375,6 +384,14 @@ private:
void deactivateFarObjects(bool force_delete);
/*
+ A few helpers used by the three above methods
+ */
+ void deleteStaticFromBlock(
+ ServerActiveObject *obj, u16 id, u32 mod_reason, bool no_emerge);
+ bool saveStaticToBlock(v3s16 blockpos, u16 store_id,
+ ServerActiveObject *obj, const StaticObject &s_obj, u32 mod_reason);
+
+ /*
Member variables
*/
@@ -419,6 +436,8 @@ private:
// peer_ids in here should be unique, except that there may be many 0s
std::vector<RemotePlayer*> m_players;
+ PlayerDatabase *m_player_database;
+
// Particles
IntervalLimiter m_particle_management_interval;
UNORDERED_MAP<u32, float> m_particle_spawners;
diff --git a/src/serverobject.cpp b/src/serverobject.cpp
index 191247829..b3cb85b83 100644
--- a/src/serverobject.cpp
+++ b/src/serverobject.cpp
@@ -25,7 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
ServerActiveObject::ServerActiveObject(ServerEnvironment *env, v3f pos):
ActiveObject(0),
m_known_by_count(0),
- m_removed(false),
+ m_pending_removal(false),
m_pending_deactivation(false),
m_static_exists(false),
m_static_block(1337,1337,1337),
diff --git a/src/serverobject.h b/src/serverobject.h
index 38204980e..d6072e1a3 100644
--- a/src/serverobject.h
+++ b/src/serverobject.h
@@ -210,24 +210,28 @@ public:
it anymore.
- Removal is delayed to preserve the id for the time during which
it could be confused to some other object by some client.
- - This is set to true by the step() method when the object wants
- to be deleted.
- - This can be set to true by anything else too.
+ - This is usually set to true by the step() method when the object wants
+ to be deleted but can be set by anything else too.
*/
- bool m_removed;
+ bool m_pending_removal;
/*
- This is set to true when an object should be removed from the active
- object list but couldn't be removed because the id has to be
- reserved for some client.
+ Same purpose as m_pending_removal but for deactivation.
+ deactvation = save static data in block, remove active object
- The environment checks this periodically. If this is true and also
- m_known_by_count is true, object is deleted from the active object
- list.
+ If this is set alongside with m_pending_removal, removal takes
+ priority.
*/
bool m_pending_deactivation;
/*
+ A getter that unifies the above to answer the question:
+ "Can the environment still interact with this object?"
+ */
+ inline bool isGone() const
+ { return m_pending_removal || m_pending_deactivation; }
+
+ /*
Whether the object's static data has been stored to a block
*/
bool m_static_exists;
diff --git a/src/settings.cpp b/src/settings.cpp
index b4083264e..61f35e911 100644
--- a/src/settings.cpp
+++ b/src/settings.cpp
@@ -362,6 +362,18 @@ const SettingsEntry &Settings::getEntry(const std::string &name) const
}
+const SettingsEntry &Settings::getEntryDefault(const std::string &name) const
+{
+ MutexAutoLock lock(m_mutex);
+
+ SettingEntries::const_iterator n;
+ if ((n = m_defaults.find(name)) == m_defaults.end()) {
+ throw SettingNotFoundException("Setting [" + name + "] not found.");
+ }
+ return n->second;
+}
+
+
Settings *Settings::getGroup(const std::string &name) const
{
const SettingsEntry &entry = getEntry(name);
@@ -380,6 +392,15 @@ const std::string &Settings::get(const std::string &name) const
}
+const std::string &Settings::getDefault(const std::string &name) const
+{
+ const SettingsEntry &entry = getEntryDefault(name);
+ if (entry.is_group)
+ throw SettingNotFoundException("Setting [" + name + "] is a group.");
+ return entry.value;
+}
+
+
bool Settings::getBool(const std::string &name) const
{
return is_yes(get(name));
@@ -568,6 +589,17 @@ bool Settings::getEntryNoEx(const std::string &name, SettingsEntry &val) const
}
+bool Settings::getEntryDefaultNoEx(const std::string &name, SettingsEntry &val) const
+{
+ try {
+ val = getEntryDefault(name);
+ return true;
+ } catch (SettingNotFoundException &e) {
+ return false;
+ }
+}
+
+
bool Settings::getGroupNoEx(const std::string &name, Settings *&val) const
{
try {
@@ -590,6 +622,17 @@ bool Settings::getNoEx(const std::string &name, std::string &val) const
}
+bool Settings::getDefaultNoEx(const std::string &name, std::string &val) const
+{
+ try {
+ val = getDefault(name);
+ return true;
+ } catch (SettingNotFoundException &e) {
+ return false;
+ }
+}
+
+
bool Settings::getFlag(const std::string &name) const
{
try {
diff --git a/src/settings.h b/src/settings.h
index 777d0eff5..a19c15a76 100644
--- a/src/settings.h
+++ b/src/settings.h
@@ -74,24 +74,21 @@ struct ValueSpec {
};
struct SettingsEntry {
- SettingsEntry()
- {
- group = NULL;
- is_group = false;
- }
-
- SettingsEntry(const std::string &value_)
- {
- value = value_;
- group = NULL;
- is_group = false;
- }
-
- SettingsEntry(Settings *group_)
- {
- group = group_;
- is_group = true;
- }
+ SettingsEntry() :
+ group(NULL),
+ is_group(false)
+ {}
+
+ SettingsEntry(const std::string &value_) :
+ value(value_),
+ group(NULL),
+ is_group(false)
+ {}
+
+ SettingsEntry(Settings *group_) :
+ group(group_),
+ is_group(true)
+ {}
std::string value;
Settings *group;
@@ -138,8 +135,10 @@ public:
***********/
const SettingsEntry &getEntry(const std::string &name) const;
+ const SettingsEntry &getEntryDefault(const std::string &name) const;
Settings *getGroup(const std::string &name) const;
const std::string &get(const std::string &name) const;
+ const std::string &getDefault(const std::string &name) const;
bool getBool(const std::string &name) const;
u16 getU16(const std::string &name) const;
s16 getS16(const std::string &name) const;
@@ -168,8 +167,10 @@ public:
***************************************/
bool getEntryNoEx(const std::string &name, SettingsEntry &val) const;
+ bool getEntryDefaultNoEx(const std::string &name, SettingsEntry &val) const;
bool getGroupNoEx(const std::string &name, Settings *&val) const;
bool getNoEx(const std::string &name, std::string &val) const;
+ bool getDefaultNoEx(const std::string &name, std::string &val) const;
bool getFlag(const std::string &name) const;
bool getU16NoEx(const std::string &name, u16 &val) const;
bool getS16NoEx(const std::string &name, s16 &val) const;
diff --git a/src/settings_translation_file.cpp b/src/settings_translation_file.cpp
index 93f2688d7..684b125b9 100644
--- a/src/settings_translation_file.cpp
+++ b/src/settings_translation_file.cpp
@@ -34,9 +34,13 @@ fake_function() {
gettext("Random input");
gettext("Enable random user input (only used for testing).");
gettext("Continuous forward");
- gettext("Continuous forward movement (only used for testing).");
+ gettext("Continuous forward movement, toggled by autoforward key.");
gettext("Enable Joysticks");
gettext("Enable Joysticks");
+ gettext("Joystick ID");
+ gettext("The identifier of the joystick to use");
+ gettext("Joystick Type");
+ gettext("The type of joystick");
gettext("Joystick button repetition interval");
gettext("The time in seconds it takes between repeated events\nwhen holding down a joystick button combination.");
gettext("Joystick frustum sensitivity");
@@ -61,6 +65,8 @@ fake_function() {
gettext("Key for opening the chat window.\nSee http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3");
gettext("Command key");
gettext("Key for opening the chat window to type commands.\nSee http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3");
+ gettext("Command key");
+ gettext("Key for opening the chat window to type local commands.\nSee http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3");
gettext("Console key");
gettext("Key for opening the chat console.\nSee http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3");
gettext("Range select key");
@@ -71,8 +77,18 @@ fake_function() {
gettext("Key for toggling fast mode.\nSee http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3");
gettext("Noclip key");
gettext("Key for toggling noclip mode.\nSee http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3");
- gettext("Autorun key");
- gettext("Key for toggling autorun.\nSee http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3");
+ gettext("Hotbar next key");
+ gettext("Key for selecting the next item in the hotbar.\nSee http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3");
+ gettext("Hotbar previous key");
+ gettext("Key for selecting the previous item in the hotbar.\nSee http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3");
+ gettext("Mute key");
+ gettext("Key for muting the game.\nSee http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3");
+ gettext("Inc. volume key");
+ gettext("Key for increasing the volume.\nSee http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3");
+ gettext("Dec. volume key");
+ gettext("Key for decreasing the volume.\nSee http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3");
+ gettext("Autoforward key");
+ gettext("Key for toggling autoforward.\nSee http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3");
gettext("Cinematic mode key");
gettext("Key for toggling cinematic mode.\nSee http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3");
gettext("Minimap key");
@@ -81,10 +97,14 @@ fake_function() {
gettext("Key for taking screenshots.\nSee http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3");
gettext("Drop item key");
gettext("Key for dropping the currently selected item.\nSee http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3");
+ gettext("View zoom key");
+ gettext("Key to use view zoom when possible.\nSee http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3");
gettext("HUD toggle key");
gettext("Key for toggling the display of the HUD.\nSee http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3");
gettext("Chat toggle key");
gettext("Key for toggling the display of the chat.\nSee http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3");
+ gettext("Large chat console key");
+ gettext("Key for toggling the display of the large chat console.\nSee http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3");
gettext("Fog toggle key");
gettext("Key for toggling the display of the fog.\nSee http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3");
gettext("Camera update toggle key");
@@ -114,6 +134,8 @@ fake_function() {
gettext("Show entity selection boxes");
gettext("Connect to external media server");
gettext("Enable usage of remote media server (if provided by server).\nRemote servers offer a significantly faster way to download media (e.g. textures)\nwhen connecting to the server.");
+ gettext("Client modding");
+ gettext("Enable Lua modding support on client.\nThis support is experimental and API can change.");
gettext("Serverlist URL");
gettext("URL to the server list displayed in the Multiplayer Tab.");
gettext("Serverlist file");
@@ -137,6 +159,8 @@ fake_function() {
gettext("Use 3D cloud look instead of flat.");
gettext("Node highlighting");
gettext("Method used to highlight selected object.");
+ gettext("Digging particles");
+ gettext("Adds particles when digging a node.");
gettext("Filtering");
gettext("Mipmapping");
gettext("Use mip mapping to scale textures. May slightly increase performance.");
@@ -156,7 +180,9 @@ fake_function() {
gettext("Undersampling is similar to using lower screen resolution, but it applies\nto the game world only, keeping the GUI intact.\nIt should give significant performance boost at the cost of less detailed image.");
gettext("Shaders");
gettext("Shaders");
- gettext("Shaders allow advanced visual effects and may increase performance on some video cards.\nThy only work with the OpenGL video backend.");
+ gettext("Shaders allow advanced visual effects and may increase performance on some video cards.\nThis only works with the OpenGL video backend.");
+ gettext("Shader path");
+ gettext("Path to shader directory. If no path is defined, default location will be used.");
gettext("Tone Mapping");
gettext("Filmic tone mapping");
gettext("Enables filmic tone mapping");
@@ -214,7 +240,7 @@ fake_function() {
gettext("Field of view for zoom");
gettext("Field of view while zooming in degrees.\nThis requires the \"zoom\" privilege on the server.");
gettext("Gamma");
- gettext("Adjust the gamma encoding for the light tables. Lower numbers are brighter.\nThis setting is for the client only and is ignored by the server.");
+ gettext("Adjust the gamma encoding for the light tables. Higher numbers are brighter.\nThis setting is for the client only and is ignored by the server.");
gettext("Texture path");
gettext("Path to texture directory. All textures are first searched from here.");
gettext("Video driver");
@@ -223,12 +249,16 @@ fake_function() {
gettext("Height on which clouds are appearing.");
gettext("Cloud radius");
gettext("Radius of cloud area stated in number of 64 node cloud squares.\nValues larger than 26 will start to produce sharp cutoffs at cloud area corners.");
- gettext("View bobbing");
- gettext("Multiplier for view bobbing.\nFor example: 0 for no view bobbing; 1.0 for normal; 2.0 for double.");
- gettext("Fall bobbing");
+ gettext("Enable view bobbing");
+ gettext("Enables view bobbing when walking.");
+ gettext("View bobbing factor");
+ gettext("Enable view bobbing and amount of view bobbing.\nFor example: 0 for no view bobbing; 1.0 for normal; 2.0 for double.");
+ gettext("Fall bobbing factor");
gettext("Multiplier for fall bobbing.\nFor example: 0 for no view bobbing; 1.0 for normal; 2.0 for double.");
gettext("3D mode");
gettext("3D support.\nCurrently supported:\n- none: no 3d output.\n- anaglyph: cyan/magenta color 3d.\n- interlaced: odd/even line based polarisation screen support.\n- topbottom: split screen top/bottom.\n- sidebyside: split screen side by side.\n- pageflip: quadbuffer based 3d.");
+ gettext("Console height");
+ gettext("In-game chat console height, between 0.1 (10%) and 1.0 (100%).");
gettext("Console color");
gettext("In-game chat console background color (R,G,B).");
gettext("Console alpha");
@@ -245,10 +275,14 @@ fake_function() {
gettext("Whether node texture animations should be desynchronized per mapblock.");
gettext("Maximum hotbar width");
gettext("Maximum proportion of current window to be used for hotbar.\nUseful if there's something to be displayed right or left of hotbar.");
+ gettext("HUD scale factor");
+ gettext("Modifies the size of the hudbar elements.");
gettext("Mesh cache");
gettext("Enables caching of facedir rotated meshes.");
gettext("Mapblock mesh generation delay");
gettext("Delay between mesh updates on the client in ms. Increasing this will slow\ndown the rate of mesh updates, thus reducing jitter on slower clients.");
+ gettext("Mapblock mesh generator's MapBlock cache size MB");
+ gettext("Size of the MapBlock cache of the mesh generator. Increasing this will\nincrease the cache hit %, reducing the data being copied from the main\nthread, thus reducing jitter.");
gettext("Minimap");
gettext("Enables minimap.");
gettext("Round minimap");
@@ -261,8 +295,12 @@ fake_function() {
gettext("The strength (darkness) of node ambient-occlusion shading.\nLower is darker, Higher is lighter. The valid range of values for this\nsetting is 0.25 to 4.0 inclusive. If the value is out of range it will be\nset to the nearest valid value.");
gettext("Inventory items animations");
gettext("Enables animation of inventory items.");
+ gettext("Inventory image hack");
+ gettext("Android systems only: Tries to create inventory textures from meshes\nwhen no supported render was found.");
gettext("Fog Start");
gettext("Fraction of the visible distance at which fog starts to be rendered");
+ gettext("Opaque liquids");
+ gettext("Makes all liquids opaque");
gettext("Menus");
gettext("Clouds in menu");
gettext("Use a cloud animation for the main menu background.");
@@ -299,6 +337,8 @@ fake_function() {
gettext("Advanced");
gettext("DPI");
gettext("Adjust dpi configuration to your screen (non X11/Android only) e.g. for 4k screens.");
+ gettext("Enable console window");
+ gettext("Windows systems only: Start Minetest with the command line window in the background.\nContains the same information as the file debug.txt (default name).");
gettext("Sound");
gettext("Sound");
gettext("Volume");
@@ -322,8 +362,8 @@ fake_function() {
gettext("Automaticaly report to the serverlist.");
gettext("Serverlist URL");
gettext("Announce to this serverlist.\nIf you want to announce your ipv6 address, use serverlist_url = v6.servers.minetest.net.");
- gettext("Disable escape sequences");
- gettext("Disable escape sequences, e.g. chat coloring.\nUse this if you want to run a server with pre-0.4.14 clients and you want to disable\nthe escape sequences generated by mods.");
+ gettext("Strip color codes");
+ gettext("Remove color codes from incoming chat messages\nUse this to stop players from being able to use color in their messages");
gettext("Network");
gettext("Server port");
gettext("Network port to listen (UDP).\nThis value will be overridden when starting from the main menu.");
@@ -355,8 +395,12 @@ fake_function() {
gettext("World directory (everything in the world is stored here).\nNot needed if starting from the main menu.");
gettext("Item entity TTL");
gettext("Time in seconds for item entity (dropped items) to live.\nSetting it to -1 disables the feature.");
+ gettext("Status message on connection");
+ gettext("If enabled, show the server status message on player connection.");
gettext("Damage");
gettext("Enable players getting damage and dying.");
+ gettext("Creative");
+ gettext("Enable creative mode for new created maps.");
gettext("Fixed map seed");
gettext("A chosen map seed for a new map, leave empty for random.\nWill be overridden when creating a new world in the main menu.");
gettext("Default password");
@@ -442,6 +486,8 @@ fake_function() {
gettext("Liquid update interval in seconds.");
gettext("block send optimize distance");
gettext("At this distance the server will aggressively optimize which blocks are sent to clients.\nSmall values potentially improve performance a lot, at the expense of visible rendering glitches.\n(some blocks will not be rendered under water and in caves, as well as sometimes on land)\nSetting this to a value greater than max_block_send_distance disables this optimization.\nStated in mapblocks (16 nodes)");
+ gettext("Server side occlusion culling");
+ gettext("If enabled the server will perform map block occlusion culling based on\non the eye position of the player. This can reduce the number of blocks\nsent to the client 50-80%. The client will not longer receive most invisible\nso that the utility of noclip mode is reduced.");
gettext("Mapgen");
gettext("Mapgen name");
gettext("Name of map generator to be used when creating a new world.\nCreating a world in the main menu will override this.");
@@ -450,7 +496,7 @@ fake_function() {
gettext("Max block generate distance");
gettext("From how far blocks are generated for clients, stated in mapblocks (16 nodes).");
gettext("Map generation limit");
- gettext("Where the map generator stops.\nPlease note:\n- Limited to 31000 (setting above has no effect)\n- The map generator works in groups of 80x80x80 nodes (5x5x5 MapBlocks).\n- Those groups have an offset of -32, -32 nodes from the origin.\n- Only groups which are within the map_generation_limit are generated");
+ gettext("Limit of map generation, in nodes, in all 6 directions from (0, 0, 0).\nOnly mapchunks completely within the mapgen limit are generated.\nValue is stored per-world.");
gettext("Mapgen flags");
gettext("Global map generation attributes.\nIn Mapgen v6 the 'decorations' flag controls all decorations except trees\nand junglegrass, in all other mapgens this flag controls all decorations.\nFlags that are not specified in the flag string are not modified from the default.\nFlags starting with 'no' are used to explicitly disable them.");
gettext("Advanced");
@@ -466,109 +512,168 @@ fake_function() {
gettext("Maximum number of blocks to be queued that are to be generated.\nSet to blank for an appropriate amount to be chosen automatically.");
gettext("Number of emerge threads");
gettext("Number of emerge threads to use. Make this field blank, or increase this number\nto use multiple threads. On multiprocessor systems, this will improve mapgen speed greatly\nat the cost of slightly buggy caves.");
- gettext("Mapgen biome heat noise parameters");
- gettext("Noise parameters for biome API temperature, humidity and biome blend.");
- gettext("Mapgen heat blend noise parameters");
- gettext("Mapgen biome humidity noise parameters");
- gettext("Mapgen biome humidity blend noise parameters");
+ gettext("Biome API temperature and humidity noise parameters");
+ gettext("Heat noise");
+ gettext("Temperature variation for biomes.");
+ gettext("Heat blend noise");
+ gettext("Small-scale temperature variation for blending biomes on borders.");
+ gettext("Humidity noise");
+ gettext("Humidity variation for biomes.");
+ gettext("Humidity blend noise");
+ gettext("Small-scale humidity variation for blending biomes on borders.");
gettext("Mapgen v5");
- gettext("Mapgen v5 cave width");
+ gettext("Mapgen v5 specific flags");
+ gettext("Map generation attributes specific to Mapgen v5.\nFlags that are not specified in the flag string are not modified from the default.\nFlags starting with 'no' are used to explicitly disable them.");
+ gettext("Cave width");
gettext("Controls width of tunnels, a smaller value creates wider tunnels.");
- gettext("Mapgen v5 filler depth noise parameters");
- gettext("Mapgen v5 factor noise parameters");
- gettext("Mapgen v5 height noise parameters");
- gettext("Mapgen v5 cave1 noise parameters");
- gettext("Mapgen v5 cave2 noise parameters");
+ gettext("Cavern limit");
+ gettext("Y-level of cavern upper limit.");
+ gettext("Cavern taper");
+ gettext("Y-distance over which caverns expand to full size.");
+ gettext("Cavern threshold");
+ gettext("Defines full size of caverns, smaller values create larger caverns.");
+ gettext("Filler depth noise");
+ gettext("Variation of biome filler depth.");
+ gettext("Factor noise");
+ gettext("Variation of terrain vertical scale.\nWhen noise is < -0.55 terrain is near-flat.");
+ gettext("Height noise");
+ gettext("Y-level of average terrain surface.");
+ gettext("Cave1 noise");
+ gettext("First of 2 3D noises that together define tunnels.");
+ gettext("Cave2 noise");
+ gettext("Second of 2 3D noises that together define tunnels.");
+ gettext("Cavern noise");
+ gettext("3D noise defining giant caverns.");
gettext("Mapgen v6");
- gettext("Mapgen v6 flags");
- gettext("Map generation attributes specific to Mapgen v6.\nWhen snowbiomes are enabled jungles are automatically enabled, the 'jungles' flag is ignored.\nFlags that are not specified in the flag string are not modified from the default.\nFlags starting with 'no' are used to explicitly disable them.");
- gettext("Mapgen v6 desert frequency");
- gettext("Controls size of deserts and beaches in Mapgen v6.\nWhen snowbiomes are enabled 'mgv6_freq_desert' is ignored.");
- gettext("Mapgen v6 beach frequency");
- gettext("Mapgen v6 terrain base noise parameters");
- gettext("Mapgen v6 terrain altitude noise parameters");
- gettext("Mapgen v6 steepness noise parameters");
- gettext("Mapgen v6 height select noise parameters");
- gettext("Mapgen v6 mud noise parameters");
- gettext("Mapgen v6 beach noise parameters");
- gettext("Mapgen v6 biome noise parameters");
- gettext("Mapgen v6 cave noise parameters");
- gettext("Mapgen v6 humidity noise parameters");
- gettext("Mapgen v6 trees noise parameters");
- gettext("Mapgen v6 apple trees noise parameters");
+ gettext("Mapgen v6 specific flags");
+ gettext("Map generation attributes specific to Mapgen v6.\nThe 'snowbiomes' flag enables the new 5 biome system.\nWhen the new biome system is enabled jungles are automatically enabled and\nthe 'jungles' flag is ignored.\nFlags that are not specified in the flag string are not modified from the default.\nFlags starting with 'no' are used to explicitly disable them.");
+ gettext("Desert noise threshold");
+ gettext("Deserts occur when np_biome exceeds this value.\nWhen the new biome system is enabled, this is ignored.");
+ gettext("Beach noise threshold");
+ gettext("Sandy beaches occur when np_beach exceeds this value.");
+ gettext("Terrain base noise");
+ gettext("Y-level of lower terrain and lakebeds.");
+ gettext("Terrain higher noise");
+ gettext("Y-level of higher (cliff-top) terrain.");
+ gettext("Steepness noise");
+ gettext("Varies steepness of cliffs.");
+ gettext("Height select noise");
+ gettext("Defines areas of 'terrain_higher' (cliff-top terrain).");
+ gettext("Mud noise");
+ gettext("Varies depth of biome surface nodes.");
+ gettext("Beach noise");
+ gettext("Defines areas with sandy beaches.");
+ gettext("Biome noise");
+ gettext("Temperature variation for biomes.");
+ gettext("Cave noise");
+ gettext("Variation of number of caves.");
+ gettext("Humidity noise");
+ gettext("Humidity variation for biomes.");
+ gettext("Trees noise");
+ gettext("Defines tree areas and tree density.");
+ gettext("Apple trees noise");
+ gettext("Defines areas where trees have apples.");
gettext("Mapgen v7");
- gettext("Mapgen v7 flags");
+ gettext("Mapgen v7 specific flags");
gettext("Map generation attributes specific to Mapgen v7.\nThe 'ridges' flag enables the rivers.\nFloatlands are currently experimental and subject to change.\nFlags that are not specified in the flag string are not modified from the default.\nFlags starting with 'no' are used to explicitly disable them.");
- gettext("Mapgen v7 cave width");
+ gettext("Cave width");
gettext("Controls width of tunnels, a smaller value creates wider tunnels.");
- gettext("Mapgen v7 floatland mountain density");
+ gettext("Floatland mountain density");
gettext("Controls the density of floatland mountain terrain.\nIs an offset added to the 'np_mountain' noise value.");
- gettext("Mapgen v7 floatland mountain height");
+ gettext("Floatland mountain height");
gettext("Typical maximum height, above and below midpoint, of floatland mountain terrain.");
- gettext("Mapgen v7 floatland level");
+ gettext("Floatland level");
gettext("Y-level of floatland midpoint and lake surface.");
- gettext("Mapgen v7 shadow limit");
+ gettext("Shadow limit");
gettext("Y-level to which floatland shadows extend.");
- gettext("Mapgen v7 terrain base noise parameters");
- gettext("Mapgen v7 terrain altitude noise parameters");
- gettext("Mapgen v7 terrain persistation noise parameters");
- gettext("Mapgen v7 height select noise parameters");
- gettext("Mapgen v7 filler depth noise parameters");
- gettext("Mapgen v7 mount height noise parameters");
- gettext("Mapgen v7 river course noise parameters");
- gettext("Mapgen v7 floatland base terrain noise parameters");
- gettext("Mapgen v7 floatland base terrain height noise parameters");
- gettext("Mapgen v7 mountain noise parameters");
- gettext("Mapgen v7 river channel wall noise parameters");
- gettext("Mapgen v7 cave1 noise parameters");
- gettext("Mapgen v7 cave2 noise parameters");
+ gettext("Cavern limit");
+ gettext("Y-level of cavern upper limit.");
+ gettext("Cavern taper");
+ gettext("Y-distance over which caverns expand to full size.");
+ gettext("Cavern threshold");
+ gettext("Defines full size of caverns, smaller values create larger caverns.");
+ gettext("Terrain base noise");
+ gettext("Y-level of higher (cliff-top) terrain.");
+ gettext("Terrain alt noise");
+ gettext("Y-level of lower terrain and lakebeds.");
+ gettext("Terrain persistence noise");
+ gettext("Varies roughness of terrain.\nDefines the 'persistence' value for terrain_base and terrain_alt noises.");
+ gettext("Height select noise");
+ gettext("Defines areas of higher (cliff-top) terrain and affects steepness of cliffs.");
+ gettext("Filler depth noise");
+ gettext("Variation of biome filler depth.");
+ gettext("Mountain height noise");
+ gettext("Variation of maximum mountain height (in nodes).");
+ gettext("Ridge underwater noise");
+ gettext("Defines large-scale river channel structure.");
+ gettext("Floatland base noise");
+ gettext("Defines areas of floatland smooth terrain.\nSmooth floatlands occur when noise > 0.");
+ gettext("Floatland base height noise");
+ gettext("Variation of hill height and lake depth on floatland smooth terrain.");
+ gettext("Mountain noise");
+ gettext("3D noise defining mountain structure and height.\nAlso defines structure of floatland mountain terrain.");
+ gettext("Ridge noise");
+ gettext("3D noise defining structure of river canyon walls.");
+ gettext("Cavern noise");
+ gettext("3D noise defining giant caverns.");
+ gettext("Cave1 noise");
+ gettext("First of 2 3D noises that together define tunnels.");
+ gettext("Cave2 noise");
+ gettext("Second of 2 3D noises that together define tunnels.");
gettext("Mapgen flat");
- gettext("Mapgen flat flags");
+ gettext("Mapgen flat specific flags");
gettext("Map generation attributes specific to Mapgen flat.\nOccasional lakes and hills can be added to the flat world.\nFlags that are not specified in the flag string are not modified from the default.\nFlags starting with 'no' are used to explicitly disable them.");
- gettext("Mapgen flat ground level");
+ gettext("Ground level");
gettext("Y of flat ground.");
- gettext("Mapgen flat large cave depth");
+ gettext("Large cave depth");
gettext("Y of upper limit of large pseudorandom caves.");
- gettext("Mapgen flat cave width");
+ gettext("Cave width");
gettext("Controls width of tunnels, a smaller value creates wider tunnels.");
- gettext("Mapgen flat lake threshold");
+ gettext("Lake threshold");
gettext("Terrain noise threshold for lakes.\nControls proportion of world area covered by lakes.\nAdjust towards 0.0 for a larger proportion.");
- gettext("Mapgen flat lake steepness");
+ gettext("Lake steepness");
gettext("Controls steepness/depth of lake depressions.");
- gettext("Mapgen flat hill threshold");
+ gettext("Hill threshold");
gettext("Terrain noise threshold for hills.\nControls proportion of world area covered by hills.\nAdjust towards 0.0 for a larger proportion.");
- gettext("Mapgen flat hill steepness");
+ gettext("Hill steepness");
gettext("Controls steepness/height of hills.");
- gettext("Mapgen flat terrain noise parameters");
- gettext("Determines terrain shape.\nThe 3 numbers in brackets control the scale of the\nterrain, the 3 numbers should be identical.");
- gettext("Mapgen flat filler depth noise parameters");
- gettext("Mapgen flat cave1 noise parameters");
- gettext("Mapgen flat cave2 noise parameters");
+ gettext("Terrain noise");
+ gettext("Defines location and terrain of optional hills and lakes.");
+ gettext("Filler depth noise");
+ gettext("Variation of biome filler depth.");
+ gettext("Cave1 noise");
+ gettext("First of 2 3D noises that together define tunnels.");
+ gettext("Cave2 noise");
+ gettext("Second of 2 3D noises that together define tunnels.");
gettext("Mapgen fractal");
- gettext("Mapgen fractal cave width");
+ gettext("Cave width");
gettext("Controls width of tunnels, a smaller value creates wider tunnels.");
- gettext("Mapgen fractal fractal");
+ gettext("Fractal type");
gettext("Choice of 18 fractals from 9 formulas.\n1 = 4D \"Roundy\" mandelbrot set.\n2 = 4D \"Roundy\" julia set.\n3 = 4D \"Squarry\" mandelbrot set.\n4 = 4D \"Squarry\" julia set.\n5 = 4D \"Mandy Cousin\" mandelbrot set.\n6 = 4D \"Mandy Cousin\" julia set.\n7 = 4D \"Variation\" mandelbrot set.\n8 = 4D \"Variation\" julia set.\n9 = 3D \"Mandelbrot/Mandelbar\" mandelbrot set.\n10 = 3D \"Mandelbrot/Mandelbar\" julia set.\n11 = 3D \"Christmas Tree\" mandelbrot set.\n12 = 3D \"Christmas Tree\" julia set.\n13 = 3D \"Mandelbulb\" mandelbrot set.\n14 = 3D \"Mandelbulb\" julia set.\n15 = 3D \"Cosine Mandelbulb\" mandelbrot set.\n16 = 3D \"Cosine Mandelbulb\" julia set.\n17 = 4D \"Mandelbulb\" mandelbrot set.\n18 = 4D \"Mandelbulb\" julia set.");
- gettext("Mapgen fractal iterations");
+ gettext("Iterations");
gettext("Iterations of the recursive function.\nControls the amount of fine detail.");
- gettext("Mapgen fractal scale");
+ gettext("Scale");
gettext("Approximate (X,Y,Z) scale of fractal in nodes.");
- gettext("Mapgen fractal offset");
+ gettext("Offset");
gettext("(X,Y,Z) offset of fractal from world centre in units of 'scale'.\nUsed to move a suitable spawn area of low land close to (0, 0).\nThe default is suitable for mandelbrot sets, it needs to be edited for julia sets.\nRange roughly -2 to 2. Multiply by 'scale' for offset in nodes.");
- gettext("Mapgen fractal slice w");
+ gettext("Slice w");
gettext("W co-ordinate of the generated 3D slice of a 4D fractal.\nDetermines which 3D slice of the 4D shape is generated.\nHas no effect on 3D fractals.\nRange roughly -2 to 2.");
- gettext("Mapgen fractal julia x");
+ gettext("Julia x");
gettext("Julia set only: X component of hypercomplex constant determining julia shape.\nRange roughly -2 to 2.");
- gettext("Mapgen fractal julia y");
+ gettext("Julia y");
gettext("Julia set only: Y component of hypercomplex constant determining julia shape.\nRange roughly -2 to 2.");
- gettext("Mapgen fractal julia z");
+ gettext("Julia z");
gettext("Julia set only: Z component of hypercomplex constant determining julia shape.\nRange roughly -2 to 2.");
- gettext("Mapgen fractal julia w");
+ gettext("Julia w");
gettext("Julia set only: W component of hypercomplex constant determining julia shape.\nHas no effect on 3D fractals.\nRange roughly -2 to 2.");
- gettext("Mapgen fractal seabed noise parameters");
- gettext("Mapgen fractal filler depth noise parameters");
- gettext("Mapgen fractal cave1 noise parameters");
- gettext("Mapgen fractal cave2 noise parameters");
+ gettext("Seabed noise");
+ gettext("Y-level of seabed.");
+ gettext("Filler depth noise");
+ gettext("Variation of biome filler depth.");
+ gettext("Cave1 noise");
+ gettext("First of 2 3D noises that together define tunnels.");
+ gettext("Cave2 noise");
+ gettext("Second of 2 3D noises that together define tunnels.");
gettext("Mapgen Valleys");
gettext("General");
gettext("Valleys C Flags");
@@ -624,7 +729,7 @@ fake_function() {
gettext("Default report format");
gettext("The default format in which profiles are being saved,\nwhen calling `/profiler save [format]` without format.");
gettext("Report path");
- gettext("The file path relative to your worldpath in which profiles will be saved to.\n");
+ gettext("The file path relative to your worldpath in which profiles will be saved to.");
gettext("Instrumentation");
gettext("Entity methods");
gettext("Instrument the methods of entities on registration.");
diff --git a/src/shader.cpp b/src/shader.cpp
index 79485025b..e525aa0aa 100644
--- a/src/shader.cpp
+++ b/src/shader.cpp
@@ -340,7 +340,7 @@ IWritableShaderSource* createShaderSource(IrrlichtDevice *device)
/*
Generate shader given the shader name.
*/
-ShaderInfo generate_shader(std::string name,
+ShaderInfo generate_shader(const std::string &name,
u8 material_type, u8 drawtype,
IrrlichtDevice *device, std::vector<ShaderCallback *> &callbacks,
const std::vector<IShaderConstantSetterFactory*> &setter_factories,
@@ -525,7 +525,7 @@ void ShaderSource::rebuildShaders()
}
-ShaderInfo generate_shader(std::string name, u8 material_type, u8 drawtype,
+ShaderInfo generate_shader(const std::string &name, u8 material_type, u8 drawtype,
IrrlichtDevice *device, std::vector<ShaderCallback *> &callbacks,
const std::vector<IShaderConstantSetterFactory*> &setter_factories,
SourceShaderCache *sourcecache)
@@ -535,24 +535,19 @@ ShaderInfo generate_shader(std::string name, u8 material_type, u8 drawtype,
shaderinfo.material_type = material_type;
shaderinfo.drawtype = drawtype;
shaderinfo.material = video::EMT_SOLID;
- switch(material_type){
- case TILE_MATERIAL_BASIC:
- shaderinfo.base_material = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
- break;
- case TILE_MATERIAL_ALPHA:
- shaderinfo.base_material = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
- break;
- case TILE_MATERIAL_LIQUID_TRANSPARENT:
- shaderinfo.base_material = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
- break;
- case TILE_MATERIAL_LIQUID_OPAQUE:
- shaderinfo.base_material = video::EMT_SOLID;
- break;
- case TILE_MATERIAL_WAVING_LEAVES:
- shaderinfo.base_material = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
- break;
- case TILE_MATERIAL_WAVING_PLANTS:
- shaderinfo.base_material = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
+ switch (material_type) {
+ case TILE_MATERIAL_OPAQUE:
+ case TILE_MATERIAL_LIQUID_OPAQUE:
+ shaderinfo.base_material = video::EMT_SOLID;
+ break;
+ case TILE_MATERIAL_ALPHA:
+ case TILE_MATERIAL_LIQUID_TRANSPARENT:
+ shaderinfo.base_material = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
+ break;
+ case TILE_MATERIAL_BASIC:
+ case TILE_MATERIAL_WAVING_LEAVES:
+ case TILE_MATERIAL_WAVING_PLANTS:
+ shaderinfo.base_material = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
break;
}
@@ -646,10 +641,11 @@ ShaderInfo generate_shader(std::string name, u8 material_type, u8 drawtype,
"TILE_MATERIAL_LIQUID_TRANSPARENT",
"TILE_MATERIAL_LIQUID_OPAQUE",
"TILE_MATERIAL_WAVING_LEAVES",
- "TILE_MATERIAL_WAVING_PLANTS"
+ "TILE_MATERIAL_WAVING_PLANTS",
+ "TILE_MATERIAL_OPAQUE"
};
- for (int i = 0; i < 6; i++){
+ for (int i = 0; i < 7; i++){
shaders_header += "#define ";
shaders_header += materialTypes[i];
shaders_header += " ";
diff --git a/src/sky.cpp b/src/sky.cpp
index 211a2dcdc..b739fe1ad 100644
--- a/src/sky.cpp
+++ b/src/sky.cpp
@@ -85,6 +85,8 @@ Sky::Sky(scene::ISceneNode* parent, scene::ISceneManager* mgr, s32 id,
}
m_directional_colored_fog = g_settings->getBool("directional_colored_fog");
+
+ m_clouds_enabled = true;
}
@@ -534,8 +536,10 @@ void Sky::update(float time_of_day, float time_brightness,
video::SColorf skycolor_bright_dawn_f = video::SColor(255, 180, 186, 250);
video::SColorf skycolor_bright_night_f = video::SColor(255, 0, 107, 255);
- video::SColorf cloudcolor_bright_normal_f = video::SColor(255, 240, 240, 255);
- video::SColorf cloudcolor_bright_dawn_f = video::SColor(255, 255, 223, 191);
+ // pure white: becomes "diffuse light component" for clouds
+ video::SColorf cloudcolor_bright_normal_f = video::SColor(255, 255, 255, 255);
+ // dawn-factoring version of pure white (note: R is above 1.0)
+ video::SColorf cloudcolor_bright_dawn_f(255.0f/240.0f, 223.0f/240.0f, 191.0f/255.0f);
float cloud_color_change_fraction = 0.95;
if (sunlight_seen) {
@@ -605,7 +609,7 @@ void Sky::update(float time_of_day, float time_brightness,
);
// Horizon coloring based on sun and moon direction during sunset and sunrise
- video::SColor pointcolor = video::SColor(255, 255, 255, m_bgcolor.getAlpha());
+ video::SColor pointcolor = video::SColor(m_bgcolor.getAlpha(), 255, 255, 255);
if (m_directional_colored_fog) {
if (m_horizon_blend() != 0) {
// Calculate hemisphere value from yaw, (inverted in third person front view)
diff --git a/src/sky.h b/src/sky.h
index 72cb2d581..a014a920b 100644
--- a/src/sky.h
+++ b/src/sky.h
@@ -65,10 +65,12 @@ public:
return m_visible ? m_skycolor : m_fallback_bg_color;
}
- bool getCloudsVisible() { return m_clouds_visible && m_visible; }
+ bool getCloudsVisible() { return m_clouds_visible && m_clouds_enabled; }
const video::SColorf &getCloudColor() { return m_cloudcolor_f; }
void setVisible(bool visible) { m_visible = visible; }
+ // Set only from set_sky API
+ void setCloudsEnabled(bool clouds_enabled) { m_clouds_enabled = clouds_enabled; }
void setFallbackBgColor(const video::SColor &fallback_bg_color)
{
m_fallback_bg_color = fallback_bg_color;
@@ -123,7 +125,8 @@ private:
bool m_sunlight_seen;
float m_brightness;
float m_cloud_brightness;
- bool m_clouds_visible;
+ bool m_clouds_visible; // Whether clouds are disabled due to player underground
+ bool m_clouds_enabled; // Initialised to true, reset only by set_sky API
bool m_directional_colored_fog;
video::SColorf m_bgcolor_bright_f;
video::SColorf m_skycolor_bright_f;
diff --git a/src/sound.h b/src/sound.h
index ba2d629d2..76c0d1be4 100644
--- a/src/sound.h
+++ b/src/sound.h
@@ -34,20 +34,22 @@ public:
struct SimpleSoundSpec
{
- std::string name;
- float gain;
- SimpleSoundSpec(std::string name = "", float gain = 1.0) : name(name), gain(gain)
+ SimpleSoundSpec(const std::string &name = "", float gain = 1.0, float fade = 0.0)
+ : name(name), gain(gain), fade(fade)
{
}
- bool exists() { return name != ""; }
- // Serialization intentionally left out
+
+ bool exists() const { return name != ""; }
+
+ std::string name;
+ float gain;
+ float fade;
};
class ISoundManager
{
public:
virtual ~ISoundManager() {}
-
// Multiple sounds can be loaded per name; when played, the sound
// should be chosen randomly from alternatives
// Return value determines success/failure
@@ -61,16 +63,21 @@ public:
// playSound functions return -1 on failure, otherwise a handle to the
// sound. If name=="", call should be ignored without error.
- virtual int playSound(const std::string &name, bool loop, float volume) = 0;
+ virtual int playSound(const std::string &name, bool loop, float volume,
+ float fade = 0) = 0;
virtual int playSoundAt(
const std::string &name, bool loop, float volume, v3f pos) = 0;
virtual void stopSound(int sound) = 0;
virtual bool soundExists(int sound) = 0;
virtual void updateSoundPosition(int sound, v3f pos) = 0;
+ virtual bool updateSoundGain(int id, float gain) = 0;
+ virtual float getSoundGain(int id) = 0;
+ virtual void step(float dtime) = 0;
+ virtual void fadeSound(int sound, float step, float gain) = 0;
int playSound(const SimpleSoundSpec &spec, bool loop)
{
- return playSound(spec.name, loop, spec.gain);
+ return playSound(spec.name, loop, spec.gain, spec.fade);
}
int playSoundAt(const SimpleSoundSpec &spec, bool loop, v3f pos)
{
@@ -91,7 +98,10 @@ public:
}
void updateListener(v3f pos, v3f vel, v3f at, v3f up) {}
void setListenerGain(float gain) {}
- int playSound(const std::string &name, bool loop, float volume) { return 0; }
+ int playSound(const std::string &name, bool loop, float volume, float fade)
+ {
+ return 0;
+ }
int playSoundAt(const std::string &name, bool loop, float volume, v3f pos)
{
return 0;
@@ -99,6 +109,10 @@ public:
void stopSound(int sound) {}
bool soundExists(int sound) { return false; }
void updateSoundPosition(int sound, v3f pos) {}
+ bool updateSoundGain(int id, float gain) { return false; }
+ float getSoundGain(int id) { return 0; }
+ void step(float dtime) {}
+ void fadeSound(int sound, float step, float gain) {}
};
// Global DummySoundManager singleton
diff --git a/src/sound_openal.cpp b/src/sound_openal.cpp
index b9af9e3a9..e56de3178 100644
--- a/src/sound_openal.cpp
+++ b/src/sound_openal.cpp
@@ -36,6 +36,7 @@ with this program; ifnot, write to the Free Software Foundation, Inc.,
#include <AL/alc.h>
#include <AL/alext.h>
#endif
+#include <cmath>
#include <vorbis/vorbisfile.h>
#include <assert.h>
#include "log.h"
@@ -274,6 +275,19 @@ private:
UNORDERED_MAP<std::string, std::vector<SoundBuffer*> > m_buffers;
UNORDERED_MAP<int, PlayingSound*> m_sounds_playing;
v3f m_listener_pos;
+ struct FadeState {
+ FadeState() {}
+ FadeState(float step, float current_gain, float target_gain):
+ step(step),
+ current_gain(current_gain),
+ target_gain(target_gain) {}
+ float step;
+ float current_gain;
+ float target_gain;
+ };
+
+ UNORDERED_MAP<int, FadeState> m_sounds_fading;
+ float m_fade_delay;
public:
bool m_is_initialized;
OpenALSoundManager(OnDemandSoundFetcher *fetcher):
@@ -281,6 +295,7 @@ public:
m_device(NULL),
m_context(NULL),
m_next_id(1),
+ m_fade_delay(0),
m_is_initialized(false)
{
ALCenum error = ALC_NO_ERROR;
@@ -317,7 +332,7 @@ public:
return;
}
- alDistanceModel(AL_INVERSE_DISTANCE);
+ alDistanceModel(AL_INVERSE_DISTANCE_CLAMPED);
infostream<<"Audio: Initialized: OpenAL "<<alGetString(AL_VERSION)
<<", using "<<alcGetString(m_device, ALC_DEVICE_SPECIFIER)
@@ -349,6 +364,11 @@ public:
infostream<<"Audio: Deinitialized."<<std::endl;
}
+ void step(float dtime)
+ {
+ doFades(dtime);
+ }
+
void addBuffer(const std::string &name, SoundBuffer *buf)
{
UNORDERED_MAP<std::string, std::vector<SoundBuffer*> >::iterator i =
@@ -388,7 +408,7 @@ public:
alSource3f(sound->source_id, AL_POSITION, 0, 0, 0);
alSource3f(sound->source_id, AL_VELOCITY, 0, 0, 0);
alSourcei(sound->source_id, AL_LOOPING, loop ? AL_TRUE : AL_FALSE);
- volume = MYMAX(0.0, volume);
+ volume = MYMAX(0.0f, volume);
alSourcef(sound->source_id, AL_GAIN, volume);
alSourcePlay(sound->source_id);
warn_if_error(alGetError(), "createPlayingSound");
@@ -409,9 +429,14 @@ public:
alSourcei(sound->source_id, AL_SOURCE_RELATIVE, false);
alSource3f(sound->source_id, AL_POSITION, pos.X, pos.Y, pos.Z);
alSource3f(sound->source_id, AL_VELOCITY, 0, 0, 0);
- alSourcef(sound->source_id, AL_REFERENCE_DISTANCE, 30.0);
+ // Use alDistanceModel(AL_INVERSE_DISTANCE_CLAMPED) and set reference
+ // distance to clamp gain at <1 node distance, to avoid excessive
+ // volume when closer
+ alSourcef(sound->source_id, AL_REFERENCE_DISTANCE, 10.0f);
alSourcei(sound->source_id, AL_LOOPING, loop ? AL_TRUE : AL_FALSE);
- volume = MYMAX(0.0, volume);
+ // Multiply by 3 to compensate for reducing AL_REFERENCE_DISTANCE from
+ // the previous value of 30 to the new value of 10
+ volume = MYMAX(0.0f, volume * 3.0f);
alSourcef(sound->source_id, AL_GAIN, volume);
alSourcePlay(sound->source_id);
warn_if_error(alGetError(), "createPlayingSoundAt");
@@ -515,6 +540,7 @@ public:
addBuffer(name, buf);
return false;
}
+
bool loadSoundData(const std::string &name,
const std::string &filedata)
{
@@ -541,7 +567,7 @@ public:
alListenerf(AL_GAIN, gain);
}
- int playSound(const std::string &name, bool loop, float volume)
+ int playSound(const std::string &name, bool loop, float volume, float fade)
{
maintain();
if(name == "")
@@ -552,8 +578,16 @@ public:
<<std::endl;
return -1;
}
- return playSoundRaw(buf, loop, volume);
+ int handle = -1;
+ if (fade > 0) {
+ handle = playSoundRaw(buf, loop, 0);
+ fadeSound(handle, fade, volume);
+ } else {
+ handle = playSoundRaw(buf, loop, volume);
+ }
+ return handle;
}
+
int playSoundAt(const std::string &name, bool loop, float volume, v3f pos)
{
maintain();
@@ -567,16 +601,55 @@ public:
}
return playSoundRawAt(buf, loop, volume, pos);
}
+
void stopSound(int sound)
{
maintain();
deleteSound(sound);
}
+
+ void fadeSound(int soundid, float step, float gain)
+ {
+ m_sounds_fading[soundid] = FadeState(step, getSoundGain(soundid), gain);
+ }
+
+ void doFades(float dtime)
+ {
+ m_fade_delay += dtime;
+
+ if (m_fade_delay < 0.1f)
+ return;
+
+ float chkGain = 0;
+ for (UNORDERED_MAP<int, FadeState>::iterator i = m_sounds_fading.begin();
+ i != m_sounds_fading.end();) {
+ if (i->second.step < 0.f)
+ chkGain = -(i->second.current_gain);
+ else
+ chkGain = i->second.current_gain;
+
+ if (chkGain < i->second.target_gain) {
+ i->second.current_gain += (i->second.step * m_fade_delay);
+ i->second.current_gain = rangelim(i->second.current_gain, 0, 1);
+
+ updateSoundGain(i->first, i->second.current_gain);
+ ++i;
+ } else {
+ if (i->second.target_gain <= 0.f)
+ stopSound(i->first);
+
+ m_sounds_fading.erase(i++);
+ }
+ }
+ m_fade_delay = 0;
+ }
+
bool soundExists(int sound)
{
maintain();
return (m_sounds_playing.count(sound) != 0);
}
+
void updateSoundPosition(int id, v3f pos)
{
UNORDERED_MAP<int, PlayingSound*>::iterator i = m_sounds_playing.find(id);
@@ -589,6 +662,29 @@ public:
alSource3f(sound->source_id, AL_VELOCITY, 0, 0, 0);
alSourcef(sound->source_id, AL_REFERENCE_DISTANCE, 30.0);
}
+
+ bool updateSoundGain(int id, float gain)
+ {
+ UNORDERED_MAP<int, PlayingSound*>::iterator i = m_sounds_playing.find(id);
+ if (i == m_sounds_playing.end())
+ return false;
+
+ PlayingSound *sound = i->second;
+ alSourcef(sound->source_id, AL_GAIN, gain);
+ return true;
+ }
+
+ float getSoundGain(int id)
+ {
+ UNORDERED_MAP<int, PlayingSound*>::iterator i = m_sounds_playing.find(id);
+ if (i == m_sounds_playing.end())
+ return 0;
+
+ PlayingSound *sound = i->second;
+ ALfloat gain;
+ alGetSourcef(sound->source_id, AL_GAIN, &gain);
+ return gain;
+ }
};
ISoundManager *createOpenALSoundManager(OnDemandSoundFetcher *fetcher)
diff --git a/src/threading/event.cpp b/src/threading/event.cpp
index 0d5928f10..a22c6628b 100644
--- a/src/threading/event.cpp
+++ b/src/threading/event.cpp
@@ -35,6 +35,8 @@ Event::Event()
pthread_mutex_init(&mutex, NULL);
notified = false;
# endif
+#elif USE_CPP11_MUTEX
+ notified = false;
#endif
}
diff --git a/src/threading/thread.cpp b/src/threading/thread.cpp
index 1909da61d..e3dde24cd 100644
--- a/src/threading/thread.cpp
+++ b/src/threading/thread.cpp
@@ -103,8 +103,8 @@ Thread::~Thread()
kill();
// Make sure start finished mutex is unlocked before it's destroyed
- m_start_finished_mutex.try_lock();
- m_start_finished_mutex.unlock();
+ if (m_start_finished_mutex.try_lock())
+ m_start_finished_mutex.unlock();
}
@@ -267,6 +267,10 @@ DWORD WINAPI Thread::threadProc(LPVOID param)
thr->m_retval = thr->run();
thr->m_running = false;
+ // Unlock m_start_finished_mutex to prevent data race condition on Windows.
+ // On Windows with VS2017 build TerminateThread is called and this mutex is not
+ // released. We try to unlock it from caller thread and it's refused by system.
+ thr->m_start_finished_mutex.unlock();
g_logger.deregisterThread();
// 0 is returned here to avoid an unnecessary ifdef clause
diff --git a/src/tool.cpp b/src/tool.cpp
index 1877a1cf8..bb884938c 100644
--- a/src/tool.cpp
+++ b/src/tool.cpp
@@ -98,7 +98,7 @@ DigParams getDigParams(const ItemGroupList &groups,
return DigParams(true, 0.5, 0, "dig_immediate");
case 3:
//infostream<<"dig_immediate=3"<<std::endl;
- return DigParams(true, 0.0, 0, "dig_immediate");
+ return DigParams(true, 0, 0, "dig_immediate");
default:
break;
}
diff --git a/src/tool.h b/src/tool.h
index ebba5b749..f33152355 100644
--- a/src/tool.h
+++ b/src/tool.h
@@ -63,8 +63,8 @@ struct ToolCapabilities
ToolCapabilities(
float full_punch_interval_=1.4,
int max_drop_level_=1,
- ToolGCMap groupcaps_=ToolGCMap(),
- DamageGroup damageGroups_=DamageGroup()
+ const ToolGCMap &groupcaps_ = ToolGCMap(),
+ const DamageGroup &damageGroups_ = DamageGroup()
):
full_punch_interval(full_punch_interval_),
max_drop_level(max_drop_level_),
@@ -85,8 +85,8 @@ struct DigParams
u16 wear;
std::string main_group;
- DigParams(bool a_diggable=false, float a_time=0, u16 a_wear=0,
- std::string a_main_group=""):
+ DigParams(bool a_diggable = false, float a_time = 0.0f, u16 a_wear = 0,
+ const std::string &a_main_group = ""):
diggable(a_diggable),
time(a_time),
wear(a_wear),
diff --git a/src/touchscreengui.cpp b/src/touchscreengui.cpp
index 8d210c63a..0139b8c4f 100644
--- a/src/touchscreengui.cpp
+++ b/src/touchscreengui.cpp
@@ -794,7 +794,7 @@ void TouchScreenGUI::translateEvent(const SEvent &event)
if (m_move_id == -1) {
m_move_id = event.TouchInput.ID;
m_move_has_really_moved = false;
- m_move_downtime = getTimeMs();
+ m_move_downtime = porting::getTimeMs();
m_move_downlocation = v2s32(event.TouchInput.X, event.TouchInput.Y);
m_move_sent_as_mouse_event = false;
}
@@ -922,7 +922,7 @@ bool TouchScreenGUI::doubleTapDetection()
m_key_events[1].x = m_move_downlocation.X;
m_key_events[1].y = m_move_downlocation.Y;
- u32 delta = porting::getDeltaMs(m_key_events[0].down_time, getTimeMs());
+ u64 delta = porting::getDeltaMs(m_key_events[0].down_time, porting::getTimeMs());
if (delta > 400)
return false;
@@ -1006,7 +1006,7 @@ void TouchScreenGUI::step(float dtime)
(!m_move_has_really_moved) &&
(!m_move_sent_as_mouse_event)) {
- u32 delta = porting::getDeltaMs(m_move_downtime,getTimeMs());
+ u64 delta = porting::getDeltaMs(m_move_downtime, porting::getTimeMs());
if (delta > MIN_DIG_TIME_MS) {
m_shootline = m_device
diff --git a/src/touchscreengui.h b/src/touchscreengui.h
index 1308d78ae..1f680ccb2 100644
--- a/src/touchscreengui.h
+++ b/src/touchscreengui.h
@@ -156,6 +156,14 @@ public:
double getPitch() { return m_camera_pitch; }
+ /*!
+ * Returns a line which describes what the player is pointing at.
+ * The starting point and looking direction are significant,
+ * the line should be scaled to match its length to the actual distance
+ * the player can reach.
+ * The line starts at the camera and ends on the camera's far plane.
+ * The coordinates do not contain the camera offset.
+ */
line3d<f32> getShootline() { return m_shootline; }
void step(float dtime);
@@ -180,13 +188,19 @@ private:
double m_camera_yaw_change;
double m_camera_pitch;
+ /*!
+ * A line starting at the camera and pointing towards the
+ * selected object.
+ * The line ends on the camera's far plane.
+ * The coordinates do not contain the camera offset.
+ */
line3d<f32> m_shootline;
rect<s32> m_control_pad_rect;
int m_move_id;
bool m_move_has_really_moved;
- s32 m_move_downtime;
+ s64 m_move_downtime;
bool m_move_sent_as_mouse_event;
v2s32 m_move_downlocation;
diff --git a/src/treegen.cpp b/src/treegen.cpp
index 505954e8e..8bf9619a0 100644
--- a/src/treegen.cpp
+++ b/src/treegen.cpp
@@ -113,7 +113,7 @@ void make_tree(MMVManip &vmanip, v3s16 p0,
// L-System tree LUA spawner
treegen::error spawn_ltree(ServerEnvironment *env, v3s16 p0,
- INodeDefManager *ndef, TreeDef tree_definition)
+ INodeDefManager *ndef, const TreeDef &tree_definition)
{
ServerMap *map = &env->getServerMap();
std::map<v3s16, MapBlock*> modified_blocks;
diff --git a/src/treegen.h b/src/treegen.h
index 4e6f95e67..8777c369c 100644
--- a/src/treegen.h
+++ b/src/treegen.h
@@ -73,7 +73,7 @@ namespace treegen {
TreeDef tree_definition);
// Spawn L-systems tree from LUA
treegen::error spawn_ltree (ServerEnvironment *env, v3s16 p0, INodeDefManager *ndef,
- TreeDef tree_definition);
+ const TreeDef &tree_definition);
// L-System tree gen helper functions
void tree_node_placement(MMVManip &vmanip, v3f p0,
diff --git a/src/unittest/test.cpp b/src/unittest/test.cpp
index 9d223b82d..570807ba7 100644
--- a/src/unittest/test.cpp
+++ b/src/unittest/test.cpp
@@ -229,7 +229,7 @@ bool run_tests()
{
DSTACK(FUNCTION_NAME);
- u32 t1 = porting::getTime(PRECISION_MILLI);
+ u64 t1 = porting::getTimeMs();
TestGameDef gamedef;
g_logger.setLevelSilenced(LL_ERROR, true);
@@ -246,7 +246,7 @@ bool run_tests()
num_total_tests_run += testmods[i]->num_tests_run;
}
- u32 tdiff = porting::getTime(PRECISION_MILLI) - t1;
+ u64 tdiff = porting::getTimeMs() - t1;
g_logger.setLevelSilenced(LL_ERROR, false);
@@ -273,12 +273,12 @@ bool run_tests()
bool TestBase::testModule(IGameDef *gamedef)
{
rawstream << "======== Testing module " << getName() << std::endl;
- u32 t1 = porting::getTime(PRECISION_MILLI);
+ u64 t1 = porting::getTimeMs();
runTests(gamedef);
- u32 tdiff = porting::getTime(PRECISION_MILLI) - t1;
+ u64 tdiff = porting::getTimeMs() - t1;
rawstream << "======== Module " << getName() << " "
<< (num_tests_failed ? "failed" : "passed") << " (" << num_tests_failed
<< " failures / " << num_tests_run << " tests) - " << tdiff
diff --git a/src/unittest/test.h b/src/unittest/test.h
index e60e657cc..bf76e8bb2 100644
--- a/src/unittest/test.h
+++ b/src/unittest/test.h
@@ -33,7 +33,7 @@ class TestFailedException : public std::exception {
// Runs a unit test and reports results
#define TEST(fxn, ...) do { \
- u32 t1 = porting::getTime(PRECISION_MILLI); \
+ u64 t1 = porting::getTimeMs(); \
try { \
fxn(__VA_ARGS__); \
rawstream << "[PASS] "; \
@@ -46,7 +46,7 @@ class TestFailedException : public std::exception {
num_tests_failed++; \
} \
num_tests_run++; \
- u32 tdiff = porting::getTime(PRECISION_MILLI) - t1; \
+ u64 tdiff = porting::getTimeMs() - t1; \
rawstream << #fxn << " - " << tdiff << "ms" << std::endl; \
} while (0)
diff --git a/src/unittest/test_connection.cpp b/src/unittest/test_connection.cpp
index 49e412fc8..d63322d69 100644
--- a/src/unittest/test_connection.cpp
+++ b/src/unittest/test_connection.cpp
@@ -305,7 +305,7 @@ void TestConnection::testConnectSendReceive()
u16 peer_id = 132;
u16 size = 0;
bool received = false;
- u32 timems0 = porting::getTimeMs();
+ u64 timems0 = porting::getTimeMs();
for (;;) {
if (porting::getTimeMs() - timems0 > 5000 || received)
break;
diff --git a/src/unittest/test_mapnode.cpp b/src/unittest/test_mapnode.cpp
index 9ecc2f82d..70e7d42cf 100644
--- a/src/unittest/test_mapnode.cpp
+++ b/src/unittest/test_mapnode.cpp
@@ -23,7 +23,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "nodedef.h"
#include "content_mapnode.h"
-class TestMapNode : public TestBase {
+class TestMapNode : public TestBase
+{
public:
TestMapNode() { TestManager::registerTestModule(this); }
const char *getName() { return "TestMapNode"; }
diff --git a/src/unittest/test_nodedef.cpp b/src/unittest/test_nodedef.cpp
index 85093f52f..cb99ae854 100644
--- a/src/unittest/test_nodedef.cpp
+++ b/src/unittest/test_nodedef.cpp
@@ -25,7 +25,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "nodedef.h"
#include "network/networkprotocol.h"
-class TestNodeDef : public TestBase {
+class TestNodeDef : public TestBase
+{
public:
TestNodeDef() { TestManager::registerTestModule(this); }
const char *getName() { return "TestNodeDef"; }
@@ -55,7 +56,7 @@ void TestNodeDef::testContentFeaturesSerialization()
std::ostringstream os(std::ios::binary);
f.serialize(os, LATEST_PROTOCOL_VERSION);
- //verbosestream<<"Test ContentFeatures size: "<<os.str().size()<<std::endl;
+ // verbosestream<<"Test ContentFeatures size: "<<os.str().size()<<std::endl;
std::istringstream is(os.str(), std::ios::binary);
ContentFeatures f2;
diff --git a/src/unittest/test_noderesolver.cpp b/src/unittest/test_noderesolver.cpp
index 55acece6a..b009f5d2e 100644
--- a/src/unittest/test_noderesolver.cpp
+++ b/src/unittest/test_noderesolver.cpp
@@ -24,6 +24,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "gamedef.h"
#include "nodedef.h"
+#include <algorithm>
+
class TestNodeResolver : public TestBase {
public:
diff --git a/src/unittest/test_objdef.cpp b/src/unittest/test_objdef.cpp
index df2633b38..c2acdcfe7 100644
--- a/src/unittest/test_objdef.cpp
+++ b/src/unittest/test_objdef.cpp
@@ -22,7 +22,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "exceptions.h"
#include "objdef.h"
-class TestObjDef : public TestBase {
+class TestObjDef : public TestBase
+{
public:
TestObjDef() { TestManager::registerTestModule(this); }
const char *getName() { return "TestObjDef"; }
@@ -60,7 +61,6 @@ void TestObjDef::testHandles()
UASSERTEQ(ObjDefHandle, OBJDEF_ORE, type);
}
-
void TestObjDef::testAddGetSetClear()
{
ObjDefManager testmgr(NULL, OBJDEF_GENERIC);
diff --git a/src/unittest/test_player.cpp b/src/unittest/test_player.cpp
index 655ee08fd..e2b1cd855 100644
--- a/src/unittest/test_player.cpp
+++ b/src/unittest/test_player.cpp
@@ -24,65 +24,17 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "content_sao.h"
#include "server.h"
-class TestPlayer : public TestBase {
+class TestPlayer : public TestBase
+{
public:
TestPlayer() { TestManager::registerTestModule(this); }
const char *getName() { return "TestPlayer"; }
void runTests(IGameDef *gamedef);
-
- void testSave(IGameDef *gamedef);
- void testLoad(IGameDef *gamedef);
};
static TestPlayer g_test_instance;
void TestPlayer::runTests(IGameDef *gamedef)
{
- TEST(testSave, gamedef);
- TEST(testLoad, gamedef);
-}
-
-void TestPlayer::testSave(IGameDef *gamedef)
-{
- RemotePlayer rplayer("testplayer_save", gamedef->idef());
- PlayerSAO sao(NULL, 1, false);
- sao.initialize(&rplayer, std::set<std::string>());
- rplayer.setPlayerSAO(&sao);
- sao.setBreath(10, false);
- sao.setHPRaw(8);
- sao.setYaw(0.1f);
- sao.setPitch(0.6f);
- sao.setBasePosition(v3f(450.2f, -15.7f, 68.1f));
- rplayer.save(".", gamedef);
- UASSERT(fs::PathExists("testplayer_save"));
-}
-
-void TestPlayer::testLoad(IGameDef *gamedef)
-{
- RemotePlayer rplayer("testplayer_load", gamedef->idef());
- PlayerSAO sao(NULL, 1, false);
- sao.initialize(&rplayer, std::set<std::string>());
- rplayer.setPlayerSAO(&sao);
- sao.setBreath(10, false);
- sao.setHPRaw(8);
- sao.setYaw(0.1f);
- sao.setPitch(0.6f);
- sao.setBasePosition(v3f(450.2f, -15.7f, 68.1f));
- rplayer.save(".", gamedef);
- UASSERT(fs::PathExists("testplayer_load"));
-
- RemotePlayer rplayer_load("testplayer_load", gamedef->idef());
- PlayerSAO sao_load(NULL, 2, false);
- std::ifstream is("testplayer_load", std::ios_base::binary);
- UASSERT(is.good());
- rplayer_load.deSerialize(is, "testplayer_load", &sao_load);
- is.close();
-
- UASSERT(strcmp(rplayer_load.getName(), "testplayer_load") == 0);
- UASSERT(sao_load.getBreath() == 10);
- UASSERT(sao_load.getHP() == 8);
- UASSERT(sao_load.getYaw() == 0.1f);
- UASSERT(sao_load.getPitch() == 0.6f);
- UASSERT(sao_load.getBasePosition() == v3f(450.2f, -15.7f, 68.1f));
}
diff --git a/src/unittest/test_profiler.cpp b/src/unittest/test_profiler.cpp
index fbc03f232..92d336a72 100644
--- a/src/unittest/test_profiler.cpp
+++ b/src/unittest/test_profiler.cpp
@@ -21,7 +21,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "profiler.h"
-class TestProfiler : public TestBase {
+class TestProfiler : public TestBase
+{
public:
TestProfiler() { TestManager::registerTestModule(this); }
const char *getName() { return "TestProfiler"; }
diff --git a/src/unittest/test_serialization.cpp b/src/unittest/test_serialization.cpp
index 4cbc999ea..cfcfec0cd 100644
--- a/src/unittest/test_serialization.cpp
+++ b/src/unittest/test_serialization.cpp
@@ -295,7 +295,7 @@ void TestSerialization::testStreamRead()
UASSERT(readU8(is) == 0x11);
UASSERT(readU16(is) == 0x2233);
UASSERT(readU32(is) == 0x44556677);
- UASSERT(readU64(is) == 0x8899AABBCCDDEEFF);
+ UASSERT(readU64(is) == 0x8899AABBCCDDEEFFLL);
UASSERT(readS8(is) == -128);
UASSERT(readS16(is) == 30000);
@@ -336,7 +336,7 @@ void TestSerialization::testStreamWrite()
writeU8(os, 0x11);
writeU16(os, 0x2233);
writeU32(os, 0x44556677);
- writeU64(os, 0x8899AABBCCDDEEFF);
+ writeU64(os, 0x8899AABBCCDDEEFFLL);
writeS8(os, -128);
writeS16(os, 30000);
@@ -382,7 +382,7 @@ void TestSerialization::testVecPut()
putU8(&buf, 0x11);
putU16(&buf, 0x2233);
putU32(&buf, 0x44556677);
- putU64(&buf, 0x8899AABBCCDDEEFF);
+ putU64(&buf, 0x8899AABBCCDDEEFFLL);
putS8(&buf, -128);
putS16(&buf, 30000);
@@ -464,7 +464,7 @@ void TestSerialization::testBufReader()
UASSERT(buf.getU8() == 0x11);
UASSERT(buf.getU16() == 0x2233);
UASSERT(buf.getU32() == 0x44556677);
- UASSERT(buf.getU64() == 0x8899AABBCCDDEEFF);
+ UASSERT(buf.getU64() == 0x8899AABBCCDDEEFFLL);
UASSERT(buf.getS8() == -128);
UASSERT(buf.getS16() == 30000);
UASSERT(buf.getS32() == -6);
@@ -576,7 +576,7 @@ void TestSerialization::testBufReader()
UASSERT(u8_data == 0x11);
UASSERT(u16_data == 0x2233);
UASSERT(u32_data == 0x44556677);
- UASSERT(u64_data == 0x8899AABBCCDDEEFF);
+ UASSERT(u64_data == 0x8899AABBCCDDEEFFLL);
UASSERT(s8_data == -128);
UASSERT(s16_data == 30000);
UASSERT(s32_data == -6);
diff --git a/src/util/basic_macros.h b/src/util/basic_macros.h
index bd4b890eb..687d7cf85 100644
--- a/src/util/basic_macros.h
+++ b/src/util/basic_macros.h
@@ -20,14 +20,13 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#ifndef BASICMACROS_HEADER
#define BASICMACROS_HEADER
-#include <algorithm>
-
#define ARRLEN(x) (sizeof(x) / sizeof((x)[0]))
#define MYMIN(a, b) ((a) < (b) ? (a) : (b))
#define MYMAX(a, b) ((a) > (b) ? (a) : (b))
+// Requires <algorithm>
#define CONTAINS(c, v) (std::find((c).begin(), (c).end(), (v)) != (c).end())
// To disable copy constructors and assignment operations for some class
diff --git a/src/util/cpp11.h b/src/util/cpp11.h
index 14913cb86..17903f2e3 100644
--- a/src/util/cpp11.h
+++ b/src/util/cpp11.h
@@ -20,7 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#ifndef MT_CPP11_HEADER
#define MT_CPP11_HEADER
-#if __cplusplus < 201103L || _MSC_VER < 1600
+#if __cplusplus < 201103L || (defined(_MSC_VER) && _MSC_VER < 1600)
#define USE_CPP11_FAKE_KEYWORD
#endif
diff --git a/src/util/cpp11_container.h b/src/util/cpp11_container.h
index 0194385fc..a09154071 100644
--- a/src/util/cpp11_container.h
+++ b/src/util/cpp11_container.h
@@ -24,7 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#define USE_UNORDERED_CONTAINERS
#endif
-#if _MSC_VER >= 1600
+#if defined(_MSC_VER) && _MSC_VER >= 1600
#define USE_UNORDERED_CONTAINERS
#endif
diff --git a/src/util/numeric.cpp b/src/util/numeric.cpp
index 87f1040ea..e6a9cb75e 100644
--- a/src/util/numeric.cpp
+++ b/src/util/numeric.cpp
@@ -24,100 +24,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "../noise.h" // PseudoRandom, PcgRandom
#include "../threading/mutex_auto_lock.h"
#include <string.h>
-#include <iostream>
-UNORDERED_MAP<u16, std::vector<v3s16> > FacePositionCache::m_cache;
-Mutex FacePositionCache::m_cache_mutex;
-// Calculate the borders of a "d-radius" cube
-// TODO: Make it work without mutex and data races, probably thread-local
-std::vector<v3s16> FacePositionCache::getFacePositions(u16 d)
-{
- MutexAutoLock cachelock(m_cache_mutex);
- if (m_cache.find(d) != m_cache.end())
- return m_cache[d];
-
- generateFacePosition(d);
- return m_cache[d];
-
-}
-
-void FacePositionCache::generateFacePosition(u16 d)
-{
- m_cache[d] = std::vector<v3s16>();
- if(d == 0) {
- m_cache[d].push_back(v3s16(0,0,0));
- return;
- }
- if(d == 1) {
- /*
- This is an optimized sequence of coordinates.
- */
- m_cache[d].push_back(v3s16( 0, 1, 0)); // top
- m_cache[d].push_back(v3s16( 0, 0, 1)); // back
- m_cache[d].push_back(v3s16(-1, 0, 0)); // left
- m_cache[d].push_back(v3s16( 1, 0, 0)); // right
- m_cache[d].push_back(v3s16( 0, 0,-1)); // front
- m_cache[d].push_back(v3s16( 0,-1, 0)); // bottom
- // 6
- m_cache[d].push_back(v3s16(-1, 0, 1)); // back left
- m_cache[d].push_back(v3s16( 1, 0, 1)); // back right
- m_cache[d].push_back(v3s16(-1, 0,-1)); // front left
- m_cache[d].push_back(v3s16( 1, 0,-1)); // front right
- m_cache[d].push_back(v3s16(-1,-1, 0)); // bottom left
- m_cache[d].push_back(v3s16( 1,-1, 0)); // bottom right
- m_cache[d].push_back(v3s16( 0,-1, 1)); // bottom back
- m_cache[d].push_back(v3s16( 0,-1,-1)); // bottom front
- m_cache[d].push_back(v3s16(-1, 1, 0)); // top left
- m_cache[d].push_back(v3s16( 1, 1, 0)); // top right
- m_cache[d].push_back(v3s16( 0, 1, 1)); // top back
- m_cache[d].push_back(v3s16( 0, 1,-1)); // top front
- // 18
- m_cache[d].push_back(v3s16(-1, 1, 1)); // top back-left
- m_cache[d].push_back(v3s16( 1, 1, 1)); // top back-right
- m_cache[d].push_back(v3s16(-1, 1,-1)); // top front-left
- m_cache[d].push_back(v3s16( 1, 1,-1)); // top front-right
- m_cache[d].push_back(v3s16(-1,-1, 1)); // bottom back-left
- m_cache[d].push_back(v3s16( 1,-1, 1)); // bottom back-right
- m_cache[d].push_back(v3s16(-1,-1,-1)); // bottom front-left
- m_cache[d].push_back(v3s16( 1,-1,-1)); // bottom front-right
- // 26
- return;
- }
- // Take blocks in all sides, starting from y=0 and going +-y
- for(s16 y=0; y<=d-1; y++) {
- // Left and right side, including borders
- for(s16 z=-d; z<=d; z++) {
- m_cache[d].push_back(v3s16(d,y,z));
- m_cache[d].push_back(v3s16(-d,y,z));
- if(y != 0) {
- m_cache[d].push_back(v3s16(d,-y,z));
- m_cache[d].push_back(v3s16(-d,-y,z));
- }
- }
- // Back and front side, excluding borders
- for(s16 x=-d+1; x<=d-1; x++) {
- m_cache[d].push_back(v3s16(x,y,d));
- m_cache[d].push_back(v3s16(x,y,-d));
- if(y != 0) {
- m_cache[d].push_back(v3s16(x,-y,d));
- m_cache[d].push_back(v3s16(x,-y,-d));
- }
- }
- }
-
- // Take the bottom and top face with borders
- // -d<x<d, y=+-d, -d<z<d
- for(s16 x=-d; x<=d; x++)
- for(s16 z=-d; z<=d; z++) {
- m_cache[d].push_back(v3s16(x,-d,z));
- m_cache[d].push_back(v3s16(x,d,z));
- }
-}
-
-/*
- myrand
-*/
+// myrand
PcgRandom g_pcgrand;
diff --git a/src/util/numeric.h b/src/util/numeric.h
index 760657171..573f03788 100644
--- a/src/util/numeric.h
+++ b/src/util/numeric.h
@@ -26,28 +26,20 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "../irr_v3d.h"
#include "../irr_aabb3d.h"
#include "../threading/mutex.h"
-#include "cpp11_container.h"
-#include <list>
-#include <vector>
+#define rangelim(d, min, max) ((d) < (min) ? (min) : ((d) > (max) ? (max) : (d)))
+#define myfloor(x) ((x) < 0.0 ? (int)(x) - 1 : (int)(x))
+// The naive swap performs better than the xor version
+#define SWAP(t, x, y) do { \
+ t temp = x; \
+ x = y; \
+ y = temp; \
+} while (0)
-/*
- * This class permits to cache getFacePosition call results
- * This reduces CPU usage and vector calls
- */
-class FacePositionCache
-{
-public:
- static std::vector<v3s16> getFacePositions(u16 d);
-private:
- static void generateFacePosition(u16 d);
- static UNORDERED_MAP<u16, std::vector<v3s16> > m_cache;
- static Mutex m_cache_mutex;
-};
inline s16 getContainerPos(s16 p, s16 d)
{
- return (p>=0 ? p : p-d+1) / d;
+ return (p >= 0 ? p : p - d + 1) / d;
}
inline v2s16 getContainerPos(v2s16 p, s16 d)
@@ -130,16 +122,6 @@ inline bool isInArea(v3s16 p, v3s16 d)
);
}
-#define rangelim(d, min, max) ((d) < (min) ? (min) : ((d)>(max)?(max):(d)))
-#define myfloor(x) ((x) > 0.0 ? (int)(x) : (int)(x) - 1)
-
-// The naive swap performs better than the xor version
-#define SWAP(t, x, y) do { \
- t temp = x; \
- x = y; \
- y = temp; \
-} while (0)
-
inline void sortBoxVerticies(v3s16 &p1, v3s16 &p2) {
if (p1.X > p2.X)
SWAP(s16, p1.X, p2.X);
@@ -266,11 +248,21 @@ inline s32 myround(f32 f)
*/
inline v3s16 floatToInt(v3f p, f32 d)
{
- v3s16 p2(
- (p.X + (p.X>0 ? d/2 : -d/2))/d,
- (p.Y + (p.Y>0 ? d/2 : -d/2))/d,
- (p.Z + (p.Z>0 ? d/2 : -d/2))/d);
- return p2;
+ return v3s16(
+ (p.X + (p.X > 0 ? d / 2 : -d / 2)) / d,
+ (p.Y + (p.Y > 0 ? d / 2 : -d / 2)) / d,
+ (p.Z + (p.Z > 0 ? d / 2 : -d / 2)) / d);
+}
+
+/*
+ Returns integer position of node in given double precision position
+ */
+inline v3s16 doubleToInt(v3d p, double d)
+{
+ return v3s16(
+ (p.X + (p.X > 0 ? d / 2 : -d / 2)) / d,
+ (p.Y + (p.Y > 0 ? d / 2 : -d / 2)) / d,
+ (p.Z + (p.Z > 0 ? d / 2 : -d / 2)) / d);
}
/*
@@ -278,34 +270,31 @@ inline v3s16 floatToInt(v3f p, f32 d)
*/
inline v3f intToFloat(v3s16 p, f32 d)
{
- v3f p2(
+ return v3f(
(f32)p.X * d,
(f32)p.Y * d,
(f32)p.Z * d
);
- return p2;
}
// Random helper. Usually d=BS
inline aabb3f getNodeBox(v3s16 p, float d)
{
return aabb3f(
- (float)p.X * d - 0.5*d,
- (float)p.Y * d - 0.5*d,
- (float)p.Z * d - 0.5*d,
- (float)p.X * d + 0.5*d,
- (float)p.Y * d + 0.5*d,
- (float)p.Z * d + 0.5*d
+ (float)p.X * d - 0.5 * d,
+ (float)p.Y * d - 0.5 * d,
+ (float)p.Z * d - 0.5 * d,
+ (float)p.X * d + 0.5 * d,
+ (float)p.Y * d + 0.5 * d,
+ (float)p.Z * d + 0.5 * d
);
}
+
class IntervalLimiter
{
public:
- IntervalLimiter():
- m_accumulator(0)
- {
- }
+ IntervalLimiter() : m_accumulator(0) {}
/*
dtime: time from last call to this method
wanted_interval: interval wanted
@@ -316,15 +305,17 @@ public:
bool step(float dtime, float wanted_interval)
{
m_accumulator += dtime;
- if(m_accumulator < wanted_interval)
+ if (m_accumulator < wanted_interval)
return false;
m_accumulator -= wanted_interval;
return true;
}
-protected:
+
+private:
float m_accumulator;
};
+
/*
Splits a list into "pages". For example, the list [1,2,3,4,5] split
into two pages would be [1,2,3],[4,5]. This function computes the
@@ -340,29 +331,21 @@ protected:
*/
inline void paging(u32 length, u32 page, u32 pagecount, u32 &minindex, u32 &maxindex)
{
- if(length < 1 || pagecount < 1 || page < 1 || page > pagecount)
- {
+ if (length < 1 || pagecount < 1 || page < 1 || page > pagecount) {
// Special cases or invalid parameters
minindex = maxindex = 0;
- }
- else if(pagecount <= length)
- {
+ } else if(pagecount <= length) {
// Less pages than entries in the list:
// Each page contains at least one entry
minindex = (length * (page-1) + (pagecount-1)) / pagecount;
maxindex = (length * page + (pagecount-1)) / pagecount;
- }
- else
- {
+ } else {
// More pages than entries in the list:
// Make sure the empty pages are at the end
- if(page < length)
- {
+ if (page < length) {
minindex = page-1;
maxindex = page;
- }
- else
- {
+ } else {
minindex = 0;
maxindex = 0;
}
@@ -371,14 +354,14 @@ inline void paging(u32 length, u32 page, u32 pagecount, u32 &minindex, u32 &maxi
inline float cycle_shift(float value, float by = 0, float max = 1)
{
- if (value + by < 0) return max + by + value;
+ if (value + by < 0) return value + by + max;
if (value + by > max) return value + by - max;
return value + by;
}
inline bool is_power_of_two(u32 n)
{
- return n != 0 && (n & (n-1)) == 0;
+ return n != 0 && (n & (n - 1)) == 0;
}
// Compute next-higher power of 2 efficiently, e.g. for power-of-2 texture sizes.
diff --git a/src/util/serialize.cpp b/src/util/serialize.cpp
index 61d369bc4..a1e6790f4 100644
--- a/src/util/serialize.cpp
+++ b/src/util/serialize.cpp
@@ -28,8 +28,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <iomanip>
#include <vector>
-SerializationError eof_ser_err("Attempted read past end of data");
-
////
//// BufReader
////
diff --git a/src/util/serialize.h b/src/util/serialize.h
index e22434191..a864d21ab 100644
--- a/src/util/serialize.h
+++ b/src/util/serialize.h
@@ -429,8 +429,6 @@ bool deSerializeStringToStruct(std::string valstr,
//// BufReader
////
-extern SerializationError eof_ser_err;
-
#define MAKE_BUFREADER_GETNOEX_FXN(T, N, S) \
inline bool get ## N ## NoEx(T *val) \
{ \
@@ -446,7 +444,7 @@ extern SerializationError eof_ser_err;
{ \
T val; \
if (!get ## N ## NoEx(&val)) \
- throw eof_ser_err; \
+ throw SerializationError("Attempted read past end of data"); \
return val; \
}
@@ -504,7 +502,7 @@ public:
inline void getRawData(void *val, size_t len)
{
if (!getRawDataNoEx(val, len))
- throw eof_ser_err;
+ throw SerializationError("Attempted read past end of data");
}
inline size_t remaining()
diff --git a/src/util/srp.cpp b/src/util/srp.cpp
index 430ba1137..f27f4f3f9 100644
--- a/src/util/srp.cpp
+++ b/src/util/srp.cpp
@@ -38,6 +38,7 @@
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
+#include <stdint.h>
#include <config.h>
@@ -417,7 +418,7 @@ static SRP_Result H_nn(
}
static SRP_Result H_ns(mpz_t result, SRP_HashAlgorithm alg, const unsigned char *n,
- size_t len_n, const unsigned char *bytes, size_t len_bytes)
+ size_t len_n, const unsigned char *bytes, uint32_t len_bytes)
{
unsigned char buff[SHA512_DIGEST_LENGTH];
size_t nbytes = len_n + len_bytes;
diff --git a/src/util/string.cpp b/src/util/string.cpp
index 141068512..d41b91f24 100644
--- a/src/util/string.cpp
+++ b/src/util/string.cpp
@@ -25,6 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "hex.h"
#include "../porting.h"
+#include <algorithm>
#include <sstream>
#include <iomanip>
#include <map>
diff --git a/src/util/string.h b/src/util/string.h
index 1a0b9f60d..cc278da13 100644
--- a/src/util/string.h
+++ b/src/util/string.h
@@ -232,7 +232,7 @@ inline std::vector<std::basic_string<T> > str_split(
*/
inline std::string lowercase(const std::string &str)
{
- std::string s2;
+ std::string s2 = "";
s2.reserve(str.size());
@@ -423,6 +423,18 @@ inline void str_replace(std::string &str, const std::string &pattern,
}
/**
+ * Escapes characters [ ] \ , ; that can not be used in formspecs
+ */
+inline void str_formspec_escape(std::string &str)
+{
+ str_replace(str, "\\", "\\\\");
+ str_replace(str, "]", "\\]");
+ str_replace(str, "[", "\\[");
+ str_replace(str, ";", "\\;");
+ str_replace(str, ",", "\\,");
+}
+
+/**
* Replace all occurrences of the character \p from in \p str with \p to.
*
* @param str The string to (potentially) modify.
diff --git a/src/util/thread.h b/src/util/thread.h
index d43e06e0a..b96f302f6 100644
--- a/src/util/thread.h
+++ b/src/util/thread.h
@@ -29,9 +29,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "container.h"
template<typename T>
-class MutexedVariable {
+class MutexedVariable
+{
public:
- MutexedVariable(T value):
+ MutexedVariable(const T &value):
m_value(value)
{}
@@ -41,21 +42,14 @@ public:
return m_value;
}
- void set(T value)
+ void set(const T &value)
{
MutexAutoLock lock(m_mutex);
m_value = value;
}
- // You'll want to grab this in a SharedPtr
- MutexAutoLock *getLock()
- {
- return new MutexAutoLock(m_mutex);
- }
-
// You pretty surely want to grab the lock when accessing this
T m_value;
-
private:
Mutex m_mutex;
};
@@ -89,8 +83,8 @@ public:
GetRequest() {}
~GetRequest() {}
- GetRequest(Key a_key) {
- key = a_key;
+ GetRequest(const Key &a_key): key(a_key)
+ {
}
Key key;
@@ -112,7 +106,7 @@ public:
return m_queue.empty();
}
- void add(Key key, Caller caller, CallerData callerdata,
+ void add(const Key &key, Caller caller, CallerData callerdata,
ResultQueue<Key, T, Caller, CallerData> *dest)
{
typename std::deque<GetRequest<Key, T, Caller, CallerData> >::iterator i;
diff --git a/src/util/timetaker.cpp b/src/util/timetaker.cpp
index dcf07dc0d..ac686c3a3 100644
--- a/src/util/timetaker.cpp
+++ b/src/util/timetaker.cpp
@@ -19,31 +19,26 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "timetaker.h"
-#include "../gettime.h"
+#include "../porting.h"
#include "../log.h"
#include <ostream>
-TimeTaker::TimeTaker(const char *name, u32 *result, TimePrecision prec)
+TimeTaker::TimeTaker(const std::string &name, u64 *result, TimePrecision prec)
{
m_name = name;
m_result = result;
m_running = true;
m_precision = prec;
- m_time1 = getTime(prec);
+ m_time1 = porting::getTime(prec);
}
-u32 TimeTaker::stop(bool quiet)
+u64 TimeTaker::stop(bool quiet)
{
- if(m_running)
- {
- u32 time2 = getTime(m_precision);
- u32 dtime = time2 - m_time1;
- if(m_result != NULL)
- {
+ if (m_running) {
+ u64 dtime = porting::getTime(m_precision) - m_time1;
+ if (m_result != NULL) {
(*m_result) += dtime;
- }
- else
- {
+ } else {
if (!quiet) {
static const char* const units[] = {
"s" /* PRECISION_SECONDS */,
@@ -62,10 +57,8 @@ u32 TimeTaker::stop(bool quiet)
return 0;
}
-u32 TimeTaker::getTimerTime()
+u64 TimeTaker::getTimerTime()
{
- u32 time2 = getTime(m_precision);
- u32 dtime = time2 - m_time1;
- return dtime;
+ return porting::getTime(m_precision) - m_time1;
}
diff --git a/src/util/timetaker.h b/src/util/timetaker.h
index 5512c205f..c10f4f535 100644
--- a/src/util/timetaker.h
+++ b/src/util/timetaker.h
@@ -30,24 +30,24 @@ with this program; if not, write to the Free Software Foundation, Inc.,
class TimeTaker
{
public:
- TimeTaker(const char *name, u32 *result=NULL,
- TimePrecision=PRECISION_MILLI);
+ TimeTaker(const std::string &name, u64 *result=NULL,
+ TimePrecision prec=PRECISION_MILLI);
~TimeTaker()
{
stop();
}
- u32 stop(bool quiet=false);
+ u64 stop(bool quiet=false);
- u32 getTimerTime();
+ u64 getTimerTime();
private:
- const char *m_name;
- u32 m_time1;
+ std::string m_name;
+ u64 m_time1;
bool m_running;
TimePrecision m_precision;
- u32 *m_result;
+ u64 *m_result;
};
#endif
diff --git a/src/voxel.cpp b/src/voxel.cpp
index 87773b240..78efde5bb 100644
--- a/src/voxel.cpp
+++ b/src/voxel.cpp
@@ -27,14 +27,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
/*
Debug stuff
*/
-u32 addarea_time = 0;
-u32 emerge_time = 0;
-u32 emerge_load_time = 0;
-u32 clearflag_time = 0;
-//u32 getwaterpressure_time = 0;
-//u32 spreadwaterpressure_time = 0;
-u32 updateareawaterpressure_time = 0;
-u32 flowwater_pre_time = 0;
+u64 addarea_time = 0;
+u64 emerge_time = 0;
+u64 emerge_load_time = 0;
+u64 clearflag_time = 0;
VoxelManipulator::VoxelManipulator():
diff --git a/src/voxel.h b/src/voxel.h
index 58ad39be4..3a64ccc79 100644
--- a/src/voxel.h
+++ b/src/voxel.h
@@ -49,8 +49,8 @@ class INodeDefManager;
/*
Debug stuff
*/
-extern u32 emerge_time;
-extern u32 emerge_load_time;
+extern u64 emerge_time;
+extern u64 emerge_load_time;
/*
This class resembles aabbox3d<s16> a lot, but has inclusive
diff --git a/src/voxelalgorithms.cpp b/src/voxelalgorithms.cpp
index 411369bd4..40f8595a7 100644
--- a/src/voxelalgorithms.cpp
+++ b/src/voxelalgorithms.cpp
@@ -1094,6 +1094,95 @@ const VoxelArea block_pad[] = {
VoxelArea(v3s16(0, 0, 0), v3s16(0, 15, 15)) //X-
};
+/*!
+ * The common part of bulk light updates - it is always executed.
+ * The procedure takes the nodes that should be unlit, and the
+ * full modified area.
+ *
+ * The procedure handles the correction of all lighting except
+ * direct sunlight spreading.
+ *
+ * \param minblock least coordinates of the changed area in block
+ * coordinates
+ * \param maxblock greatest coordinates of the changed area in block
+ * coordinates
+ * \param unlight the first queue is for day light, the second is for
+ * night light. Contains all nodes on the borders that need to be unlit.
+ * \param relight the first queue is for day light, the second is for
+ * night light. Contains nodes that were not modified, but got sunlight
+ * because the changes.
+ * \param modified_blocks the procedure adds all modified blocks to
+ * this map
+ */
+void finish_bulk_light_update(Map *map, mapblock_v3 minblock,
+ mapblock_v3 maxblock, UnlightQueue unlight[2], ReLightQueue relight[2],
+ std::map<v3s16, MapBlock*> *modified_blocks)
+{
+ INodeDefManager *ndef = map->getNodeDefManager();
+ // dummy boolean
+ bool is_valid;
+
+ // --- STEP 1: Do unlighting
+
+ for (size_t bank = 0; bank < 2; bank++) {
+ LightBank b = banks[bank];
+ unspread_light(map, ndef, b, unlight[bank], relight[bank],
+ *modified_blocks);
+ }
+
+ // --- STEP 2: Get all newly inserted light sources
+
+ // For each block:
+ for (s16 b_x = minblock.X; b_x <= maxblock.X; b_x++)
+ for (s16 b_y = minblock.Y; b_y <= maxblock.Y; b_y++)
+ for (s16 b_z = minblock.Z; b_z <= maxblock.Z; b_z++) {
+ const v3s16 blockpos(b_x, b_y, b_z);
+ MapBlock *block = map->getBlockNoCreateNoEx(blockpos);
+ if (!block || block->isDummy())
+ // Skip not existing blocks
+ continue;
+ // For each node in the block:
+ for (s32 x = 0; x < MAP_BLOCKSIZE; x++)
+ for (s32 z = 0; z < MAP_BLOCKSIZE; z++)
+ for (s32 y = 0; y < MAP_BLOCKSIZE; y++) {
+ v3s16 relpos(x, y, z);
+ MapNode node = block->getNodeNoCheck(x, y, z, &is_valid);
+ const ContentFeatures &f = ndef->get(node);
+ // For each light bank
+ for (size_t b = 0; b < 2; b++) {
+ LightBank bank = banks[b];
+ u8 light = f.param_type == CPT_LIGHT ?
+ node.getLightNoChecks(bank, &f):
+ f.light_source;
+ if (light > 1)
+ relight[b].push(light, relpos, blockpos, block, 6);
+ } // end of banks
+ } // end of nodes
+ } // end of blocks
+
+ // --- STEP 3: do light spreading
+
+ // For each light bank:
+ for (size_t b = 0; b < 2; b++) {
+ LightBank bank = banks[b];
+ // Sunlight is already initialized.
+ u8 maxlight = (b == 0) ? LIGHT_MAX : LIGHT_SUN;
+ // Initialize light values for light spreading.
+ for (u8 i = 0; i <= maxlight; i++) {
+ const std::vector<ChangingLight> &lights = relight[b].lights[i];
+ for (std::vector<ChangingLight>::const_iterator it = lights.begin();
+ it < lights.end(); ++it) {
+ MapNode n = it->block->getNodeNoCheck(it->rel_position,
+ &is_valid);
+ n.setLight(bank, i, ndef);
+ it->block->setNodeNoCheck(it->rel_position, n);
+ }
+ }
+ // Spread lights.
+ spread_light(map, ndef, bank, relight[b], *modified_blocks);
+ }
+}
+
void blit_back_with_light(ServerMap *map, MMVManip *vm,
std::map<v3s16, MapBlock*> *modified_blocks)
{
@@ -1187,30 +1276,107 @@ void blit_back_with_light(ServerMap *map, MMVManip *vm,
vm->blitBackAll(modified_blocks, true);
- // --- STEP 4: Do unlighting
+ // --- STEP 4: Finish light update
- for (size_t bank = 0; bank < 2; bank++) {
- LightBank b = banks[bank];
- unspread_light(map, ndef, b, unlight[bank], relight[bank],
- *modified_blocks);
+ finish_bulk_light_update(map, minblock, maxblock, unlight, relight,
+ modified_blocks);
+}
+
+/*!
+ * Resets the lighting of the given map block to
+ * complete darkness and full sunlight.
+ *
+ * \param light incoming sunlight, light[x][z] is true if there
+ * is sunlight above the map block at the given x-z coordinates.
+ * The array's indices are relative node coordinates in the block.
+ * After the procedure returns, this contains outgoing light at
+ * the bottom of the map block.
+ */
+void fill_with_sunlight(MapBlock *block, INodeDefManager *ndef,
+ bool light[MAP_BLOCKSIZE][MAP_BLOCKSIZE])
+{
+ if (block->isDummy())
+ return;
+ // dummy boolean
+ bool is_valid;
+ // For each column of nodes:
+ for (s16 z = 0; z < MAP_BLOCKSIZE; z++)
+ for (s16 x = 0; x < MAP_BLOCKSIZE; x++) {
+ // True if the current node has sunlight.
+ bool lig = light[z][x];
+ // For each node, downwards:
+ for (s16 y = MAP_BLOCKSIZE - 1; y >= 0; y--) {
+ MapNode n = block->getNodeNoCheck(x, y, z, &is_valid);
+ // Ignore IGNORE nodes, these are not generated yet.
+ if (n.getContent() == CONTENT_IGNORE)
+ continue;
+ const ContentFeatures &f = ndef->get(n.getContent());
+ if (lig && !f.sunlight_propagates) {
+ // Sunlight is stopped.
+ lig = false;
+ }
+ // Reset light
+ n.setLight(LIGHTBANK_DAY, lig ? 15 : 0, f);
+ n.setLight(LIGHTBANK_NIGHT, 0, f);
+ block->setNodeNoCheck(x, y, z, n);
+ }
+ // Output outgoing light.
+ light[z][x] = lig;
}
+}
- // --- STEP 5: Get all newly inserted light sources
+void repair_block_light(ServerMap *map, MapBlock *block,
+ std::map<v3s16, MapBlock*> *modified_blocks)
+{
+ if (!block || block->isDummy())
+ return;
+ INodeDefManager *ndef = map->getNodeDefManager();
+ // First queue is for day light, second is for night light.
+ UnlightQueue unlight[] = { UnlightQueue(256), UnlightQueue(256) };
+ ReLightQueue relight[] = { ReLightQueue(256), ReLightQueue(256) };
+ // Will hold sunlight data.
+ bool lights[MAP_BLOCKSIZE][MAP_BLOCKSIZE];
+ SunlightPropagationData data;
+ // Dummy boolean.
+ bool is_valid;
- // For each block:
- for (s16 b_x = minblock.X; b_x <= maxblock.X; b_x++)
- for (s16 b_y = minblock.Y; b_y <= maxblock.Y; b_y++)
- for (s16 b_z = minblock.Z; b_z <= maxblock.Z; b_z++) {
- v3s16 blockpos(b_x, b_y, b_z);
- MapBlock *block = map->getBlockNoCreateNoEx(blockpos);
- if (!block || block->isDummy())
- // Skip not existing blocks
- continue;
- // For each node in the block:
- for (s32 x = 0; x < MAP_BLOCKSIZE; x++)
- for (s32 z = 0; z < MAP_BLOCKSIZE; z++)
- for (s32 y = 0; y < MAP_BLOCKSIZE; y++) {
+ // --- STEP 1: reset everything to sunlight
+
+ mapblock_v3 blockpos = block->getPos();
+ (*modified_blocks)[blockpos] = block;
+ // For each map block:
+ // Extract sunlight above.
+ is_sunlight_above_block(map, blockpos, ndef, lights);
+ // Reset the voxel manipulator.
+ fill_with_sunlight(block, ndef, lights);
+ // Copy sunlight data
+ data.target_block = v3s16(blockpos.X, blockpos.Y - 1, blockpos.Z);
+ for (s16 z = 0; z < MAP_BLOCKSIZE; z++)
+ for (s16 x = 0; x < MAP_BLOCKSIZE; x++) {
+ data.data.push_back(
+ SunlightPropagationUnit(v2s16(x, z), lights[z][x]));
+ }
+ // Propagate sunlight and shadow below the voxel manipulator.
+ while (!data.data.empty()) {
+ if (propagate_block_sunlight(map, ndef, &data, &unlight[0],
+ &relight[0]))
+ (*modified_blocks)[data.target_block] =
+ map->getBlockNoCreateNoEx(data.target_block);
+ // Step downwards.
+ data.target_block.Y--;
+ }
+
+ // --- STEP 2: Get nodes from borders to unlight
+
+ // For each border of the block:
+ for (direction d = 0; d < 6; d++) {
+ VoxelArea a = block_pad[d];
+ // For each node of the border:
+ for (s32 x = a.MinEdge.X; x <= a.MaxEdge.X; x++)
+ for (s32 z = a.MinEdge.Z; z <= a.MaxEdge.Z; z++)
+ for (s32 y = a.MinEdge.Y; y <= a.MaxEdge.Y; y++) {
v3s16 relpos(x, y, z);
+ // Get node
MapNode node = block->getNodeNoCheck(x, y, z, &is_valid);
const ContentFeatures &f = ndef->get(node);
// For each light bank
@@ -1219,33 +1385,21 @@ void blit_back_with_light(ServerMap *map, MMVManip *vm,
u8 light = f.param_type == CPT_LIGHT ?
node.getLightNoChecks(bank, &f):
f.light_source;
- if (light > 1)
- relight[b].push(light, relpos, blockpos, block, 6);
+ // If the new node is dimmer than sunlight, unlight.
+ // (if it has maximal light, it is pointless to remove
+ // surrounding light, as it can only become brighter)
+ if (LIGHT_SUN > light) {
+ unlight[b].push(
+ LIGHT_SUN, relpos, blockpos, block, 6);
+ }
} // end of banks
} // end of nodes
- } // end of blocks
+ } // end of borders
- // --- STEP 6: do light spreading
+ // STEP 3: Remove and spread light
- // For each light bank:
- for (size_t b = 0; b < 2; b++) {
- LightBank bank = banks[b];
- // Sunlight is already initialized.
- u8 maxlight = (b == 0) ? LIGHT_MAX : LIGHT_SUN;
- // Initialize light values for light spreading.
- for (u8 i = 0; i <= maxlight; i++) {
- const std::vector<ChangingLight> &lights = relight[b].lights[i];
- for (std::vector<ChangingLight>::const_iterator it = lights.begin();
- it < lights.end(); ++it) {
- MapNode n = it->block->getNodeNoCheck(it->rel_position,
- &is_valid);
- n.setLight(bank, i, ndef);
- it->block->setNodeNoCheck(it->rel_position, n);
- }
- }
- // Spread lights.
- spread_light(map, ndef, bank, relight[b], *modified_blocks);
- }
+ finish_bulk_light_update(map, blockpos, blockpos, unlight, relight,
+ modified_blocks);
}
VoxelLineIterator::VoxelLineIterator(
diff --git a/src/voxelalgorithms.h b/src/voxelalgorithms.h
index cdffe86c8..b518979d7 100644
--- a/src/voxelalgorithms.h
+++ b/src/voxelalgorithms.h
@@ -98,6 +98,15 @@ void blit_back_with_light(ServerMap *map, MMVManip *vm,
std::map<v3s16, MapBlock*> *modified_blocks);
/*!
+ * Corrects the light in a map block.
+ * For server use only.
+ *
+ * \param block the block to update
+ */
+void repair_block_light(ServerMap *map, MapBlock *block,
+ std::map<v3s16, MapBlock*> *modified_blocks);
+
+/*!
* This class iterates trough voxels that intersect with
* a line. The collision detection does not see nodeboxes,
* every voxel is a cube and is returned.
diff --git a/src/wieldmesh.cpp b/src/wieldmesh.cpp
index 40af0be5f..7736ec2a2 100644
--- a/src/wieldmesh.cpp
+++ b/src/wieldmesh.cpp
@@ -235,27 +235,16 @@ WieldMeshSceneNode::~WieldMeshSceneNode()
g_extrusion_mesh_cache = NULL;
}
-void WieldMeshSceneNode::setCube(const TileSpec tiles[6],
+void WieldMeshSceneNode::setCube(const ContentFeatures &f,
v3f wield_scale, ITextureSource *tsrc)
{
scene::IMesh *cubemesh = g_extrusion_mesh_cache->createCube();
- changeToMesh(cubemesh);
+ scene::SMesh *copy = cloneMesh(cubemesh);
cubemesh->drop();
-
+ postProcessNodeMesh(copy, f, false, true, &m_material_type, &m_colors);
+ changeToMesh(copy);
+ copy->drop();
m_meshnode->setScale(wield_scale * WIELD_SCALE_FACTOR);
-
- // Customize materials
- for (u32 i = 0; i < m_meshnode->getMaterialCount(); ++i) {
- assert(i < 6);
- video::SMaterial &material = m_meshnode->getMaterial(i);
- if (tiles[i].animation_frame_count == 1) {
- material.setTexture(0, tiles[i].texture);
- } else {
- FrameSpec animation_frame = tiles[i].frames[0];
- material.setTexture(0, animation_frame.texture);
- }
- tiles[i].applyMaterialOptions(material);
- }
}
void WieldMeshSceneNode::setExtruded(const std::string &imagename,
@@ -274,8 +263,10 @@ void WieldMeshSceneNode::setExtruded(const std::string &imagename,
dim = core::dimension2d<u32>(dim.Width, frame_height);
}
scene::IMesh *mesh = g_extrusion_mesh_cache->create(dim);
- changeToMesh(mesh);
+ scene::SMesh *copy = cloneMesh(mesh);
mesh->drop();
+ changeToMesh(copy);
+ copy->drop();
m_meshnode->setScale(wield_scale * WIELD_SCALE_FACTOR_EXTRUDED);
@@ -321,12 +312,12 @@ void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client)
// Color-related
m_colors.clear();
- video::SColor basecolor = idef->getItemstackColor(item, client);
+ m_base_color = idef->getItemstackColor(item, client);
// If wield_image is defined, it overrides everything else
if (def.wield_image != "") {
setExtruded(def.wield_image, def.wield_scale, tsrc, 1);
- m_colors.push_back(basecolor);
+ m_colors.push_back(ItemPartColor());
return;
}
// Handle nodes
@@ -334,66 +325,50 @@ void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client)
else if (def.type == ITEM_NODE) {
if (f.mesh_ptr[0]) {
// e.g. mesh nodes and nodeboxes
- changeToMesh(f.mesh_ptr[0]);
- // mesh_ptr[0] is pre-scaled by BS * f->visual_scale
+ scene::SMesh *mesh = cloneMesh(f.mesh_ptr[0]);
+ postProcessNodeMesh(mesh, f, m_enable_shaders, true,
+ &m_material_type, &m_colors);
+ changeToMesh(mesh);
+ mesh->drop();
+ // mesh is pre-scaled by BS * f->visual_scale
m_meshnode->setScale(
def.wield_scale * WIELD_SCALE_FACTOR
/ (BS * f.visual_scale));
} else if (f.drawtype == NDT_AIRLIKE) {
changeToMesh(NULL);
} else if (f.drawtype == NDT_PLANTLIKE) {
- setExtruded(tsrc->getTextureName(f.tiles[0].texture_id), def.wield_scale, tsrc, f.tiles[0].animation_frame_count);
+ setExtruded(tsrc->getTextureName(f.tiles[0].layers[0].texture_id),
+ def.wield_scale, tsrc,
+ f.tiles[0].layers[0].animation_frame_count);
} else if (f.drawtype == NDT_NORMAL || f.drawtype == NDT_ALLFACES) {
- setCube(f.tiles, def.wield_scale, tsrc);
+ setCube(f, def.wield_scale, tsrc);
} else {
MeshMakeData mesh_make_data(client, false);
MapNode mesh_make_node(id, 255, 0);
mesh_make_data.fillSingleNode(&mesh_make_node);
MapBlockMesh mapblock_mesh(&mesh_make_data, v3s16(0, 0, 0));
- changeToMesh(mapblock_mesh.getMesh());
- translateMesh(m_meshnode->getMesh(), v3f(-BS, -BS, -BS));
+ scene::SMesh *mesh = cloneMesh(mapblock_mesh.getMesh());
+ translateMesh(mesh, v3f(-BS, -BS, -BS));
+ postProcessNodeMesh(mesh, f, m_enable_shaders, true,
+ &m_material_type, &m_colors);
+ changeToMesh(mesh);
+ mesh->drop();
m_meshnode->setScale(
def.wield_scale * WIELD_SCALE_FACTOR
/ (BS * f.visual_scale));
}
u32 material_count = m_meshnode->getMaterialCount();
- if (material_count > 6) {
- errorstream << "WieldMeshSceneNode::setItem: Invalid material "
- "count " << material_count << ", truncating to 6" << std::endl;
- material_count = 6;
- }
for (u32 i = 0; i < material_count; ++i) {
- const TileSpec *tile = &(f.tiles[i]);
video::SMaterial &material = m_meshnode->getMaterial(i);
material.setFlag(video::EMF_BACK_FACE_CULLING, true);
material.setFlag(video::EMF_BILINEAR_FILTER, m_bilinear_filter);
material.setFlag(video::EMF_TRILINEAR_FILTER, m_trilinear_filter);
- bool animated = (tile->animation_frame_count > 1);
- if (animated) {
- FrameSpec animation_frame = tile->frames[0];
- material.setTexture(0, animation_frame.texture);
- } else {
- material.setTexture(0, tile->texture);
- }
- m_colors.push_back(tile->has_color ? tile->color : basecolor);
- material.MaterialType = m_material_type;
- if (m_enable_shaders) {
- if (tile->normal_texture) {
- if (animated) {
- FrameSpec animation_frame = tile->frames[0];
- material.setTexture(1, animation_frame.normal_texture);
- } else {
- material.setTexture(1, tile->normal_texture);
- }
- }
- material.setTexture(2, tile->flags_texture);
- }
}
return;
}
else if (def.inventory_image != "") {
setExtruded(def.inventory_image, def.wield_scale, tsrc, 1);
- m_colors.push_back(basecolor);
+ m_colors.push_back(ItemPartColor());
return;
}
@@ -413,9 +388,9 @@ void WieldMeshSceneNode::setColor(video::SColor c)
u8 blue = c.getBlue();
u32 mc = mesh->getMeshBufferCount();
for (u32 j = 0; j < mc; j++) {
- video::SColor bc(0xFFFFFFFF);
- if (m_colors.size() > j)
- bc = m_colors[j];
+ video::SColor bc(m_base_color);
+ if ((m_colors.size() > j) && (m_colors[j].override_base))
+ bc = m_colors[j].color;
video::SColor buffercolor(255,
bc.getRed() * red / 255,
bc.getGreen() * green / 255,
@@ -439,19 +414,7 @@ void WieldMeshSceneNode::changeToMesh(scene::IMesh *mesh)
m_meshnode->setMesh(dummymesh);
dummymesh->drop(); // m_meshnode grabbed it
} else {
- if (m_lighting) {
- m_meshnode->setMesh(mesh);
- } else {
- /*
- Lighting is disabled, this means the caller can (and probably will)
- call setColor later. We therefore need to clone the mesh so that
- setColor will only modify this scene node's mesh, not others'.
- */
- scene::IMeshManipulator *meshmanip = SceneManager->getMeshManipulator();
- scene::IMesh *new_mesh = meshmanip->createMeshCopy(mesh);
- m_meshnode->setMesh(new_mesh);
- new_mesh->drop(); // m_meshnode grabbed it
- }
+ m_meshnode->setMesh(mesh);
}
m_meshnode->setMaterialFlag(video::EMF_LIGHTING, m_lighting);
@@ -475,24 +438,29 @@ void getItemMesh(Client *client, const ItemStack &item, ItemMesh *result)
g_extrusion_mesh_cache->grab();
}
- scene::IMesh *mesh;
+ scene::SMesh *mesh = NULL;
+
+ // Shading is on by default
+ result->needs_shading = true;
// If inventory_image is defined, it overrides everything else
if (def.inventory_image != "") {
mesh = getExtrudedMesh(tsrc, def.inventory_image);
- result->mesh = mesh;
- result->buffer_colors.push_back(
- std::pair<bool, video::SColor>(false, video::SColor(0xFFFFFFFF)));
+ result->buffer_colors.push_back(ItemPartColor());
+ // Items with inventory images do not need shading
+ result->needs_shading = false;
} else if (def.type == ITEM_NODE) {
if (f.mesh_ptr[0]) {
mesh = cloneMesh(f.mesh_ptr[0]);
scaleMesh(mesh, v3f(0.12, 0.12, 0.12));
} else if (f.drawtype == NDT_PLANTLIKE) {
mesh = getExtrudedMesh(tsrc,
- tsrc->getTextureName(f.tiles[0].texture_id));
+ tsrc->getTextureName(f.tiles[0].layers[0].texture_id));
} else if (f.drawtype == NDT_NORMAL || f.drawtype == NDT_ALLFACES
|| f.drawtype == NDT_LIQUID || f.drawtype == NDT_FLOWINGLIQUID) {
- mesh = cloneMesh(g_extrusion_mesh_cache->createCube());
+ scene::IMesh *cube = g_extrusion_mesh_cache->createCube();
+ mesh = cloneMesh(cube);
+ cube->drop();
scaleMesh(mesh, v3f(1.2, 1.2, 1.2));
} else {
MeshMakeData mesh_make_data(client, false);
@@ -519,32 +487,27 @@ void getItemMesh(Client *client, const ItemStack &item, ItemMesh *result)
u32 mc = mesh->getMeshBufferCount();
for (u32 i = 0; i < mc; ++i) {
- const TileSpec *tile = &(f.tiles[i]);
scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
- result->buffer_colors.push_back(
- std::pair<bool, video::SColor>(tile->has_color, tile->color));
- colorizeMeshBuffer(buf, &tile->color);
video::SMaterial &material = buf->getMaterial();
material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
material.setFlag(video::EMF_BILINEAR_FILTER, false);
material.setFlag(video::EMF_TRILINEAR_FILTER, false);
material.setFlag(video::EMF_BACK_FACE_CULLING, true);
material.setFlag(video::EMF_LIGHTING, false);
- if (tile->animation_frame_count > 1) {
- FrameSpec animation_frame = tile->frames[0];
- material.setTexture(0, animation_frame.texture);
- } else {
- material.setTexture(0, tile->texture);
- }
}
rotateMeshXZby(mesh, -45);
rotateMeshYZby(mesh, -30);
- result->mesh = mesh;
+
+ postProcessNodeMesh(mesh, f, false, false, NULL,
+ &result->buffer_colors);
}
+ result->mesh = mesh;
}
-scene::IMesh * getExtrudedMesh(ITextureSource *tsrc,
+
+
+scene::SMesh * getExtrudedMesh(ITextureSource *tsrc,
const std::string &imagename)
{
video::ITexture *texture = tsrc->getTextureForMesh(imagename);
@@ -553,7 +516,9 @@ scene::IMesh * getExtrudedMesh(ITextureSource *tsrc,
}
core::dimension2d<u32> dim = texture->getSize();
- scene::IMesh *mesh = cloneMesh(g_extrusion_mesh_cache->create(dim));
+ scene::IMesh *original = g_extrusion_mesh_cache->create(dim);
+ scene::SMesh *mesh = cloneMesh(original);
+ original->drop();
// Customize material
video::SMaterial &material = mesh->getMeshBuffer(0)->getMaterial();
@@ -569,3 +534,57 @@ scene::IMesh * getExtrudedMesh(ITextureSource *tsrc,
return mesh;
}
+
+void postProcessNodeMesh(scene::SMesh *mesh, const ContentFeatures &f,
+ bool use_shaders, bool set_material, video::E_MATERIAL_TYPE *mattype,
+ std::vector<ItemPartColor> *colors)
+{
+ u32 mc = mesh->getMeshBufferCount();
+ // Allocate colors for existing buffers
+ colors->clear();
+ for (u32 i = 0; i < mc; ++i)
+ colors->push_back(ItemPartColor());
+
+ for (u32 i = 0; i < mc; ++i) {
+ const TileSpec *tile = &(f.tiles[i]);
+ scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
+ for (int layernum = 0; layernum < MAX_TILE_LAYERS; layernum++) {
+ const TileLayer *layer = &tile->layers[layernum];
+ if (layer->texture_id == 0)
+ continue;
+ if (layernum != 0) {
+ scene::IMeshBuffer *copy = cloneMeshBuffer(buf);
+ copy->getMaterial() = buf->getMaterial();
+ mesh->addMeshBuffer(copy);
+ copy->drop();
+ buf = copy;
+ colors->push_back(
+ ItemPartColor(layer->has_color, layer->color));
+ } else {
+ (*colors)[i] = ItemPartColor(layer->has_color, layer->color);
+ }
+ video::SMaterial &material = buf->getMaterial();
+ if (set_material)
+ layer->applyMaterialOptions(material);
+ if (mattype) {
+ material.MaterialType = *mattype;
+ }
+ if (layer->animation_frame_count > 1) {
+ FrameSpec animation_frame = layer->frames[0];
+ material.setTexture(0, animation_frame.texture);
+ } else {
+ material.setTexture(0, layer->texture);
+ }
+ if (use_shaders) {
+ if (layer->normal_texture) {
+ if (layer->animation_frame_count > 1) {
+ FrameSpec animation_frame = layer->frames[0];
+ material.setTexture(1, animation_frame.normal_texture);
+ } else
+ material.setTexture(1, layer->normal_texture);
+ }
+ material.setTexture(2, layer->flags_texture);
+ }
+ }
+ }
+}
diff --git a/src/wieldmesh.h b/src/wieldmesh.h
index d3946b4e0..faedce484 100644
--- a/src/wieldmesh.h
+++ b/src/wieldmesh.h
@@ -26,19 +26,46 @@ with this program; if not, write to the Free Software Foundation, Inc.,
struct ItemStack;
class Client;
class ITextureSource;
-struct TileSpec;
+struct ContentFeatures;
+
+/*!
+ * Holds color information of an item mesh's buffer.
+ */
+struct ItemPartColor
+{
+ /*!
+ * If this is false, the global base color of the item
+ * will be used instead of the specific color of the
+ * buffer.
+ */
+ bool override_base;
+ /*!
+ * The color of the buffer.
+ */
+ video::SColor color;
+
+ ItemPartColor() : override_base(false), color(0) {}
+
+ ItemPartColor(bool override, video::SColor color)
+ : override_base(override), color(color)
+ {
+ }
+};
struct ItemMesh
{
scene::IMesh *mesh;
/*!
* Stores the color of each mesh buffer.
- * If the boolean is true, the color is fixed, else
- * palettes can modify it.
*/
- std::vector<std::pair<bool, video::SColor> > buffer_colors;
+ std::vector<ItemPartColor> buffer_colors;
+ /*!
+ * If false, all faces of the item should have the same brightness.
+ * Disables shading based on normal vectors.
+ */
+ bool needs_shading;
- ItemMesh() : mesh(NULL), buffer_colors() {}
+ ItemMesh() : mesh(NULL), buffer_colors(), needs_shading(true) {}
};
/*
@@ -51,7 +78,7 @@ public:
s32 id = -1, bool lighting = false);
virtual ~WieldMeshSceneNode();
- void setCube(const TileSpec tiles[6], v3f wield_scale, ITextureSource *tsrc);
+ void setCube(const ContentFeatures &f, v3f wield_scale, ITextureSource *tsrc);
void setExtruded(const std::string &imagename, v3f wield_scale,
ITextureSource *tsrc, u8 num_frames);
void setItem(const ItemStack &item, Client *client);
@@ -84,7 +111,12 @@ private:
* Stores the colors of the mesh's mesh buffers.
* This does not include lighting.
*/
- std::vector<video::SColor> m_colors;
+ std::vector<ItemPartColor> m_colors;
+ /*!
+ * The base color of this mesh. This is the default
+ * for all mesh buffers.
+ */
+ video::SColor m_base_color;
// Bounding box culling is disabled for this type of scene node,
// so this variable is just required so we can implement
@@ -94,5 +126,16 @@ private:
void getItemMesh(Client *client, const ItemStack &item, ItemMesh *result);
-scene::IMesh *getExtrudedMesh(ITextureSource *tsrc, const std::string &imagename);
+scene::SMesh *getExtrudedMesh(ITextureSource *tsrc, const std::string &imagename);
+
+/*!
+ * Applies overlays, textures and optionally materials to the given mesh and
+ * extracts tile colors for colorization.
+ * \param mattype overrides the buffer's material type, but can also
+ * be NULL to leave the original material.
+ * \param colors returns the colors of the mesh buffers in the mesh.
+ */
+void postProcessNodeMesh(scene::SMesh *mesh, const ContentFeatures &f, bool use_shaders,
+ bool set_material, video::E_MATERIAL_TYPE *mattype,
+ std::vector<ItemPartColor> *colors);
#endif