aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorElias Fleckenstein <eliasfleckenstein@web.de>2020-11-28 13:48:33 +0100
committerElias Fleckenstein <eliasfleckenstein@web.de>2020-11-28 13:48:33 +0100
commiteb6aca8b4a67ef55108231e71ff29a18a29bf5ae (patch)
treef891914d25cae2cdaa24392381436a287340651e /src
parent8de51dae97aa2fe6ea02e4cf437bfe2b2a38eb06 (diff)
parentf1d72d212a0661588be27003069abf4bd8092e55 (diff)
downloaddragonfireclient-eb6aca8b4a67ef55108231e71ff29a18a29bf5ae.tar.xz
Merged Minetest
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt35
-rw-r--r--src/activeobject.h4
-rw-r--r--src/chat.cpp13
-rw-r--r--src/chat.h2
-rw-r--r--src/client/camera.cpp2
-rw-r--r--src/client/camera.h2
-rw-r--r--src/client/client.cpp33
-rw-r--r--src/client/client.h2
-rw-r--r--src/client/clientenvironment.cpp130
-rw-r--r--src/client/clientlauncher.cpp6
-rw-r--r--src/client/clientmap.cpp39
-rw-r--r--src/client/clientmedia.cpp3
-rw-r--r--src/client/clouds.cpp60
-rw-r--r--src/client/content_cao.cpp165
-rw-r--r--src/client/content_cao.h12
-rw-r--r--src/client/content_mapblock.cpp7
-rw-r--r--src/client/content_mapblock.h2
-rw-r--r--src/client/event_manager.h2
-rw-r--r--src/client/fontengine.h2
-rw-r--r--src/client/game.cpp264
-rw-r--r--src/client/game.h36
-rw-r--r--src/client/gameui.cpp33
-rw-r--r--src/client/guiscalingfilter.cpp5
-rw-r--r--src/client/guiscalingfilter.h3
-rw-r--r--src/client/hud.cpp168
-rw-r--r--src/client/hud.h10
-rw-r--r--src/client/inputhandler.cpp166
-rw-r--r--src/client/inputhandler.h154
-rw-r--r--src/client/joystick_controller.cpp22
-rw-r--r--src/client/keys.h8
-rw-r--r--src/client/localplayer.cpp2
-rw-r--r--src/client/mapblock_mesh.cpp79
-rw-r--r--src/client/mapblock_mesh.h10
-rw-r--r--src/client/mesh.cpp9
-rw-r--r--src/client/mesh.h7
-rw-r--r--src/client/mesh_generator_thread.cpp6
-rw-r--r--src/client/mesh_generator_thread.h1
-rw-r--r--src/client/minimap.cpp270
-rw-r--r--src/client/minimap.h52
-rw-r--r--src/client/render/core.cpp46
-rw-r--r--src/client/shader.cpp248
-rw-r--r--src/client/sky.cpp225
-rw-r--r--src/client/sky.h15
-rw-r--r--src/client/sound_openal.cpp56
-rw-r--r--src/client/wieldmesh.cpp211
-rw-r--r--src/clientiface.cpp121
-rw-r--r--src/clientiface.h10
-rw-r--r--src/content/CMakeLists.txt1
-rw-r--r--src/content/packages.cpp69
-rw-r--r--src/content/packages.h52
-rw-r--r--src/content/subgames.cpp62
-rw-r--r--src/content/subgames.h8
-rw-r--r--src/content_nodemeta.cpp18
-rw-r--r--src/database/database-dummy.cpp23
-rw-r--r--src/database/database-dummy.h9
-rw-r--r--src/database/database-leveldb.cpp16
-rw-r--r--src/defaultsettings.cpp49
-rw-r--r--src/emerge.cpp4
-rw-r--r--src/emerge.h1
-rw-r--r--src/environment.cpp1
-rw-r--r--src/environment.h1
-rw-r--r--src/filesys.cpp15
-rw-r--r--src/filesys.h2
-rw-r--r--src/gui/CMakeLists.txt1
-rw-r--r--src/gui/StyleSpec.h67
-rw-r--r--src/gui/guiBox.cpp87
-rw-r--r--src/gui/guiBox.h11
-rw-r--r--src/gui/guiButton.cpp21
-rw-r--r--src/gui/guiButton.h1
-rw-r--r--src/gui/guiChatConsole.cpp9
-rw-r--r--src/gui/guiChatConsole.h4
-rw-r--r--src/gui/guiConfirmRegistration.cpp19
-rw-r--r--src/gui/guiEditBoxWithScrollbar.cpp4
-rw-r--r--src/gui/guiEngine.cpp18
-rw-r--r--src/gui/guiEngine.h12
-rw-r--r--src/gui/guiFormSpecMenu.cpp267
-rw-r--r--src/gui/guiFormSpecMenu.h26
-rw-r--r--src/gui/guiPasswordChange.cpp19
-rw-r--r--src/gui/guiScene.cpp257
-rw-r--r--src/gui/guiScene.h85
-rw-r--r--src/gui/guiScrollBar.cpp2
-rw-r--r--src/gui/guiScrollBar.h1
-rw-r--r--src/gui/guiScrollContainer.cpp12
-rw-r--r--src/gui/guiScrollContainer.h2
-rw-r--r--src/gui/guiSkin.cpp2
-rw-r--r--src/gui/guiTable.cpp27
-rw-r--r--src/gui/guiTable.h6
-rw-r--r--src/gui/intlGUIEditBox.cpp4
-rw-r--r--src/httpfetch.cpp66
-rw-r--r--src/httpfetch.h22
-rw-r--r--src/hud.cpp3
-rw-r--r--src/hud.h21
-rw-r--r--src/inventory.cpp23
-rw-r--r--src/inventory.h1
-rw-r--r--src/inventorymanager.cpp303
-rw-r--r--src/inventorymanager.h12
-rw-r--r--src/itemdef.cpp61
-rw-r--r--src/itemdef.h1
-rw-r--r--src/light.cpp7
-rw-r--r--src/log.cpp11
-rw-r--r--src/log.h10
-rw-r--r--src/map.cpp98
-rw-r--r--src/map.h27
-rw-r--r--src/map_settings_manager.cpp16
-rw-r--r--src/mapblock.cpp10
-rw-r--r--src/mapgen/mapgen.cpp36
-rw-r--r--src/mapgen/mapgen.h8
-rw-r--r--src/mapgen/mapgen_carpathian.cpp9
-rw-r--r--src/mapgen/mapgen_flat.cpp73
-rw-r--r--src/mapgen/mapgen_flat.h22
-rw-r--r--src/mapgen/mapgen_fractal.cpp9
-rw-r--r--src/mapgen/mapgen_singlenode.cpp6
-rw-r--r--src/mapgen/mapgen_v5.cpp9
-rw-r--r--src/mapgen/mapgen_v6.cpp15
-rw-r--r--src/mapgen/mapgen_v6.h1
-rw-r--r--src/mapgen/mapgen_v7.cpp88
-rw-r--r--src/mapgen/mapgen_v7.h2
-rw-r--r--src/mapgen/mapgen_valleys.cpp9
-rw-r--r--src/mapgen/mg_ore.cpp6
-rw-r--r--src/mapgen/mg_schematic.cpp4
-rw-r--r--src/mapgen/treegen.cpp13
-rw-r--r--src/mapgen/treegen.h2
-rw-r--r--src/mapsector.h1
-rw-r--r--src/mtevent.h (renamed from src/event.h)0
-rw-r--r--src/nameidmapping.cpp4
-rw-r--r--src/network/CMakeLists.txt5
-rw-r--r--src/network/clientopcodes.cpp1
-rw-r--r--src/network/clientpackethandler.cpp59
-rw-r--r--src/network/connection.cpp5
-rw-r--r--src/network/connection.h11
-rw-r--r--src/network/connectionthreads.cpp31
-rw-r--r--src/network/networkpacket.cpp7
-rw-r--r--src/network/networkpacket.h1
-rw-r--r--src/network/networkprotocol.h14
-rw-r--r--src/network/serveropcodes.cpp1
-rw-r--r--src/network/serverpackethandler.cpp510
-rw-r--r--src/nodedef.cpp127
-rw-r--r--src/nodedef.h34
-rw-r--r--src/nodemetadata.cpp8
-rw-r--r--src/noise.cpp2
-rw-r--r--src/noise.h7
-rw-r--r--src/object_properties.cpp36
-rw-r--r--src/object_properties.h1
-rw-r--r--src/particles.cpp4
-rw-r--r--src/player.h12
-rw-r--r--src/porting.cpp12
-rw-r--r--src/porting.h2
-rw-r--r--src/script/common/c_content.cpp16
-rw-r--r--src/script/common/c_content.h3
-rw-r--r--src/script/common/c_converter.cpp45
-rw-r--r--src/script/common/c_converter.h2
-rw-r--r--src/script/common/c_internal.cpp20
-rw-r--r--src/script/common/c_internal.h20
-rw-r--r--src/script/common/helper.cpp44
-rw-r--r--src/script/common/helper.h5
-rw-r--r--src/script/cpp_api/s_security.cpp4
-rw-r--r--src/script/lua_api/l_base.cpp60
-rw-r--r--src/script/lua_api/l_base.h21
-rw-r--r--src/script/lua_api/l_camera.cpp3
-rw-r--r--src/script/lua_api/l_env.cpp52
-rw-r--r--src/script/lua_api/l_env.h5
-rw-r--r--src/script/lua_api/l_http.cpp29
-rw-r--r--src/script/lua_api/l_http.h4
-rw-r--r--src/script/lua_api/l_internal.h11
-rw-r--r--src/script/lua_api/l_inventory.cpp1
-rw-r--r--src/script/lua_api/l_item.cpp14
-rw-r--r--src/script/lua_api/l_item.h3
-rw-r--r--src/script/lua_api/l_localplayer.cpp4
-rw-r--r--src/script/lua_api/l_mainmenu.cpp10
-rw-r--r--src/script/lua_api/l_minimap.cpp24
-rw-r--r--src/script/lua_api/l_noise.cpp2
-rw-r--r--src/script/lua_api/l_object.cpp1307
-rw-r--r--src/script/lua_api/l_object.h68
-rw-r--r--src/server.cpp125
-rw-r--r--src/server.h26
-rw-r--r--src/server/luaentity_sao.cpp30
-rw-r--r--src/server/luaentity_sao.h1
-rw-r--r--src/server/mods.cpp10
-rw-r--r--src/server/player_sao.cpp62
-rw-r--r--src/server/player_sao.h1
-rw-r--r--src/server/serveractiveobject.cpp2
-rw-r--r--src/server/serveractiveobject.h9
-rw-r--r--src/server/unit_sao.cpp23
-rw-r--r--src/server/unit_sao.h5
-rw-r--r--src/serverenvironment.cpp98
-rw-r--r--src/serverenvironment.h3
-rw-r--r--src/serverlist.cpp23
-rw-r--r--src/settings.cpp118
-rw-r--r--src/settings.h55
-rw-r--r--src/settings_translation_file.cpp8
-rw-r--r--src/sound.h4
-rw-r--r--src/staticobject.cpp4
-rw-r--r--src/texture_override.cpp38
-rw-r--r--src/texture_override.h18
-rw-r--r--src/threading/thread.cpp69
-rw-r--r--src/threading/thread.h12
-rw-r--r--src/tool.cpp8
-rw-r--r--src/translation.cpp8
-rw-r--r--src/translation.h5
-rw-r--r--src/unittest/test_map_settings_manager.cpp28
-rw-r--r--src/unittest/test_serialization.cpp374
-rw-r--r--src/unittest/test_servermodmanager.cpp2
-rw-r--r--src/unittest/test_settings.cpp18
-rw-r--r--src/unittest/test_threading.cpp25
-rw-r--r--src/util/base64.cpp5
-rw-r--r--src/util/md32_common.h2
-rw-r--r--src/util/serialize.cpp412
-rw-r--r--src/util/serialize.h250
-rw-r--r--src/util/sha256.c6
-rw-r--r--src/util/string.cpp68
-rw-r--r--src/util/string.h8
211 files changed, 5045 insertions, 4647 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 3d6d1b0ea..b8ce69f1d 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -351,6 +351,10 @@ else()
if (ICONV_LIBRARY)
set(PLATFORM_LIBS ${PLATFORM_LIBS} ${ICONV_LIBRARY})
endif()
+ if (HAIKU)
+ set(PLATFORM_LIBS ${PLATFORM_LIBS} intl network)
+ endif()
+
endif()
check_include_files(endian.h HAVE_ENDIAN_H)
@@ -673,10 +677,11 @@ endif(BUILD_SERVER)
# Blacklisted locales that don't work.
# see issue #4638
set(GETTEXT_BLACKLISTED_LOCALES
- be
+ ar
he
- ko
ky
+ ms_Arab
+ th
)
option(APPLY_LOCALE_BLACKLIST "Use a blacklist to avoid broken locales" TRUE)
@@ -697,21 +702,14 @@ include(CheckCSourceCompiles)
if(MSVC)
# Visual Studio
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /D WIN32_LEAN_AND_MEAN /MP")
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /D WIN32_LEAN_AND_MEAN")
# EHa enables SEH exceptions (used for catching segfaults)
- set(CMAKE_CXX_FLAGS_RELEASE "/EHa /Ox /GL /FD /MD /GS- /Zi /fp:fast /D NDEBUG /D _HAS_ITERATOR_DEBUGGING=0 /TP")
+ set(CMAKE_CXX_FLAGS_RELEASE "/EHa /Ox /MD /GS- /Zi /fp:fast /D NDEBUG /D _HAS_ITERATOR_DEBUGGING=0")
if(CMAKE_SIZEOF_VOID_P EQUAL 4)
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /arch:SSE")
endif()
-
- if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
- set(CMAKE_EXE_LINKER_FLAGS_RELEASE "/INCREMENTAL:NO /DEBUG /OPT:REF /OPT:ICF")
- else()
- set(CMAKE_EXE_LINKER_FLAGS_RELEASE "/LTCG /INCREMENTAL:NO /DEBUG /OPT:REF /OPT:ICF")
- endif()
-
- set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /SUBSYSTEM:WINDOWS /ENTRY:mainCRTStartup")
+ set(CMAKE_EXE_LINKER_FLAGS_RELEASE "/INCREMENTAL:NO /DEBUG /OPT:REF /OPT:ICF /SUBSYSTEM:WINDOWS /ENTRY:mainCRTStartup")
set(CMAKE_CXX_FLAGS_SEMIDEBUG "/MDd /Zi /Ob0 /O1 /RTC1")
@@ -722,6 +720,19 @@ if(MSVC)
# Flags for C files (sqlite)
# /MD = dynamically link to MSVCRxxx.dll
set(CMAKE_C_FLAGS_RELEASE "/O2 /Ob2 /MD")
+
+ # Flags that cannot be shared between cl and clang-cl
+ # https://clang.llvm.org/docs/UsersManual.html#clang-cl
+ if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fuse-ld=lld")
+
+ # Disable pragma-pack warning
+ set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -Wno-pragma-pack")
+ else()
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP")
+ set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /TP /FD /GL")
+ set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /LTCG")
+ endif()
else()
# GCC or compatible compilers such as Clang
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
diff --git a/src/activeobject.h b/src/activeobject.h
index 85e160d10..0829858ad 100644
--- a/src/activeobject.h
+++ b/src/activeobject.h
@@ -120,9 +120,9 @@ public:
virtual void setAttachment(int parent_id, const std::string &bone, v3f position,
- v3f rotation) {}
+ v3f rotation, bool force_visible) {}
virtual void getAttachment(int *parent_id, std::string *bone, v3f *position,
- v3f *rotation) const {}
+ v3f *rotation, bool *force_visible) const {}
virtual void clearChildAttachments() {}
virtual void clearParentAttachment() {}
virtual void addAttachmentChild(int child_id) {}
diff --git a/src/chat.cpp b/src/chat.cpp
index c3ed59804..2f65e68b3 100644
--- a/src/chat.cpp
+++ b/src/chat.cpp
@@ -123,14 +123,14 @@ void ChatBuffer::deleteByAge(f32 maxAge)
deleteOldest(count);
}
-u32 ChatBuffer::getColumns() const
+u32 ChatBuffer::getRows() const
{
- return m_cols;
+ return m_rows;
}
-u32 ChatBuffer::getRows() const
+void ChatBuffer::scrollTop()
{
- return m_rows;
+ m_scroll = getTopScrollPos();
}
void ChatBuffer::reformat(u32 cols, u32 rows)
@@ -220,11 +220,6 @@ void ChatBuffer::scrollBottom()
m_scroll = getBottomScrollPos();
}
-void ChatBuffer::scrollTop()
-{
- m_scroll = getTopScrollPos();
-}
-
u32 ChatBuffer::formatChatLine(const ChatLine& line, u32 cols,
std::vector<ChatFormattedLine>& destination) const
{
diff --git a/src/chat.h b/src/chat.h
index f84ece206..0b98e4d3c 100644
--- a/src/chat.h
+++ b/src/chat.h
@@ -94,8 +94,6 @@ public:
// Delete lines older than maxAge.
void deleteByAge(f32 maxAge);
- // Get number of columns, 0 if reformat has not been called yet.
- u32 getColumns() const;
// Get number of rows, 0 if reformat has not been called yet.
u32 getRows() const;
// Update console size and reformat all formatted lines.
diff --git a/src/client/camera.cpp b/src/client/camera.cpp
index c9e8fab6a..398bc8377 100644
--- a/src/client/camera.cpp
+++ b/src/client/camera.cpp
@@ -30,7 +30,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "wieldmesh.h"
#include "noise.h" // easeCurve
#include "sound.h"
-#include "event.h"
+#include "mtevent.h"
#include "nodedef.h"
#include "util/numeric.h"
#include "constants.h"
diff --git a/src/client/camera.h b/src/client/camera.h
index 3a59637bc..16a1961be 100644
--- a/src/client/camera.h
+++ b/src/client/camera.h
@@ -169,8 +169,6 @@ public:
void removeNametag(Nametag *nametag);
- const std::list<Nametag *> &getNametags() { return m_nametags; }
-
void drawNametags();
inline void addArmInertia(f32 player_yaw);
diff --git a/src/client/client.cpp b/src/client/client.cpp
index aa3e257ac..9bbb57668 100644
--- a/src/client/client.cpp
+++ b/src/client/client.cpp
@@ -130,6 +130,7 @@ Client::Client(
if (g_settings->getBool("enable_minimap")) {
m_minimap = new Minimap(this);
}
+
m_cache_save_interval = g_settings->getU16("server_map_save_interval");
}
@@ -241,18 +242,13 @@ void Client::scanModSubfolder(const std::string &mod_name, const std::string &mo
infostream << "Client::scanModSubfolder(): Loading \"" << real_path
<< "\" as \"" << vfs_path << "\"." << std::endl;
- std::ifstream is(real_path, std::ios::binary | std::ios::ate);
- if(!is.good()) {
+ std::string contents;
+ if (!fs::ReadFile(real_path, contents)) {
errorstream << "Client::scanModSubfolder(): Can't read file \""
<< real_path << "\"." << std::endl;
continue;
}
- auto size = is.tellg();
- std::string contents(size, '\0');
- is.seekg(0);
- is.read(&contents[0], size);
- infostream << " size: " << size << " bytes" << std::endl;
m_mod_vfs.emplace(vfs_path, contents);
}
}
@@ -333,6 +329,8 @@ Client::~Client()
}
delete m_minimap;
+ m_minimap = nullptr;
+
delete m_media_downloader;
}
@@ -1310,7 +1308,7 @@ void Client::sendPlayerPos(v3f pos)
player->last_pitch == player->getPitch() &&
player->last_yaw == player->getYaw() &&
player->last_keyPressed == player->keyPressed &&
- player->last_camera_fov == camera_fov &&
+ player->last_camera_fov == camera_fov &&
player->last_wanted_range == wanted_range)
return;
@@ -1671,15 +1669,13 @@ void Client::updateAllMapBlocks()
for (s16 Z = currentBlock.Z - 2; Z <= currentBlock.Z + 2; Z++)
addUpdateMeshTask(v3s16(X, Y, Z), false, true);
- std::map<v2s16, MapSector*> *sectors = m_env.getMap().getSectorsPtr();
+ Map &map = m_env.getMap();
- for (auto &sector_it : *sectors) {
- MapSector *sector = sector_it.second;
- MapBlockVect blocks;
- sector->getBlocks(blocks);
- for (MapBlock *block : blocks) {
- addUpdateMeshTask(block->getPos(), false, false);
- }
+ std::vector<v3s16> positions;
+ map.listAllLoadedBlocks(positions);
+
+ for (v3s16 p : positions) {
+ addUpdateMeshTask(p, false, false);
}
}
@@ -1693,11 +1689,6 @@ ClientEvent *Client::getClientEvent()
return event;
}
-bool Client::connectedToServer()
-{
- return m_con->Connected();
-}
-
const Address Client::getServerAddress()
{
return m_con->GetPeerAddress(PEER_ID_SERVER);
diff --git a/src/client/client.h b/src/client/client.h
index 7455d78dc..979636eba 100644
--- a/src/client/client.h
+++ b/src/client/client.h
@@ -222,6 +222,7 @@ public:
void handleCommand_CSMRestrictionFlags(NetworkPacket *pkt);
void handleCommand_PlayerSpeed(NetworkPacket *pkt);
void handleCommand_MediaPush(NetworkPacket *pkt);
+ void handleCommand_MinimapModes(NetworkPacket *pkt);
void ProcessData(NetworkPacket *pkt);
@@ -338,7 +339,6 @@ public:
u16 getProtoVersion()
{ return m_proto_ver; }
- bool connectedToServer();
void confirmRegistration();
bool m_is_registration_confirmation_state = false;
bool m_simple_singleplayer_mode;
diff --git a/src/client/clientenvironment.cpp b/src/client/clientenvironment.cpp
index 3f82bd316..2b50fbf64 100644
--- a/src/client/clientenvironment.cpp
+++ b/src/client/clientenvironment.cpp
@@ -25,7 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "clientmap.h"
#include "scripting_client.h"
#include "mapblock_mesh.h"
-#include "event.h"
+#include "mtevent.h"
#include "collision.h"
#include "nodedef.h"
#include "profiler.h"
@@ -183,84 +183,61 @@ void ClientEnvironment::step(float dtime)
if(dtime > 0.5)
dtime = 0.5;
- f32 dtime_downcount = dtime;
-
/*
Stuff that has a maximum time increment
*/
- u32 loopcount = 0;
- do
- {
- loopcount++;
-
- f32 dtime_part;
- if(dtime_downcount > dtime_max_increment)
- {
- dtime_part = dtime_max_increment;
- dtime_downcount -= dtime_part;
- }
- else
- {
- dtime_part = dtime_downcount;
- /*
- Setting this to 0 (no -=dtime_part) disables an infinite loop
- when dtime_part is so small that dtime_downcount -= dtime_part
- does nothing
- */
- dtime_downcount = 0;
- }
-
+ u32 steps = ceil(dtime / dtime_max_increment);
+ f32 dtime_part = dtime / steps;
+ for (; steps > 0; --steps) {
/*
- Handle local player
+ Local player handling
*/
- {
- // Control local player
- lplayer->applyControl(dtime_part, this);
-
- // Apply physics
- if (!free_move && !is_climbing && ! g_settings->getBool("freecam")) {
- // Gravity
- v3f speed = lplayer->getSpeed();
- if (!lplayer->in_liquid)
- speed.Y -= lplayer->movement_gravity *
- lplayer->physics_override_gravity * dtime_part * 2.0f;
-
- // Liquid floating / sinking
- if (lplayer->in_liquid && !lplayer->swimming_vertical &&
- !lplayer->swimming_pitch)
- speed.Y -= lplayer->movement_liquid_sink * dtime_part * 2.0f;
-
- // Liquid resistance
- if (lplayer->in_liquid_stable || lplayer->in_liquid) {
- // How much the node's viscosity blocks movement, ranges
- // between 0 and 1. Should match the scale at which viscosity
- // increase affects other liquid attributes.
- static const f32 viscosity_factor = 0.3f;
-
- v3f d_wanted = -speed / lplayer->movement_liquid_fluidity;
- f32 dl = d_wanted.getLength();
- if (dl > lplayer->movement_liquid_fluidity_smooth)
- dl = lplayer->movement_liquid_fluidity_smooth;
-
- dl *= (lplayer->liquid_viscosity * viscosity_factor) +
- (1 - viscosity_factor);
- v3f d = d_wanted.normalize() * (dl * dtime_part * 100.0f);
- speed += d;
- }
-
- lplayer->setSpeed(speed);
+ // Control local player
+ lplayer->applyControl(dtime_part, this);
+
+ // Apply physics
+ if (!free_move && !is_climbing && !g_settings->getBool("freecam")) {
+ // Gravity
+ v3f speed = lplayer->getSpeed();
+ if (!lplayer->in_liquid)
+ speed.Y -= lplayer->movement_gravity *
+ lplayer->physics_override_gravity * dtime_part * 2.0f;
+
+ // Liquid floating / sinking
+ if (lplayer->in_liquid && !lplayer->swimming_vertical &&
+ !lplayer->swimming_pitch)
+ speed.Y -= lplayer->movement_liquid_sink * dtime_part * 2.0f;
+
+ // Liquid resistance
+ if (lplayer->in_liquid_stable || lplayer->in_liquid) {
+ // How much the node's viscosity blocks movement, ranges
+ // between 0 and 1. Should match the scale at which viscosity
+ // increase affects other liquid attributes.
+ static const f32 viscosity_factor = 0.3f;
+
+ v3f d_wanted = -speed / lplayer->movement_liquid_fluidity;
+ f32 dl = d_wanted.getLength();
+ if (dl > lplayer->movement_liquid_fluidity_smooth)
+ dl = lplayer->movement_liquid_fluidity_smooth;
+
+ dl *= (lplayer->liquid_viscosity * viscosity_factor) +
+ (1 - viscosity_factor);
+ v3f d = d_wanted.normalize() * (dl * dtime_part * 100.0f);
+ speed += d;
}
- /*
- Move the lplayer.
- This also does collision detection.
- */
- lplayer->move(dtime_part, this, position_max_increment,
- &player_collisions);
+ lplayer->setSpeed(speed);
}
- } while (dtime_downcount > 0.001);
+
+ /*
+ Move the lplayer.
+ This also does collision detection.
+ */
+ lplayer->move(dtime_part, this, position_max_increment,
+ &player_collisions);
+ }
bool player_immortal = lplayer->getCAO() && lplayer->getCAO()->isImmortal();
@@ -368,21 +345,6 @@ bool isFreeClientActiveObjectId(const u16 id,
}
-u16 getFreeClientActiveObjectId(ClientActiveObjectMap &objects)
-{
- // try to reuse id's as late as possible
- static u16 last_used_id = 0;
- u16 startid = last_used_id;
- for(;;) {
- last_used_id ++;
- if (isFreeClientActiveObjectId(last_used_id, objects))
- return last_used_id;
-
- if (last_used_id == startid)
- return 0;
- }
-}
-
u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
{
// Register object. If failed return zero id
diff --git a/src/client/clientlauncher.cpp b/src/client/clientlauncher.cpp
index ce16797e6..29427f609 100644
--- a/src/client/clientlauncher.cpp
+++ b/src/client/clientlauncher.cpp
@@ -327,13 +327,13 @@ void ClientLauncher::init_args(GameStartData &start_data, const Settings &cmd_ar
// Join a remote server
start_data.address = cmd_args.get("address");
start_data.world_path.clear();
+ start_data.name = g_settings->get("name");
}
if (!start_data.world_path.empty()) {
// Start a singleplayer instance
start_data.address = "";
}
- start_data.name = g_settings->get("name");
if (cmd_args.exists("name"))
start_data.name = cmd_args.get("name");
@@ -419,7 +419,6 @@ bool ClientLauncher::launch_game(std::string &error_message,
/* Show the GUI menu
*/
std::string server_name, server_description;
- start_data.local_server = false;
if (!skip_main_menu) {
// Initialize menu data
// TODO: Re-use existing structs (GameStartData)
@@ -467,6 +466,9 @@ bool ClientLauncher::launch_game(std::string &error_message,
start_data.local_server = !menudata.simple_singleplayer_mode &&
start_data.address.empty();
+ } else {
+ start_data.local_server = !start_data.world_path.empty() &&
+ start_data.address.empty() && !start_data.name.empty();
}
if (!RenderingEngine::run())
diff --git a/src/client/clientmap.cpp b/src/client/clientmap.cpp
index d41b66741..294687ff8 100644
--- a/src/client/clientmap.cpp
+++ b/src/client/clientmap.cpp
@@ -36,7 +36,7 @@ ClientMap::ClientMap(
MapDrawControl &control,
s32 id
):
- Map(dout_client, client),
+ Map(client),
scene::ISceneNode(RenderingEngine::get_scene_manager()->getRootSceneNode(),
RenderingEngine::get_scene_manager(), id),
m_client(client),
@@ -122,20 +122,20 @@ void ClientMap::updateDrawList()
}
m_drawlist.clear();
- v3f camera_position = m_camera_position;
- v3f camera_direction = m_camera_direction;
- f32 camera_fov = m_camera_fov;
+ const v3f camera_position = m_camera_position;
+ const v3f camera_direction = m_camera_direction;
// Use a higher fov to accomodate faster camera movements.
// Blocks are cropped better when they are drawn.
- // Or maybe they aren't? Well whatever.
- camera_fov *= 1.2;
+ const f32 camera_fov = m_camera_fov * 1.1f;
v3s16 cam_pos_nodes = floatToInt(camera_position, BS);
v3s16 p_blocks_min;
v3s16 p_blocks_max;
getBlocksInViewRange(cam_pos_nodes, &p_blocks_min, &p_blocks_max);
+ // Number of blocks currently loaded by the client
+ u32 blocks_loaded = 0;
// Number of blocks with mesh in rendering range
u32 blocks_in_range_with_mesh = 0;
// Number of blocks occlusion culled
@@ -160,6 +160,7 @@ void ClientMap::updateDrawList()
MapSector *sector = sector_it.second;
v2s16 sp = sector->getPos();
+ blocks_loaded += sector->size();
if (!m_control.range_all) {
if (sp.X < p_blocks_min.X || sp.X > p_blocks_max.X ||
sp.Y < p_blocks_min.Z || sp.Y > p_blocks_max.Z)
@@ -181,8 +182,12 @@ void ClientMap::updateDrawList()
if not seen on display
*/
- if (block->mesh)
+ if (block->mesh) {
block->mesh->updateCameraOffset(m_camera_offset);
+ } else {
+ // Ignore if mesh doesn't exist
+ continue;
+ }
float range = 100000 * BS;
if (!m_control.range_all)
@@ -193,13 +198,6 @@ void ClientMap::updateDrawList()
camera_direction, camera_fov, range, &d))
continue;
-
- /*
- Ignore if mesh doesn't exist
- */
- if (!block->mesh)
- continue;
-
blocks_in_range_with_mesh++;
/*
@@ -228,6 +226,7 @@ void ClientMap::updateDrawList()
g_profiler->avg("MapBlock meshes in range [#]", blocks_in_range_with_mesh);
g_profiler->avg("MapBlocks occlusion culled [#]", blocks_occlusion_culled);
g_profiler->avg("MapBlocks drawn [#]", m_drawlist.size());
+ g_profiler->avg("MapBlocks loaded [#]", blocks_loaded);
}
struct MeshBufList
@@ -293,13 +292,13 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
/*
Get animation parameters
*/
- float animation_time = m_client->getAnimationTime();
- int crack = m_client->getCrackLevel();
- u32 daynight_ratio = m_client->getEnv().getDayNightRatio();
+ const float animation_time = m_client->getAnimationTime();
+ const int crack = m_client->getCrackLevel();
+ const u32 daynight_ratio = m_client->getEnv().getDayNightRatio();
- v3f camera_position = m_camera_position;
- v3f camera_direction = m_camera_direction;
- f32 camera_fov = m_camera_fov;
+ const v3f camera_position = m_camera_position;
+ const v3f camera_direction = m_camera_direction;
+ const f32 camera_fov = m_camera_fov;
/*
Get all blocks and draw all visible ones
diff --git a/src/client/clientmedia.cpp b/src/client/clientmedia.cpp
index 8cd3b6bcc..c4c08c05d 100644
--- a/src/client/clientmedia.cpp
+++ b/src/client/clientmedia.cpp
@@ -260,7 +260,8 @@ void ClientMediaDownloader::initialStep(Client *client)
fetch_request.request_id = m_httpfetch_next_id; // == i
fetch_request.timeout = m_httpfetch_timeout;
fetch_request.connect_timeout = m_httpfetch_timeout;
- fetch_request.post_data = required_hash_set;
+ fetch_request.method = HTTP_POST;
+ fetch_request.raw_data = required_hash_set;
fetch_request.extra_headers.emplace_back(
"Content-Type: application/octet-stream");
diff --git a/src/client/clouds.cpp b/src/client/clouds.cpp
index 887a62f25..253dee8b9 100644
--- a/src/client/clouds.cpp
+++ b/src/client/clouds.cpp
@@ -170,8 +170,9 @@ void Clouds::render()
// Read noise
- bool *grid = new bool[m_cloud_radius_i * 2 * m_cloud_radius_i * 2];
-
+ std::vector<char> grid(m_cloud_radius_i * 2 * m_cloud_radius_i * 2); // vector<bool> is broken
+ std::vector<video::S3DVertex> vertices;
+ vertices.reserve(16 * m_cloud_radius_i * m_cloud_radius_i);
for(s16 zi = -m_cloud_radius_i; zi < m_cloud_radius_i; zi++) {
u32 si = (zi + m_cloud_radius_i) * m_cloud_radius_i * 2 + m_cloud_radius_i;
@@ -195,12 +196,7 @@ void Clouds::render()
{
s16 zi = zi0;
s16 xi = xi0;
- // Draw from front to back (needed for transparency)
- /*if(zi <= 0)
- zi = -m_cloud_radius_i - zi;
- if(xi <= 0)
- xi = -m_cloud_radius_i - xi;*/
- // Draw from back to front
+ // Draw from back to front for proper transparency
if(zi >= 0)
zi = m_cloud_radius_i - zi - 1;
if(xi >= 0)
@@ -220,17 +216,10 @@ void Clouds::render()
video::S3DVertex(0,0,0, 0,0,0, c_top, 0, 0)
};
- /*if(zi <= 0 && xi <= 0){
- v[0].Color.setBlue(255);
- v[1].Color.setBlue(255);
- v[2].Color.setBlue(255);
- v[3].Color.setBlue(255);
- }*/
-
- f32 rx = cloud_size / 2.0f;
+ const 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;
+ const f32 ry = m_enable_3d ? m_params.thickness * BS : 0.0f;
+ const f32 rz = cloud_size / 2;
for(int i=0; i<num_faces_to_draw; i++)
{
@@ -320,15 +309,25 @@ void Clouds::render()
v3f pos(p0.X, m_params.height * BS, p0.Y);
pos -= intToFloat(m_camera_offset, BS);
- for (video::S3DVertex &vertex : v)
+ for (video::S3DVertex &vertex : v) {
vertex.Pos += pos;
- u16 indices[] = {0,1,2,2,3,0};
- driver->drawVertexPrimitiveList(v, 4, indices, 2,
- video::EVT_STANDARD, scene::EPT_TRIANGLES, video::EIT_16BIT);
+ vertices.push_back(vertex);
+ }
}
}
-
- delete[] grid;
+ int quad_count = vertices.size() / 4;
+ std::vector<u16> indices;
+ indices.reserve(quad_count * 6);
+ for (int k = 0; k < quad_count; k++) {
+ indices.push_back(4 * k + 0);
+ indices.push_back(4 * k + 1);
+ indices.push_back(4 * k + 2);
+ indices.push_back(4 * k + 2);
+ indices.push_back(4 * k + 3);
+ indices.push_back(4 * k + 0);
+ }
+ driver->drawVertexPrimitiveList(vertices.data(), vertices.size(), indices.data(), 2 * quad_count,
+ video::EVT_STANDARD, scene::EPT_TRIANGLES, video::EIT_16BIT);
// Restore fog settings
driver->setFog(fog_color, fog_type, fog_start, fog_end, fog_density,
@@ -342,14 +341,13 @@ void Clouds::step(float dtime)
void Clouds::update(const v3f &camera_p, const video::SColorf &color_diffuse)
{
+ video::SColorf ambient(m_params.color_ambient);
+ video::SColorf bright(m_params.color_bright);
m_camera_pos = camera_p;
- 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;
+ m_color.r = core::clamp(color_diffuse.r * bright.r, ambient.r, 1.0f);
+ m_color.g = core::clamp(color_diffuse.g * bright.g, ambient.g, 1.0f);
+ m_color.b = core::clamp(color_diffuse.b * bright.b, ambient.b, 1.0f);
+ m_color.a = bright.a;
// is the camera inside the cloud mesh?
m_camera_inside_cloud = false; // default
diff --git a/src/client/content_cao.cpp b/src/client/content_cao.cpp
index e1cebf461..7208212d4 100644
--- a/src/client/content_cao.cpp
+++ b/src/client/content_cao.cpp
@@ -48,6 +48,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <cmath>
#include "client/shader.h"
#include "script/scripting_client.h"
+#include "client/minimap.h"
class Settings;
struct ToolCapabilities;
@@ -354,6 +355,8 @@ void GenericCAO::initialize(const std::string &data)
m_is_local_player = true;
m_is_visible = false;
player->setCAO(this);
+
+ m_prop.show_on_minimap = false;
}
}
@@ -372,7 +375,7 @@ void GenericCAO::processInitData(const std::string &data)
}
// PROTOCOL_VERSION >= 37
- m_name = deSerializeString(is);
+ m_name = deSerializeString16(is);
m_is_player = readU8(is);
m_id = readU16(is);
m_position = readV3F32(is);
@@ -382,7 +385,7 @@ void GenericCAO::processInitData(const std::string &data)
const u8 num_messages = readU8(is);
for (int i = 0; i < num_messages; i++) {
- std::string message = deSerializeLongString(is);
+ std::string message = deSerializeString32(is);
processMessage(message);
}
@@ -457,18 +460,21 @@ void GenericCAO::setChildrenVisible(bool toset)
for (u16 cao_id : m_attachment_child_ids) {
GenericCAO *obj = m_env->getGenericCAO(cao_id);
if (obj) {
- obj->setVisible(toset);
+ // Check if the entity is forced to appear in first person.
+ obj->setVisible(obj->m_force_visible ? true : toset);
}
}
}
-void GenericCAO::setAttachment(int parent_id, const std::string &bone, v3f position, v3f rotation)
+void GenericCAO::setAttachment(int parent_id, const std::string &bone,
+ v3f position, v3f rotation, bool force_visible)
{
int old_parent = m_attachment_parent_id;
m_attachment_parent_id = parent_id;
m_attachment_bone = bone;
m_attachment_position = position;
m_attachment_rotation = rotation;
+ m_force_visible = force_visible;
ClientActiveObject *parent = m_env->getActiveObject(parent_id);
@@ -480,17 +486,32 @@ void GenericCAO::setAttachment(int parent_id, const std::string &bone, v3f posit
if (parent)
parent->addAttachmentChild(m_id);
}
-
+
updateAttachments();
+
+ // Forcibly show attachments if required by set_attach
+ if (m_force_visible) {
+ m_is_visible = true;
+ } else if (!m_is_local_player) {
+ // Objects attached to the local player should be hidden in first person
+ m_is_visible = !m_attached_to_local ||
+ m_client->getCamera()->getCameraMode() != CAMERA_MODE_FIRST;
+ m_force_visible = false;
+ } else {
+ // Local players need to have this set,
+ // otherwise first person attachments fail.
+ m_is_visible = true;
+ }
}
void GenericCAO::getAttachment(int *parent_id, std::string *bone, v3f *position,
- v3f *rotation) const
+ v3f *rotation, bool *force_visible) const
{
*parent_id = m_attachment_parent_id;
*bone = m_attachment_bone;
*position = m_attachment_position;
*rotation = m_attachment_rotation;
+ *force_visible = m_force_visible;
}
void GenericCAO::clearChildAttachments()
@@ -500,7 +521,7 @@ void GenericCAO::clearChildAttachments()
int child_id = *m_attachment_child_ids.begin();
if (ClientActiveObject *child = m_env->getActiveObject(child_id))
- child->setAttachment(0, "", v3f(), v3f());
+ child->setAttachment(0, "", v3f(), v3f(), false);
removeAttachmentChild(child_id);
}
@@ -509,9 +530,9 @@ void GenericCAO::clearChildAttachments()
void GenericCAO::clearParentAttachment()
{
if (m_attachment_parent_id)
- setAttachment(0, "", m_attachment_position, m_attachment_rotation);
+ setAttachment(0, "", m_attachment_position, m_attachment_rotation, false);
else
- setAttachment(0, "", v3f(), v3f());
+ setAttachment(0, "", v3f(), v3f(), false);
}
void GenericCAO::addAttachmentChild(int child_id)
@@ -569,6 +590,9 @@ void GenericCAO::removeFromScene(bool permanent)
m_client->getCamera()->removeNametag(m_nametag);
m_nametag = nullptr;
}
+
+ if (m_marker && m_client->getMinimap())
+ m_client->getMinimap()->removeMarker(&m_marker);
}
void GenericCAO::addToScene(ITextureSource *tsrc)
@@ -797,11 +821,13 @@ void GenericCAO::addToScene(ITextureSource *tsrc)
node->setParent(m_matrixnode);
updateNametag();
+ updateMarker();
updateNodePos();
updateAnimation();
updateBonePosition();
updateAttachments();
setNodeLight(m_last_light);
+ updateMeshCulling();
}
void GenericCAO::updateLight(u32 day_night_ratio)
@@ -889,6 +915,26 @@ u16 GenericCAO::getLightPosition(v3s16 *pos)
return 3;
}
+void GenericCAO::updateMarker()
+{
+ if (!m_client->getMinimap())
+ return;
+
+ if (!m_prop.show_on_minimap) {
+ if (m_marker)
+ m_client->getMinimap()->removeMarker(&m_marker);
+ return;
+ }
+
+ if (m_marker)
+ return;
+
+ scene::ISceneNode *node = getSceneNode();
+ if (!node)
+ return;
+ m_marker = m_client->getMinimap()->addMarker(node);
+}
+
void GenericCAO::updateNametag()
{
if (m_is_local_player && ! g_settings->getBool("freecam")) // No nametag for local player
@@ -981,13 +1027,13 @@ void GenericCAO::step(float dtime, ClientEnvironment *env)
if (controls.sneak && walking && ! g_settings->getBool("no_slow"))
new_speed /= 2;
- if (walking && (controls.LMB || controls.RMB)) {
+ if (walking && (controls.dig || controls.place)) {
new_anim = player->local_animations[3];
player->last_animation = WD_ANIM;
- } else if(walking) {
+ } else if (walking) {
new_anim = player->local_animations[1];
player->last_animation = WALK_ANIM;
- } else if(controls.LMB || controls.RMB) {
+ } else if (controls.dig || controls.place) {
new_anim = player->local_animations[2];
player->last_animation = DIG_ANIM;
}
@@ -1010,9 +1056,9 @@ void GenericCAO::step(float dtime, ClientEnvironment *env)
// Update local player animations
if ((player->last_animation != old_anim ||
- m_animation_speed != old_anim_speed) &&
- player->last_animation != NO_ANIM && allow_update)
- updateAnimation();
+ m_animation_speed != old_anim_speed) &&
+ player->last_animation != NO_ANIM && allow_update)
+ updateAnimation();
}
}
@@ -1182,6 +1228,7 @@ void GenericCAO::updateTexturePos()
int row = m_tx_basepos.Y;
int col = m_tx_basepos.X;
+ // Yawpitch goes rightwards
if (m_tx_select_horiz_by_yawpitch) {
if (cam_to_entity.Y > 0.75)
col += 5;
@@ -1212,6 +1259,27 @@ void GenericCAO::updateTexturePos()
float tys = m_tx_size.Y;
setBillboardTextureMatrix(m_spritenode, txs, tys, col, row);
}
+
+ else if (m_meshnode) {
+ if (m_prop.visual == "upright_sprite") {
+ int row = m_tx_basepos.Y;
+ int col = m_tx_basepos.X;
+
+ // Animation goes downwards
+ row += m_anim_frame;
+
+ const auto &tx = m_tx_size;
+ v2f t[4] = { // cf. vertices in GenericCAO::addToScene()
+ tx * v2f(col+1, row+1),
+ tx * v2f(col, row+1),
+ tx * v2f(col, row),
+ tx * v2f(col+1, row),
+ };
+ auto mesh = m_meshnode->getMesh();
+ setMeshBufferTextureCoords(mesh->getMeshBuffer(0), t, 4);
+ setMeshBufferTextureCoords(mesh->getMeshBuffer(1), t, 4);
+ }
+ }
}
// Do not pass by reference, see header.
@@ -1253,7 +1321,7 @@ void GenericCAO::updateTextures(std::string mod)
}
}
- if (m_animated_meshnode) {
+ else if (m_animated_meshnode) {
if (m_prop.visual == "mesh") {
for (u32 i = 0; i < m_prop.textures.size() &&
i < m_animated_meshnode->getMaterialCount(); ++i) {
@@ -1302,8 +1370,8 @@ void GenericCAO::updateTextures(std::string mod)
}
}
}
- if(m_meshnode)
- {
+
+ else if (m_meshnode) {
if(m_prop.visual == "cube")
{
for (u32 i = 0; i < 6; ++i)
@@ -1395,6 +1463,9 @@ void GenericCAO::updateTextures(std::string mod)
setMeshColor(mesh, m_prop.colors[0]);
}
}
+ // Prevent showing the player after changing texture
+ if (m_is_local_player)
+ updateMeshCulling();
}
void GenericCAO::updateAnimation()
@@ -1564,6 +1635,8 @@ void GenericCAO::processMessage(const std::string &data)
u8 cmd = readU8(is);
if (cmd == AO_CMD_SET_PROPERTIES) {
ObjectProperties newprops;
+ newprops.show_on_minimap = m_is_player; // default
+
newprops.deSerialize(is);
// Check what exactly changed
@@ -1597,6 +1670,8 @@ void GenericCAO::processMessage(const std::string &data)
if ((m_is_player && !m_is_local_player) && m_prop.nametag.empty())
m_prop.nametag = m_name;
+ if (m_is_local_player)
+ m_prop.show_on_minimap = false;
if (expire_visuals) {
expireVisuals();
@@ -1609,6 +1684,7 @@ void GenericCAO::processMessage(const std::string &data)
updateTextures(m_current_texture_modifier);
}
updateNametag();
+ updateMarker();
}
} else if (cmd == AO_CMD_UPDATE_POSITION) {
// Not sent by the server if this object is an attachment.
@@ -1641,7 +1717,7 @@ void GenericCAO::processMessage(const std::string &data)
rot_translator.update(m_rotation, false, update_interval);
updateNodePos();
} else if (cmd == AO_CMD_SET_TEXTURE_MOD) {
- std::string mod = deSerializeString(is);
+ std::string mod = deSerializeString16(is);
// immediately reset a engine issued texture modifier if a mod sends a different one
if (m_reset_textures_timer > 0) {
@@ -1724,7 +1800,7 @@ void GenericCAO::processMessage(const std::string &data)
m_animation_speed = readF32(is);
updateAnimationSpeed();
} else if (cmd == AO_CMD_SET_BONE_POSITION) {
- std::string bone = deSerializeString(is);
+ std::string bone = deSerializeString16(is);
v3f position = readV3F32(is);
v3f rotation = readV3F32(is);
m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
@@ -1732,15 +1808,12 @@ void GenericCAO::processMessage(const std::string &data)
// updateBonePosition(); now called every step
} else if (cmd == AO_CMD_ATTACH_TO) {
u16 parent_id = readS16(is);
- std::string bone = deSerializeString(is);
+ std::string bone = deSerializeString16(is);
v3f position = readV3F32(is);
v3f rotation = readV3F32(is);
+ bool force_visible = readU8(is); // Returns false for EOF
- setAttachment(parent_id, bone, position, rotation);
-
- // localplayer itself can't be attached to localplayer
- if (!m_is_local_player)
- m_is_visible = !m_attached_to_local;
+ setAttachment(parent_id, bone, position, rotation, force_visible);
} else if (cmd == AO_CMD_PUNCHED) {
u16 result_hp = readU16(is);
@@ -1782,7 +1855,7 @@ void GenericCAO::processMessage(const std::string &data)
int armor_groups_size = readU16(is);
for(int i=0; i<armor_groups_size; i++)
{
- std::string name = deSerializeString(is);
+ std::string name = deSerializeString16(is);
int rating = readS16(is);
m_armor_groups[name] = rating;
}
@@ -1854,5 +1927,43 @@ std::string GenericCAO::debugInfoText()
return os.str();
}
+void GenericCAO::updateMeshCulling()
+{
+ if (!m_is_local_player)
+ return;
+
+ const bool hidden = m_client->getCamera()->getCameraMode() == CAMERA_MODE_FIRST;
+
+ if (m_meshnode && m_prop.visual == "upright_sprite") {
+ u32 buffers = m_meshnode->getMesh()->getMeshBufferCount();
+ for (u32 i = 0; i < buffers; i++) {
+ video::SMaterial &mat = m_meshnode->getMesh()->getMeshBuffer(i)->getMaterial();
+ // upright sprite has no backface culling
+ mat.setFlag(video::EMF_FRONT_FACE_CULLING, hidden);
+ }
+ return;
+ }
+
+ irr::scene::ISceneNode *node = getSceneNode();
+ if (!node)
+ return;
+
+ if (hidden) {
+ // Hide the mesh by culling both front and
+ // back faces. Serious hackyness but it works for our
+ // purposes. This also preserves the skeletal armature.
+ node->setMaterialFlag(video::EMF_BACK_FACE_CULLING,
+ true);
+ node->setMaterialFlag(video::EMF_FRONT_FACE_CULLING,
+ true);
+ } else {
+ // Restore mesh visibility.
+ node->setMaterialFlag(video::EMF_BACK_FACE_CULLING,
+ m_prop.backface_culling);
+ node->setMaterialFlag(video::EMF_FRONT_FACE_CULLING,
+ false);
+ }
+}
+
// Prototype
GenericCAO proto_GenericCAO(NULL, NULL);
diff --git a/src/client/content_cao.h b/src/client/content_cao.h
index 75882a074..09c26bd9c 100644
--- a/src/client/content_cao.h
+++ b/src/client/content_cao.h
@@ -30,6 +30,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
class Camera;
class Client;
struct Nametag;
+struct MinimapMarker;
/*
SmoothTranslator
@@ -84,6 +85,7 @@ private:
scene::IBillboardSceneNode *m_spritenode = nullptr;
scene::IDummyTransformationSceneNode *m_matrixnode = nullptr;
Nametag *m_nametag = nullptr;
+ MinimapMarker *m_marker = nullptr;
v3f m_position = v3f(0.0f, 10.0f * BS, 0);
v3f m_velocity;
v3f m_acceleration;
@@ -109,6 +111,7 @@ private:
v3f m_attachment_position;
v3f m_attachment_rotation;
bool m_attached_to_local = false;
+ bool m_force_visible = false;
int m_anim_frame = 0;
int m_anim_num_frames = 1;
@@ -241,9 +244,10 @@ public:
}
void setChildrenVisible(bool toset);
- void setAttachment(int parent_id, const std::string &bone, v3f position, v3f rotation);
+ void setAttachment(int parent_id, const std::string &bone, v3f position,
+ v3f rotation, bool force_visible);
void getAttachment(int *parent_id, std::string *bone, v3f *position,
- v3f *rotation) const;
+ v3f *rotation, bool *force_visible) const;
void clearChildAttachments();
void clearParentAttachment();
void addAttachmentChild(int child_id);
@@ -274,6 +278,8 @@ public:
void updateNametag();
+ void updateMarker();
+
void updateNodePos();
void step(float dtime, ClientEnvironment *env);
@@ -308,4 +314,6 @@ public:
{
return &m_prop;
}
+
+ void updateMeshCulling();
};
diff --git a/src/client/content_mapblock.cpp b/src/client/content_mapblock.cpp
index 3d06584c4..df2748212 100644
--- a/src/client/content_mapblock.cpp
+++ b/src/client/content_mapblock.cpp
@@ -723,7 +723,8 @@ void MapblockMeshGenerator::drawGlasslikeFramedNode()
for (auto &glass_tile : glass_tiles)
glass_tile = tiles[4];
- u8 param2 = n.getParam2();
+ // Only respect H/V merge bits when paramtype2 = "glasslikeliquidlevel" (liquid tank)
+ u8 param2 = (f->param_type_2 == CPT2_GLASSLIKE_LIQUID_LEVEL) ? n.getParam2() : 0;
bool H_merge = !(param2 & 128);
bool V_merge = !(param2 & 64);
param2 &= 63;
@@ -1454,10 +1455,10 @@ void MapblockMeshGenerator::generate()
}
}
-void MapblockMeshGenerator::renderSingle(content_t node)
+void MapblockMeshGenerator::renderSingle(content_t node, u8 param2)
{
p = {0, 0, 0};
- n = MapNode(node, 0xff, 0x00);
+ n = MapNode(node, 0xff, param2);
f = &nodedef->get(n);
drawNode();
}
diff --git a/src/client/content_mapblock.h b/src/client/content_mapblock.h
index 97947cdbe..487d84a07 100644
--- a/src/client/content_mapblock.h
+++ b/src/client/content_mapblock.h
@@ -174,5 +174,5 @@ public:
public:
MapblockMeshGenerator(MeshMakeData *input, MeshCollector *output);
void generate();
- void renderSingle(content_t node);
+ void renderSingle(content_t node, u8 param2 = 0x00);
};
diff --git a/src/client/event_manager.h b/src/client/event_manager.h
index 3762e89bf..16f7bcf07 100644
--- a/src/client/event_manager.h
+++ b/src/client/event_manager.h
@@ -19,7 +19,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#pragma once
-#include "event.h"
+#include "mtevent.h"
#include <list>
#include <map>
diff --git a/src/client/fontengine.h b/src/client/fontengine.h
index 865b2d3ff..c6efa0df4 100644
--- a/src/client/fontengine.h
+++ b/src/client/fontengine.h
@@ -48,7 +48,7 @@ struct FontSpec {
u16 getHash()
{
- return (mode << 2) | (bold << 1) | italic;
+ return (mode << 2) | (static_cast<u8>(bold) << 1) | static_cast<u8>(italic);
}
unsigned int size;
diff --git a/src/client/game.cpp b/src/client/game.cpp
index 88607d1d8..cc2a1bc43 100644
--- a/src/client/game.cpp
+++ b/src/client/game.cpp
@@ -96,7 +96,7 @@ Game::Game() :
&settingChangedCallback, this);
g_settings->registerChangedCallback("joystick_frustum_sensitivity",
&settingChangedCallback, this);
- g_settings->registerChangedCallback("repeat_rightclick_time",
+ g_settings->registerChangedCallback("repeat_place_time",
&settingChangedCallback, this);
g_settings->registerChangedCallback("noclip",
&settingChangedCallback, this);
@@ -164,7 +164,7 @@ Game::~Game()
&settingChangedCallback, this);
g_settings->deregisterChangedCallback("mouse_sensitivity",
&settingChangedCallback, this);
- g_settings->deregisterChangedCallback("repeat_rightclick_time",
+ g_settings->deregisterChangedCallback("repeat_place_time",
&settingChangedCallback, this);
g_settings->deregisterChangedCallback("noclip",
&settingChangedCallback, this);
@@ -476,7 +476,8 @@ bool Game::createSingleplayerServer(const std::string &map_dir,
return false;
}
- server = new Server(map_dir, gamespec, simple_singleplayer_mode, bind_addr, false);
+ server = new Server(map_dir, gamespec, simple_singleplayer_mode, bind_addr,
+ false, nullptr, error_message);
server->start();
return true;
@@ -545,7 +546,7 @@ bool Game::createClient(const GameStartData &start_data)
/* Skybox
*/
- sky = new Sky(-1, texture_src);
+ sky = new Sky(-1, texture_src, shader_src);
scsf->setSky(sky);
skybox = NULL; // This is used/set later on in the main run loop
@@ -591,11 +592,9 @@ bool Game::createClient(const GameStartData &start_data)
}
mapper = client->getMinimap();
- if (mapper) {
- mapper->setMinimapMode(MINIMAP_MODE_OFF);
- if (client->modsLoaded())
- client->getScript()->on_minimap_ready(mapper);
- }
+
+ if (mapper && client->modsLoaded())
+ client->getScript()->on_minimap_ready(mapper);
return true;
}
@@ -834,7 +833,10 @@ bool Game::getServerContent(bool *aborted)
std::stringstream message;
std::fixed(message);
message.precision(0);
- message << gettext("Media...") << " " << (client->mediaReceiveProgress()*100) << "%";
+ float receive = client->mediaReceiveProgress() * 100;
+ message << gettext("Media...");
+ if (receive > 0)
+ message << " " << receive << "%";
message.precision(2);
if ((USE_CURL == 0) ||
@@ -1228,15 +1230,11 @@ void Game::processItemSelection(u16 *new_playeritem)
s32 dir = wheel;
- if (input->joystick.wasKeyDown(KeyType::SCROLL_DOWN) ||
- wasKeyDown(KeyType::HOTBAR_NEXT)) {
+ if (wasKeyDown(KeyType::HOTBAR_NEXT))
dir = -1;
- }
- if (input->joystick.wasKeyDown(KeyType::SCROLL_UP) ||
- wasKeyDown(KeyType::HOTBAR_PREV)) {
+ if (wasKeyDown(KeyType::HOTBAR_PREV))
dir = 1;
- }
if (dir < 0)
*new_playeritem = *new_playeritem < max_item ? *new_playeritem + 1 : 0;
@@ -1289,7 +1287,7 @@ void Game::openInventory()
TextDest *txt_dst = new TextDestPlayerInventory(client);
auto *&formspec = m_game_ui->updateFormspec("");
GUIFormSpecMenu::create(formspec, client, &input->joystick, fs_src,
- txt_dst, client->getFormspecPrepend());
+ txt_dst, client->getFormspecPrepend(), sound);
formspec->setFormSpec(fs_src->getForm(), inventoryloc);
}
@@ -1490,52 +1488,37 @@ void Game::toggleMinimap(bool shift_pressed)
if (!mapper || !m_game_ui->m_flags.show_hud || !g_settings->getBool("enable_minimap"))
return;
- if (shift_pressed) {
+ if (shift_pressed)
mapper->toggleMinimapShape();
- return;
- }
+ else
+ mapper->nextMode();
+ // TODO: When legacy minimap is deprecated, keep only HUD minimap stuff here
+
+ // Not so satisying code to keep compatibility with old fixed mode system
+ // -->
u32 hud_flags = client->getEnv().getLocalPlayer()->hud_flags;
- MinimapMode mode = MINIMAP_MODE_OFF;
- if (hud_flags & HUD_FLAG_MINIMAP_VISIBLE) {
- mode = mapper->getMinimapMode();
- mode = (MinimapMode)((int)mode + 1);
- // If radar is disabled and in, or switching to, radar mode
- if (!(hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE) && mode > 3)
- mode = MINIMAP_MODE_OFF;
- }
+ if (!(hud_flags & HUD_FLAG_MINIMAP_VISIBLE)) {
+ m_game_ui->m_flags.show_minimap = false;
+ } else {
- m_game_ui->m_flags.show_minimap = true;
- switch (mode) {
- case MINIMAP_MODE_SURFACEx1:
- m_game_ui->showTranslatedStatusText("Minimap in surface mode, Zoom x1");
- break;
- case MINIMAP_MODE_SURFACEx2:
- m_game_ui->showTranslatedStatusText("Minimap in surface mode, Zoom x2");
- break;
- case MINIMAP_MODE_SURFACEx4:
- m_game_ui->showTranslatedStatusText("Minimap in surface mode, Zoom x4");
- break;
- case MINIMAP_MODE_RADARx1:
- m_game_ui->showTranslatedStatusText("Minimap in radar mode, Zoom x1");
- break;
- case MINIMAP_MODE_RADARx2:
- m_game_ui->showTranslatedStatusText("Minimap in radar mode, Zoom x2");
- break;
- case MINIMAP_MODE_RADARx4:
- m_game_ui->showTranslatedStatusText("Minimap in radar mode, Zoom x4");
- break;
- default:
- mode = MINIMAP_MODE_OFF;
- m_game_ui->m_flags.show_minimap = false;
- if (hud_flags & HUD_FLAG_MINIMAP_VISIBLE)
- m_game_ui->showTranslatedStatusText("Minimap hidden");
- else
- m_game_ui->showTranslatedStatusText("Minimap currently disabled by game or mod");
- }
+ // If radar is disabled, try to find a non radar mode or fall back to 0
+ if (!(hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE))
+ while (mapper->getModeIndex() &&
+ mapper->getModeDef().type == MINIMAP_TYPE_RADAR)
+ mapper->nextMode();
- mapper->setMinimapMode(mode);
+ m_game_ui->m_flags.show_minimap = mapper->getModeDef().type !=
+ MINIMAP_TYPE_OFF;
+ }
+ // <--
+ // End of 'not so satifying code'
+ if ((hud_flags & HUD_FLAG_MINIMAP_VISIBLE) ||
+ (hud && hud->hasElementOfType(HUD_ELEM_MINIMAP)))
+ m_game_ui->showStatusText(utf8_to_wide(mapper->getModeDef().label));
+ else
+ m_game_ui->showTranslatedStatusText("Minimap currently disabled by game or mod");
}
void Game::toggleFog()
@@ -1743,8 +1726,8 @@ void Game::updatePlayerControl(const CameraOrientation &cam)
isKeyDown(KeyType::SPECIAL1),
isKeyDown(KeyType::SNEAK),
isKeyDown(KeyType::ZOOM),
- input->getLeftState(),
- input->getRightState(),
+ isKeyDown(KeyType::DIG),
+ isKeyDown(KeyType::PLACE),
cam.camera_pitch,
cam.camera_yaw,
input->joystick.getAxisWithoutDead(JA_SIDEWARD_MOVE),
@@ -1759,8 +1742,8 @@ void Game::updatePlayerControl(const CameraOrientation &cam)
( (u32)(isKeyDown(KeyType::JUMP) & 0x1) << 4) |
( (u32)(isKeyDown(KeyType::SPECIAL1) & 0x1) << 5) |
( (u32)(isKeyDown(KeyType::SNEAK) & 0x1) << 6) |
- ( (u32)(input->getLeftState() & 0x1) << 7) |
- ( (u32)(input->getRightState() & 0x1) << 8) |
+ ( (u32)(isKeyDown(KeyType::DIG) & 0x1) << 7) |
+ ( (u32)(isKeyDown(KeyType::PLACE) & 0x1) << 8) |
( (u32)(isKeyDown(KeyType::ZOOM) & 0x1) << 9)
);
@@ -1898,7 +1881,7 @@ void Game::handleClientEvent_ShowFormSpec(ClientEvent *event, CameraOrientation
auto *&formspec = m_game_ui->updateFormspec(*(event->show_formspec.formname));
GUIFormSpecMenu::create(formspec, client, &input->joystick,
- fs_src, txt_dst, client->getFormspecPrepend());
+ fs_src, txt_dst, client->getFormspecPrepend(), sound);
}
delete event->show_formspec.formspec;
@@ -1911,7 +1894,7 @@ void Game::handleClientEvent_ShowLocalFormSpec(ClientEvent *event, CameraOrienta
LocalFormspecHandler *txt_dst =
new LocalFormspecHandler(*event->show_formspec.formname, client);
GUIFormSpecMenu::create(m_game_ui->getFormspecGUI(), client, &input->joystick,
- fs_src, txt_dst, client->getFormspecPrepend());
+ fs_src, txt_dst, client->getFormspecPrepend(), sound);
delete event->show_formspec.formspec;
delete event->show_formspec.formname;
@@ -2246,12 +2229,13 @@ void Game::updateCamera(u32 busy_time, f32 dtime)
void Game::updatePlayerCAOVisibility()
{
+ // Make the player visible depending on camera mode.
LocalPlayer *player = client->getEnv().getLocalPlayer();
GenericCAO *playercao = player->getCAO();
if (!playercao)
return;
- playercao->setVisible(camera->getCameraMode() > CAMERA_MODE_FIRST || g_settings->getBool("freecam"));
- playercao->setChildrenVisible(camera->getCameraMode() > CAMERA_MODE_FIRST || g_settings->getBool("freecam"));
+ playercao->updateMeshCulling();
+ playercao->setChildrenVisible(camera->getCameraMode() > CAMERA_MODE_FIRST || g_settings->getBool("freecam"));
}
void Game::updateSound(f32 dtime)
@@ -2348,7 +2332,7 @@ void Game::processPlayerInteraction(f32 dtime, bool show_hud, bool show_debug)
PointedThing pointed = updatePointedThing(shootline,
selected_def.liquids_pointable,
- !runData.ldown_for_dig,
+ !runData.btn_down_for_dig,
camera_offset);
if (pointed != runData.pointed_old) {
@@ -2356,20 +2340,18 @@ void Game::processPlayerInteraction(f32 dtime, bool show_hud, bool show_debug)
hud->updateSelectionMesh(camera_offset);
}
- if (runData.digging_blocked && !input->getLeftState()) {
- // allow digging again if button is not pressed
+ // Allow digging again if button is not pressed
+ if (runData.digging_blocked && !isKeyDown(KeyType::DIG))
runData.digging_blocked = false;
- }
/*
Stop digging when
- - releasing left mouse button
+ - releasing dig button
- pointing away from node
*/
if (runData.digging) {
- if (input->getLeftReleased()) {
- infostream << "Left button released"
- << " (stopped digging)" << std::endl;
+ if (wasKeyReleased(KeyType::DIG)) {
+ infostream << "Dig button released (stopped digging)" << std::endl;
runData.digging = false;
} else if (pointed != runData.pointed_old) {
if (pointed.type == POINTEDTHING_NODE
@@ -2379,8 +2361,7 @@ void Game::processPlayerInteraction(f32 dtime, bool show_hud, bool show_debug)
// Still pointing to the same node, but a different face.
// Don't reset.
} else {
- infostream << "Pointing away from node"
- << " (stopped digging)" << std::endl;
+ infostream << "Pointing away from node (stopped digging)" << std::endl;
runData.digging = false;
hud->updateSelectionMesh(camera_offset);
}
@@ -2391,55 +2372,57 @@ 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 && input->getLeftReleased()) {
- // Remove e.g. torches faster when clicking instead of holding LMB
+ } else if (runData.dig_instantly && wasKeyReleased(KeyType::DIG)) {
+ // Remove e.g. torches faster when clicking instead of holding dig button
runData.nodig_delay_timer = 0;
runData.dig_instantly = false;
}
- if (!runData.digging && runData.ldown_for_dig && !input->getLeftState()) {
- runData.ldown_for_dig = false;
- }
+ if (!runData.digging && runData.btn_down_for_dig && !isKeyDown(KeyType::DIG))
+ runData.btn_down_for_dig = false;
- runData.left_punch = false;
+ runData.punching = false;
soundmaker->m_player_leftpunch_sound.name = "";
// Prepare for repeating, unless we're not supposed to
- if ((input->getRightState() || g_settings->getBool("autoplace")) && !g_settings->getBool("safe_dig_and_place"))
- runData.repeat_rightclick_timer += dtime;
+ if ((isKeyDown(KeyType::PLACE) || g_settings->getBool("autoplace")) && !g_settings->getBool("safe_dig_and_place"))
+ runData.repeat_place_timer += dtime;
else
- runData.repeat_rightclick_timer = 0;
+ runData.repeat_place_timer = 0;
- if (selected_def.usable && input->getLeftState()) {
- if (input->getLeftClicked() && (!client->modsLoaded()
- || !client->getScript()->on_item_use(selected_item, pointed)))
+ if (selected_def.usable && isKeyDown(KeyType::DIG)) {
+ if (wasKeyPressed(KeyType::DIG) && (!client->modsLoaded() ||
+ !client->getScript()->on_item_use(selected_item, pointed)))
client->interact(INTERACT_USE, pointed);
} else if (pointed.type == POINTEDTHING_NODE) {
handlePointingAtNode(pointed, selected_item, hand_item, dtime);
} else if (pointed.type == POINTEDTHING_OBJECT) {
v3f player_position = player->getPosition();
handlePointingAtObject(pointed, tool_item, player_position, show_debug);
- } else if (input->getLeftState()) {
+ } else if (isKeyDown(KeyType::DIG)) {
// When button is held down in air, show continuous animation
- runData.left_punch = true;
+ runData.punching = true;
// Run callback even though item is not usable
- if (input->getLeftClicked() && client->modsLoaded())
+ if (wasKeyPressed(KeyType::DIG) && client->modsLoaded())
client->getScript()->on_item_use(selected_item, pointed);
- } else if (input->getRightClicked()) {
+ } else if (wasKeyPressed(KeyType::PLACE)) {
handlePointingAtNothing(selected_item);
}
runData.pointed_old = pointed;
- if (runData.left_punch || input->getLeftClicked())
- camera->setDigging(0); // left click animation
+ if (runData.punching || wasKeyPressed(KeyType::DIG))
+ camera->setDigging(0); // dig animation
+
+ input->clearWasKeyPressed();
+ input->clearWasKeyReleased();
- input->resetLeftClicked();
- input->resetRightClicked();
+ input->joystick.clearWasKeyDown(KeyType::DIG);
+ input->joystick.clearWasKeyDown(KeyType::PLACE);
- input->resetLeftReleased();
- input->resetRightReleased();
+ input->joystick.clearWasKeyReleased(KeyType::DIG);
+ input->joystick.clearWasKeyReleased(KeyType::PLACE);
}
@@ -2537,7 +2520,7 @@ PointedThing Game::updatePointedThing(
void Game::handlePointingAtNothing(const ItemStack &playerItem)
{
- infostream << "Right Clicked in Air" << std::endl;
+ infostream << "Attempted to place item while pointing at nothing" << std::endl;
PointedThing fauxPointed;
fauxPointed.type = POINTEDTHING_NOTHING;
client->interact(INTERACT_ACTIVATE, fauxPointed);
@@ -2556,7 +2539,7 @@ void Game::handlePointingAtNode(const PointedThing &pointed,
ClientMap &map = client->getEnv().getClientMap();
- if (((runData.nodig_delay_timer <= 0.0 || g_settings->getBool("fastdig")) && (input->getLeftState() || g_settings->getBool("autodig"))
+ if (((runData.nodig_delay_timer <= 0.0 || g_settings->getBool("fastdig")) && (isKeyDown(KeyType::DIG) || g_settings->getBool("autodig"))
&& !runData.digging_blocked
&& client->checkPrivilege("interact"))
) {
@@ -2578,14 +2561,14 @@ void Game::handlePointingAtNode(const PointedThing &pointed,
}
}
- if ((input->getRightState() || g_settings->getBool("autoplace")) &&
- (input->getRightClicked() ||
- (runData.repeat_rightclick_timer >= (g_settings->getBool("fastplace") ? 0 : m_repeat_right_click_time))) &&
+ if (((wasKeyPressed(KeyType::PLACE) || g_settings->getBool("autoplace")) ||
+ (runData.repeat_place_timer >= (g_settings->getBool("fastplace") ? 0 : m_repeat_place_time))) &&
client->checkPrivilege("interact")) {
- runData.repeat_rightclick_timer = 0;
- infostream << "Ground right-clicked" << std::endl;
+ runData.repeat_place_timer = 0;
+ infostream << "Place button pressed while looking at ground" << std::endl;
- camera->setDigging(1); // right click animation (always shown for feedback)
+ // Placing animation (always shown for feedback)
+ camera->setDigging(1);
soundmaker->m_player_rightpunch_sound = SimpleSoundSpec();
@@ -2636,7 +2619,7 @@ bool Game::nodePlacement(const ItemDefinition &selected_def,
auto *&formspec = m_game_ui->updateFormspec("");
GUIFormSpecMenu::create(formspec, client, &input->joystick, fs_src,
- txt_dst, client->getFormspecPrepend());
+ txt_dst, client->getFormspecPrepend(), sound);
formspec->setFormSpec(meta->getString("formspec"), inventoryloc);
return false;
@@ -2651,8 +2634,7 @@ bool Game::nodePlacement(const ItemDefinition &selected_def,
}
verbosestream << "Node placement prediction for "
- << selected_def.name << " is "
- << prediction << std::endl;
+ << selected_def.name << " is " << prediction << std::endl;
v3s16 p = neighbourpos;
// Place inside node itself if buildable_to
@@ -2663,6 +2645,7 @@ bool Game::nodePlacement(const ItemDefinition &selected_def,
} else {
node = map.getNode(p, &is_valid_position);
if (is_valid_position && !nodedef->get(node).buildable_to) {
+ soundmaker->m_player_rightpunch_sound = selected_def.sound_place_failed;
// Report to server
client->interact(INTERACT_PLACE, pointed);
return false;
@@ -2735,6 +2718,7 @@ bool Game::nodePlacement(const ItemDefinition &selected_def,
pp = p + v3s16(0, -1, 0);
if (!nodedef->get(map.getNode(pp)).walkable) {
+ soundmaker->m_player_rightpunch_sound = selected_def.sound_place_failed;
// Report to server
client->interact(INTERACT_PLACE, pointed);
return false;
@@ -2811,7 +2795,7 @@ void Game::handlePointingAtObject(const PointedThing &pointed,
m_game_ui->setInfoText(infotext);
- if (input->getLeftState() || g_settings->getBool("autohit")) {
+ if (isKeyDown(KeyType::DIG) || g_settings->getBool("autohit")) {
bool do_punch = false;
bool do_punch_damage = false;
@@ -2821,12 +2805,12 @@ void Game::handlePointingAtObject(const PointedThing &pointed,
runData.object_hit_delay_timer = object_hit_delay;
}
- if (input->getLeftClicked())
+ if (wasKeyPressed(KeyType::DIG))
do_punch = true;
if (do_punch) {
- infostream << "Left-clicked object" << std::endl;
- runData.left_punch = true;
+ infostream << "Punched object" << std::endl;
+ runData.punching = true;
}
if (do_punch_damage) {
@@ -2842,8 +2826,8 @@ void Game::handlePointingAtObject(const PointedThing &pointed,
client->interact(INTERACT_START_DIGGING, pointed);
}
}
- } else if (input->getRightClicked()) {
- infostream << "Right-clicked object" << std::endl;
+ } else if (wasKeyDown(KeyType::PLACE)) {
+ infostream << "Pressed place button while pointing at object" << std::endl;
client->interact(INTERACT_PLACE, pointed); // place
}
}
@@ -2893,7 +2877,7 @@ void Game::handleDigging(const PointedThing &pointed, const v3s16 &nodepos,
return;
client->interact(INTERACT_START_DIGGING, pointed);
runData.digging = true;
- runData.ldown_for_dig = true;
+ runData.btn_down_for_dig = true;
}
if (!runData.dig_instantly) {
@@ -2987,10 +2971,9 @@ void Game::handleDigging(const PointedThing &pointed, const v3s16 &nodepos,
client->setCrack(-1, nodepos);
}
- camera->setDigging(0); // left click animation
+ camera->setDigging(0); // Dig animation
}
-
void Game::updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime,
const CameraOrientation &cam)
{
@@ -3242,7 +3225,7 @@ void Game::updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime,
/*
Update minimap pos and rotation
*/
- if (mapper && m_game_ui->m_flags.show_minimap && m_game_ui->m_flags.show_hud) {
+ if (mapper && m_game_ui->m_flags.show_hud) {
mapper->setPos(floatToInt(player->getPosition(), BS));
mapper->setAngle(player->getYaw());
}
@@ -3250,7 +3233,27 @@ void Game::updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime,
/*
End scene
*/
-
+ if (++m_reset_HW_buffer_counter > 500) {
+ /*
+ Periodically remove all mesh HW buffers.
+
+ Work around for a quirk in Irrlicht where a HW buffer is only
+ released after 20000 iterations (triggered from endScene()).
+
+ Without this, all loaded but unused meshes will retain their HW
+ buffers for at least 5 minutes, at which point looking up the HW buffers
+ becomes a bottleneck and the framerate drops (as much as 30%).
+
+ Tests showed that numbers between 50 and 1000 are good, so picked 500.
+ There are no other public Irrlicht APIs that allow interacting with the
+ HW buffers without tracking the status of every individual mesh.
+
+ The HW buffers for _visible_ meshes will be reinitialized in the next frame.
+ */
+ infostream << "Game::updateFrame(): Removing all HW buffers." << std::endl;
+ driver->removeAllHardwareBuffers();
+ m_reset_HW_buffer_counter = 0;
+ }
driver->endScene();
stats->drawtime = tt_draw.stop(true);
@@ -3286,9 +3289,10 @@ inline void Game::limitFps(FpsControl *fps_timings, f32 *dtime)
else
fps_timings->busy_time = 0;
- u32 frametime_min = 1000 / (g_menumgr.pausesGame()
- ? g_settings->getFloat("pause_fps_max")
- : g_settings->getFloat("fps_max"));
+ u32 frametime_min = 1000 / (
+ device->isWindowFocused() && !g_menumgr.pausesGame()
+ ? g_settings->getFloat("fps_max")
+ : g_settings->getFloat("fps_max_unfocused"));
if (fps_timings->busy_time < frametime_min) {
fps_timings->sleep_time = frametime_min - fps_timings->busy_time;
@@ -3354,7 +3358,7 @@ void Game::readSettings()
m_cache_enable_fog = g_settings->getBool("enable_fog");
m_cache_mouse_sensitivity = g_settings->getFloat("mouse_sensitivity");
m_cache_joystick_frustum_sensitivity = g_settings->getFloat("joystick_frustum_sensitivity");
- m_repeat_right_click_time = g_settings->getFloat("repeat_rightclick_time");
+ m_repeat_place_time = g_settings->getFloat("repeat_place_time");
m_cache_enable_noclip = g_settings->getBool("noclip");
m_cache_enable_free_move = g_settings->getBool("free_move");
@@ -3419,7 +3423,7 @@ void Game::showDeathFormspec()
auto *&formspec = m_game_ui->getFormspecGUI();
GUIFormSpecMenu::create(formspec, client, &input->joystick,
- fs_src, txt_dst, client->getFormspecPrepend());
+ fs_src, txt_dst, client->getFormspecPrepend(), sound);
formspec->setFocus("btn_respawn");
}
@@ -3446,14 +3450,14 @@ void Game::showPauseMenu()
"- %s: move backwards\n"
"- %s: move left\n"
"- %s: move right\n"
- "- %s: jump/climb\n"
- "- %s: sneak/go down\n"
+ "- %s: jump/climb up\n"
+ "- %s: dig/punch\n"
+ "- %s: place/use\n"
+ "- %s: sneak/climb down\n"
"- %s: drop item\n"
"- %s: inventory\n"
"- %s: enderchest\n"
"- Mouse: turn/look\n"
- "- Mouse left: dig/punch\n"
- "- Mouse right: place/use\n"
"- Mouse wheel: select item\n"
"- %s: chat\n"
"- %s: Killaura\n"
@@ -3470,6 +3474,8 @@ void Game::showPauseMenu()
GET_KEY_NAME(keymap_left),
GET_KEY_NAME(keymap_right),
GET_KEY_NAME(keymap_jump),
+ GET_KEY_NAME(keymap_dig),
+ GET_KEY_NAME(keymap_place),
GET_KEY_NAME(keymap_sneak),
GET_KEY_NAME(keymap_drop),
GET_KEY_NAME(keymap_inventory),
@@ -3561,7 +3567,7 @@ void Game::showPauseMenu()
auto *&formspec = m_game_ui->getFormspecGUI();
GUIFormSpecMenu::create(formspec, client, &input->joystick,
- fs_src, txt_dst, client->getFormspecPrepend());
+ fs_src, txt_dst, client->getFormspecPrepend(), sound);
formspec->setFocus("btn_continue");
formspec->doPause = true;
}
diff --git a/src/client/game.h b/src/client/game.h
index 51accc679..26117ab86 100644
--- a/src/client/game.h
+++ b/src/client/game.h
@@ -295,7 +295,7 @@ public:
m_sound(sound),
m_ndef(ndef),
makes_footstep_sound(true),
- m_player_step_timer(0),
+ m_player_step_timer(0.0f),
m_player_jump_timer(0.0f)
{
}
@@ -441,14 +441,13 @@ class GameGlobalShaderConstantSetter : public IShaderConstantSetter
CachedVertexShaderSetting<float> m_animation_timer_vertex;
CachedPixelShaderSetting<float> m_animation_timer_pixel;
CachedPixelShaderSetting<float, 3> m_day_light;
+ CachedPixelShaderSetting<float, 4> m_star_color;
CachedPixelShaderSetting<float, 3> m_eye_position_pixel;
CachedVertexShaderSetting<float, 3> m_eye_position_vertex;
CachedPixelShaderSetting<float, 3> m_minimap_yaw;
CachedPixelShaderSetting<float, 3> m_camera_offset_pixel;
CachedPixelShaderSetting<float, 3> m_camera_offset_vertex;
CachedPixelShaderSetting<SamplerLayer_t> m_base_texture;
- CachedPixelShaderSetting<SamplerLayer_t> m_normal_texture;
- CachedPixelShaderSetting<SamplerLayer_t> m_texture_flags;
Client *m_client;
public:
@@ -475,14 +474,13 @@ public:
m_animation_timer_vertex("animationTimer"),
m_animation_timer_pixel("animationTimer"),
m_day_light("dayLight"),
+ m_star_color("starColor"),
m_eye_position_pixel("eyePosition"),
m_eye_position_vertex("eyePosition"),
m_minimap_yaw("yawVec"),
m_camera_offset_pixel("cameraOffset"),
m_camera_offset_vertex("cameraOffset"),
m_base_texture("baseTexture"),
- m_normal_texture("normalTexture"),
- m_texture_flags("textureFlags"),
m_client(client)
{
g_settings->registerChangedCallback("enable_fog", settingsCallback, this);
@@ -528,6 +526,10 @@ public:
sunlight.b };
m_day_light.set(dnc, services);
+ video::SColorf star_color = m_sky->getCurrentStarColor();
+ float clr[4] = {star_color.r, star_color.g, star_color.b, star_color.a};
+ m_star_color.set(clr, services);
+
u32 animation_timer = porting::getTimeMs() % 1000000;
float animation_timer_f = (float)animation_timer / 100000.f;
m_animation_timer_vertex.set(&animation_timer_f, services);
@@ -570,12 +572,8 @@ public:
m_camera_offset_pixel.set(camera_offset_array, services);
m_camera_offset_vertex.set(camera_offset_array, services);
- SamplerLayer_t base_tex = 0,
- normal_tex = 1,
- flags_tex = 2;
+ SamplerLayer_t base_tex = 0;
m_base_texture.set(&base_tex, services);
- m_normal_texture.set(&normal_tex, services);
- m_texture_flags.set(&flags_tex, services);
}
};
@@ -621,7 +619,6 @@ public:
#endif
/****************************************************************************
-
****************************************************************************/
const float object_hit_delay = 0.2;
@@ -642,7 +639,8 @@ struct GameRunData {
u16 new_playeritem;
PointedThing pointed_old;
bool digging;
- bool ldown_for_dig;
+ bool punching;
+ bool btn_down_for_dig;
bool dig_instantly;
bool digging_blocked;
bool left_punch;
@@ -650,7 +648,7 @@ struct GameRunData {
float nodig_delay_timer;
float dig_time;
float dig_time_complete;
- float repeat_rightclick_timer;
+ float repeat_place_timer;
float object_hit_delay_timer;
float time_from_last_punch;
ClientActiveObject *selected_object;
@@ -802,6 +800,14 @@ public:
{
return input->wasKeyDown(k);
}
+ inline bool wasKeyPressed(GameKeyType k)
+ {
+ return input->wasKeyPressed(k);
+ }
+ inline bool wasKeyReleased(GameKeyType k)
+ {
+ return input->wasKeyReleased(k);
+ }
#ifdef __ANDROID__
void handleAndroidChatInput();
@@ -891,7 +897,6 @@ public:
bool *reconnect_requested;
scene::ISceneNode *skybox;
- bool random_input;
bool simple_singleplayer_mode;
/* End 'cache' */
@@ -916,7 +921,7 @@ public:
bool m_cache_enable_free_move;
f32 m_cache_mouse_sensitivity;
f32 m_cache_joystick_frustum_sensitivity;
- f32 m_repeat_right_click_time;
+ f32 m_repeat_place_time;
f32 m_cache_cam_smoothing;
f32 m_cache_fog_start;
@@ -929,6 +934,7 @@ public:
CameraOrientation cam_view_target = { 0 };
CameraOrientation cam_view = { 0 };
+ int m_reset_HW_buffer_counter = 0;
#ifdef __ANDROID__
bool m_cache_hold_aux1;
bool m_android_chat_open;
diff --git a/src/client/gameui.cpp b/src/client/gameui.cpp
index a9057052e..0c1da3915 100644
--- a/src/client/gameui.cpp
+++ b/src/client/gameui.cpp
@@ -64,17 +64,6 @@ void GameUI::init()
m_guitext2 = gui::StaticText::add(guienv, L"", core::rect<s32>(0, 0, 0, 0), false,
false, guiroot);
- // At the middle of the screen
- // Object infos are shown in this
- m_guitext_info = gui::StaticText::add(guienv, L"",
- core::rect<s32>(0, 0, 400, g_fontengine->getTextHeight() * 5 + 5)
- + v2s32(100, 200), false, true, guiroot);
-
- // Status text (displays info when showing and hiding GUI stuff, etc.)
- m_guitext_status = gui::StaticText::add(guienv, L"<Status>",
- core::rect<s32>(0, 0, 0, 0), false, false, guiroot);
- m_guitext_status->setVisible(false);
-
// Chat text
m_guitext_chat = gui::StaticText::add(guienv, L"", core::rect<s32>(0, 0, 0, 0),
//false, false); // Disable word wrap as of now
@@ -85,6 +74,20 @@ void GameUI::init()
chat_font_size, FM_Unspecified));
}
+ // At the middle of the screen
+ // Object infos are shown in this
+ u32 chat_font_height = m_guitext_chat->getActiveFont()->getDimension(L"Ay").Height;
+ m_guitext_info = gui::StaticText::add(guienv, L"",
+ core::rect<s32>(0, 0, 400, g_fontengine->getTextHeight() * 5 + 5) +
+ v2s32(100, chat_font_height *
+ (g_settings->getU16("recent_chat_messages") + 3)),
+ false, true, guiroot);
+
+ // Status text (displays info when showing and hiding GUI stuff, etc.)
+ m_guitext_status = gui::StaticText::add(guienv, L"<Status>",
+ core::rect<s32>(0, 0, 0, 0), false, false, guiroot);
+ m_guitext_status->setVisible(false);
+
// Profiler text (size is updated when text is updated)
m_guitext_profiler = gui::StaticText::add(guienv, L"<Profiler>",
core::rect<s32>(0, 0, 0, 0), false, false, guiroot);
@@ -132,8 +135,8 @@ void GameUI::update(const RunStats &stats, Client *client, MapDrawControl *draw_
<< std::setprecision(1)
<< " | view range: "
<< (draw_control->range_all ? "All" : itos(draw_control->wanted_range))
- << std::setprecision(3)
- << " | RTT: " << client->getRTT() << "s";
+ << std::setprecision(2)
+ << " | RTT: " << (client->getRTT() * 1000.0f) << "ms";
setStaticText(m_guitext, utf8_to_wide(os.str()).c_str());
m_guitext->setRelativePosition(core::rect<s32>(5, 5, screensize.X,
@@ -149,9 +152,9 @@ void GameUI::update(const RunStats &stats, Client *client, MapDrawControl *draw_
<< "pos: (" << (player_position.X / BS)
<< ", " << (player_position.Y / BS)
<< ", " << (player_position.Z / BS)
- << ") | yaw: " << (wrapDegrees_0_360(cam.camera_yaw)) << "\xC2\xB0 "
+ << ") | yaw: " << (wrapDegrees_0_360(cam.camera_yaw)) << "° "
<< yawToDirectionString(cam.camera_yaw)
- << " | pitch: " << (-wrapDegrees_180(cam.camera_pitch)) << "\xC2\xB0"
+ << " | pitch: " << (-wrapDegrees_180(cam.camera_pitch)) << "°"
<< " | seed: " << ((u64)client->getMapSeed());
if (pointed_old.type == POINTEDTHING_NODE) {
diff --git a/src/client/guiscalingfilter.cpp b/src/client/guiscalingfilter.cpp
index 4262331bd..406c096e6 100644
--- a/src/client/guiscalingfilter.cpp
+++ b/src/client/guiscalingfilter.cpp
@@ -172,11 +172,8 @@ void draw2DImageFilterScaled(video::IVideoDriver *driver, video::ITexture *txr,
void draw2DImage9Slice(video::IVideoDriver *driver, video::ITexture *texture,
const core::rect<s32> &rect, const core::rect<s32> &middle,
- const core::rect<s32> *cliprect)
+ const core::rect<s32> *cliprect, const video::SColor *const colors)
{
- const video::SColor color(255,255,255,255);
- const video::SColor colors[] = {color,color,color,color};
-
auto originalSize = texture->getOriginalSize();
core::vector2di lowerRightOffset = core::vector2di(originalSize.Width, originalSize.Height) - middle.LowerRightCorner;
diff --git a/src/client/guiscalingfilter.h b/src/client/guiscalingfilter.h
index b703d91f0..379a4bdb0 100644
--- a/src/client/guiscalingfilter.h
+++ b/src/client/guiscalingfilter.h
@@ -54,4 +54,5 @@ void draw2DImageFilterScaled(video::IVideoDriver *driver, video::ITexture *txr,
*/
void draw2DImage9Slice(video::IVideoDriver *driver, video::ITexture *texture,
const core::rect<s32> &rect, const core::rect<s32> &middle,
- const core::rect<s32> *cliprect = nullptr);
+ const core::rect<s32> *cliprect = nullptr,
+ const video::SColor *const colors = nullptr);
diff --git a/src/client/hud.cpp b/src/client/hud.cpp
index 2b347c1e0..f6497fe25 100644
--- a/src/client/hud.cpp
+++ b/src/client/hud.cpp
@@ -36,6 +36,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "mesh.h"
#include "wieldmesh.h"
#include "client/renderingengine.h"
+#include "client/minimap.h"
#ifdef HAVE_TOUCHSCREENGUI
#include "gui/touchscreengui.h"
@@ -114,6 +115,28 @@ Hud::Hud(gui::IGUIEnvironment *guienv, Client *client, LocalPlayer *player,
} else {
m_selection_material.MaterialType = video::EMT_SOLID;
}
+
+ // Prepare mesh for compass drawing
+ m_rotation_mesh_buffer.Vertices.set_used(4);
+ m_rotation_mesh_buffer.Indices.set_used(6);
+
+ video::SColor white(255, 255, 255, 255);
+ v3f normal(0.f, 0.f, 1.f);
+
+ m_rotation_mesh_buffer.Vertices[0] = video::S3DVertex(v3f(-1.f, -1.f, 0.f), normal, white, v2f(0.f, 1.f));
+ m_rotation_mesh_buffer.Vertices[1] = video::S3DVertex(v3f(-1.f, 1.f, 0.f), normal, white, v2f(0.f, 0.f));
+ m_rotation_mesh_buffer.Vertices[2] = video::S3DVertex(v3f( 1.f, 1.f, 0.f), normal, white, v2f(1.f, 0.f));
+ m_rotation_mesh_buffer.Vertices[3] = video::S3DVertex(v3f( 1.f, -1.f, 0.f), normal, white, v2f(1.f, 1.f));
+
+ m_rotation_mesh_buffer.Indices[0] = 0;
+ m_rotation_mesh_buffer.Indices[1] = 1;
+ m_rotation_mesh_buffer.Indices[2] = 2;
+ m_rotation_mesh_buffer.Indices[3] = 2;
+ m_rotation_mesh_buffer.Indices[4] = 3;
+ m_rotation_mesh_buffer.Indices[5] = 0;
+
+ m_rotation_mesh_buffer.getMaterial().Lighting = false;
+ m_rotation_mesh_buffer.getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
}
Hud::~Hud()
@@ -275,6 +298,18 @@ void Hud::drawItems(v2s32 upperleftpos, v2s32 screen_offset, s32 itemcount,
}
}
+bool Hud::hasElementOfType(HudElementType type)
+{
+ for (size_t i = 0; i != player->maxHudId(); i++) {
+ HudElement *e = player->getHud(i);
+ if (!e)
+ continue;
+ if (e->type == type)
+ return true;
+ }
+ return false;
+}
+
// Calculates screen position of waypoint. Returns true if waypoint is visible (in front of the player), else false.
bool Hud::calculateScreenPos(const v3s16 &camera_offset, HudElement *e, v2s32 *pos)
{
@@ -423,6 +458,69 @@ void Hud::drawLuaElements(const v3s16 &camera_offset)
core::rect<s32>(core::position2d<s32>(0,0), imgsize),
NULL, colors, true);
break; }
+ case HUD_ELEM_COMPASS: {
+ video::ITexture *texture = tsrc->getTexture(e->text);
+ if (!texture)
+ continue;
+
+ // Positionning :
+ v2s32 dstsize(e->size.X, e->size.Y);
+ if (e->size.X < 0)
+ dstsize.X = m_screensize.X * (e->size.X * -0.01);
+ if (e->size.Y < 0)
+ dstsize.Y = m_screensize.Y * (e->size.Y * -0.01);
+
+ if (dstsize.X <= 0 || dstsize.Y <= 0)
+ return; // Avoid zero divides
+
+ // Angle according to camera view
+ v3f fore(0.f, 0.f, 1.f);
+ scene::ICameraSceneNode *cam = RenderingEngine::get_scene_manager()->getActiveCamera();
+ cam->getAbsoluteTransformation().rotateVect(fore);
+ int angle = - fore.getHorizontalAngle().Y;
+
+ // Limit angle and ajust with given offset
+ angle = (angle + (int)e->number) % 360;
+
+ core::rect<s32> dstrect(0, 0, dstsize.X, dstsize.Y);
+ dstrect += pos + v2s32(
+ (e->align.X - 1.0) * dstsize.X / 2,
+ (e->align.Y - 1.0) * dstsize.Y / 2) +
+ v2s32(e->offset.X * m_hud_scaling, e->offset.Y * m_hud_scaling);
+
+ switch (e->dir) {
+ case HUD_COMPASS_ROTATE:
+ drawCompassRotate(e, texture, dstrect, angle);
+ break;
+ case HUD_COMPASS_ROTATE_REVERSE:
+ drawCompassRotate(e, texture, dstrect, -angle);
+ break;
+ case HUD_COMPASS_TRANSLATE:
+ drawCompassTranslate(e, texture, dstrect, angle);
+ break;
+ case HUD_COMPASS_TRANSLATE_REVERSE:
+ drawCompassTranslate(e, texture, dstrect, -angle);
+ break;
+ default:
+ break;
+ }
+ break; }
+ case HUD_ELEM_MINIMAP: {
+ if (e->size.X <= 0 || e->size.Y <= 0)
+ break;
+ if (!client->getMinimap())
+ break;
+ // Draw a minimap of size "size"
+ v2s32 dstsize(e->size.X * m_scale_factor,
+ e->size.Y * m_scale_factor);
+ // (no percent size as minimap would likely be anamorphosed)
+ v2s32 offset((e->align.X - 1.0) * dstsize.X / 2,
+ (e->align.Y - 1.0) * dstsize.Y / 2);
+ core::rect<s32> rect(0, 0, dstsize.X, dstsize.Y);
+ rect += pos + offset + v2s32(e->offset.X * m_scale_factor,
+ e->offset.Y * m_scale_factor);
+ client->getMinimap()->drawMinimap(rect);
+ break; }
default:
infostream << "Hud::drawLuaElements: ignoring drawform " << e->type <<
" of hud element ID " << i << " due to unrecognized type" << std::endl;
@@ -430,6 +528,76 @@ void Hud::drawLuaElements(const v3s16 &camera_offset)
}
}
+void Hud::drawCompassTranslate(HudElement *e, video::ITexture *texture,
+ const core::rect<s32> &rect, int angle)
+{
+ const video::SColor color(255, 255, 255, 255);
+ const video::SColor colors[] = {color, color, color, color};
+
+ // Compute source image scaling
+ core::dimension2di imgsize(texture->getOriginalSize());
+ core::rect<s32> srcrect(0, 0, imgsize.Width, imgsize.Height);
+
+ v2s32 dstsize(rect.getHeight() * e->scale.X * imgsize.Width / imgsize.Height,
+ rect.getHeight() * e->scale.Y);
+
+ // Avoid infinite loop
+ if (dstsize.X <= 0 || dstsize.Y <= 0)
+ return;
+
+ core::rect<s32> tgtrect(0, 0, dstsize.X, dstsize.Y);
+ tgtrect += v2s32(
+ (rect.getWidth() - dstsize.X) / 2,
+ (rect.getHeight() - dstsize.Y) / 2) +
+ rect.UpperLeftCorner;
+
+ int offset = angle * dstsize.X / 360;
+
+ tgtrect += v2s32(offset, 0);
+
+ // Repeat image as much as needed
+ while (tgtrect.UpperLeftCorner.X > rect.UpperLeftCorner.X)
+ tgtrect -= v2s32(dstsize.X, 0);
+
+ draw2DImageFilterScaled(driver, texture, tgtrect, srcrect, &rect, colors, true);
+ tgtrect += v2s32(dstsize.X, 0);
+
+ while (tgtrect.UpperLeftCorner.X < rect.LowerRightCorner.X) {
+ draw2DImageFilterScaled(driver, texture, tgtrect, srcrect, &rect, colors, true);
+ tgtrect += v2s32(dstsize.X, 0);
+ }
+}
+
+void Hud::drawCompassRotate(HudElement *e, video::ITexture *texture,
+ const core::rect<s32> &rect, int angle)
+{
+ core::dimension2di imgsize(texture->getOriginalSize());
+
+ core::rect<s32> oldViewPort = driver->getViewPort();
+ core::matrix4 oldProjMat = driver->getTransform(video::ETS_PROJECTION);
+ core::matrix4 oldViewMat = driver->getTransform(video::ETS_VIEW);
+
+ core::matrix4 Matrix;
+ Matrix.makeIdentity();
+ Matrix.setRotationDegrees(v3f(0.f, 0.f, angle));
+
+ driver->setViewPort(rect);
+ driver->setTransform(video::ETS_PROJECTION, core::matrix4());
+ driver->setTransform(video::ETS_VIEW, core::matrix4());
+ driver->setTransform(video::ETS_WORLD, Matrix);
+
+ video::SMaterial &material = m_rotation_mesh_buffer.getMaterial();
+ material.TextureLayer[0].Texture = texture;
+ driver->setMaterial(material);
+ driver->drawMeshBuffer(&m_rotation_mesh_buffer);
+
+ driver->setTransform(video::ETS_WORLD, core::matrix4());
+ driver->setTransform(video::ETS_VIEW, oldViewMat);
+ driver->setTransform(video::ETS_PROJECTION, oldProjMat);
+
+ // restore the view area
+ driver->setViewPort(oldViewPort);
+}
void Hud::drawStatbar(v2s32 pos, u16 corner, u16 drawdir,
const std::string &texture, const std::string &bgtexture,
diff --git a/src/client/hud.h b/src/client/hud.h
index ba34d479d..d46545d71 100644
--- a/src/client/hud.h
+++ b/src/client/hud.h
@@ -81,6 +81,8 @@ public:
m_selected_face_normal = face_normal;
}
+ bool hasElementOfType(HudElementType type);
+
void drawLuaElements(const v3s16 &camera_offset);
private:
@@ -95,6 +97,12 @@ private:
void drawItem(const ItemStack &item, const core::rect<s32> &rect, bool selected);
+ void drawCompassTranslate(HudElement *e, video::ITexture *texture,
+ const core::rect<s32> &rect, int way);
+
+ void drawCompassRotate(HudElement *e, video::ITexture *texture,
+ const core::rect<s32> &rect, int way);
+
float m_hud_scaling; // cached minetest setting
float m_scale_factor;
v3s16 m_camera_offset;
@@ -115,6 +123,8 @@ private:
video::SMaterial m_selection_material;
+ scene::SMeshBuffer m_rotation_mesh_buffer;
+
enum
{
HIGHLIGHT_BOX,
diff --git a/src/client/inputhandler.cpp b/src/client/inputhandler.cpp
index 418e41931..37ab838c0 100644
--- a/src/client/inputhandler.cpp
+++ b/src/client/inputhandler.cpp
@@ -37,6 +37,8 @@ void KeyCache::populate()
key[KeyType::JUMP] = getKeySetting("keymap_jump");
key[KeyType::SPECIAL1] = getKeySetting("keymap_special1");
key[KeyType::SNEAK] = getKeySetting("keymap_sneak");
+ key[KeyType::DIG] = getKeySetting("keymap_dig");
+ key[KeyType::PLACE] = getKeySetting("keymap_place");
key[KeyType::AUTOFORWARD] = getKeySetting("keymap_autoforward");
@@ -122,57 +124,81 @@ bool MyEventReceiver::OnEvent(const SEvent &event)
if (event.EventType == irr::EET_KEY_INPUT_EVENT) {
const KeyPress &keyCode = event.KeyInput;
if (keysListenedFor[keyCode]) {
+ // If the key is being held down then the OS may
+ // send a continuous stream of keydown events.
+ // In this case, we don't want to let this
+ // stream reach the application as it will cause
+ // certain actions to repeat constantly.
if (event.KeyInput.PressedDown) {
+ if (!IsKeyDown(keyCode)) {
+ keyWasDown.set(keyCode);
+ keyWasPressed.set(keyCode);
+ }
keyIsDown.set(keyCode);
- keyWasDown.set(keyCode);
} else {
+ if (IsKeyDown(keyCode))
+ keyWasReleased.set(keyCode);
+
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) {
+ } else if (m_touchscreengui && event.EventType == irr::EET_TOUCH_INPUT_EVENT) {
+ // In case of touchscreengui, we have to handle different events
m_touchscreengui->translateEvent(event);
return true;
- }
#endif
- if (event.EventType == irr::EET_JOYSTICK_INPUT_EVENT) {
+ } else 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_MOUSE_INPUT_EVENT) {
+ // Handle mouse events
+ KeyPress key;
+ switch (event.MouseInput.Event) {
+ case EMIE_LMOUSE_PRESSED_DOWN:
+ key = "KEY_LBUTTON";
+ keyIsDown.set(key);
+ keyWasDown.set(key);
+ keyWasPressed.set(key);
+ break;
+ case EMIE_MMOUSE_PRESSED_DOWN:
+ key = "KEY_MBUTTON";
+ keyIsDown.set(key);
+ keyWasDown.set(key);
+ keyWasPressed.set(key);
+ break;
+ case EMIE_RMOUSE_PRESSED_DOWN:
+ key = "KEY_RBUTTON";
+ keyIsDown.set(key);
+ keyWasDown.set(key);
+ keyWasPressed.set(key);
+ break;
+ case EMIE_LMOUSE_LEFT_UP:
+ key = "KEY_LBUTTON";
+ keyIsDown.unset(key);
+ keyWasReleased.set(key);
+ break;
+ case EMIE_MMOUSE_LEFT_UP:
+ key = "KEY_MBUTTON";
+ keyIsDown.unset(key);
+ keyWasReleased.set(key);
+ break;
+ case EMIE_RMOUSE_LEFT_UP:
+ key = "KEY_RBUTTON";
+ keyIsDown.unset(key);
+ keyWasReleased.set(key);
+ break;
+ case EMIE_MOUSE_WHEEL:
+ mouse_wheel += event.MouseInput.Wheel;
+ break;
+ default: break;
}
} else if (event.EventType == irr::EET_LOG_TEXT_EVENT) {
static const LogLevel irr_loglev_conv[] = {
@@ -199,38 +225,28 @@ s32 RandomInputHandler::Rand(s32 min, s32 max)
return (myrand() % (max - min + 1)) + min;
}
+struct RandomInputHandlerSimData {
+ std::string key;
+ float counter;
+ int time_max;
+};
+
void RandomInputHandler::step(float dtime)
{
- {
- static float counter1 = 0;
- counter1 -= dtime;
- if (counter1 < 0.0) {
- counter1 = 0.1 * Rand(1, 40);
- keydown.toggle(getKeySetting("keymap_jump"));
- }
- }
- {
- static float counter1 = 0;
- counter1 -= dtime;
- if (counter1 < 0.0) {
- counter1 = 0.1 * Rand(1, 40);
- keydown.toggle(getKeySetting("keymap_special1"));
- }
- }
- {
- static float counter1 = 0;
- counter1 -= dtime;
- if (counter1 < 0.0) {
- counter1 = 0.1 * Rand(1, 40);
- keydown.toggle(getKeySetting("keymap_forward"));
- }
- }
- {
- static float counter1 = 0;
- counter1 -= dtime;
- if (counter1 < 0.0) {
- counter1 = 0.1 * Rand(1, 40);
- keydown.toggle(getKeySetting("keymap_left"));
+ static RandomInputHandlerSimData rnd_data[] = {
+ { "keymap_jump", 0.0f, 40 },
+ { "keymap_special1", 0.0f, 40 },
+ { "keymap_forward", 0.0f, 40 },
+ { "keymap_left", 0.0f, 40 },
+ { "keymap_dig", 0.0f, 30 },
+ { "keymap_place", 0.0f, 15 }
+ };
+
+ for (auto &i : rnd_data) {
+ i.counter -= dtime;
+ if (i.counter < 0.0) {
+ i.counter = 0.1 * Rand(1, i.time_max);
+ keydown.toggle(getKeySetting(i.key.c_str()));
}
}
{
@@ -241,29 +257,5 @@ void RandomInputHandler::step(float dtime)
mousespeed = v2s32(Rand(-20, 20), Rand(-15, 20));
}
}
- {
- static float counter1 = 0;
- counter1 -= dtime;
- if (counter1 < 0.0) {
- counter1 = 0.1 * Rand(1, 30);
- leftdown = !leftdown;
- if (leftdown)
- leftclicked = true;
- if (!leftdown)
- leftreleased = true;
- }
- }
- {
- static float counter1 = 0;
- counter1 -= dtime;
- if (counter1 < 0.0) {
- counter1 = 0.1 * Rand(1, 15);
- rightdown = !rightdown;
- if (rightdown)
- rightclicked = true;
- if (!rightdown)
- rightreleased = true;
- }
- }
mousepos += mousespeed;
}
diff --git a/src/client/inputhandler.h b/src/client/inputhandler.h
index e006affb2..b3a7d4ba3 100644
--- a/src/client/inputhandler.h
+++ b/src/client/inputhandler.h
@@ -144,6 +144,14 @@ public:
return b;
}
+ // Checks whether a key was just pressed. State will be cleared
+ // in the subsequent iteration of Game::processPlayerInteraction
+ bool WasKeyPressed(const KeyPress &keycode) const { return keyWasPressed[keycode]; }
+
+ // Checks whether a key was just released. State will be cleared
+ // in the subsequent iteration of Game::processPlayerInteraction
+ bool WasKeyReleased(const KeyPress &keycode) const { return keyWasReleased[keycode]; }
+
void listenForKey(const KeyPress &keyCode) { keysListenedFor.set(keyCode); }
void dontListenForKeys() { keysListenedFor.clear(); }
@@ -158,17 +166,20 @@ public:
{
keyIsDown.clear();
keyWasDown.clear();
+ keyWasPressed.clear();
+ keyWasReleased.clear();
- leftclicked = false;
- rightclicked = false;
- leftreleased = false;
- rightreleased = false;
+ mouse_wheel = 0;
+ }
- left_active = false;
- middle_active = false;
- right_active = false;
+ void clearWasKeyPressed()
+ {
+ keyWasPressed.clear();
+ }
- mouse_wheel = 0;
+ void clearWasKeyReleased()
+ {
+ keyWasReleased.clear();
}
MyEventReceiver()
@@ -178,15 +189,6 @@ public:
#endif
}
- bool leftclicked = false;
- bool rightclicked = false;
- bool leftreleased = false;
- bool rightreleased = false;
-
- bool left_active = false;
- bool middle_active = false;
- bool right_active = false;
-
s32 mouse_wheel = 0;
JoystickController *joystick = nullptr;
@@ -197,8 +199,16 @@ public:
// The current state of keys
KeyList keyIsDown;
- // Whether a key has been pressed or not
+
+ // Whether a key was down
KeyList keyWasDown;
+
+ // Whether a key has just been pressed
+ KeyList keyWasPressed;
+
+ // Whether a key has just been released
+ KeyList keyWasReleased;
+
// List of keys we listen for
// TODO perhaps the type of this is not really
// performant as KeyList is designed for few but
@@ -227,27 +237,19 @@ public:
virtual void setKeypress(const KeyPress &keyCode) = 0;
virtual void unsetKeypress(const KeyPress &keyCode) = 0;
virtual bool wasKeyDown(GameKeyType k) = 0;
+ virtual bool wasKeyPressed(GameKeyType k) = 0;
+ virtual bool wasKeyReleased(GameKeyType k) = 0;
virtual bool cancelPressed() = 0;
+ virtual void clearWasKeyPressed() {}
+ virtual void clearWasKeyReleased() {}
+
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) {}
@@ -285,10 +287,26 @@ public:
{
return m_receiver->WasKeyDown(keycache.key[k]) || joystick.wasKeyDown(k);
}
+ virtual bool wasKeyPressed(GameKeyType k)
+ {
+ return m_receiver->WasKeyPressed(keycache.key[k]) || joystick.wasKeyReleased(k);
+ }
+ virtual bool wasKeyReleased(GameKeyType k)
+ {
+ return m_receiver->WasKeyReleased(keycache.key[k]) || joystick.wasKeyReleased(k);
+ }
virtual bool cancelPressed()
{
return wasKeyDown(KeyType::ESC) || m_receiver->WasKeyDown(CancelKey);
}
+ virtual void clearWasKeyPressed()
+ {
+ m_receiver->clearWasKeyPressed();
+ }
+ virtual void clearWasKeyReleased()
+ {
+ m_receiver->clearWasKeyReleased();
+ }
virtual void listenForKey(const KeyPress &keyCode)
{
m_receiver->listenForKey(keyCode);
@@ -316,59 +334,6 @@ public:
}
}
- virtual bool getLeftState()
- {
- return m_receiver->left_active || joystick.isKeyDown(KeyType::MOUSE_L);
- }
- virtual bool getRightState()
- {
- return m_receiver->right_active || joystick.isKeyDown(KeyType::MOUSE_R);
- }
-
- virtual bool getLeftClicked()
- {
- return m_receiver->leftclicked ||
- joystick.getWasKeyDown(KeyType::MOUSE_L);
- }
- virtual bool getRightClicked()
- {
- return m_receiver->rightclicked ||
- joystick.getWasKeyDown(KeyType::MOUSE_R);
- }
-
- virtual void resetLeftClicked()
- {
- m_receiver->leftclicked = false;
- joystick.clearWasKeyDown(KeyType::MOUSE_L);
- }
- virtual void resetRightClicked()
- {
- m_receiver->rightclicked = false;
- joystick.clearWasKeyDown(KeyType::MOUSE_R);
- }
-
- virtual bool getLeftReleased()
- {
- return m_receiver->leftreleased ||
- joystick.wasKeyReleased(KeyType::MOUSE_L);
- }
- virtual bool getRightReleased()
- {
- return m_receiver->rightreleased ||
- joystick.wasKeyReleased(KeyType::MOUSE_R);
- }
-
- virtual void resetLeftReleased()
- {
- m_receiver->leftreleased = false;
- joystick.clearWasKeyReleased(KeyType::MOUSE_L);
- }
- virtual void resetRightReleased()
- {
- m_receiver->rightreleased = false;
- joystick.clearWasKeyReleased(KeyType::MOUSE_R);
- }
-
virtual s32 getMouseWheel() { return m_receiver->getMouseWheel(); }
void clear()
@@ -402,23 +367,12 @@ public:
keydown.unset(keyCode);
}
virtual bool wasKeyDown(GameKeyType k) { return false; }
+ virtual bool wasKeyPressed(GameKeyType k) { return false; }
+ virtual bool wasKeyReleased(GameKeyType k) { return false; }
virtual bool cancelPressed() { 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 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 s32 getMouseWheel() { return 0; }
virtual void step(float dtime);
@@ -429,10 +383,4 @@ private:
KeyList keydown;
v2s32 mousepos;
v2s32 mousespeed;
- bool leftdown = false;
- bool rightdown = false;
- bool leftclicked = false;
- bool rightclicked = false;
- bool leftreleased = false;
- bool rightreleased = false;
};
diff --git a/src/client/joystick_controller.cpp b/src/client/joystick_controller.cpp
index c29e8b639..742115046 100644
--- a/src/client/joystick_controller.cpp
+++ b/src/client/joystick_controller.cpp
@@ -74,8 +74,8 @@ JoystickLayout create_default_layout()
// Accessible without four modifier button pressed
// regardless whether start is pressed or not
- JLO_B_PB(KeyType::MOUSE_L, fb | 1 << 4, 1 << 4);
- JLO_B_PB(KeyType::MOUSE_R, fb | 1 << 5, 1 << 5);
+ JLO_B_PB(KeyType::DIG, fb | 1 << 4, 1 << 4);
+ JLO_B_PB(KeyType::PLACE, fb | 1 << 5, 1 << 5);
// Accessible without any modifier pressed
JLO_B_PB(KeyType::JUMP, bm | 1 << 0, 1 << 0);
@@ -83,9 +83,9 @@ JoystickLayout create_default_layout()
// Accessible with start button not pressed, but four pressed
// TODO find usage for button 0
- JLO_B_PB(KeyType::DROP, bm | 1 << 1, fb | 1 << 1);
- JLO_B_PB(KeyType::SCROLL_UP, bm | 1 << 4, fb | 1 << 4);
- JLO_B_PB(KeyType::SCROLL_DOWN,bm | 1 << 5, fb | 1 << 5);
+ JLO_B_PB(KeyType::DROP, bm | 1 << 1, fb | 1 << 1);
+ JLO_B_PB(KeyType::HOTBAR_PREV, bm | 1 << 4, fb | 1 << 4);
+ JLO_B_PB(KeyType::HOTBAR_NEXT, bm | 1 << 5, fb | 1 << 5);
// Accessible with start button and four pressed
// TODO find usage for buttons 0, 1 and 4, 5
@@ -99,8 +99,8 @@ JoystickLayout create_default_layout()
JLO_A_PB(KeyType::RIGHT, 0, -1, 1024);
// Scroll buttons
- JLO_A_PB(KeyType::SCROLL_UP, 2, -1, 1024);
- JLO_A_PB(KeyType::SCROLL_DOWN, 5, -1, 1024);
+ JLO_A_PB(KeyType::HOTBAR_PREV, 2, -1, 1024);
+ JLO_A_PB(KeyType::HOTBAR_NEXT, 5, -1, 1024);
return jlo;
}
@@ -134,10 +134,10 @@ JoystickLayout create_xbox_layout()
JLO_B_PB(KeyType::SNEAK, 1 << 12, 1 << 12); // right
// Triggers
- JLO_B_PB(KeyType::MOUSE_L, 1 << 6, 1 << 6); // lt
- JLO_B_PB(KeyType::MOUSE_R, 1 << 7, 1 << 7); // rt
- JLO_B_PB(KeyType::SCROLL_UP, 1 << 4, 1 << 4); // lb
- JLO_B_PB(KeyType::SCROLL_DOWN, 1 << 5, 1 << 5); // rb
+ JLO_B_PB(KeyType::DIG, 1 << 6, 1 << 6); // lt
+ JLO_B_PB(KeyType::PLACE, 1 << 7, 1 << 7); // rt
+ JLO_B_PB(KeyType::HOTBAR_PREV, 1 << 4, 1 << 4); // lb
+ JLO_B_PB(KeyType::HOTBAR_NEXT, 1 << 5, 1 << 5); // rb
// D-PAD
JLO_B_PB(KeyType::ZOOM, 1 << 15, 1 << 15); // up
diff --git a/src/client/keys.h b/src/client/keys.h
index 43a032a7b..828ea3ec4 100644
--- a/src/client/keys.h
+++ b/src/client/keys.h
@@ -35,6 +35,8 @@ public:
SPECIAL1,
SNEAK,
AUTOFORWARD,
+ DIG,
+ PLACE,
ESC,
@@ -119,12 +121,6 @@ public:
SLOT_31,
SLOT_32,
- // joystick specific keys
- MOUSE_L,
- MOUSE_R,
- SCROLL_UP,
- SCROLL_DOWN,
-
// Fake keycode for array size and internal checks
INTERNAL_ENUM_COUNT
diff --git a/src/client/localplayer.cpp b/src/client/localplayer.cpp
index 3b52b18e1..7d532c602 100644
--- a/src/client/localplayer.cpp
+++ b/src/client/localplayer.cpp
@@ -19,7 +19,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "localplayer.h"
#include <cmath>
-#include "event.h"
+#include "mtevent.h"
#include "collision.h"
#include "nodedef.h"
#include "settings.h"
diff --git a/src/client/mapblock_mesh.cpp b/src/client/mapblock_mesh.cpp
index f65864599..7b342fc33 100644
--- a/src/client/mapblock_mesh.cpp
+++ b/src/client/mapblock_mesh.cpp
@@ -35,11 +35,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
MeshMakeData
*/
-MeshMakeData::MeshMakeData(Client *client, bool use_shaders,
- bool use_tangent_vertices):
+MeshMakeData::MeshMakeData(Client *client, bool use_shaders):
m_client(client),
- m_use_shaders(use_shaders),
- m_use_tangent_vertices(use_tangent_vertices)
+ m_use_shaders(use_shaders)
{}
void MeshMakeData::fillBlockDataBegin(const v3s16 &blockpos)
@@ -81,33 +79,6 @@ void MeshMakeData::fill(MapBlock *block)
}
}
-void MeshMakeData::fillSingleNode(MapNode *node)
-{
- m_blockpos = v3s16(0,0,0);
-
- v3s16 blockpos_nodes = v3s16(0,0,0);
- VoxelArea area(blockpos_nodes-v3s16(1,1,1)*MAP_BLOCKSIZE,
- blockpos_nodes+v3s16(1,1,1)*MAP_BLOCKSIZE*2-v3s16(1,1,1));
- s32 volume = area.getVolume();
- s32 our_node_index = area.index(1,1,1);
-
- // Allocate this block + neighbors
- m_vmanip.clear();
- m_vmanip.addArea(area);
-
- // Fill in data
- MapNode *data = new MapNode[volume];
- for(s32 i = 0; i < volume; i++)
- {
- if (i == our_node_index)
- data[i] = *node;
- else
- data[i] = MapNode(CONTENT_AIR, LIGHT_MAX, 0);
- }
- m_vmanip.copyFrom(data, area, area.MinEdge, area.MinEdge, area.getExtent());
- delete[] data;
-}
-
void MeshMakeData::setCrack(int crack_level, v3s16 crack_pos)
{
if (crack_level >= 0)
@@ -422,7 +393,16 @@ static void getNodeVertexDirs(const v3s16 &dir, v3s16 *vertex_dirs)
u8 idx = (dir.X + 2 * dir.Y + 3 * dir.Z) & 7;
idx = (idx - 1) * 4;
+#if defined(__GNUC__) && !defined(__clang__)
+#pragma GCC diagnostic push
+#if __GNUC__ > 7
+#pragma GCC diagnostic ignored "-Wclass-memaccess"
+#endif
+#endif
memcpy(vertex_dirs, &vertex_dirs_table[idx], 4 * sizeof(v3s16));
+#if defined(__GNUC__) && !defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
}
static void getNodeTextureCoords(v3f base, const v3f &scale, const v3s16 &dir, float *u, float *v)
@@ -1076,10 +1056,9 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
for (auto &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");
- if (g_settings->getBool("enable_minimap")) {
+ if (data->m_client->getMinimap()) {
m_minimap_mapblock = new MinimapMapblock;
m_minimap_mapblock->getMinimapNodes(
&data->m_vmanip, data->m_blockpos * MAP_BLOCKSIZE);
@@ -1256,28 +1235,12 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
scene::SMesh *mesh = (scene::SMesh *)m_mesh[layer];
- // Create meshbuffer, add to mesh
- if (m_use_tangent_vertices) {
- scene::SMeshBufferTangents *buf =
- new scene::SMeshBufferTangents();
- buf->Material = material;
- buf->Vertices.reallocate(p.vertices.size());
- buf->Indices.reallocate(p.indices.size());
- for (const video::S3DVertex &v: p.vertices)
- buf->Vertices.push_back(video::S3DVertexTangents(v.Pos, v.Color, v.TCoords));
- for (u16 i: p.indices)
- buf->Indices.push_back(i);
- buf->recalculateBoundingBox();
- mesh->addMeshBuffer(buf);
- buf->drop();
- } else {
- scene::SMeshBuffer *buf = new scene::SMeshBuffer();
- buf->Material = material;
- buf->append(&p.vertices[0], p.vertices.size(),
- &p.indices[0], p.indices.size());
- mesh->addMeshBuffer(buf);
- buf->drop();
- }
+ scene::SMeshBuffer *buf = new scene::SMeshBuffer();
+ buf->Material = material;
+ buf->append(&p.vertices[0], p.vertices.size(),
+ &p.indices[0], p.indices.size());
+ mesh->addMeshBuffer(buf);
+ buf->drop();
}
/*
@@ -1287,12 +1250,6 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
translateMesh(m_mesh[layer],
intToFloat(data->m_blockpos * MAP_BLOCKSIZE - camera_offset, BS));
- if (m_use_tangent_vertices) {
- scene::IMeshManipulator* meshmanip =
- RenderingEngine::get_scene_manager()->getMeshManipulator();
- meshmanip->recalculateTangents(m_mesh[layer], true, false, false);
- }
-
if (m_mesh[layer]) {
#if 0
// Usually 1-700 faces and 1-7 materials
diff --git a/src/client/mapblock_mesh.h b/src/client/mapblock_mesh.h
index 95fda83e5..ec1faea32 100644
--- a/src/client/mapblock_mesh.h
+++ b/src/client/mapblock_mesh.h
@@ -45,10 +45,8 @@ struct MeshMakeData
Client *m_client;
bool m_use_shaders;
- bool m_use_tangent_vertices;
- MeshMakeData(Client *client, bool use_shaders,
- bool use_tangent_vertices = false);
+ MeshMakeData(Client *client, bool use_shaders);
/*
Copy block data manually (to allow optimizations by the caller)
@@ -63,11 +61,6 @@ struct MeshMakeData
void fill(MapBlock *block);
/*
- Set up with only a single node at (1,1,1)
- */
- void fillSingleNode(MapNode *node);
-
- /*
Set the (node) position of a crack
*/
void setCrack(int crack_level, v3s16 crack_pos);
@@ -143,7 +136,6 @@ private:
IShaderSource *m_shdrsrc;
bool m_enable_shaders;
- bool m_use_tangent_vertices;
bool m_enable_vbo;
// Must animate() be called before rendering?
diff --git a/src/client/mesh.cpp b/src/client/mesh.cpp
index e1ec22068..2400a374c 100644
--- a/src/client/mesh.cpp
+++ b/src/client/mesh.cpp
@@ -203,6 +203,15 @@ void setMeshColor(scene::IMesh *mesh, const video::SColor &color)
setMeshBufferColor(mesh->getMeshBuffer(j), color);
}
+void setMeshBufferTextureCoords(scene::IMeshBuffer *buf, const v2f *uv, u32 count)
+{
+ const u32 stride = getVertexPitchFromType(buf->getVertexType());
+ assert(buf->getVertexCount() >= count);
+ u8 *vertices = (u8 *) buf->getVertices();
+ for (u32 i = 0; i < count; i++)
+ ((video::S3DVertex*) (vertices + i * stride))->TCoords = uv[i];
+}
+
template <typename F>
static void applyToMesh(scene::IMesh *mesh, const F &fn)
{
diff --git a/src/client/mesh.h b/src/client/mesh.h
index 103c61e45..dbc091a06 100644
--- a/src/client/mesh.h
+++ b/src/client/mesh.h
@@ -58,6 +58,13 @@ void setMeshBufferColor(scene::IMeshBuffer *buf, const video::SColor &color);
*/
void setMeshColor(scene::IMesh *mesh, const video::SColor &color);
+
+/*
+ Sets texture coords for vertices in the mesh buffer.
+ `uv[]` must have `count` elements
+*/
+void setMeshBufferTextureCoords(scene::IMeshBuffer *buf, const v2f *uv, u32 count);
+
/*
Set a constant color for an animated mesh
*/
diff --git a/src/client/mesh_generator_thread.cpp b/src/client/mesh_generator_thread.cpp
index 53b980eeb..c8d1cba26 100644
--- a/src/client/mesh_generator_thread.cpp
+++ b/src/client/mesh_generator_thread.cpp
@@ -52,9 +52,6 @@ MeshUpdateQueue::MeshUpdateQueue(Client *client):
m_client(client)
{
m_cache_enable_shaders = g_settings->getBool("enable_shaders");
- m_cache_use_tangent_vertices = m_cache_enable_shaders && (
- g_settings->getBool("enable_bumpmapping") ||
- g_settings->getBool("enable_parallax_occlusion"));
m_cache_smooth_lighting = g_settings->getBool("smooth_lighting");
m_meshgen_block_cache_size = g_settings->getS32("meshgen_block_cache_size");
}
@@ -207,8 +204,7 @@ CachedMapBlockData* MeshUpdateQueue::getCachedBlock(const v3s16 &p)
void MeshUpdateQueue::fillDataFromMapBlockCache(QueuedMeshUpdate *q)
{
- MeshMakeData *data = new MeshMakeData(m_client, m_cache_enable_shaders,
- m_cache_use_tangent_vertices);
+ MeshMakeData *data = new MeshMakeData(m_client, m_cache_enable_shaders);
q->data = data;
data->fillBlockDataBegin(q->p);
diff --git a/src/client/mesh_generator_thread.h b/src/client/mesh_generator_thread.h
index 2bb74589e..8e13dec3e 100644
--- a/src/client/mesh_generator_thread.h
+++ b/src/client/mesh_generator_thread.h
@@ -88,7 +88,6 @@ private:
// TODO: Add callback to update these when g_settings changes
bool m_cache_enable_shaders;
- bool m_cache_use_tangent_vertices;
bool m_cache_smooth_lighting;
int m_meshgen_block_cache_size;
diff --git a/src/client/minimap.cpp b/src/client/minimap.cpp
index 8ea1a0283..60b095712 100644
--- a/src/client/minimap.cpp
+++ b/src/client/minimap.cpp
@@ -25,7 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "shader.h"
#include "mapblock.h"
#include "client/renderingengine.h"
-
+#include "gettext.h"
////
//// MinimapUpdateThread
@@ -108,8 +108,11 @@ void MinimapUpdateThread::doUpdate()
}
}
- if (data->map_invalidated && data->mode != MINIMAP_MODE_OFF) {
- getMap(data->pos, data->map_size, data->scan_height);
+
+ if (data->map_invalidated && (
+ data->mode.type == MINIMAP_TYPE_RADAR ||
+ data->mode.type == MINIMAP_TYPE_SURFACE)) {
+ getMap(data->pos, data->mode.map_size, data->mode.scan_height);
data->map_invalidated = false;
}
}
@@ -181,19 +184,26 @@ Minimap::Minimap(Client *client)
this->m_ndef = client->getNodeDefManager();
m_angle = 0.f;
+ m_current_mode_index = 0;
// Initialize static settings
m_enable_shaders = g_settings->getBool("enable_shaders");
m_surface_mode_scan_height =
g_settings->getBool("minimap_double_scan_height") ? 256 : 128;
+ // Initialize minimap modes
+ addMode(MINIMAP_TYPE_OFF);
+ addMode(MINIMAP_TYPE_SURFACE, 256);
+ addMode(MINIMAP_TYPE_SURFACE, 128);
+ addMode(MINIMAP_TYPE_SURFACE, 64);
+ addMode(MINIMAP_TYPE_RADAR, 512);
+ addMode(MINIMAP_TYPE_RADAR, 256);
+ addMode(MINIMAP_TYPE_RADAR, 128);
+
// Initialize minimap data
data = new MinimapData;
- data->mode = MINIMAP_MODE_OFF;
- data->is_radar = false;
- data->map_invalidated = true;
- data->texture = NULL;
- data->heightmap_texture = NULL;
+ data->map_invalidated = true;
+
data->minimap_shape_round = g_settings->getBool("minimap_shape_round");
// Get round minimap textures
@@ -215,6 +225,8 @@ Minimap::Minimap(Client *client)
// Create object marker texture
data->object_marker_red = m_tsrc->getTexture("object_marker_red.png");
+ setModeIndex(0);
+
// Create mesh buffer for minimap
m_meshbuffer = getMinimapMeshBuffer();
@@ -240,6 +252,10 @@ Minimap::~Minimap()
driver->removeTexture(data->minimap_overlay_square);
driver->removeTexture(data->object_marker_red);
+ for (MinimapMarker *m : m_markers)
+ delete m;
+ m_markers.clear();
+
delete data;
delete m_minimap_update_thread;
}
@@ -280,29 +296,101 @@ MinimapShape Minimap::getMinimapShape()
return MINIMAP_SHAPE_SQUARE;
}
-void Minimap::setMinimapMode(MinimapMode mode)
+void Minimap::setModeIndex(size_t index)
{
- static const MinimapModeDef modedefs[MINIMAP_MODE_COUNT] = {
- {false, 0, 0},
- {false, m_surface_mode_scan_height, 256},
- {false, m_surface_mode_scan_height, 128},
- {false, m_surface_mode_scan_height, 64},
- {true, 32, 128},
- {true, 32, 64},
- {true, 32, 32}
- };
-
- if (mode >= MINIMAP_MODE_COUNT)
- return;
-
MutexAutoLock lock(m_mutex);
- data->is_radar = modedefs[mode].is_radar;
- data->scan_height = modedefs[mode].scan_height;
- data->map_size = modedefs[mode].map_size;
- data->mode = mode;
+ if (index < m_modes.size()) {
+ data->mode = m_modes[index];
+ m_current_mode_index = index;
+ } else {
+ data->mode = MinimapModeDef{MINIMAP_TYPE_OFF, N_("Minimap hidden"), 0, 0, ""};
+ m_current_mode_index = 0;
+ }
- m_minimap_update_thread->deferUpdate();
+ data->map_invalidated = true;
+
+ if (m_minimap_update_thread)
+ m_minimap_update_thread->deferUpdate();
+}
+
+void Minimap::addMode(MinimapModeDef mode)
+{
+ // Check validity
+ if (mode.type == MINIMAP_TYPE_TEXTURE) {
+ if (mode.texture.empty())
+ return;
+ if (mode.scale < 1)
+ mode.scale = 1;
+ }
+
+ int zoom = -1;
+
+ // Build a default standard label
+ if (mode.label == "") {
+ switch (mode.type) {
+ case MINIMAP_TYPE_OFF:
+ mode.label = N_("Minimap hidden");
+ break;
+ case MINIMAP_TYPE_SURFACE:
+ mode.label = N_("Minimap in surface mode, Zoom x%d");
+ if (mode.map_size > 0)
+ zoom = 256 / mode.map_size;
+ break;
+ case MINIMAP_TYPE_RADAR:
+ mode.label = N_("Minimap in radar mode, Zoom x%d");
+ if (mode.map_size > 0)
+ zoom = 512 / mode.map_size;
+ break;
+ case MINIMAP_TYPE_TEXTURE:
+ mode.label = N_("Minimap in texture mode");
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (zoom >= 0) {
+ char label_buf[1024];
+ porting::mt_snprintf(label_buf, sizeof(label_buf),
+ mode.label.c_str(), zoom);
+ mode.label = label_buf;
+ }
+
+ m_modes.push_back(mode);
+}
+
+void Minimap::addMode(MinimapType type, u16 size, std::string label,
+ std::string texture, u16 scale)
+{
+ MinimapModeDef mode;
+ mode.type = type;
+ mode.label = label;
+ mode.map_size = size;
+ mode.texture = texture;
+ mode.scale = scale;
+ switch (type) {
+ case MINIMAP_TYPE_SURFACE:
+ mode.scan_height = m_surface_mode_scan_height;
+ break;
+ case MINIMAP_TYPE_RADAR:
+ mode.scan_height = 32;
+ break;
+ default:
+ mode.scan_height = 0;
+ }
+ addMode(mode);
+}
+
+void Minimap::nextMode()
+{
+ if (m_modes.empty())
+ return;
+ m_current_mode_index++;
+ if (m_current_mode_index >= m_modes.size())
+ m_current_mode_index = 0;
+
+ setModeIndex(m_current_mode_index);
}
void Minimap::setPos(v3s16 pos)
@@ -331,16 +419,16 @@ void Minimap::setAngle(f32 angle)
void Minimap::blitMinimapPixelsToImageRadar(video::IImage *map_image)
{
video::SColor c(240, 0, 0, 0);
- for (s16 x = 0; x < data->map_size; x++)
- for (s16 z = 0; z < data->map_size; z++) {
- MinimapPixel *mmpixel = &data->minimap_scan[x + z * data->map_size];
+ for (s16 x = 0; x < data->mode.map_size; x++)
+ for (s16 z = 0; z < data->mode.map_size; z++) {
+ MinimapPixel *mmpixel = &data->minimap_scan[x + z * data->mode.map_size];
if (mmpixel->air_count > 0)
c.setGreen(core::clamp(core::round32(32 + mmpixel->air_count * 8), 0, 255));
else
c.setGreen(0);
- map_image->setPixel(x, data->map_size - z - 1, c);
+ map_image->setPixel(x, data->mode.map_size - z - 1, c);
}
}
@@ -349,9 +437,9 @@ void Minimap::blitMinimapPixelsToImageSurface(
{
// This variable creation/destruction has a 1% cost on rendering minimap
video::SColor tilecolor;
- for (s16 x = 0; x < data->map_size; x++)
- for (s16 z = 0; z < data->map_size; z++) {
- MinimapPixel *mmpixel = &data->minimap_scan[x + z * data->map_size];
+ for (s16 x = 0; x < data->mode.map_size; x++)
+ for (s16 z = 0; z < data->mode.map_size; z++) {
+ MinimapPixel *mmpixel = &data->minimap_scan[x + z * data->mode.map_size];
const ContentFeatures &f = m_ndef->get(mmpixel->n);
const TileDef *tile = &f.tiledef[0];
@@ -367,10 +455,10 @@ void Minimap::blitMinimapPixelsToImageSurface(
tilecolor.setBlue(tilecolor.getBlue() * f.minimap_color.getBlue() / 255);
tilecolor.setAlpha(240);
- map_image->setPixel(x, data->map_size - z - 1, tilecolor);
+ map_image->setPixel(x, data->mode.map_size - z - 1, tilecolor);
u32 h = mmpixel->height;
- heightmap_image->setPixel(x,data->map_size - z - 1,
+ heightmap_image->setPixel(x,data->mode.map_size - z - 1,
video::SColor(255, h, h, h));
}
}
@@ -378,21 +466,46 @@ void Minimap::blitMinimapPixelsToImageSurface(
video::ITexture *Minimap::getMinimapTexture()
{
// update minimap textures when new scan is ready
- if (data->map_invalidated)
+ if (data->map_invalidated && data->mode.type != MINIMAP_TYPE_TEXTURE)
return data->texture;
// create minimap and heightmap images in memory
- core::dimension2d<u32> dim(data->map_size, data->map_size);
+ core::dimension2d<u32> dim(data->mode.map_size, data->mode.map_size);
video::IImage *map_image = driver->createImage(video::ECF_A8R8G8B8, dim);
video::IImage *heightmap_image = driver->createImage(video::ECF_A8R8G8B8, dim);
video::IImage *minimap_image = driver->createImage(video::ECF_A8R8G8B8,
core::dimension2d<u32>(MINIMAP_MAX_SX, MINIMAP_MAX_SY));
// Blit MinimapPixels to images
- if (data->is_radar)
- blitMinimapPixelsToImageRadar(map_image);
- else
+ switch(data->mode.type) {
+ case MINIMAP_TYPE_OFF:
+ break;
+ case MINIMAP_TYPE_SURFACE:
blitMinimapPixelsToImageSurface(map_image, heightmap_image);
+ break;
+ case MINIMAP_TYPE_RADAR:
+ blitMinimapPixelsToImageRadar(map_image);
+ break;
+ case MINIMAP_TYPE_TEXTURE:
+ // Want to use texture source, to : 1 find texture, 2 cache it
+ video::ITexture* texture = m_tsrc->getTexture(data->mode.texture);
+ video::IImage* image = driver->createImageFromData(
+ texture->getColorFormat(), texture->getSize(), texture->lock(), true, false);
+ texture->unlock();
+
+ auto dim = image->getDimension();
+
+ map_image->fill(video::SColor(255, 0, 0, 0));
+
+ image->copyTo(map_image,
+ irr::core::vector2d<int> {
+ ((data->mode.map_size - (static_cast<int>(dim.Width))) >> 1)
+ - data->pos.X / data->mode.scale,
+ ((data->mode.map_size - (static_cast<int>(dim.Height))) >> 1)
+ + data->pos.Z / data->mode.scale
+ });
+ image->drop();
+ }
map_image->copyToScaling(minimap_image);
map_image->drop();
@@ -461,21 +574,34 @@ scene::SMeshBuffer *Minimap::getMinimapMeshBuffer()
void Minimap::drawMinimap()
{
+ // Non hud managed minimap drawing (legacy minimap)
+ v2u32 screensize = RenderingEngine::get_instance()->getWindowSize();
+ const u32 size = 0.25 * screensize.Y;
+
+ drawMinimap(core::rect<s32>(
+ screensize.X - size - 10, 10,
+ screensize.X - 10, size + 10));
+}
+
+void Minimap::drawMinimap(core::rect<s32> rect) {
+
video::ITexture *minimap_texture = getMinimapTexture();
if (!minimap_texture)
return;
+ if (data->mode.type == MINIMAP_TYPE_OFF)
+ return;
+
updateActiveMarkers();
- v2u32 screensize = RenderingEngine::get_instance()->getWindowSize();
- const u32 size = 0.25 * screensize.Y;
core::rect<s32> oldViewPort = driver->getViewPort();
core::matrix4 oldProjMat = driver->getTransform(video::ETS_PROJECTION);
core::matrix4 oldViewMat = driver->getTransform(video::ETS_VIEW);
- driver->setViewPort(core::rect<s32>(
- screensize.X - size * 2 - 10, 10,
- screensize.X - size - 10, size + 10));
+// driver->setViewPort(core::rect<s32>(
+// screensize.X - size * 2 - 10, 10,
+// screensize.X - size - 10, size + 10));
+ driver->setViewPort(rect);
driver->setTransform(video::ETS_PROJECTION, core::matrix4());
driver->setTransform(video::ETS_VIEW, core::matrix4());
@@ -488,7 +614,7 @@ void Minimap::drawMinimap()
material.TextureLayer[0].Texture = minimap_texture;
material.TextureLayer[1].Texture = data->heightmap_texture;
- if (m_enable_shaders && !data->is_radar) {
+ if (m_enable_shaders && data->mode.type == MINIMAP_TYPE_SURFACE) {
u16 sid = m_shdrsrc->getShader("minimap_shader", 1, 1);
material.MaterialType = m_shdrsrc->getShaderInfo(sid).material;
} else {
@@ -529,14 +655,15 @@ void Minimap::drawMinimap()
driver->setViewPort(oldViewPort);
// Draw player markers
- v2s32 s_pos(screensize.X - size * 2 - 10, 10);
+// v2s32 s_pos(screensize.X - size * 2 - 10, 10);
+ v2s32 s_pos(rect.UpperLeftCorner.X, rect.UpperLeftCorner.Y);
core::dimension2di imgsize(data->object_marker_red->getOriginalSize());
core::rect<s32> img_rect(0, 0, imgsize.Width, imgsize.Height);
static const video::SColor col(255, 255, 255, 255);
static const video::SColor c[4] = {col, col, col, col};
f32 sin_angle = std::sin(m_angle * core::DEGTORAD);
f32 cos_angle = std::cos(m_angle * core::DEGTORAD);
- s32 marker_size2 = 0.025 * (float)size;
+ s32 marker_size2 = 0.025 * (float)rect.getWidth();;
for (std::list<v2f>::const_iterator
i = m_active_markers.begin();
i != m_active_markers.end(); ++i) {
@@ -547,8 +674,8 @@ void Minimap::drawMinimap()
posf.X = t1;
posf.Y = t2;
}
- posf.X = (posf.X + 0.5) * (float)size;
- posf.Y = (posf.Y + 0.5) * (float)size;
+ posf.X = (posf.X + 0.5) * (float)rect.getWidth();
+ posf.Y = (posf.Y + 0.5) * (float)rect.getHeight();
core::rect<s32> dest_rect(
s_pos.X + posf.X - marker_size2,
s_pos.Y + posf.Y - marker_size2,
@@ -559,28 +686,41 @@ void Minimap::drawMinimap()
}
}
+MinimapMarker* Minimap::addMarker(scene::ISceneNode *parent_node)
+{
+ MinimapMarker *m = new MinimapMarker(parent_node);
+ m_markers.push_back(m);
+ return m;
+}
+
+void Minimap::removeMarker(MinimapMarker **m)
+{
+ m_markers.remove(*m);
+ delete *m;
+ *m = nullptr;
+}
+
void Minimap::updateActiveMarkers()
{
video::IImage *minimap_mask = data->minimap_shape_round ?
data->minimap_mask_round : data->minimap_mask_square;
- const std::list<Nametag *> &nametags = client->getCamera()->getNametags();
-
m_active_markers.clear();
-
- for (Nametag *nametag : nametags) {
- v3s16 pos = floatToInt(nametag->parent_node->getAbsolutePosition() +
- intToFloat(client->getCamera()->getOffset(), BS), BS);
- pos -= data->pos - v3s16(data->map_size / 2,
- data->scan_height / 2,
- data->map_size / 2);
- if (pos.X < 0 || pos.X > data->map_size ||
- pos.Y < 0 || pos.Y > data->scan_height ||
- pos.Z < 0 || pos.Z > data->map_size) {
+ v3f cam_offset = intToFloat(client->getCamera()->getOffset(), BS);
+ v3s16 pos_offset = data->pos - v3s16(data->mode.map_size / 2,
+ data->mode.scan_height / 2,
+ data->mode.map_size / 2);
+
+ for (MinimapMarker *marker : m_markers) {
+ v3s16 pos = floatToInt(marker->parent_node->getAbsolutePosition() +
+ cam_offset, BS) - pos_offset;
+ if (pos.X < 0 || pos.X > data->mode.map_size ||
+ pos.Y < 0 || pos.Y > data->mode.scan_height ||
+ pos.Z < 0 || pos.Z > data->mode.map_size) {
continue;
}
- pos.X = ((float)pos.X / data->map_size) * MINIMAP_MAX_SX;
- pos.Z = ((float)pos.Z / data->map_size) * MINIMAP_MAX_SY;
+ pos.X = ((float)pos.X / data->mode.map_size) * MINIMAP_MAX_SX;
+ pos.Z = ((float)pos.Z / data->mode.map_size) * MINIMAP_MAX_SY;
const video::SColor &mask_col = minimap_mask->getPixel(pos.X, pos.Z);
if (!mask_col.getAlpha()) {
continue;
diff --git a/src/client/minimap.h b/src/client/minimap.h
index 258d5330d..4a2c462f8 100644
--- a/src/client/minimap.h
+++ b/src/client/minimap.h
@@ -19,6 +19,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#pragma once
+#include "../hud.h"
#include "irrlichttypes_extrabloated.h"
#include "util/thread.h"
#include "voxel.h"
@@ -33,28 +34,27 @@ class IShaderSource;
#define MINIMAP_MAX_SX 512
#define MINIMAP_MAX_SY 512
-enum MinimapMode {
- MINIMAP_MODE_OFF,
- MINIMAP_MODE_SURFACEx1,
- MINIMAP_MODE_SURFACEx2,
- MINIMAP_MODE_SURFACEx4,
- MINIMAP_MODE_RADARx1,
- MINIMAP_MODE_RADARx2,
- MINIMAP_MODE_RADARx4,
- MINIMAP_MODE_COUNT,
-};
-
enum MinimapShape {
MINIMAP_SHAPE_SQUARE,
MINIMAP_SHAPE_ROUND,
};
struct MinimapModeDef {
- bool is_radar;
+ MinimapType type;
+ std::string label;
u16 scan_height;
u16 map_size;
+ std::string texture;
+ u16 scale;
};
+struct MinimapMarker {
+ MinimapMarker(scene::ISceneNode *parent_node):
+ parent_node(parent_node)
+ {
+ }
+ scene::ISceneNode *parent_node;
+};
struct MinimapPixel {
//! The topmost node that the minimap displays.
MapNode n;
@@ -69,12 +69,9 @@ struct MinimapMapblock {
};
struct MinimapData {
- bool is_radar;
- MinimapMode mode;
+ MinimapModeDef mode;
v3s16 pos;
v3s16 old_pos;
- u16 scan_height;
- u16 map_size;
MinimapPixel minimap_scan[MINIMAP_MAX_SX * MINIMAP_MAX_SY];
bool map_invalidated;
bool minimap_shape_round;
@@ -127,12 +124,22 @@ public:
v3s16 getPos() const { return data->pos; }
void setAngle(f32 angle);
f32 getAngle() const { return m_angle; }
- void setMinimapMode(MinimapMode mode);
- MinimapMode getMinimapMode() const { return data->mode; }
void toggleMinimapShape();
void setMinimapShape(MinimapShape shape);
MinimapShape getMinimapShape();
+ void clearModes() { m_modes.clear(); };
+ void addMode(MinimapModeDef mode);
+ void addMode(MinimapType type, u16 size = 0, std::string label = "",
+ std::string texture = "", u16 scale = 1);
+
+ void setModeIndex(size_t index);
+ size_t getModeIndex() const { return m_current_mode_index; };
+ size_t getMaxModeIndex() const { return m_modes.size() - 1; };
+ void nextMode();
+
+ void setModesFromString(std::string modes_string);
+ MinimapModeDef getModeDef() const { return data->mode; }
video::ITexture *getMinimapTexture();
@@ -142,8 +149,12 @@ public:
scene::SMeshBuffer *getMinimapMeshBuffer();
+ MinimapMarker* addMarker(scene::ISceneNode *parent_node);
+ void removeMarker(MinimapMarker **marker);
+
void updateActiveMarkers();
void drawMinimap();
+ void drawMinimap(core::rect<s32> rect);
video::IVideoDriver *driver;
Client* client;
@@ -153,11 +164,14 @@ private:
ITextureSource *m_tsrc;
IShaderSource *m_shdrsrc;
const NodeDefManager *m_ndef;
- MinimapUpdateThread *m_minimap_update_thread;
+ MinimapUpdateThread *m_minimap_update_thread = nullptr;
scene::SMeshBuffer *m_meshbuffer;
bool m_enable_shaders;
+ std::vector<MinimapModeDef> m_modes;
+ size_t m_current_mode_index;
u16 m_surface_mode_scan_height;
f32 m_angle;
std::mutex m_mutex;
+ std::list<MinimapMarker*> m_markers;
std::list<v2f> m_active_markers;
};
diff --git a/src/client/render/core.cpp b/src/client/render/core.cpp
index 1fad4af52..44e3ed744 100644
--- a/src/client/render/core.cpp
+++ b/src/client/render/core.cpp
@@ -123,34 +123,30 @@ void RenderingCore::drawTracersAndESP()
}
if (draw_node_esp || draw_node_tracers) {
Map &map = env.getMap();
- std::map<v2s16, MapSector*> *sectors = map.getSectorsPtr();
-
- for (auto &sector_it : *sectors) {
- MapSector *sector = sector_it.second;
- MapBlockVect blocks;
- sector->getBlocks(blocks);
- for (MapBlock *block : blocks) {
- if (! block->mesh)
- continue;
- for (v3s16 p : block->mesh->esp_nodes) {
- v3f pos = intToFloat(p, BS) - camera_offset;
- MapNode node = map.getNode(p);
- std::vector<aabb3f> boxes;
- node.getSelectionBoxes(client->getNodeDefManager(), &boxes, node.getNeighbors(p, &map));
- video::SColor color = client->getNodeDefManager()->get(node).minimap_color;
-
- for (aabb3f box : boxes) {
- box.MinEdge += pos;
- box.MaxEdge += pos;
- if (draw_node_esp)
- driver->draw3DBox(box, color);
- if (draw_node_tracers)
- driver->draw3DLine(eye_pos, box.getCenter(), color);
- }
+
+ std::vector<v3s16> positions;
+ map.listAllLoadedBlocks(positions);
+
+ for (v3s16 blockp : positions) {
+ MapBlock *block = map.getBlockNoCreate(blockp);
+ if (! block->mesh)
+ continue;
+ for (v3s16 p : block->mesh->esp_nodes) {
+ v3f pos = intToFloat(p, BS) - camera_offset;
+ MapNode node = map.getNode(p);
+ std::vector<aabb3f> boxes;
+ node.getSelectionBoxes(client->getNodeDefManager(), &boxes, node.getNeighbors(p, &map));
+ video::SColor color = client->getNodeDefManager()->get(node).minimap_color;
+ for (aabb3f box : boxes) {
+ box.MinEdge += pos;
+ box.MaxEdge += pos;
+ if (draw_node_esp)
+ driver->draw3DBox(box, color);
+ if (draw_node_tracers)
+ driver->draw3DLine(eye_pos, box.getCenter(), color);
}
}
}
-
}
driver->setMaterial(oldmaterial);
diff --git a/src/client/shader.cpp b/src/client/shader.cpp
index ee6079f7a..1cec20d2c 100644
--- a/src/client/shader.cpp
+++ b/src/client/shader.cpp
@@ -37,6 +37,22 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "log.h"
#include "gamedef.h"
#include "client/tile.h"
+#include "config.h"
+
+#if ENABLE_GLES
+#ifdef _IRR_COMPILE_WITH_OGLES1_
+#include <GLES/gl.h>
+#else
+#include <GLES2/gl2.h>
+#endif
+#else
+#ifndef __APPLE__
+#include <GL/gl.h>
+#else
+#define GL_SILENCE_DEPRECATION
+#include <OpenGL/gl.h>
+#endif
+#endif
/*
A cache from shader name to shader path
@@ -215,11 +231,24 @@ class MainShaderConstantSetter : public IShaderConstantSetter
{
CachedVertexShaderSetting<float, 16> m_world_view_proj;
CachedVertexShaderSetting<float, 16> m_world;
+#if ENABLE_GLES
+ // Modelview matrix
+ CachedVertexShaderSetting<float, 16> m_world_view;
+ // Texture matrix
+ CachedVertexShaderSetting<float, 16> m_texture;
+ // Normal matrix
+ CachedVertexShaderSetting<float, 9> m_normal;
+#endif
public:
MainShaderConstantSetter() :
- m_world_view_proj("mWorldViewProj"),
- m_world("mWorld")
+ m_world_view_proj("mWorldViewProj")
+ , m_world("mWorld")
+#if ENABLE_GLES
+ , m_world_view("mWorldView")
+ , m_texture("mTexture")
+ , m_normal("mNormal")
+#endif
{}
~MainShaderConstantSetter() = default;
@@ -229,23 +258,42 @@ public:
video::IVideoDriver *driver = services->getVideoDriver();
sanity_check(driver);
+ // Set world matrix
+ core::matrix4 world = driver->getTransform(video::ETS_WORLD);
+ if (is_highlevel)
+ m_world.set(*reinterpret_cast<float(*)[16]>(world.pointer()), services);
+ else
+ services->setVertexShaderConstant(world.pointer(), 4, 4);
+
// Set clip matrix
+ core::matrix4 worldView;
+ worldView = driver->getTransform(video::ETS_VIEW);
+ worldView *= world;
core::matrix4 worldViewProj;
worldViewProj = driver->getTransform(video::ETS_PROJECTION);
- worldViewProj *= driver->getTransform(video::ETS_VIEW);
- worldViewProj *= driver->getTransform(video::ETS_WORLD);
+ worldViewProj *= worldView;
if (is_highlevel)
m_world_view_proj.set(*reinterpret_cast<float(*)[16]>(worldViewProj.pointer()), services);
else
services->setVertexShaderConstant(worldViewProj.pointer(), 0, 4);
- // Set world matrix
- core::matrix4 world = driver->getTransform(video::ETS_WORLD);
- if (is_highlevel)
- m_world.set(*reinterpret_cast<float(*)[16]>(world.pointer()), services);
- else
- services->setVertexShaderConstant(world.pointer(), 4, 4);
-
+#if ENABLE_GLES
+ if (is_highlevel) {
+ core::matrix4 texture = driver->getTransform(video::ETS_TEXTURE_0);
+ m_world_view.set(*reinterpret_cast<float(*)[16]>(worldView.pointer()), services);
+ m_texture.set(*reinterpret_cast<float(*)[16]>(texture.pointer()), services);
+
+ core::matrix4 normal;
+ worldView.getTransposed(normal);
+ sanity_check(normal.makeInverse());
+ float m[9] = {
+ normal[0], normal[1], normal[2],
+ normal[4], normal[5], normal[6],
+ normal[8], normal[9], normal[10],
+ };
+ m_normal.set(m, services);
+ }
+#endif
}
};
@@ -529,7 +577,6 @@ ShaderInfo generate_shader(const std::string &name, u8 material_type, u8 drawtyp
shaderinfo.name = name;
shaderinfo.material_type = material_type;
shaderinfo.drawtype = drawtype;
- shaderinfo.material = video::EMT_SOLID;
switch (material_type) {
case TILE_MATERIAL_OPAQUE:
case TILE_MATERIAL_LIQUID_OPAQUE:
@@ -550,6 +597,7 @@ ShaderInfo generate_shader(const std::string &name, u8 material_type, u8 drawtyp
shaderinfo.base_material = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
break;
}
+ shaderinfo.material = shaderinfo.base_material;
bool enable_shaders = g_settings->getBool("enable_shaders");
if (!enable_shaders)
@@ -605,7 +653,62 @@ ShaderInfo generate_shader(const std::string &name, u8 material_type, u8 drawtyp
return shaderinfo;
// Create shaders header
- std::string shaders_header = "#version 120\n";
+ bool use_gles = false;
+#if ENABLE_GLES
+ use_gles = driver->getDriverType() == video::EDT_OGLES2;
+#endif
+ std::string shaders_header, vertex_header, pixel_header; // geometry shaders aren’t supported in GLES<3
+ if (use_gles) {
+ shaders_header =
+ "#version 100\n"
+ ;
+ vertex_header = R"(
+ uniform highp mat4 mWorldView;
+ uniform highp mat4 mWorldViewProj;
+ uniform mediump mat4 mTexture;
+ uniform mediump mat3 mNormal;
+
+ attribute highp vec4 inVertexPosition;
+ attribute lowp vec4 inVertexColor;
+ attribute mediump vec4 inTexCoord0;
+ attribute mediump vec3 inVertexNormal;
+ attribute mediump vec4 inVertexTangent;
+ attribute mediump vec4 inVertexBinormal;
+ )";
+ pixel_header = R"(
+ precision mediump float;
+ )";
+ } else {
+ shaders_header = R"(
+ #version 120
+ #define lowp
+ #define mediump
+ #define highp
+ )";
+ vertex_header = R"(
+ #define mWorldView gl_ModelViewMatrix
+ #define mWorldViewProj gl_ModelViewProjectionMatrix
+ #define mTexture (gl_TextureMatrix[0])
+ #define mNormal gl_NormalMatrix
+
+ #define inVertexPosition gl_Vertex
+ #define inVertexColor gl_Color
+ #define inTexCoord0 gl_MultiTexCoord0
+ #define inVertexNormal gl_Normal
+ #define inVertexTangent gl_MultiTexCoord1
+ #define inVertexBinormal gl_MultiTexCoord2
+ )";
+ }
+
+ bool use_discard = use_gles;
+#ifdef __unix__
+ // For renderers that should use discard instead of GL_ALPHA_TEST
+ const char* gl_renderer = (const char*)glGetString(GL_RENDERER);
+ if (strstr(gl_renderer, "GC7000"))
+ use_discard = true;
+#endif
+ if (use_discard && shaderinfo.base_material != video::EMT_SOLID)
+ shaders_header += "#define USE_DISCARD\n";
static const char* drawTypes[] = {
"NDT_NORMAL",
@@ -665,73 +768,16 @@ ShaderInfo generate_shader(const std::string &name, u8 material_type, u8 drawtyp
shaders_header += itos(drawtype);
shaders_header += "\n";
- if (g_settings->getBool("generate_normalmaps")) {
- shaders_header += "#define GENERATE_NORMALMAPS 1\n";
- } else {
- shaders_header += "#define GENERATE_NORMALMAPS 0\n";
- }
- shaders_header += "#define NORMALMAPS_STRENGTH ";
- shaders_header += ftos(g_settings->getFloat("normalmaps_strength"));
- shaders_header += "\n";
- float sample_step;
- int smooth = (int)g_settings->getFloat("normalmaps_smooth");
- switch (smooth){
- case 0:
- sample_step = 0.0078125; // 1.0 / 128.0
- break;
- case 1:
- sample_step = 0.00390625; // 1.0 / 256.0
- break;
- case 2:
- sample_step = 0.001953125; // 1.0 / 512.0
- break;
- default:
- sample_step = 0.0078125;
- break;
- }
- shaders_header += "#define SAMPLE_STEP ";
- shaders_header += ftos(sample_step);
- shaders_header += "\n";
-
- if (g_settings->getBool("enable_bumpmapping"))
- shaders_header += "#define ENABLE_BUMPMAPPING\n";
-
- if (g_settings->getBool("enable_parallax_occlusion")){
- int mode = g_settings->getFloat("parallax_occlusion_mode");
- float scale = g_settings->getFloat("parallax_occlusion_scale");
- float bias = g_settings->getFloat("parallax_occlusion_bias");
- int iterations = g_settings->getFloat("parallax_occlusion_iterations");
- shaders_header += "#define ENABLE_PARALLAX_OCCLUSION\n";
- shaders_header += "#define PARALLAX_OCCLUSION_MODE ";
- shaders_header += itos(mode);
- shaders_header += "\n";
- shaders_header += "#define PARALLAX_OCCLUSION_SCALE ";
- shaders_header += ftos(scale);
- shaders_header += "\n";
- shaders_header += "#define PARALLAX_OCCLUSION_BIAS ";
- shaders_header += ftos(bias);
- shaders_header += "\n";
- shaders_header += "#define PARALLAX_OCCLUSION_ITERATIONS ";
- shaders_header += itos(iterations);
- shaders_header += "\n";
- }
-
- shaders_header += "#define USE_NORMALMAPS ";
- if (g_settings->getBool("enable_bumpmapping") || g_settings->getBool("enable_parallax_occlusion"))
- shaders_header += "1\n";
- else
- shaders_header += "0\n";
-
if (g_settings->getBool("enable_waving_water")){
shaders_header += "#define ENABLE_WAVING_WATER 1\n";
shaders_header += "#define WATER_WAVE_HEIGHT ";
- shaders_header += ftos(g_settings->getFloat("water_wave_height"));
+ shaders_header += std::to_string(g_settings->getFloat("water_wave_height"));
shaders_header += "\n";
shaders_header += "#define WATER_WAVE_LENGTH ";
- shaders_header += ftos(g_settings->getFloat("water_wave_length"));
+ shaders_header += std::to_string(g_settings->getFloat("water_wave_length"));
shaders_header += "\n";
shaders_header += "#define WATER_WAVE_SPEED ";
- shaders_header += ftos(g_settings->getFloat("water_wave_speed"));
+ shaders_header += std::to_string(g_settings->getFloat("water_wave_speed"));
shaders_header += "\n";
} else{
shaders_header += "#define ENABLE_WAVING_WATER 0\n";
@@ -753,7 +799,7 @@ ShaderInfo generate_shader(const std::string &name, u8 material_type, u8 drawtyp
shaders_header += "#define ENABLE_TONE_MAPPING\n";
shaders_header += "#define FOG_START ";
- shaders_header += ftos(rangelim(g_settings->getFloat("fog_start"), 0.0f, 0.99f));
+ shaders_header += std::to_string(rangelim(g_settings->getFloat("fog_start"), 0.0f, 0.99f));
shaders_header += "\n";
// Call addHighLevelShaderMaterial() or addShaderMaterial()
@@ -761,11 +807,11 @@ ShaderInfo generate_shader(const std::string &name, u8 material_type, u8 drawtyp
const c8* pixel_program_ptr = 0;
const c8* geometry_program_ptr = 0;
if (!vertex_program.empty()) {
- vertex_program = shaders_header + vertex_program;
+ vertex_program = shaders_header + vertex_header + vertex_program;
vertex_program_ptr = vertex_program.c_str();
}
if (!pixel_program.empty()) {
- pixel_program = shaders_header + pixel_program;
+ pixel_program = shaders_header + pixel_header + pixel_program;
pixel_program_ptr = pixel_program.c_str();
}
if (!geometry_program.empty()) {
@@ -847,27 +893,37 @@ void load_shaders(const std::string &name, SourceShaderCache *sourcecache,
geometry_program = "";
is_highlevel = false;
- if(enable_shaders){
- // Look for high level shaders
- if(drivertype == video::EDT_DIRECT3D9){
- // Direct3D 9: HLSL
- // (All shaders in one file)
- vertex_program = sourcecache->getOrLoad(name, "d3d9.hlsl");
- pixel_program = vertex_program;
- geometry_program = vertex_program;
- }
- else if(drivertype == video::EDT_OPENGL){
- // OpenGL: GLSL
- vertex_program = sourcecache->getOrLoad(name, "opengl_vertex.glsl");
- pixel_program = sourcecache->getOrLoad(name, "opengl_fragment.glsl");
- geometry_program = sourcecache->getOrLoad(name, "opengl_geometry.glsl");
- }
- if (!vertex_program.empty() || !pixel_program.empty() || !geometry_program.empty()){
- is_highlevel = true;
- return;
- }
- }
+ if (!enable_shaders)
+ return;
+
+ // Look for high level shaders
+ switch (drivertype) {
+ case video::EDT_DIRECT3D9:
+ // Direct3D 9: HLSL
+ // (All shaders in one file)
+ vertex_program = sourcecache->getOrLoad(name, "d3d9.hlsl");
+ pixel_program = vertex_program;
+ geometry_program = vertex_program;
+ break;
+ case video::EDT_OPENGL:
+#if ENABLE_GLES
+ case video::EDT_OGLES2:
+#endif
+ // OpenGL: GLSL
+ vertex_program = sourcecache->getOrLoad(name, "opengl_vertex.glsl");
+ pixel_program = sourcecache->getOrLoad(name, "opengl_fragment.glsl");
+ geometry_program = sourcecache->getOrLoad(name, "opengl_geometry.glsl");
+ break;
+
+ default:
+ // e.g. OpenGL ES 1 (with no shader support)
+ break;
+ }
+ if (!vertex_program.empty() || !pixel_program.empty() || !geometry_program.empty()){
+ is_highlevel = true;
+ return;
+ }
}
void dumpShaderProgram(std::ostream &output_stream,
diff --git a/src/client/sky.cpp b/src/client/sky.cpp
index 2e0cbca86..9a2614eda 100644
--- a/src/client/sky.cpp
+++ b/src/client/sky.cpp
@@ -1,6 +1,7 @@
/*
Minetest
Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
+Copyright (C) 2020 numzero, Lobachevskiy Vitaliy <numzer0@yandex.ru>
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
@@ -34,16 +35,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "config.h"
using namespace irr::core;
-Sky::Sky(s32 id, ITextureSource *tsrc) :
- scene::ISceneNode(RenderingEngine::get_scene_manager()->getRootSceneNode(),
- RenderingEngine::get_scene_manager(), id)
+static video::SMaterial baseMaterial()
{
- setAutomaticCulling(scene::EAC_OFF);
- m_box.MaxEdge.set(0, 0, 0);
- m_box.MinEdge.set(0, 0, 0);
-
- // Create material
-
video::SMaterial mat;
mat.Lighting = false;
#if ENABLE_GLES
@@ -56,14 +49,31 @@ Sky::Sky(s32 id, ITextureSource *tsrc) :
mat.TextureLayer[0].TextureWrapU = video::ETC_CLAMP_TO_EDGE;
mat.TextureLayer[0].TextureWrapV = video::ETC_CLAMP_TO_EDGE;
mat.BackfaceCulling = false;
+ return mat;
+};
+
+Sky::Sky(s32 id, ITextureSource *tsrc, IShaderSource *ssrc) :
+ scene::ISceneNode(RenderingEngine::get_scene_manager()->getRootSceneNode(),
+ RenderingEngine::get_scene_manager(), id)
+{
+ setAutomaticCulling(scene::EAC_OFF);
+ m_box.MaxEdge.set(0, 0, 0);
+ m_box.MinEdge.set(0, 0, 0);
+
+ m_enable_shaders = g_settings->getBool("enable_shaders");
- m_materials[0] = mat;
+ // Create materials
- m_materials[1] = mat;
+ m_materials[0] = baseMaterial();
+ m_materials[0].MaterialType = ssrc->getShaderInfo(ssrc->getShader("stars_shader", TILE_MATERIAL_ALPHA, 0)).material;
+ m_materials[0].Lighting = true;
+ m_materials[0].ColorMaterial = video::ECM_NONE;
+
+ m_materials[1] = baseMaterial();
//m_materials[1].MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
m_materials[1].MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
- m_materials[2] = mat;
+ m_materials[2] = baseMaterial();
m_materials[2].setTexture(0, tsrc->getTextureForMesh("sunrisebg.png"));
m_materials[2].MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
//m_materials[2].MaterialType = video::EMT_TRANSPARENT_ADD_COLOR;
@@ -80,7 +90,7 @@ Sky::Sky(s32 id, ITextureSource *tsrc) :
tsrc->getTexture(m_moon_params.tonemap) : NULL;
if (m_sun_texture) {
- m_materials[3] = mat;
+ m_materials[3] = baseMaterial();
m_materials[3].setTexture(0, m_sun_texture);
m_materials[3].MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
// Disables texture filtering
@@ -92,7 +102,7 @@ Sky::Sky(s32 id, ITextureSource *tsrc) :
m_materials[3].Lighting = true;
}
if (m_moon_texture) {
- m_materials[4] = mat;
+ m_materials[4] = baseMaterial();
m_materials[4].setTexture(0, m_moon_texture);
m_materials[4].MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
// Disables texture filtering
@@ -105,7 +115,7 @@ Sky::Sky(s32 id, ITextureSource *tsrc) :
}
for (int i = 5; i < 11; i++) {
- m_materials[i] = mat;
+ m_materials[i] = baseMaterial();
m_materials[i].Lighting = true;
m_materials[i].MaterialType = video::EMT_SOLID;
}
@@ -202,7 +212,7 @@ void Sky::render()
const f32 t = 1.0f;
const f32 o = 0.0f;
- static const u16 indices[4] = {0, 1, 2, 3};
+ static const u16 indices[6] = {0, 1, 2, 0, 2, 3};
video::S3DVertex vertices[4];
driver->setMaterial(m_materials[1]);
@@ -244,7 +254,7 @@ void Sky::render()
vertex.Pos.rotateXZBy(180);
}
}
- driver->drawIndexedTriangleFan(&vertices[0], 4, indices, 2);
+ driver->drawIndexedTriangleList(&vertices[0], 4, indices, 2);
}
}
@@ -270,7 +280,7 @@ void Sky::render()
// Switch from -Z (south) to +Z (north)
vertex.Pos.rotateXZBy(-180);
}
- driver->drawIndexedTriangleFan(&vertices[0], 4, indices, 2);
+ driver->drawIndexedTriangleList(&vertices[0], 4, indices, 2);
}
}
@@ -301,7 +311,7 @@ void Sky::render()
// Switch from -Z (south) to -X (west)
vertex.Pos.rotateXZBy(-90);
}
- driver->drawIndexedTriangleFan(&vertices[0], 4, indices, 2);
+ driver->drawIndexedTriangleList(&vertices[0], 4, indices, 2);
}
// Draw sun
@@ -337,7 +347,7 @@ void Sky::render()
// Switch from -Z (south) to +Z (north)
vertex.Pos.rotateXZBy(-180);
}
- driver->drawIndexedTriangleFan(&vertices[0], 4, indices, 2);
+ driver->drawIndexedTriangleList(&vertices[0], 4, indices, 2);
}
// Draw bottom far cloudy fog thing in front of sun, moon and stars
@@ -346,7 +356,7 @@ void Sky::render()
vertices[1] = video::S3DVertex( 1, -1.0, -1, 0, 1, 0, c, o, t);
vertices[2] = video::S3DVertex( 1, -1.0, 1, 0, 1, 0, c, o, o);
vertices[3] = video::S3DVertex(-1, -1.0, 1, 0, 1, 0, c, t, o);
- driver->drawIndexedTriangleFan(&vertices[0], 4, indices, 2);
+ driver->drawIndexedTriangleList(&vertices[0], 4, indices, 2);
}
}
}
@@ -590,7 +600,7 @@ void Sky::draw_sun(video::IVideoDriver *driver, float sunsize, const video::SCol
* wicked_time_of_day: current time of day, to know where should be the sun in the sky
*/
{
- static const u16 indices[4] = {0, 1, 2, 3};
+ static const u16 indices[] = {0, 1, 2, 0, 2, 3};
std::array<video::S3DVertex, 4> vertices;
if (!m_sun_texture) {
driver->setMaterial(m_materials[1]);
@@ -608,7 +618,7 @@ void Sky::draw_sun(video::IVideoDriver *driver, float sunsize, const video::SCol
for (int i = 0; i < 4; i++) {
draw_sky_body(vertices, -sunsizes[i], sunsizes[i], colors[i]);
place_sky_body(vertices, 90, wicked_time_of_day * 360 - 90);
- driver->drawIndexedTriangleFan(&vertices[0], 4, indices, 2);
+ driver->drawIndexedTriangleList(&vertices[0], 4, indices, 2);
}
} else {
driver->setMaterial(m_materials[3]);
@@ -620,7 +630,7 @@ void Sky::draw_sun(video::IVideoDriver *driver, float sunsize, const video::SCol
c = video::SColor(255, 255, 255, 255);
draw_sky_body(vertices, -d, d, c);
place_sky_body(vertices, 90, wicked_time_of_day * 360 - 90);
- driver->drawIndexedTriangleFan(&vertices[0], 4, indices, 2);
+ driver->drawIndexedTriangleList(&vertices[0], 4, indices, 2);
}
}
@@ -637,7 +647,7 @@ void Sky::draw_moon(video::IVideoDriver *driver, float moonsize, const video::SC
* the sky
*/
{
- static const u16 indices[4] = {0, 1, 2, 3};
+ static const u16 indices[] = {0, 1, 2, 0, 2, 3};
std::array<video::S3DVertex, 4> vertices;
if (!m_moon_texture) {
driver->setMaterial(m_materials[1]);
@@ -661,7 +671,7 @@ void Sky::draw_moon(video::IVideoDriver *driver, float moonsize, const video::SC
for (int i = 0; i < 4; i++) {
draw_sky_body(vertices, moonsizes_1[i], moonsizes_2[i], colors[i]);
place_sky_body(vertices, -90, wicked_time_of_day * 360 - 90);
- driver->drawIndexedTriangleFan(&vertices[0], 4, indices, 2);
+ driver->drawIndexedTriangleList(&vertices[0], 4, indices, 2);
}
} else {
driver->setMaterial(m_materials[4]);
@@ -673,13 +683,12 @@ void Sky::draw_moon(video::IVideoDriver *driver, float moonsize, const video::SC
c = video::SColor(255, 255, 255, 255);
draw_sky_body(vertices, -d, d, c);
place_sky_body(vertices, -90, wicked_time_of_day * 360 - 90);
- driver->drawIndexedTriangleFan(&vertices[0], 4, indices, 2);
+ driver->drawIndexedTriangleList(&vertices[0], 4, indices, 2);
}
}
void Sky::draw_stars(video::IVideoDriver * driver, float wicked_time_of_day)
{
- driver->setMaterial(m_materials[1]);
// Tune values so that stars first appear just after the sun
// disappears over the horizon, and disappear just before the sun
// appears over the horizon.
@@ -687,87 +696,18 @@ void Sky::draw_stars(video::IVideoDriver * driver, float wicked_time_of_day)
// to time 4000.
float tod = wicked_time_of_day < 0.5f ? wicked_time_of_day : (1.0f - wicked_time_of_day);
- float starbrightness = clamp((0.25f - fabsf(tod)) * 20.0f, 0.0f, 1.0f);
-
- float f = starbrightness;
- float d = (0.006 / 2) * m_star_params.scale;
-
- video::SColor starcolor = m_star_params.starcolor;
- starcolor.setAlpha(f * m_star_params.starcolor.getAlpha());
-
- // Stars are only drawn when not fully transparent
- if (m_star_params.starcolor.getAlpha() < 1)
+ float starbrightness = (0.25f - fabsf(tod)) * 20.0f;
+ m_star_color = m_star_params.starcolor;
+ m_star_color.a *= clamp(starbrightness, 0.0f, 1.0f);
+ if (m_star_color.a <= 0.0f) // Stars are only drawn when not fully transparent
return;
-#if ENABLE_GLES
- u16 *indices = new u16[m_star_params.count * 3];
- video::S3DVertex *vertices =
- new video::S3DVertex[m_star_params.count * 3];
- for (u32 i = 0; i < m_star_params.count; i++) {
- indices[i * 3 + 0] = i * 3 + 0;
- indices[i * 3 + 1] = i * 3 + 1;
- indices[i * 3 + 2] = i * 3 + 2;
- v3f r = m_stars[i];
- core::CMatrix4<f32> a;
- a.buildRotateFromTo(v3f(0, 1, 0), r);
- v3f p = v3f(-d, 1, -d);
- v3f p1 = v3f(d, 1, 0);
- v3f p2 = v3f(-d, 1, d);
- a.rotateVect(p);
- a.rotateVect(p1);
- a.rotateVect(p2);
- p.rotateXYBy(wicked_time_of_day * 360 - 90);
- p1.rotateXYBy(wicked_time_of_day * 360 - 90);
- p2.rotateXYBy(wicked_time_of_day * 360 - 90);
- vertices[i * 3 + 0].Pos = p;
- vertices[i * 3 + 0].Color = starcolor;
- vertices[i * 3 + 1].Pos = p1;
- vertices[i * 3 + 1].Color = starcolor;
- vertices[i * 3 + 2].Pos = p2;
- vertices[i * 3 + 2].Color = starcolor;
- }
- driver->drawIndexedTriangleList(vertices, m_star_params.count * 3,
- indices, m_star_params.count);
- delete[] indices;
- delete[] vertices;
-#else
- u16 *indices = new u16[m_star_params.count * 4];
- video::S3DVertex *vertices =
- new video::S3DVertex[m_star_params.count * 4];
- for (u32 i = 0; i < m_star_params.count; i++) {
- indices[i * 4 + 0] = i * 4 + 0;
- indices[i * 4 + 1] = i * 4 + 1;
- indices[i * 4 + 2] = i * 4 + 2;
- indices[i * 4 + 3] = i * 4 + 3;
- v3f r = m_stars[i];
- core::CMatrix4<f32> a;
- a.buildRotateFromTo(v3f(0, 1, 0), r);
- v3f p = v3f(-d, 1, -d);
- v3f p1 = v3f(d, 1, -d);
- v3f p2 = v3f(d, 1, d);
- v3f p3 = v3f(-d, 1, d);
- a.rotateVect(p);
- a.rotateVect(p1);
- a.rotateVect(p2);
- a.rotateVect(p3);
- p.rotateXYBy(wicked_time_of_day * 360 - 90);
- p1.rotateXYBy(wicked_time_of_day * 360 - 90);
- p2.rotateXYBy(wicked_time_of_day * 360 - 90);
- p3.rotateXYBy(wicked_time_of_day * 360 - 90);
- vertices[i * 4 + 0].Pos = p;
- vertices[i * 4 + 0].Color = starcolor;
- vertices[i * 4 + 1].Pos = p1;
- vertices[i * 4 + 1].Color = starcolor;
- vertices[i * 4 + 2].Pos = p2;
- vertices[i * 4 + 2].Color = starcolor;
- vertices[i * 4 + 3].Pos = p3;
- vertices[i * 4 + 3].Color = starcolor;
- }
- driver->drawVertexPrimitiveList(vertices, m_star_params.count * 4,
- indices, m_star_params.count, video::EVT_STANDARD,
- scene::EPT_QUADS, video::EIT_16BIT);
- delete[] indices;
- delete[] vertices;
-#endif
+ m_materials[0].DiffuseColor = m_materials[0].EmissiveColor = m_star_color.toSColor();
+ auto sky_rotation = core::matrix4().setRotationAxisRadians(2.0f * M_PI * (wicked_time_of_day - 0.25f), v3f(0.0f, 0.0f, 1.0f));
+ auto world_matrix = driver->getTransform(video::ETS_WORLD);
+ driver->setTransform(video::ETS_WORLD, world_matrix * sky_rotation);
+ driver->setMaterial(m_materials[0]);
+ driver->drawMeshBuffer(m_stars.get());
+ driver->setTransform(video::ETS_WORLD, world_matrix);
}
void Sky::draw_sky_body(std::array<video::S3DVertex, 4> &vertices, float pos_1, float pos_2, const video::SColor &c)
@@ -822,7 +762,7 @@ void Sky::setSunTexture(std::string sun_texture,
m_sun_texture = tsrc->getTextureForMesh(m_sun_params.texture);
if (m_sun_texture) {
- m_materials[3] = m_materials[0];
+ m_materials[3] = baseMaterial();
m_materials[3].setTexture(0, m_sun_texture);
m_materials[3].MaterialType = video::
EMT_TRANSPARENT_ALPHA_CHANNEL;
@@ -870,7 +810,7 @@ void Sky::setMoonTexture(std::string moon_texture,
m_moon_texture = tsrc->getTextureForMesh(m_moon_params.texture);
if (m_moon_texture) {
- m_materials[4] = m_materials[0];
+ m_materials[4] = baseMaterial();
m_materials[4].setTexture(0, m_moon_texture);
m_materials[4].MaterialType = video::
EMT_TRANSPARENT_ALPHA_CHANNEL;
@@ -892,19 +832,58 @@ void Sky::setStarCount(u16 star_count, bool force_update)
// Allow force updating star count at game init.
if (m_star_params.count != star_count || force_update) {
m_star_params.count = star_count;
- m_stars.clear();
- // Rebuild the stars surrounding the camera
- for (u16 i = 0; i < star_count; i++) {
- v3f star = v3f(
- myrand_range(-10000, 10000),
- myrand_range(-10000, 10000),
- myrand_range(-10000, 10000)
- );
-
- star.normalize();
- m_stars.emplace_back(star);
- }
+ m_seed = (u64)myrand() << 32 | myrand();
+ updateStars();
+ }
+}
+
+void Sky::updateStars()
+{
+ m_stars.reset(new scene::SMeshBuffer());
+ // Stupid IrrLicht doesn’t allow non-indexed rendering, and indexed quad
+ // rendering is slow due to lack of hardware support. So as indices are
+ // 16-bit and there are 4 vertices per star... the limit is 2^16/4 = 0x4000.
+ // That should be well enough actually.
+ if (m_star_params.count > 0x4000) {
+ warningstream << "Requested " << m_star_params.count << " stars but " << 0x4000 << " is the max\n";
+ m_star_params.count = 0x4000;
+ }
+ m_stars->Vertices.reallocate(4 * m_star_params.count);
+ m_stars->Indices.reallocate(6 * m_star_params.count);
+
+ video::SColor fallback_color = m_star_params.starcolor; // used on GLES 2 “without shaders”
+ PcgRandom rgen(m_seed);
+ float d = (0.006 / 2) * m_star_params.scale;
+ for (u16 i = 0; i < m_star_params.count; i++) {
+ v3f r = v3f(
+ rgen.range(-10000, 10000),
+ rgen.range(-10000, 10000),
+ rgen.range(-10000, 10000)
+ );
+ core::CMatrix4<f32> a;
+ a.buildRotateFromTo(v3f(0, 1, 0), r);
+ v3f p = v3f(-d, 1, -d);
+ v3f p1 = v3f(d, 1, -d);
+ v3f p2 = v3f(d, 1, d);
+ v3f p3 = v3f(-d, 1, d);
+ a.rotateVect(p);
+ a.rotateVect(p1);
+ a.rotateVect(p2);
+ a.rotateVect(p3);
+ m_stars->Vertices.push_back(video::S3DVertex(p, {}, fallback_color, {}));
+ m_stars->Vertices.push_back(video::S3DVertex(p1, {}, fallback_color, {}));
+ m_stars->Vertices.push_back(video::S3DVertex(p2, {}, fallback_color, {}));
+ m_stars->Vertices.push_back(video::S3DVertex(p3, {}, fallback_color, {}));
+ }
+ for (u16 i = 0; i < m_star_params.count; i++) {
+ m_stars->Indices.push_back(i * 4 + 0);
+ m_stars->Indices.push_back(i * 4 + 1);
+ m_stars->Indices.push_back(i * 4 + 2);
+ m_stars->Indices.push_back(i * 4 + 2);
+ m_stars->Indices.push_back(i * 4 + 3);
+ m_stars->Indices.push_back(i * 4 + 0);
}
+ m_stars->setHardwareMappingHint(scene::EHM_STATIC);
}
void Sky::setSkyColors(const SkyColor &sky_color)
@@ -936,7 +915,7 @@ void Sky::addTextureToSkybox(std::string texture, int material_id,
// Keep a list of texture names handy.
m_sky_params.textures.emplace_back(texture);
video::ITexture *result = tsrc->getTextureForMesh(texture);
- m_materials[material_id+5] = m_materials[0];
+ m_materials[material_id+5] = baseMaterial();
m_materials[material_id+5].setTexture(0, result);
m_materials[material_id+5].MaterialType = video::EMT_SOLID;
}
diff --git a/src/client/sky.h b/src/client/sky.h
index 3227e8f59..10e1cd976 100644
--- a/src/client/sky.h
+++ b/src/client/sky.h
@@ -21,6 +21,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <array>
#include "camera.h"
#include "irrlichttypes_extrabloated.h"
+#include "irr_ptr.h"
+#include "shader.h"
#include "skyparams.h"
#pragma once
@@ -34,7 +36,7 @@ class Sky : public scene::ISceneNode
{
public:
//! constructor
- Sky(s32 id, ITextureSource *tsrc);
+ Sky(s32 id, ITextureSource *tsrc, IShaderSource *ssrc);
virtual void OnRegisterSceneNode();
@@ -77,7 +79,7 @@ public:
void setStarsVisible(bool stars_visible) { m_star_params.visible = stars_visible; }
void setStarCount(u16 star_count, bool force_update);
void setStarColor(video::SColor star_color) { m_star_params.starcolor = star_color; }
- void setStarScale(f32 star_scale) { m_star_params.scale = star_scale; }
+ void setStarScale(f32 star_scale) { m_star_params.scale = star_scale; updateStars(); }
bool getCloudsVisible() const { return m_clouds_visible && m_clouds_enabled; }
const video::SColorf &getCloudColor() const { return m_cloudcolor_f; }
@@ -101,6 +103,8 @@ public:
void clearSkyboxTextures() { m_sky_params.textures.clear(); }
void addTextureToSkybox(std::string texture, int material_id,
ITextureSource *tsrc);
+ const video::SColorf &getCurrentStarColor() const { return m_star_color; }
+
private:
aabb3f m_box;
video::SMaterial m_materials[SKY_MATERIAL_COUNT];
@@ -154,6 +158,7 @@ private:
bool m_clouds_enabled = true; // Initialised to true, reset only by set_sky API
bool m_directional_colored_fog;
bool m_in_clouds = true; // Prevent duplicating bools to remember old values
+ bool m_enable_shaders = false;
video::SColorf m_bgcolor_bright_f = video::SColorf(1.0f, 1.0f, 1.0f, 1.0f);
video::SColorf m_skycolor_bright_f = video::SColorf(1.0f, 1.0f, 1.0f, 1.0f);
@@ -178,13 +183,17 @@ private:
bool m_default_tint = true;
- std::vector<v3f> m_stars;
+ u64 m_seed = 0;
+ irr_ptr<scene::SMeshBuffer> m_stars;
+ video::SColorf m_star_color;
video::ITexture *m_sun_texture;
video::ITexture *m_moon_texture;
video::ITexture *m_sun_tonemap;
video::ITexture *m_moon_tonemap;
+ void updateStars();
+
void draw_sun(video::IVideoDriver *driver, float sunsize, const video::SColor &suncolor,
const video::SColor &suncolor2, float wicked_time_of_day);
void draw_moon(video::IVideoDriver *driver, float moonsize, const video::SColor &mooncolor,
diff --git a/src/client/sound_openal.cpp b/src/client/sound_openal.cpp
index 20a651c1d..f4e61f93e 100644
--- a/src/client/sound_openal.cpp
+++ b/src/client/sound_openal.cpp
@@ -28,6 +28,7 @@ with this program; ifnot, write to the Free Software Foundation, Inc.,
#include <alc.h>
//#include <alext.h>
#elif defined(__APPLE__)
+ #define OPENAL_DEPRECATED
#include <OpenAL/al.h>
#include <OpenAL/alc.h>
//#include <OpenAL/alext.h>
@@ -337,14 +338,12 @@ private:
};
std::unordered_map<int, FadeState> m_sounds_fading;
- float m_fade_delay;
public:
OpenALSoundManager(SoundManagerSingleton *smg, OnDemandSoundFetcher *fetcher):
m_fetcher(fetcher),
m_device(smg->m_device.get()),
m_context(smg->m_context.get()),
- m_next_id(1),
- m_fade_delay(0)
+ m_next_id(1)
{
infostream << "Audio: Initialized: OpenAL " << std::endl;
}
@@ -616,38 +615,45 @@ public:
void fadeSound(int soundid, float step, float gain)
{
- m_sounds_fading[soundid] = FadeState(step, getSoundGain(soundid), gain);
+ // Ignore the command if step isn't valid.
+ if (step == 0)
+ return;
+ float current_gain = getSoundGain(soundid);
+ step = gain - current_gain > 0 ? abs(step) : -abs(step);
+ if (m_sounds_fading.find(soundid) != m_sounds_fading.end()) {
+ auto current_fade = m_sounds_fading[soundid];
+ // Do not replace the fade if it's equivalent.
+ if (current_fade.target_gain == gain && current_fade.step == step)
+ return;
+ m_sounds_fading.erase(soundid);
+ }
+ gain = rangelim(gain, 0, 1);
+ m_sounds_fading[soundid] = FadeState(step, current_gain, gain);
}
void doFades(float dtime)
{
- m_fade_delay += dtime;
-
- if (m_fade_delay < 0.1f)
- return;
+ for (auto i = m_sounds_fading.begin(); i != m_sounds_fading.end();) {
+ FadeState& fade = i->second;
+ assert(fade.step != 0);
+ fade.current_gain += (fade.step * dtime);
- float chkGain = 0;
- for (auto i = m_sounds_fading.begin();
- i != m_sounds_fading.end();) {
- if (i->second.step < 0.f)
- chkGain = -(i->second.current_gain);
+ if (fade.step < 0.f)
+ fade.current_gain = std::max(fade.current_gain, fade.target_gain);
else
- chkGain = i->second.current_gain;
+ fade.current_gain = std::min(fade.current_gain, fade.target_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);
+ if (fade.current_gain <= 0.f)
+ stopSound(i->first);
+ else
+ updateSoundGain(i->first, fade.current_gain);
+ // The increment must happen during the erase call, or else it'll segfault.
+ if (fade.current_gain == fade.target_gain)
m_sounds_fading.erase(i++);
- }
+ else
+ i++;
}
- m_fade_delay = 0;
}
bool soundExists(int sound)
diff --git a/src/client/wieldmesh.cpp b/src/client/wieldmesh.cpp
index 8cd3e29a9..ad583210a 100644
--- a/src/client/wieldmesh.cpp
+++ b/src/client/wieldmesh.cpp
@@ -303,13 +303,24 @@ void WieldMeshSceneNode::setExtruded(const std::string &imagename,
}
}
-scene::SMesh *createSpecialNodeMesh(Client *client, content_t id, std::vector<ItemPartColor> *colors)
+scene::SMesh *createSpecialNodeMesh(Client *client, content_t id, std::vector<ItemPartColor> *colors, const ContentFeatures &f)
{
- MeshMakeData mesh_make_data(client, false, false);
+ MeshMakeData mesh_make_data(client, false);
MeshCollector collector;
mesh_make_data.setSmoothLighting(false);
MapblockMeshGenerator gen(&mesh_make_data, &collector);
- gen.renderSingle(id);
+ u8 param2 = 0;
+ if (f.param_type_2 == CPT2_WALLMOUNTED ||
+ f.param_type_2 == CPT2_COLORED_WALLMOUNTED) {
+ if (f.drawtype == NDT_TORCHLIKE)
+ param2 = 1;
+ else if (f.drawtype == NDT_SIGNLIKE ||
+ f.drawtype == NDT_NODEBOX ||
+ f.drawtype == NDT_MESH)
+ param2 = 4;
+ }
+ gen.renderSingle(id, param2);
+
colors->clear();
scene::SMesh *mesh = new scene::SMesh();
for (auto &prebuffers : collector.prebuffers)
@@ -319,8 +330,9 @@ scene::SMesh *createSpecialNodeMesh(Client *client, content_t id, std::vector<It
p.layer.texture = frame.texture;
p.layer.normal_texture = frame.normal_texture;
}
- for (video::S3DVertex &v : p.vertices)
+ for (video::S3DVertex &v : p.vertices) {
v.Color.setAlpha(255);
+ }
scene::SMeshBuffer *buf = new scene::SMeshBuffer();
buf->Material.setTexture(0, p.layer.texture);
p.layer.applyMaterialOptions(buf->Material);
@@ -368,73 +380,61 @@ void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client, bool che
// Handle nodes
// See also CItemDefManager::createClientCached()
if (def.type == ITEM_NODE) {
- if (f.mesh_ptr[0]) {
- // e.g. mesh nodes and nodeboxes
- mesh = cloneMesh(f.mesh_ptr[0]);
- postProcessNodeMesh(mesh, f, m_enable_shaders, true,
- &m_material_type, &m_colors);
+ bool cull_backface = f.needsBackfaceCulling();
+
+ // Select rendering method
+ switch (f.drawtype) {
+ case NDT_AIRLIKE:
+ setExtruded("no_texture_airlike.png", "",
+ v3f(1.0, 1.0, 1.0), tsrc, 1);
+ break;
+ case NDT_SIGNLIKE:
+ case NDT_TORCHLIKE:
+ case NDT_RAILLIKE:
+ case NDT_PLANTLIKE:
+ case NDT_PLANTLIKE_ROOTED:
+ case NDT_FLOWINGLIQUID: {
+ v3f wscale = def.wield_scale;
+ if (f.drawtype == NDT_FLOWINGLIQUID)
+ wscale.Z *= 0.1f;
+ setExtruded(tsrc->getTextureName(f.tiles[0].layers[0].texture_id),
+ tsrc->getTextureName(f.tiles[0].layers[1].texture_id),
+ wscale, tsrc,
+ f.tiles[0].layers[0].animation_frame_count);
+ // Add color
+ const TileLayer &l0 = f.tiles[0].layers[0];
+ m_colors.emplace_back(l0.has_color, l0.color);
+ const TileLayer &l1 = f.tiles[0].layers[1];
+ m_colors.emplace_back(l1.has_color, l1.color);
+ break;
+ }
+ case NDT_NORMAL:
+ case NDT_ALLFACES:
+ case NDT_LIQUID:
+ setCube(f, def.wield_scale);
+ break;
+ default:
+ // Render non-trivial drawtypes like the actual node
+ mesh = createSpecialNodeMesh(client, id, &m_colors, f);
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 {
- switch (f.drawtype) {
- case NDT_AIRLIKE: {
- changeToMesh(nullptr);
- break;
- }
- case NDT_PLANTLIKE: {
- setExtruded(tsrc->getTextureName(f.tiles[0].layers[0].texture_id),
- tsrc->getTextureName(f.tiles[0].layers[1].texture_id),
- def.wield_scale, tsrc,
- f.tiles[0].layers[0].animation_frame_count);
- // Add color
- const TileLayer &l0 = f.tiles[0].layers[0];
- m_colors.emplace_back(l0.has_color, l0.color);
- const TileLayer &l1 = f.tiles[0].layers[1];
- m_colors.emplace_back(l1.has_color, l1.color);
- break;
- }
- case NDT_PLANTLIKE_ROOTED: {
- setExtruded(tsrc->getTextureName(f.special_tiles[0].layers[0].texture_id),
- "", def.wield_scale, tsrc,
- f.special_tiles[0].layers[0].animation_frame_count);
- // Add color
- const TileLayer &l0 = f.special_tiles[0].layers[0];
- m_colors.emplace_back(l0.has_color, l0.color);
- break;
- }
- case NDT_NORMAL:
- case NDT_ALLFACES:
- case NDT_LIQUID:
- case NDT_FLOWINGLIQUID: {
- setCube(f, def.wield_scale);
- break;
- }
- default: {
- mesh = createSpecialNodeMesh(client, id, &m_colors);
- changeToMesh(mesh);
- mesh->drop();
- m_meshnode->setScale(
- def.wield_scale * WIELD_SCALE_FACTOR
- / (BS * f.visual_scale));
- }
- }
+ def.wield_scale * WIELD_SCALE_FACTOR
+ / (BS * f.visual_scale));
+ break;
}
+
u32 material_count = m_meshnode->getMaterialCount();
for (u32 i = 0; i < material_count; ++i) {
video::SMaterial &material = m_meshnode->getMaterial(i);
material.MaterialType = m_material_type;
material.MaterialTypeParam = 0.5f;
- material.setFlag(video::EMF_BACK_FACE_CULLING, true);
+ material.setFlag(video::EMF_BACK_FACE_CULLING, cull_backface);
material.setFlag(video::EMF_BILINEAR_FILTER, m_bilinear_filter);
material.setFlag(video::EMF_TRILINEAR_FILTER, m_trilinear_filter);
}
return;
- }
- else if (!def.inventory_image.empty()) {
+ } else if (!def.inventory_image.empty()) {
setExtruded(def.inventory_image, def.inventory_overlay, def.wield_scale,
tsrc, 1);
m_colors.emplace_back();
@@ -529,6 +529,8 @@ void getItemMesh(Client *client, const ItemStack &item, ItemMesh *result)
// Shading is on by default
result->needs_shading = true;
+ bool cull_backface = f.needsBackfaceCulling();
+
// If inventory_image is defined, it overrides everything else
if (!def.inventory_image.empty()) {
mesh = getExtrudedMesh(tsrc, def.inventory_image,
@@ -538,51 +540,56 @@ void getItemMesh(Client *client, const ItemStack &item, ItemMesh *result)
result->buffer_colors.emplace_back(true, video::SColor(0xFFFFFFFF));
// Items with inventory images do not need shading
result->needs_shading = false;
+ } else if (def.type == ITEM_NODE && f.drawtype == NDT_AIRLIKE) {
+ // Fallback image for airlike node
+ mesh = getExtrudedMesh(tsrc, "no_texture_airlike.png",
+ def.inventory_overlay);
+ 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));
+ switch (f.drawtype) {
+ case NDT_NORMAL:
+ case NDT_ALLFACES:
+ case NDT_LIQUID:
+ case NDT_FLOWINGLIQUID: {
+ scene::IMesh *cube = g_extrusion_mesh_cache->createCube();
+ mesh = cloneMesh(cube);
+ cube->drop();
+ if (f.drawtype == NDT_FLOWINGLIQUID) {
+ scaleMesh(mesh, v3f(1.2, 0.03, 1.2));
+ translateMesh(mesh, v3f(0, -0.57, 0));
+ } else
+ scaleMesh(mesh, v3f(1.2, 1.2, 1.2));
+ // add overlays
postProcessNodeMesh(mesh, f, false, false, nullptr,
- &result->buffer_colors);
- } else {
- switch (f.drawtype) {
- case NDT_PLANTLIKE: {
- mesh = getExtrudedMesh(tsrc,
- tsrc->getTextureName(f.tiles[0].layers[0].texture_id),
- tsrc->getTextureName(f.tiles[0].layers[1].texture_id));
- // Add color
- const TileLayer &l0 = f.tiles[0].layers[0];
- result->buffer_colors.emplace_back(l0.has_color, l0.color);
- const TileLayer &l1 = f.tiles[0].layers[1];
- result->buffer_colors.emplace_back(l1.has_color, l1.color);
- break;
- }
- case NDT_PLANTLIKE_ROOTED: {
- mesh = getExtrudedMesh(tsrc,
- tsrc->getTextureName(f.special_tiles[0].layers[0].texture_id), "");
- // Add color
- const TileLayer &l0 = f.special_tiles[0].layers[0];
- result->buffer_colors.emplace_back(l0.has_color, l0.color);
- break;
- }
- case NDT_NORMAL:
- case NDT_ALLFACES:
- case NDT_LIQUID:
- case NDT_FLOWINGLIQUID: {
- scene::IMesh *cube = g_extrusion_mesh_cache->createCube();
- mesh = cloneMesh(cube);
- cube->drop();
- scaleMesh(mesh, v3f(1.2, 1.2, 1.2));
- // add overlays
- postProcessNodeMesh(mesh, f, false, false, nullptr,
- &result->buffer_colors);
- break;
- }
- default: {
- mesh = createSpecialNodeMesh(client, id, &result->buffer_colors);
- scaleMesh(mesh, v3f(0.12, 0.12, 0.12));
- }
- }
+ &result->buffer_colors, true);
+ if (f.drawtype == NDT_ALLFACES)
+ scaleMesh(mesh, v3f(f.visual_scale));
+ break;
+ }
+ case NDT_PLANTLIKE: {
+ mesh = getExtrudedMesh(tsrc,
+ tsrc->getTextureName(f.tiles[0].layers[0].texture_id),
+ tsrc->getTextureName(f.tiles[0].layers[1].texture_id));
+ // Add color
+ const TileLayer &l0 = f.tiles[0].layers[0];
+ result->buffer_colors.emplace_back(l0.has_color, l0.color);
+ const TileLayer &l1 = f.tiles[0].layers[1];
+ result->buffer_colors.emplace_back(l1.has_color, l1.color);
+ break;
+ }
+ case NDT_PLANTLIKE_ROOTED: {
+ mesh = getExtrudedMesh(tsrc,
+ tsrc->getTextureName(f.special_tiles[0].layers[0].texture_id), "");
+ // Add color
+ const TileLayer &l0 = f.special_tiles[0].layers[0];
+ result->buffer_colors.emplace_back(l0.has_color, l0.color);
+ break;
+ }
+ default:
+ // Render non-trivial drawtypes like the actual node
+ mesh = createSpecialNodeMesh(client, id, &result->buffer_colors, f);
+ scaleMesh(mesh, v3f(0.12, 0.12, 0.12));
+ break;
}
u32 mc = mesh->getMeshBufferCount();
@@ -593,7 +600,7 @@ void getItemMesh(Client *client, const ItemStack &item, ItemMesh *result)
material.MaterialTypeParam = 0.5f;
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_BACK_FACE_CULLING, cull_backface);
material.setFlag(video::EMF_LIGHTING, false);
}
diff --git a/src/clientiface.cpp b/src/clientiface.cpp
index 602a44c90..01852c5d1 100644
--- a/src/clientiface.cpp
+++ b/src/clientiface.cpp
@@ -80,10 +80,11 @@ LuaEntitySAO *getAttachedObject(PlayerSAO *sao, ServerEnvironment *env)
int id;
std::string bone;
v3f dummy;
- sao->getAttachment(&id, &bone, &dummy, &dummy);
+ bool force_visible;
+ sao->getAttachment(&id, &bone, &dummy, &dummy, &force_visible);
ServerActiveObject *ao = env->getActiveObject(id);
while (id && ao) {
- ao->getAttachment(&id, &bone, &dummy, &dummy);
+ ao->getAttachment(&id, &bone, &dummy, &dummy, &force_visible);
if (id)
ao = env->getActiveObject(id);
}
@@ -98,7 +99,6 @@ void RemoteClient::GetNextBlocks (
{
// Increment timers
m_nothing_to_send_pause_timer -= dtime;
- m_nearest_unsent_reset_timer += dtime;
if (m_nothing_to_send_pause_timer >= 0)
return;
@@ -138,34 +138,6 @@ void RemoteClient::GetNextBlocks (
camera_dir.rotateYZBy(sao->getLookPitch());
camera_dir.rotateXZBy(sao->getRotation().Y);
- /*infostream<<"camera_dir=("<<camera_dir.X<<","<<camera_dir.Y<<","
- <<camera_dir.Z<<")"<<std::endl;*/
-
- /*
- Get the starting value of the block finder radius.
- */
-
- if (m_last_center != center) {
- m_nearest_unsent_d = 0;
- m_last_center = center;
- }
-
- /*infostream<<"m_nearest_unsent_reset_timer="
- <<m_nearest_unsent_reset_timer<<std::endl;*/
-
- // Reset periodically to workaround for some bugs or stuff
- if (m_nearest_unsent_reset_timer > 20.0f) {
- m_nearest_unsent_reset_timer = 0.0f;
- m_nearest_unsent_d = 0;
- //infostream<<"Resetting m_nearest_unsent_d for "
- // <<server->getPlayerName(peer_id)<<std::endl;
- }
-
- //s16 last_nearest_unsent_d = m_nearest_unsent_d;
- s16 d_start = m_nearest_unsent_d;
-
- //infostream<<"d_start="<<d_start<<std::endl;
-
u16 max_simul_sends_usually = m_max_simul_sends;
/*
@@ -197,6 +169,29 @@ void RemoteClient::GetNextBlocks (
s16 wanted_range = sao->getWantedRange() + 1;
float camera_fov = sao->getFov();
+ /*
+ Get the starting value of the block finder radius.
+ */
+ if (m_last_center != center) {
+ m_nearest_unsent_d = 0;
+ m_last_center = center;
+ }
+ // reset the unsent distance if the view angle has changed more that 10% of the fov
+ // (this matches isBlockInSight which allows for an extra 10%)
+ if (camera_dir.dotProduct(m_last_camera_dir) < std::cos(camera_fov * 0.1f)) {
+ m_nearest_unsent_d = 0;
+ m_last_camera_dir = camera_dir;
+ }
+ if (m_nearest_unsent_d > 0) {
+ // make sure any blocks modified since the last time we sent blocks are resent
+ for (const v3s16 &p : m_blocks_modified) {
+ m_nearest_unsent_d = std::min(m_nearest_unsent_d, center.getDistanceFrom(p));
+ }
+ }
+ m_blocks_modified.clear();
+
+ s16 d_start = m_nearest_unsent_d;
+
// Distrust client-sent FOV and get server-set player object property
// zoom FOV (degrees) as a check to avoid hacked clients using FOV to load
// distant world.
@@ -211,13 +206,13 @@ void RemoteClient::GetNextBlocks (
wanted_range);
const s16 d_blocks_in_sight = full_d_max * BS * MAP_BLOCKSIZE;
- s16 d_max = full_d_max;
s16 d_max_gen = std::min(adjustDist(m_max_gen_distance, prop_zoom_fov),
wanted_range);
- // Don't loop very much at a time, adjust with distance,
- // do more work per RTT with greater distances.
- s16 max_d_increment_at_time = full_d_max / 9 + 1;
+ s16 d_max = full_d_max;
+
+ // Don't loop very much at a time
+ s16 max_d_increment_at_time = 2;
if (d_max > d_start + max_d_increment_at_time)
d_max = d_start + max_d_increment_at_time;
@@ -310,20 +305,14 @@ void RemoteClient::GetNextBlocks (
*/
MapBlock *block = env->getMap().getBlockNoCreateNoEx(p);
- bool surely_not_found_on_disk = false;
- bool block_is_invalid = false;
+ bool block_not_found = false;
if (block) {
// Reset usage timer, this block will be of use in the future.
block->resetUsageTimer();
- // Block is dummy if data doesn't exist.
- // It means it has been not found from disk and not generated
- if (block->isDummy()) {
- surely_not_found_on_disk = true;
- }
-
- if (!block->isGenerated())
- block_is_invalid = true;
+ // Check whether the block exists (with data)
+ if (block->isDummy() || !block->isGenerated())
+ block_not_found = true;
/*
If block is not close, don't send it unless it is near
@@ -337,7 +326,7 @@ void RemoteClient::GetNextBlocks (
continue;
}
- if (m_occ_cull && !block_is_invalid &&
+ if (m_occ_cull && !block_not_found &&
env->getMap().isBlockOccluded(block, cam_pos_nodes)) {
continue;
}
@@ -347,7 +336,7 @@ void RemoteClient::GetNextBlocks (
If block has been marked to not exist on disk (dummy) or is
not generated and generating new ones is not wanted, skip block.
*/
- if (!generate && (surely_not_found_on_disk || block_is_invalid)) {
+ if (!generate && block_not_found) {
// get next one.
continue;
}
@@ -355,7 +344,7 @@ void RemoteClient::GetNextBlocks (
/*
Add inexistent block to emerge queue.
*/
- if (block == NULL || surely_not_found_on_disk || block_is_invalid) {
+ if (block == NULL || block_not_found) {
if (emerge->enqueueBlockEmerge(peer_id, p, generate)) {
if (nearest_emerged_d == -1)
nearest_emerged_d = d;
@@ -408,21 +397,18 @@ queue_full_break:
void RemoteClient::GotBlock(v3s16 p)
{
- if (m_blocks_modified.find(p) == m_blocks_modified.end()) {
- if (m_blocks_sending.find(p) != m_blocks_sending.end())
- m_blocks_sending.erase(p);
- else
- m_excess_gotblocks++;
-
+ if (m_blocks_sending.find(p) != m_blocks_sending.end()) {
+ m_blocks_sending.erase(p);
+ // only add to sent blocks if it actually was sending
+ // (it might have been modified since)
m_blocks_sent.insert(p);
+ } else {
+ m_excess_gotblocks++;
}
}
void RemoteClient::SentBlock(v3s16 p)
{
- if (m_blocks_modified.find(p) != m_blocks_modified.end())
- m_blocks_modified.erase(p);
-
if (m_blocks_sending.find(p) == m_blocks_sending.end())
m_blocks_sending[p] = 0.0f;
else
@@ -432,29 +418,24 @@ void RemoteClient::SentBlock(v3s16 p)
void RemoteClient::SetBlockNotSent(v3s16 p)
{
- m_nearest_unsent_d = 0;
m_nothing_to_send_pause_timer = 0;
- if (m_blocks_sending.find(p) != m_blocks_sending.end())
- m_blocks_sending.erase(p);
- if (m_blocks_sent.find(p) != m_blocks_sent.end())
- m_blocks_sent.erase(p);
- m_blocks_modified.insert(p);
+ // remove the block from sending and sent sets,
+ // and mark as modified if found
+ if (m_blocks_sending.erase(p) + m_blocks_sent.erase(p) > 0)
+ m_blocks_modified.insert(p);
}
void RemoteClient::SetBlocksNotSent(std::map<v3s16, MapBlock*> &blocks)
{
- m_nearest_unsent_d = 0;
m_nothing_to_send_pause_timer = 0;
for (auto &block : blocks) {
v3s16 p = block.first;
- m_blocks_modified.insert(p);
-
- if (m_blocks_sending.find(p) != m_blocks_sending.end())
- m_blocks_sending.erase(p);
- if (m_blocks_sent.find(p) != m_blocks_sent.end())
- m_blocks_sent.erase(p);
+ // remove the block from sending and sent sets,
+ // and mark as modified if found
+ if (m_blocks_sending.erase(p) + m_blocks_sent.erase(p) > 0)
+ m_blocks_modified.insert(p);
}
}
diff --git a/src/clientiface.h b/src/clientiface.h
index 83fa6fe99..eabffb0b6 100644
--- a/src/clientiface.h
+++ b/src/clientiface.h
@@ -364,7 +364,7 @@ private:
std::set<v3s16> m_blocks_sent;
s16 m_nearest_unsent_d = 0;
v3s16 m_last_center;
- float m_nearest_unsent_reset_timer = 0.0f;
+ v3f m_last_camera_dir;
const u16 m_max_simul_sends;
const float m_min_time_from_building;
@@ -384,10 +384,10 @@ private:
std::map<v3s16, float> m_blocks_sending;
/*
- Blocks that have been modified since last sending them.
- These blocks will not be marked as sent, even if the
- client reports it has received them to account for blocks
- that are being modified while on the line.
+ Blocks that have been modified since blocks were
+ sent to the client last (getNextBlocks()).
+ This is used to reset the unsent distance, so that
+ modified blocks are resent to the client.
List of block positions.
*/
diff --git a/src/content/CMakeLists.txt b/src/content/CMakeLists.txt
index 5adcf6b1e..6dd049418 100644
--- a/src/content/CMakeLists.txt
+++ b/src/content/CMakeLists.txt
@@ -1,6 +1,5 @@
set(content_SRCS
${CMAKE_CURRENT_SOURCE_DIR}/content.cpp
- ${CMAKE_CURRENT_SOURCE_DIR}/packages.cpp
${CMAKE_CURRENT_SOURCE_DIR}/mods.cpp
${CMAKE_CURRENT_SOURCE_DIR}/subgames.cpp
PARENT_SCOPE
diff --git a/src/content/packages.cpp b/src/content/packages.cpp
deleted file mode 100644
index 2d488eb76..000000000
--- a/src/content/packages.cpp
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
-Minetest
-Copyright (C) 2018 rubenwardy <rw@rubenwardy.com>
-
-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 "content/packages.h"
-#include "log.h"
-#include "filesys.h"
-#include "porting.h"
-#include "settings.h"
-#include "content/mods.h"
-#include "content/subgames.h"
-
-std::string Package::getDownloadURL(const std::string &baseURL) const
-{
- return baseURL + "/packages/" + author + "/" + name + "/releases/" +
- std::to_string(release) + "/download/";
-}
-
-#if USE_CURL
-std::vector<Package> getPackagesFromURL(const std::string &url)
-{
- std::vector<std::string> extra_headers;
- extra_headers.emplace_back("Accept: application/json");
-
- Json::Value json = fetchJsonValue(url, &extra_headers);
- if (!json.isArray()) {
- errorstream << "Invalid JSON download " << std::endl;
- return std::vector<Package>();
- }
-
- std::vector<Package> packages;
-
- // Note: `unsigned int` is required to index JSON
- for (unsigned int i = 0; i < json.size(); ++i) {
- Package package;
-
- package.author = json[i]["author"].asString();
- package.name = json[i]["name"].asString();
- package.title = json[i]["title"].asString();
- package.type = json[i]["type"].asString();
- package.shortDesc = json[i]["shortDesc"].asString();
- package.release = json[i]["release"].asInt();
- if (json[i].isMember("thumbnail"))
- package.thumbnail = json[i]["thumbnail"].asString();
-
- if (package.valid())
- packages.push_back(package);
- else
- errorstream << "Invalid package at " << i << std::endl;
- }
-
- return packages;
-}
-#endif
diff --git a/src/content/packages.h b/src/content/packages.h
deleted file mode 100644
index 9029475ef..000000000
--- a/src/content/packages.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
-Minetest
-Copyright (C) 2018 rubenwardy <rw@rubenwardy.com>
-
-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.
-*/
-
-#pragma once
-#include "config.h"
-#include "convert_json.h"
-#include "irrlichttypes.h"
-
-struct Package
-{
- std::string author;
- std::string name; // Technical name
- std::string title;
- std::string type; // One of "mod", "game", or "txp"
-
- std::string shortDesc;
- u32 release;
- std::string thumbnail;
-
- bool valid() const
- {
- return !(author.empty() || name.empty() || title.empty() ||
- type.empty() || release <= 0);
- }
-
- std::string getDownloadURL(const std::string &baseURL) const;
-};
-
-#if USE_CURL
-std::vector<Package> getPackagesFromURL(const std::string &url);
-#else
-inline std::vector<Package> getPackagesFromURL(const std::string &url)
-{
- return std::vector<Package>();
-}
-#endif
diff --git a/src/content/subgames.cpp b/src/content/subgames.cpp
index 170f54e20..c6350f2dd 100644
--- a/src/content/subgames.cpp
+++ b/src/content/subgames.cpp
@@ -31,12 +31,20 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "client/tile.h" // getImagePath
#endif
+// The maximum number of identical world names allowed
+#define MAX_WORLD_NAMES 100
+
+namespace
+{
+
bool getGameMinetestConfig(const std::string &game_path, Settings &conf)
{
std::string conf_path = game_path + DIR_DELIM + "minetest.conf";
return conf.readConfigFile(conf_path.c_str());
}
+}
+
struct GameFindPath
{
std::string path;
@@ -213,6 +221,21 @@ bool getWorldExists(const std::string &world_path)
fs::PathExists(world_path + DIR_DELIM + "world.mt"));
}
+//! Try to get the displayed name of a world
+std::string getWorldName(const std::string &world_path, const std::string &default_name)
+{
+ std::string conf_path = world_path + DIR_DELIM + "world.mt";
+ Settings conf;
+ bool succeeded = conf.readConfigFile(conf_path.c_str());
+ if (!succeeded) {
+ return default_name;
+ }
+
+ if (!conf.exists("world_name"))
+ return default_name;
+ return conf.get("world_name");
+}
+
std::string getWorldGameId(const std::string &world_path, bool can_be_legacy)
{
std::string conf_path = world_path + DIR_DELIM + "world.mt";
@@ -259,7 +282,7 @@ std::vector<WorldSpec> getAvailableWorlds()
if (!dln.dir)
continue;
std::string fullpath = worldspath + DIR_DELIM + dln.name;
- std::string name = dln.name;
+ std::string name = getWorldName(fullpath, dln.name);
// Just allow filling in the gameid always for now
bool can_be_legacy = true;
std::string gameid = getWorldGameId(fullpath, can_be_legacy);
@@ -288,27 +311,47 @@ std::vector<WorldSpec> getAvailableWorlds()
return worlds;
}
-bool loadGameConfAndInitWorld(const std::string &path, const SubgameSpec &gamespec)
+void loadGameConfAndInitWorld(const std::string &path, const std::string &name,
+ const SubgameSpec &gamespec, bool create_world)
{
+ std::string final_path = path;
+
+ // If we're creating a new world, ensure that the path isn't already taken
+ if (create_world) {
+ int counter = 1;
+ while (fs::PathExists(final_path) && counter < MAX_WORLD_NAMES) {
+ final_path = path + "_" + std::to_string(counter);
+ counter++;
+ }
+
+ if (fs::PathExists(final_path)) {
+ throw BaseException("Too many similar filenames");
+ }
+ }
+
// Override defaults with those provided by the game.
// We clear and reload the defaults because the defaults
// might have been overridden by other subgame config
// files that were loaded before.
g_settings->clearDefaults();
set_default_settings(g_settings);
+
Settings game_defaults;
getGameMinetestConfig(gamespec.path, game_defaults);
+ game_defaults.removeSecureSettings();
+
g_settings->overrideDefaults(&game_defaults);
- infostream << "Initializing world at " << path << std::endl;
+ infostream << "Initializing world at " << final_path << std::endl;
- fs::CreateAllDirs(path);
+ fs::CreateAllDirs(final_path);
// Create world.mt if does not already exist
- std::string worldmt_path = path + DIR_DELIM "world.mt";
+ std::string worldmt_path = final_path + DIR_DELIM "world.mt";
if (!fs::PathExists(worldmt_path)) {
Settings conf;
+ conf.set("world_name", name);
conf.set("gameid", gamespec.id);
conf.set("backend", "sqlite3");
conf.set("player_backend", "sqlite3");
@@ -316,16 +359,16 @@ bool loadGameConfAndInitWorld(const std::string &path, const SubgameSpec &gamesp
conf.setBool("creative_mode", g_settings->getBool("creative_mode"));
conf.setBool("enable_damage", g_settings->getBool("enable_damage"));
- if (!conf.updateConfigFile(worldmt_path.c_str()))
- return false;
+ if (!conf.updateConfigFile(worldmt_path.c_str())) {
+ throw BaseException("Failed to update the config file");
+ }
}
// Create map_meta.txt if does not already exist
- std::string map_meta_path = path + DIR_DELIM + "map_meta.txt";
+ std::string map_meta_path = final_path + DIR_DELIM + "map_meta.txt";
if (!fs::PathExists(map_meta_path)) {
verbosestream << "Creating map_meta.txt (" << map_meta_path << ")"
<< std::endl;
- fs::CreateAllDirs(path);
std::ostringstream oss(std::ios_base::binary);
Settings conf;
@@ -338,5 +381,4 @@ bool loadGameConfAndInitWorld(const std::string &path, const SubgameSpec &gamesp
fs::safeWriteToFile(map_meta_path, oss.str());
}
- return true;
}
diff --git a/src/content/subgames.h b/src/content/subgames.h
index 4198ea860..60392639b 100644
--- a/src/content/subgames.h
+++ b/src/content/subgames.h
@@ -53,9 +53,6 @@ struct SubgameSpec
bool isValid() const { return (!id.empty() && !path.empty()); }
};
-// minetest.conf
-bool getGameMinetestConfig(const std::string &game_path, Settings &conf);
-
SubgameSpec findSubgame(const std::string &id);
SubgameSpec findWorldSubgame(const std::string &world_path);
@@ -63,6 +60,8 @@ std::set<std::string> getAvailableGameIds();
std::vector<SubgameSpec> getAvailableGames();
bool getWorldExists(const std::string &world_path);
+//! Try to get the displayed name of a world
+std::string getWorldName(const std::string &world_path, const std::string &default_name);
std::string getWorldGameId(const std::string &world_path, bool can_be_legacy = false);
struct WorldSpec
@@ -88,4 +87,5 @@ std::vector<WorldSpec> getAvailableWorlds();
// loads the subgame's config and creates world directory
// and world.mt if they don't exist
-bool loadGameConfAndInitWorld(const std::string &path, const SubgameSpec &gamespec);
+void loadGameConfAndInitWorld(const std::string &path, const std::string &name,
+ const SubgameSpec &gamespec, bool create_world);
diff --git a/src/content_nodemeta.cpp b/src/content_nodemeta.cpp
index fc2859d27..39743c11f 100644
--- a/src/content_nodemeta.cpp
+++ b/src/content_nodemeta.cpp
@@ -43,26 +43,26 @@ static bool content_nodemeta_deserialize_legacy_body(
if(id == NODEMETA_GENERIC) // GenericNodeMetadata (0.4-dev)
{
meta->getInventory()->deSerialize(is);
- deSerializeLongString(is); // m_text
- deSerializeString(is); // m_owner
+ deSerializeString32(is); // m_text
+ deSerializeString16(is); // m_owner
- meta->setString("infotext",deSerializeString(is));
- meta->setString("formspec",deSerializeString(is));
+ meta->setString("infotext",deSerializeString16(is));
+ meta->setString("formspec",deSerializeString16(is));
readU8(is); // m_allow_text_input
readU8(is); // m_allow_removal
readU8(is); // m_enforce_owner
int num_vars = readU32(is);
for(int i=0; i<num_vars; i++){
- std::string name = deSerializeString(is);
- std::string var = deSerializeLongString(is);
+ std::string name = deSerializeString16(is);
+ std::string var = deSerializeString32(is);
meta->setString(name, var);
}
return false;
}
else if(id == NODEMETA_SIGN) // SignNodeMetadata
{
- meta->setString("text", deSerializeString(is));
+ meta->setString("text", deSerializeString16(is));
//meta->setString("infotext","\"${text}\"");
meta->setString("infotext",
std::string("\"") + meta->getString("text") + "\"");
@@ -87,7 +87,7 @@ static bool content_nodemeta_deserialize_legacy_body(
}
else if(id == NODEMETA_LOCKABLE_CHEST) // LockingChestNodeMetadata
{
- meta->setString("owner", deSerializeString(is));
+ meta->setString("owner", deSerializeString16(is));
meta->getInventory()->deSerialize(is);
// Rename inventory list "0" to "main"
@@ -138,7 +138,7 @@ static bool content_nodemeta_deserialize_legacy_meta(
s16 id = readS16(is);
// Read data
- std::string data = deSerializeString(is);
+ std::string data = deSerializeString16(is);
std::istringstream tmp_is(data, std::ios::binary);
return content_nodemeta_deserialize_legacy_body(tmp_is, id, meta);
}
diff --git a/src/database/database-dummy.cpp b/src/database/database-dummy.cpp
index a3d8cd579..b56f341c5 100644
--- a/src/database/database-dummy.cpp
+++ b/src/database/database-dummy.cpp
@@ -22,6 +22,7 @@ Dummy database class
*/
#include "database-dummy.h"
+#include "remoteplayer.h"
bool Database_Dummy::saveBlock(const v3s16 &pos, const std::string &data)
@@ -57,3 +58,25 @@ void Database_Dummy::listAllLoadableBlocks(std::vector<v3s16> &dst)
}
}
+void Database_Dummy::savePlayer(RemotePlayer *player)
+{
+ m_player_database.insert(player->getName());
+}
+
+bool Database_Dummy::loadPlayer(RemotePlayer *player, PlayerSAO *sao)
+{
+ return m_player_database.find(player->getName()) != m_player_database.end();
+}
+
+bool Database_Dummy::removePlayer(const std::string &name)
+{
+ m_player_database.erase(name);
+ return true;
+}
+
+void Database_Dummy::listPlayers(std::vector<std::string> &res)
+{
+ for (const auto &player : m_player_database) {
+ res.emplace_back(player);
+ }
+}
diff --git a/src/database/database-dummy.h b/src/database/database-dummy.h
index 2d87d58f6..b69919f84 100644
--- a/src/database/database-dummy.h
+++ b/src/database/database-dummy.h
@@ -32,14 +32,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> &res) {}
+ void savePlayer(RemotePlayer *player);
+ bool loadPlayer(RemotePlayer *player, PlayerSAO *sao);
+ bool removePlayer(const std::string &name);
+ void listPlayers(std::vector<std::string> &res);
void beginSave() {}
void endSave() {}
private:
std::map<s64, std::string> m_database;
+ std::set<std::string> m_player_database;
};
diff --git a/src/database/database-leveldb.cpp b/src/database/database-leveldb.cpp
index 1976ae13d..73cd63f6d 100644
--- a/src/database/database-leveldb.cpp
+++ b/src/database/database-leveldb.cpp
@@ -145,8 +145,8 @@ void PlayerDatabaseLevelDB::savePlayer(RemotePlayer *player)
StringMap stringvars = sao->getMeta().getStrings();
writeU32(os, stringvars.size());
for (const auto &it : stringvars) {
- os << serializeString(it.first);
- os << serializeLongString(it.second);
+ os << serializeString16(it.first);
+ os << serializeString32(it.second);
}
player->inventory.serialize(os);
@@ -183,8 +183,8 @@ bool PlayerDatabaseLevelDB::loadPlayer(RemotePlayer *player, PlayerSAO *sao)
u32 attribute_count = readU32(is);
for (u32 i = 0; i < attribute_count; i++) {
- std::string name = deSerializeString(is);
- std::string value = deSerializeLongString(is);
+ std::string name = deSerializeString16(is);
+ std::string value = deSerializeString32(is);
sao->getMeta().setString(name, value);
}
sao->getMeta().setModified(false);
@@ -247,13 +247,13 @@ bool AuthDatabaseLevelDB::getAuth(const std::string &name, AuthEntry &res)
res.id = 1;
res.name = name;
- res.password = deSerializeString(is);
+ res.password = deSerializeString16(is);
u16 privilege_count = readU16(is);
res.privileges.clear();
res.privileges.reserve(privilege_count);
for (u16 i = 0; i < privilege_count; i++) {
- res.privileges.push_back(deSerializeString(is));
+ res.privileges.push_back(deSerializeString16(is));
}
res.last_login = readS64(is);
@@ -264,14 +264,14 @@ bool AuthDatabaseLevelDB::saveAuth(const AuthEntry &authEntry)
{
std::ostringstream os;
writeU8(os, 1);
- os << serializeString(authEntry.password);
+ os << serializeString16(authEntry.password);
size_t privilege_count = authEntry.privileges.size();
FATAL_ERROR_IF(privilege_count > U16_MAX,
"Unsupported number of privileges");
writeU16(os, privilege_count);
for (const std::string &privilege : authEntry.privileges) {
- os << serializeString(privilege);
+ os << serializeString16(privilege);
}
writeS64(os, authEntry.last_login);
diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp
index bfcee01f8..8050b533c 100644
--- a/src/defaultsettings.cpp
+++ b/src/defaultsettings.cpp
@@ -42,7 +42,7 @@ void set_default_settings(Settings *settings)
settings->setDefault("mute_sound", "false");
settings->setDefault("enable_mesh_cache", "false");
settings->setDefault("mesh_generation_interval", "0");
- settings->setDefault("meshgen_block_cache_size", "20");
+ settings->setDefault("meshgen_block_cache_size", "40");
settings->setDefault("enable_vbo", "true");
settings->setDefault("free_move", "false");
settings->setDefault("pitch_move", "false");
@@ -52,7 +52,7 @@ void set_default_settings(Settings *settings)
settings->setDefault("screenshot_format", "png");
settings->setDefault("screenshot_quality", "0");
settings->setDefault("client_unload_unused_data_timeout", "600");
- settings->setDefault("client_mapblock_limit", "5000");
+ settings->setDefault("client_mapblock_limit", "7500");
settings->setDefault("enable_build_where_you_stand", "true");
settings->setDefault("curl_timeout", "5000");
settings->setDefault("curl_parallel_limit", "8");
@@ -155,6 +155,8 @@ void set_default_settings(Settings *settings)
settings->setDefault("keymap_right", "KEY_KEY_D");
settings->setDefault("keymap_jump", "KEY_SPACE");
settings->setDefault("keymap_sneak", "KEY_LSHIFT");
+ settings->setDefault("keymap_dig", "KEY_LBUTTON");
+ settings->setDefault("keymap_place", "KEY_RBUTTON");
settings->setDefault("keymap_drop", "KEY_KEY_Q");
settings->setDefault("keymap_zoom", "KEY_KEY_Z");
settings->setDefault("keymap_inventory", "KEY_KEY_I");
@@ -256,8 +258,8 @@ void set_default_settings(Settings *settings)
settings->setDefault("tooltip_show_delay", "400");
settings->setDefault("tooltip_append_itemname", "false");
settings->setDefault("fps_max", "60");
- settings->setDefault("pause_fps_max", "20");
- settings->setDefault("viewing_range", "100");
+ settings->setDefault("fps_max_unfocused", "20");
+ settings->setDefault("viewing_range", "190");
#if ENABLE_GLES
settings->setDefault("near_plane", "0.1");
#endif
@@ -321,7 +323,7 @@ void set_default_settings(Settings *settings)
settings->setDefault("show_entity_selectionbox", "false");
settings->setDefault("texture_clean_transparent", "false");
settings->setDefault("texture_min_size", "64");
- settings->setDefault("ambient_occlusion_gamma", "2.2");
+ settings->setDefault("ambient_occlusion_gamma", "1.8");
#if ENABLE_GLES
settings->setDefault("enable_shaders", "false");
#else
@@ -342,15 +344,6 @@ void set_default_settings(Settings *settings)
settings->setDefault("bilinear_filter", "false");
settings->setDefault("trilinear_filter", "false");
settings->setDefault("tone_mapping", "false");
- settings->setDefault("enable_bumpmapping", "false");
- settings->setDefault("enable_parallax_occlusion", "false");
- settings->setDefault("generate_normalmaps", "false");
- settings->setDefault("normalmaps_strength", "0.6");
- settings->setDefault("normalmaps_smooth", "1");
- settings->setDefault("parallax_occlusion_mode", "1");
- settings->setDefault("parallax_occlusion_iterations", "4");
- settings->setDefault("parallax_occlusion_scale", "0.08");
- settings->setDefault("parallax_occlusion_bias", "0.04");
settings->setDefault("enable_waving_water", "false");
settings->setDefault("water_wave_height", "1.0");
settings->setDefault("water_wave_length", "20.0");
@@ -362,7 +355,7 @@ void set_default_settings(Settings *settings)
// Input
settings->setDefault("invert_mouse", "false");
settings->setDefault("mouse_sensitivity", "0.2");
- settings->setDefault("repeat_rightclick_time", "0.25");
+ settings->setDefault("repeat_place_time", "0.25");
settings->setDefault("safe_dig_and_place", "false");
settings->setDefault("random_input", "false");
settings->setDefault("aux1_descends", "false");
@@ -381,7 +374,6 @@ void set_default_settings(Settings *settings)
settings->setDefault("joystick_frustum_sensitivity", "170");
// Main menu
- settings->setDefault("main_menu_style", "full");
settings->setDefault("main_menu_path", "");
settings->setDefault("serverlist_file", "favoriteservers.txt");
@@ -421,6 +413,8 @@ void set_default_settings(Settings *settings)
// ContentDB
settings->setDefault("contentdb_url", "https://content.minetest.net");
+ settings->setDefault("contentdb_max_concurrent_downloads", "3");
+
#ifdef __ANDROID__
settings->setDefault("contentdb_flag_blacklist", "nonfree, android_default");
#else
@@ -442,7 +436,7 @@ void set_default_settings(Settings *settings)
settings->setDefault("port", "30000");
settings->setDefault("strict_protocol_version_checking", "false");
settings->setDefault("player_transfer_distance", "0");
- settings->setDefault("max_simultaneous_block_sends_per_client", "40");
+ settings->setDefault("max_simultaneous_block_sends_per_client", "128");
settings->setDefault("time_send_interval", "5");
settings->setDefault("default_game", "mineclone2");
@@ -457,11 +451,7 @@ void set_default_settings(Settings *settings)
settings->setDefault("disallow_empty_password", "false");
settings->setDefault("disable_anticheat", "false");
settings->setDefault("enable_rollback_recording", "false");
-#ifdef NDEBUG
- settings->setDefault("deprecated_lua_api_handling", "legacy");
-#else
settings->setDefault("deprecated_lua_api_handling", "log");
-#endif
settings->setDefault("kick_msg_shutdown", "Server shutting down.");
settings->setDefault("kick_msg_crash", "This server has experienced an internal error. You will now be disconnected.");
@@ -469,11 +459,11 @@ void set_default_settings(Settings *settings)
settings->setDefault("chat_message_format", "<@name> @message");
settings->setDefault("profiler_print_interval", "0");
- settings->setDefault("active_object_send_range_blocks", "4");
- settings->setDefault("active_block_range", "3");
+ settings->setDefault("active_object_send_range_blocks", "8");
+ settings->setDefault("active_block_range", "4");
//settings->setDefault("max_simultaneous_block_sends_per_client", "1");
// This causes frametime jitter on client side, or does it?
- settings->setDefault("max_block_send_distance", "10");
+ settings->setDefault("max_block_send_distance", "12");
settings->setDefault("block_send_optimize_distance", "4");
settings->setDefault("server_side_occlusion_culling", "true");
settings->setDefault("csm_restriction_flags", "62");
@@ -492,15 +482,16 @@ void set_default_settings(Settings *settings)
settings->setDefault("dedicated_server_step", "0.09");
settings->setDefault("active_block_mgmt_interval", "2.0");
settings->setDefault("abm_interval", "1.0");
+ settings->setDefault("abm_time_budget", "0.2");
settings->setDefault("nodetimer_interval", "0.2");
settings->setDefault("ignore_world_load_errors", "false");
settings->setDefault("remote_media", "");
settings->setDefault("debug_log_level", "action");
settings->setDefault("debug_log_size_max", "50");
settings->setDefault("chat_log_level", "error");
- settings->setDefault("emergequeue_limit_total", "512");
- settings->setDefault("emergequeue_limit_diskonly", "64");
- settings->setDefault("emergequeue_limit_generate", "64");
+ settings->setDefault("emergequeue_limit_total", "1024");
+ settings->setDefault("emergequeue_limit_diskonly", "128");
+ settings->setDefault("emergequeue_limit_generate", "128");
settings->setDefault("num_emerge_threads", "1");
settings->setDefault("secure.enable_security", "true");
settings->setDefault("secure.trusted_mods", "");
@@ -531,7 +522,7 @@ void set_default_settings(Settings *settings)
settings->setDefault("mapgen_limit", "31000");
settings->setDefault("chunksize", "5");
settings->setDefault("fixed_map_seed", "");
- settings->setDefault("max_block_generate_distance", "8");
+ settings->setDefault("max_block_generate_distance", "10");
settings->setDefault("enable_mapgen_debug_info", "false");
Mapgen::setDefaultSettings(settings);
@@ -569,7 +560,7 @@ void set_default_settings(Settings *settings)
settings->setDefault("max_block_generate_distance", "5");
settings->setDefault("enable_3d_clouds", "false");
settings->setDefault("fps_max", "30");
- settings->setDefault("pause_fps_max", "10");
+ settings->setDefault("fps_max_unfocused", "10");
settings->setDefault("max_objects_per_block", "20");
settings->setDefault("sqlite_synchronous", "1");
settings->setDefault("server_map_save_interval", "15");
diff --git a/src/emerge.cpp b/src/emerge.cpp
index 0ac26a682..12e407797 100644
--- a/src/emerge.cpp
+++ b/src/emerge.cpp
@@ -426,6 +426,10 @@ bool EmergeManager::pushBlockEmergeData(
m_qlimit_generate : m_qlimit_diskonly;
if (count_peer >= qlimit_peer)
return false;
+ } else {
+ // limit block enqueue requests for active blocks to 1/2 of total
+ if (count_peer * 2 >= m_qlimit_total)
+ return false;
}
}
diff --git a/src/emerge.h b/src/emerge.h
index 6f204666d..da845e243 100644
--- a/src/emerge.h
+++ b/src/emerge.h
@@ -52,7 +52,6 @@ struct BlockMakeData {
u64 seed = 0;
v3s16 blockpos_min;
v3s16 blockpos_max;
- v3s16 blockpos_requested;
UniqueQueue<v3s16> transforming_liquid;
const NodeDefManager *nodedef = nullptr;
diff --git a/src/environment.cpp b/src/environment.cpp
index 7acad313e..f10f773cf 100644
--- a/src/environment.cpp
+++ b/src/environment.cpp
@@ -36,6 +36,7 @@ Environment::Environment(IGameDef *gamedef):
m_cache_active_block_mgmt_interval = g_settings->getFloat("active_block_mgmt_interval");
m_cache_abm_interval = g_settings->getFloat("abm_interval");
m_cache_nodetimer_interval = g_settings->getFloat("nodetimer_interval");
+ m_cache_abm_time_budget = g_settings->getFloat("abm_time_budget");
m_time_of_day = g_settings->getU32("world_start_time");
m_time_of_day_f = (float)m_time_of_day / 24000.0f;
diff --git a/src/environment.h b/src/environment.h
index 91c33ba15..b4884fdb3 100644
--- a/src/environment.h
+++ b/src/environment.h
@@ -147,6 +147,7 @@ protected:
float m_cache_active_block_mgmt_interval;
float m_cache_abm_interval;
float m_cache_nodetimer_interval;
+ float m_cache_abm_time_budget;
IGameDef *m_gamedef;
diff --git a/src/filesys.cpp b/src/filesys.cpp
index 0bc351669..2470b1b64 100644
--- a/src/filesys.cpp
+++ b/src/filesys.cpp
@@ -750,6 +750,21 @@ bool safeWriteToFile(const std::string &path, const std::string &content)
return true;
}
+bool ReadFile(const std::string &path, std::string &out)
+{
+ std::ifstream is(path, std::ios::binary | std::ios::ate);
+ if (!is.good()) {
+ return false;
+ }
+
+ auto size = is.tellg();
+ out.resize(size);
+ is.seekg(0);
+ is.read(&out[0], size);
+
+ return true;
+}
+
bool Rename(const std::string &from, const std::string &to)
{
return rename(from.c_str(), to.c_str()) == 0;
diff --git a/src/filesys.h b/src/filesys.h
index 09f129aa3..b2c800c55 100644
--- a/src/filesys.h
+++ b/src/filesys.h
@@ -128,6 +128,8 @@ const char *GetFilenameFromPath(const char *path);
bool safeWriteToFile(const std::string &path, const std::string &content);
+bool ReadFile(const std::string &path, std::string &out);
+
bool Rename(const std::string &from, const std::string &to);
} // namespace fs
diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt
index 7befba37c..91476ada6 100644
--- a/src/gui/CMakeLists.txt
+++ b/src/gui/CMakeLists.txt
@@ -16,6 +16,7 @@ set(gui_SRCS
${CMAKE_CURRENT_SOURCE_DIR}/guiKeyChangeMenu.cpp
${CMAKE_CURRENT_SOURCE_DIR}/guiPasswordChange.cpp
${CMAKE_CURRENT_SOURCE_DIR}/guiPathSelectMenu.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/guiScene.cpp
${CMAKE_CURRENT_SOURCE_DIR}/guiScrollBar.cpp
${CMAKE_CURRENT_SOURCE_DIR}/guiScrollContainer.cpp
${CMAKE_CURRENT_SOURCE_DIR}/guiSkin.cpp
diff --git a/src/gui/StyleSpec.h b/src/gui/StyleSpec.h
index 67caf4f7b..f2844ce28 100644
--- a/src/gui/StyleSpec.h
+++ b/src/gui/StyleSpec.h
@@ -24,6 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "util/string.h"
#include <algorithm>
#include <array>
+#include <vector>
#pragma once
@@ -50,6 +51,10 @@ public:
PADDING,
FONT,
FONT_SIZE,
+ COLORS,
+ BORDERCOLORS,
+ BORDERWIDTHS,
+ SOUND,
NUM_PROPERTIES,
NONE
};
@@ -106,6 +111,14 @@ public:
return FONT;
} else if (name == "font_size") {
return FONT_SIZE;
+ } else if (name == "colors") {
+ return COLORS;
+ } else if (name == "bordercolors") {
+ return BORDERCOLORS;
+ } else if (name == "borderwidths") {
+ return BORDERWIDTHS;
+ } else if (name == "sound") {
+ return SOUND;
} else {
return NONE;
}
@@ -187,6 +200,42 @@ public:
return color;
}
+ std::array<video::SColor, 4> getColorArray(Property prop,
+ std::array<video::SColor, 4> def) const
+ {
+ const auto &val = properties[prop];
+ if (val.empty())
+ return def;
+
+ std::vector<std::string> strs;
+ if (!parseArray(val, strs))
+ return def;
+
+ for (size_t i = 0; i <= 3; i++) {
+ video::SColor color;
+ if (parseColorString(strs[i], color, false, 0xff))
+ def[i] = color;
+ }
+
+ return def;
+ }
+
+ std::array<s32, 4> getIntArray(Property prop, std::array<s32, 4> def) const
+ {
+ const auto &val = properties[prop];
+ if (val.empty())
+ return def;
+
+ std::vector<std::string> strs;
+ if (!parseArray(val, strs))
+ return def;
+
+ for (size_t i = 0; i <= 3; i++)
+ def[i] = stoi(strs[i]);
+
+ return def;
+ }
+
irr::core::rect<s32> getRect(Property prop, irr::core::rect<s32> def) const
{
const auto &val = properties[prop];
@@ -334,6 +383,24 @@ public:
}
private:
+ bool parseArray(const std::string &value, std::vector<std::string> &arr) const
+ {
+ std::vector<std::string> strs = split(value, ',');
+
+ if (strs.size() == 1) {
+ arr = {strs[0], strs[0], strs[0], strs[0]};
+ } else if (strs.size() == 2) {
+ arr = {strs[0], strs[1], strs[0], strs[1]};
+ } else if (strs.size() == 4) {
+ arr = strs;
+ } else {
+ warningstream << "Invalid array size (" << strs.size()
+ << " arguments): \"" << value << "\"" << std::endl;
+ return false;
+ }
+ return true;
+ }
+
bool parseRect(const std::string &value, irr::core::rect<s32> *parsed_rect) const
{
irr::core::rect<s32> rect;
diff --git a/src/gui/guiBox.cpp b/src/gui/guiBox.cpp
index 7f329cc32..443f1064f 100644
--- a/src/gui/guiBox.cpp
+++ b/src/gui/guiBox.cpp
@@ -20,9 +20,14 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "guiBox.h"
GUIBox::GUIBox(gui::IGUIEnvironment *env, gui::IGUIElement *parent, s32 id,
- const core::rect<s32> &rectangle, const video::SColor &color) :
+ const core::rect<s32> &rectangle,
+ const std::array<video::SColor, 4> &colors,
+ const std::array<video::SColor, 4> &bordercolors,
+ const std::array<s32, 4> &borderwidths) :
gui::IGUIElement(gui::EGUIET_ELEMENT, env, parent, id, rectangle),
- m_color(color)
+ m_colors(colors),
+ m_bordercolors(bordercolors),
+ m_borderwidths(borderwidths)
{
}
@@ -31,8 +36,82 @@ void GUIBox::draw()
if (!IsVisible)
return;
- Environment->getVideoDriver()->draw2DRectangle(m_color, AbsoluteRect,
- &AbsoluteClippingRect);
+ std::array<s32, 4> negative_borders = {0, 0, 0, 0};
+ std::array<s32, 4> positive_borders = {0, 0, 0, 0};
+
+ for (size_t i = 0; i <= 3; i++) {
+ if (m_borderwidths[i] > 0)
+ positive_borders[i] = m_borderwidths[i];
+ else
+ negative_borders[i] = m_borderwidths[i];
+ }
+
+ v2s32 upperleft = AbsoluteRect.UpperLeftCorner;
+ v2s32 lowerright = AbsoluteRect.LowerRightCorner;
+
+ v2s32 topleft_border = {
+ upperleft.X - positive_borders[3],
+ upperleft.Y - positive_borders[0]
+ };
+ v2s32 topleft_rect = {
+ upperleft.X - negative_borders[3],
+ upperleft.Y - negative_borders[0]
+ };
+
+ v2s32 lowerright_border = {
+ lowerright.X + positive_borders[1],
+ lowerright.Y + positive_borders[2]
+ };
+ v2s32 lowerright_rect = {
+ lowerright.X + negative_borders[1],
+ lowerright.Y + negative_borders[2]
+ };
+
+ core::rect<s32> main_rect(
+ topleft_rect.X,
+ topleft_rect.Y,
+ lowerright_rect.X,
+ lowerright_rect.Y
+ );
+
+ std::array<core::rect<s32>, 4> border_rects;
+
+ border_rects[0] = core::rect<s32>(
+ topleft_border.X,
+ topleft_border.Y,
+ lowerright_border.X,
+ topleft_rect.Y
+ );
+
+ border_rects[1] = core::rect<s32>(
+ lowerright_rect.X,
+ topleft_rect.Y,
+ lowerright_border.X,
+ lowerright_rect.Y
+ );
+
+ border_rects[2] = core::rect<s32>(
+ topleft_border.X,
+ lowerright_rect.Y,
+ lowerright_border.X,
+ lowerright_border.Y
+ );
+
+ border_rects[3] = core::rect<s32>(
+ topleft_border.X,
+ topleft_rect.Y,
+ topleft_rect.X,
+ lowerright_rect.Y
+ );
+
+ video::IVideoDriver *driver = Environment->getVideoDriver();
+
+ driver->draw2DRectangle(main_rect, m_colors[0], m_colors[1], m_colors[3],
+ m_colors[2], &AbsoluteClippingRect);
+
+ for (size_t i = 0; i <= 3; i++)
+ driver->draw2DRectangle(m_bordercolors[i], border_rects[i],
+ &AbsoluteClippingRect);
IGUIElement::draw();
}
diff --git a/src/gui/guiBox.h b/src/gui/guiBox.h
index 5306fdf65..ca8f83771 100644
--- a/src/gui/guiBox.h
+++ b/src/gui/guiBox.h
@@ -19,16 +19,23 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#pragma once
+#include <vector>
+#include <array>
#include "irrlichttypes_extrabloated.h"
class GUIBox : public gui::IGUIElement
{
public:
GUIBox(gui::IGUIEnvironment *env, gui::IGUIElement *parent, s32 id,
- const core::rect<s32> &rectangle, const video::SColor &color);
+ const core::rect<s32> &rectangle,
+ const std::array<video::SColor, 4> &colors,
+ const std::array<video::SColor, 4> &bordercolors,
+ const std::array<s32, 4> &borderwidths);
virtual void draw() override;
private:
- video::SColor m_color;
+ std::array<video::SColor, 4> m_colors;
+ std::array<video::SColor, 4> m_bordercolors;
+ std::array<s32, 4> m_borderwidths;
};
diff --git a/src/gui/guiButton.cpp b/src/gui/guiButton.cpp
index e0d6337cd..b98e5de82 100644
--- a/src/gui/guiButton.cpp
+++ b/src/gui/guiButton.cpp
@@ -313,11 +313,12 @@ void GUIButton::draw()
// PATCH
video::ITexture* texture = ButtonImages[(u32)imageState].Texture;
+ video::SColor image_colors[] = { BgColor, BgColor, BgColor, BgColor };
if (BgMiddle.getArea() == 0) {
driver->draw2DImage(texture,
ScaleImage? AbsoluteRect : core::rect<s32>(pos, sourceRect.getSize()),
sourceRect, &AbsoluteClippingRect,
- 0, UseAlphaChannel);
+ image_colors, UseAlphaChannel);
} else {
core::rect<s32> middle = BgMiddle;
// `-x` is interpreted as `w - x`
@@ -327,7 +328,7 @@ void GUIButton::draw()
middle.LowerRightCorner.Y += texture->getOriginalSize().Height;
draw2DImage9Slice(driver, texture,
ScaleImage ? AbsoluteRect : core::rect<s32>(pos, sourceRect.getSize()),
- middle, &AbsoluteClippingRect);
+ middle, &AbsoluteClippingRect, image_colors);
}
// END PATCH
}
@@ -722,6 +723,8 @@ GUIButton* GUIButton::addButton(IGUIEnvironment *environment,
void GUIButton::setColor(video::SColor color)
{
+ BgColor = color;
+
float d = 0.65f;
for (size_t i = 0; i < 4; i++) {
video::SColor base = Environment->getSkin()->getColor((gui::EGUI_DEFAULT_COLOR)i);
@@ -750,22 +753,26 @@ void GUIButton::setFromStyle(const StyleSpec& style)
bool pressed = (style.getState() & StyleSpec::STATE_PRESSED) != 0;
if (style.isNotDefault(StyleSpec::BGCOLOR)) {
-
setColor(style.getColor(StyleSpec::BGCOLOR));
// If we have a propagated hover/press color, we need to automatically
// lighten/darken it
if (!Styles[style.getState()].isNotDefault(StyleSpec::BGCOLOR)) {
- for (size_t i = 0; i < 4; i++) {
if (pressed) {
- Colors[i] = multiplyColorValue(Colors[i], COLOR_PRESSED_MOD);
+ BgColor = multiplyColorValue(BgColor, COLOR_PRESSED_MOD);
+
+ for (size_t i = 0; i < 4; i++)
+ Colors[i] = multiplyColorValue(Colors[i], COLOR_PRESSED_MOD);
} else if (hovered) {
- Colors[i] = multiplyColorValue(Colors[i], COLOR_HOVERED_MOD);
+ BgColor = multiplyColorValue(BgColor, COLOR_HOVERED_MOD);
+
+ for (size_t i = 0; i < 4; i++)
+ Colors[i] = multiplyColorValue(Colors[i], COLOR_HOVERED_MOD);
}
- }
}
} else {
+ BgColor = video::SColor(255, 255, 255, 255);
for (size_t i = 0; i < 4; i++) {
video::SColor base =
Environment->getSkin()->getColor((gui::EGUI_DEFAULT_COLOR)i);
diff --git a/src/gui/guiButton.h b/src/gui/guiButton.h
index 95fa1a2a1..4e1b04aac 100644
--- a/src/gui/guiButton.h
+++ b/src/gui/guiButton.h
@@ -338,5 +338,6 @@ private:
core::rect<s32> BgMiddle;
core::rect<s32> Padding;
core::vector2d<s32> ContentOffset;
+ video::SColor BgColor;
// END PATCH
};
diff --git a/src/gui/guiChatConsole.cpp b/src/gui/guiChatConsole.cpp
index 8de00c12f..ef471106d 100644
--- a/src/gui/guiChatConsole.cpp
+++ b/src/gui/guiChatConsole.cpp
@@ -136,11 +136,6 @@ void GUIChatConsole::closeConsoleAtOnce()
recalculateConsolePosition();
}
-f32 GUIChatConsole::getDesiredHeight() const
-{
- return m_desired_height_fraction;
-}
-
void GUIChatConsole::replaceAndAddToHistory(const std::wstring &line)
{
ChatPrompt& prompt = m_chat_backend->getPrompt();
@@ -545,7 +540,7 @@ bool GUIChatConsole::OnEvent(const SEvent& event)
if (prompt.getCursorLength() <= 0)
return true;
std::wstring wselected = prompt.getSelection();
- std::string selected(wselected.begin(), wselected.end());
+ std::string selected = wide_to_utf8(wselected);
Environment->getOSOperator()->copyToClipboard(selected.c_str());
return true;
}
@@ -575,7 +570,7 @@ bool GUIChatConsole::OnEvent(const SEvent& event)
if (prompt.getCursorLength() <= 0)
return true;
std::wstring wselected = prompt.getSelection();
- std::string selected(wselected.begin(), wselected.end());
+ std::string selected = wide_to_utf8(wselected);
Environment->getOSOperator()->copyToClipboard(selected.c_str());
prompt.cursorOperation(
ChatPrompt::CURSOROP_DELETE,
diff --git a/src/gui/guiChatConsole.h b/src/gui/guiChatConsole.h
index 7be40e27c..204f9f9cc 100644
--- a/src/gui/guiChatConsole.h
+++ b/src/gui/guiChatConsole.h
@@ -55,10 +55,6 @@ public:
// Set whether to close the console after the user presses enter.
void setCloseOnEnter(bool close) { m_close_on_enter = close; }
- // Return the desired height (fraction of screen size)
- // Zero if the console is closed or getting closed
- f32 getDesiredHeight() const;
-
// Replace actual line when adding the actual to the history (if there is any)
void replaceAndAddToHistory(const std::wstring &line);
diff --git a/src/gui/guiConfirmRegistration.cpp b/src/gui/guiConfirmRegistration.cpp
index 55c111df8..020a2796a 100644
--- a/src/gui/guiConfirmRegistration.cpp
+++ b/src/gui/guiConfirmRegistration.cpp
@@ -73,7 +73,11 @@ void GUIConfirmRegistration::regenerateGui(v2u32 screensize)
/*
Calculate new sizes and positions
*/
+#ifdef __ANDROID__
+ const float s = m_gui_scale * porting::getDisplayDensity() / 2;
+#else
const float s = m_gui_scale;
+#endif
DesiredRect = core::rect<s32>(
screensize.X / 2 - 600 * s / 2,
screensize.Y / 2 - 360 * s / 2,
@@ -257,12 +261,19 @@ bool GUIConfirmRegistration::getAndroidUIInput()
if (!hasAndroidUIInput() || m_jni_field_name != "password")
return false;
- std::string text = porting::getInputDialogValue();
- gui::IGUIElement *e = getElementFromId(ID_confirmPassword);
- if (e)
- e->setText(utf8_to_wide(text).c_str());
+ // still waiting
+ if (porting::getInputDialogState() == -1)
+ return true;
m_jni_field_name.clear();
+
+ gui::IGUIElement *e = getElementFromId(ID_confirmPassword);
+
+ if (!e || e->getType() != irr::gui::EGUIET_EDIT_BOX)
+ return false;
+
+ std::string text = porting::getInputDialogValue();
+ e->setText(utf8_to_wide(text).c_str());
return false;
}
#endif
diff --git a/src/gui/guiEditBoxWithScrollbar.cpp b/src/gui/guiEditBoxWithScrollbar.cpp
index 442406688..169425a9a 100644
--- a/src/gui/guiEditBoxWithScrollbar.cpp
+++ b/src/gui/guiEditBoxWithScrollbar.cpp
@@ -1028,7 +1028,7 @@ void GUIEditBoxWithScrollBar::breakText()
s32 last_line_start = 0;
s32 size = Text.size();
s32 length = 0;
- s32 el_width = RelativeRect.getWidth() - 6;
+ s32 el_width = RelativeRect.getWidth() - m_scrollbar_width - 10;
wchar_t c;
for (s32 i = 0; i < size; ++i) {
@@ -1399,8 +1399,6 @@ void GUIEditBoxWithScrollBar::createVScrollBar()
m_scrollbar_width = skin ? skin->getSize(gui::EGDS_SCROLLBAR_SIZE) : 16;
- RelativeRect.LowerRightCorner.X -= m_scrollbar_width + 4;
-
irr::core::rect<s32> scrollbarrect = m_frame_rect;
scrollbarrect.UpperLeftCorner.X += m_frame_rect.getWidth() - m_scrollbar_width;
m_vscrollbar = new GUIScrollBar(Environment, getParent(), -1,
diff --git a/src/gui/guiEngine.cpp b/src/gui/guiEngine.cpp
index b40707d01..c5ad5c323 100644
--- a/src/gui/guiEngine.cpp
+++ b/src/gui/guiEngine.cpp
@@ -170,6 +170,7 @@ GUIEngine::GUIEngine(JoystickController *joystick,
m_menumanager,
NULL /* &client */,
m_texture_source,
+ m_sound_manager,
m_formspecgui,
m_buttonhandler,
"",
@@ -297,10 +298,14 @@ void GUIEngine::run()
driver->endScene();
+ IrrlichtDevice *device = RenderingEngine::get_raw_device();
+ u32 frametime_min = 1000 / (device->isWindowFocused()
+ ? g_settings->getFloat("fps_max")
+ : g_settings->getFloat("fps_max_unfocused"));
if (m_clouds_enabled)
- cloudPostProcess();
+ cloudPostProcess(frametime_min, device);
else
- sleep_ms(25);
+ sleep_ms(frametime_min);
m_script->step();
@@ -367,9 +372,8 @@ void GUIEngine::cloudPreProcess()
}
/******************************************************************************/
-void GUIEngine::cloudPostProcess()
+void GUIEngine::cloudPostProcess(u32 frametime_min, IrrlichtDevice *device)
{
- float fps_max = g_settings->getFloat("pause_fps_max");
// Time of frame without fps limit
u32 busytime_u32;
@@ -380,12 +384,10 @@ void GUIEngine::cloudPostProcess()
else
busytime_u32 = 0;
- // FPS limiter
- u32 frametime_min = 1000./fps_max;
-
+ // FPS limit
if (busytime_u32 < frametime_min) {
u32 sleeptime = frametime_min - busytime_u32;
- RenderingEngine::get_raw_device()->sleep(sleeptime);
+ device->sleep(sleeptime);
}
}
diff --git a/src/gui/guiEngine.h b/src/gui/guiEngine.h
index f9ad0fb0a..eef1ad8aa 100644
--- a/src/gui/guiEngine.h
+++ b/src/gui/guiEngine.h
@@ -29,22 +29,22 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "util/enriched_string.h"
/******************************************************************************/
-/* Typedefs and macros */
+/* Structs and macros */
/******************************************************************************/
/** texture layer ids */
-typedef enum {
+enum texture_layer {
TEX_LAYER_BACKGROUND = 0,
TEX_LAYER_OVERLAY,
TEX_LAYER_HEADER,
TEX_LAYER_FOOTER,
TEX_LAYER_MAX
-} texture_layer;
+};
-typedef struct {
+struct image_definition {
video::ITexture *texture = nullptr;
bool tile;
unsigned int minsize;
-} image_definition;
+};
/******************************************************************************/
/* forward declarations */
@@ -277,7 +277,7 @@ private:
/** do preprocessing for cloud subsystem */
void cloudPreProcess();
/** do postprocessing for cloud subsystem */
- void cloudPostProcess();
+ void cloudPostProcess(u32 frametime_min, IrrlichtDevice *device);
/** internam data required for drawing clouds */
struct clouddata {
diff --git a/src/gui/guiFormSpecMenu.cpp b/src/gui/guiFormSpecMenu.cpp
index 601c5c18e..632b15992 100644
--- a/src/gui/guiFormSpecMenu.cpp
+++ b/src/gui/guiFormSpecMenu.cpp
@@ -48,6 +48,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "settings.h"
#include "client/client.h"
#include "client/fontengine.h"
+#include "client/sound.h"
#include "util/hex.h"
#include "util/numeric.h"
#include "util/string.h" // for parseColorString()
@@ -65,6 +66,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "guiScrollContainer.h"
#include "intlGUIEditBox.h"
#include "guiHyperText.h"
+#include "guiScene.h"
#define MY_CHECKPOS(a,b) \
if (v_pos.size() != 2) { \
@@ -94,11 +96,13 @@ inline u32 clamp_u8(s32 value)
GUIFormSpecMenu::GUIFormSpecMenu(JoystickController *joystick,
gui::IGUIElement *parent, s32 id, IMenuManager *menumgr,
- Client *client, ISimpleTextureSource *tsrc, IFormSource *fsrc, TextDest *tdst,
+ Client *client, ISimpleTextureSource *tsrc, ISoundManager *sound_manager,
+ IFormSource *fsrc, TextDest *tdst,
const std::string &formspecPrepend, bool remap_dbl_click):
GUIModalMenu(RenderingEngine::get_gui_env(), parent, id, menumgr, remap_dbl_click),
m_invmgr(client),
m_tsrc(tsrc),
+ m_sound_manager(sound_manager),
m_client(client),
m_formspec_prepend(formspecPrepend),
m_form_src(fsrc),
@@ -142,11 +146,12 @@ GUIFormSpecMenu::~GUIFormSpecMenu()
void GUIFormSpecMenu::create(GUIFormSpecMenu *&cur_formspec, Client *client,
JoystickController *joystick, IFormSource *fs_src, TextDest *txt_dest,
- const std::string &formspecPrepend)
+ const std::string &formspecPrepend, ISoundManager *sound_manager)
{
if (cur_formspec == nullptr) {
cur_formspec = new GUIFormSpecMenu(joystick, guiroot, -1, &g_menumgr,
- client, client->getTextureSource(), fs_src, txt_dest, formspecPrepend);
+ client, client->getTextureSource(), sound_manager, fs_src,
+ txt_dest, formspecPrepend);
cur_formspec->doPause = false;
/*
@@ -613,6 +618,9 @@ void GUIFormSpecMenu::parseCheckbox(parserData* data, const std::string &element
data->current_parent, spec.fid, spec.flabel.c_str());
auto style = getDefaultStyleForElement("checkbox", name);
+
+ spec.sound = style.get(StyleSpec::Property::SOUND, "");
+
e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false));
if (spec.fname == m_focused_element) {
@@ -1019,6 +1027,9 @@ void GUIFormSpecMenu::parseButton(parserData* data, const std::string &element,
data->current_parent, spec.fid, spec.flabel.c_str());
auto style = getStyleForElement(type, name, (type != "button") ? "button" : "");
+
+ spec.sound = style[StyleSpec::STATE_DEFAULT].get(StyleSpec::Property::SOUND, "");
+
e->setStyles(style);
if (spec.fname == m_focused_element) {
@@ -1225,6 +1236,7 @@ void GUIFormSpecMenu::parseTable(parserData* data, const std::string &element)
auto style = getDefaultStyleForElement("table", name);
e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false));
+ e->setOverrideFont(style.getFont());
m_tables.emplace_back(spec, e);
m_fields.push_back(spec);
@@ -1302,6 +1314,7 @@ void GUIFormSpecMenu::parseTextList(parserData* data, const std::string &element
auto style = getDefaultStyleForElement("textlist", name);
e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false));
+ e->setOverrideFont(style.getFont());
m_tables.emplace_back(spec, e);
m_fields.push_back(spec);
@@ -1378,6 +1391,9 @@ void GUIFormSpecMenu::parseDropDown(parserData* data, const std::string &element
e->setSelected(stoi(str_initial_selection)-1);
auto style = getDefaultStyleForElement("dropdown", name);
+
+ spec.sound = style.get(StyleSpec::Property::SOUND, "");
+
e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false));
m_fields.push_back(spec);
@@ -1738,12 +1754,16 @@ void GUIFormSpecMenu::parseHyperText(parserData *data, const std::string &elemen
FieldSpec spec(
name,
- utf8_to_wide(unescape_string(text)),
+ translate_string(utf8_to_wide(unescape_string(text))),
L"",
258 + m_fields.size()
);
spec.ftype = f_HyperText;
+
+ auto style = getDefaultStyleForElement("hypertext", spec.fname);
+ spec.sound = style.get(StyleSpec::Property::SOUND, "");
+
GUIHyperText *e = new GUIHyperText(spec.flabel.c_str(), Environment,
data->current_parent, spec.fid, rect, m_client, m_tsrc);
e->drop();
@@ -1996,6 +2016,8 @@ void GUIFormSpecMenu::parseImageButton(parserData* data, const std::string &elem
auto style = getStyleForElement("image_button", spec.fname);
+ spec.sound = style[StyleSpec::STATE_DEFAULT].get(StyleSpec::Property::SOUND, "");
+
// Override style properties with values specified directly in the element
if (!image_name.empty())
style[StyleSpec::STATE_DEFAULT].set(StyleSpec::FGIMG, image_name);
@@ -2104,6 +2126,9 @@ void GUIFormSpecMenu::parseTabHeader(parserData* data, const std::string &elemen
e->setTabHeight(geom.Y);
auto style = getDefaultStyleForElement("tabheader", name);
+
+ spec.sound = style.get(StyleSpec::Property::SOUND, "");
+
e->setNotClipped(style.getBool(StyleSpec::NOCLIP, true));
for (const std::string &button : buttons) {
@@ -2192,6 +2217,9 @@ void GUIFormSpecMenu::parseItemImageButton(parserData* data, const std::string &
item_name, m_client);
auto style = getStyleForElement("item_image_button", spec_btn.fname, "image_button");
+
+ spec_btn.sound = style[StyleSpec::STATE_DEFAULT].get(StyleSpec::Property::SOUND, "");
+
e_btn->setStyles(style);
if (spec_btn.fname == m_focused_element) {
@@ -2209,16 +2237,16 @@ void GUIFormSpecMenu::parseItemImageButton(parserData* data, const std::string &
void GUIFormSpecMenu::parseBox(parserData* data, const std::string &element)
{
- std::vector<std::string> parts = split(element,';');
+ std::vector<std::string> parts = split(element, ';');
if ((parts.size() == 3) ||
((parts.size() > 3) && (m_formspec_version > FORMSPEC_API_VERSION)))
{
- std::vector<std::string> v_pos = split(parts[0],',');
- std::vector<std::string> v_geom = split(parts[1],',');
+ std::vector<std::string> v_pos = split(parts[0], ',');
+ std::vector<std::string> v_geom = split(parts[1], ',');
- MY_CHECKPOS("box",0);
- MY_CHECKGEOM("box",1);
+ MY_CHECKPOS("box", 0);
+ MY_CHECKGEOM("box", 1);
v2s32 pos;
v2s32 geom;
@@ -2232,36 +2260,43 @@ void GUIFormSpecMenu::parseBox(parserData* data, const std::string &element)
geom.Y = stof(v_geom[1]) * spacing.Y;
}
- video::SColor tmp_color;
-
- if (parseColorString(parts[2], tmp_color, false, 0x8C)) {
- FieldSpec spec(
- "",
- L"",
- L"",
- 258 + m_fields.size(),
- -2
- );
- spec.ftype = f_Box;
+ FieldSpec spec(
+ "",
+ L"",
+ L"",
+ 258 + m_fields.size(),
+ -2
+ );
+ spec.ftype = f_Box;
- core::rect<s32> rect(pos, pos + geom);
+ auto style = getDefaultStyleForElement("box", spec.fname);
- GUIBox *e = new GUIBox(Environment, data->current_parent, spec.fid,
- rect, tmp_color);
+ video::SColor tmp_color;
+ std::array<video::SColor, 4> colors;
+ std::array<video::SColor, 4> bordercolors = {0x0, 0x0, 0x0, 0x0};
+ std::array<s32, 4> borderwidths = {0, 0, 0, 0};
- auto style = getDefaultStyleForElement("box", spec.fname);
- e->setNotClipped(style.getBool(StyleSpec::NOCLIP, m_formspec_version < 3));
+ if (parseColorString(parts[2], tmp_color, true, 0x8C)) {
+ colors = {tmp_color, tmp_color, tmp_color, tmp_color};
+ } else {
+ colors = style.getColorArray(StyleSpec::COLORS, {0x0, 0x0, 0x0, 0x0});
+ bordercolors = style.getColorArray(StyleSpec::BORDERCOLORS,
+ {0x0, 0x0, 0x0, 0x0});
+ borderwidths = style.getIntArray(StyleSpec::BORDERWIDTHS, {0, 0, 0, 0});
+ }
- e->drop();
+ core::rect<s32> rect(pos, pos + geom);
- m_fields.push_back(spec);
+ GUIBox *e = new GUIBox(Environment, data->current_parent, spec.fid, rect,
+ colors, bordercolors, borderwidths);
+ e->setNotClipped(style.getBool(StyleSpec::NOCLIP, m_formspec_version < 3));
+ e->drop();
- } else {
- errorstream<< "Invalid Box element(" << parts.size() << "): '" << element << "' INVALID COLOR" << std::endl;
- }
+ m_fields.push_back(spec);
return;
}
- errorstream<< "Invalid Box element(" << parts.size() << "): '" << element << "'" << std::endl;
+ errorstream << "Invalid Box element(" << parts.size() << "): '" << element
+ << "'" << std::endl;
}
void GUIFormSpecMenu::parseBackgroundColor(parserData* data, const std::string &element)
@@ -2686,6 +2721,86 @@ void GUIFormSpecMenu::parseSetFocus(const std::string &element)
<< "'" << std::endl;
}
+void GUIFormSpecMenu::parseModel(parserData *data, const std::string &element)
+{
+ std::vector<std::string> parts = split(element, ';');
+
+ if (parts.size() < 5 || (parts.size() > 8 &&
+ m_formspec_version <= FORMSPEC_API_VERSION)) {
+ errorstream << "Invalid model element (" << parts.size() << "): '" << element
+ << "'" << std::endl;
+ return;
+ }
+
+ // Avoid length checks by resizing
+ if (parts.size() < 8)
+ parts.resize(8);
+
+ std::vector<std::string> v_pos = split(parts[0], ',');
+ std::vector<std::string> v_geom = split(parts[1], ',');
+ std::string name = unescape_string(parts[2]);
+ std::string meshstr = unescape_string(parts[3]);
+ std::vector<std::string> textures = split(parts[4], ',');
+ std::vector<std::string> vec_rot = split(parts[5], ',');
+ bool inf_rotation = is_yes(parts[6]);
+ bool mousectrl = is_yes(parts[7]) || parts[7].empty(); // default true
+
+ MY_CHECKPOS("model", 0);
+ MY_CHECKGEOM("model", 1);
+
+ v2s32 pos;
+ v2s32 geom;
+
+ if (data->real_coordinates) {
+ pos = getRealCoordinateBasePos(v_pos);
+ geom = getRealCoordinateGeometry(v_geom);
+ } else {
+ pos = getElementBasePos(&v_pos);
+ geom.X = stof(v_geom[0]) * (float)imgsize.X;
+ geom.Y = stof(v_geom[1]) * (float)imgsize.Y;
+ }
+
+ if (!data->explicit_size)
+ warningstream << "invalid use of model without a size[] element" << std::endl;
+
+ scene::IAnimatedMesh *mesh = m_client->getMesh(meshstr);
+
+ if (!mesh) {
+ errorstream << "Invalid model element: Unable to load mesh:"
+ << std::endl << "\t" << meshstr << std::endl;
+ return;
+ }
+
+ FieldSpec spec(
+ name,
+ L"",
+ L"",
+ 258 + m_fields.size()
+ );
+
+ core::rect<s32> rect(pos, pos + geom);
+
+ GUIScene *e = new GUIScene(Environment, RenderingEngine::get_scene_manager(),
+ data->current_parent, rect, spec.fid);
+
+ auto meshnode = e->setMesh(mesh);
+
+ for (u32 i = 0; i < textures.size() && i < meshnode->getMaterialCount(); ++i)
+ e->setTexture(i, m_tsrc->getTexture(textures[i]));
+
+ if (vec_rot.size() >= 2)
+ e->setRotation(v2f(stof(vec_rot[0]), stof(vec_rot[1])));
+
+ e->enableContinuousRotation(inf_rotation);
+ e->enableMouseControl(mousectrl);
+
+ auto style = getStyleForElement("model", spec.fname);
+ e->setStyles(style);
+ e->drop();
+
+ m_fields.push_back(spec);
+}
+
void GUIFormSpecMenu::parseElement(parserData* data, const std::string &element)
{
//some prechecks
@@ -2882,6 +2997,11 @@ void GUIFormSpecMenu::parseElement(parserData* data, const std::string &element)
return;
}
+ if (type == "model") {
+ parseModel(data, description);
+ return;
+ }
+
// Ignore others
infostream << "Unknown DrawSpec: type=" << type << ", data=\"" << description << "\""
<< std::endl;
@@ -3110,42 +3230,42 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
// and default scaling (1.00).
use_imgsize = 0.5555 * screen_dpi * gui_scaling;
} else {
- // In variable-size mode, we prefer to make the
- // inventory image size 1/15 of screen height,
- // multiplied by the gui_scaling config parameter.
- // If the preferred size won't fit the whole
- // form on the screen, either horizontally or
- // vertically, then we scale it down to fit.
- // (The magic numbers in the computation of what
- // fits arise from the scaling factors in the
- // following stanza, including the form border,
- // help text space, and 0.1 inventory slot spare.)
- // However, a minimum size is also set, that
- // the image size can't be less than 0.3 inch
- // multiplied by gui_scaling, even if this means
- // the form doesn't fit the screen.
+ // Variables for the maximum imgsize that can fit in the screen.
+ double fitx_imgsize;
+ double fity_imgsize;
+
+ // Pad the screensize with 5% of the screensize on all sides to ensure
+ // that even the largest formspecs don't touch the screen borders.
+ v2f padded_screensize(
+ mydata.screensize.X * 0.9f,
+ mydata.screensize.Y * 0.9f
+ );
+
+ if (mydata.real_coordinates) {
+ fitx_imgsize = padded_screensize.X / mydata.invsize.X;
+ fity_imgsize = padded_screensize.Y / mydata.invsize.Y;
+ } else {
+ // The maximum imgsize in the old coordinate system also needs to
+ // factor in padding and spacing along with 0.1 inventory slot spare
+ // and help text space, hence the magic numbers.
+ fitx_imgsize = padded_screensize.X /
+ ((5.0 / 4.0) * (0.5 + mydata.invsize.X));
+ fity_imgsize = padded_screensize.Y /
+ ((15.0 / 13.0) * (0.85 + mydata.invsize.Y));
+ }
+
#ifdef __ANDROID__
- // For mobile devices these magic numbers are
- // different and forms should always use the
- // maximum screen space available.
- double prefer_imgsize = mydata.screensize.Y / 10 * gui_scaling;
- double fitx_imgsize = mydata.screensize.X /
- ((12.0 / 8.0) * (0.5 + mydata.invsize.X));
- double fity_imgsize = mydata.screensize.Y /
- ((15.0 / 11.0) * (0.85 + mydata.invsize.Y));
- use_imgsize = MYMIN(prefer_imgsize,
- MYMIN(fitx_imgsize, fity_imgsize));
+ // In Android, the preferred imgsize should be larger to accommodate the
+ // smaller screensize.
+ double prefer_imgsize = padded_screensize.Y / 10 * gui_scaling;
#else
- double prefer_imgsize = mydata.screensize.Y / 15 * gui_scaling;
- double fitx_imgsize = mydata.screensize.X /
- ((5.0 / 4.0) * (0.5 + mydata.invsize.X));
- double fity_imgsize = mydata.screensize.Y /
- ((15.0 / 13.0) * (0.85 * mydata.invsize.Y));
- double screen_dpi = RenderingEngine::getDisplayDensity() * 96;
- double min_imgsize = 0.3 * screen_dpi * gui_scaling;
- use_imgsize = MYMAX(min_imgsize, MYMIN(prefer_imgsize,
- MYMIN(fitx_imgsize, fity_imgsize)));
+ // Desktop computers have more space, so try to fit 15 coordinates.
+ double prefer_imgsize = padded_screensize.Y / 15 * gui_scaling;
#endif
+ // Try to use the preferred imgsize, but if that's bigger than the maximum
+ // size, use the maximum size.
+ use_imgsize = std::min(prefer_imgsize,
+ std::min(fitx_imgsize, fity_imgsize));
}
// Everything else is scaled in proportion to the
@@ -3474,10 +3594,14 @@ void GUIFormSpecMenu::drawMenu()
e->setVisible(true);
/*
- Call base class
- (This is where all the drawing happens.)
+ This is where all the drawing happens.
*/
- gui::IGUIElement::draw();
+ core::list<IGUIElement*>::Iterator it = Children.begin();
+ for (; it != Children.end(); ++it)
+ if ((*it)->isNotClipped() ||
+ AbsoluteClippingRect.isRectCollided(
+ (*it)->getAbsolutePosition()))
+ (*it)->draw();
for (gui::IGUIElement *e : m_clickthrough_elements)
e->setVisible(false);
@@ -4387,6 +4511,8 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
for (GUIFormSpecMenu::FieldSpec &s : m_fields) {
if ((s.ftype == f_TabHeader) &&
(s.fid == event.GUIEvent.Caller->getID())) {
+ if (!s.sound.empty() && m_sound_manager)
+ m_sound_manager->playSound(s.sound, false, 1.0f);
s.send = true;
acceptInput();
s.send = false;
@@ -4430,6 +4556,9 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
continue;
if (s.ftype == f_Button || s.ftype == f_CheckBox) {
+ if (!s.sound.empty() && m_sound_manager)
+ m_sound_manager->playSound(s.sound, false, 1.0f);
+
s.send = true;
if (s.is_exit) {
if (m_allowclose) {
@@ -4452,6 +4581,8 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
s2.send = false;
}
}
+ if (!s.sound.empty() && m_sound_manager)
+ m_sound_manager->playSound(s.sound, false, 1.0f);
s.send = true;
acceptInput(quit_mode_no);
@@ -4468,6 +4599,8 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
acceptInput(quit_mode_no);
s.fdefault = L"";
} else if (s.ftype == f_Unknown || s.ftype == f_HyperText) {
+ if (!s.sound.empty() && m_sound_manager)
+ m_sound_manager->playSound(s.sound, false, 1.0f);
s.send = true;
acceptInput();
s.send = false;
diff --git a/src/gui/guiFormSpecMenu.h b/src/gui/guiFormSpecMenu.h
index 613acaa04..37106cb65 100644
--- a/src/gui/guiFormSpecMenu.h
+++ b/src/gui/guiFormSpecMenu.h
@@ -38,10 +38,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
class InventoryManager;
class ISimpleTextureSource;
class Client;
-class TexturePool;
class GUIScrollContainer;
+class ISoundManager;
-typedef enum {
+enum FormspecFieldType {
f_Button,
f_Table,
f_TabHeader,
@@ -53,13 +53,13 @@ typedef enum {
f_HyperText,
f_AnimatedImage,
f_Unknown
-} FormspecFieldType;
+};
-typedef enum {
+enum FormspecQuitMode {
quit_mode_no,
quit_mode_accept,
quit_mode_cancel
-} FormspecQuitMode;
+};
struct TextDest
{
@@ -128,6 +128,7 @@ class GUIFormSpecMenu : public GUIModalMenu
int priority;
core::rect<s32> rect;
gui::ECURSOR_ICON fcursor_icon;
+ std::string sound;
};
struct TooltipSpec
@@ -152,6 +153,7 @@ public:
IMenuManager *menumgr,
Client *client,
ISimpleTextureSource *tsrc,
+ ISoundManager *sound_manager,
IFormSource* fs_src,
TextDest* txt_dst,
const std::string &formspecPrepend,
@@ -161,7 +163,7 @@ public:
static void create(GUIFormSpecMenu *&cur_formspec, Client *client,
JoystickController *joystick, IFormSource *fs_src, TextDest *txt_dest,
- const std::string &formspecPrepend);
+ const std::string &formspecPrepend, ISoundManager *sound_manager);
void setFormSpec(const std::string &formspec_string,
const InventoryLocation &current_inventory_location)
@@ -294,6 +296,7 @@ protected:
InventoryManager *m_invmgr;
ISimpleTextureSource *m_tsrc;
+ ISoundManager *m_sound_manager;
Client *m_client;
std::string m_formspec_string;
@@ -353,7 +356,7 @@ private:
JoystickController *m_joystick;
bool m_show_debug = false;
- typedef struct {
+ struct parserData {
bool explicit_size;
bool real_coordinates;
u8 simple_field_count;
@@ -381,16 +384,16 @@ private:
// used to restore table selection/scroll/treeview state
std::unordered_map<std::string, GUITable::DynamicData> table_dyndata;
- } parserData;
+ };
- typedef struct {
+ struct fs_key_pending {
bool key_up;
bool key_down;
bool key_enter;
bool key_escape;
- } fs_key_pendig;
+ };
- fs_key_pendig current_keys_pending;
+ fs_key_pending current_keys_pending;
std::string current_field_enter_pending = "";
std::vector<std::string> m_hovered_item_tooltips;
@@ -444,6 +447,7 @@ private:
void parseAnchor(parserData *data, const std::string &element);
bool parseStyle(parserData *data, const std::string &element, bool style_type);
void parseSetFocus(const std::string &element);
+ void parseModel(parserData *data, const std::string &element);
void tryClose();
diff --git a/src/gui/guiPasswordChange.cpp b/src/gui/guiPasswordChange.cpp
index 5311c6fef..74cd62f5b 100644
--- a/src/gui/guiPasswordChange.cpp
+++ b/src/gui/guiPasswordChange.cpp
@@ -79,7 +79,11 @@ void GUIPasswordChange::regenerateGui(v2u32 screensize)
/*
Calculate new sizes and positions
*/
+#ifdef __ANDROID__
+ const float s = m_gui_scale * porting::getDisplayDensity() / 2;
+#else
const float s = m_gui_scale;
+#endif
DesiredRect = core::rect<s32>(
screensize.X / 2 - 580 * s / 2,
screensize.Y / 2 - 300 * s / 2,
@@ -289,6 +293,10 @@ bool GUIPasswordChange::getAndroidUIInput()
if (!hasAndroidUIInput())
return false;
+ // still waiting
+ if (porting::getInputDialogState() == -1)
+ return true;
+
gui::IGUIElement *e = nullptr;
if (m_jni_field_name == "old_password")
e = getElementFromId(ID_oldPassword);
@@ -296,12 +304,13 @@ bool GUIPasswordChange::getAndroidUIInput()
e = getElementFromId(ID_newPassword1);
else if (m_jni_field_name == "new_password_2")
e = getElementFromId(ID_newPassword2);
-
- if (e) {
- std::string text = porting::getInputDialogValue();
- e->setText(utf8_to_wide(text).c_str());
- }
m_jni_field_name.clear();
+
+ if (!e || e->getType() != irr::gui::EGUIET_EDIT_BOX)
+ return false;
+
+ std::string text = porting::getInputDialogValue();
+ e->setText(utf8_to_wide(text).c_str());
return false;
}
#endif
diff --git a/src/gui/guiScene.cpp b/src/gui/guiScene.cpp
new file mode 100644
index 000000000..08f119e07
--- /dev/null
+++ b/src/gui/guiScene.cpp
@@ -0,0 +1,257 @@
+/*
+Minetest
+Copyright (C) 2020 Jean-Patrick Guerrero <jeanpatrick.guerrero@gmail.com>
+
+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 "guiScene.h"
+
+#include <SViewFrustum.h>
+#include <IAnimatedMeshSceneNode.h>
+#include <ILightSceneNode.h>
+#include "porting.h"
+
+GUIScene::GUIScene(gui::IGUIEnvironment *env, scene::ISceneManager *smgr,
+ gui::IGUIElement *parent, core::recti rect, s32 id)
+ : IGUIElement(gui::EGUIET_ELEMENT, env, parent, id, rect)
+{
+ m_driver = env->getVideoDriver();
+ m_smgr = smgr->createNewSceneManager(false);
+
+ m_cam = m_smgr->addCameraSceneNode(0, v3f(0.f, 0.f, -100.f), v3f(0.f));
+ m_cam->setFOV(30.f * core::DEGTORAD);
+
+ scene::ILightSceneNode *light = m_smgr->addLightSceneNode(m_cam);
+ light->setRadius(1000.f);
+
+ m_smgr->getParameters()->setAttribute(scene::ALLOW_ZWRITE_ON_TRANSPARENT, true);
+}
+
+GUIScene::~GUIScene()
+{
+ setMesh(nullptr);
+
+ m_smgr->drop();
+}
+
+scene::IAnimatedMeshSceneNode *GUIScene::setMesh(scene::IAnimatedMesh *mesh)
+{
+ if (m_mesh) {
+ m_mesh->remove();
+ m_mesh = nullptr;
+ }
+
+ if (!mesh)
+ return nullptr;
+
+ m_mesh = m_smgr->addAnimatedMeshSceneNode(mesh);
+ m_mesh->setPosition(-m_mesh->getBoundingBox().getCenter());
+ m_mesh->animateJoints();
+ return m_mesh;
+}
+
+void GUIScene::setTexture(u32 idx, video::ITexture *texture)
+{
+ video::SMaterial &material = m_mesh->getMaterial(idx);
+ material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
+ material.MaterialTypeParam = 0.5f;
+ material.TextureLayer[0].Texture = texture;
+ material.setFlag(video::EMF_LIGHTING, false);
+ material.setFlag(video::EMF_FOG_ENABLE, true);
+ material.setFlag(video::EMF_BILINEAR_FILTER, false);
+ material.setFlag(video::EMF_BACK_FACE_CULLING, false);
+}
+
+void GUIScene::draw()
+{
+ // Control rotation speed based on time
+ u64 new_time = porting::getTimeMs();
+ u64 dtime_ms = 0;
+ if (m_last_time != 0)
+ dtime_ms = porting::getDeltaMs(m_last_time, new_time);
+ m_last_time = new_time;
+
+ core::rect<s32> oldViewPort = m_driver->getViewPort();
+ m_driver->setViewPort(getAbsoluteClippingRect());
+ core::recti borderRect = Environment->getRootGUIElement()->getAbsoluteClippingRect();
+
+ if (m_bgcolor != 0) {
+ Environment->getSkin()->draw3DSunkenPane(
+ this, m_bgcolor, false, true, borderRect, 0);
+ }
+
+ core::dimension2d<s32> size = getAbsoluteClippingRect().getSize();
+ m_smgr->getActiveCamera()->setAspectRatio((f32)size.Width / (f32)size.Height);
+
+ if (!m_target) {
+ updateCamera(m_smgr->addEmptySceneNode());
+ rotateCamera(v3f(0.f));
+ m_cam->bindTargetAndRotation(true);
+ }
+
+ cameraLoop();
+
+ // Continuous rotation
+ if (m_inf_rot)
+ rotateCamera(v3f(0.f, -0.03f * (float)dtime_ms, 0.f));
+
+ m_smgr->drawAll();
+
+ if (m_initial_rotation && m_mesh) {
+ rotateCamera(v3f(m_custom_rot.X, m_custom_rot.Y, 0.f));
+ calcOptimalDistance();
+
+ m_initial_rotation = false;
+ }
+
+ m_driver->setViewPort(oldViewPort);
+}
+
+bool GUIScene::OnEvent(const SEvent &event)
+{
+ if (m_mouse_ctrl && event.EventType == EET_MOUSE_INPUT_EVENT) {
+ if (event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN) {
+ m_last_pos = v2f((f32)event.MouseInput.X, (f32)event.MouseInput.Y);
+ return true;
+ } else if (event.MouseInput.Event == EMIE_MOUSE_MOVED) {
+ if (event.MouseInput.isLeftPressed()) {
+ m_curr_pos = v2f((f32)event.MouseInput.X, (f32)event.MouseInput.Y);
+
+ rotateCamera(v3f(
+ m_last_pos.Y - m_curr_pos.Y,
+ m_curr_pos.X - m_last_pos.X, 0.f));
+
+ m_last_pos = m_curr_pos;
+ return true;
+ }
+ }
+ }
+
+ return gui::IGUIElement::OnEvent(event);
+}
+
+void GUIScene::setStyles(const std::array<StyleSpec, StyleSpec::NUM_STATES> &styles)
+{
+ StyleSpec::State state = StyleSpec::STATE_DEFAULT;
+ StyleSpec style = StyleSpec::getStyleFromStatePropagation(styles, state);
+
+ setNotClipped(style.getBool(StyleSpec::NOCLIP, false));
+ setBackgroundColor(style.getColor(StyleSpec::BGCOLOR, m_bgcolor));
+}
+
+/* Camera control functions */
+
+inline void GUIScene::calcOptimalDistance()
+{
+ core::aabbox3df box = m_mesh->getBoundingBox();
+ f32 width = box.MaxEdge.X - box.MinEdge.X;
+ f32 height = box.MaxEdge.Y - box.MinEdge.Y;
+ f32 depth = box.MaxEdge.Z - box.MinEdge.Z;
+ f32 max_width = width > depth ? width : depth;
+
+ const scene::SViewFrustum *f = m_cam->getViewFrustum();
+ f32 cam_far = m_cam->getFarValue();
+ f32 far_width = core::line3df(f->getFarLeftUp(), f->getFarRightUp()).getLength();
+ f32 far_height = core::line3df(f->getFarLeftUp(), f->getFarLeftDown()).getLength();
+
+ core::recti rect = getAbsolutePosition();
+ f32 zoomX = rect.getWidth() / max_width;
+ f32 zoomY = rect.getHeight() / height;
+ f32 dist;
+
+ if (zoomX < zoomY)
+ dist = (max_width / (far_width / cam_far)) + (0.5f * max_width);
+ else
+ dist = (height / (far_height / cam_far)) + (0.5f * max_width);
+
+ m_cam_distance = dist;
+ m_update_cam = true;
+}
+
+void GUIScene::updateCamera(scene::ISceneNode *target)
+{
+ m_target = target;
+ updateTargetPos();
+
+ m_last_target_pos = m_target_pos;
+ updateCameraPos();
+
+ m_update_cam = true;
+}
+
+void GUIScene::updateTargetPos()
+{
+ m_last_target_pos = m_target_pos;
+ m_target->updateAbsolutePosition();
+ m_target_pos = m_target->getAbsolutePosition();
+}
+
+void GUIScene::setCameraRotation(v3f rot)
+{
+ correctBounds(rot);
+
+ core::matrix4 mat;
+ mat.setRotationDegrees(rot);
+
+ m_cam_pos = v3f(0.f, 0.f, m_cam_distance);
+ mat.rotateVect(m_cam_pos);
+
+ m_cam_pos += m_target_pos;
+ m_cam->setPosition(m_cam_pos);
+ m_update_cam = false;
+}
+
+bool GUIScene::correctBounds(v3f &rot)
+{
+ const float ROTATION_MAX_1 = 60.0f;
+ const float ROTATION_MAX_2 = 300.0f;
+
+ // Limit and correct the rotation when needed
+ if (rot.X < 90.f) {
+ if (rot.X > ROTATION_MAX_1) {
+ rot.X = ROTATION_MAX_1;
+ return true;
+ }
+ } else if (rot.X < ROTATION_MAX_2) {
+ rot.X = ROTATION_MAX_2;
+ return true;
+ }
+
+ // Not modified
+ return false;
+}
+
+void GUIScene::cameraLoop()
+{
+ updateCameraPos();
+ updateTargetPos();
+
+ if (m_target_pos != m_last_target_pos)
+ m_update_cam = true;
+
+ if (m_update_cam) {
+ m_cam_pos = m_target_pos + (m_cam_pos - m_target_pos).normalize() * m_cam_distance;
+
+ v3f rot = getCameraRotation();
+ if (correctBounds(rot))
+ setCameraRotation(rot);
+
+ m_cam->setPosition(m_cam_pos);
+ m_cam->setTarget(m_target_pos);
+
+ m_update_cam = false;
+ }
+}
diff --git a/src/gui/guiScene.h b/src/gui/guiScene.h
new file mode 100644
index 000000000..707e6f66a
--- /dev/null
+++ b/src/gui/guiScene.h
@@ -0,0 +1,85 @@
+/*
+Minetest
+Copyright (C) 2020 Jean-Patrick Guerrero <jeanpatrick.guerrero@gmail.com>
+
+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.
+*/
+
+#pragma once
+
+#include "irrlichttypes_extrabloated.h"
+#include "ICameraSceneNode.h"
+#include "StyleSpec.h"
+
+using namespace irr;
+
+class GUIScene : public gui::IGUIElement
+{
+public:
+ GUIScene(gui::IGUIEnvironment *env, scene::ISceneManager *smgr,
+ gui::IGUIElement *parent, core::recti rect, s32 id = -1);
+
+ ~GUIScene();
+
+ scene::IAnimatedMeshSceneNode *setMesh(scene::IAnimatedMesh *mesh = nullptr);
+ void setTexture(u32 idx, video::ITexture *texture);
+ void setBackgroundColor(const video::SColor &color) noexcept { m_bgcolor = color; };
+ void enableMouseControl(bool enable) noexcept { m_mouse_ctrl = enable; };
+ void setRotation(v2f rot) noexcept { m_custom_rot = rot; };
+ void enableContinuousRotation(bool enable) noexcept { m_inf_rot = enable; };
+ void setStyles(const std::array<StyleSpec, StyleSpec::NUM_STATES> &styles);
+
+ virtual void draw();
+ virtual bool OnEvent(const SEvent &event);
+
+private:
+ void calcOptimalDistance();
+ void updateTargetPos();
+ void updateCamera(scene::ISceneNode *target);
+ void setCameraRotation(v3f rot);
+ /// @return true indicates that the rotation was corrected
+ bool correctBounds(v3f &rot);
+ void cameraLoop();
+
+ void updateCameraPos() { m_cam_pos = m_cam->getPosition(); };
+ v3f getCameraRotation() const { return (m_cam_pos - m_target_pos).getHorizontalAngle(); };
+ void rotateCamera(const v3f &delta) { setCameraRotation(getCameraRotation() + delta); };
+
+ scene::ISceneManager *m_smgr;
+ video::IVideoDriver *m_driver;
+ scene::ICameraSceneNode *m_cam;
+ scene::ISceneNode *m_target = nullptr;
+ scene::IAnimatedMeshSceneNode *m_mesh = nullptr;
+
+ f32 m_cam_distance = 50.f;
+
+ u64 m_last_time = 0;
+
+ v3f m_cam_pos;
+ v3f m_target_pos;
+ v3f m_last_target_pos;
+ // Cursor positions
+ v2f m_curr_pos;
+ v2f m_last_pos;
+ // Initial rotation
+ v2f m_custom_rot;
+
+ bool m_mouse_ctrl = true;
+ bool m_update_cam = false;
+ bool m_inf_rot = false;
+ bool m_initial_rotation = true;
+
+ video::SColor m_bgcolor = 0;
+};
diff --git a/src/gui/guiScrollBar.cpp b/src/gui/guiScrollBar.cpp
index b04ccb9d5..c6a03f3e4 100644
--- a/src/gui/guiScrollBar.cpp
+++ b/src/gui/guiScrollBar.cpp
@@ -21,7 +21,7 @@ GUIScrollBar::GUIScrollBar(IGUIEnvironment *environment, IGUIElement *parent, s3
is_horizontal(horizontal), is_auto_scaling(auto_scale),
dragged_by_slider(false), tray_clicked(false), scroll_pos(0),
draw_center(0), thumb_size(0), min_pos(0), max_pos(100), small_step(10),
- large_step(50), last_change(0), drag_offset(0), page_size(100), border_size(0)
+ large_step(50), drag_offset(0), page_size(100), border_size(0)
{
refreshControls();
setNotClipped(false);
diff --git a/src/gui/guiScrollBar.h b/src/gui/guiScrollBar.h
index 29493bb99..d18f8e875 100644
--- a/src/gui/guiScrollBar.h
+++ b/src/gui/guiScrollBar.h
@@ -68,7 +68,6 @@ private:
s32 max_pos;
s32 small_step;
s32 large_step;
- u32 last_change;
s32 drag_offset;
s32 page_size;
s32 border_size;
diff --git a/src/gui/guiScrollContainer.cpp b/src/gui/guiScrollContainer.cpp
index 88cdc7057..0fe4c41bd 100644
--- a/src/gui/guiScrollContainer.cpp
+++ b/src/gui/guiScrollContainer.cpp
@@ -56,6 +56,18 @@ bool GUIScrollContainer::OnEvent(const SEvent &event)
return IGUIElement::OnEvent(event);
}
+void GUIScrollContainer::draw()
+{
+ if (isVisible()) {
+ core::list<IGUIElement *>::Iterator it = Children.begin();
+ for (; it != Children.end(); ++it)
+ if ((*it)->isNotClipped() ||
+ AbsoluteClippingRect.isRectCollided(
+ (*it)->getAbsolutePosition()))
+ (*it)->draw();
+ }
+}
+
void GUIScrollContainer::updateScrolling()
{
s32 pos = m_scrollbar->getPos();
diff --git a/src/gui/guiScrollContainer.h b/src/gui/guiScrollContainer.h
index a0306291e..9e3ec6e93 100644
--- a/src/gui/guiScrollContainer.h
+++ b/src/gui/guiScrollContainer.h
@@ -32,6 +32,8 @@ public:
virtual bool OnEvent(const SEvent &event) override;
+ virtual void draw() override;
+
inline void onScrollEvent(gui::IGUIElement *caller)
{
if (caller == m_scrollbar)
diff --git a/src/gui/guiSkin.cpp b/src/gui/guiSkin.cpp
index 8892a00b4..e09209bd9 100644
--- a/src/gui/guiSkin.cpp
+++ b/src/gui/guiSkin.cpp
@@ -29,7 +29,7 @@ GUISkin::GUISkin(EGUI_SKIN_TYPE type, video::IVideoDriver* driver)
{
Colors[EGDC_3D_DARK_SHADOW] = video::SColor(101,50,50,50);
Colors[EGDC_3D_SHADOW] = video::SColor(101,130,130,130);
- Colors[EGDC_3D_FACE] = video::SColor(101,210,210,210);
+ Colors[EGDC_3D_FACE] = video::SColor(220,100,100,100);
Colors[EGDC_3D_HIGH_LIGHT] = video::SColor(101,255,255,255);
Colors[EGDC_3D_LIGHT] = video::SColor(101,210,210,210);
Colors[EGDC_ACTIVE_BORDER] = video::SColor(101,16,14,115);
diff --git a/src/gui/guiTable.cpp b/src/gui/guiTable.cpp
index c705e17fb..cab2e19fd 100644
--- a/src/gui/guiTable.cpp
+++ b/src/gui/guiTable.cpp
@@ -56,7 +56,7 @@ GUITable::GUITable(gui::IGUIEnvironment *env,
m_font = skin->getFont();
if (m_font) {
m_font->grab();
- m_rowheight = m_font->getDimension(L"A").Height + 4;
+ m_rowheight = m_font->getDimension(L"Ay").Height + 4;
m_rowheight = MYMAX(m_rowheight, 1);
}
@@ -586,6 +586,31 @@ void GUITable::setSelected(s32 index)
}
}
+void GUITable::setOverrideFont(IGUIFont *font)
+{
+ if (m_font == font)
+ return;
+
+ if (font == nullptr)
+ font = Environment->getSkin()->getFont();
+
+ if (m_font)
+ m_font->drop();
+
+ m_font = font;
+ m_font->grab();
+
+ m_rowheight = m_font->getDimension(L"Ay").Height + 4;
+ m_rowheight = MYMAX(m_rowheight, 1);
+
+ updateScrollBar();
+}
+
+IGUIFont *GUITable::getOverrideFont() const
+{
+ return m_font;
+}
+
GUITable::DynamicData GUITable::getDynamicData() const
{
DynamicData dyndata;
diff --git a/src/gui/guiTable.h b/src/gui/guiTable.h
index 11093ea72..76a0e94d0 100644
--- a/src/gui/guiTable.h
+++ b/src/gui/guiTable.h
@@ -123,6 +123,12 @@ public:
// Autoscroll to make the selected row fully visible
void setSelected(s32 index);
+ //! Sets another skin independent font. If this is set to zero, the button uses the font of the skin.
+ virtual void setOverrideFont(gui::IGUIFont *font = nullptr);
+
+ //! Gets the override font (if any)
+ virtual gui::IGUIFont *getOverrideFont() const;
+
/* Get selection, scroll position and opened (sub)trees */
DynamicData getDynamicData() const;
diff --git a/src/gui/intlGUIEditBox.cpp b/src/gui/intlGUIEditBox.cpp
index 10395423c..8be63fd6f 100644
--- a/src/gui/intlGUIEditBox.cpp
+++ b/src/gui/intlGUIEditBox.cpp
@@ -1165,7 +1165,7 @@ void intlGUIEditBox::breakText()
s32 lastLineStart = 0;
s32 size = Text.size();
s32 length = 0;
- s32 elWidth = RelativeRect.getWidth() - 6;
+ s32 elWidth = RelativeRect.getWidth() - m_scrollbar_width - 10;
wchar_t c;
for (s32 i=0; i<size; ++i)
@@ -1478,8 +1478,6 @@ void intlGUIEditBox::createVScrollBar()
}
}
- RelativeRect.LowerRightCorner.X -= m_scrollbar_width + 4;
-
irr::core::rect<s32> scrollbarrect = FrameRect;
scrollbarrect.UpperLeftCorner.X += FrameRect.getWidth() - m_scrollbar_width;
m_vscrollbar = new GUIScrollBar(Environment, getParent(), -1,
diff --git a/src/httpfetch.cpp b/src/httpfetch.cpp
index 326b5052f..65202ce3e 100644
--- a/src/httpfetch.cpp
+++ b/src/httpfetch.cpp
@@ -294,13 +294,11 @@ HTTPFetchOngoing::HTTPFetchOngoing(const HTTPFetchRequest &request_,
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &oss);
}
- // Set POST (or GET) data
- if (request.post_fields.empty() && request.post_data.empty()) {
- curl_easy_setopt(curl, CURLOPT_HTTPGET, 1);
- } else if (request.multipart) {
+ // Set data from fields or raw_data
+ if (request.multipart) {
curl_httppost *last = NULL;
- for (StringMap::iterator it = request.post_fields.begin();
- it != request.post_fields.end(); ++it) {
+ for (StringMap::iterator it = request.fields.begin();
+ it != request.fields.end(); ++it) {
curl_formadd(&post, &last,
CURLFORM_NAMELENGTH, it->first.size(),
CURLFORM_PTRNAME, it->first.c_str(),
@@ -311,28 +309,42 @@ HTTPFetchOngoing::HTTPFetchOngoing(const HTTPFetchRequest &request_,
curl_easy_setopt(curl, CURLOPT_HTTPPOST, post);
// request.post_fields must now *never* be
// modified until CURLOPT_HTTPPOST is cleared
- } else if (request.post_data.empty()) {
- curl_easy_setopt(curl, CURLOPT_POST, 1);
- std::string str;
- for (auto &post_field : request.post_fields) {
- if (!str.empty())
- str += "&";
- str += urlencode(post_field.first);
- str += "=";
- str += urlencode(post_field.second);
- }
- curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE,
- str.size());
- curl_easy_setopt(curl, CURLOPT_COPYPOSTFIELDS,
- str.c_str());
} else {
- curl_easy_setopt(curl, CURLOPT_POST, 1);
- curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE,
- request.post_data.size());
- curl_easy_setopt(curl, CURLOPT_POSTFIELDS,
- request.post_data.c_str());
- // request.post_data must now *never* be
- // modified until CURLOPT_POSTFIELDS is cleared
+ switch (request.method) {
+ case HTTP_GET:
+ curl_easy_setopt(curl, CURLOPT_HTTPGET, 1);
+ break;
+ case HTTP_POST:
+ curl_easy_setopt(curl, CURLOPT_POST, 1);
+ break;
+ case HTTP_PUT:
+ curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PUT");
+ break;
+ case HTTP_DELETE:
+ curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE");
+ break;
+ }
+ if (request.method != HTTP_GET) {
+ if (!request.raw_data.empty()) {
+ curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE,
+ request.raw_data.size());
+ curl_easy_setopt(curl, CURLOPT_POSTFIELDS,
+ request.raw_data.c_str());
+ } else if (!request.fields.empty()) {
+ std::string str;
+ for (auto &field : request.fields) {
+ if (!str.empty())
+ str += "&";
+ str += urlencode(field.first);
+ str += "=";
+ str += urlencode(field.second);
+ }
+ curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE,
+ str.size());
+ curl_easy_setopt(curl, CURLOPT_COPYPOSTFIELDS,
+ str.c_str());
+ }
+ }
}
// Set additional HTTP headers
for (const std::string &extra_header : request.extra_headers) {
diff --git a/src/httpfetch.h b/src/httpfetch.h
index ae8b5afb5..3b9f17f0a 100644
--- a/src/httpfetch.h
+++ b/src/httpfetch.h
@@ -28,6 +28,15 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#define HTTPFETCH_DISCARD 0
#define HTTPFETCH_SYNC 1
+// Methods
+enum HttpMethod : u8
+{
+ HTTP_GET,
+ HTTP_POST,
+ HTTP_PUT,
+ HTTP_DELETE,
+};
+
struct HTTPFetchRequest
{
std::string url = "";
@@ -50,12 +59,15 @@ struct HTTPFetchRequest
// application/x-www-form-urlencoded. POST-only.
bool multipart = false;
- // POST fields. Fields are escaped properly.
- // If this is empty a GET request is done instead.
- StringMap post_fields;
+ // The Method to use default = GET
+ // Avaible methods GET, POST, PUT, DELETE
+ HttpMethod method = HTTP_GET;
+
+ // Fields of the request
+ StringMap fields;
- // Raw POST data, overrides post_fields.
- std::string post_data;
+ // Raw data of the request overrides fields
+ std::string raw_data;
// If not empty, should contain entries such as "Accept: text/html"
std::vector<std::string> extra_headers;
diff --git a/src/hud.cpp b/src/hud.cpp
index 3079b5cd8..1791e04df 100644
--- a/src/hud.cpp
+++ b/src/hud.cpp
@@ -28,6 +28,8 @@ const struct EnumString es_HudElementType[] =
{HUD_ELEM_INVENTORY, "inventory"},
{HUD_ELEM_WAYPOINT, "waypoint"},
{HUD_ELEM_IMAGE_WAYPOINT, "image_waypoint"},
+ {HUD_ELEM_COMPASS, "compass"},
+ {HUD_ELEM_MINIMAP, "minimap"},
{0, NULL},
};
@@ -40,6 +42,7 @@ const struct EnumString es_HudElementStat[] =
{HUD_STAT_TEXT, "text"},
{HUD_STAT_NUMBER, "number"},
{HUD_STAT_ITEM, "item"},
+ {HUD_STAT_ITEM, "precision"},
{HUD_STAT_DIR, "direction"},
{HUD_STAT_ALIGN, "alignment"},
{HUD_STAT_OFFSET, "offset"},
diff --git a/src/hud.h b/src/hud.h
index e015baec1..a0613ae98 100644
--- a/src/hud.h
+++ b/src/hud.h
@@ -51,7 +51,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#define HUD_HOTBAR_ITEMCOUNT_DEFAULT 8
#define HUD_HOTBAR_ITEMCOUNT_MAX 32
-
#define HOTBAR_IMAGE_SIZE 48
enum HudElementType {
@@ -60,7 +59,9 @@ enum HudElementType {
HUD_ELEM_STATBAR = 2,
HUD_ELEM_INVENTORY = 3,
HUD_ELEM_WAYPOINT = 4,
- HUD_ELEM_IMAGE_WAYPOINT = 5
+ HUD_ELEM_IMAGE_WAYPOINT = 5,
+ HUD_ELEM_COMPASS = 6,
+ HUD_ELEM_MINIMAP = 7
};
enum HudElementStat {
@@ -79,6 +80,13 @@ enum HudElementStat {
HUD_STAT_TEXT2,
};
+enum HudCompassDir {
+ HUD_COMPASS_ROTATE = 0,
+ HUD_COMPASS_ROTATE_REVERSE,
+ HUD_COMPASS_TRANSLATE,
+ HUD_COMPASS_TRANSLATE_REVERSE,
+};
+
struct HudElement {
HudElementType type;
v2f pos;
@@ -100,3 +108,12 @@ extern const EnumString es_HudElementType[];
extern const EnumString es_HudElementStat[];
extern const EnumString es_HudBuiltinElement[];
+// Minimap stuff
+
+enum MinimapType {
+ MINIMAP_TYPE_OFF,
+ MINIMAP_TYPE_SURFACE,
+ MINIMAP_TYPE_RADAR,
+ MINIMAP_TYPE_TEXTURE,
+};
+
diff --git a/src/inventory.cpp b/src/inventory.cpp
index 349ee503d..1ef9b13cd 100644
--- a/src/inventory.cpp
+++ b/src/inventory.cpp
@@ -258,6 +258,20 @@ std::string ItemStack::getDescription(IItemDefManager *itemdef) const
return desc.empty() ? name : desc;
}
+std::string ItemStack::getShortDescription(IItemDefManager *itemdef) const
+{
+ std::string desc = metadata.getString("short_description");
+ if (desc.empty())
+ desc = getDefinition(itemdef).short_description;
+ if (!desc.empty())
+ return desc;
+ // no short_description because of old server version or modified builtin
+ // return first line of description
+ std::stringstream sstr(getDescription(itemdef));
+ std::getline(sstr, desc, '\n');
+ return desc;
+}
+
ItemStack ItemStack::addItem(ItemStack newitem, IItemDefManager *itemdef)
{
@@ -732,17 +746,17 @@ void InventoryList::moveItemSomewhere(u32 i, InventoryList *dest, u32 count)
u32 InventoryList::moveItem(u32 i, InventoryList *dest, u32 dest_i,
u32 count, bool swap_if_needed, bool *did_swap)
{
- if(this == dest && i == dest_i)
+ if (this == dest && i == dest_i)
return count;
// Take item from source list
ItemStack item1;
- if(count == 0)
+ if (count == 0)
item1 = changeItem(i, ItemStack());
else
item1 = takeItem(i, count);
- if(item1.empty())
+ if (item1.empty())
return 0;
// Try to add the item to destination list
@@ -750,8 +764,7 @@ u32 InventoryList::moveItem(u32 i, InventoryList *dest, u32 dest_i,
item1 = dest->addItem(dest_i, item1);
// If something is returned, the item was not fully added
- if(!item1.empty())
- {
+ if (!item1.empty()) {
// If olditem is returned, nothing was added.
bool nothing_added = (item1.count == oldcount);
diff --git a/src/inventory.h b/src/inventory.h
index 67a7859ed..f36bc57cf 100644
--- a/src/inventory.h
+++ b/src/inventory.h
@@ -49,6 +49,7 @@ struct ItemStack
std::string getItemString(bool include_meta = true) const;
// Returns the tooltip
std::string getDescription(IItemDefManager *itemdef) const;
+ std::string getShortDescription(IItemDefManager *itemdef) const;
/*
Quantity methods
diff --git a/src/inventorymanager.cpp b/src/inventorymanager.cpp
index b6f464901..635bd2e4b 100644
--- a/src/inventorymanager.cpp
+++ b/src/inventorymanager.cpp
@@ -154,6 +154,93 @@ IMoveAction::IMoveAction(std::istream &is, bool somewhere) :
}
}
+void IMoveAction::swapDirections()
+{
+ std::swap(from_inv, to_inv);
+ std::swap(from_list, to_list);
+ std::swap(from_i, to_i);
+}
+
+void IMoveAction::onPutAndOnTake(const ItemStack &src_item, ServerActiveObject *player) const
+{
+ ServerScripting *sa = PLAYER_TO_SA(player);
+ if (to_inv.type == InventoryLocation::DETACHED)
+ sa->detached_inventory_OnPut(*this, src_item, player);
+ else if (to_inv.type == InventoryLocation::NODEMETA)
+ sa->nodemeta_inventory_OnPut(*this, src_item, player);
+ else if (to_inv.type == InventoryLocation::PLAYER)
+ sa->player_inventory_OnPut(*this, src_item, player);
+ else
+ assert(false);
+
+ if (from_inv.type == InventoryLocation::DETACHED)
+ sa->detached_inventory_OnTake(*this, src_item, player);
+ else if (from_inv.type == InventoryLocation::NODEMETA)
+ sa->nodemeta_inventory_OnTake(*this, src_item, player);
+ else if (from_inv.type == InventoryLocation::PLAYER)
+ sa->player_inventory_OnTake(*this, src_item, player);
+ else
+ assert(false);
+}
+
+void IMoveAction::onMove(int count, ServerActiveObject *player) const
+{
+ ServerScripting *sa = PLAYER_TO_SA(player);
+ if (from_inv.type == InventoryLocation::DETACHED)
+ sa->detached_inventory_OnMove(*this, count, player);
+ else if (from_inv.type == InventoryLocation::NODEMETA)
+ sa->nodemeta_inventory_OnMove(*this, count, player);
+ else if (from_inv.type == InventoryLocation::PLAYER)
+ sa->player_inventory_OnMove(*this, count, player);
+ else
+ assert(false);
+}
+
+int IMoveAction::allowPut(const ItemStack &dst_item, ServerActiveObject *player) const
+{
+ ServerScripting *sa = PLAYER_TO_SA(player);
+ int dst_can_put_count = 0xffff;
+ if (to_inv.type == InventoryLocation::DETACHED)
+ dst_can_put_count = sa->detached_inventory_AllowPut(*this, dst_item, player);
+ else if (to_inv.type == InventoryLocation::NODEMETA)
+ dst_can_put_count = sa->nodemeta_inventory_AllowPut(*this, dst_item, player);
+ else if (to_inv.type == InventoryLocation::PLAYER)
+ dst_can_put_count = sa->player_inventory_AllowPut(*this, dst_item, player);
+ else
+ assert(false);
+ return dst_can_put_count;
+}
+
+int IMoveAction::allowTake(const ItemStack &src_item, ServerActiveObject *player) const
+{
+ ServerScripting *sa = PLAYER_TO_SA(player);
+ int src_can_take_count = 0xffff;
+ if (from_inv.type == InventoryLocation::DETACHED)
+ src_can_take_count = sa->detached_inventory_AllowTake(*this, src_item, player);
+ else if (from_inv.type == InventoryLocation::NODEMETA)
+ src_can_take_count = sa->nodemeta_inventory_AllowTake(*this, src_item, player);
+ else if (from_inv.type == InventoryLocation::PLAYER)
+ src_can_take_count = sa->player_inventory_AllowTake(*this, src_item, player);
+ else
+ assert(false);
+ return src_can_take_count;
+}
+
+int IMoveAction::allowMove(int try_take_count, ServerActiveObject *player) const
+{
+ ServerScripting *sa = PLAYER_TO_SA(player);
+ int src_can_take_count = 0xffff;
+ if (from_inv.type == InventoryLocation::DETACHED)
+ src_can_take_count = sa->detached_inventory_AllowMove(*this, try_take_count, player);
+ else if (from_inv.type == InventoryLocation::NODEMETA)
+ src_can_take_count = sa->nodemeta_inventory_AllowMove(*this, try_take_count, player);
+ else if (from_inv.type == InventoryLocation::PLAYER)
+ src_can_take_count = sa->player_inventory_AllowMove(*this, try_take_count, player);
+ else
+ assert(false);
+ return src_can_take_count;
+}
+
void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGameDef *gamedef)
{
Inventory *inv_from = mgr->getInventory(from_inv);
@@ -251,93 +338,80 @@ void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame
Collect information of endpoints
*/
- int try_take_count = count;
- if (try_take_count == 0)
- try_take_count = list_from->getItem(from_i).count;
+ ItemStack src_item = list_from->getItem(from_i);
+ if (count > 0)
+ src_item.count = count;
+ if (src_item.empty())
+ return;
int src_can_take_count = 0xffff;
int dst_can_put_count = 0xffff;
- /* Query detached inventories */
+ // this is needed for swapping items inside one inventory to work
+ ItemStack restitem;
+ bool allow_swap = !list_to->itemFits(to_i, src_item, &restitem)
+ && restitem.count == src_item.count
+ && !caused_by_move_somewhere;
- // Move occurs in the same detached inventory
- if (from_inv.type == InventoryLocation::DETACHED &&
- from_inv == to_inv) {
- src_can_take_count = PLAYER_TO_SA(player)->detached_inventory_AllowMove(
- *this, try_take_count, player);
- dst_can_put_count = src_can_take_count;
- } else {
- // Destination is detached
- if (to_inv.type == InventoryLocation::DETACHED) {
- ItemStack src_item = list_from->getItem(from_i);
- src_item.count = try_take_count;
- dst_can_put_count = PLAYER_TO_SA(player)->detached_inventory_AllowPut(
- *this, src_item, player);
- }
- // Source is detached
- if (from_inv.type == InventoryLocation::DETACHED) {
- ItemStack src_item = list_from->getItem(from_i);
- src_item.count = try_take_count;
- src_can_take_count = PLAYER_TO_SA(player)->detached_inventory_AllowTake(
- *this, src_item, player);
- }
- }
-
- /* Query node metadata inventories */
+ // Shift-click: Cannot fill this stack, proceed with next slot
+ if (caused_by_move_somewhere && restitem.count == src_item.count)
+ return;
- // Both endpoints are nodemeta
- // Move occurs in the same nodemeta inventory
- if (from_inv.type == InventoryLocation::NODEMETA &&
- from_inv == to_inv) {
- src_can_take_count = PLAYER_TO_SA(player)->nodemeta_inventory_AllowMove(
- *this, try_take_count, player);
- dst_can_put_count = src_can_take_count;
- } else {
- // Destination is nodemeta
- if (to_inv.type == InventoryLocation::NODEMETA) {
- ItemStack src_item = list_from->getItem(from_i);
- src_item.count = try_take_count;
- dst_can_put_count = PLAYER_TO_SA(player)->nodemeta_inventory_AllowPut(
- *this, src_item, player);
+ if (allow_swap) {
+ // Swap will affect the entire stack if it can performed.
+ src_item = list_from->getItem(from_i);
+ count = src_item.count;
+ }
+
+ if (from_inv == to_inv) {
+ // Move action within the same inventory
+ src_can_take_count = allowMove(src_item.count, player);
+
+ bool swap_expected = allow_swap;
+ allow_swap = allow_swap
+ && (src_can_take_count == -1 || src_can_take_count >= src_item.count);
+ if (allow_swap) {
+ int try_put_count = list_to->getItem(to_i).count;
+ swapDirections();
+ dst_can_put_count = allowMove(try_put_count, player);
+ allow_swap = allow_swap
+ && (dst_can_put_count == -1 || dst_can_put_count >= try_put_count);
+ swapDirections();
+ } else {
+ dst_can_put_count = src_can_take_count;
}
- // Source is nodemeta
- if (from_inv.type == InventoryLocation::NODEMETA) {
- ItemStack src_item = list_from->getItem(from_i);
- src_item.count = try_take_count;
- src_can_take_count = PLAYER_TO_SA(player)->nodemeta_inventory_AllowTake(
- *this, src_item, player);
- }
- }
-
- // Query player inventories
-
- // Move occurs in the same player inventory
- if (from_inv.type == InventoryLocation::PLAYER &&
- from_inv == to_inv) {
- src_can_take_count = PLAYER_TO_SA(player)->player_inventory_AllowMove(
- *this, try_take_count, player);
- dst_can_put_count = src_can_take_count;
+ if (swap_expected != allow_swap)
+ src_can_take_count = dst_can_put_count = 0;
} else {
- // Destination is a player
- if (to_inv.type == InventoryLocation::PLAYER) {
- ItemStack src_item = list_from->getItem(from_i);
- src_item.count = try_take_count;
- dst_can_put_count = PLAYER_TO_SA(player)->player_inventory_AllowPut(
- *this, src_item, player);
- }
- // Source is a player
- if (from_inv.type == InventoryLocation::PLAYER) {
- ItemStack src_item = list_from->getItem(from_i);
- src_item.count = try_take_count;
- src_can_take_count = PLAYER_TO_SA(player)->player_inventory_AllowTake(
- *this, src_item, player);
+ // Take from one inventory, put into another
+ dst_can_put_count = allowPut(src_item, player);
+ src_can_take_count = allowTake(src_item, player);
+
+ bool swap_expected = allow_swap;
+ allow_swap = allow_swap
+ && (src_can_take_count == -1 || src_can_take_count >= src_item.count)
+ && (dst_can_put_count == -1 || dst_can_put_count >= src_item.count);
+ // A swap is expected, which means that we have to
+ // run the "allow" callbacks a second time with swapped inventories
+ if (allow_swap) {
+ ItemStack dst_item = list_to->getItem(to_i);
+ swapDirections();
+
+ int src_can_take = allowPut(dst_item, player);
+ int dst_can_put = allowTake(dst_item, player);
+ allow_swap = allow_swap
+ && (src_can_take == -1 || src_can_take >= dst_item.count)
+ && (dst_can_put == -1 || dst_can_put >= dst_item.count);
+ swapDirections();
}
+ if (swap_expected != allow_swap)
+ src_can_take_count = dst_can_put_count = 0;
}
int old_count = count;
/* Modify count according to collected data */
- count = try_take_count;
+ count = src_item.count;
if (src_can_take_count != -1 && count > src_can_take_count)
count = src_can_take_count;
if (dst_can_put_count != -1 && count > dst_can_put_count)
@@ -367,7 +441,7 @@ void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame
return;
}
- ItemStack src_item = list_from->getItem(from_i);
+ src_item = list_from->getItem(from_i);
src_item.count = count;
ItemStack from_stack_was = list_from->getItem(from_i);
ItemStack to_stack_was = list_to->getItem(to_i);
@@ -380,7 +454,8 @@ void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame
*/
bool did_swap = false;
move_count = list_from->moveItem(from_i,
- list_to, to_i, count, !caused_by_move_somewhere, &did_swap);
+ list_to, to_i, count, allow_swap, &did_swap);
+ assert(allow_swap == did_swap);
// If source is infinite, reset it's stack
if (src_can_take_count == -1) {
@@ -471,69 +546,29 @@ void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame
Report move to endpoints
*/
- /* Detached inventories */
-
- // Both endpoints are same detached
- if (from_inv.type == InventoryLocation::DETACHED &&
- from_inv == to_inv) {
- PLAYER_TO_SA(player)->detached_inventory_OnMove(
- *this, count, player);
- } else {
- // Destination is detached
- if (to_inv.type == InventoryLocation::DETACHED) {
- PLAYER_TO_SA(player)->detached_inventory_OnPut(
- *this, src_item, player);
- }
- // Source is detached
- if (from_inv.type == InventoryLocation::DETACHED) {
- PLAYER_TO_SA(player)->detached_inventory_OnTake(
- *this, src_item, player);
+ // Source = destination => move
+ if (from_inv == to_inv) {
+ onMove(count, player);
+ if (did_swap) {
+ // Item is now placed in source list
+ src_item = list_from->getItem(from_i);
+ swapDirections();
+ onMove(src_item.count, player);
+ swapDirections();
}
- }
-
- /* Node metadata inventories */
-
- // Both endpoints are same nodemeta
- if (from_inv.type == InventoryLocation::NODEMETA &&
- from_inv == to_inv) {
- PLAYER_TO_SA(player)->nodemeta_inventory_OnMove(
- *this, count, player);
- } else {
- // Destination is nodemeta
- if (to_inv.type == InventoryLocation::NODEMETA) {
- PLAYER_TO_SA(player)->nodemeta_inventory_OnPut(
- *this, src_item, player);
- }
- // Source is nodemeta
- if (from_inv.type == InventoryLocation::NODEMETA) {
- PLAYER_TO_SA(player)->nodemeta_inventory_OnTake(
- *this, src_item, player);
- }
- }
-
- // Player inventories
-
- // Both endpoints are same player inventory
- if (from_inv.type == InventoryLocation::PLAYER &&
- from_inv == to_inv) {
- PLAYER_TO_SA(player)->player_inventory_OnMove(
- *this, count, player);
+ mgr->setInventoryModified(from_inv);
} else {
- // Destination is player inventory
- if (to_inv.type == InventoryLocation::PLAYER) {
- PLAYER_TO_SA(player)->player_inventory_OnPut(
- *this, src_item, player);
+ onPutAndOnTake(src_item, player);
+ if (did_swap) {
+ // Item is now placed in source list
+ src_item = list_from->getItem(from_i);
+ swapDirections();
+ onPutAndOnTake(src_item, player);
+ swapDirections();
}
- // Source is player inventory
- if (from_inv.type == InventoryLocation::PLAYER) {
- PLAYER_TO_SA(player)->player_inventory_OnTake(
- *this, src_item, player);
- }
- }
-
- mgr->setInventoryModified(from_inv);
- if (inv_from != inv_to)
mgr->setInventoryModified(to_inv);
+ mgr->setInventoryModified(from_inv);
+ }
}
void IMoveAction::clientApply(InventoryManager *mgr, IGameDef *gamedef)
diff --git a/src/inventorymanager.h b/src/inventorymanager.h
index 69bf30169..4ad5d3f49 100644
--- a/src/inventorymanager.h
+++ b/src/inventorymanager.h
@@ -183,6 +183,18 @@ struct IMoveAction : public InventoryAction, public MoveAction
void apply(InventoryManager *mgr, ServerActiveObject *player, IGameDef *gamedef);
void clientApply(InventoryManager *mgr, IGameDef *gamedef);
+
+ void swapDirections();
+
+ void onPutAndOnTake(const ItemStack &src_item, ServerActiveObject *player) const;
+
+ void onMove(int count, ServerActiveObject *player) const;
+
+ int allowPut(const ItemStack &dst_item, ServerActiveObject *player) const;
+
+ int allowTake(const ItemStack &src_item, ServerActiveObject *player) const;
+
+ int allowMove(int try_take_count, ServerActiveObject *player) const;
};
struct IDropAction : public InventoryAction, public MoveAction
diff --git a/src/itemdef.cpp b/src/itemdef.cpp
index 8e0492827..5fb1e4c47 100644
--- a/src/itemdef.cpp
+++ b/src/itemdef.cpp
@@ -62,6 +62,7 @@ ItemDefinition& ItemDefinition::operator=(const ItemDefinition &def)
type = def.type;
name = def.name;
description = def.description;
+ short_description = def.short_description;
inventory_image = def.inventory_image;
inventory_overlay = def.inventory_overlay;
wield_image = def.wield_image;
@@ -102,6 +103,7 @@ void ItemDefinition::reset()
type = ITEM_NONE;
name = "";
description = "";
+ short_description = "";
inventory_image = "";
inventory_overlay = "";
wield_image = "";
@@ -128,10 +130,10 @@ void ItemDefinition::serialize(std::ostream &os, u16 protocol_version) const
u8 version = 6;
writeU8(os, version);
writeU8(os, type);
- os << serializeString(name);
- os << serializeString(description);
- os << serializeString(inventory_image);
- os << serializeString(wield_image);
+ os << serializeString16(name);
+ os << serializeString16(description);
+ os << serializeString16(inventory_image);
+ os << serializeString16(wield_image);
writeV3F32(os, wield_scale);
writeS16(os, stack_max);
writeU8(os, usable);
@@ -143,25 +145,27 @@ void ItemDefinition::serialize(std::ostream &os, u16 protocol_version) const
tool_capabilities->serialize(tmp_os, protocol_version);
tool_capabilities_s = tmp_os.str();
}
- os << serializeString(tool_capabilities_s);
+ os << serializeString16(tool_capabilities_s);
writeU16(os, groups.size());
for (const auto &group : groups) {
- os << serializeString(group.first);
+ os << serializeString16(group.first);
writeS16(os, group.second);
}
- os << serializeString(node_placement_prediction);
+ os << serializeString16(node_placement_prediction);
// Version from ContentFeatures::serialize to keep in sync
sound_place.serialize(os, CONTENTFEATURES_VERSION);
sound_place_failed.serialize(os, CONTENTFEATURES_VERSION);
writeF32(os, range);
- os << serializeString(palette_image);
+ os << serializeString16(palette_image);
writeARGB8(os, color);
- os << serializeString(inventory_overlay);
- os << serializeString(wield_overlay);
+ os << serializeString16(inventory_overlay);
+ os << serializeString16(wield_overlay);
+
+ os << serializeString16(short_description);
}
void ItemDefinition::deSerialize(std::istream &is)
@@ -175,16 +179,16 @@ void ItemDefinition::deSerialize(std::istream &is)
throw SerializationError("unsupported ItemDefinition version");
type = (enum ItemType)readU8(is);
- name = deSerializeString(is);
- description = deSerializeString(is);
- inventory_image = deSerializeString(is);
- wield_image = deSerializeString(is);
+ name = deSerializeString16(is);
+ description = deSerializeString16(is);
+ inventory_image = deSerializeString16(is);
+ wield_image = deSerializeString16(is);
wield_scale = readV3F32(is);
stack_max = readS16(is);
usable = readU8(is);
liquids_pointable = readU8(is);
- std::string tool_capabilities_s = deSerializeString(is);
+ std::string tool_capabilities_s = deSerializeString16(is);
if (!tool_capabilities_s.empty()) {
std::istringstream tmp_is(tool_capabilities_s, std::ios::binary);
tool_capabilities = new ToolCapabilities;
@@ -194,27 +198,28 @@ void ItemDefinition::deSerialize(std::istream &is)
groups.clear();
u32 groups_size = readU16(is);
for(u32 i=0; i<groups_size; i++){
- std::string name = deSerializeString(is);
+ std::string name = deSerializeString16(is);
int value = readS16(is);
groups[name] = value;
}
- node_placement_prediction = deSerializeString(is);
+ node_placement_prediction = deSerializeString16(is);
// Version from ContentFeatures::serialize to keep in sync
sound_place.deSerialize(is, CONTENTFEATURES_VERSION);
sound_place_failed.deSerialize(is, CONTENTFEATURES_VERSION);
range = readF32(is);
- palette_image = deSerializeString(is);
+ palette_image = deSerializeString16(is);
color = readARGB8(is);
- inventory_overlay = deSerializeString(is);
- wield_overlay = deSerializeString(is);
+ inventory_overlay = deSerializeString16(is);
+ wield_overlay = deSerializeString16(is);
// If you add anything here, insert it primarily inside the try-catch
// block to not need to increase the version.
- //try {
- //} catch(SerializationError &e) {};
+ try {
+ short_description = deSerializeString16(is);
+ } catch(SerializationError &e) {};
}
@@ -521,14 +526,14 @@ public:
// Serialize ItemDefinition and write wrapped in a string
std::ostringstream tmp_os(std::ios::binary);
def->serialize(tmp_os, protocol_version);
- os << serializeString(tmp_os.str());
+ os << serializeString16(tmp_os.str());
}
writeU16(os, m_aliases.size());
for (const auto &it : m_aliases) {
- os << serializeString(it.first);
- os << serializeString(it.second);
+ os << serializeString16(it.first);
+ os << serializeString16(it.second);
}
}
void deSerialize(std::istream &is)
@@ -543,7 +548,7 @@ public:
for(u16 i=0; i<count; i++)
{
// Deserialize a string and grab an ItemDefinition from it
- std::istringstream tmp_is(deSerializeString(is), std::ios::binary);
+ std::istringstream tmp_is(deSerializeString16(is), std::ios::binary);
ItemDefinition def;
def.deSerialize(tmp_is);
// Register
@@ -552,8 +557,8 @@ public:
u16 num_aliases = readU16(is);
for(u16 i=0; i<num_aliases; i++)
{
- std::string name = deSerializeString(is);
- std::string convert_to = deSerializeString(is);
+ std::string name = deSerializeString16(is);
+ std::string convert_to = deSerializeString16(is);
registerAlias(name, convert_to);
}
}
diff --git a/src/itemdef.h b/src/itemdef.h
index f47e6cbe7..ebf0d3527 100644
--- a/src/itemdef.h
+++ b/src/itemdef.h
@@ -56,6 +56,7 @@ struct ItemDefinition
ItemType type;
std::string name; // "" = hand
std::string description; // Shown in tooltip.
+ std::string short_description;
/*
Visual properties
diff --git a/src/light.cpp b/src/light.cpp
index 8196fedff..d5389b450 100644
--- a/src/light.cpp
+++ b/src/light.cpp
@@ -18,6 +18,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
*/
#include "light.h"
+#include <algorithm>
#include <cmath>
#include "util/numeric.h"
#include "settings.h"
@@ -81,9 +82,11 @@ void set_light_table(float gamma)
// Strictly speaking, rangelim is not necessary here—if the implementation
// is conforming. But we don’t want problems in any case.
light_LUT[i] = rangelim((s32)(255.0f * brightness), 0, 255);
+
// Ensure light brightens with each level
- if (i > 1 && light_LUT[i] <= light_LUT[i - 1])
- light_LUT[i] = light_LUT[i - 1] + 1;
+ if (i > 0 && light_LUT[i] <= light_LUT[i - 1]) {
+ light_LUT[i] = std::min((u8)254, light_LUT[i - 1]) + 1;
+ }
}
}
diff --git a/src/log.cpp b/src/log.cpp
index 54442c39b..3c61414e9 100644
--- a/src/log.cpp
+++ b/src/log.cpp
@@ -97,16 +97,7 @@ LogBuffer verbose_buf(g_logger, LL_VERBOSE);
std::ostream *dout_con_ptr = &null_stream;
std::ostream *derr_con_ptr = &verbosestream;
-// Server
-std::ostream *dout_server_ptr = &infostream;
-std::ostream *derr_server_ptr = &errorstream;
-
-#ifndef SERVER
-// Client
-std::ostream *dout_client_ptr = &infostream;
-std::ostream *derr_client_ptr = &errorstream;
-#endif
-
+// Common streams
std::ostream rawstream(&raw_buf);
std::ostream dstream(&none_buf);
std::ostream errorstream(&error_buf);
diff --git a/src/log.h b/src/log.h
index 856d3479b..6ed6b1fb7 100644
--- a/src/log.h
+++ b/src/log.h
@@ -192,14 +192,8 @@ extern std::ostream null_stream;
extern std::ostream *dout_con_ptr;
extern std::ostream *derr_con_ptr;
-extern std::ostream *dout_server_ptr;
extern std::ostream *derr_server_ptr;
-#ifndef SERVER
-extern std::ostream *dout_client_ptr;
-extern std::ostream *derr_client_ptr;
-#endif
-
extern Logger g_logger;
// Writes directly to all LL_NONE log outputs for g_logger with no prefix.
@@ -222,8 +216,4 @@ extern std::ostream dstream;
#define dout_con (*dout_con_ptr)
#define derr_con (*derr_con_ptr)
-#define dout_server (*dout_server_ptr)
-#ifndef SERVER
- #define dout_client (*dout_client_ptr)
-#endif
diff --git a/src/map.cpp b/src/map.cpp
index b9ab7c066..6d53351ef 100644
--- a/src/map.cpp
+++ b/src/map.cpp
@@ -62,8 +62,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
Map
*/
-Map::Map(std::ostream &dout, IGameDef *gamedef):
- m_dout(dout),
+Map::Map(IGameDef *gamedef):
m_gamedef(gamedef),
m_nodedef(gamedef->ndef())
{
@@ -140,6 +139,21 @@ MapBlock * Map::getBlockNoCreate(v3s16 p3d)
return block;
}
+void Map::listAllLoadedBlocks(std::vector<v3s16> &dst)
+{
+ for (auto &sector_it : m_sectors) {
+ MapSector *sector = sector_it.second;
+
+ MapBlockVect blocks;
+ sector->getBlocks(blocks);
+
+ for (MapBlock *block : blocks) {
+ v3s16 p = block->getPos();
+ dst.push_back(p);
+ }
+ }
+}
+
bool Map::isNodeUnderground(v3s16 p)
{
v3s16 blockpos = getNodeBlockPos(p);
@@ -1201,7 +1215,7 @@ bool Map::isBlockOccluded(MapBlock *block, v3s16 cam_pos_nodes)
*/
ServerMap::ServerMap(const std::string &savedir, IGameDef *gamedef,
EmergeManager *emerge, MetricsBackend *mb):
- Map(dout_server, gamedef),
+ Map(gamedef),
settings_mgr(g_settings, savedir + DIR_DELIM + "map_meta.txt"),
m_emerge(emerge)
{
@@ -1327,11 +1341,6 @@ u64 ServerMap::getSeed()
return getMapgenParams()->seed;
}
-s16 ServerMap::getWaterLevel()
-{
- return getMapgenParams()->water_level;
-}
-
bool ServerMap::blockpos_over_mapgen_limit(v3s16 p)
{
const s16 mapgen_limit_bp = rangelim(
@@ -1351,6 +1360,9 @@ bool ServerMap::initBlockMake(v3s16 blockpos, BlockMakeData *data)
v3s16 bpmin = EmergeManager::getContainingChunk(blockpos, csize);
v3s16 bpmax = bpmin + v3s16(1, 1, 1) * (csize - 1);
+ if (!m_chunks_in_progress.insert(bpmin).second)
+ return false;
+
bool enable_mapgen_debug_info = m_emerge->enable_mapgen_debug_info;
EMERGE_DBG_OUT("initBlockMake(): " PP(bpmin) " - " PP(bpmax));
@@ -1366,7 +1378,6 @@ bool ServerMap::initBlockMake(v3s16 blockpos, BlockMakeData *data)
data->seed = getSeed();
data->blockpos_min = bpmin;
data->blockpos_max = bpmax;
- data->blockpos_requested = blockpos;
data->nodedef = m_nodedef;
/*
@@ -1488,6 +1499,7 @@ void ServerMap::finishBlockMake(BlockMakeData *data,
NOTE: Will be saved later.
*/
//save(MOD_STATE_WRITE_AT_UNLOAD);
+ m_chunks_in_progress.erase(bpmin);
}
MapSector *ServerMap::createSector(v2s16 p2d)
@@ -1732,59 +1744,6 @@ void ServerMap::updateVManip(v3s16 pos)
vm->m_is_dirty = true;
}
-s16 ServerMap::findGroundLevel(v2s16 p2d)
-{
-#if 0
- /*
- Uh, just do something random...
- */
- // Find existing map from top to down
- s16 max=63;
- s16 min=-64;
- v3s16 p(p2d.X, max, p2d.Y);
- for(; p.Y>min; p.Y--)
- {
- MapNode n = getNodeNoEx(p);
- if(n.getContent() != CONTENT_IGNORE)
- break;
- }
- if(p.Y == min)
- goto plan_b;
- // If this node is not air, go to plan b
- if(getNodeNoEx(p).getContent() != CONTENT_AIR)
- goto plan_b;
- // Search existing walkable and return it
- for(; p.Y>min; p.Y--)
- {
- MapNode n = getNodeNoEx(p);
- if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
- return p.Y;
- }
-
- // Move to plan b
-plan_b:
-#endif
-
- /*
- Determine from map generator noise functions
- */
-
- s16 level = m_emerge->getGroundLevelAtPoint(p2d);
- return level;
-
- //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
- //return (s16)level;
-}
-
-void ServerMap::createDirs(const std::string &path)
-{
- if (!fs::CreateAllDirs(path)) {
- m_dout<<"ServerMap: Failed to create directory "
- <<"\""<<path<<"\""<<std::endl;
- throw BaseException("ServerMap failed to create directory");
- }
-}
-
void ServerMap::save(ModifiedState save_level)
{
if (!m_map_saving_enabled) {
@@ -1864,21 +1823,6 @@ void ServerMap::listAllLoadableBlocks(std::vector<v3s16> &dst)
dbase_ro->listAllLoadableBlocks(dst);
}
-void ServerMap::listAllLoadedBlocks(std::vector<v3s16> &dst)
-{
- for (auto &sector_it : m_sectors) {
- MapSector *sector = sector_it.second;
-
- MapBlockVect blocks;
- sector->getBlocks(blocks);
-
- for (MapBlock *block : blocks) {
- v3s16 p = block->getPos();
- dst.push_back(p);
- }
- }
-}
-
MapDatabase *ServerMap::createDatabase(
const std::string &name,
const std::string &savedir,
diff --git a/src/map.h b/src/map.h
index 4d9847063..0b0213ca0 100644
--- a/src/map.h
+++ b/src/map.h
@@ -123,7 +123,7 @@ class Map /*: public NodeContainer*/
{
public:
- Map(std::ostream &dout, IGameDef *gamedef);
+ Map(IGameDef *gamedef);
virtual ~Map();
DISABLE_CLASS_COPY(Map);
@@ -149,8 +149,6 @@ public:
MapSector * getSectorNoGenerateNoLock(v2s16 p2d);
// Same as the above (there exists no lock anymore)
MapSector * getSectorNoGenerate(v2s16 p2d);
- // Gets an existing sector or creates an empty one
- //MapSector * getSectorCreate(v2s16 p2d);
/*
This is overloaded by ClientMap and ServerMap to allow
@@ -162,7 +160,9 @@ public:
MapBlock * getBlockNoCreate(v3s16 p);
// Returns NULL if not found
MapBlock * getBlockNoCreateNoEx(v3s16 p);
-
+
+ void listAllLoadedBlocks(std::vector<v3s16> &dst);
+
/* Server overrides */
virtual MapBlock * emergeBlock(v3s16 p, bool create_blank=true)
{ return getBlockNoCreateNoEx(p); }
@@ -269,11 +269,6 @@ public:
void removeNodeTimer(v3s16 p);
/*
- Misc.
- */
- std::map<v2s16, MapSector*> *getSectorsPtr(){return &m_sectors;}
-
- /*
Variables
*/
@@ -283,8 +278,6 @@ public:
protected:
friend class LuaVoxelManip;
- std::ostream &m_dout; // A bit deprecated, could be removed
-
IGameDef *m_gamedef;
std::set<MapEventReceiver*> m_event_receivers;
@@ -374,15 +367,6 @@ public:
*/
MapBlock *getBlockOrEmerge(v3s16 p3d);
- // Helper for placing objects on ground level
- s16 findGroundLevel(v2s16 p2d);
-
- /*
- Misc. helper functions for fiddling with directory and file
- names when saving
- */
- void createDirs(const std::string &path);
-
/*
Database functions
*/
@@ -394,7 +378,6 @@ public:
void save(ModifiedState save_level);
void listAllLoadableBlocks(std::vector<v3s16> &dst);
- void listAllLoadedBlocks(std::vector<v3s16> &dst);
MapgenParams *getMapgenParams();
@@ -414,7 +397,6 @@ public:
bool isSavingEnabled(){ return m_map_saving_enabled; }
u64 getSeed();
- s16 getWaterLevel();
/*!
* Fixes lighting in one map block.
@@ -442,6 +424,7 @@ private:
// Chunks
core::map<v2s16, MapChunk*> m_chunks;
#endif
+ std::set<v3s16> m_chunks_in_progress;
/*
Metadata is re-written on disk only if this is true.
diff --git a/src/map_settings_manager.cpp b/src/map_settings_manager.cpp
index 4f070e910..9c447b3d0 100644
--- a/src/map_settings_manager.cpp
+++ b/src/map_settings_manager.cpp
@@ -32,6 +32,10 @@ MapSettingsManager::MapSettingsManager(Settings *user_settings,
m_user_settings(user_settings)
{
assert(m_user_settings != NULL);
+
+ Mapgen::setDefaultSettings(m_map_settings);
+ // This inherits the combined defaults provided by loadGameConfAndInitWorld.
+ m_map_settings->overrideDefaults(user_settings);
}
@@ -179,20 +183,8 @@ MapgenParams *MapSettingsManager::makeMapgenParams()
params->mgtype = mgtype;
- // Load the mapgen param defaults
- /* FIXME: Why is it done like this? MapgenParams should just
- * set the defaults in its constructor instead. */
- {
- Settings default_settings;
- Mapgen::setDefaultSettings(&default_settings);
- params->MapgenParams::readParams(&default_settings);
- params->readParams(&default_settings);
- }
-
// Load the rest of the mapgen params from our active settings
- params->MapgenParams::readParams(m_user_settings);
params->MapgenParams::readParams(m_map_settings);
- params->readParams(m_user_settings);
params->readParams(m_map_settings);
// Hold onto our params
diff --git a/src/mapblock.cpp b/src/mapblock.cpp
index 5b755b7a6..8bfecd755 100644
--- a/src/mapblock.cpp
+++ b/src/mapblock.cpp
@@ -533,7 +533,7 @@ void MapBlock::deSerialize(std::istream &is, u8 version, bool disk)
// Timestamp
TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
<<": Timestamp"<<std::endl);
- setTimestamp(readU32(is));
+ setTimestampNoChangedFlag(readU32(is));
m_disk_timestamp = m_timestamp;
// Dynamically re-set ids based on node names
@@ -668,13 +668,13 @@ void MapBlock::deSerialize_pre22(std::istream &is, u8 version, bool disk)
// Ignore errors
try {
if (version <= 15) {
- std::string data = deSerializeString(is);
+ std::string data = deSerializeString16(is);
std::istringstream iss(data, std::ios_base::binary);
content_nodemeta_deserialize_legacy(iss,
&m_node_metadata, &m_node_timers,
m_gamedef->idef());
} else {
- //std::string data = deSerializeLongString(is);
+ //std::string data = deSerializeString32(is);
std::ostringstream oss(std::ios_base::binary);
decompressZlib(is, oss);
std::istringstream iss(oss.str(), std::ios_base::binary);
@@ -716,10 +716,10 @@ void MapBlock::deSerialize_pre22(std::istream &is, u8 version, bool disk)
// Timestamp
if (version >= 17) {
- setTimestamp(readU32(is));
+ setTimestampNoChangedFlag(readU32(is));
m_disk_timestamp = m_timestamp;
} else {
- setTimestamp(BLOCK_TIMESTAMP_UNDEFINED);
+ setTimestampNoChangedFlag(BLOCK_TIMESTAMP_UNDEFINED);
}
// Dynamically re-set ids based on node names
diff --git a/src/mapgen/mapgen.cpp b/src/mapgen/mapgen.cpp
index f57529082..e0dfd2d71 100644
--- a/src/mapgen/mapgen.cpp
+++ b/src/mapgen/mapgen.cpp
@@ -58,6 +58,7 @@ FlagDesc flagdesc_mapgen[] = {
{"light", MG_LIGHT},
{"decorations", MG_DECORATIONS},
{"biomes", MG_BIOMES},
+ {"ores", MG_ORES},
{NULL, 0}
};
@@ -217,7 +218,7 @@ void Mapgen::getMapgenNames(std::vector<const char *> *mgnames, bool include_hid
void Mapgen::setDefaultSettings(Settings *settings)
{
settings->setDefault("mg_flags", flagdesc_mapgen,
- MG_CAVES | MG_DUNGEONS | MG_LIGHT | MG_DECORATIONS | MG_BIOMES);
+ MG_CAVES | MG_DUNGEONS | MG_LIGHT | MG_DECORATIONS | MG_BIOMES | MG_ORES);
for (int i = 0; i < (int)MAPGEN_INVALID; ++i) {
MapgenParams *params = createMapgenParams((MapgenType)i);
@@ -243,26 +244,6 @@ u32 Mapgen::getBlockSeed2(v3s16 p, s32 seed)
}
-// Returns Y one under area minimum if not found
-s16 Mapgen::findGroundLevelFull(v2s16 p2d)
-{
- const v3s16 &em = vm->m_area.getExtent();
- s16 y_nodes_max = vm->m_area.MaxEdge.Y;
- s16 y_nodes_min = vm->m_area.MinEdge.Y;
- u32 i = vm->m_area.index(p2d.X, y_nodes_max, p2d.Y);
- s16 y;
-
- for (y = y_nodes_max; y >= y_nodes_min; y--) {
- MapNode &n = vm->m_data[i];
- if (ndef->get(n).walkable)
- break;
-
- VoxelArea::add_y(em, i, -1);
- }
- return (y >= y_nodes_min) ? y : y_nodes_min - 1;
-}
-
-
// Returns -MAX_MAP_GENERATION_LIMIT if not found
s16 Mapgen::findGroundLevel(v2s16 p2d, s16 ymin, s16 ymax)
{
@@ -983,19 +964,6 @@ GenerateNotifier::GenerateNotifier(u32 notify_on,
}
-void GenerateNotifier::setNotifyOn(u32 notify_on)
-{
- m_notify_on = notify_on;
-}
-
-
-void GenerateNotifier::setNotifyOnDecoIds(
- const std::set<u32> *notify_on_deco_ids)
-{
- m_notify_on_deco_ids = notify_on_deco_ids;
-}
-
-
bool GenerateNotifier::addEvent(GenNotifyType type, v3s16 pos, u32 id)
{
if (!(m_notify_on & (1 << type)))
diff --git a/src/mapgen/mapgen.h b/src/mapgen/mapgen.h
index a92b3b0d0..1487731e2 100644
--- a/src/mapgen/mapgen.h
+++ b/src/mapgen/mapgen.h
@@ -37,6 +37,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#define MG_LIGHT 0x10
#define MG_DECORATIONS 0x20
#define MG_BIOMES 0x40
+#define MG_ORES 0x80
typedef u16 biome_t; // copy from mg_biome.h to avoid an unnecessary include
@@ -87,19 +88,17 @@ struct GenNotifyEvent {
class GenerateNotifier {
public:
+ // Use only for temporary Mapgen objects with no map generation!
GenerateNotifier() = default;
GenerateNotifier(u32 notify_on, const std::set<u32> *notify_on_deco_ids);
- void setNotifyOn(u32 notify_on);
- void setNotifyOnDecoIds(const 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);
void clearEvents();
private:
u32 m_notify_on = 0;
- const std::set<u32> *m_notify_on_deco_ids;
+ const std::set<u32> *m_notify_on_deco_ids = nullptr;
std::list<GenNotifyEvent> m_notify_events;
};
@@ -185,7 +184,6 @@ public:
static u32 getBlockSeed(v3s16 p, s32 seed);
static u32 getBlockSeed2(v3s16 p, s32 seed);
- s16 findGroundLevelFull(v2s16 p2d);
s16 findGroundLevel(v2s16 p2d, s16 ymin, s16 ymax);
s16 findLiquidSurface(v2s16 p2d, s16 ymin, s16 ymax);
void updateHeightmap(v3s16 nmin, v3s16 nmax);
diff --git a/src/mapgen/mapgen_carpathian.cpp b/src/mapgen/mapgen_carpathian.cpp
index feb9b428c..b3a0bd270 100644
--- a/src/mapgen/mapgen_carpathian.cpp
+++ b/src/mapgen/mapgen_carpathian.cpp
@@ -260,12 +260,6 @@ void MapgenCarpathian::makeChunk(BlockMakeData *data)
// Pre-conditions
assert(data->vmanip);
assert(data->nodedef);
- assert(data->blockpos_requested.X >= data->blockpos_min.X &&
- data->blockpos_requested.Y >= data->blockpos_min.Y &&
- data->blockpos_requested.Z >= data->blockpos_min.Z);
- assert(data->blockpos_requested.X <= data->blockpos_max.X &&
- data->blockpos_requested.Y <= data->blockpos_max.Y &&
- data->blockpos_requested.Z <= data->blockpos_max.Z);
this->generating = true;
this->vm = data->vmanip;
@@ -315,7 +309,8 @@ void MapgenCarpathian::makeChunk(BlockMakeData *data)
}
// Generate the registered ores
- m_emerge->oremgr->placeAllOres(this, blockseed, node_min, node_max);
+ if (flags & MG_ORES)
+ m_emerge->oremgr->placeAllOres(this, blockseed, node_min, node_max);
// Generate dungeons
if (flags & MG_DUNGEONS)
diff --git a/src/mapgen/mapgen_flat.cpp b/src/mapgen/mapgen_flat.cpp
index 369777ad2..342455029 100644
--- a/src/mapgen/mapgen_flat.cpp
+++ b/src/mapgen/mapgen_flat.cpp
@@ -1,7 +1,7 @@
/*
Minetest
-Copyright (C) 2015-2018 paramat
-Copyright (C) 2015-2018 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
+Copyright (C) 2015-2020 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
@@ -39,8 +39,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
FlagDesc flagdesc_mapgen_flat[] = {
- {"lakes", MGFLAT_LAKES},
- {"hills", MGFLAT_HILLS},
+ {"lakes", MGFLAT_LAKES},
+ {"hills", MGFLAT_HILLS},
+ {"caverns", MGFLAT_CAVERNS},
{NULL, 0}
};
@@ -52,17 +53,21 @@ MapgenFlat::MapgenFlat(MapgenFlatParams *params, EmergeParams *emerge)
{
spflags = params->spflags;
ground_level = params->ground_level;
- large_cave_depth = params->large_cave_depth;
+ lake_threshold = params->lake_threshold;
+ lake_steepness = params->lake_steepness;
+ hill_threshold = params->hill_threshold;
+ hill_steepness = params->hill_steepness;
+
+ cave_width = params->cave_width;
small_cave_num_min = params->small_cave_num_min;
small_cave_num_max = params->small_cave_num_max;
large_cave_num_min = params->large_cave_num_min;
large_cave_num_max = params->large_cave_num_max;
+ large_cave_depth = params->large_cave_depth;
large_cave_flooded = params->large_cave_flooded;
- cave_width = params->cave_width;
- lake_threshold = params->lake_threshold;
- lake_steepness = params->lake_steepness;
- hill_threshold = params->hill_threshold;
- hill_steepness = params->hill_steepness;
+ cavern_limit = params->cavern_limit;
+ cavern_taper = params->cavern_taper;
+ cavern_threshold = params->cavern_threshold;
dungeon_ymin = params->dungeon_ymin;
dungeon_ymax = params->dungeon_ymax;
@@ -71,9 +76,11 @@ MapgenFlat::MapgenFlat(MapgenFlatParams *params, EmergeParams *emerge)
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;
+ MapgenBasic::np_cavern = params->np_cavern;
MapgenBasic::np_dungeons = params->np_dungeons;
}
@@ -88,11 +95,12 @@ MapgenFlat::~MapgenFlat()
MapgenFlatParams::MapgenFlatParams():
- np_terrain (0, 1, v3f(600, 600, 600), 7244, 5, 0.6, 2.0),
- np_filler_depth (0, 1.2, v3f(150, 150, 150), 261, 3, 0.7, 2.0),
- np_cave1 (0, 12, v3f(61, 61, 61), 52534, 3, 0.5, 2.0),
- np_cave2 (0, 12, v3f(67, 67, 67), 10325, 3, 0.5, 2.0),
- np_dungeons (0.9, 0.5, v3f(500, 500, 500), 0, 2, 0.8, 2.0)
+ np_terrain (0, 1, v3f(600, 600, 600), 7244, 5, 0.6, 2.0),
+ np_filler_depth (0, 1.2, v3f(150, 150, 150), 261, 3, 0.7, 2.0),
+ np_cavern (0.0, 1.0, v3f(384, 128, 384), 723, 5, 0.63, 2.0),
+ np_cave1 (0, 12, v3f(61, 61, 61), 52534, 3, 0.5, 2.0),
+ np_cave2 (0, 12, v3f(67, 67, 67), 10325, 3, 0.5, 2.0),
+ np_dungeons (0.9, 0.5, v3f(500, 500, 500), 0, 2, 0.8, 2.0)
{
}
@@ -112,11 +120,15 @@ void MapgenFlatParams::readParams(const Settings *settings)
settings->getFloatNoEx("mgflat_lake_steepness", lake_steepness);
settings->getFloatNoEx("mgflat_hill_threshold", hill_threshold);
settings->getFloatNoEx("mgflat_hill_steepness", hill_steepness);
+ settings->getS16NoEx("mgflat_cavern_limit", cavern_limit);
+ settings->getS16NoEx("mgflat_cavern_taper", cavern_taper);
+ settings->getFloatNoEx("mgflat_cavern_threshold", cavern_threshold);
settings->getS16NoEx("mgflat_dungeon_ymin", dungeon_ymin);
settings->getS16NoEx("mgflat_dungeon_ymax", dungeon_ymax);
settings->getNoiseParams("mgflat_np_terrain", np_terrain);
settings->getNoiseParams("mgflat_np_filler_depth", np_filler_depth);
+ settings->getNoiseParams("mgflat_np_cavern", np_cavern);
settings->getNoiseParams("mgflat_np_cave1", np_cave1);
settings->getNoiseParams("mgflat_np_cave2", np_cave2);
settings->getNoiseParams("mgflat_np_dungeons", np_dungeons);
@@ -138,11 +150,15 @@ void MapgenFlatParams::writeParams(Settings *settings) const
settings->setFloat("mgflat_lake_steepness", lake_steepness);
settings->setFloat("mgflat_hill_threshold", hill_threshold);
settings->setFloat("mgflat_hill_steepness", hill_steepness);
+ settings->setS16("mgflat_cavern_limit", cavern_limit);
+ settings->setS16("mgflat_cavern_taper", cavern_taper);
+ settings->setFloat("mgflat_cavern_threshold", cavern_threshold);
settings->setS16("mgflat_dungeon_ymin", dungeon_ymin);
settings->setS16("mgflat_dungeon_ymax", dungeon_ymax);
settings->setNoiseParams("mgflat_np_terrain", np_terrain);
settings->setNoiseParams("mgflat_np_filler_depth", np_filler_depth);
+ settings->setNoiseParams("mgflat_np_cavern", np_cavern);
settings->setNoiseParams("mgflat_np_cave1", np_cave1);
settings->setNoiseParams("mgflat_np_cave2", np_cave2);
settings->setNoiseParams("mgflat_np_dungeons", np_dungeons);
@@ -193,12 +209,6 @@ void MapgenFlat::makeChunk(BlockMakeData *data)
// Pre-conditions
assert(data->vmanip);
assert(data->nodedef);
- assert(data->blockpos_requested.X >= data->blockpos_min.X &&
- data->blockpos_requested.Y >= data->blockpos_min.Y &&
- data->blockpos_requested.Z >= data->blockpos_min.Z);
- assert(data->blockpos_requested.X <= data->blockpos_max.X &&
- data->blockpos_requested.Y <= data->blockpos_max.Y &&
- data->blockpos_requested.Z <= data->blockpos_max.Z);
this->generating = true;
this->vm = data->vmanip;
@@ -226,15 +236,30 @@ void MapgenFlat::makeChunk(BlockMakeData *data)
generateBiomes();
}
+ // Generate tunnels, caverns and large randomwalk caves
if (flags & MG_CAVES) {
- // Generate tunnels
+ // Generate tunnels first as caverns confuse them
generateCavesNoiseIntersection(stone_surface_max_y);
+
+ // Generate caverns
+ bool near_cavern = false;
+ if (spflags & MGFLAT_CAVERNS)
+ near_cavern = generateCavernsNoise(stone_surface_max_y);
+
// Generate large randomwalk caves
- generateCavesRandomWalk(stone_surface_max_y, large_cave_depth);
+ if (near_cavern)
+ // Disable large randomwalk caves in this mapchunk by setting
+ // 'large cave depth' to world base. Avoids excessive liquid in
+ // large caverns and floating blobs of overgenerated liquid.
+ generateCavesRandomWalk(stone_surface_max_y,
+ -MAX_MAP_GENERATION_LIMIT);
+ else
+ generateCavesRandomWalk(stone_surface_max_y, large_cave_depth);
}
// Generate the registered ores
- m_emerge->oremgr->placeAllOres(this, blockseed, node_min, node_max);
+ if (flags & MG_ORES)
+ m_emerge->oremgr->placeAllOres(this, blockseed, node_min, node_max);
if (flags & MG_DUNGEONS)
generateDungeons(stone_surface_max_y);
diff --git a/src/mapgen/mapgen_flat.h b/src/mapgen/mapgen_flat.h
index 4902a802c..4b46aff27 100644
--- a/src/mapgen/mapgen_flat.h
+++ b/src/mapgen/mapgen_flat.h
@@ -1,7 +1,7 @@
/*
Minetest
-Copyright (C) 2015-2018 paramat
-Copyright (C) 2015-2018 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
+Copyright (C) 2015-2020 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
@@ -25,6 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
/////// Mapgen Flat flags
#define MGFLAT_LAKES 0x01
#define MGFLAT_HILLS 0x02
+#define MGFLAT_CAVERNS 0x04
class BiomeManager;
@@ -33,22 +34,27 @@ extern FlagDesc flagdesc_mapgen_flat[];
struct MapgenFlatParams : public MapgenParams
{
s16 ground_level = 8;
- s16 large_cave_depth = -33;
+ float lake_threshold = -0.45f;
+ float lake_steepness = 48.0f;
+ float hill_threshold = 0.45f;
+ float hill_steepness = 64.0f;
+
+ float cave_width = 0.09f;
u16 small_cave_num_min = 0;
u16 small_cave_num_max = 0;
u16 large_cave_num_min = 0;
u16 large_cave_num_max = 2;
+ s16 large_cave_depth = -33;
float large_cave_flooded = 0.5f;
- float cave_width = 0.09f;
- float lake_threshold = -0.45f;
- float lake_steepness = 48.0f;
- float hill_threshold = 0.45f;
- float hill_steepness = 64.0f;
+ s16 cavern_limit = -256;
+ s16 cavern_taper = 256;
+ float cavern_threshold = 0.7f;
s16 dungeon_ymin = -31000;
s16 dungeon_ymax = 31000;
NoiseParams np_terrain;
NoiseParams np_filler_depth;
+ NoiseParams np_cavern;
NoiseParams np_cave1;
NoiseParams np_cave2;
NoiseParams np_dungeons;
diff --git a/src/mapgen/mapgen_fractal.cpp b/src/mapgen/mapgen_fractal.cpp
index cb55bc288..fabb1b2b1 100644
--- a/src/mapgen/mapgen_fractal.cpp
+++ b/src/mapgen/mapgen_fractal.cpp
@@ -209,12 +209,6 @@ void MapgenFractal::makeChunk(BlockMakeData *data)
// Pre-conditions
assert(data->vmanip);
assert(data->nodedef);
- assert(data->blockpos_requested.X >= data->blockpos_min.X &&
- data->blockpos_requested.Y >= data->blockpos_min.Y &&
- data->blockpos_requested.Z >= data->blockpos_min.Z);
- assert(data->blockpos_requested.X <= data->blockpos_max.X &&
- data->blockpos_requested.Y <= data->blockpos_max.Y &&
- data->blockpos_requested.Z <= data->blockpos_max.Z);
//TimeTaker t("makeChunk");
@@ -250,7 +244,8 @@ void MapgenFractal::makeChunk(BlockMakeData *data)
}
// Generate the registered ores
- m_emerge->oremgr->placeAllOres(this, blockseed, node_min, node_max);
+ if (flags & MG_ORES)
+ m_emerge->oremgr->placeAllOres(this, blockseed, node_min, node_max);
// Generate dungeons
if (flags & MG_DUNGEONS)
diff --git a/src/mapgen/mapgen_singlenode.cpp b/src/mapgen/mapgen_singlenode.cpp
index cade9e7a8..5382423fa 100644
--- a/src/mapgen/mapgen_singlenode.cpp
+++ b/src/mapgen/mapgen_singlenode.cpp
@@ -50,12 +50,6 @@ void MapgenSinglenode::makeChunk(BlockMakeData *data)
// Pre-conditions
assert(data->vmanip);
assert(data->nodedef);
- assert(data->blockpos_requested.X >= data->blockpos_min.X &&
- data->blockpos_requested.Y >= data->blockpos_min.Y &&
- data->blockpos_requested.Z >= data->blockpos_min.Z);
- assert(data->blockpos_requested.X <= data->blockpos_max.X &&
- data->blockpos_requested.Y <= data->blockpos_max.Y &&
- data->blockpos_requested.Z <= data->blockpos_max.Z);
this->generating = true;
this->vm = data->vmanip;
diff --git a/src/mapgen/mapgen_v5.cpp b/src/mapgen/mapgen_v5.cpp
index 124667e5d..87e54755f 100644
--- a/src/mapgen/mapgen_v5.cpp
+++ b/src/mapgen/mapgen_v5.cpp
@@ -201,12 +201,6 @@ void MapgenV5::makeChunk(BlockMakeData *data)
// Pre-conditions
assert(data->vmanip);
assert(data->nodedef);
- assert(data->blockpos_requested.X >= data->blockpos_min.X &&
- data->blockpos_requested.Y >= data->blockpos_min.Y &&
- data->blockpos_requested.Z >= data->blockpos_min.Z);
- assert(data->blockpos_requested.X <= data->blockpos_max.X &&
- data->blockpos_requested.Y <= data->blockpos_max.Y &&
- data->blockpos_requested.Z <= data->blockpos_max.Z);
this->generating = true;
this->vm = data->vmanip;
@@ -257,7 +251,8 @@ void MapgenV5::makeChunk(BlockMakeData *data)
}
// Generate the registered ores
- m_emerge->oremgr->placeAllOres(this, blockseed, node_min, node_max);
+ if (flags & MG_ORES)
+ m_emerge->oremgr->placeAllOres(this, blockseed, node_min, node_max);
// Generate dungeons and desert temples
if (flags & MG_DUNGEONS)
diff --git a/src/mapgen/mapgen_v6.cpp b/src/mapgen/mapgen_v6.cpp
index e9692246c..e04180f96 100644
--- a/src/mapgen/mapgen_v6.cpp
+++ b/src/mapgen/mapgen_v6.cpp
@@ -341,12 +341,6 @@ float MapgenV6::baseTerrainLevelFromMap(int index)
}
-s16 MapgenV6::find_ground_level_from_noise(u64 seed, v2s16 p2d, s16 precision)
-{
- return baseTerrainLevelFromNoise(p2d) + MGV6_AVERAGE_MUD_AMOUNT;
-}
-
-
int MapgenV6::getGroundLevelAtPoint(v2s16 p)
{
return baseTerrainLevelFromNoise(p) + MGV6_AVERAGE_MUD_AMOUNT;
@@ -518,12 +512,6 @@ void MapgenV6::makeChunk(BlockMakeData *data)
// Pre-conditions
assert(data->vmanip);
assert(data->nodedef);
- assert(data->blockpos_requested.X >= data->blockpos_min.X &&
- data->blockpos_requested.Y >= data->blockpos_min.Y &&
- data->blockpos_requested.Z >= data->blockpos_min.Z);
- assert(data->blockpos_requested.X <= data->blockpos_max.X &&
- data->blockpos_requested.Y <= data->blockpos_max.Y &&
- data->blockpos_requested.Z <= data->blockpos_max.Z);
this->generating = true;
this->vm = data->vmanip;
@@ -652,7 +640,8 @@ void MapgenV6::makeChunk(BlockMakeData *data)
m_emerge->decomgr->placeAllDecos(this, blockseed, node_min, node_max);
// Generate the registered ores
- m_emerge->oremgr->placeAllOres(this, blockseed, node_min, node_max);
+ if (flags & MG_ORES)
+ m_emerge->oremgr->placeAllOres(this, blockseed, node_min, node_max);
// Calculate lighting
if (flags & MG_LIGHT)
diff --git a/src/mapgen/mapgen_v6.h b/src/mapgen/mapgen_v6.h
index ff565edec..a6e6da8c6 100644
--- a/src/mapgen/mapgen_v6.h
+++ b/src/mapgen/mapgen_v6.h
@@ -150,7 +150,6 @@ public:
s16 find_stone_level(v2s16 p2d);
bool block_is_underground(u64 seed, v3s16 blockpos);
- s16 find_ground_level_from_noise(u64 seed, v2s16 p2d, s16 precision);
float getHumidity(v2s16 p);
float getTreeAmount(v2s16 p);
diff --git a/src/mapgen/mapgen_v7.cpp b/src/mapgen/mapgen_v7.cpp
index e93dc9140..cc5f5726d 100644
--- a/src/mapgen/mapgen_v7.cpp
+++ b/src/mapgen/mapgen_v7.cpp
@@ -317,12 +317,6 @@ void MapgenV7::makeChunk(BlockMakeData *data)
// Pre-conditions
assert(data->vmanip);
assert(data->nodedef);
- assert(data->blockpos_requested.X >= data->blockpos_min.X &&
- data->blockpos_requested.Y >= data->blockpos_min.Y &&
- data->blockpos_requested.Z >= data->blockpos_min.Z);
- assert(data->blockpos_requested.X <= data->blockpos_max.X &&
- data->blockpos_requested.Y <= data->blockpos_max.Y &&
- data->blockpos_requested.Z <= data->blockpos_max.Z);
//TimeTaker t("makeChunk");
@@ -342,10 +336,6 @@ void MapgenV7::makeChunk(BlockMakeData *data)
// Generate base and mountain terrain
s16 stone_surface_max_y = generateTerrain();
- // Generate rivers
- if (spflags & MGV7_RIDGES)
- generateRidgeTerrain();
-
// Create heightmap
updateHeightmap(node_min, node_max);
@@ -377,7 +367,8 @@ void MapgenV7::makeChunk(BlockMakeData *data)
}
// Generate the registered ores
- m_emerge->oremgr->placeAllOres(this, blockseed, node_min, node_max);
+ if (flags & MG_ORES)
+ m_emerge->oremgr->placeAllOres(this, blockseed, node_min, node_max);
// Generate dungeons
if (flags & MG_DUNGEONS)
@@ -466,6 +457,23 @@ bool MapgenV7::getMountainTerrainFromMap(int idx_xyz, int idx_xz, s16 y)
}
+bool MapgenV7::getRiverChannelFromMap(int idx_xyz, int idx_xz, s16 y)
+{
+ // Maximum width of river channel. Creates the vertical canyon walls
+ float width = 0.2f;
+ float absuwatern = std::fabs(noise_ridge_uwater->result[idx_xz]) * 2.0f;
+ if (absuwatern > width)
+ return false;
+
+ float altitude = y - water_level;
+ float height_mod = (altitude + 17.0f) / 2.5f;
+ float width_mod = width - absuwatern;
+ float nridge = noise_ridge->result[idx_xyz] * std::fmax(altitude, 0.0f) / 7.0f;
+
+ return nridge + width_mod * height_mod >= 0.6f;
+}
+
+
bool MapgenV7::getFloatlandTerrainFromMap(int idx_xyz, float float_offset)
{
return noise_floatland->result[idx_xyz] + floatland_density - float_offset >= 0.0f;
@@ -520,6 +528,15 @@ int MapgenV7::generateTerrain()
}
}
+ // 'Generate rivers in this mapchunk' bool for
+ // simplification of condition checks in y-loop.
+ bool gen_rivers = (spflags & MGV7_RIDGES) && node_max.Y >= water_level - 16 &&
+ !gen_floatlands;
+ if (gen_rivers) {
+ noise_ridge->perlinMap3D(node_min.X, node_min.Y - 1, node_min.Z);
+ noise_ridge_uwater->perlinMap2D(node_min.X, node_min.Z);
+ }
+
//// Place nodes
const v3s16 &em = vm->m_area.getExtent();
s16 stone_surface_max_y = -MAX_MAP_GENERATION_LIMIT;
@@ -543,10 +560,13 @@ int MapgenV7::generateTerrain()
if (vm->m_data[vi].getContent() != CONTENT_IGNORE)
continue;
- if (y <= surface_y) {
+ bool is_river_channel = gen_rivers &&
+ getRiverChannelFromMap(index3d, index2d, y);
+ if (y <= surface_y && !is_river_channel) {
vm->m_data[vi] = n_stone; // Base terrain
} else if ((spflags & MGV7_MOUNTAINS) &&
- getMountainTerrainFromMap(index3d, index2d, y)) {
+ getMountainTerrainFromMap(index3d, index2d, y) &&
+ !is_river_channel) {
vm->m_data[vi] = n_stone; // Mountain terrain
if (y > stone_surface_max_y)
stone_surface_max_y = y;
@@ -568,45 +588,3 @@ int MapgenV7::generateTerrain()
return stone_surface_max_y;
}
-
-
-void MapgenV7::generateRidgeTerrain()
-{
- if (node_max.Y < water_level - 16 ||
- (node_max.Y >= floatland_ymin && node_min.Y <= floatland_ymax))
- return;
-
- noise_ridge->perlinMap3D(node_min.X, node_min.Y - 1, node_min.Z);
- noise_ridge_uwater->perlinMap2D(node_min.X, node_min.Z);
-
- MapNode n_water(c_water_source);
- MapNode n_air(CONTENT_AIR);
- u32 index3d = 0;
- float width = 0.2f;
-
- for (s16 z = node_min.Z; z <= node_max.Z; z++)
- for (s16 y = node_min.Y - 1; y <= node_max.Y + 1; y++) {
- u32 vi = vm->m_area.index(node_min.X, y, z);
- for (s16 x = node_min.X; x <= node_max.X; x++, index3d++, vi++) {
- u32 index2d = (z - node_min.Z) * csize.X + (x - node_min.X);
- float uwatern = noise_ridge_uwater->result[index2d] * 2.0f;
- if (std::fabs(uwatern) > width)
- continue;
- // Optimises, but also avoids removing nodes placed by mods in
- // 'on-generated', when generating outside mapchunk.
- content_t c = vm->m_data[vi].getContent();
- if (c != c_stone)
- continue;
-
- float altitude = y - water_level;
- float height_mod = (altitude + 17.0f) / 2.5f;
- float width_mod = width - std::fabs(uwatern);
- float nridge = noise_ridge->result[index3d] *
- std::fmax(altitude, 0.0f) / 7.0f;
- if (nridge + width_mod * height_mod < 0.6f)
- continue;
-
- vm->m_data[vi] = (y > water_level) ? n_air : n_water;
- }
- }
-}
diff --git a/src/mapgen/mapgen_v7.h b/src/mapgen/mapgen_v7.h
index 4020cd935..5db10a304 100644
--- a/src/mapgen/mapgen_v7.h
+++ b/src/mapgen/mapgen_v7.h
@@ -94,10 +94,10 @@ public:
float baseTerrainLevelFromMap(int index);
bool getMountainTerrainAtPoint(s16 x, s16 y, s16 z);
bool getMountainTerrainFromMap(int idx_xyz, int idx_xz, s16 y);
+ bool getRiverChannelFromMap(int idx_xyz, int idx_xz, s16 y);
bool getFloatlandTerrainFromMap(int idx_xyz, float float_offset);
int generateTerrain();
- void generateRidgeTerrain();
private:
s16 mount_zero_level;
diff --git a/src/mapgen/mapgen_valleys.cpp b/src/mapgen/mapgen_valleys.cpp
index efcc8ee85..c4234857e 100644
--- a/src/mapgen/mapgen_valleys.cpp
+++ b/src/mapgen/mapgen_valleys.cpp
@@ -210,12 +210,6 @@ void MapgenValleys::makeChunk(BlockMakeData *data)
// Pre-conditions
assert(data->vmanip);
assert(data->nodedef);
- assert(data->blockpos_requested.X >= data->blockpos_min.X &&
- data->blockpos_requested.Y >= data->blockpos_min.Y &&
- data->blockpos_requested.Z >= data->blockpos_min.Z);
- assert(data->blockpos_requested.X <= data->blockpos_max.X &&
- data->blockpos_requested.Y <= data->blockpos_max.Y &&
- data->blockpos_requested.Z <= data->blockpos_max.Z);
//TimeTaker t("makeChunk");
@@ -268,7 +262,8 @@ void MapgenValleys::makeChunk(BlockMakeData *data)
}
// Generate the registered ores
- m_emerge->oremgr->placeAllOres(this, blockseed, node_min, node_max);
+ if (flags & MG_ORES)
+ m_emerge->oremgr->placeAllOres(this, blockseed, node_min, node_max);
// Dungeon creation
if (flags & MG_DUNGEONS)
diff --git a/src/mapgen/mg_ore.cpp b/src/mapgen/mg_ore.cpp
index b50ed6a32..5814f433a 100644
--- a/src/mapgen/mg_ore.cpp
+++ b/src/mapgen/mg_ore.cpp
@@ -498,7 +498,11 @@ void OreVein::generate(MMVManip *vm, int mapseed, u32 blockseed,
}
// randval ranges from -1..1
- float randval = (float)pr.next() / (pr.RANDOM_RANGE / 2) - 1.f;
+ /*
+ Note: can generate values slightly larger than 1
+ but this can't be changed as mapgen must be deterministic accross versions.
+ */
+ float randval = (float)pr.next() / float(pr.RANDOM_RANGE / 2) - 1.f;
float noiseval = contour(noise->result[index]);
float noiseval2 = contour(noise2->result[index]);
if (noiseval * noiseval2 + randval * random_factor < nthresh)
diff --git a/src/mapgen/mg_schematic.cpp b/src/mapgen/mg_schematic.cpp
index ba102d997..dfd414709 100644
--- a/src/mapgen/mg_schematic.cpp
+++ b/src/mapgen/mg_schematic.cpp
@@ -314,7 +314,7 @@ bool Schematic::deserializeFromMts(std::istream *is,
//// Read node names
u16 nidmapcount = readU16(ss);
for (int i = 0; i != nidmapcount; i++) {
- std::string name = deSerializeString(ss);
+ std::string name = deSerializeString16(ss);
// Instances of "ignore" from v1 are converted to air (and instances
// are fixed to have MTSCHEM_PROB_NEVER later on).
@@ -372,7 +372,7 @@ bool Schematic::serializeToMts(std::ostream *os,
writeU16(ss, names.size()); // name count
for (size_t i = 0; i != names.size(); i++)
- ss << serializeString(names[i]); // node names
+ ss << serializeString16(names[i]); // node names
// compressed bulk node data
MapNode::serializeBulk(ss, SER_FMT_VER_HIGHEST_WRITE,
diff --git a/src/mapgen/treegen.cpp b/src/mapgen/treegen.cpp
index e7e30c880..e633d800a 100644
--- a/src/mapgen/treegen.cpp
+++ b/src/mapgen/treegen.cpp
@@ -527,19 +527,6 @@ treegen::error make_ltree(MMVManip &vmanip, v3s16 p0,
}
-void tree_node_placement(MMVManip &vmanip, v3f p0, MapNode node)
-{
- v3s16 p1 = v3s16(myround(p0.X), myround(p0.Y), myround(p0.Z));
- if (!vmanip.m_area.contains(p1))
- return;
- u32 vi = vmanip.m_area.index(p1);
- if (vmanip.m_data[vi].getContent() != CONTENT_AIR
- && vmanip.m_data[vi].getContent() != CONTENT_IGNORE)
- return;
- vmanip.m_data[vmanip.m_area.index(p1)] = node;
-}
-
-
void tree_trunk_placement(MMVManip &vmanip, v3f p0, TreeDef &tree_definition)
{
v3s16 p1 = v3s16(myround(p0.X), myround(p0.Y), myround(p0.Z));
diff --git a/src/mapgen/treegen.h b/src/mapgen/treegen.h
index 447baabb3..59a418824 100644
--- a/src/mapgen/treegen.h
+++ b/src/mapgen/treegen.h
@@ -76,8 +76,6 @@ namespace treegen {
const NodeDefManager *ndef, const TreeDef &tree_definition);
// L-System tree gen helper functions
- void tree_node_placement(MMVManip &vmanip, v3f p0,
- MapNode node);
void tree_trunk_placement(MMVManip &vmanip, v3f p0,
TreeDef &tree_definition);
void tree_leaves_placement(MMVManip &vmanip, v3f p0,
diff --git a/src/mapsector.h b/src/mapsector.h
index dede364f6..ffd4cdd1d 100644
--- a/src/mapsector.h
+++ b/src/mapsector.h
@@ -62,6 +62,7 @@ public:
bool empty() const { return m_blocks.empty(); }
+ int size() const { return m_blocks.size(); }
protected:
// The pile of MapBlocks
diff --git a/src/event.h b/src/mtevent.h
index 149f7eecd..149f7eecd 100644
--- a/src/event.h
+++ b/src/mtevent.h
diff --git a/src/nameidmapping.cpp b/src/nameidmapping.cpp
index bd5bb1fc8..05cfae069 100644
--- a/src/nameidmapping.cpp
+++ b/src/nameidmapping.cpp
@@ -27,7 +27,7 @@ void NameIdMapping::serialize(std::ostream &os) const
writeU16(os, m_id_to_name.size());
for (const auto &i : m_id_to_name) {
writeU16(os, i.first);
- os << serializeString(i.second);
+ os << serializeString16(i.second);
}
}
@@ -41,7 +41,7 @@ void NameIdMapping::deSerialize(std::istream &is)
m_name_to_id.clear();
for (u32 i = 0; i < count; i++) {
u16 id = readU16(is);
- std::string name = deSerializeString(is);
+ std::string name = deSerializeString16(is);
m_id_to_name[id] = name;
m_name_to_id[name] = id;
}
diff --git a/src/network/CMakeLists.txt b/src/network/CMakeLists.txt
index c6995ab22..d2e2f52e9 100644
--- a/src/network/CMakeLists.txt
+++ b/src/network/CMakeLists.txt
@@ -16,8 +16,3 @@ if (BUILD_CLIENT)
PARENT_SCOPE
)
endif()
-
-# Haiku networking support
-if(HAIKU)
- set(PLATFORM_LIBS -lnetwork ${PLATFORM_LIBS})
-endif()
diff --git a/src/network/clientopcodes.cpp b/src/network/clientopcodes.cpp
index f812a08a1..55cfdd4dc 100644
--- a/src/network/clientopcodes.cpp
+++ b/src/network/clientopcodes.cpp
@@ -122,6 +122,7 @@ const ToClientCommandHandler toClientCommandTable[TOCLIENT_NUM_MSG_TYPES] =
null_command_handler,
{ "TOCLIENT_SRP_BYTES_S_B", TOCLIENT_STATE_NOT_CONNECTED, &Client::handleCommand_SrpBytesSandB }, // 0x60
{ "TOCLIENT_FORMSPEC_PREPEND", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_FormspecPrepend }, // 0x61,
+ { "TOCLIENT_MINIMAP_MODES", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_MinimapModes }, // 0x62,
};
const static ServerCommandFactory null_command_factory = { "TOSERVER_NULL", 0, false };
diff --git a/src/network/clientpackethandler.cpp b/src/network/clientpackethandler.cpp
index a3f1e668d..c39586f2c 100644
--- a/src/network/clientpackethandler.cpp
+++ b/src/network/clientpackethandler.cpp
@@ -504,7 +504,7 @@ void Client::handleCommand_ActiveObjectMessages(NetworkPacket* pkt)
if (!is.good())
break;
- std::string message = deSerializeString(is);
+ std::string message = deSerializeString16(is);
// Pass on to the environment
m_env.processActiveObjectMessage(id, message);
@@ -1017,7 +1017,7 @@ void Client::handleCommand_AddParticleSpawner(NetworkPacket* pkt)
p.minsize = readF32(is);
p.maxsize = readF32(is);
p.collisiondetection = readU8(is);
- p.texture = deSerializeLongString(is);
+ p.texture = deSerializeString32(is);
server_id = readU32(is);
@@ -1185,26 +1185,32 @@ void Client::handleCommand_HudSetFlags(NetworkPacket* pkt)
player->hud_flags |= flags;
if (g_settings->getBool("hud_flags_bypass"))
- player->hud_flags = HUD_FLAG_HOTBAR_VISIBLE | HUD_FLAG_HEALTHBAR_VISIBLE |
+ player->hud_flags = HUD_FLAG_HOTBAR_VISIBLE | HUD_FLAG_HEALTHBAR_VISIBLE |
HUD_FLAG_CROSSHAIR_VISIBLE | HUD_FLAG_WIELDITEM_VISIBLE |
HUD_FLAG_BREATHBAR_VISIBLE | HUD_FLAG_MINIMAP_VISIBLE |
HUD_FLAG_MINIMAP_RADAR_VISIBLE;
-
m_minimap_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE);
bool m_minimap_radar_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE);
+ // Not so satisying code to keep compatibility with old fixed mode system
+ // -->
+
// Hide minimap if it has been disabled by the server
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);
-
- // Switch to surface mode if radar disabled by server
- if (m_minimap && m_minimap_radar_disabled_by_server && was_minimap_radar_visible)
- m_minimap->setMinimapMode(MINIMAP_MODE_SURFACEx1);
-
-
+ m_minimap->setModeIndex(0);
+
+ // If radar has been disabled, try to find a non radar mode or fall back to 0
+ if (m_minimap && m_minimap_radar_disabled_by_server
+ && was_minimap_radar_visible) {
+ while (m_minimap->getModeIndex() > 0 &&
+ m_minimap->getModeDef().type == MINIMAP_TYPE_RADAR)
+ m_minimap->nextMode();
+ }
+ // <--
+ // End of 'not so satifying code'
}
void Client::handleCommand_HudSetParam(NetworkPacket* pkt)
@@ -1239,11 +1245,11 @@ void Client::handleCommand_HudSetSky(NetworkPacket* pkt)
SkyboxParams skybox;
skybox.bgcolor = video::SColor(readARGB8(is));
- skybox.type = std::string(deSerializeString(is));
+ skybox.type = std::string(deSerializeString16(is));
u16 count = readU16(is);
for (size_t i = 0; i < count; i++)
- skybox.textures.emplace_back(deSerializeString(is));
+ skybox.textures.emplace_back(deSerializeString16(is));
skybox.clouds = true;
try {
@@ -1645,3 +1651,30 @@ void Client::handleCommand_ModChannelSignal(NetworkPacket *pkt)
if (valid_signal)
m_script->on_modchannel_signal(channel, signal);
}
+
+void Client::handleCommand_MinimapModes(NetworkPacket *pkt)
+{
+ u16 count; // modes
+ u16 mode; // wanted current mode index after change
+
+ *pkt >> count >> mode;
+
+ if (m_minimap)
+ m_minimap->clearModes();
+
+ for (size_t index = 0; index < count; index++) {
+ u16 type;
+ std::string label;
+ u16 size;
+ std::string texture;
+ u16 scale;
+
+ *pkt >> type >> label >> size >> texture >> scale;
+
+ if (m_minimap)
+ m_minimap->addMode(MinimapType(type), size, label, texture, scale);
+ }
+
+ if (m_minimap)
+ m_minimap->setModeIndex(mode);
+}
diff --git a/src/network/connection.cpp b/src/network/connection.cpp
index 3692e45a9..0ba8c36b2 100644
--- a/src/network/connection.cpp
+++ b/src/network/connection.cpp
@@ -1269,7 +1269,8 @@ bool Connection::deletePeer(session_t peer_id, bool timeout)
return false;
peer = m_peers[peer_id];
m_peers.erase(peer_id);
- m_peer_ids.remove(peer_id);
+ auto it = std::find(m_peer_ids.begin(), m_peer_ids.end(), peer_id);
+ m_peer_ids.erase(it);
}
Address peer_address;
@@ -1565,7 +1566,7 @@ void Connection::sendAck(session_t peer_id, u8 channelnum, u16 seqnum)
UDPPeer* Connection::createServerPeer(Address& address)
{
- if (getPeerNoEx(PEER_ID_SERVER) != 0)
+ if (ConnectedToServer())
{
throw ConnectionException("Already connected to a server");
}
diff --git a/src/network/connection.h b/src/network/connection.h
index 47b0805ce..24cd4fe4a 100644
--- a/src/network/connection.h
+++ b/src/network/connection.h
@@ -30,7 +30,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "networkprotocol.h"
#include <iostream>
#include <fstream>
-#include <list>
+#include <vector>
#include <map>
class NetworkPacket;
@@ -795,7 +795,7 @@ protected:
void PrintInfo(std::ostream &out);
- std::list<session_t> getPeerIDs()
+ std::vector<session_t> getPeerIDs()
{
MutexAutoLock peerlock(m_peers_mutex);
return m_peer_ids;
@@ -809,6 +809,11 @@ protected:
void putEvent(ConnectionEvent &e);
void TriggerSend();
+
+ bool ConnectedToServer()
+ {
+ return getPeerNoEx(PEER_ID_SERVER) != nullptr;
+ }
private:
MutexedQueue<ConnectionEvent> m_event_queue;
@@ -816,7 +821,7 @@ private:
u32 m_protocol_id;
std::map<session_t, Peer *> m_peers;
- std::list<session_t> m_peer_ids;
+ std::vector<session_t> m_peer_ids;
std::mutex m_peers_mutex;
std::unique_ptr<ConnectionSendThread> m_sendThread;
diff --git a/src/network/connectionthreads.cpp b/src/network/connectionthreads.cpp
index 9a6617a1c..7b62bc792 100644
--- a/src/network/connectionthreads.cpp
+++ b/src/network/connectionthreads.cpp
@@ -144,7 +144,7 @@ void ConnectionSendThread::Trigger()
bool ConnectionSendThread::packetsQueued()
{
- std::list<session_t> peerIds = m_connection->getPeerIDs();
+ std::vector<session_t> peerIds = m_connection->getPeerIDs();
if (!m_outgoing_queue.empty() && !peerIds.empty())
return true;
@@ -171,8 +171,8 @@ bool ConnectionSendThread::packetsQueued()
void ConnectionSendThread::runTimeouts(float dtime)
{
- std::list<session_t> timeouted_peers;
- std::list<session_t> peerIds = m_connection->getPeerIDs();
+ std::vector<session_t> timeouted_peers;
+ std::vector<session_t> peerIds = m_connection->getPeerIDs();
for (session_t &peerId : peerIds) {
PeerHelper peer = m_connection->getPeerNoEx(peerId);
@@ -548,7 +548,7 @@ void ConnectionSendThread::disconnect()
// Send to all
- std::list<session_t> peerids = m_connection->getPeerIDs();
+ std::vector<session_t> peerids = m_connection->getPeerIDs();
for (session_t peerid : peerids) {
sendAsPacket(peerid, 0, data, false);
@@ -620,7 +620,7 @@ void ConnectionSendThread::sendReliable(ConnectionCommand &c)
void ConnectionSendThread::sendToAll(u8 channelnum, const SharedBuffer<u8> &data)
{
- std::list<session_t> peerids = m_connection->getPeerIDs();
+ std::vector<session_t> peerids = m_connection->getPeerIDs();
for (session_t peerid : peerids) {
send(peerid, channelnum, data);
@@ -629,7 +629,7 @@ void ConnectionSendThread::sendToAll(u8 channelnum, const SharedBuffer<u8> &data
void ConnectionSendThread::sendToAllReliable(ConnectionCommand &c)
{
- std::list<session_t> peerids = m_connection->getPeerIDs();
+ std::vector<session_t> peerids = m_connection->getPeerIDs();
for (session_t peerid : peerids) {
PeerHelper peer = m_connection->getPeerNoEx(peerid);
@@ -643,8 +643,8 @@ void ConnectionSendThread::sendToAllReliable(ConnectionCommand &c)
void ConnectionSendThread::sendPackets(float dtime)
{
- std::list<session_t> peerIds = m_connection->getPeerIDs();
- std::list<session_t> pendingDisconnect;
+ std::vector<session_t> peerIds = m_connection->getPeerIDs();
+ std::vector<session_t> pendingDisconnect;
std::map<session_t, bool> pending_unreliable;
const unsigned int peer_packet_quota = m_iteration_packets_avaialble
@@ -843,13 +843,11 @@ void *ConnectionReceiveThread::run()
if (debug_print_timer > 20.0) {
debug_print_timer -= 20.0;
- std::list<session_t> peerids = m_connection->getPeerIDs();
+ std::vector<session_t> peerids = m_connection->getPeerIDs();
- for (std::list<session_t>::iterator i = peerids.begin();
- i != peerids.end();
- i++)
+ for (auto id : peerids)
{
- PeerHelper peer = m_connection->getPeerNoEx(*i);
+ PeerHelper peer = m_connection->getPeerNoEx(id);
if (!peer)
continue;
@@ -958,8 +956,11 @@ void ConnectionReceiveThread::receive(SharedBuffer<u8> &packetdata,
// command was sent reliably.
}
- /* The peer was not found in our lists. Add it. */
if (peer_id == PEER_ID_INEXISTENT) {
+ /* Ignore it if we are a client */
+ if (m_connection->ConnectedToServer())
+ return;
+ /* The peer was not found in our lists. Add it. */
peer_id = m_connection->createPeer(sender, MTP_MINETEST_RELIABLE_UDP, 0);
}
@@ -1039,7 +1040,7 @@ void ConnectionReceiveThread::receive(SharedBuffer<u8> &packetdata,
bool ConnectionReceiveThread::getFromBuffers(session_t &peer_id, SharedBuffer<u8> &dst)
{
- std::list<session_t> peerids = m_connection->getPeerIDs();
+ std::vector<session_t> peerids = m_connection->getPeerIDs();
for (session_t peerid : peerids) {
PeerHelper peer = m_connection->getPeerNoEx(peerid);
diff --git a/src/network/networkpacket.cpp b/src/network/networkpacket.cpp
index 4d531b611..6d0abb12c 100644
--- a/src/network/networkpacket.cpp
+++ b/src/network/networkpacket.cpp
@@ -223,13 +223,6 @@ NetworkPacket& NetworkPacket::operator>>(char& dst)
return *this;
}
-char NetworkPacket::getChar(u32 offset)
-{
- checkReadOffset(offset, 1);
-
- return readU8(&m_data[offset]);
-}
-
NetworkPacket& NetworkPacket::operator<<(char src)
{
checkDataSize(1);
diff --git a/src/network/networkpacket.h b/src/network/networkpacket.h
index fc8617651..e77bfb744 100644
--- a/src/network/networkpacket.h
+++ b/src/network/networkpacket.h
@@ -64,7 +64,6 @@ public:
std::string readLongString();
- char getChar(u32 offset);
NetworkPacket &operator>>(char &dst);
NetworkPacket &operator<<(char src);
diff --git a/src/network/networkprotocol.h b/src/network/networkprotocol.h
index e57a7a794..98d143c89 100644
--- a/src/network/networkprotocol.h
+++ b/src/network/networkprotocol.h
@@ -204,6 +204,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
PROTOCOL VERSION 39:
Updated set_sky packet
Adds new sun, moon and stars packets
+ Minimap modes
*/
#define LATEST_PROTOCOL_VERSION 40
@@ -764,6 +765,17 @@ enum ToClientCommand
u8[len] formspec
*/
+ TOCLIENT_MINIMAP_MODES = 0x62,
+ /*
+ u16 count // modes
+ u16 mode // wanted current mode index after change
+ for each mode
+ u16 type
+ std::string label
+ u16 size
+ std::string extra
+ */
+
TOCLIENT_NUM_MSG_TYPES = 0x63,
};
@@ -1034,7 +1046,7 @@ const static std::string accessDeniedStrings[SERVER_ACCESSDENIED_MAX] = {
"This server has experienced an internal error. You will now be disconnected."
};
-enum PlayerListModifer: u8
+enum PlayerListModifer : u8
{
PLAYER_LIST_INIT,
PLAYER_LIST_ADD,
diff --git a/src/network/serveropcodes.cpp b/src/network/serveropcodes.cpp
index 2fc3197c2..aea5d7174 100644
--- a/src/network/serveropcodes.cpp
+++ b/src/network/serveropcodes.cpp
@@ -221,4 +221,5 @@ const ClientCommandFactory clientCommandFactoryTable[TOCLIENT_NUM_MSG_TYPES] =
null_command_factory, // 0x5f
{ "TOSERVER_SRP_BYTES_S_B", 0, true }, // 0x60
{ "TOCLIENT_FORMSPEC_PREPEND", 0, true }, // 0x61
+ { "TOCLIENT_MINIMAP_MODES", 0, true }, // 0x62
};
diff --git a/src/network/serverpackethandler.cpp b/src/network/serverpackethandler.cpp
index b3008bb50..3db4eb286 100644
--- a/src/network/serverpackethandler.cpp
+++ b/src/network/serverpackethandler.cpp
@@ -491,17 +491,18 @@ void Server::process_PlayerPos(RemotePlayer *player, PlayerSAO *playersao,
playersao->setPlayerYaw(yaw);
playersao->setFov(fov);
playersao->setWantedRange(wanted_range);
+
player->keyPressed = keyPressed;
- player->control.up = (keyPressed & 1);
- player->control.down = (keyPressed & 2);
- player->control.left = (keyPressed & 4);
- player->control.right = (keyPressed & 8);
- player->control.jump = (keyPressed & 16);
- player->control.aux1 = (keyPressed & 32);
- player->control.sneak = (keyPressed & 64);
- player->control.LMB = (keyPressed & 128);
- player->control.RMB = (keyPressed & 256);
- player->control.zoom = (keyPressed & 512);
+ player->control.up = (keyPressed & (0x1 << 0));
+ player->control.down = (keyPressed & (0x1 << 1));
+ player->control.left = (keyPressed & (0x1 << 2));
+ player->control.right = (keyPressed & (0x1 << 3));
+ player->control.jump = (keyPressed & (0x1 << 4));
+ player->control.aux1 = (keyPressed & (0x1 << 5));
+ player->control.sneak = (keyPressed & (0x1 << 6));
+ player->control.dig = (keyPressed & (0x1 << 7));
+ player->control.place = (keyPressed & (0x1 << 8));
+ player->control.zoom = (keyPressed & (0x1 << 9));
if (playersao->checkMovementCheat()) {
// Call callbacks
@@ -599,7 +600,7 @@ void Server::handleCommand_InventoryAction(NetworkPacket* pkt)
<< std::endl;
std::istringstream is(datastring, std::ios_base::binary);
// Create an action
- InventoryAction *a = InventoryAction::deSerialize(is);
+ std::unique_ptr<InventoryAction> a(InventoryAction::deSerialize(is));
if (!a) {
infostream << "TOSERVER_INVENTORY_ACTION: "
<< "InventoryAction::deSerialize() returned NULL"
@@ -616,11 +617,30 @@ void Server::handleCommand_InventoryAction(NetworkPacket* pkt)
where the client made a bad prediction.
*/
+ const bool player_has_interact = checkPriv(player->getName(), "interact");
+
+ auto check_inv_access = [player, player_has_interact] (
+ const InventoryLocation &loc) -> bool {
+ if (loc.type == InventoryLocation::CURRENT_PLAYER)
+ return false; // Only used internally on the client, never sent
+ if (loc.type == InventoryLocation::PLAYER) {
+ // Allow access to own inventory in all cases
+ return loc.name == player->getName();
+ }
+
+ if (!player_has_interact) {
+ infostream << "Cannot modify foreign inventory: "
+ << "No interact privilege" << std::endl;
+ return false;
+ }
+ return true;
+ };
+
/*
Handle restrictions and special cases of the move action
*/
if (a->getType() == IAction::Move) {
- IMoveAction *ma = (IMoveAction*)a;
+ IMoveAction *ma = (IMoveAction*)a.get();
ma->from_inv.applyCurrentPlayer(player->getName());
ma->to_inv.applyCurrentPlayer(player->getName());
@@ -629,15 +649,11 @@ void Server::handleCommand_InventoryAction(NetworkPacket* pkt)
if (ma->from_inv != ma->to_inv)
m_inventory_mgr->setInventoryModified(ma->to_inv);
- bool from_inv_is_current_player =
- (ma->from_inv.type == InventoryLocation::PLAYER) &&
- (ma->from_inv.name == player->getName());
-
- bool to_inv_is_current_player =
- (ma->to_inv.type == InventoryLocation::PLAYER) &&
- (ma->to_inv.name == player->getName());
+ if (!check_inv_access(ma->from_inv) ||
+ !check_inv_access(ma->to_inv))
+ return;
- InventoryLocation *remote = from_inv_is_current_player ?
+ InventoryLocation *remote = ma->from_inv.type == InventoryLocation::PLAYER ?
&ma->to_inv : &ma->from_inv;
// Check for out-of-range interaction
@@ -657,7 +673,6 @@ void Server::handleCommand_InventoryAction(NetworkPacket* pkt)
<< (ma->from_inv.dump()) << ":" << ma->from_list
<< " to " << (ma->to_inv.dump()) << ":" << ma->to_list
<< " because src is " << ma->from_list << std::endl;
- delete a;
return;
}
@@ -669,18 +684,6 @@ void Server::handleCommand_InventoryAction(NetworkPacket* pkt)
<< (ma->from_inv.dump()) << ":" << ma->from_list
<< " to " << (ma->to_inv.dump()) << ":" << ma->to_list
<< " because dst is " << ma->to_list << std::endl;
- delete a;
- return;
- }
-
- // Disallow moving items in elsewhere than player's inventory
- // if not allowed to interact
- if (!checkPriv(player->getName(), "interact") &&
- (!from_inv_is_current_player ||
- !to_inv_is_current_player)) {
- infostream << "Cannot move outside of player's inventory: "
- << "No interact privilege" << std::endl;
- delete a;
return;
}
}
@@ -688,7 +691,7 @@ void Server::handleCommand_InventoryAction(NetworkPacket* pkt)
Handle restrictions and special cases of the drop action
*/
else if (a->getType() == IAction::Drop) {
- IDropAction *da = (IDropAction*)a;
+ IDropAction *da = (IDropAction*)a.get();
da->from_inv.applyCurrentPlayer(player->getName());
@@ -701,22 +704,18 @@ void Server::handleCommand_InventoryAction(NetworkPacket* pkt)
infostream << "Ignoring IDropAction from "
<< (da->from_inv.dump()) << ":" << da->from_list
<< " because src is " << da->from_list << std::endl;
- delete a;
return;
}
// Disallow dropping items if not allowed to interact
- if (!checkPriv(player->getName(), "interact")) {
- delete a;
+ if (!player_has_interact || !check_inv_access(da->from_inv))
return;
- }
// Disallow dropping items if dead
if (playersao->isDead()) {
infostream << "Ignoring IDropAction from "
<< (da->from_inv.dump()) << ":" << da->from_list
<< " because player is dead." << std::endl;
- delete a;
return;
}
}
@@ -724,29 +723,28 @@ void Server::handleCommand_InventoryAction(NetworkPacket* pkt)
Handle restrictions and special cases of the craft action
*/
else if (a->getType() == IAction::Craft) {
- ICraftAction *ca = (ICraftAction*)a;
+ ICraftAction *ca = (ICraftAction*)a.get();
ca->craft_inv.applyCurrentPlayer(player->getName());
m_inventory_mgr->setInventoryModified(ca->craft_inv);
- //bool craft_inv_is_current_player =
- // (ca->craft_inv.type == InventoryLocation::PLAYER) &&
- // (ca->craft_inv.name == player->getName());
-
// Disallow crafting if not allowed to interact
- if (!checkPriv(player->getName(), "interact")) {
+ if (!player_has_interact) {
infostream << "Cannot craft: "
<< "No interact privilege" << std::endl;
- delete a;
return;
}
+
+ if (!check_inv_access(ca->craft_inv))
+ return;
+ } else {
+ // Unknown action. Ignored.
+ return;
}
// Do the action
a->apply(m_inventory_mgr.get(), playersao, this);
- // Eat the action
- delete a;
}
void Server::handleCommand_ChatMessage(NetworkPacket* pkt)
@@ -862,6 +860,15 @@ void Server::handleCommand_PlayerItem(NetworkPacket* pkt)
*pkt >> item;
+ if (item >= player->getHotbarItemcount()) {
+ actionstream << "Player: " << player->getName()
+ << " tried to access item=" << item
+ << " out of hotbar_itemcount="
+ << player->getHotbarItemcount()
+ << "; ignoring." << std::endl;
+ return;
+ }
+
playersao->getPlayer()->setWieldIndex(item);
}
@@ -977,11 +984,17 @@ void Server::handleCommand_Interact(NetworkPacket *pkt)
v3f player_pos = playersao->getLastGoodPosition();
// Update wielded item
- playersao->getPlayer()->setWieldIndex(item_i);
- // Get pointed to node (undefined if not POINTEDTYPE_NODE)
- v3s16 p_under = pointed.node_undersurface;
- v3s16 p_above = pointed.node_abovesurface;
+ if (item_i >= player->getHotbarItemcount()) {
+ actionstream << "Player: " << player->getName()
+ << " tried to access item=" << item_i
+ << " out of hotbar_itemcount="
+ << player->getHotbarItemcount()
+ << "; ignoring." << std::endl;
+ return;
+ }
+
+ playersao->getPlayer()->setWieldIndex(item_i);
// Get pointed to object (NULL if not POINTEDTYPE_OBJECT)
ServerActiveObject *pointed_object = NULL;
@@ -995,17 +1008,6 @@ void Server::handleCommand_Interact(NetworkPacket *pkt)
}
- v3f pointed_pos_under = player_pos;
- v3f pointed_pos_above = player_pos;
- if (pointed.type == POINTEDTHING_NODE) {
- pointed_pos_under = intToFloat(p_under, BS);
- pointed_pos_above = intToFloat(p_above, BS);
- }
- else if (pointed.type == POINTEDTHING_OBJECT) {
- pointed_pos_under = pointed_object->getBasePosition();
- pointed_pos_above = pointed_pos_under;
- }
-
/*
Make sure the player is allowed to do it
*/
@@ -1013,16 +1015,19 @@ void Server::handleCommand_Interact(NetworkPacket *pkt)
actionstream << player->getName() << " attempted to interact with " <<
pointed.dump() << " without 'interact' privilege" << std::endl;
+ if (pointed.type != POINTEDTHING_NODE)
+ return;
+
// Re-send block to revert change on client-side
RemoteClient *client = getClient(peer_id);
// Digging completed -> under
if (action == INTERACT_DIGGING_COMPLETED) {
- v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
+ v3s16 blockpos = getNodeBlockPos(pointed.node_undersurface);
client->SetBlockNotSent(blockpos);
}
// Placement -> above
else if (action == INTERACT_PLACE) {
- v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_above, BS));
+ v3s16 blockpos = getNodeBlockPos(pointed.node_abovesurface);
client->SetBlockNotSent(blockpos);
}
return;
@@ -1030,7 +1035,6 @@ void Server::handleCommand_Interact(NetworkPacket *pkt)
/*
Check that target is reasonably close
- (only when digging or placing things)
*/
static thread_local const bool enable_anticheat =
!g_settings->getBool("disable_anticheat");
@@ -1038,13 +1042,21 @@ void Server::handleCommand_Interact(NetworkPacket *pkt)
if ((action == INTERACT_START_DIGGING || action == INTERACT_DIGGING_COMPLETED ||
action == INTERACT_PLACE || action == INTERACT_USE) &&
enable_anticheat && !isSingleplayer()) {
- float d = playersao->getEyePosition().getDistanceFrom(pointed_pos_under);
+ v3f target_pos = player_pos;
+ if (pointed.type == POINTEDTHING_NODE) {
+ target_pos = intToFloat(pointed.node_undersurface, BS);
+ } else if (pointed.type == POINTEDTHING_OBJECT) {
+ target_pos = pointed_object->getBasePosition();
+ }
+ float d = playersao->getEyePosition().getDistanceFrom(target_pos);
if (!checkInteractDistance(player, d, pointed.dump())) {
- // Re-send block to revert change on client-side
- RemoteClient *client = getClient(peer_id);
- v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
- client->SetBlockNotSent(blockpos);
+ if (pointed.type == POINTEDTHING_NODE) {
+ // Re-send block to revert change on client-side
+ RemoteClient *client = getClient(peer_id);
+ v3s16 blockpos = getNodeBlockPos(pointed.node_undersurface);
+ client->SetBlockNotSent(blockpos);
+ }
return;
}
}
@@ -1055,20 +1067,20 @@ void Server::handleCommand_Interact(NetworkPacket *pkt)
RollbackScopeActor rollback_scope(m_rollback,
std::string("player:")+player->getName());
- /*
- 0: start digging or punch object
- */
- if (action == INTERACT_START_DIGGING) {
+ switch (action) {
+ // Start digging or punch object
+ case INTERACT_START_DIGGING: {
if (pointed.type == POINTEDTHING_NODE) {
MapNode n(CONTENT_IGNORE);
bool pos_ok;
+ v3s16 p_under = pointed.node_undersurface;
n = m_env->getMap().getNode(p_under, &pos_ok);
if (!pos_ok) {
infostream << "Server: Not punching: Node not found. "
"Adding block to emerge queue." << std::endl;
- m_emerge->enqueueBlockEmerge(peer_id, getNodeBlockPos(p_above),
- false);
+ m_emerge->enqueueBlockEmerge(peer_id,
+ getNodeBlockPos(pointed.node_abovesurface), false);
}
if (n.getContent() != CONTENT_IGNORE)
@@ -1076,159 +1088,155 @@ void Server::handleCommand_Interact(NetworkPacket *pkt)
// Cheat prevention
playersao->noCheatDigStart(p_under);
+
+ return;
}
- else if (pointed.type == POINTEDTHING_OBJECT) {
- // Skip if object can't be interacted with anymore
- if (pointed_object->isGone())
- return;
- ItemStack selected_item, hand_item;
- ItemStack tool_item = playersao->getWieldedItem(&selected_item, &hand_item);
- ToolCapabilities toolcap =
- tool_item.getToolCapabilities(m_itemdef);
- v3f dir = (pointed_object->getBasePosition() -
- (playersao->getBasePosition() + playersao->getEyeOffset())
- ).normalize();
- float time_from_last_punch =
- playersao->resetTimeFromLastPunch();
-
- u16 src_original_hp = pointed_object->getHP();
- u16 dst_origin_hp = playersao->getHP();
-
- u16 wear = pointed_object->punch(dir, &toolcap, playersao,
- time_from_last_punch);
-
- // Callback may have changed item, so get it again
- playersao->getWieldedItem(&selected_item);
- bool changed = selected_item.addWear(wear, m_itemdef);
- if (changed)
- playersao->setWieldedItem(selected_item);
-
- // If the object is a player and its HP changed
- if (src_original_hp != pointed_object->getHP() &&
- pointed_object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
- SendPlayerHPOrDie((PlayerSAO *)pointed_object,
- PlayerHPChangeReason(PlayerHPChangeReason::PLAYER_PUNCH, playersao));
- }
+ // Skip if the object can't be interacted with anymore
+ if (pointed.type != POINTEDTHING_OBJECT || pointed_object->isGone())
+ return;
- // If the puncher is a player and its HP changed
- if (dst_origin_hp != playersao->getHP())
- SendPlayerHPOrDie(playersao,
- PlayerHPChangeReason(PlayerHPChangeReason::PLAYER_PUNCH, pointed_object));
+ ItemStack selected_item, hand_item;
+ ItemStack tool_item = playersao->getWieldedItem(&selected_item, &hand_item);
+ ToolCapabilities toolcap =
+ tool_item.getToolCapabilities(m_itemdef);
+ v3f dir = (pointed_object->getBasePosition() -
+ (playersao->getBasePosition() + playersao->getEyeOffset())
+ ).normalize();
+ float time_from_last_punch =
+ playersao->resetTimeFromLastPunch();
+
+ u16 src_original_hp = pointed_object->getHP();
+ u16 dst_origin_hp = playersao->getHP();
+
+ u16 wear = pointed_object->punch(dir, &toolcap, playersao,
+ time_from_last_punch);
+
+ // Callback may have changed item, so get it again
+ playersao->getWieldedItem(&selected_item);
+ bool changed = selected_item.addWear(wear, m_itemdef);
+ if (changed)
+ playersao->setWieldedItem(selected_item);
+
+ // If the object is a player and its HP changed
+ if (src_original_hp != pointed_object->getHP() &&
+ pointed_object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
+ SendPlayerHPOrDie((PlayerSAO *)pointed_object,
+ PlayerHPChangeReason(PlayerHPChangeReason::PLAYER_PUNCH, playersao));
}
+ // If the puncher is a player and its HP changed
+ if (dst_origin_hp != playersao->getHP())
+ SendPlayerHPOrDie(playersao,
+ PlayerHPChangeReason(PlayerHPChangeReason::PLAYER_PUNCH, pointed_object));
+
+ return;
} // action == INTERACT_START_DIGGING
- /*
- 1: stop digging
- */
- else if (action == INTERACT_STOP_DIGGING) {
- } // action == INTERACT_STOP_DIGGING
+ case INTERACT_STOP_DIGGING:
+ // Nothing to do
+ return;
- /*
- 2: Digging completed
- */
- else if (action == INTERACT_DIGGING_COMPLETED) {
+ case INTERACT_DIGGING_COMPLETED: {
// Only digging of nodes
- if (pointed.type == POINTEDTHING_NODE) {
- bool pos_ok;
- MapNode n = m_env->getMap().getNode(p_under, &pos_ok);
- if (!pos_ok) {
- infostream << "Server: Not finishing digging: Node not found. "
- "Adding block to emerge queue." << std::endl;
- m_emerge->enqueueBlockEmerge(peer_id, getNodeBlockPos(p_above),
- false);
- }
-
- /* Cheat prevention */
- bool is_valid_dig = true;
- if (enable_anticheat && !isSingleplayer()) {
- v3s16 nocheat_p = playersao->getNoCheatDigPos();
- float nocheat_t = playersao->getNoCheatDigTime();
- playersao->noCheatDigEnd();
- // If player didn't start digging this, ignore dig
- if (nocheat_p != p_under) {
- infostream << "Server: " << player->getName()
- << " started digging "
- << PP(nocheat_p) << " and completed digging "
- << PP(p_under) << "; not digging." << std::endl;
- is_valid_dig = false;
- // Call callbacks
- m_script->on_cheat(playersao, "finished_unknown_dig");
- }
+ if (pointed.type != POINTEDTHING_NODE)
+ return;
+ bool pos_ok;
+ v3s16 p_under = pointed.node_undersurface;
+ MapNode n = m_env->getMap().getNode(p_under, &pos_ok);
+ if (!pos_ok) {
+ infostream << "Server: Not finishing digging: Node not found. "
+ "Adding block to emerge queue." << std::endl;
+ m_emerge->enqueueBlockEmerge(peer_id,
+ getNodeBlockPos(pointed.node_abovesurface), false);
+ }
- // Get player's wielded item
- // See also: Game::handleDigging
- ItemStack selected_item, hand_item;
- playersao->getPlayer()->getWieldedItem(&selected_item, &hand_item);
-
- // Get diggability and expected digging time
- DigParams params = getDigParams(m_nodedef->get(n).groups,
- &selected_item.getToolCapabilities(m_itemdef));
- // If can't dig, try hand
- if (!params.diggable) {
- params = getDigParams(m_nodedef->get(n).groups,
- &hand_item.getToolCapabilities(m_itemdef));
- }
- // If can't dig, ignore dig
- if (!params.diggable) {
- infostream << "Server: " << player->getName()
- << " completed digging " << PP(p_under)
- << ", which is not diggable with tool; not digging."
- << std::endl;
- is_valid_dig = false;
- // Call callbacks
- m_script->on_cheat(playersao, "dug_unbreakable");
- }
- // Check digging time
- // If already invalidated, we don't have to
- if (!is_valid_dig) {
- // Well not our problem then
- }
- // Clean and long dig
- else if (params.time > 2.0 && nocheat_t * 1.2 > params.time) {
- // All is good, but grab time from pool; don't care if
- // it's actually available
- playersao->getDigPool().grab(params.time);
- }
- // Short or laggy dig
- // Try getting the time from pool
- else if (playersao->getDigPool().grab(params.time)) {
- // All is good
- }
- // Dig not possible
- else {
- infostream << "Server: " << player->getName()
- << " completed digging " << PP(p_under)
- << "too fast; not digging." << std::endl;
- is_valid_dig = false;
- // Call callbacks
- m_script->on_cheat(playersao, "dug_too_fast");
- }
+ /* Cheat prevention */
+ bool is_valid_dig = true;
+ if (enable_anticheat && !isSingleplayer()) {
+ v3s16 nocheat_p = playersao->getNoCheatDigPos();
+ float nocheat_t = playersao->getNoCheatDigTime();
+ playersao->noCheatDigEnd();
+ // If player didn't start digging this, ignore dig
+ if (nocheat_p != p_under) {
+ infostream << "Server: " << player->getName()
+ << " started digging "
+ << PP(nocheat_p) << " and completed digging "
+ << PP(p_under) << "; not digging." << std::endl;
+ is_valid_dig = false;
+ // Call callbacks
+ m_script->on_cheat(playersao, "finished_unknown_dig");
}
- /* Actually dig node */
-
- if (is_valid_dig && n.getContent() != CONTENT_IGNORE)
- m_script->node_on_dig(p_under, n, playersao);
-
- v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
- RemoteClient *client = getClient(peer_id);
- // Send unusual result (that is, node not being removed)
- if (m_env->getMap().getNode(p_under).getContent() != CONTENT_AIR) {
- // Re-send block to revert change on client-side
- client->SetBlockNotSent(blockpos);
+ // Get player's wielded item
+ // See also: Game::handleDigging
+ ItemStack selected_item, hand_item;
+ playersao->getPlayer()->getWieldedItem(&selected_item, &hand_item);
+
+ // Get diggability and expected digging time
+ DigParams params = getDigParams(m_nodedef->get(n).groups,
+ &selected_item.getToolCapabilities(m_itemdef));
+ // If can't dig, try hand
+ if (!params.diggable) {
+ params = getDigParams(m_nodedef->get(n).groups,
+ &hand_item.getToolCapabilities(m_itemdef));
+ }
+ // If can't dig, ignore dig
+ if (!params.diggable) {
+ infostream << "Server: " << player->getName()
+ << " completed digging " << PP(p_under)
+ << ", which is not diggable with tool; not digging."
+ << std::endl;
+ is_valid_dig = false;
+ // Call callbacks
+ m_script->on_cheat(playersao, "dug_unbreakable");
+ }
+ // Check digging time
+ // If already invalidated, we don't have to
+ if (!is_valid_dig) {
+ // Well not our problem then
+ }
+ // Clean and long dig
+ else if (params.time > 2.0 && nocheat_t * 1.2 > params.time) {
+ // All is good, but grab time from pool; don't care if
+ // it's actually available
+ playersao->getDigPool().grab(params.time);
}
+ // Short or laggy dig
+ // Try getting the time from pool
+ else if (playersao->getDigPool().grab(params.time)) {
+ // All is good
+ }
+ // Dig not possible
else {
- client->ResendBlockIfOnWire(blockpos);
+ infostream << "Server: " << player->getName()
+ << " completed digging " << PP(p_under)
+ << "too fast; not digging." << std::endl;
+ is_valid_dig = false;
+ // Call callbacks
+ m_script->on_cheat(playersao, "dug_too_fast");
}
}
+
+ /* Actually dig node */
+
+ if (is_valid_dig && n.getContent() != CONTENT_IGNORE)
+ m_script->node_on_dig(p_under, n, playersao);
+
+ v3s16 blockpos = getNodeBlockPos(p_under);
+ RemoteClient *client = getClient(peer_id);
+ // Send unusual result (that is, node not being removed)
+ if (m_env->getMap().getNode(p_under).getContent() != CONTENT_AIR)
+ // Re-send block to revert change on client-side
+ client->SetBlockNotSent(blockpos);
+ else
+ client->ResendBlockIfOnWire(blockpos);
+
+ return;
} // action == INTERACT_DIGGING_COMPLETED
- /*
- 3: place block or right-click object
- */
- else if (action == INTERACT_PLACE) {
+ // Place block or right-click object
+ case INTERACT_PLACE: {
ItemStack selected_item;
playersao->getWieldedItem(&selected_item, nullptr);
@@ -1257,59 +1265,54 @@ void Server::handleCommand_Interact(NetworkPacket *pkt)
}
pointed_object->rightClick(playersao);
- } else if (m_script->item_OnPlace(
- selected_item, playersao, pointed)) {
+ } else if (m_script->item_OnPlace(selected_item, playersao, pointed)) {
// Placement was handled in lua
// Apply returned ItemStack
- if (playersao->setWieldedItem(selected_item)) {
+ if (playersao->setWieldedItem(selected_item))
SendInventory(playersao, true);
- }
}
+ if (pointed.type != POINTEDTHING_NODE)
+ return;
+
// If item has node placement prediction, always send the
// blocks to make sure the client knows what exactly happened
RemoteClient *client = getClient(peer_id);
- v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_above, BS));
- v3s16 blockpos2 = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
- if (!selected_item.getDefinition(m_itemdef).node_placement_prediction.empty()) {
+ v3s16 blockpos = getNodeBlockPos(pointed.node_abovesurface);
+ v3s16 blockpos2 = getNodeBlockPos(pointed.node_undersurface);
+ if (!selected_item.getDefinition(m_itemdef
+ ).node_placement_prediction.empty()) {
client->SetBlockNotSent(blockpos);
- if (blockpos2 != blockpos) {
+ if (blockpos2 != blockpos)
client->SetBlockNotSent(blockpos2);
- }
- }
- else {
+ } else {
client->ResendBlockIfOnWire(blockpos);
- if (blockpos2 != blockpos) {
+ if (blockpos2 != blockpos)
client->ResendBlockIfOnWire(blockpos2);
- }
}
+
+ return;
} // action == INTERACT_PLACE
- /*
- 4: use
- */
- else if (action == INTERACT_USE) {
+ case INTERACT_USE: {
ItemStack selected_item;
playersao->getWieldedItem(&selected_item, nullptr);
actionstream << player->getName() << " uses " << selected_item.name
<< ", pointing at " << pointed.dump() << std::endl;
- if (m_script->item_OnUse(
- selected_item, playersao, pointed)) {
+ if (m_script->item_OnUse(selected_item, playersao, pointed)) {
// Apply returned ItemStack
- if (playersao->setWieldedItem(selected_item)) {
+ if (playersao->setWieldedItem(selected_item))
SendInventory(playersao, true);
- }
}
- } // action == INTERACT_USE
+ return;
+ }
- /*
- 5: rightclick air
- */
- else if (action == INTERACT_ACTIVATE) {
+ // Rightclick air
+ case INTERACT_ACTIVATE: {
ItemStack selected_item;
playersao->getWieldedItem(&selected_item, nullptr);
@@ -1318,21 +1321,17 @@ void Server::handleCommand_Interact(NetworkPacket *pkt)
pointed.type = POINTEDTHING_NOTHING; // can only ever be NOTHING
- if (m_script->item_OnSecondaryUse(
- selected_item, playersao, pointed)) {
- if (playersao->setWieldedItem(selected_item)) {
+ if (m_script->item_OnSecondaryUse(selected_item, playersao, pointed)) {
+ if (playersao->setWieldedItem(selected_item))
SendInventory(playersao, true);
- }
}
- } // action == INTERACT_ACTIVATE
+ return;
+ }
+
+ default:
+ warningstream << "Server: Invalid action " << action << std::endl;
- /*
- Catch invalid actions
- */
- else {
- warningstream << "Server: Invalid action "
- << action << std::endl;
}
}
@@ -1658,19 +1657,18 @@ void Server::handleCommand_SrpBytesM(NetworkPacket* pkt)
bool wantSudo = (cstate == CS_Active);
- verbosestream << "Server: Received TOCLIENT_SRP_BYTES_M." << std::endl;
+ verbosestream << "Server: Received TOSERVER_SRP_BYTES_M." << std::endl;
if (!((cstate == CS_HelloSent) || (cstate == CS_Active))) {
- actionstream << "Server: got SRP _M packet in wrong state "
- << cstate << " from " << addr_s
- << ". Ignoring." << std::endl;
+ warningstream << "Server: got SRP_M packet in wrong state "
+ << cstate << " from " << addr_s << ". Ignoring." << std::endl;
return;
}
if (client->chosen_mech != AUTH_MECHANISM_SRP &&
client->chosen_mech != AUTH_MECHANISM_LEGACY_PASSWORD) {
- actionstream << "Server: got SRP _M packet, while auth"
- << "is going on with mech " << client->chosen_mech << " from "
+ warningstream << "Server: got SRP_M packet, while auth "
+ "is going on with mech " << client->chosen_mech << " from "
<< addr_s << " (wantSudo=" << wantSudo << "). Denying." << std::endl;
if (wantSudo) {
DenySudoAccess(peer_id);
@@ -1718,7 +1716,7 @@ void Server::handleCommand_SrpBytesM(NetworkPacket* pkt)
std::string checkpwd; // not used, but needed for passing something
if (!m_script->getAuth(playername, &checkpwd, NULL)) {
- actionstream << "Server: " << playername <<
+ errorstream << "Server: " << playername <<
" cannot be authenticated (auth handler does not work?)" <<
std::endl;
DenyAccess(peer_id, SERVER_ACCESSDENIED_SERVER_FAIL);
diff --git a/src/nodedef.cpp b/src/nodedef.cpp
index 540ed6086..4d4fc7a7a 100644
--- a/src/nodedef.cpp
+++ b/src/nodedef.cpp
@@ -207,7 +207,7 @@ void TileDef::serialize(std::ostream &os, u16 protocol_version) const
u8 version = 6;
writeU8(os, version);
- os << serializeString(name);
+ os << serializeString16(name);
animation.serialize(os, version);
bool has_scale = scale > 0;
u16 flags = 0;
@@ -241,7 +241,7 @@ void TileDef::deSerialize(std::istream &is, u8 contentfeatures_version,
int version = readU8(is);
if (version < 6)
throw SerializationError("unsupported TileDef version");
- name = deSerializeString(is);
+ name = deSerializeString16(is);
animation.deSerialize(is, version);
u16 flags = readU16(is);
backface_culling = flags & TILE_FLAG_BACKFACE_CULLING;
@@ -266,9 +266,6 @@ void TextureSettings::readSettings()
{
connected_glass = g_settings->getBool("connected_glass");
opaque_water = g_settings->getBool("opaque_water");
- bool enable_shaders = g_settings->getBool("enable_shaders");
- bool enable_bumpmapping = g_settings->getBool("enable_bumpmapping");
- bool enable_parallax_occlusion = g_settings->getBool("enable_parallax_occlusion");
bool smooth_lighting = g_settings->getBool("smooth_lighting");
enable_mesh_cache = g_settings->getBool("enable_mesh_cache");
enable_minimap = g_settings->getBool("enable_minimap");
@@ -281,8 +278,6 @@ void TextureSettings::readSettings()
if (smooth_lighting)
enable_mesh_cache = false;
- use_normal_texture = enable_shaders &&
- (enable_bumpmapping || enable_parallax_occlusion);
if (leaves_style_str == "fancy") {
leaves_style = LEAVES_FANCY;
} else if (leaves_style_str == "simple") {
@@ -416,10 +411,10 @@ void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const
writeU8(os, version);
// general
- os << serializeString(name);
+ os << serializeString16(name);
writeU16(os, groups.size());
for (const auto &group : groups) {
- os << serializeString(group.first);
+ os << serializeString16(group.first);
writeS16(os, group.second);
}
writeU8(os, param_type);
@@ -427,7 +422,7 @@ void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const
// visual
writeU8(os, drawtype);
- os << serializeString(mesh);
+ os << serializeString16(mesh);
writeF32(os, visual_scale);
writeU8(os, 6);
for (const TileDef &td : tiledef)
@@ -442,7 +437,7 @@ void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const
writeU8(os, color.getRed());
writeU8(os, color.getGreen());
writeU8(os, color.getBlue());
- os << serializeString(palette_name);
+ os << serializeString16(palette_name);
writeU8(os, waving);
writeU8(os, connect_sides);
writeU16(os, connects_to_ids.size());
@@ -470,8 +465,8 @@ void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const
// liquid
writeU8(os, liquid_type);
- os << serializeString(liquid_alternative_flowing);
- os << serializeString(liquid_alternative_source);
+ os << serializeString16(liquid_alternative_flowing);
+ os << serializeString16(liquid_alternative_source);
writeU8(os, liquid_viscosity);
writeU8(os, liquid_renewable);
writeU8(os, liquid_range);
@@ -492,7 +487,7 @@ void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const
writeU8(os, legacy_facedir_simple);
writeU8(os, legacy_wallmounted);
- os << serializeString(node_dig_prediction);
+ os << serializeString16(node_dig_prediction);
writeU8(os, leveled_max);
}
@@ -519,11 +514,11 @@ void ContentFeatures::deSerialize(std::istream &is)
throw SerializationError("unsupported ContentFeatures version");
// general
- name = deSerializeString(is);
+ name = deSerializeString16(is);
groups.clear();
u32 groups_size = readU16(is);
for (u32 i = 0; i < groups_size; i++) {
- std::string name = deSerializeString(is);
+ std::string name = deSerializeString16(is);
int value = readS16(is);
groups[name] = value;
}
@@ -532,7 +527,7 @@ void ContentFeatures::deSerialize(std::istream &is)
// visual
drawtype = (enum NodeDrawType) readU8(is);
- mesh = deSerializeString(is);
+ mesh = deSerializeString16(is);
visual_scale = readF32(is);
if (readU8(is) != 6)
throw SerializationError("unsupported tile count");
@@ -548,7 +543,7 @@ void ContentFeatures::deSerialize(std::istream &is)
color.setRed(readU8(is));
color.setGreen(readU8(is));
color.setBlue(readU8(is));
- palette_name = deSerializeString(is);
+ palette_name = deSerializeString16(is);
waving = readU8(is);
connect_sides = readU8(is);
u16 connects_to_size = readU16(is);
@@ -578,8 +573,8 @@ void ContentFeatures::deSerialize(std::istream &is)
// liquid
liquid_type = (enum LiquidType) readU8(is);
- liquid_alternative_flowing = deSerializeString(is);
- liquid_alternative_source = deSerializeString(is);
+ liquid_alternative_flowing = deSerializeString16(is);
+ liquid_alternative_source = deSerializeString16(is);
liquid_viscosity = readU8(is);
liquid_renewable = readU8(is);
liquid_range = readU8(is);
@@ -601,7 +596,7 @@ void ContentFeatures::deSerialize(std::istream &is)
legacy_wallmounted = readU8(is);
try {
- node_dig_prediction = deSerializeString(is);
+ node_dig_prediction = deSerializeString16(is);
u8 tmp_leveled_max = readU8(is);
if (is.eof()) /* readU8 doesn't throw exceptions so we have to do this */
throw SerializationError("");
@@ -635,10 +630,6 @@ static void fillTileAttribs(ITextureSource *tsrc, TileLayer *layer,
if (!tile.world_aligned)
layer->scale = 1;
- // Normal texture and shader flags texture
- if (tsettings.use_normal_texture) {
- layer->normal_texture = tsrc->getNormalTexture(tiledef.name);
- }
layer->flags_texture = tsrc->getShaderFlagsTexture(layer->normal_texture ? true : false);
// Material flags
@@ -695,9 +686,54 @@ static void fillTileAttribs(ITextureSource *tsrc, TileLayer *layer,
}
}
}
-#endif
-#ifndef SERVER
+bool ContentFeatures::textureAlphaCheck(ITextureSource *tsrc, const TileDef *tiles, int length)
+{
+ video::IVideoDriver *driver = RenderingEngine::get_video_driver();
+ static thread_local bool long_warning_printed = false;
+ std::set<std::string> seen;
+ for (int i = 0; i < length; i++) {
+ if (seen.find(tiles[i].name) != seen.end())
+ continue;
+ seen.insert(tiles[i].name);
+
+ // Load the texture and see if there's any transparent pixels
+ video::ITexture *texture = tsrc->getTexture(tiles[i].name);
+ video::IImage *image = driver->createImage(texture,
+ core::position2d<s32>(0, 0), texture->getOriginalSize());
+ if (!image)
+ continue;
+ core::dimension2d<u32> dim = image->getDimension();
+ bool ok = true;
+ for (u16 x = 0; x < dim.Width; x++) {
+ for (u16 y = 0; y < dim.Height; y++) {
+ if (image->getPixel(x, y).getAlpha() < 255) {
+ ok = false;
+ goto break_loop;
+ }
+ }
+ }
+
+break_loop:
+ image->drop();
+ if (!ok) {
+ warningstream << "Texture \"" << tiles[i].name << "\" of "
+ << name << " has transparent pixels, assuming "
+ "use_texture_alpha = true." << std::endl;
+ if (!long_warning_printed) {
+ warningstream << " This warning can be a false-positive if "
+ "unused pixels in the texture are transparent. However if "
+ "it is meant to be transparent, you *MUST* update the "
+ "nodedef and set use_texture_alpha = true! This compatibility "
+ "code will be removed in a few releases." << std::endl;
+ long_warning_printed = true;
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
bool isWorldAligned(AlignStyle style, WorldAlignMode mode, NodeDrawType drawtype)
{
if (style == ALIGN_STYLE_WORLD)
@@ -814,13 +850,19 @@ void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc
break;
case NDT_MESH:
case NDT_NODEBOX:
+ if (alpha == 255 && textureAlphaCheck(tsrc, tdef, 6))
+ alpha = 0;
+
solidness = 0;
if (waving == 1)
material_type = TILE_MATERIAL_WAVING_PLANTS;
else if (waving == 2)
material_type = TILE_MATERIAL_WAVING_LEAVES;
else if (waving == 3)
- material_type = TILE_MATERIAL_WAVING_LIQUID_BASIC;
+ material_type = (alpha == 255) ? TILE_MATERIAL_WAVING_LIQUID_OPAQUE :
+ TILE_MATERIAL_WAVING_LIQUID_BASIC;
+ else if (alpha == 255)
+ material_type = TILE_MATERIAL_OPAQUE;
break;
case NDT_TORCHLIKE:
case NDT_SIGNLIKE:
@@ -1353,6 +1395,7 @@ void NodeDefManager::applyTextureOverrides(const std::vector<TextureOverride> &o
ContentFeatures &nodedef = m_content_features[id];
+ // Override tiles
if (texture_override.hasTarget(OverrideTarget::TOP))
nodedef.tiledef[0].name = texture_override.texture;
@@ -1370,6 +1413,26 @@ void NodeDefManager::applyTextureOverrides(const std::vector<TextureOverride> &o
if (texture_override.hasTarget(OverrideTarget::FRONT))
nodedef.tiledef[5].name = texture_override.texture;
+
+
+ // Override special tiles, if applicable
+ if (texture_override.hasTarget(OverrideTarget::SPECIAL_1))
+ nodedef.tiledef_special[0].name = texture_override.texture;
+
+ if (texture_override.hasTarget(OverrideTarget::SPECIAL_2))
+ nodedef.tiledef_special[1].name = texture_override.texture;
+
+ if (texture_override.hasTarget(OverrideTarget::SPECIAL_3))
+ nodedef.tiledef_special[2].name = texture_override.texture;
+
+ if (texture_override.hasTarget(OverrideTarget::SPECIAL_4))
+ nodedef.tiledef_special[3].name = texture_override.texture;
+
+ if (texture_override.hasTarget(OverrideTarget::SPECIAL_5))
+ nodedef.tiledef_special[4].name = texture_override.texture;
+
+ if (texture_override.hasTarget(OverrideTarget::SPECIAL_6))
+ nodedef.tiledef_special[5].name = texture_override.texture;
}
}
@@ -1416,7 +1479,7 @@ void NodeDefManager::serialize(std::ostream &os, u16 protocol_version) const
// strict version incompatibilities
std::ostringstream wrapper_os(std::ios::binary);
f->serialize(wrapper_os, protocol_version);
- os2<<serializeString(wrapper_os.str());
+ os2<<serializeString16(wrapper_os.str());
// must not overflow
u16 next = count + 1;
@@ -1424,7 +1487,7 @@ void NodeDefManager::serialize(std::ostream &os, u16 protocol_version) const
count++;
}
writeU16(os, count);
- os << serializeLongString(os2.str());
+ os << serializeString32(os2.str());
}
@@ -1435,13 +1498,13 @@ void NodeDefManager::deSerialize(std::istream &is)
if (version != 1)
throw SerializationError("unsupported NodeDefinitionManager version");
u16 count = readU16(is);
- std::istringstream is2(deSerializeLongString(is), std::ios::binary);
+ std::istringstream is2(deSerializeString32(is), std::ios::binary);
ContentFeatures f;
for (u16 n = 0; n < count; n++) {
u16 i = readU16(is2);
// Read it from the string wrapper
- std::string wrapper = deSerializeString(is2);
+ std::string wrapper = deSerializeString16(is2);
std::istringstream wrapper_is(wrapper, std::ios::binary);
f.deSerialize(wrapper_is);
diff --git a/src/nodedef.h b/src/nodedef.h
index cf03abaae..66c21cc07 100644
--- a/src/nodedef.h
+++ b/src/nodedef.h
@@ -158,7 +158,6 @@ public:
int node_texture_size;
bool opaque_water;
bool connected_glass;
- bool use_normal_texture;
bool enable_mesh_cache;
bool enable_minimap;
@@ -261,6 +260,11 @@ struct TileDef
NodeDrawType drawtype);
};
+// Defines the number of special tiles per nodedef
+//
+// NOTE: When changing this value, the enum entries of OverrideTarget and
+// parser in TextureOverrideSource must be updated so that all special
+// tiles can be overridden.
#define CF_SPECIAL_COUNT 6
struct ContentFeatures
@@ -413,6 +417,7 @@ struct ContentFeatures
void reset();
void serialize(std::ostream &os, u16 protocol_version) const;
void deSerialize(std::istream &is);
+
/*!
* Since vertex alpha is no longer supported, this method
* adds opacity directly to the texture pixels.
@@ -422,9 +427,36 @@ struct ContentFeatures
*/
void correctAlpha(TileDef *tiles, int length);
+#ifndef SERVER
+ /*
+ * Checks if any tile texture has any transparent pixels.
+ * Prints a warning and returns true if that is the case, false otherwise.
+ * This is supposed to be used for use_texture_alpha backwards compatibility.
+ */
+ bool textureAlphaCheck(ITextureSource *tsrc, const TileDef *tiles,
+ int length);
+#endif
+
+
/*
Some handy methods
*/
+ bool needsBackfaceCulling() const
+ {
+ switch (drawtype) {
+ case NDT_TORCHLIKE:
+ case NDT_SIGNLIKE:
+ case NDT_FIRELIKE:
+ case NDT_RAILLIKE:
+ case NDT_PLANTLIKE:
+ case NDT_PLANTLIKE_ROOTED:
+ case NDT_MESH:
+ return false;
+ default:
+ return true;
+ }
+ }
+
bool isLiquid() const{
return (liquid_type != LIQUID_NONE);
}
diff --git a/src/nodemetadata.cpp b/src/nodemetadata.cpp
index b84ffc8cb..6447c8785 100644
--- a/src/nodemetadata.cpp
+++ b/src/nodemetadata.cpp
@@ -49,8 +49,8 @@ void NodeMetadata::serialize(std::ostream &os, u8 version, bool disk) const
if (!disk && priv)
continue;
- os << serializeString(sv.first);
- os << serializeLongString(sv.second);
+ os << serializeString16(sv.first);
+ os << serializeString32(sv.second);
if (version >= 2)
writeU8(os, (priv) ? 1 : 0);
}
@@ -63,8 +63,8 @@ void NodeMetadata::deSerialize(std::istream &is, u8 version)
clear();
int num_vars = readU32(is);
for(int i=0; i<num_vars; i++){
- std::string name = deSerializeString(is);
- std::string var = deSerializeLongString(is);
+ std::string name = deSerializeString16(is);
+ std::string var = deSerializeString32(is);
m_stringvars[name] = var;
if (version >= 2) {
if (readU8(is) == 1)
diff --git a/src/noise.cpp b/src/noise.cpp
index 5a1d989cb..e16564b05 100644
--- a/src/noise.cpp
+++ b/src/noise.cpp
@@ -424,7 +424,7 @@ float NoisePerlin3D(NoiseParams *np, float x, float y, float z, s32 seed)
Noise::Noise(NoiseParams *np_, s32 seed, u32 sx, u32 sy, u32 sz)
{
- memcpy(&np, np_, sizeof(np));
+ np = *np_;
this->seed = seed;
this->sx = sx;
this->sy = sy;
diff --git a/src/noise.h b/src/noise.h
index 7b5e83251..613879890 100644
--- a/src/noise.h
+++ b/src/noise.h
@@ -29,6 +29,13 @@
#include "exceptions.h"
#include "util/string.h"
+#if defined(RANDOM_MIN)
+#undef RANDOM_MIN
+#endif
+#if defined(RANDOM_MAX)
+#undef RANDOM_MAX
+#endif
+
extern FlagDesc flagdesc_noiseparams[];
// Note: this class is not polymorphic so that its high level of
diff --git a/src/object_properties.cpp b/src/object_properties.cpp
index 8d51bcbfa..f31773060 100644
--- a/src/object_properties.cpp
+++ b/src/object_properties.cpp
@@ -70,6 +70,7 @@ std::string ObjectProperties::dump()
os << ", use_texture_alpha=" << use_texture_alpha;
os << ", damage_texture_modifier=" << damage_texture_modifier;
os << ", shaded=" << shaded;
+ os << ", show_on_minimap=" << show_on_minimap;
return os.str();
}
@@ -84,11 +85,11 @@ void ObjectProperties::serialize(std::ostream &os) const
writeV3F32(os, selectionbox.MinEdge);
writeV3F32(os, selectionbox.MaxEdge);
writeU8(os, pointable);
- os << serializeString(visual);
+ os << serializeString16(visual);
writeV3F32(os, visual_size);
writeU16(os, textures.size());
for (const std::string &texture : textures) {
- os << serializeString(texture);
+ os << serializeString16(texture);
}
writeV2S16(os, spritediv);
writeV2S16(os, initial_sprite_basepos);
@@ -96,7 +97,7 @@ void ObjectProperties::serialize(std::ostream &os) const
writeU8(os, makes_footstep_sound);
writeF32(os, automatic_rotate);
// Added in protocol version 14
- os << serializeString(mesh);
+ os << serializeString16(mesh);
writeU16(os, colors.size());
for (video::SColor color : colors) {
writeARGB8(os, color);
@@ -106,18 +107,19 @@ void ObjectProperties::serialize(std::ostream &os) const
writeU8(os, automatic_face_movement_dir);
writeF32(os, automatic_face_movement_dir_offset);
writeU8(os, backface_culling);
- os << serializeString(nametag);
+ os << serializeString16(nametag);
writeARGB8(os, nametag_color);
writeF32(os, automatic_face_movement_max_rotation_per_sec);
- os << serializeString(infotext);
- os << serializeString(wield_item);
+ os << serializeString16(infotext);
+ os << serializeString16(wield_item);
writeS8(os, glow);
writeU16(os, breath_max);
writeF32(os, eye_height);
writeF32(os, zoom_fov);
writeU8(os, use_texture_alpha);
- os << serializeString(damage_texture_modifier);
+ os << serializeString16(damage_texture_modifier);
writeU8(os, shaded);
+ writeU8(os, show_on_minimap);
// Add stuff only at the bottom.
// Never remove anything, because we don't want new versions of this
@@ -137,19 +139,19 @@ void ObjectProperties::deSerialize(std::istream &is)
selectionbox.MinEdge = readV3F32(is);
selectionbox.MaxEdge = readV3F32(is);
pointable = readU8(is);
- visual = deSerializeString(is);
+ visual = deSerializeString16(is);
visual_size = readV3F32(is);
textures.clear();
u32 texture_count = readU16(is);
for (u32 i = 0; i < texture_count; i++){
- textures.push_back(deSerializeString(is));
+ textures.push_back(deSerializeString16(is));
}
spritediv = readV2S16(is);
initial_sprite_basepos = readV2S16(is);
is_visible = readU8(is);
makes_footstep_sound = readU8(is);
automatic_rotate = readF32(is);
- mesh = deSerializeString(is);
+ mesh = deSerializeString16(is);
colors.clear();
u32 color_count = readU16(is);
for (u32 i = 0; i < color_count; i++){
@@ -160,21 +162,25 @@ void ObjectProperties::deSerialize(std::istream &is)
automatic_face_movement_dir = readU8(is);
automatic_face_movement_dir_offset = readF32(is);
backface_culling = readU8(is);
- nametag = deSerializeString(is);
+ nametag = deSerializeString16(is);
nametag_color = readARGB8(is);
automatic_face_movement_max_rotation_per_sec = readF32(is);
- infotext = deSerializeString(is);
- wield_item = deSerializeString(is);
+ infotext = deSerializeString16(is);
+ wield_item = deSerializeString16(is);
glow = readS8(is);
breath_max = readU16(is);
eye_height = readF32(is);
zoom_fov = readF32(is);
use_texture_alpha = readU8(is);
try {
- damage_texture_modifier = deSerializeString(is);
+ damage_texture_modifier = deSerializeString16(is);
u8 tmp = readU8(is);
if (is.eof())
- throw SerializationError("");
+ return;
shaded = tmp;
+ tmp = readU8(is);
+ if (is.eof())
+ return;
+ show_on_minimap = tmp;
} catch (SerializationError &e) {}
}
diff --git a/src/object_properties.h b/src/object_properties.h
index f010c1daf..adb483527 100644
--- a/src/object_properties.h
+++ b/src/object_properties.h
@@ -62,6 +62,7 @@ struct ObjectProperties
float zoom_fov = 0.0f;
bool use_texture_alpha = false;
bool shaded = true;
+ bool show_on_minimap = false;
ObjectProperties();
std::string dump();
diff --git a/src/particles.cpp b/src/particles.cpp
index fd81238dc..14c987958 100644
--- a/src/particles.cpp
+++ b/src/particles.cpp
@@ -28,7 +28,7 @@ void ParticleParameters::serialize(std::ostream &os, u16 protocol_ver) const
writeF32(os, expirationtime);
writeF32(os, size);
writeU8(os, collisiondetection);
- os << serializeLongString(texture);
+ os << serializeString32(texture);
writeU8(os, vertical);
writeU8(os, collision_removal);
animation.serialize(os, 6); /* NOT the protocol ver */
@@ -47,7 +47,7 @@ void ParticleParameters::deSerialize(std::istream &is, u16 protocol_ver)
expirationtime = readF32(is);
size = readF32(is);
collisiondetection = readU8(is);
- texture = deSerializeLongString(is);
+ texture = deSerializeString32(is);
vertical = readU8(is);
collision_removal = readU8(is);
animation.deSerialize(is, 6); /* NOT the protocol ver */
diff --git a/src/player.h b/src/player.h
index f1b848a2a..9a4a41fa6 100644
--- a/src/player.h
+++ b/src/player.h
@@ -57,8 +57,8 @@ struct PlayerControl
bool a_aux1,
bool a_sneak,
bool a_zoom,
- bool a_LMB,
- bool a_RMB,
+ bool a_dig,
+ bool a_place,
float a_pitch,
float a_yaw,
float a_sidew_move_joystick_axis,
@@ -73,8 +73,8 @@ struct PlayerControl
aux1 = a_aux1;
sneak = a_sneak;
zoom = a_zoom;
- LMB = a_LMB;
- RMB = a_RMB;
+ dig = a_dig;
+ place = a_place;
pitch = a_pitch;
yaw = a_yaw;
sidew_move_joystick_axis = a_sidew_move_joystick_axis;
@@ -88,8 +88,8 @@ struct PlayerControl
bool aux1 = false;
bool sneak = false;
bool zoom = false;
- bool LMB = false;
- bool RMB = false;
+ bool dig = false;
+ bool place = false;
float pitch = 0.0f;
float yaw = 0.0f;
float sidew_move_joystick_axis = 0.0f;
diff --git a/src/porting.cpp b/src/porting.cpp
index d902d3737..e7ed4e090 100644
--- a/src/porting.cpp
+++ b/src/porting.cpp
@@ -25,7 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "porting.h"
-#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__DragonFly__)
+#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__DragonFly__) || defined(__OpenBSD__)
#include <sys/types.h>
#include <sys/sysctl.h>
extern char **environ;
@@ -56,6 +56,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <crt_externs.h>
#endif
+#if defined(__HAIKU__)
+ #include <FindDirectory.h>
+#endif
+
#include "config.h"
#include "debug.h"
#include "filesys.h"
@@ -321,6 +325,12 @@ bool getCurrentExecPath(char *buf, size_t len)
return true;
}
+#elif defined(__HAIKU__)
+
+bool getCurrentExecPath(char *buf, size_t len)
+{
+ return find_path(B_APP_IMAGE_SYMBOL, B_FIND_PATH_IMAGE_PATH, NULL, buf, len) == B_OK;
+}
//// Solaris
#elif defined(__sun) || defined(sun)
diff --git a/src/porting.h b/src/porting.h
index f50f0a950..c7adf12a2 100644
--- a/src/porting.h
+++ b/src/porting.h
@@ -309,6 +309,8 @@ inline const char *getPlatformName()
#else
"SunOS"
#endif
+#elif defined(__HAIKU__)
+ "Haiku"
#elif defined(__CYGWIN__)
"Cygwin"
#elif defined(__unix__) || defined(__unix)
diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp
index 78c46f0e0..04f4c335c 100644
--- a/src/script/common/c_content.cpp
+++ b/src/script/common/c_content.cpp
@@ -57,6 +57,7 @@ void read_item_definition(lua_State* L, int index,
es_ItemType, ITEM_NONE);
getstringfield(L, index, "name", def.name);
getstringfield(L, index, "description", def.description);
+ getstringfield(L, index, "short_description", def.short_description);
getstringfield(L, index, "inventory_image", def.inventory_image);
getstringfield(L, index, "inventory_overlay", def.inventory_overlay);
getstringfield(L, index, "wield_image", def.wield_image);
@@ -143,6 +144,8 @@ void push_item_definition_full(lua_State *L, const ItemDefinition &i)
lua_setfield(L, -2, "name");
lua_pushstring(L, i.description.c_str());
lua_setfield(L, -2, "description");
+ lua_pushstring(L, i.short_description.c_str());
+ lua_setfield(L, -2, "short_description");
lua_pushstring(L, type.c_str());
lua_setfield(L, -2, "type");
lua_pushstring(L, i.inventory_image.c_str());
@@ -331,6 +334,7 @@ void read_object_properties(lua_State *L, int index,
getfloatfield(L, -1, "zoom_fov", prop->zoom_fov);
getboolfield(L, -1, "use_texture_alpha", prop->use_texture_alpha);
getboolfield(L, -1, "shaded", prop->shaded);
+ getboolfield(L, -1, "show_on_minimap", prop->show_on_minimap);
getstringfield(L, -1, "damage_texture_modifier", prop->damage_texture_modifier);
}
@@ -419,6 +423,8 @@ void push_object_properties(lua_State *L, ObjectProperties *prop)
lua_setfield(L, -2, "shaded");
lua_pushlstring(L, prop->damage_texture_modifier.c_str(), prop->damage_texture_modifier.size());
lua_setfield(L, -2, "damage_texture_modifier");
+ lua_pushboolean(L, prop->show_on_minimap);
+ lua_setfield(L, -2, "show_on_minimap");
}
/******************************************************************************/
@@ -491,13 +497,11 @@ TileDef read_tiledef(lua_State *L, int index, u8 drawtype)
}
/******************************************************************************/
-ContentFeatures read_content_features(lua_State *L, int index)
+void read_content_features(lua_State *L, ContentFeatures &f, int index)
{
if(index < 0)
index = lua_gettop(L) + 1 + index;
- ContentFeatures f;
-
/* Cache existence of some callbacks */
lua_getfield(L, index, "on_construct");
if(!lua_isnil(L, -1)) f.has_on_construct = true;
@@ -800,7 +804,6 @@ ContentFeatures read_content_features(lua_State *L, int index)
getstringfield(L, index, "node_dig_prediction",
f.node_dig_prediction);
- return f;
}
void push_content_features(lua_State *L, const ContentFeatures &c)
@@ -1977,9 +1980,10 @@ void push_hud_element(lua_State *L, HudElement *elem)
HudElementStat read_hud_change(lua_State *L, HudElement *elem, void **value)
{
HudElementStat stat = HUD_STAT_NUMBER;
+ std::string statstr;
if (lua_isstring(L, 3)) {
int statint;
- std::string statstr = lua_tostring(L, 3);
+ statstr = lua_tostring(L, 3);
stat = string_to_enum(es_HudElementStat, statint, statstr) ?
(HudElementStat)statint : stat;
}
@@ -2007,6 +2011,8 @@ HudElementStat read_hud_change(lua_State *L, HudElement *elem, void **value)
break;
case HUD_STAT_ITEM:
elem->item = luaL_checknumber(L, 4);
+ if (elem->type == HUD_ELEM_WAYPOINT && statstr == "precision")
+ elem->item++;
*value = &elem->item;
break;
case HUD_STAT_DIR:
diff --git a/src/script/common/c_content.h b/src/script/common/c_content.h
index eeef71c36..08c74c292 100644
--- a/src/script/common/c_content.h
+++ b/src/script/common/c_content.h
@@ -67,7 +67,8 @@ struct collisionMoveResult;
extern struct EnumString es_TileAnimationType[];
-ContentFeatures read_content_features (lua_State *L, int index);
+void read_content_features (lua_State *L, ContentFeatures &f,
+ int index);
void push_content_features (lua_State *L,
const ContentFeatures &c);
diff --git a/src/script/common/c_converter.cpp b/src/script/common/c_converter.cpp
index eb6ab5331..c00401b58 100644
--- a/src/script/common/c_converter.cpp
+++ b/src/script/common/c_converter.cpp
@@ -679,48 +679,3 @@ size_t write_array_slice_float(
return elem_index - 1;
}
-
-
-size_t write_array_slice_u16(
- lua_State *L,
- int table_index,
- u16 *data,
- v3u16 data_size,
- v3u16 slice_offset,
- v3u16 slice_size)
-{
- v3u16 pmin, pmax(data_size);
-
- if (slice_offset.X > 0) {
- slice_offset.X--;
- pmin.X = slice_offset.X;
- pmax.X = MYMIN(slice_offset.X + slice_size.X, data_size.X);
- }
-
- if (slice_offset.Y > 0) {
- slice_offset.Y--;
- pmin.Y = slice_offset.Y;
- pmax.Y = MYMIN(slice_offset.Y + slice_size.Y, data_size.Y);
- }
-
- if (slice_offset.Z > 0) {
- slice_offset.Z--;
- pmin.Z = slice_offset.Z;
- pmax.Z = MYMIN(slice_offset.Z + slice_size.Z, data_size.Z);
- }
-
- const u32 ystride = data_size.X;
- const u32 zstride = data_size.X * data_size.Y;
-
- u32 elem_index = 1;
- for (u32 z = pmin.Z; z != pmax.Z; z++)
- for (u32 y = pmin.Y; y != pmax.Y; y++)
- for (u32 x = pmin.X; x != pmax.X; x++) {
- u32 i = z * zstride + y * ystride + x;
- lua_pushinteger(L, data[i]);
- lua_rawseti(L, table_index, elem_index);
- elem_index++;
- }
-
- return elem_index - 1;
-}
diff --git a/src/script/common/c_converter.h b/src/script/common/c_converter.h
index a4a7079fd..6ad6f3212 100644
--- a/src/script/common/c_converter.h
+++ b/src/script/common/c_converter.h
@@ -136,5 +136,3 @@ void warn_if_field_exists(lua_State *L, int table,
size_t write_array_slice_float(lua_State *L, int table_index, float *data,
v3u16 data_size, v3u16 slice_offset, v3u16 slice_size);
-size_t write_array_slice_u16(lua_State *L, int table_index, u16 *data,
- v3u16 data_size, v3u16 slice_offset, v3u16 slice_size);
diff --git a/src/script/common/c_internal.cpp b/src/script/common/c_internal.cpp
index 6df1f8b7b..ad5f836c5 100644
--- a/src/script/common/c_internal.cpp
+++ b/src/script/common/c_internal.cpp
@@ -155,24 +155,28 @@ static void script_log(lua_State *L, const std::string &message,
infostream << script_get_backtrace(L) << std::endl;
}
-void log_deprecated(lua_State *L, const std::string &message, int stack_depth)
+DeprecatedHandlingMode get_deprecated_handling_mode()
{
static thread_local bool configured = false;
- static thread_local bool do_log = false;
- static thread_local bool do_error = false;
+ static thread_local DeprecatedHandlingMode ret = DeprecatedHandlingMode::Ignore;
// Only read settings on first call
if (!configured) {
std::string value = g_settings->get("deprecated_lua_api_handling");
if (value == "log") {
- do_log = true;
+ ret = DeprecatedHandlingMode::Log;
} else if (value == "error") {
- do_log = true;
- do_error = true;
+ ret = DeprecatedHandlingMode::Error;
}
configured = true;
}
- if (do_log)
- script_log(L, message, warningstream, do_error, stack_depth);
+ return ret;
+}
+
+void log_deprecated(lua_State *L, const std::string &message, int stack_depth)
+{
+ DeprecatedHandlingMode mode = get_deprecated_handling_mode();
+ if (mode != DeprecatedHandlingMode::Ignore)
+ script_log(L, message, warningstream, mode == DeprecatedHandlingMode::Error, stack_depth);
}
diff --git a/src/script/common/c_internal.h b/src/script/common/c_internal.h
index 442546332..452c2dd5e 100644
--- a/src/script/common/c_internal.h
+++ b/src/script/common/c_internal.h
@@ -114,5 +114,25 @@ void script_error(lua_State *L, int pcall_result, const char *mod, const char *f
void script_run_callbacks_f(lua_State *L, int nargs,
RunCallbacksMode mode, const char *fxn);
+enum class DeprecatedHandlingMode {
+ Ignore,
+ Log,
+ Error
+};
+
+/**
+ * Reads `deprecated_lua_api_handling` in settings, returns cached value.
+ *
+ * @return DeprecatedHandlingMode
+ */
+DeprecatedHandlingMode get_deprecated_handling_mode();
+
+/**
+ * Handles a deprecation warning based on user settings
+ *
+ * @param L Lua State
+ * @param message The deprecation method
+ * @param stack_depth How far on the stack to the first user function (ie: not builtin or core)
+ */
void log_deprecated(lua_State *L, const std::string &message,
int stack_depth=1);
diff --git a/src/script/common/helper.cpp b/src/script/common/helper.cpp
index f53a2b7e8..488144790 100644
--- a/src/script/common/helper.cpp
+++ b/src/script/common/helper.cpp
@@ -21,6 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <cmath>
#include <sstream>
#include <irr_v2d.h>
+#include <irr_v3d.h>
#include "c_types.h"
#include "c_internal.h"
@@ -54,17 +55,14 @@ template <> bool LuaHelper::readParam(lua_State *L, int index)
return lua_toboolean(L, index) != 0;
}
-template <> bool LuaHelper::readParam(lua_State *L, int index, const bool &default_value)
+template <> s16 LuaHelper::readParam(lua_State *L, int index)
{
- if (lua_isnil(L, index))
- return default_value;
-
- return lua_toboolean(L, index) != 0;
+ return lua_tonumber(L, index);
}
-template <> s16 LuaHelper::readParam(lua_State *L, int index)
+template <> int LuaHelper::readParam(lua_State *L, int index)
{
- return lua_tonumber(L, index);
+ return luaL_checkint(L, index);
}
template <> float LuaHelper::readParam(lua_State *L, int index)
@@ -105,6 +103,25 @@ template <> v2f LuaHelper::readParam(lua_State *L, int index)
return p;
}
+template <> v3f LuaHelper::readParam(lua_State *L, int index)
+{
+ v3f p;
+ CHECK_POS_TAB(index);
+ lua_getfield(L, index, "x");
+ CHECK_POS_COORD("x");
+ p.X = readParam<float>(L, -1);
+ lua_pop(L, 1);
+ lua_getfield(L, index, "y");
+ CHECK_POS_COORD("y");
+ p.Y = readParam<float>(L, -1);
+ lua_pop(L, 1);
+ lua_getfield(L, index, "z");
+ CHECK_POS_COORD("z");
+ p.Z = readParam<float>(L, -1);
+ lua_pop(L, 1);
+ return p;
+}
+
template <> std::string LuaHelper::readParam(lua_State *L, int index)
{
size_t length;
@@ -113,16 +130,3 @@ template <> std::string LuaHelper::readParam(lua_State *L, int index)
result.assign(str, length);
return result;
}
-
-template <>
-std::string LuaHelper::readParam(
- lua_State *L, int index, const std::string &default_value)
-{
- std::string result;
- const char *str = lua_tostring(L, index);
- if (str)
- result.append(str);
- else
- result = default_value;
- return result;
-}
diff --git a/src/script/common/helper.h b/src/script/common/helper.h
index d639d6e16..7a794dc9b 100644
--- a/src/script/common/helper.h
+++ b/src/script/common/helper.h
@@ -50,5 +50,8 @@ protected:
* @return read value from Lua or default value if nil
*/
template <typename T>
- static T readParam(lua_State *L, int index, const T &default_value);
+ static inline T readParam(lua_State *L, int index, const T &default_value)
+ {
+ return lua_isnoneornil(L, index) ? default_value : readParam<T>(L, index);
+ }
};
diff --git a/src/script/cpp_api/s_security.cpp b/src/script/cpp_api/s_security.cpp
index 9d65819c0..d5db43db9 100644
--- a/src/script/cpp_api/s_security.cpp
+++ b/src/script/cpp_api/s_security.cpp
@@ -632,7 +632,11 @@ int ScriptApiSecurity::sl_g_loadfile(lua_State *L)
{
#ifndef SERVER
lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_SCRIPTAPI);
+#if INDIRECT_SCRIPTAPI_RIDX
+ ScriptApiBase *script = (ScriptApiBase *) *(void**)(lua_touserdata(L, -1));
+#else
ScriptApiBase *script = (ScriptApiBase *) lua_touserdata(L, -1);
+#endif
lua_pop(L, 1);
// Client implementation
diff --git a/src/script/lua_api/l_base.cpp b/src/script/lua_api/l_base.cpp
index 011434845..fce6282a5 100644
--- a/src/script/lua_api/l_base.cpp
+++ b/src/script/lua_api/l_base.cpp
@@ -105,32 +105,21 @@ bool ModApiBase::registerFunction(lua_State *L, const char *name,
return true;
}
-std::unordered_map<std::string, luaL_Reg> ModApiBase::m_deprecated_wrappers;
-bool ModApiBase::m_error_deprecated_calls = false;
-
-int ModApiBase::l_deprecated_function(lua_State *L)
+int ModApiBase::l_deprecated_function(lua_State *L, const char *good, const char *bad, lua_CFunction func)
{
thread_local std::vector<u64> deprecated_logged;
+ DeprecatedHandlingMode dep_mode = get_deprecated_handling_mode();
+ if (dep_mode == DeprecatedHandlingMode::Ignore)
+ return func(L);
+
u64 start_time = porting::getTimeUs();
lua_Debug ar;
- // Get function name for lookup
- FATAL_ERROR_IF(!lua_getstack(L, 0, &ar), "lua_getstack() failed");
- FATAL_ERROR_IF(!lua_getinfo(L, "n", &ar), "lua_getinfo() failed");
-
- // Combine name with line and script backtrace
+ // Get caller name with line and script backtrace
FATAL_ERROR_IF(!lua_getstack(L, 1, &ar), "lua_getstack() failed");
FATAL_ERROR_IF(!lua_getinfo(L, "Sl", &ar), "lua_getinfo() failed");
- // Get parent class to get the wrappers map
- luaL_checktype(L, 1, LUA_TUSERDATA);
- void *ud = lua_touserdata(L, 1);
- ModApiBase *o = *(ModApiBase**)ud;
-
- // New function and new function name
- auto it = o->m_deprecated_wrappers.find(ar.name);
-
// Get backtrace and hash it to reduce the warning flood
std::string backtrace = ar.short_src;
backtrace.append(":").append(std::to_string(ar.currentline));
@@ -140,45 +129,16 @@ int ModApiBase::l_deprecated_function(lua_State *L)
== deprecated_logged.end()) {
deprecated_logged.emplace_back(hash);
- warningstream << "Call to deprecated function '" << ar.name << "', please use '"
- << it->second.name << "' at " << backtrace << std::endl;
+ warningstream << "Call to deprecated function '" << bad << "', please use '"
+ << good << "' at " << backtrace << std::endl;
- if (m_error_deprecated_calls)
+ if (dep_mode == DeprecatedHandlingMode::Error)
script_error(L, LUA_ERRRUN, NULL, NULL);
}
u64 end_time = porting::getTimeUs();
g_profiler->avg("l_deprecated_function", end_time - start_time);
- return it->second.func(L);
+ return func(L);
}
-void ModApiBase::markAliasDeprecated(luaL_Reg *reg)
-{
- std::string value = g_settings->get("deprecated_lua_api_handling");
- m_error_deprecated_calls = value == "error";
-
- if (!m_error_deprecated_calls && value != "log")
- return;
-
- const char *last_name = nullptr;
- lua_CFunction last_func = nullptr;
-
- // ! Null termination !
- while (reg->func) {
- if (last_func == reg->func) {
- // Duplicate found
- luaL_Reg original_reg;
- // Do not inline struct. Breaks MSVC or is error-prone
- original_reg.name = last_name;
- original_reg.func = reg->func;
- m_deprecated_wrappers.emplace(
- std::pair<std::string, luaL_Reg>(reg->name, original_reg));
- reg->func = l_deprecated_function;
- }
-
- last_func = reg->func;
- last_name = reg->name;
- ++reg;
- }
-}
diff --git a/src/script/lua_api/l_base.h b/src/script/lua_api/l_base.h
index 0cbee7756..9ba50dabf 100644
--- a/src/script/lua_api/l_base.h
+++ b/src/script/lua_api/l_base.h
@@ -42,7 +42,6 @@ class Environment;
class ServerInventoryManager;
class ModApiBase : protected LuaHelper {
-
public:
static ScriptApiBase* getScriptApiBase(lua_State *L);
static Server* getServer(lua_State *L);
@@ -76,10 +75,18 @@ public:
lua_CFunction func,
int top);
- static int l_deprecated_function(lua_State *L);
- static void markAliasDeprecated(luaL_Reg *reg);
-private:
- // <old_name> = { <new_name>, <new_function> }
- static std::unordered_map<std::string, luaL_Reg> m_deprecated_wrappers;
- static bool m_error_deprecated_calls;
+ /**
+ * A wrapper for deprecated functions.
+ *
+ * When called, handles the deprecation according to user settings and then calls `func`.
+ *
+ * @throws Lua Error if required by the user settings.
+ *
+ * @param L Lua state
+ * @param good Name of good function/method
+ * @param bad Name of deprecated function/method
+ * @param func Actual implementation of function
+ * @return value from `func`
+ */
+ static int l_deprecated_function(lua_State *L, const char *good, const char *bad, lua_CFunction func);
};
diff --git a/src/script/lua_api/l_camera.cpp b/src/script/lua_api/l_camera.cpp
index bfa60be67..40251154c 100644
--- a/src/script/lua_api/l_camera.cpp
+++ b/src/script/lua_api/l_camera.cpp
@@ -63,7 +63,8 @@ int LuaCamera::l_set_camera_mode(lua_State *L)
return 0;
camera->setCameraMode((CameraMode)((int)lua_tonumber(L, 2)));
- playercao->setVisible(camera->getCameraMode() > CAMERA_MODE_FIRST);
+ // Make the player visible depending on camera mode.
+ playercao->updateMeshCulling();
playercao->setChildrenVisible(camera->getCameraMode() > CAMERA_MODE_FIRST);
return 0;
}
diff --git a/src/script/lua_api/l_env.cpp b/src/script/lua_api/l_env.cpp
index ee384ad10..dbb0a5ab7 100644
--- a/src/script/lua_api/l_env.cpp
+++ b/src/script/lua_api/l_env.cpp
@@ -407,6 +407,46 @@ int ModApiEnvMod::l_get_node_light(lua_State *L)
return 1;
}
+
+// get_natural_light(pos, timeofday)
+// pos = {x=num, y=num, z=num}
+// timeofday: nil = current time, 0 = night, 0.5 = day
+int ModApiEnvMod::l_get_natural_light(lua_State *L)
+{
+ GET_ENV_PTR;
+
+ v3s16 pos = read_v3s16(L, 1);
+
+ bool is_position_ok;
+ MapNode n = env->getMap().getNode(pos, &is_position_ok);
+ if (!is_position_ok)
+ return 0;
+
+ // If the daylight is 0, nothing needs to be calculated
+ u8 daylight = n.param1 & 0x0f;
+ if (daylight == 0) {
+ lua_pushinteger(L, 0);
+ return 1;
+ }
+
+ u32 time_of_day;
+ if (lua_isnumber(L, 2)) {
+ time_of_day = 24000.0 * lua_tonumber(L, 2);
+ time_of_day %= 24000;
+ } else {
+ time_of_day = env->getTimeOfDay();
+ }
+ u32 dnr = time_to_daynight_ratio(time_of_day, true);
+
+ // If it's the same as the artificial light, the sunlight needs to be
+ // searched for because the value may not emanate from the sun
+ if (daylight == n.param1 >> 4)
+ daylight = env->findSunlight(pos);
+
+ lua_pushinteger(L, dnr * daylight / 1000);
+ return 1;
+}
+
// place_node(pos, node)
// pos = {x=num, y=num, z=num}
int ModApiEnvMod::l_place_node(lua_State *L)
@@ -711,8 +751,9 @@ int ModApiEnvMod::l_set_timeofday(lua_State *L)
// Do it
float timeofday_f = readParam<float>(L, 1);
- sanity_check(timeofday_f >= 0.0 && timeofday_f <= 1.0);
- int timeofday_mh = (int)(timeofday_f * 24000.0);
+ luaL_argcheck(L, timeofday_f >= 0.0f && timeofday_f <= 1.0f, 1,
+ "value must be between 0 and 1");
+ int timeofday_mh = (int)(timeofday_f * 24000.0f);
// This should be set directly in the environment but currently
// such changes aren't immediately sent to the clients, so call
// the server instead.
@@ -1498,9 +1539,9 @@ int ModApiEnvMod::l_get_translated_string(lua_State * L)
GET_ENV_PTR;
std::string lang_code = luaL_checkstring(L, 1);
std::string string = luaL_checkstring(L, 2);
- getServer(L)->loadTranslationLanguage(lang_code);
- string = wide_to_utf8(translate_string(utf8_to_wide(string),
- &(*g_server_translations)[lang_code]));
+
+ auto *translations = getServer(L)->getTranslationLanguage(lang_code);
+ string = wide_to_utf8(translate_string(utf8_to_wide(string), translations));
lua_pushstring(L, string.c_str());
return 1;
}
@@ -1516,6 +1557,7 @@ void ModApiEnvMod::Initialize(lua_State *L, int top)
API_FCT(get_node);
API_FCT(get_node_or_nil);
API_FCT(get_node_light);
+ API_FCT(get_natural_light);
API_FCT(place_node);
API_FCT(dig_node);
API_FCT(punch_node);
diff --git a/src/script/lua_api/l_env.h b/src/script/lua_api/l_env.h
index e1b89494b..ad9a0f509 100644
--- a/src/script/lua_api/l_env.h
+++ b/src/script/lua_api/l_env.h
@@ -56,6 +56,11 @@ private:
// timeofday: nil = current time, 0 = night, 0.5 = day
static int l_get_node_light(lua_State *L);
+ // get_natural_light(pos, timeofday)
+ // pos = {x=num, y=num, z=num}
+ // timeofday: nil = current time, 0 = night, 0.5 = day
+ static int l_get_natural_light(lua_State *L);
+
// place_node(pos, node)
// pos = {x=num, y=num, z=num}
static int l_place_node(lua_State *L);
diff --git a/src/script/lua_api/l_http.cpp b/src/script/lua_api/l_http.cpp
index 84837e71b..5a28cb369 100644
--- a/src/script/lua_api/l_http.cpp
+++ b/src/script/lua_api/l_http.cpp
@@ -49,17 +49,40 @@ void ModApiHttp::read_http_fetch_request(lua_State *L, HTTPFetchRequest &req)
req.multipart = getboolfield_default(L, 1, "multipart", false);
req.timeout = getintfield_default(L, 1, "timeout", 3) * 1000;
- // post_data: if table, post form data, otherwise raw data
+ lua_getfield(L, 1, "method");
+ if (lua_isstring(L, -1)) {
+ std::string mth = getstringfield_default(L, 1, "method", "");
+ if (mth == "GET")
+ req.method = HTTP_GET;
+ else if (mth == "POST")
+ req.method = HTTP_POST;
+ else if (mth == "PUT")
+ req.method = HTTP_PUT;
+ else if (mth == "DELETE")
+ req.method = HTTP_DELETE;
+ }
+ lua_pop(L, 1);
+
+ // post_data: if table, post form data, otherwise raw data DEPRECATED use data and method instead
lua_getfield(L, 1, "post_data");
+ if (lua_isnil(L, 2)) {
+ lua_pop(L, 1);
+ lua_getfield(L, 1, "data");
+ }
+ else {
+ req.method = HTTP_POST;
+ }
+
if (lua_istable(L, 2)) {
lua_pushnil(L);
while (lua_next(L, 2) != 0) {
- req.post_fields[readParam<std::string>(L, -2)] = readParam<std::string>(L, -1);
+ req.fields[readParam<std::string>(L, -2)] = readParam<std::string>(L, -1);
lua_pop(L, 1);
}
} else if (lua_isstring(L, 2)) {
- req.post_data = readParam<std::string>(L, 2);
+ req.raw_data = readParam<std::string>(L, 2);
}
+
lua_pop(L, 1);
lua_getfield(L, 1, "extra_headers");
diff --git a/src/script/lua_api/l_http.h b/src/script/lua_api/l_http.h
index de6e51b37..c3a2a5276 100644
--- a/src/script/lua_api/l_http.h
+++ b/src/script/lua_api/l_http.h
@@ -32,10 +32,10 @@ private:
static void read_http_fetch_request(lua_State *L, HTTPFetchRequest &req);
static void push_http_fetch_result(lua_State *L, HTTPFetchResult &res, bool completed = true);
- // http_fetch_sync({url=, timeout=, post_data=})
+ // http_fetch_sync({url=, timeout=, data=})
static int l_http_fetch_sync(lua_State *L);
- // http_fetch_async({url=, timeout=, post_data=})
+ // http_fetch_async({url=, timeout=, data=})
static int l_http_fetch_async(lua_State *L);
// http_fetch_async_get(handle)
diff --git a/src/script/lua_api/l_internal.h b/src/script/lua_api/l_internal.h
index a86eeaf79..672e535ca 100644
--- a/src/script/lua_api/l_internal.h
+++ b/src/script/lua_api/l_internal.h
@@ -29,7 +29,16 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "common/c_internal.h"
#define luamethod(class, name) {#name, class::l_##name}
-#define luamethod_aliased(class, name, alias) {#name, class::l_##name}, {#alias, class::l_##name}
+
+#define luamethod_dep(class, good, bad) \
+ {#bad, [](lua_State *L) -> int { \
+ return l_deprecated_function(L, #good, #bad, &class::l_##good); \
+ }}
+
+#define luamethod_aliased(class, good, bad) \
+ luamethod(class, good), \
+ luamethod_dep(class, good, bad)
+
#define API_FCT(name) registerFunction(L, #name, l_##name, top)
// For future use
diff --git a/src/script/lua_api/l_inventory.cpp b/src/script/lua_api/l_inventory.cpp
index e41b5cb41..434d0a76c 100644
--- a/src/script/lua_api/l_inventory.cpp
+++ b/src/script/lua_api/l_inventory.cpp
@@ -280,6 +280,7 @@ int InvRef::l_set_lists(lua_State *L)
Server *server = getServer(L);
lua_pushnil(L);
+ luaL_checktype(L, 2, LUA_TTABLE);
while (lua_next(L, 2)) {
const char *listname = lua_tostring(L, -2);
read_inventory_list(L, -1, tempInv, listname, server);
diff --git a/src/script/lua_api/l_item.cpp b/src/script/lua_api/l_item.cpp
index bea7d122f..b098eccf0 100644
--- a/src/script/lua_api/l_item.cpp
+++ b/src/script/lua_api/l_item.cpp
@@ -198,6 +198,16 @@ int LuaItemStack::l_get_description(lua_State *L)
return 1;
}
+// get_short_description(self)
+int LuaItemStack::l_get_short_description(lua_State *L)
+{
+ NO_MAP_LOCK_REQUIRED;
+ LuaItemStack *o = checkobject(L, 1);
+ std::string desc = o->m_stack.getShortDescription(getGameDef(L)->idef());
+ lua_pushstring(L, desc.c_str());
+ return 1;
+}
+
// clear(self) -> true
int LuaItemStack::l_clear(lua_State *L)
{
@@ -498,6 +508,7 @@ const luaL_Reg LuaItemStack::methods[] = {
luamethod(LuaItemStack, get_metadata),
luamethod(LuaItemStack, set_metadata),
luamethod(LuaItemStack, get_description),
+ luamethod(LuaItemStack, get_short_description),
luamethod(LuaItemStack, clear),
luamethod(LuaItemStack, replace),
luamethod(LuaItemStack, to_string),
@@ -564,7 +575,8 @@ int ModApiItemMod::l_register_item_raw(lua_State *L)
// Read the node definition (content features) and register it
if (def.type == ITEM_NODE) {
- ContentFeatures f = read_content_features(L, table);
+ ContentFeatures f;
+ read_content_features(L, f, table);
// when a mod reregisters ignore, only texture changes and such should
// be done
if (f.name == "ignore")
diff --git a/src/script/lua_api/l_item.h b/src/script/lua_api/l_item.h
index 98744c071..16878c101 100644
--- a/src/script/lua_api/l_item.h
+++ b/src/script/lua_api/l_item.h
@@ -72,6 +72,9 @@ private:
// get_description(self)
static int l_get_description(lua_State *L);
+ // get_short_description(self)
+ static int l_get_short_description(lua_State *L);
+
// clear(self) -> true
static int l_clear(lua_State *L);
diff --git a/src/script/lua_api/l_localplayer.cpp b/src/script/lua_api/l_localplayer.cpp
index 1cb6ff156..3f4147227 100644
--- a/src/script/lua_api/l_localplayer.cpp
+++ b/src/script/lua_api/l_localplayer.cpp
@@ -303,8 +303,8 @@ int LuaLocalPlayer::l_get_control(lua_State *L)
set("aux1", c.aux1);
set("sneak", c.sneak);
set("zoom", c.zoom);
- set("LMB", c.LMB);
- set("RMB", c.RMB);
+ set("dig", c.dig);
+ set("place", c.place);
return 1;
}
diff --git a/src/script/lua_api/l_mainmenu.cpp b/src/script/lua_api/l_mainmenu.cpp
index f32c477c2..0aa2760e9 100644
--- a/src/script/lua_api/l_mainmenu.cpp
+++ b/src/script/lua_api/l_mainmenu.cpp
@@ -29,7 +29,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "porting.h"
#include "filesys.h"
#include "convert_json.h"
-#include "content/packages.h"
#include "content/content.h"
#include "content/subgames.h"
#include "serverlist.h"
@@ -618,7 +617,7 @@ int ModApiMainMenu::l_create_world(lua_State *L)
std::string path = porting::path_user + DIR_DELIM
"worlds" + DIR_DELIM
- + name;
+ + sanitizeDirName(name, "world_");
std::vector<SubgameSpec> games = getAvailableGames();
@@ -626,10 +625,11 @@ int ModApiMainMenu::l_create_world(lua_State *L)
(gameidx < (int) games.size())) {
// Create world if it doesn't exist
- if (!loadGameConfAndInitWorld(path, games[gameidx])) {
- lua_pushstring(L, "Failed to initialize world");
- } else {
+ try {
+ loadGameConfAndInitWorld(path, name, games[gameidx], true);
lua_pushnil(L);
+ } catch (const BaseException &e) {
+ lua_pushstring(L, (std::string("Failed to initialize world: ") + e.what()).c_str());
}
} else {
lua_pushstring(L, "Invalid game index");
diff --git a/src/script/lua_api/l_minimap.cpp b/src/script/lua_api/l_minimap.cpp
index 5fba76eb8..3bbb6e5e3 100644
--- a/src/script/lua_api/l_minimap.cpp
+++ b/src/script/lua_api/l_minimap.cpp
@@ -89,7 +89,7 @@ int LuaMinimap::l_get_mode(lua_State *L)
LuaMinimap *ref = checkobject(L, 1);
Minimap *m = getobject(ref);
- lua_pushinteger(L, m->getMinimapMode());
+ lua_pushinteger(L, m->getModeIndex());
return 1;
}
@@ -98,13 +98,11 @@ int LuaMinimap::l_set_mode(lua_State *L)
LuaMinimap *ref = checkobject(L, 1);
Minimap *m = getobject(ref);
- s32 mode = lua_tointeger(L, 2);
- if (mode < MINIMAP_MODE_OFF ||
- mode >= MINIMAP_MODE_COUNT) {
+ u32 mode = lua_tointeger(L, 2);
+ if (mode >= m->getMaxModeIndex())
return 0;
- }
- m->setMinimapMode((MinimapMode) mode);
+ m->setModeIndex(mode);
return 1;
}
@@ -140,8 +138,11 @@ int LuaMinimap::l_show(lua_State *L)
LuaMinimap *ref = checkobject(L, 1);
Minimap *m = getobject(ref);
- if (m->getMinimapMode() == MINIMAP_MODE_OFF)
- m->setMinimapMode(MINIMAP_MODE_SURFACEx1);
+ // This is not very adapted to new minimap mode management. Btw, tried
+ // to do something compatible.
+
+ if (m->getModeIndex() == 0 && m->getMaxModeIndex() > 0)
+ m->setModeIndex(1);
client->showMinimap(true);
return 1;
@@ -155,8 +156,11 @@ int LuaMinimap::l_hide(lua_State *L)
LuaMinimap *ref = checkobject(L, 1);
Minimap *m = getobject(ref);
- if (m->getMinimapMode() != MINIMAP_MODE_OFF)
- m->setMinimapMode(MINIMAP_MODE_OFF);
+ // This is not very adapted to new minimap mode management. Btw, tried
+ // to do something compatible.
+
+ if (m->getModeIndex() != 0)
+ m->setModeIndex(0);
client->showMinimap(false);
return 1;
diff --git a/src/script/lua_api/l_noise.cpp b/src/script/lua_api/l_noise.cpp
index 9aeb15709..e0861126a 100644
--- a/src/script/lua_api/l_noise.cpp
+++ b/src/script/lua_api/l_noise.cpp
@@ -122,7 +122,6 @@ void LuaPerlinNoise::Register(lua_State *L)
lua_pop(L, 1);
- markAliasDeprecated(methods);
luaL_openlib(L, 0, methods, 0);
lua_pop(L, 1);
@@ -381,7 +380,6 @@ void LuaPerlinNoiseMap::Register(lua_State *L)
lua_pop(L, 1);
- markAliasDeprecated(methods);
luaL_openlib(L, 0, methods, 0);
lua_pop(L, 1);
diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp
index e7394133a..bc59bd55c 100644
--- a/src/script/lua_api/l_object.cpp
+++ b/src/script/lua_api/l_object.cpp
@@ -44,43 +44,44 @@ ObjectRef* ObjectRef::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);
+ if (ud == nullptr)
+ luaL_typerror(L, narg, className);
return *(ObjectRef**)ud; // unbox pointer
}
ServerActiveObject* ObjectRef::getobject(ObjectRef *ref)
{
- ServerActiveObject *co = ref->m_object;
- if (co && co->isGone())
- return NULL;
- return co;
+ ServerActiveObject *sao = ref->m_object;
+ if (sao && sao->isGone())
+ return nullptr;
+ return sao;
}
LuaEntitySAO* ObjectRef::getluaobject(ObjectRef *ref)
{
- ServerActiveObject *obj = getobject(ref);
- if (obj == NULL)
- return NULL;
- if (obj->getType() != ACTIVEOBJECT_TYPE_LUAENTITY)
- return NULL;
- return (LuaEntitySAO*)obj;
+ ServerActiveObject *sao = getobject(ref);
+ if (sao == nullptr)
+ return nullptr;
+ if (sao->getType() != ACTIVEOBJECT_TYPE_LUAENTITY)
+ return nullptr;
+ return (LuaEntitySAO*)sao;
}
PlayerSAO* ObjectRef::getplayersao(ObjectRef *ref)
{
- ServerActiveObject *obj = getobject(ref);
- if (obj == NULL)
- return NULL;
- if (obj->getType() != ACTIVEOBJECT_TYPE_PLAYER)
- return NULL;
- return (PlayerSAO*)obj;
+ ServerActiveObject *sao = getobject(ref);
+ if (sao == nullptr)
+ return nullptr;
+ if (sao->getType() != ACTIVEOBJECT_TYPE_PLAYER)
+ return nullptr;
+ return (PlayerSAO*)sao;
}
RemotePlayer *ObjectRef::getplayer(ObjectRef *ref)
{
PlayerSAO *playersao = getplayersao(ref);
- if (playersao == NULL)
- return NULL;
+ if (playersao == nullptr)
+ return nullptr;
return playersao->getPlayer();
}
@@ -88,9 +89,8 @@ RemotePlayer *ObjectRef::getplayer(ObjectRef *ref)
// garbage collector
int ObjectRef::gc_object(lua_State *L) {
- ObjectRef *o = *(ObjectRef **)(lua_touserdata(L, 1));
- //infostream<<"ObjectRef::gc_object: o="<<o<<std::endl;
- delete o;
+ ObjectRef *obj = *(ObjectRef **)(lua_touserdata(L, 1));
+ delete obj;
return 0;
}
@@ -100,29 +100,30 @@ int ObjectRef::l_remove(lua_State *L)
GET_ENV_PTR;
ObjectRef *ref = checkobject(L, 1);
- ServerActiveObject *co = getobject(ref);
- if (co == NULL)
+ ServerActiveObject *sao = getobject(ref);
+ if (sao == nullptr)
return 0;
- if (co->getType() == ACTIVEOBJECT_TYPE_PLAYER)
+ if (sao->getType() == ACTIVEOBJECT_TYPE_PLAYER)
return 0;
- co->clearChildAttachments();
- co->clearParentAttachment();
+ sao->clearChildAttachments();
+ sao->clearParentAttachment();
- verbosestream << "ObjectRef::l_remove(): id=" << co->getId() << std::endl;
- co->m_pending_removal = true;
+ verbosestream << "ObjectRef::l_remove(): id=" << sao->getId() << std::endl;
+ sao->m_pending_removal = true;
return 0;
}
// get_pos(self)
-// returns: {x=num, y=num, z=num}
int ObjectRef::l_get_pos(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
- ServerActiveObject *co = getobject(ref);
- if (co == NULL) return 0;
- push_v3f(L, co->getBasePosition() / BS);
+ ServerActiveObject *sao = getobject(ref);
+ if (sao == nullptr)
+ return 0;
+
+ push_v3f(L, sao->getBasePosition() / BS);
return 1;
}
@@ -131,28 +132,29 @@ int ObjectRef::l_set_pos(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
- ServerActiveObject *co = getobject(ref);
- if (co == NULL) return 0;
- // pos
+ ServerActiveObject *sao = getobject(ref);
+ if (sao == nullptr)
+ return 0;
+
v3f pos = checkFloatPos(L, 2);
- // Do it
- co->setPos(pos);
+
+ sao->setPos(pos);
return 0;
}
-// move_to(self, pos, continuous=false)
+// move_to(self, pos, continuous)
int ObjectRef::l_move_to(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
- ServerActiveObject *co = getobject(ref);
- if (co == NULL) return 0;
- // pos
+ ServerActiveObject *sao = getobject(ref);
+ if (sao == nullptr)
+ return 0;
+
v3f pos = checkFloatPos(L, 2);
- // continuous
bool continuous = readParam<bool>(L, 3);
- // Do it
- co->moveTo(pos, continuous);
+
+ sao->moveTo(pos, continuous);
return 0;
}
@@ -162,32 +164,26 @@ int ObjectRef::l_punch(lua_State *L)
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
ObjectRef *puncher_ref = checkobject(L, 2);
- ServerActiveObject *co = getobject(ref);
+ ServerActiveObject *sao = getobject(ref);
ServerActiveObject *puncher = getobject(puncher_ref);
- if (!co || !puncher)
+ if (sao == nullptr || puncher == nullptr)
return 0;
- v3f dir;
- if (lua_type(L, 5) != LUA_TTABLE)
- dir = co->getBasePosition() - puncher->getBasePosition();
- else
- dir = read_v3f(L, 5);
- float time_from_last_punch = 1000000;
- if (lua_isnumber(L, 3))
- time_from_last_punch = lua_tonumber(L, 3);
+
+ float time_from_last_punch = readParam<float>(L, 3, 1000000.0f);
ToolCapabilities toolcap = read_tool_capabilities(L, 4);
- dir.normalize();
+ v3f dir = readParam<v3f>(L, 5, sao->getBasePosition() - puncher->getBasePosition());
- u16 src_original_hp = co->getHP();
+ dir.normalize();
+ u16 src_original_hp = sao->getHP();
u16 dst_origin_hp = puncher->getHP();
- // Do it
- u16 wear = co->punch(dir, &toolcap, puncher, time_from_last_punch);
+ u16 wear = sao->punch(dir, &toolcap, puncher, time_from_last_punch);
lua_pushnumber(L, wear);
// If the punched is a player, and its HP changed
- if (src_original_hp != co->getHP() &&
- co->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
- getServer(L)->SendPlayerHPOrDie((PlayerSAO *)co,
+ if (src_original_hp != sao->getHP() &&
+ sao->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
+ getServer(L)->SendPlayerHPOrDie((PlayerSAO *)sao,
PlayerHPChangeReason(PlayerHPChangeReason::PLAYER_PUNCH, puncher));
}
@@ -195,45 +191,38 @@ int ObjectRef::l_punch(lua_State *L)
if (dst_origin_hp != puncher->getHP() &&
puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
getServer(L)->SendPlayerHPOrDie((PlayerSAO *)puncher,
- PlayerHPChangeReason(PlayerHPChangeReason::PLAYER_PUNCH, co));
+ PlayerHPChangeReason(PlayerHPChangeReason::PLAYER_PUNCH, sao));
}
return 1;
}
-// right_click(self, clicker); clicker = an another ObjectRef
+// right_click(self, clicker)
int ObjectRef::l_right_click(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
ObjectRef *ref2 = checkobject(L, 2);
- ServerActiveObject *co = getobject(ref);
- ServerActiveObject *co2 = getobject(ref2);
- if (co == NULL) return 0;
- if (co2 == NULL) return 0;
- // Do it
- co->rightClick(co2);
+ ServerActiveObject *sao = getobject(ref);
+ ServerActiveObject *sao2 = getobject(ref2);
+ if (sao == nullptr || sao2 == nullptr)
+ return 0;
+
+ sao->rightClick(sao2);
return 0;
}
-// set_hp(self, hp)
-// hp = number of hitpoints (2 * number of hearts)
-// returns: nil
+// set_hp(self, hp, reason)
int ObjectRef::l_set_hp(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
-
- // Get Object
ObjectRef *ref = checkobject(L, 1);
- luaL_checknumber(L, 2);
- ServerActiveObject *co = getobject(ref);
- if (co == NULL)
+ ServerActiveObject *sao = getobject(ref);
+ if (sao == nullptr)
return 0;
- // Get HP
- int hp = lua_tonumber(L, 2);
-
- // Get Reason
+ int hp = readParam<float>(L, 2);
PlayerHPChangeReason reason(PlayerHPChangeReason::SET_HP);
+
reason.from_mod = true;
if (lua_istable(L, 3)) {
lua_pushvalue(L, 3);
@@ -248,35 +237,28 @@ int ObjectRef::l_set_hp(lua_State *L)
reason.lua_reference = luaL_ref(L, LUA_REGISTRYINDEX);
}
- // Do it
- co->setHP(hp, reason);
- if (co->getType() == ACTIVEOBJECT_TYPE_PLAYER)
- getServer(L)->SendPlayerHPOrDie((PlayerSAO *)co, reason);
-
+ sao->setHP(hp, reason);
+ if (sao->getType() == ACTIVEOBJECT_TYPE_PLAYER)
+ getServer(L)->SendPlayerHPOrDie((PlayerSAO *)sao, reason);
if (reason.hasLuaReference())
luaL_unref(L, LUA_REGISTRYINDEX, reason.lua_reference);
-
- // Return
return 0;
}
// get_hp(self)
-// returns: number of hitpoints (2 * number of hearts)
-// 0 if not applicable to this type of object
int ObjectRef::l_get_hp(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
- ServerActiveObject *co = getobject(ref);
- if (co == NULL) {
+ ServerActiveObject *sao = getobject(ref);
+ if (sao == nullptr) {
// Default hp is 1
lua_pushnumber(L, 1);
return 1;
}
- int hp = co->getHP();
- /*infostream<<"ObjectRef::l_get_hp(): id="<<co->getId()
- <<" hp="<<hp<<std::endl;*/
- // Return
+
+ int hp = sao->getHP();
+
lua_pushnumber(L, hp);
return 1;
}
@@ -286,11 +268,12 @@ int ObjectRef::l_get_inventory(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
- ServerActiveObject *co = getobject(ref);
- if (co == NULL) return 0;
- // Do it
- InventoryLocation loc = co->getInventoryLocation();
- if (getServerInventoryMgr(L)->getInventory(loc) != NULL)
+ ServerActiveObject *sao = getobject(ref);
+ if (sao == nullptr)
+ return 0;
+
+ InventoryLocation loc = sao->getInventoryLocation();
+ if (getServerInventoryMgr(L)->getInventory(loc) != nullptr)
InvRef::create(L, loc);
else
lua_pushnil(L); // An object may have no inventory (nil)
@@ -302,11 +285,11 @@ int ObjectRef::l_get_wield_list(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
- ServerActiveObject *co = getobject(ref);
- if (!co)
+ ServerActiveObject *sao = getobject(ref);
+ if (sao == nullptr)
return 0;
- lua_pushstring(L, co->getWieldList().c_str());
+ lua_pushstring(L, sao->getWieldList().c_str());
return 1;
}
@@ -315,11 +298,11 @@ int ObjectRef::l_get_wield_index(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
- ServerActiveObject *co = getobject(ref);
- if (!co)
+ ServerActiveObject *sao = getobject(ref);
+ if (sao == nullptr)
return 0;
- lua_pushinteger(L, co->getWieldIndex() + 1);
+ lua_pushinteger(L, sao->getWieldIndex() + 1);
return 1;
}
@@ -328,31 +311,33 @@ int ObjectRef::l_get_wielded_item(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
- ServerActiveObject *co = getobject(ref);
- if (!co) {
+ ServerActiveObject *sao = getobject(ref);
+ if (sao == nullptr) {
// Empty ItemStack
LuaItemStack::create(L, ItemStack());
return 1;
}
ItemStack selected_item;
- co->getWieldedItem(&selected_item, nullptr);
+ sao->getWieldedItem(&selected_item, nullptr);
LuaItemStack::create(L, selected_item);
return 1;
}
-// set_wielded_item(self, itemstack or itemstring or table or nil)
+// set_wielded_item(self, item)
int ObjectRef::l_set_wielded_item(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
- ServerActiveObject *co = getobject(ref);
- if (co == NULL) return 0;
- // Do it
+ ServerActiveObject *sao = getobject(ref);
+ if (sao == nullptr)
+ return 0;
+
ItemStack item = read_item(L, 2, getServer(L)->idef());
- bool success = co->setWieldedItem(item);
- if (success && co->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
- getServer(L)->SendInventory((PlayerSAO *)co, true);
+
+ bool success = sao->setWieldedItem(item);
+ if (success && sao->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
+ getServer(L)->SendInventory((PlayerSAO *)sao, true);
}
lua_pushboolean(L, success);
return 1;
@@ -363,12 +348,14 @@ int ObjectRef::l_set_armor_groups(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
- ServerActiveObject *co = getobject(ref);
- if (co == NULL) return 0;
- // Do it
+ ServerActiveObject *sao = getobject(ref);
+ if (sao == nullptr)
+ return 0;
+
ItemGroupList groups;
+
read_groups(L, 2, groups);
- co->setArmorGroups(groups);
+ sao->setArmorGroups(groups);
return 0;
}
@@ -377,77 +364,11 @@ int ObjectRef::l_get_armor_groups(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
- ServerActiveObject *co = getobject(ref);
- if (co == NULL)
+ ServerActiveObject *sao = getobject(ref);
+ if (sao == nullptr)
return 0;
- // Do it
- push_groups(L, co->getArmorGroups());
- return 1;
-}
-// set_physics_override(self, physics_override_speed, physics_override_jump,
-// physics_override_gravity, sneak, sneak_glitch, new_move)
-int ObjectRef::l_set_physics_override(lua_State *L)
-{
- NO_MAP_LOCK_REQUIRED;
- ObjectRef *ref = checkobject(L, 1);
- PlayerSAO *co = (PlayerSAO *) getobject(ref);
- if (co == NULL) return 0;
- // Do it
- if (lua_istable(L, 2)) {
- co->m_physics_override_speed = getfloatfield_default(
- L, 2, "speed", co->m_physics_override_speed);
- co->m_physics_override_jump = getfloatfield_default(
- L, 2, "jump", co->m_physics_override_jump);
- co->m_physics_override_gravity = getfloatfield_default(
- L, 2, "gravity", co->m_physics_override_gravity);
- co->m_physics_override_sneak = getboolfield_default(
- L, 2, "sneak", co->m_physics_override_sneak);
- co->m_physics_override_sneak_glitch = getboolfield_default(
- L, 2, "sneak_glitch", co->m_physics_override_sneak_glitch);
- co->m_physics_override_new_move = getboolfield_default(
- L, 2, "new_move", co->m_physics_override_new_move);
- co->m_physics_override_sent = false;
- } else {
- // old, non-table format
- if (!lua_isnil(L, 2)) {
- co->m_physics_override_speed = lua_tonumber(L, 2);
- co->m_physics_override_sent = false;
- }
- if (!lua_isnil(L, 3)) {
- co->m_physics_override_jump = lua_tonumber(L, 3);
- co->m_physics_override_sent = false;
- }
- if (!lua_isnil(L, 4)) {
- co->m_physics_override_gravity = lua_tonumber(L, 4);
- co->m_physics_override_sent = false;
- }
- }
- return 0;
-}
-
-// get_physics_override(self)
-int ObjectRef::l_get_physics_override(lua_State *L)
-{
- NO_MAP_LOCK_REQUIRED;
- ObjectRef *ref = checkobject(L, 1);
- PlayerSAO *co = (PlayerSAO *)getobject(ref);
- if (co == NULL)
- return 0;
- // Do it
- lua_newtable(L);
- lua_pushnumber(L, co->m_physics_override_speed);
- lua_setfield(L, -2, "speed");
- lua_pushnumber(L, co->m_physics_override_jump);
- lua_setfield(L, -2, "jump");
- lua_pushnumber(L, co->m_physics_override_gravity);
- lua_setfield(L, -2, "gravity");
- lua_pushboolean(L, co->m_physics_override_sneak);
- lua_setfield(L, -2, "sneak");
- lua_pushboolean(L, co->m_physics_override_sneak_glitch);
- lua_setfield(L, -2, "sneak_glitch");
- lua_pushboolean(L, co->m_physics_override_new_move);
- lua_setfield(L, -2, "new_move");
+ push_groups(L, sao->getArmorGroups());
return 1;
}
@@ -456,22 +377,16 @@ int ObjectRef::l_set_animation(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
- ServerActiveObject *co = getobject(ref);
- if (co == NULL) return 0;
- // Do it
- v2f frames = v2f(1, 1);
- if (!lua_isnil(L, 2))
- frames = readParam<v2f>(L, 2);
- float frame_speed = 15;
- if (!lua_isnil(L, 3))
- frame_speed = lua_tonumber(L, 3);
- float frame_blend = 0;
- if (!lua_isnil(L, 4))
- frame_blend = lua_tonumber(L, 4);
- bool frame_loop = true;
- if (lua_isboolean(L, 5))
- frame_loop = readParam<bool>(L, 5);
- co->setAnimation(frames, frame_speed, frame_blend, frame_loop);
+ ServerActiveObject *sao = getobject(ref);
+ if (sao == nullptr)
+ return 0;
+
+ v2f frame_range = readParam<v2f>(L, 2, v2f(1, 1));
+ float frame_speed = readParam<float>(L, 3, 15.0f);
+ float frame_blend = readParam<float>(L, 4, 0.0f);
+ bool frame_loop = readParam<bool>(L, 5, true);
+
+ sao->setAnimation(frame_range, frame_speed, frame_blend, frame_loop);
return 0;
}
@@ -480,16 +395,16 @@ int ObjectRef::l_get_animation(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
- ServerActiveObject *co = getobject(ref);
- if (co == NULL)
+ ServerActiveObject *sao = getobject(ref);
+ if (sao == nullptr)
return 0;
- // Do it
+
v2f frames = v2f(1,1);
float frame_speed = 15;
float frame_blend = 0;
bool frame_loop = true;
- co->getAnimation(&frames, &frame_speed, &frame_blend, &frame_loop);
+ sao->getAnimation(&frames, &frame_speed, &frame_blend, &frame_loop);
push_v2f(L, frames);
lua_pushnumber(L, frame_speed);
lua_pushnumber(L, frame_blend);
@@ -497,23 +412,21 @@ int ObjectRef::l_get_animation(lua_State *L)
return 4;
}
-// set_local_animation(self, {stand/idle}, {walk}, {dig}, {walk+dig}, frame_speed)
+// set_local_animation(self, idle, walk, dig, walk_while_dig, frame_speed)
int ObjectRef::l_set_local_animation(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
RemotePlayer *player = getplayer(ref);
- if (player == NULL)
+ if (player == nullptr)
return 0;
- // Do it
+
v2s32 frames[4];
for (int i=0;i<4;i++) {
if (!lua_isnil(L, 2+1))
frames[i] = read_v2s32(L, 2+i);
}
- float frame_speed = 30;
- if (!lua_isnil(L, 6))
- frame_speed = lua_tonumber(L, 6);
+ float frame_speed = readParam<float>(L, 6, 30.0f);
getServer(L)->setLocalPlayerAnimations(player, frames, frame_speed);
lua_pushboolean(L, true);
@@ -526,7 +439,7 @@ int ObjectRef::l_get_local_animation(lua_State *L)
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
RemotePlayer *player = getplayer(ref);
- if (player == NULL)
+ if (player == nullptr)
return 0;
v2s32 frames[4];
@@ -541,27 +454,22 @@ int ObjectRef::l_get_local_animation(lua_State *L)
return 5;
}
-// set_eye_offset(self, v3f first pv, v3f third pv)
+// set_eye_offset(self, firstperson, thirdperson)
int ObjectRef::l_set_eye_offset(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
RemotePlayer *player = getplayer(ref);
- if (player == NULL)
+ if (player == nullptr)
return 0;
- // Do it
- v3f offset_first = v3f(0, 0, 0);
- v3f offset_third = v3f(0, 0, 0);
- if (!lua_isnil(L, 2))
- offset_first = read_v3f(L, 2);
- if (!lua_isnil(L, 3))
- offset_third = read_v3f(L, 3);
+ v3f offset_first = read_v3f(L, 2);
+ v3f offset_third = read_v3f(L, 3);
// Prevent abuse of offset values (keep player always visible)
offset_third.X = rangelim(offset_third.X,-10,10);
offset_third.Z = rangelim(offset_third.Z,-5,5);
- /* TODO: if possible: improve the camera colision detetion to allow Y <= -1.5) */
+ /* TODO: if possible: improve the camera collision detection to allow Y <= -1.5) */
offset_third.Y = rangelim(offset_third.Y,-10,15); //1.5*BS
getServer(L)->setPlayerEyeOffset(player, offset_first, offset_third);
@@ -575,9 +483,9 @@ int ObjectRef::l_get_eye_offset(lua_State *L)
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
RemotePlayer *player = getplayer(ref);
- if (player == NULL)
+ if (player == nullptr)
return 0;
- // Do it
+
push_v3f(L, player->eye_offset_first);
push_v3f(L, player->eye_offset_third);
return 2;
@@ -588,14 +496,14 @@ int ObjectRef::l_send_mapblock(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
-
RemotePlayer *player = getplayer(ref);
- if (!player)
+ if (player == nullptr)
return 0;
- v3s16 p = read_v3s16(L, 2);
+
+ v3s16 pos = read_v3s16(L, 2);
session_t peer_id = player->getPeerId();
- bool r = getServer(L)->SendBlock(peer_id, p);
+ bool r = getServer(L)->SendBlock(peer_id, pos);
lua_pushboolean(L, r);
return 1;
@@ -606,14 +514,13 @@ int ObjectRef::l_set_animation_frame_speed(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
- ServerActiveObject *co = getobject(ref);
- if (co == NULL)
+ ServerActiveObject *sao = getobject(ref);
+ if (sao == nullptr)
return 0;
- // Do it
if (!lua_isnil(L, 2)) {
- float frame_speed = lua_tonumber(L, 2);
- co->setAnimationSpeed(frame_speed);
+ float frame_speed = readParam<float>(L, 2);
+ sao->setAnimationSpeed(frame_speed);
lua_pushboolean(L, true);
} else {
lua_pushboolean(L, false);
@@ -621,24 +528,20 @@ int ObjectRef::l_set_animation_frame_speed(lua_State *L)
return 1;
}
-// set_bone_position(self, std::string bone, v3f position, v3f rotation)
+// set_bone_position(self, bone, position, rotation)
int ObjectRef::l_set_bone_position(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
- ServerActiveObject *co = getobject(ref);
- if (co == NULL) return 0;
- // Do it
- std::string bone;
- if (!lua_isnil(L, 2))
- bone = readParam<std::string>(L, 2);
- v3f position = v3f(0, 0, 0);
- if (!lua_isnil(L, 3))
- position = check_v3f(L, 3);
- v3f rotation = v3f(0, 0, 0);
- if (!lua_isnil(L, 4))
- rotation = check_v3f(L, 4);
- co->setBonePosition(bone, position, rotation);
+ ServerActiveObject *sao = getobject(ref);
+ if (sao == nullptr)
+ return 0;
+
+ std::string bone = readParam<std::string>(L, 2);
+ v3f position = check_v3f(L, 3);
+ v3f rotation = check_v3f(L, 4);
+
+ sao->setBonePosition(bone, position, rotation);
return 0;
}
@@ -647,63 +550,53 @@ int ObjectRef::l_get_bone_position(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
- ServerActiveObject *co = getobject(ref);
- if (co == NULL)
+ ServerActiveObject *sao = getobject(ref);
+ if (sao == nullptr)
return 0;
- // Do it
- std::string bone;
- if (!lua_isnil(L, 2))
- bone = readParam<std::string>(L, 2);
+
+ std::string bone = readParam<std::string>(L, 2);
v3f position = v3f(0, 0, 0);
v3f rotation = v3f(0, 0, 0);
- co->getBonePosition(bone, &position, &rotation);
+ sao->getBonePosition(bone, &position, &rotation);
push_v3f(L, position);
push_v3f(L, rotation);
return 2;
}
-// set_attach(self, parent, bone, position, rotation)
+// set_attach(self, parent, bone, position, rotation, force_visible)
int ObjectRef::l_set_attach(lua_State *L)
{
GET_ENV_PTR;
-
ObjectRef *ref = checkobject(L, 1);
ObjectRef *parent_ref = checkobject(L, 2);
- ServerActiveObject *co = getobject(ref);
+ ServerActiveObject *sao = getobject(ref);
ServerActiveObject *parent = getobject(parent_ref);
- if (co == NULL)
- return 0;
-
- if (parent == NULL)
+ if (sao == nullptr || parent == nullptr)
return 0;
-
- if (co == parent)
+ if (sao == parent)
throw LuaError("ObjectRef::set_attach: attaching object to itself is not allowed.");
- // Do it
int parent_id = 0;
std::string bone;
v3f position = v3f(0, 0, 0);
v3f rotation = v3f(0, 0, 0);
- co->getAttachment(&parent_id, &bone, &position, &rotation);
+ bool force_visible;
+
+ sao->getAttachment(&parent_id, &bone, &position, &rotation, &force_visible);
if (parent_id) {
ServerActiveObject *old_parent = env->getActiveObject(parent_id);
- old_parent->removeAttachmentChild(co->getId());
+ old_parent->removeAttachmentChild(sao->getId());
}
- bone = "";
- if (!lua_isnil(L, 3))
- bone = readParam<std::string>(L, 3);
- position = v3f(0, 0, 0);
- if (!lua_isnil(L, 4))
- position = read_v3f(L, 4);
- rotation = v3f(0, 0, 0);
- if (!lua_isnil(L, 5))
- rotation = read_v3f(L, 5);
- co->setAttachment(parent->getId(), bone, position, rotation);
- parent->addAttachmentChild(co->getId());
+ bone = readParam<std::string>(L, 3, "");
+ position = read_v3f(L, 4);
+ rotation = read_v3f(L, 5);
+ force_visible = readParam<bool>(L, 6, false);
+
+ sao->setAttachment(parent->getId(), bone, position, rotation, force_visible);
+ parent->addAttachmentChild(sao->getId());
return 0;
}
@@ -711,40 +604,61 @@ int ObjectRef::l_set_attach(lua_State *L)
int ObjectRef::l_get_attach(lua_State *L)
{
GET_ENV_PTR;
-
ObjectRef *ref = checkobject(L, 1);
- ServerActiveObject *co = getobject(ref);
- if (co == NULL)
+ ServerActiveObject *sao = getobject(ref);
+ if (sao == nullptr)
return 0;
- // Do it
int parent_id = 0;
std::string bone;
v3f position = v3f(0, 0, 0);
v3f rotation = v3f(0, 0, 0);
- co->getAttachment(&parent_id, &bone, &position, &rotation);
- if (!parent_id)
+ bool force_visible;
+
+ sao->getAttachment(&parent_id, &bone, &position, &rotation, &force_visible);
+ if (parent_id == 0)
return 0;
- ServerActiveObject *parent = env->getActiveObject(parent_id);
+ ServerActiveObject *parent = env->getActiveObject(parent_id);
getScriptApiBase(L)->objectrefGetOrCreate(L, parent);
lua_pushlstring(L, bone.c_str(), bone.size());
push_v3f(L, position);
push_v3f(L, rotation);
- return 4;
+ lua_pushboolean(L, force_visible);
+ return 5;
+}
+
+// get_children(self)
+int ObjectRef::l_get_children(lua_State *L)
+{
+ GET_ENV_PTR;
+ ObjectRef *ref = checkobject(L, 1);
+ ServerActiveObject *sao = getobject(ref);
+ if (sao == nullptr)
+ return 0;
+
+ const std::unordered_set<int> child_ids = sao->getAttachmentChildIds();
+ int i = 0;
+
+ lua_createtable(L, child_ids.size(), 0);
+ for (const int id : child_ids) {
+ ServerActiveObject *child = env->getActiveObject(id);
+ getScriptApiBase(L)->objectrefGetOrCreate(L, child);
+ lua_rawseti(L, -2, ++i);
+ }
+ return 1;
}
// set_detach(self)
int ObjectRef::l_set_detach(lua_State *L)
{
GET_ENV_PTR;
-
ObjectRef *ref = checkobject(L, 1);
- ServerActiveObject *co = getobject(ref);
- if (co == NULL)
+ ServerActiveObject *sao = getobject(ref);
+ if (sao == nullptr)
return 0;
- co->clearParentAttachment();
+ sao->clearParentAttachment();
return 0;
}
@@ -753,16 +667,16 @@ int ObjectRef::l_set_properties(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
- ServerActiveObject *co = getobject(ref);
- if (!co)
+ ServerActiveObject *sao = getobject(ref);
+ if (sao == nullptr)
return 0;
- ObjectProperties *prop = co->accessObjectProperties();
- if (!prop)
+ ObjectProperties *prop = sao->accessObjectProperties();
+ if (prop == nullptr)
return 0;
- read_object_properties(L, 2, co, prop, getServer(L)->idef());
- co->notifyObjectPropertiesModified();
+ read_object_properties(L, 2, sao, prop, getServer(L)->idef());
+ sao->notifyObjectPropertiesModified();
return 0;
}
@@ -771,12 +685,14 @@ int ObjectRef::l_get_properties(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
- ServerActiveObject *co = getobject(ref);
- if (co == NULL)
+ ServerActiveObject *sao = getobject(ref);
+ if (sao == nullptr)
return 0;
- ObjectProperties *prop = co->accessObjectProperties();
- if (!prop)
+
+ ObjectProperties *prop = sao->accessObjectProperties();
+ if (prop == nullptr)
return 0;
+
push_object_properties(L, prop);
return 1;
}
@@ -787,7 +703,7 @@ int ObjectRef::l_is_player(lua_State *L)
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
RemotePlayer *player = getplayer(ref);
- lua_pushboolean(L, (player != NULL));
+ lua_pushboolean(L, (player != nullptr));
return 1;
}
@@ -796,12 +712,12 @@ int ObjectRef::l_set_nametag_attributes(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
- ServerActiveObject *co = getobject(ref);
-
- if (co == NULL)
+ ServerActiveObject *sao = getobject(ref);
+ if (sao == nullptr)
return 0;
- ObjectProperties *prop = co->accessObjectProperties();
- if (!prop)
+
+ ObjectProperties *prop = sao->accessObjectProperties();
+ if (prop == nullptr)
return 0;
lua_getfield(L, 2, "color");
@@ -815,7 +731,7 @@ int ObjectRef::l_set_nametag_attributes(lua_State *L)
std::string nametag = getstringfield_default(L, 2, "text", "");
prop->nametag = nametag;
- co->notifyObjectPropertiesModified();
+ sao->notifyObjectPropertiesModified();
lua_pushboolean(L, true);
return 1;
}
@@ -825,11 +741,11 @@ int ObjectRef::l_get_nametag_attributes(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
- ServerActiveObject *co = getobject(ref);
-
- if (co == NULL)
+ ServerActiveObject *sao = getobject(ref);
+ if (sao == nullptr)
return 0;
- ObjectProperties *prop = co->accessObjectProperties();
+
+ ObjectProperties *prop = sao->accessObjectProperties();
if (!prop)
return 0;
@@ -845,30 +761,41 @@ int ObjectRef::l_get_nametag_attributes(lua_State *L)
/* LuaEntitySAO-only */
-// set_velocity(self, {x=num, y=num, z=num})
+// set_velocity(self, velocity)
int ObjectRef::l_set_velocity(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
- LuaEntitySAO *co = getluaobject(ref);
- if (co == NULL) return 0;
- v3f pos = checkFloatPos(L, 2);
- // Do it
- co->setVelocity(pos);
+ LuaEntitySAO *sao = getluaobject(ref);
+ if (sao == nullptr)
+ return 0;
+
+ v3f vel = checkFloatPos(L, 2);
+
+ sao->setVelocity(vel);
return 0;
}
-// add_velocity(self, {x=num, y=num, z=num})
+// add_velocity(self, velocity)
int ObjectRef::l_add_velocity(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
- LuaEntitySAO *co = getluaobject(ref);
- if (!co)
+ ServerActiveObject *sao = getobject(ref);
+ if (sao == nullptr)
return 0;
- v3f pos = checkFloatPos(L, 2);
- // Do it
- co->addVelocity(pos);
+
+ v3f vel = checkFloatPos(L, 2);
+
+ if (sao->getType() == ACTIVEOBJECT_TYPE_LUAENTITY) {
+ LuaEntitySAO *entitysao = dynamic_cast<LuaEntitySAO*>(sao);
+ entitysao->addVelocity(vel);
+ } else if (sao->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
+ PlayerSAO *playersao = dynamic_cast<PlayerSAO*>(sao);
+ playersao->setMaxSpeedOverride(vel);
+ getServer(L)->SendPlayerSpeed(playersao->getPeerID(), vel);
+ }
+
return 0;
}
@@ -877,25 +804,37 @@ int ObjectRef::l_get_velocity(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
- LuaEntitySAO *co = getluaobject(ref);
- if (co == NULL) return 0;
- // Do it
- v3f v = co->getVelocity();
- pushFloatPos(L, v);
+ ServerActiveObject *sao = getobject(ref);
+ if (sao == nullptr)
+ return 0;
+
+ if (sao->getType() == ACTIVEOBJECT_TYPE_LUAENTITY) {
+ LuaEntitySAO *entitysao = dynamic_cast<LuaEntitySAO*>(sao);
+ v3f vel = entitysao->getVelocity();
+ pushFloatPos(L, vel);
+ return 1;
+ } else if (sao->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
+ RemotePlayer *player = dynamic_cast<PlayerSAO*>(sao)->getPlayer();
+ push_v3f(L, player->getSpeed() / BS);
+ return 1;
+ }
+
+ lua_pushnil(L);
return 1;
}
-// set_acceleration(self, {x=num, y=num, z=num})
+// set_acceleration(self, acceleration)
int ObjectRef::l_set_acceleration(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
- LuaEntitySAO *co = getluaobject(ref);
- if (co == NULL) return 0;
- // pos
- v3f pos = checkFloatPos(L, 2);
- // Do it
- co->setAcceleration(pos);
+ LuaEntitySAO *entitysao = getluaobject(ref);
+ if (entitysao == nullptr)
+ return 0;
+
+ v3f acceleration = checkFloatPos(L, 2);
+
+ entitysao->setAcceleration(acceleration);
return 0;
}
@@ -904,59 +843,61 @@ int ObjectRef::l_get_acceleration(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
- LuaEntitySAO *co = getluaobject(ref);
- if (co == NULL) return 0;
- // Do it
- v3f v = co->getAcceleration();
- pushFloatPos(L, v);
+ LuaEntitySAO *entitysao = getluaobject(ref);
+ if (entitysao == nullptr)
+ return 0;
+
+ v3f acceleration = entitysao->getAcceleration();
+ pushFloatPos(L, acceleration);
return 1;
}
-// set_rotation(self, {x=num, y=num, z=num})
-// Each 'num' is in radians
+// set_rotation(self, rotation)
int ObjectRef::l_set_rotation(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
- LuaEntitySAO *co = getluaobject(ref);
- if (!co)
+ LuaEntitySAO *entitysao = getluaobject(ref);
+ if (entitysao == nullptr)
return 0;
v3f rotation = check_v3f(L, 2) * core::RADTODEG;
- co->setRotation(rotation);
+
+ entitysao->setRotation(rotation);
return 0;
}
// get_rotation(self)
-// returns: {x=num, y=num, z=num}
-// Each 'num' is in radians
int ObjectRef::l_get_rotation(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
- LuaEntitySAO *co = getluaobject(ref);
- if (!co)
+ LuaEntitySAO *entitysao = getluaobject(ref);
+ if (entitysao == nullptr)
return 0;
+ v3f rotation = entitysao->getRotation() * core::DEGTORAD;
+
lua_newtable(L);
- v3f rotation = co->getRotation() * core::DEGTORAD;
push_v3f(L, rotation);
return 1;
}
-// set_yaw(self, radians)
+// set_yaw(self, yaw)
int ObjectRef::l_set_yaw(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
- LuaEntitySAO *co = getluaobject(ref);
+ LuaEntitySAO *entitysao = getluaobject(ref);
+ if (entitysao == nullptr)
+ return 0;
- if (co == NULL) return 0;
if (isNaN(L, 2))
throw LuaError("ObjectRef::set_yaw: NaN value is not allowed.");
float yaw = readParam<float>(L, 2) * core::RADTODEG;
- co->setRotation(v3f(0, yaw, 0));
+
+ entitysao->setRotation(v3f(0, yaw, 0));
return 0;
}
@@ -965,11 +906,12 @@ int ObjectRef::l_get_yaw(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
- LuaEntitySAO *co = getluaobject(ref);
- if (!co)
+ LuaEntitySAO *entitysao = getluaobject(ref);
+ if (entitysao == nullptr)
return 0;
- float yaw = co->getRotation().Y * core::DEGTORAD;
+ float yaw = entitysao->getRotation().Y * core::DEGTORAD;
+
lua_pushnumber(L, yaw);
return 1;
}
@@ -979,11 +921,13 @@ int ObjectRef::l_set_texture_mod(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
- LuaEntitySAO *co = getluaobject(ref);
- if (co == NULL) return 0;
- // Do it
- std::string mod = luaL_checkstring(L, 2);
- co->setTextureMod(mod);
+ LuaEntitySAO *entitysao = getluaobject(ref);
+ if (entitysao == nullptr)
+ return 0;
+
+ std::string mod = readParam<std::string>(L, 2);
+
+ entitysao->setTextureMod(mod);
return 0;
}
@@ -992,36 +936,31 @@ int ObjectRef::l_get_texture_mod(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
- LuaEntitySAO *co = getluaobject(ref);
- if (co == NULL) return 0;
- // Do it
- std::string mod = co->getTextureMod();
+ LuaEntitySAO *entitysao = getluaobject(ref);
+ if (entitysao == nullptr)
+ return 0;
+
+ std::string mod = entitysao->getTextureMod();
+
lua_pushstring(L, mod.c_str());
return 1;
}
-// set_sprite(self, p={x=0,y=0}, num_frames=1, framelength=0.2,
-// select_horiz_by_yawpitch=false)
+// set_sprite(self, start_frame, num_frames, framelength, select_x_by_camera)
int ObjectRef::l_set_sprite(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
- LuaEntitySAO *co = getluaobject(ref);
- if (co == NULL) return 0;
- // Do it
- v2s16 p(0,0);
- if (!lua_isnil(L, 2))
- p = readParam<v2s16>(L, 2);
- int num_frames = 1;
- if (!lua_isnil(L, 3))
- num_frames = lua_tonumber(L, 3);
- float framelength = 0.2;
- if (!lua_isnil(L, 4))
- framelength = lua_tonumber(L, 4);
- bool select_horiz_by_yawpitch = false;
- if (!lua_isnil(L, 5))
- select_horiz_by_yawpitch = readParam<bool>(L, 5);
- co->setSprite(p, num_frames, framelength, select_horiz_by_yawpitch);
+ LuaEntitySAO *entitysao = getluaobject(ref);
+ if (entitysao == nullptr)
+ return 0;
+
+ v2s16 start_frame = readParam<v2s16>(L, 2, v2s16(0,0));
+ int num_frames = readParam<int>(L, 3, 1);
+ float framelength = readParam<float>(L, 4, 0.2f);
+ bool select_x_by_camera = readParam<bool>(L, 5, false);
+
+ entitysao->setSprite(start_frame, num_frames, framelength, select_x_by_camera);
return 0;
}
@@ -1031,11 +970,13 @@ int ObjectRef::l_get_entity_name(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
- LuaEntitySAO *co = getluaobject(ref);
+ LuaEntitySAO *entitysao = getluaobject(ref);
log_deprecated(L,"Deprecated call to \"get_entity_name");
- if (co == NULL) return 0;
- // Do it
- std::string name = co->getName();
+ if (entitysao == nullptr)
+ return 0;
+
+ std::string name = entitysao->getName();
+
lua_pushstring(L, name.c_str());
return 1;
}
@@ -1045,87 +986,45 @@ int ObjectRef::l_get_luaentity(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
- LuaEntitySAO *co = getluaobject(ref);
- if (co == NULL) return 0;
- // Do it
- luaentity_get(L, co->getId());
+ LuaEntitySAO *entitysao = getluaobject(ref);
+ if (entitysao == nullptr)
+ return 0;
+
+ luaentity_get(L, entitysao->getId());
return 1;
}
/* Player-only */
-// is_player_connected(self)
-int ObjectRef::l_is_player_connected(lua_State *L)
-{
- NO_MAP_LOCK_REQUIRED;
- // This method was once added for a bugfix, but never documented
- log_deprecated(L, "is_player_connected is undocumented and "
- "will be removed in a future release");
- ObjectRef *ref = checkobject(L, 1);
- RemotePlayer *player = getplayer(ref);
- lua_pushboolean(L, (player != NULL && player->getPeerId() != PEER_ID_INEXISTENT));
- return 1;
-}
-
// get_player_name(self)
int ObjectRef::l_get_player_name(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
RemotePlayer *player = getplayer(ref);
- if (player == NULL) {
+ if (player == nullptr) {
lua_pushlstring(L, "", 0);
return 1;
}
- // Do it
- lua_pushstring(L, player->getName());
- return 1;
-}
-// get_player_velocity(self)
-int ObjectRef::l_get_player_velocity(lua_State *L)
-{
- NO_MAP_LOCK_REQUIRED;
- ObjectRef *ref = checkobject(L, 1);
- RemotePlayer *player = getplayer(ref);
- if (player == NULL) {
- lua_pushnil(L);
- return 1;
- }
- // Do it
- push_v3f(L, player->getSpeed() / BS);
+ lua_pushstring(L, player->getName());
return 1;
}
-// add_player_velocity(self, {x=num, y=num, z=num})
-int ObjectRef::l_add_player_velocity(lua_State *L)
-{
- NO_MAP_LOCK_REQUIRED;
- ObjectRef *ref = checkobject(L, 1);
- v3f vel = checkFloatPos(L, 2);
-
- PlayerSAO *co = getplayersao(ref);
- if (!co)
- return 0;
-
- // Do it
- co->setMaxSpeedOverride(vel);
- getServer(L)->SendPlayerSpeed(co->getPeerID(), vel);
- return 0;
-}
-
// get_look_dir(self)
int ObjectRef::l_get_look_dir(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
- PlayerSAO* co = getplayersao(ref);
- if (co == NULL) return 0;
- // Do it
- float pitch = co->getRadLookPitchDep();
- float yaw = co->getRadYawDep();
+ PlayerSAO* playersao = getplayersao(ref);
+ if (playersao == nullptr)
+ return 0;
+
+ float pitch = playersao->getRadLookPitchDep();
+ float yaw = playersao->getRadYawDep();
v3f v(std::cos(pitch) * std::cos(yaw), std::sin(pitch), std::cos(pitch) *
std::sin(yaw));
+
push_v3f(L, v);
return 1;
}
@@ -1140,10 +1039,11 @@ int ObjectRef::l_get_look_pitch(lua_State *L)
"Deprecated call to get_look_pitch, use get_look_vertical instead");
ObjectRef *ref = checkobject(L, 1);
- PlayerSAO* co = getplayersao(ref);
- if (co == NULL) return 0;
- // Do it
- lua_pushnumber(L, co->getRadLookPitchDep());
+ PlayerSAO* playersao = getplayersao(ref);
+ if (playersao == nullptr)
+ return 0;
+
+ lua_pushnumber(L, playersao->getRadLookPitchDep());
return 1;
}
@@ -1157,34 +1057,37 @@ int ObjectRef::l_get_look_yaw(lua_State *L)
"Deprecated call to get_look_yaw, use get_look_horizontal instead");
ObjectRef *ref = checkobject(L, 1);
- PlayerSAO* co = getplayersao(ref);
- if (co == NULL) return 0;
- // Do it
- lua_pushnumber(L, co->getRadYawDep());
+ PlayerSAO* playersao = getplayersao(ref);
+ if (playersao == nullptr)
+ return 0;
+
+ lua_pushnumber(L, playersao->getRadYawDep());
return 1;
}
-// get_look_pitch2(self)
+// get_look_vertical(self)
int ObjectRef::l_get_look_vertical(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
- PlayerSAO* co = getplayersao(ref);
- if (co == NULL) return 0;
- // Do it
- lua_pushnumber(L, co->getRadLookPitch());
+ PlayerSAO* playersao = getplayersao(ref);
+ if (playersao == nullptr)
+ return 0;
+
+ lua_pushnumber(L, playersao->getRadLookPitch());
return 1;
}
-// get_look_yaw2(self)
+// get_look_horizontal(self)
int ObjectRef::l_get_look_horizontal(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
- PlayerSAO* co = getplayersao(ref);
- if (co == NULL) return 0;
- // Do it
- lua_pushnumber(L, co->getRadRotation().Y);
+ PlayerSAO* playersao = getplayersao(ref);
+ if (playersao == nullptr)
+ return 0;
+
+ lua_pushnumber(L, playersao->getRadRotation().Y);
return 1;
}
@@ -1193,11 +1096,13 @@ int ObjectRef::l_set_look_vertical(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
- PlayerSAO* co = getplayersao(ref);
- if (co == NULL) return 0;
+ PlayerSAO* playersao = getplayersao(ref);
+ if (playersao == nullptr)
+ return 0;
+
float pitch = readParam<float>(L, 2) * core::RADTODEG;
- // Do it
- co->setLookPitchAndSend(pitch);
+
+ playersao->setLookPitchAndSend(pitch);
return 1;
}
@@ -1206,11 +1111,13 @@ int ObjectRef::l_set_look_horizontal(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
- PlayerSAO* co = getplayersao(ref);
- if (co == NULL) return 0;
+ PlayerSAO* playersao = getplayersao(ref);
+ if (playersao == nullptr)
+ return 0;
+
float yaw = readParam<float>(L, 2) * core::RADTODEG;
- // Do it
- co->setPlayerYawAndSend(yaw);
+
+ playersao->setPlayerYawAndSend(yaw);
return 1;
}
@@ -1224,11 +1131,13 @@ int ObjectRef::l_set_look_pitch(lua_State *L)
"Deprecated call to set_look_pitch, use set_look_vertical instead.");
ObjectRef *ref = checkobject(L, 1);
- PlayerSAO* co = getplayersao(ref);
- if (co == NULL) return 0;
+ PlayerSAO* playersao = getplayersao(ref);
+ if (playersao == nullptr)
+ return 0;
+
float pitch = readParam<float>(L, 2) * core::RADTODEG;
- // Do it
- co->setLookPitchAndSend(pitch);
+
+ playersao->setLookPitchAndSend(pitch);
return 1;
}
@@ -1242,30 +1151,32 @@ int ObjectRef::l_set_look_yaw(lua_State *L)
"Deprecated call to set_look_yaw, use set_look_horizontal instead.");
ObjectRef *ref = checkobject(L, 1);
- PlayerSAO* co = getplayersao(ref);
- if (co == NULL) return 0;
+ PlayerSAO* playersao = getplayersao(ref);
+ if (playersao == nullptr)
+ return 0;
+
float yaw = readParam<float>(L, 2) * core::RADTODEG;
- // Do it
- co->setPlayerYawAndSend(yaw);
+
+ playersao->setPlayerYawAndSend(yaw);
return 1;
}
-// set_fov(self, degrees[, is_multiplier, transition_time])
+// set_fov(self, degrees, is_multiplier, transition_time)
int ObjectRef::l_set_fov(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
RemotePlayer *player = getplayer(ref);
- if (!player)
+ if (player == nullptr)
return 0;
- player->setFov({
- static_cast<f32>(luaL_checknumber(L, 2)),
- readParam<bool>(L, 3, false),
- lua_isnumber(L, 4) ? static_cast<f32>(luaL_checknumber(L, 4)) : 0.0f
- });
- getServer(L)->SendPlayerFov(player->getPeerId());
+ float degrees = static_cast<f32>(luaL_checknumber(L, 2));
+ bool is_multiplier = readParam<bool>(L, 3, false);
+ float transition_time = lua_isnumber(L, 4) ?
+ static_cast<f32>(luaL_checknumber(L, 4)) : 0.0f;
+ player->setFov({degrees, is_multiplier, transition_time});
+ getServer(L)->SendPlayerFov(player->getPeerId());
return 0;
}
@@ -1275,14 +1186,14 @@ int ObjectRef::l_get_fov(lua_State *L)
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
RemotePlayer *player = getplayer(ref);
- if (!player)
+ if (player == nullptr)
return 0;
PlayerFovSpec fov_spec = player->getFov();
+
lua_pushnumber(L, fov_spec.fov);
lua_pushboolean(L, fov_spec.is_multiplier);
lua_pushnumber(L, fov_spec.transition_time);
-
return 3;
}
@@ -1291,11 +1202,13 @@ int ObjectRef::l_set_breath(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
- PlayerSAO* co = getplayersao(ref);
- if (co == NULL) return 0;
+ PlayerSAO* playersao = getplayersao(ref);
+ if (playersao == nullptr)
+ return 0;
+
u16 breath = luaL_checknumber(L, 2);
- co->setBreath(breath);
+ playersao->setBreath(breath);
return 0;
}
@@ -1304,11 +1217,13 @@ int ObjectRef::l_get_breath(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
- PlayerSAO* co = getplayersao(ref);
- if (co == NULL) return 0;
- // Do it
- u16 breath = co->getBreath();
- lua_pushinteger (L, breath);
+ PlayerSAO* playersao = getplayersao(ref);
+ if (playersao == nullptr)
+ return 0;
+
+ u16 breath = playersao->getBreath();
+
+ lua_pushinteger(L, breath);
return 1;
}
@@ -1319,16 +1234,16 @@ int ObjectRef::l_set_attribute(lua_State *L)
"Deprecated call to set_attribute, use MetaDataRef methods instead.");
ObjectRef *ref = checkobject(L, 1);
- PlayerSAO* co = getplayersao(ref);
- if (co == NULL)
+ PlayerSAO* playersao = getplayersao(ref);
+ if (playersao == nullptr)
return 0;
std::string attr = luaL_checkstring(L, 2);
if (lua_isnil(L, 3)) {
- co->getMeta().removeString(attr);
+ playersao->getMeta().removeString(attr);
} else {
std::string value = luaL_checkstring(L, 3);
- co->getMeta().setString(attr, value);
+ playersao->getMeta().setString(attr, value);
}
return 1;
}
@@ -1340,14 +1255,14 @@ int ObjectRef::l_get_attribute(lua_State *L)
"Deprecated call to get_attribute, use MetaDataRef methods instead.");
ObjectRef *ref = checkobject(L, 1);
- PlayerSAO* co = getplayersao(ref);
- if (co == NULL)
+ PlayerSAO* playersao = getplayersao(ref);
+ if (playersao == nullptr)
return 0;
std::string attr = luaL_checkstring(L, 2);
std::string value;
- if (co->getMeta().getStringToRef(attr, value)) {
+ if (playersao->getMeta().getStringToRef(attr, value)) {
lua_pushstring(L, value.c_str());
return 1;
}
@@ -1360,11 +1275,11 @@ int ObjectRef::l_get_attribute(lua_State *L)
int ObjectRef::l_get_meta(lua_State *L)
{
ObjectRef *ref = checkobject(L, 1);
- PlayerSAO *co = getplayersao(ref);
- if (co == NULL)
+ PlayerSAO *playersao = getplayersao(ref);
+ if (playersao == nullptr)
return 0;
- PlayerMetaRef::create(L, &co->getMeta());
+ PlayerMetaRef::create(L, &playersao->getMeta());
return 1;
}
@@ -1375,7 +1290,9 @@ int ObjectRef::l_set_inventory_formspec(lua_State *L)
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
RemotePlayer *player = getplayer(ref);
- if (player == NULL) return 0;
+ if (player == nullptr)
+ return 0;
+
std::string formspec = luaL_checkstring(L, 2);
player->inventory_formspec = formspec;
@@ -1390,9 +1307,11 @@ int ObjectRef::l_get_inventory_formspec(lua_State *L)
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
RemotePlayer *player = getplayer(ref);
- if (player == NULL) return 0;
+ if (player == nullptr)
+ return 0;
std::string formspec = player->inventory_formspec;
+
lua_pushlstring(L, formspec.c_str(), formspec.size());
return 1;
}
@@ -1403,7 +1322,7 @@ int ObjectRef::l_set_formspec_prepend(lua_State *L)
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
RemotePlayer *player = getplayer(ref);
- if (player == NULL)
+ if (player == nullptr)
return 0;
std::string formspec = luaL_checkstring(L, 2);
@@ -1414,16 +1333,17 @@ int ObjectRef::l_set_formspec_prepend(lua_State *L)
return 1;
}
-// get_formspec_prepend(self) -> formspec
+// get_formspec_prepend(self)
int ObjectRef::l_get_formspec_prepend(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
RemotePlayer *player = getplayer(ref);
- if (player == NULL)
+ if (player == nullptr)
return 0;
std::string formspec = player->formspec_prepend;
+
lua_pushlstring(L, formspec.c_str(), formspec.size());
return 1;
}
@@ -1434,7 +1354,7 @@ int ObjectRef::l_get_player_control(lua_State *L)
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
RemotePlayer *player = getplayer(ref);
- if (player == NULL) {
+ if (player == nullptr) {
lua_pushlstring(L, "", 0);
return 1;
}
@@ -1455,9 +1375,14 @@ int ObjectRef::l_get_player_control(lua_State *L)
lua_setfield(L, -2, "aux1");
lua_pushboolean(L, control.sneak);
lua_setfield(L, -2, "sneak");
- lua_pushboolean(L, control.LMB);
+ lua_pushboolean(L, control.dig);
+ lua_setfield(L, -2, "dig");
+ lua_pushboolean(L, control.place);
+ lua_setfield(L, -2, "place");
+ // Legacy fields to ensure mod compatibility
+ lua_pushboolean(L, control.dig);
lua_setfield(L, -2, "LMB");
- lua_pushboolean(L, control.RMB);
+ lua_pushboolean(L, control.place);
lua_setfield(L, -2, "RMB");
lua_pushboolean(L, control.zoom);
lua_setfield(L, -2, "zoom");
@@ -1470,22 +1395,87 @@ int ObjectRef::l_get_player_control_bits(lua_State *L)
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
RemotePlayer *player = getplayer(ref);
- if (player == NULL) {
+ if (player == nullptr) {
lua_pushlstring(L, "", 0);
return 1;
}
- // Do it
+
lua_pushnumber(L, player->keyPressed);
return 1;
}
-// hud_add(self, form)
+// set_physics_override(self, override_table)
+int ObjectRef::l_set_physics_override(lua_State *L)
+{
+ NO_MAP_LOCK_REQUIRED;
+ ObjectRef *ref = checkobject(L, 1);
+ PlayerSAO *playersao = (PlayerSAO *) getobject(ref);
+ if (playersao == nullptr)
+ return 0;
+
+ if (lua_istable(L, 2)) {
+ bool modified = false;
+ modified |= getfloatfield(L, 2, "speed", playersao->m_physics_override_speed);
+ modified |= getfloatfield(L, 2, "jump", playersao->m_physics_override_jump);
+ modified |= getfloatfield(L, 2, "gravity", playersao->m_physics_override_gravity);
+ modified |= getboolfield(L, 2, "sneak", playersao->m_physics_override_sneak);
+ modified |= getboolfield(L, 2, "sneak_glitch", playersao->m_physics_override_sneak_glitch);
+ modified |= getboolfield(L, 2, "new_move", playersao->m_physics_override_new_move);
+ if (modified)
+ playersao->m_physics_override_sent = false;
+ } else {
+ // old, non-table format
+ // TODO: Remove this code after version 5.4.0
+ log_deprecated(L, "Deprecated use of set_physics_override(num, num, num)");
+
+ if (!lua_isnil(L, 2)) {
+ playersao->m_physics_override_speed = lua_tonumber(L, 2);
+ playersao->m_physics_override_sent = false;
+ }
+ if (!lua_isnil(L, 3)) {
+ playersao->m_physics_override_jump = lua_tonumber(L, 3);
+ playersao->m_physics_override_sent = false;
+ }
+ if (!lua_isnil(L, 4)) {
+ playersao->m_physics_override_gravity = lua_tonumber(L, 4);
+ playersao->m_physics_override_sent = false;
+ }
+ }
+ return 0;
+}
+
+// get_physics_override(self)
+int ObjectRef::l_get_physics_override(lua_State *L)
+{
+ NO_MAP_LOCK_REQUIRED;
+ ObjectRef *ref = checkobject(L, 1);
+ PlayerSAO *playersao = (PlayerSAO *)getobject(ref);
+ if (playersao == nullptr)
+ return 0;
+
+ lua_newtable(L);
+ lua_pushnumber(L, playersao->m_physics_override_speed);
+ lua_setfield(L, -2, "speed");
+ lua_pushnumber(L, playersao->m_physics_override_jump);
+ lua_setfield(L, -2, "jump");
+ lua_pushnumber(L, playersao->m_physics_override_gravity);
+ lua_setfield(L, -2, "gravity");
+ lua_pushboolean(L, playersao->m_physics_override_sneak);
+ lua_setfield(L, -2, "sneak");
+ lua_pushboolean(L, playersao->m_physics_override_sneak_glitch);
+ lua_setfield(L, -2, "sneak_glitch");
+ lua_pushboolean(L, playersao->m_physics_override_new_move);
+ lua_setfield(L, -2, "new_move");
+ return 1;
+}
+
+// hud_add(self, hud)
int ObjectRef::l_hud_add(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
RemotePlayer *player = getplayer(ref);
- if (player == NULL)
+ if (player == nullptr)
return 0;
HudElement *elem = new HudElement;
@@ -1507,12 +1497,10 @@ int ObjectRef::l_hud_remove(lua_State *L)
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
RemotePlayer *player = getplayer(ref);
- if (player == NULL)
+ if (player == nullptr)
return 0;
- u32 id = -1;
- if (!lua_isnil(L, 2))
- id = lua_tonumber(L, 2);
+ u32 id = luaL_checkint(L, 2);
if (!getServer(L)->hudRemove(player, id))
return 0;
@@ -1527,17 +1515,17 @@ int ObjectRef::l_hud_change(lua_State *L)
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
RemotePlayer *player = getplayer(ref);
- if (player == NULL)
+ if (player == nullptr)
return 0;
- u32 id = lua_isnumber(L, 2) ? lua_tonumber(L, 2) : -1;
+ u32 id = luaL_checkint(L, 2);
- HudElement *e = player->getHud(id);
- if (!e)
+ HudElement *elem = player->getHud(id);
+ if (elem == nullptr)
return 0;
- void *value = NULL;
- HudElementStat stat = read_hud_change(L, e, &value);
+ void *value = nullptr;
+ HudElementStat stat = read_hud_change(L, elem, &value);
getServer(L)->hudChange(player, id, stat, value);
@@ -1551,15 +1539,16 @@ int ObjectRef::l_hud_get(lua_State *L)
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
RemotePlayer *player = getplayer(ref);
- if (player == NULL)
+ if (player == nullptr)
return 0;
- u32 id = lua_tonumber(L, -1);
+ u32 id = luaL_checkint(L, 2);
- HudElement *e = player->getHud(id);
- if (!e)
+ HudElement *elem = player->getHud(id);
+ if (elem == nullptr)
return 0;
- push_hud_element(L, e);
+
+ push_hud_element(L, elem);
return 1;
}
@@ -1569,7 +1558,7 @@ int ObjectRef::l_hud_set_flags(lua_State *L)
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
RemotePlayer *player = getplayer(ref);
- if (player == NULL)
+ if (player == nullptr)
return 0;
u32 flags = 0;
@@ -1590,12 +1579,13 @@ int ObjectRef::l_hud_set_flags(lua_State *L)
return 1;
}
+// hud_get_flags(self)
int ObjectRef::l_hud_get_flags(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
RemotePlayer *player = getplayer(ref);
- if (player == NULL)
+ if (player == nullptr)
return 0;
lua_newtable(L);
@@ -1613,7 +1603,6 @@ int ObjectRef::l_hud_get_flags(lua_State *L)
lua_setfield(L, -2, "minimap");
lua_pushboolean(L, player->hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE);
lua_setfield(L, -2, "minimap_radar");
-
return 1;
}
@@ -1623,10 +1612,10 @@ int ObjectRef::l_hud_set_hotbar_itemcount(lua_State *L)
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
RemotePlayer *player = getplayer(ref);
- if (player == NULL)
+ if (player == nullptr)
return 0;
- s32 hotbar_itemcount = lua_tonumber(L, 2);
+ s32 hotbar_itemcount = luaL_checkint(L, 2);
if (!getServer(L)->hudSetHotbarItemcount(player, hotbar_itemcount))
return 0;
@@ -1641,7 +1630,7 @@ int ObjectRef::l_hud_get_hotbar_itemcount(lua_State *L)
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
RemotePlayer *player = getplayer(ref);
- if (player == NULL)
+ if (player == nullptr)
return 0;
lua_pushnumber(L, player->getHotbarItemcount());
@@ -1654,7 +1643,7 @@ int ObjectRef::l_hud_set_hotbar_image(lua_State *L)
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
RemotePlayer *player = getplayer(ref);
- if (player == NULL)
+ if (player == nullptr)
return 0;
std::string name = readParam<std::string>(L, 2);
@@ -1669,10 +1658,11 @@ int ObjectRef::l_hud_get_hotbar_image(lua_State *L)
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
RemotePlayer *player = getplayer(ref);
- if (player == NULL)
+ if (player == nullptr)
return 0;
const std::string &name = player->getHotbarImage();
+
lua_pushlstring(L, name.c_str(), name.size());
return 1;
}
@@ -1683,7 +1673,7 @@ int ObjectRef::l_hud_set_hotbar_selected_image(lua_State *L)
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
RemotePlayer *player = getplayer(ref);
- if (player == NULL)
+ if (player == nullptr)
return 0;
std::string name = readParam<std::string>(L, 2);
@@ -1698,44 +1688,45 @@ int ObjectRef::l_hud_get_hotbar_selected_image(lua_State *L)
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
RemotePlayer *player = getplayer(ref);
- if (player == NULL)
+ if (player == nullptr)
return 0;
const std::string &name = player->getHotbarSelectedImage();
+
lua_pushlstring(L, name.c_str(), name.size());
return 1;
}
-// set_sky(self, {base_color=, type=, textures=, clouds=, sky_colors={}})
+// set_sky(self, sky_parameters)
int ObjectRef::l_set_sky(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
RemotePlayer *player = getplayer(ref);
- if (!player)
+ if (player == nullptr)
return 0;
+ SkyboxParams sky_params = player->getSkyParams();
bool is_colorspec = is_color_table(L, 2);
- SkyboxParams skybox_params = player->getSkyParams();
if (lua_istable(L, 2) && !is_colorspec) {
lua_getfield(L, 2, "base_color");
if (!lua_isnil(L, -1))
- read_color(L, -1, &skybox_params.bgcolor);
+ read_color(L, -1, &sky_params.bgcolor);
lua_pop(L, 1);
lua_getfield(L, 2, "type");
if (!lua_isnil(L, -1))
- skybox_params.type = luaL_checkstring(L, -1);
+ sky_params.type = luaL_checkstring(L, -1);
lua_pop(L, 1);
lua_getfield(L, 2, "textures");
- skybox_params.textures.clear();
- if (lua_istable(L, -1) && skybox_params.type == "skybox") {
+ sky_params.textures.clear();
+ if (lua_istable(L, -1) && sky_params.type == "skybox") {
lua_pushnil(L);
while (lua_next(L, -2) != 0) {
// Key is at index -2 and value at index -1
- skybox_params.textures.emplace_back(readParam<std::string>(L, -1));
+ sky_params.textures.emplace_back(readParam<std::string>(L, -1));
// Removes the value, but keeps the key for iteration
lua_pop(L, 1);
}
@@ -1748,56 +1739,56 @@ int ObjectRef::l_set_sky(lua_State *L)
using "regular" or "plain" skybox modes as textures aren't needed.
*/
- if (skybox_params.textures.size() != 6 && skybox_params.textures.size() > 0)
+ if (sky_params.textures.size() != 6 && sky_params.textures.size() > 0)
throw LuaError("Skybox expects 6 textures!");
- skybox_params.clouds = getboolfield_default(L, 2,
- "clouds", skybox_params.clouds);
+ sky_params.clouds = getboolfield_default(L, 2,
+ "clouds", sky_params.clouds);
lua_getfield(L, 2, "sky_color");
if (lua_istable(L, -1)) {
lua_getfield(L, -1, "day_sky");
- read_color(L, -1, &skybox_params.sky_color.day_sky);
+ read_color(L, -1, &sky_params.sky_color.day_sky);
lua_pop(L, 1);
lua_getfield(L, -1, "day_horizon");
- read_color(L, -1, &skybox_params.sky_color.day_horizon);
+ read_color(L, -1, &sky_params.sky_color.day_horizon);
lua_pop(L, 1);
lua_getfield(L, -1, "dawn_sky");
- read_color(L, -1, &skybox_params.sky_color.dawn_sky);
+ read_color(L, -1, &sky_params.sky_color.dawn_sky);
lua_pop(L, 1);
lua_getfield(L, -1, "dawn_horizon");
- read_color(L, -1, &skybox_params.sky_color.dawn_horizon);
+ read_color(L, -1, &sky_params.sky_color.dawn_horizon);
lua_pop(L, 1);
lua_getfield(L, -1, "night_sky");
- read_color(L, -1, &skybox_params.sky_color.night_sky);
+ read_color(L, -1, &sky_params.sky_color.night_sky);
lua_pop(L, 1);
lua_getfield(L, -1, "night_horizon");
- read_color(L, -1, &skybox_params.sky_color.night_horizon);
+ read_color(L, -1, &sky_params.sky_color.night_horizon);
lua_pop(L, 1);
lua_getfield(L, -1, "indoors");
- read_color(L, -1, &skybox_params.sky_color.indoors);
+ read_color(L, -1, &sky_params.sky_color.indoors);
lua_pop(L, 1);
// Prevent flickering clouds at dawn/dusk:
- skybox_params.fog_sun_tint = video::SColor(255, 255, 255, 255);
+ sky_params.fog_sun_tint = video::SColor(255, 255, 255, 255);
lua_getfield(L, -1, "fog_sun_tint");
- read_color(L, -1, &skybox_params.fog_sun_tint);
+ read_color(L, -1, &sky_params.fog_sun_tint);
lua_pop(L, 1);
- skybox_params.fog_moon_tint = video::SColor(255, 255, 255, 255);
+ sky_params.fog_moon_tint = video::SColor(255, 255, 255, 255);
lua_getfield(L, -1, "fog_moon_tint");
- read_color(L, -1, &skybox_params.fog_moon_tint);
+ read_color(L, -1, &sky_params.fog_moon_tint);
lua_pop(L, 1);
lua_getfield(L, -1, "fog_tint_type");
if (!lua_isnil(L, -1))
- skybox_params.fog_tint_type = luaL_checkstring(L, -1);
+ sky_params.fog_tint_type = luaL_checkstring(L, -1);
lua_pop(L, 1);
// Because we need to leave the "sky_color" table.
@@ -1813,14 +1804,14 @@ int ObjectRef::l_set_sky(lua_State *L)
StarParams star_params = player->getStarParams();
// Prevent erroneous background colors
- skybox_params.bgcolor = video::SColor(255, 255, 255, 255);
- read_color(L, 2, &skybox_params.bgcolor);
+ sky_params.bgcolor = video::SColor(255, 255, 255, 255);
+ read_color(L, 2, &sky_params.bgcolor);
- skybox_params.type = luaL_checkstring(L, 3);
+ sky_params.type = luaL_checkstring(L, 3);
// Preserve old behaviour of the sun, moon and stars
// when using the old set_sky call.
- if (skybox_params.type == "regular") {
+ if (sky_params.type == "regular") {
sun_params.visible = true;
sun_params.sunrise_visible = true;
moon_params.visible = true;
@@ -1832,31 +1823,31 @@ int ObjectRef::l_set_sky(lua_State *L)
star_params.visible = false;
}
- skybox_params.textures.clear();
+ sky_params.textures.clear();
if (lua_istable(L, 4)) {
lua_pushnil(L);
while (lua_next(L, 4) != 0) {
// Key at index -2, and value at index -1
if (lua_isstring(L, -1))
- skybox_params.textures.emplace_back(readParam<std::string>(L, -1));
+ sky_params.textures.emplace_back(readParam<std::string>(L, -1));
else
- skybox_params.textures.emplace_back("");
+ sky_params.textures.emplace_back("");
// Remove the value, keep the key for the next iteration
lua_pop(L, 1);
}
}
- if (skybox_params.type == "skybox" && skybox_params.textures.size() != 6)
+ if (sky_params.type == "skybox" && sky_params.textures.size() != 6)
throw LuaError("Skybox expects 6 textures.");
- skybox_params.clouds = true;
+ sky_params.clouds = true;
if (lua_isboolean(L, 5))
- skybox_params.clouds = readParam<bool>(L, 5);
+ sky_params.clouds = readParam<bool>(L, 5);
getServer(L)->setSun(player, sun_params);
getServer(L)->setMoon(player, moon_params);
getServer(L)->setStars(player, star_params);
}
- getServer(L)->setSky(player, skybox_params);
+ getServer(L)->setSky(player, sky_params);
lua_pushboolean(L, true);
return 1;
}
@@ -1867,18 +1858,17 @@ int ObjectRef::l_get_sky(lua_State *L)
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
RemotePlayer *player = getplayer(ref);
-
- if (!player)
+ if (player == nullptr)
return 0;
- SkyboxParams skybox_params;
- skybox_params = player->getSkyParams();
+
+ SkyboxParams skybox_params = player->getSkyParams();
push_ARGB8(L, skybox_params.bgcolor);
lua_pushlstring(L, skybox_params.type.c_str(), skybox_params.type.size());
lua_newtable(L);
s16 i = 1;
- for (const std::string& texture : skybox_params.textures) {
+ for (const std::string &texture : skybox_params.textures) {
lua_pushlstring(L, texture.c_str(), texture.size());
lua_rawseti(L, -2, i++);
}
@@ -1892,11 +1882,10 @@ int ObjectRef::l_get_sky_color(lua_State *L)
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
RemotePlayer *player = getplayer(ref);
-
- if (!player)
+ if (player == nullptr)
return 0;
- const SkyboxParams& skybox_params = player->getSkyParams();
+ const SkyboxParams &skybox_params = player->getSkyParams();
lua_newtable(L);
if (skybox_params.type == "regular") {
@@ -1924,18 +1913,16 @@ int ObjectRef::l_get_sky_color(lua_State *L)
return 1;
}
-// set_sun(self, {visible, texture=, tonemap=, sunrise=, rotation=, scale=})
+// set_sun(self, sun_parameters)
int ObjectRef::l_set_sun(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))
+ if (player == nullptr)
return 0;
+ luaL_checktype(L, 2, LUA_TTABLE);
SunParams sun_params = player->getSunParams();
sun_params.visible = getboolfield_default(L, 2,
@@ -1962,8 +1949,9 @@ int ObjectRef::l_get_sun(lua_State *L)
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
RemotePlayer *player = getplayer(ref);
- if (!player)
+ if (player == nullptr)
return 0;
+
const SunParams &sun_params = player->getSunParams();
lua_newtable(L);
@@ -1979,21 +1967,19 @@ int ObjectRef::l_get_sun(lua_State *L)
lua_setfield(L, -2, "sunrise_visible");
lua_pushnumber(L, sun_params.scale);
lua_setfield(L, -2, "scale");
-
return 1;
}
-// set_moon(self, {visible, texture=, tonemap=, sunrise=, rotation=, scale=})
+// set_moon(self, moon_parameters)
int ObjectRef::l_set_moon(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))
+ if (player == nullptr)
return 0;
+ luaL_checktype(L, 2, LUA_TTABLE);
MoonParams moon_params = player->getMoonParams();
moon_params.visible = getboolfield_default(L, 2,
@@ -2016,8 +2002,9 @@ int ObjectRef::l_get_moon(lua_State *L)
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
RemotePlayer *player = getplayer(ref);
- if (!player)
+ if (player == nullptr)
return 0;
+
const MoonParams &moon_params = player->getMoonParams();
lua_newtable(L);
@@ -2029,21 +2016,19 @@ int ObjectRef::l_get_moon(lua_State *L)
lua_setfield(L, -2, "tonemap");
lua_pushnumber(L, moon_params.scale);
lua_setfield(L, -2, "scale");
-
return 1;
}
-// set_stars(self, {visible, count=, starcolor=, rotation=, scale=})
+// set_stars(self, star_parameters)
int ObjectRef::l_set_stars(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))
+ if (player == nullptr)
return 0;
+ luaL_checktype(L, 2, LUA_TTABLE);
StarParams star_params = player->getStarParams();
star_params.visible = getboolfield_default(L, 2,
@@ -2070,8 +2055,9 @@ int ObjectRef::l_get_stars(lua_State *L)
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
RemotePlayer *player = getplayer(ref);
- if (!player)
+ if (player == nullptr)
return 0;
+
const StarParams &star_params = player->getStarParams();
lua_newtable(L);
@@ -2083,21 +2069,19 @@ int ObjectRef::l_get_stars(lua_State *L)
lua_setfield(L, -2, "star_color");
lua_pushnumber(L, star_params.scale);
lua_setfield(L, -2, "scale");
-
return 1;
}
-// set_clouds(self, {density=, color=, ambient=, height=, thickness=, speed=})
+// set_clouds(self, cloud_parameters)
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))
+ if (player == nullptr)
return 0;
+ luaL_checktype(L, 2, LUA_TTABLE);
CloudParams cloud_params = player->getCloudParams();
cloud_params.density = getfloatfield_default(L, 2, "density", cloud_params.density);
@@ -2133,8 +2117,9 @@ int ObjectRef::l_get_clouds(lua_State *L)
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
RemotePlayer *player = getplayer(ref);
- if (!player)
+ if (player == nullptr)
return 0;
+
const CloudParams &cloud_params = player->getCloudParams();
lua_newtable(L);
@@ -2154,25 +2139,27 @@ int ObjectRef::l_get_clouds(lua_State *L)
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)
+// override_day_night_ratio(self, ratio)
int ObjectRef::l_override_day_night_ratio(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
RemotePlayer *player = getplayer(ref);
- if (player == NULL)
+ if (player == nullptr)
return 0;
bool do_override = false;
float ratio = 0.0f;
+
if (!lua_isnil(L, 2)) {
do_override = true;
ratio = readParam<float>(L, 2);
+ luaL_argcheck(L, ratio >= 0.0f && ratio <= 1.0f, 1,
+ "value must be between 0 and 1");
}
getServer(L)->overrideDayNightRatio(player, do_override, ratio);
@@ -2186,7 +2173,7 @@ int ObjectRef::l_get_day_night_ratio(lua_State *L)
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
RemotePlayer *player = getplayer(ref);
- if (player == NULL)
+ if (player == nullptr)
return 0;
bool do_override;
@@ -2201,27 +2188,77 @@ int ObjectRef::l_get_day_night_ratio(lua_State *L)
return 1;
}
-ObjectRef::ObjectRef(ServerActiveObject *object):
- m_object(object)
+// set_minimap_modes(self, modes, selected_mode)
+int ObjectRef::l_set_minimap_modes(lua_State *L)
{
- //infostream<<"ObjectRef created for id="<<m_object->getId()<<std::endl;
+ NO_MAP_LOCK_REQUIRED;
+ ObjectRef *ref = checkobject(L, 1);
+ RemotePlayer *player = getplayer(ref);
+ if (player == nullptr)
+ return 0;
+
+ luaL_checktype(L, 2, LUA_TTABLE);
+ std::vector<MinimapMode> modes;
+ s16 selected_mode = luaL_checkint(L, 3);
+
+ lua_pushnil(L);
+ while (lua_next(L, 2) != 0) {
+ /* key is at index -2, value is at index -1 */
+ if (lua_istable(L, -1)) {
+ bool ok = true;
+ MinimapMode mode;
+ std::string type = getstringfield_default(L, -1, "type", "");
+ if (type == "off")
+ mode.type = MINIMAP_TYPE_OFF;
+ else if (type == "surface")
+ mode.type = MINIMAP_TYPE_SURFACE;
+ else if (type == "radar")
+ mode.type = MINIMAP_TYPE_RADAR;
+ else if (type == "texture") {
+ mode.type = MINIMAP_TYPE_TEXTURE;
+ mode.texture = getstringfield_default(L, -1, "texture", "");
+ mode.scale = getintfield_default(L, -1, "scale", 1);
+ } else {
+ warningstream << "Minimap mode of unknown type \"" << type.c_str()
+ << "\" ignored.\n" << std::endl;
+ ok = false;
+ }
+
+ if (ok) {
+ mode.label = getstringfield_default(L, -1, "label", "");
+ // Size is limited to 512. Performance gets poor if size too large, and
+ // segfaults have been experienced.
+ mode.size = rangelim(getintfield_default(L, -1, "size", 0), 1, 512);
+ modes.push_back(mode);
+ }
+ }
+ /* removes 'value'; keeps 'key' for next iteration */
+ lua_pop(L, 1);
+ }
+ lua_pop(L, 1); // Remove key
+
+ getServer(L)->SendMinimapModes(player->getPeerId(), modes, selected_mode);
+ return 0;
}
+ObjectRef::ObjectRef(ServerActiveObject *object):
+ m_object(object)
+{}
+
// Creates an ObjectRef and leaves it on top of stack
// Not callable from Lua; all references are created on the C side.
void ObjectRef::create(lua_State *L, ServerActiveObject *object)
{
- ObjectRef *o = new ObjectRef(object);
- //infostream<<"ObjectRef::create: o="<<o<<std::endl;
- *(void **)(lua_newuserdata(L, sizeof(void *))) = o;
+ ObjectRef *obj = new ObjectRef(object);
+ *(void **)(lua_newuserdata(L, sizeof(void *))) = obj;
luaL_getmetatable(L, className);
lua_setmetatable(L, -2);
}
void ObjectRef::set_null(lua_State *L)
{
- ObjectRef *o = checkobject(L, -1);
- o->m_object = NULL;
+ ObjectRef *obj = checkobject(L, -1);
+ obj->m_object = nullptr;
}
void ObjectRef::Register(lua_State *L)
@@ -2245,12 +2282,8 @@ void ObjectRef::Register(lua_State *L)
lua_pop(L, 1); // drop metatable
- markAliasDeprecated(methods);
luaL_openlib(L, 0, methods, 0); // fill methodtable
lua_pop(L, 1); // drop methodtable
-
- // Cannot be created from Lua
- //lua_register(L, className, create_object);
}
const char ObjectRef::className[] = "ObjectRef";
@@ -2278,15 +2311,19 @@ luaL_Reg ObjectRef::methods[] = {
luamethod(ObjectRef, get_bone_position),
luamethod(ObjectRef, set_attach),
luamethod(ObjectRef, get_attach),
+ luamethod(ObjectRef, get_children),
luamethod(ObjectRef, set_detach),
luamethod(ObjectRef, set_properties),
luamethod(ObjectRef, get_properties),
luamethod(ObjectRef, set_nametag_attributes),
luamethod(ObjectRef, get_nametag_attributes),
- // LuaEntitySAO-only
+
luamethod_aliased(ObjectRef, set_velocity, setvelocity),
- luamethod(ObjectRef, add_velocity),
+ luamethod_aliased(ObjectRef, add_velocity, add_player_velocity),
luamethod_aliased(ObjectRef, get_velocity, getvelocity),
+ luamethod_dep(ObjectRef, get_velocity, get_player_velocity),
+
+ // LuaEntitySAO-only
luamethod_aliased(ObjectRef, set_acceleration, setacceleration),
luamethod_aliased(ObjectRef, get_acceleration, getacceleration),
luamethod_aliased(ObjectRef, set_yaw, setyaw),
@@ -2294,15 +2331,14 @@ luaL_Reg ObjectRef::methods[] = {
luamethod(ObjectRef, set_rotation),
luamethod(ObjectRef, get_rotation),
luamethod_aliased(ObjectRef, set_texture_mod, settexturemod),
+ luamethod(ObjectRef, get_texture_mod),
luamethod_aliased(ObjectRef, set_sprite, setsprite),
luamethod(ObjectRef, get_entity_name),
luamethod(ObjectRef, get_luaentity),
+
// Player-only
luamethod(ObjectRef, is_player),
- luamethod(ObjectRef, is_player_connected),
luamethod(ObjectRef, get_player_name),
- luamethod(ObjectRef, get_player_velocity),
- luamethod(ObjectRef, add_player_velocity),
luamethod(ObjectRef, get_look_dir),
luamethod(ObjectRef, get_look_pitch),
luamethod(ObjectRef, get_look_yaw),
@@ -2357,5 +2393,6 @@ luaL_Reg ObjectRef::methods[] = {
luamethod(ObjectRef, set_eye_offset),
luamethod(ObjectRef, get_eye_offset),
luamethod(ObjectRef, send_mapblock),
+ luamethod(ObjectRef, set_minimap_modes),
{0,0}
};
diff --git a/src/script/lua_api/l_object.h b/src/script/lua_api/l_object.h
index a75c59fd9..db3a3a7cf 100644
--- a/src/script/lua_api/l_object.h
+++ b/src/script/lua_api/l_object.h
@@ -69,29 +69,24 @@ private:
static int l_remove(lua_State *L);
// get_pos(self)
- // returns: {x=num, y=num, z=num}
static int l_get_pos(lua_State *L);
// set_pos(self, pos)
static int l_set_pos(lua_State *L);
- // move_to(self, pos, continuous=false)
+ // move_to(self, pos, continuous)
static int l_move_to(lua_State *L);
// punch(self, puncher, time_from_last_punch, tool_capabilities, dir)
static int l_punch(lua_State *L);
- // right_click(self, clicker); clicker = an another ObjectRef
+ // right_click(self, clicker)
static int l_right_click(lua_State *L);
- // set_hp(self, hp)
- // hp = number of hitpoints (2 * number of hearts)
- // returns: nil
+ // set_hp(self, hp, reason)
static int l_set_hp(lua_State *L);
// get_hp(self)
- // returns: number of hitpoints (2 * number of hearts)
- // 0 if not applicable to this type of object
static int l_get_hp(lua_State *L);
// get_inventory(self)
@@ -106,7 +101,7 @@ private:
// get_wielded_item(self)
static int l_get_wielded_item(lua_State *L);
- // set_wielded_item(self, itemstack or itemstring or table or nil)
+ // set_wielded_item(self, item)
static int l_set_wielded_item(lua_State *L);
// set_armor_groups(self, groups)
@@ -115,8 +110,7 @@ private:
// get_armor_groups(self)
static int l_get_armor_groups(lua_State *L);
- // set_physics_override(self, physics_override_speed, physics_override_jump,
- // physics_override_gravity, sneak, sneak_glitch, new_move)
+ // set_physics_override(self, override_table)
static int l_set_physics_override(lua_State *L);
// get_physics_override(self)
@@ -131,7 +125,7 @@ private:
// get_animation(self)
static int l_get_animation(lua_State *L);
- // set_bone_position(self, std::string bone, v3f position, v3f rotation)
+ // set_bone_position(self, bone, position, rotation)
static int l_set_bone_position(lua_State *L);
// get_bone_position(self, bone)
@@ -143,6 +137,9 @@ private:
// get_attach(self)
static int l_get_attach(lua_State *L);
+ // get_children(self)
+ static int l_get_children(lua_State *L);
+
// set_detach(self)
static int l_set_detach(lua_State *L);
@@ -157,28 +154,28 @@ private:
/* LuaEntitySAO-only */
- // set_velocity(self, {x=num, y=num, z=num})
+ // set_velocity(self, velocity)
static int l_set_velocity(lua_State *L);
- // add_velocity(self, {x=num, y=num, z=num})
+ // add_velocity(self, velocity)
static int l_add_velocity(lua_State *L);
// get_velocity(self)
static int l_get_velocity(lua_State *L);
- // set_acceleration(self, {x=num, y=num, z=num})
+ // set_acceleration(self, acceleration)
static int l_set_acceleration(lua_State *L);
// get_acceleration(self)
static int l_get_acceleration(lua_State *L);
- // set_rotation(self, {x=num, y=num, z=num})
+ // set_rotation(self, rotation)
static int l_set_rotation(lua_State *L);
// get_rotation(self)
static int l_get_rotation(lua_State *L);
- // set_yaw(self, radians)
+ // set_yaw(self, yaw)
static int l_set_yaw(lua_State *L);
// get_yaw(self)
@@ -190,8 +187,7 @@ private:
// l_get_texture_mod(self)
static int l_get_texture_mod(lua_State *L);
- // set_sprite(self, p={x=0,y=0}, num_frames=1, framelength=0.2,
- // select_horiz_by_yawpitch=false)
+ // set_sprite(self, start_frame, num_frames, framelength, select_x_by_camera)
static int l_set_sprite(lua_State *L);
// DEPRECATED
@@ -203,18 +199,9 @@ private:
/* Player-only */
- // is_player_connected(self)
- static int l_is_player_connected(lua_State *L);
-
// get_player_name(self)
static int l_get_player_name(lua_State *L);
- // get_player_velocity(self)
- static int l_get_player_velocity(lua_State *L);
-
- // add_player_velocity(self, {x=num, y=num, z=num})
- static int l_add_player_velocity(lua_State *L);
-
// get_fov(self)
static int l_get_fov(lua_State *L);
@@ -235,7 +222,7 @@ private:
// get_look_yaw2(self)
static int l_get_look_horizontal(lua_State *L);
- // set_fov(self, degrees, is_multiplier)
+ // set_fov(self, degrees, is_multiplier, transition_time)
static int l_set_fov(lua_State *L);
// set_look_vertical(self, radians)
@@ -258,9 +245,11 @@ private:
// get_breath(self, breath)
static int l_get_breath(lua_State *L);
+ // DEPRECATED
// set_attribute(self, attribute, value)
static int l_set_attribute(lua_State *L);
+ // DEPRECATED
// get_attribute(self, attribute)
static int l_get_attribute(lua_State *L);
@@ -270,13 +259,13 @@ private:
// set_inventory_formspec(self, formspec)
static int l_set_inventory_formspec(lua_State *L);
- // get_inventory_formspec(self) -> formspec
+ // get_inventory_formspec(self)
static int l_get_inventory_formspec(lua_State *L);
// set_formspec_prepend(self, formspec)
static int l_set_formspec_prepend(lua_State *L);
- // get_formspec_prepend(self) -> formspec
+ // get_formspec_prepend(self)
static int l_get_formspec_prepend(lua_State *L);
// get_player_control(self)
@@ -324,7 +313,7 @@ private:
// hud_get_hotbar_selected_image(self)
static int l_hud_get_hotbar_selected_image(lua_State *L);
- // set_sky({base_color=, type=, textures=, clouds=, sky_colors={}})
+ // set_sky(self, sky_parameters)
static int l_set_sky(lua_State *L);
// get_sky(self)
@@ -333,25 +322,25 @@ private:
// get_sky_color(self)
static int l_get_sky_color(lua_State* L);
- // set_sun(self, {visible, texture=, tonemap=, sunrise=, rotation=, scale=})
+ // set_sun(self, sun_parameters)
static int l_set_sun(lua_State *L);
// get_sun(self)
static int l_get_sun(lua_State *L);
- // set_moon(self, {visible, texture=, tonemap=, rotation, scale=})
+ // set_moon(self, moon_parameters)
static int l_set_moon(lua_State *L);
// get_moon(self)
static int l_get_moon(lua_State *L);
- // set_stars(self, {visible, count=, starcolor=, rotation, scale=})
+ // set_stars(self, star_parameters)
static int l_set_stars(lua_State *L);
// get_stars(self)
static int l_get_stars(lua_State *L);
- // set_clouds(self, {density=, color=, ambient=, height=, thickness=, speed=})
+ // set_clouds(self, cloud_parameters)
static int l_set_clouds(lua_State *L);
// get_clouds(self)
@@ -363,13 +352,13 @@ private:
// get_day_night_ratio(self)
static int l_get_day_night_ratio(lua_State *L);
- // set_local_animation(self, {stand/idle}, {walk}, {dig}, {walk+dig}, frame_speed)
+ // set_local_animation(self, idle, walk, dig, walk_while_dig, frame_speed)
static int l_set_local_animation(lua_State *L);
// get_local_animation(self)
static int l_get_local_animation(lua_State *L);
- // set_eye_offset(self, v3f first pv, v3f third pv)
+ // set_eye_offset(self, firstperson, thirdperson)
static int l_set_eye_offset(lua_State *L);
// get_eye_offset(self)
@@ -383,4 +372,7 @@ private:
// send_mapblock(pos)
static int l_send_mapblock(lua_State *L);
+
+ // set_minimap_modes(self, modes, wanted_mode)
+ static int l_set_minimap_modes(lua_State *L);
};
diff --git a/src/server.cpp b/src/server.cpp
index fe2bb3840..8f6257afe 100644
--- a/src/server.cpp
+++ b/src/server.cpp
@@ -213,7 +213,8 @@ Server::Server(
bool simple_singleplayer_mode,
Address bind_addr,
bool dedicated,
- ChatInterface *iface
+ ChatInterface *iface,
+ std::string *on_shutdown_errmsg
):
m_bind_addr(bind_addr),
m_path_world(path_world),
@@ -232,6 +233,7 @@ Server::Server(
m_thread(new ServerThread(this)),
m_clients(m_con),
m_admin_chat(iface),
+ m_on_shutdown_errmsg(on_shutdown_errmsg),
m_modchannel_mgr(new ModChannelMgr())
{
if (m_path_world.empty())
@@ -314,7 +316,18 @@ Server::~Server()
// Execute script shutdown hooks
infostream << "Executing shutdown hooks" << std::endl;
- m_script->on_shutdown();
+ try {
+ m_script->on_shutdown();
+ } catch (ModError &e) {
+ errorstream << "ModError: " << e.what() << std::endl;
+ if (m_on_shutdown_errmsg) {
+ if (m_on_shutdown_errmsg->empty()) {
+ *m_on_shutdown_errmsg = std::string("ModError: ") + e.what();
+ } else {
+ *m_on_shutdown_errmsg += std::string("\nModError: ") + e.what();
+ }
+ }
+ }
infostream << "Server: Saving environment metadata" << std::endl;
m_env->saveMeta();
@@ -356,8 +369,13 @@ void Server::init()
infostream << "- game: " << m_gamespec.path << std::endl;
// Create world if it doesn't exist
- if (!loadGameConfAndInitWorld(m_path_world, m_gamespec))
- throw ServerError("Failed to initialize world");
+ try {
+ loadGameConfAndInitWorld(m_path_world,
+ fs::GetFilenameFromPath(m_path_world.c_str()),
+ m_gamespec, false);
+ } catch (const BaseException &e) {
+ throw ServerError(std::string("Failed to initialize world: ") + e.what());
+ }
// Create emerge manager
m_emerge = new EmergeManager(this);
@@ -635,7 +653,12 @@ void Server::AsyncRunStep(bool initial_step)
}
m_clients.step(dtime);
- m_lag_gauge->increment((m_lag_gauge->get() > dtime ? -1 : 1) * dtime/100);
+ // increase/decrease lag gauge gradually
+ if (m_lag_gauge->get() > dtime) {
+ m_lag_gauge->decrement(dtime/100);
+ } else {
+ m_lag_gauge->increment(dtime/100);
+ }
#if USE_CURL
// send masterserver announce
{
@@ -779,7 +802,7 @@ void Server::AsyncRunStep(bool initial_step)
// u16 id
// std::string data
buffer.append(idbuf, sizeof(idbuf));
- buffer.append(serializeString(aom.datastring));
+ buffer.append(serializeString16(aom.datastring));
}
}
/*
@@ -1970,7 +1993,7 @@ void Server::SendActiveObjectRemoveAdd(RemoteClient *client, PlayerSAO *playersa
writeU8((u8*)buf, type);
data.append(buf, 1);
- data.append(serializeLongString(
+ data.append(serializeString32(
obj->getClientInitializationData(client->net_proto_version)));
// Add to known objects
@@ -2315,7 +2338,7 @@ void Server::SendBlockNoLock(session_t peer_id, MapBlock *block, u8 ver,
block->serializeNetworkSpecific(os);
std::string s = os.str();
- NetworkPacket pkt(TOCLIENT_BLOCKDATA, 2 + 2 + 2 + 2 + s.size(), peer_id);
+ NetworkPacket pkt(TOCLIENT_BLOCKDATA, 2 + 2 + 2 + s.size(), peer_id);
pkt << block->getPos();
pkt.putRawString(s.c_str(), s.size());
@@ -2433,31 +2456,14 @@ bool Server::addMediaFile(const std::string &filename,
// Ok, attempt to load the file and add to cache
// Read data
- std::ifstream fis(filepath.c_str(), std::ios_base::binary);
- if (!fis.good()) {
- errorstream << "Server::addMediaFile(): Could not open \""
- << filename << "\" for reading" << std::endl;
- return false;
- }
std::string filedata;
- bool bad = false;
- for (;;) {
- char buf[1024];
- fis.read(buf, sizeof(buf));
- std::streamsize len = fis.gcount();
- filedata.append(buf, len);
- if (fis.eof())
- break;
- if (!fis.good()) {
- bad = true;
- break;
- }
- }
- if (bad) {
- errorstream << "Server::addMediaFile(): Failed to read \""
- << filename << "\"" << std::endl;
+ if (!fs::ReadFile(filepath, filedata)) {
+ errorstream << "Server::addMediaFile(): Failed to open \""
+ << filename << "\" for reading" << std::endl;
return false;
- } else if (filedata.empty()) {
+ }
+
+ if (filedata.empty()) {
errorstream << "Server::addMediaFile(): Empty file \""
<< filepath << "\"" << std::endl;
return false;
@@ -2489,19 +2495,25 @@ void Server::fillMediaCache()
// Collect all media file paths
std::vector<std::string> paths;
- m_modmgr->getModsMediaPaths(paths);
- fs::GetRecursiveDirs(paths, m_gamespec.path + DIR_DELIM + "textures");
+ // The paths are ordered in descending priority
fs::GetRecursiveDirs(paths, porting::path_user + DIR_DELIM + "textures" + DIR_DELIM + "server");
+ fs::GetRecursiveDirs(paths, m_gamespec.path + DIR_DELIM + "textures");
+ m_modmgr->getModsMediaPaths(paths);
// Collect media file information from paths into cache
for (const std::string &mediapath : paths) {
std::vector<fs::DirListNode> dirlist = fs::GetDirListing(mediapath);
for (const fs::DirListNode &dln : dirlist) {
- if (dln.dir) // Ignore dirs
+ if (dln.dir) // Ignore dirs (already in paths)
+ continue;
+
+ const std::string &filename = dln.name;
+ if (m_media.find(filename) != m_media.end()) // Do not override
continue;
+
std::string filepath = mediapath;
- filepath.append(DIR_DELIM).append(dln.name);
- addMediaFile(dln.name, filepath);
+ filepath.append(DIR_DELIM).append(filename);
+ addMediaFile(filename, filepath);
}
}
@@ -2650,6 +2662,23 @@ void Server::sendRequestedMedia(session_t peer_id,
}
}
+void Server::SendMinimapModes(session_t peer_id,
+ std::vector<MinimapMode> &modes, size_t wanted_mode)
+{
+ RemotePlayer *player = m_env->getPlayer(peer_id);
+ assert(player);
+ if (player->getPeerId() == PEER_ID_INEXISTENT)
+ return;
+
+ NetworkPacket pkt(TOCLIENT_MINIMAP_MODES, 0, peer_id);
+ pkt << (u16)modes.size() << (u16)wanted_mode;
+
+ for (auto &mode : modes)
+ pkt << (u16)mode.type << mode.label << mode.size << mode.texture << mode.scale;
+
+ Send(&pkt);
+}
+
void Server::sendDetachedInventory(Inventory *inventory, const std::string &name, session_t peer_id)
{
NetworkPacket pkt(TOCLIENT_DETACHED_INVENTORY, 0, peer_id);
@@ -3866,19 +3895,27 @@ void Server::broadcastModChannelMessage(const std::string &channel,
}
}
-void Server::loadTranslationLanguage(const std::string &lang_code)
+Translations *Server::getTranslationLanguage(const std::string &lang_code)
{
- if (g_server_translations->count(lang_code))
- return; // Already loaded
+ if (lang_code.empty())
+ return nullptr;
+
+ auto it = server_translations.find(lang_code);
+ if (it != server_translations.end())
+ return &it->second; // Already loaded
+
+ // [] will create an entry
+ auto *translations = &server_translations[lang_code];
std::string suffix = "." + lang_code + ".tr";
for (const auto &i : m_media) {
if (str_ends_with(i.first, suffix)) {
- std::ifstream t(i.second.path);
- std::string data((std::istreambuf_iterator<char>(t)),
- std::istreambuf_iterator<char>());
-
- (*g_server_translations)[lang_code].loadTranslation(data);
+ std::string data;
+ if (fs::ReadFile(i.second.path, data)) {
+ translations->loadTranslation(data);
+ }
}
}
+
+ return translations;
}
diff --git a/src/server.h b/src/server.h
index f44716531..4b3ac5cf7 100644
--- a/src/server.h
+++ b/src/server.h
@@ -38,6 +38,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "serverenvironment.h"
#include "clientiface.h"
#include "chatmessage.h"
+#include "translation.h"
#include <string>
#include <list>
#include <map>
@@ -117,6 +118,14 @@ struct ServerPlayingSound
std::unordered_set<session_t> clients; // peer ids
};
+struct MinimapMode {
+ MinimapType type = MINIMAP_TYPE_OFF;
+ std::string label;
+ u16 size = 0;
+ std::string texture;
+ u16 scale = 1;
+};
+
class Server : public con::PeerHandler, public MapEventReceiver,
public IGameDef
{
@@ -131,7 +140,8 @@ public:
bool simple_singleplayer_mode,
Address bind_addr,
bool dedicated,
- ChatInterface *iface = nullptr
+ ChatInterface *iface = nullptr,
+ std::string *on_shutdown_errmsg = nullptr
);
~Server();
DISABLE_CLASS_COPY(Server);
@@ -329,6 +339,10 @@ public:
void SendPlayerSpeed(session_t peer_id, const v3f &added_vel);
void SendPlayerFov(session_t peer_id);
+ void SendMinimapModes(session_t peer_id,
+ std::vector<MinimapMode> &modes,
+ size_t wanted_mode);
+
void sendDetachedInventories(session_t peer_id, bool incremental);
virtual bool registerModStorage(ModMetadata *storage);
@@ -342,8 +356,8 @@ public:
// Send block to specific player only
bool SendBlock(session_t peer_id, const v3s16 &blockpos);
- // Load translations for a language
- void loadTranslationLanguage(const std::string &lang_code);
+ // Get or load translations for a language
+ Translations *getTranslationLanguage(const std::string &lang_code);
// Bind address
Address m_bind_addr;
@@ -556,6 +570,8 @@ private:
// Mods
std::unique_ptr<ServerModManager> m_modmgr;
+ std::unordered_map<std::string, Translations> server_translations;
+
/*
Threads
*/
@@ -596,6 +612,10 @@ private:
ChatInterface *m_admin_chat;
std::string m_admin_nick;
+ // if a mod-error occurs in the on_shutdown callback, the error message will
+ // be written into this
+ std::string *const m_on_shutdown_errmsg;
+
/*
Map edit event queue. Automatically receives all map edits.
The constructor of this class registers us to receive them through
diff --git a/src/server/luaentity_sao.cpp b/src/server/luaentity_sao.cpp
index d504c42ca..b39797531 100644
--- a/src/server/luaentity_sao.cpp
+++ b/src/server/luaentity_sao.cpp
@@ -42,8 +42,8 @@ LuaEntitySAO::LuaEntitySAO(ServerEnvironment *env, v3f pos, const std::string &d
u8 version2 = 0;
u8 version = readU8(is);
- name = deSerializeString(is);
- state = deSerializeLongString(is);
+ name = deSerializeString16(is);
+ state = deSerializeString32(is);
if (version < 1)
break;
@@ -225,7 +225,7 @@ std::string LuaEntitySAO::getClientInitializationData(u16 protocol_version)
// PROTOCOL_VERSION >= 37
writeU8(os, 1); // version
- os << serializeString(""); // name
+ os << serializeString16(""); // name
writeU8(os, 0); // is_player
writeU16(os, getId()); //id
writeV3F32(os, m_base_position);
@@ -233,26 +233,26 @@ std::string LuaEntitySAO::getClientInitializationData(u16 protocol_version)
writeU16(os, m_hp);
std::ostringstream msg_os(std::ios::binary);
- msg_os << serializeLongString(getPropertyPacket()); // message 1
- msg_os << serializeLongString(generateUpdateArmorGroupsCommand()); // 2
- msg_os << serializeLongString(generateUpdateAnimationCommand()); // 3
+ msg_os << serializeString32(getPropertyPacket()); // message 1
+ msg_os << serializeString32(generateUpdateArmorGroupsCommand()); // 2
+ msg_os << serializeString32(generateUpdateAnimationCommand()); // 3
for (const auto &bone_pos : m_bone_position) {
- msg_os << serializeLongString(generateUpdateBonePositionCommand(
- bone_pos.first, bone_pos.second.X, bone_pos.second.Y)); // m_bone_position.size
+ msg_os << serializeString32(generateUpdateBonePositionCommand(
+ bone_pos.first, bone_pos.second.X, bone_pos.second.Y)); // 3 + N
}
- msg_os << serializeLongString(generateUpdateAttachmentCommand()); // 4
+ msg_os << serializeString32(generateUpdateAttachmentCommand()); // 4 + m_bone_position.size
int message_count = 4 + m_bone_position.size();
for (const auto &id : getAttachmentChildIds()) {
if (ServerActiveObject *obj = m_env->getActiveObject(id)) {
message_count++;
- msg_os << serializeLongString(obj->generateUpdateInfantCommand(
+ msg_os << serializeString32(obj->generateUpdateInfantCommand(
id, protocol_version));
}
}
- msg_os << serializeLongString(generateSetTextureModCommand());
+ msg_os << serializeString32(generateSetTextureModCommand());
message_count++;
writeU8(os, message_count);
@@ -270,14 +270,14 @@ void LuaEntitySAO::getStaticData(std::string *result) const
// version must be 1 to keep backwards-compatibility. See version2
writeU8(os, 1);
// name
- os<<serializeString(m_init_name);
+ os<<serializeString16(m_init_name);
// state
if(m_registered){
std::string state = m_env->getScriptIface()->
luaentity_GetStaticdata(m_id);
- os<<serializeLongString(state);
+ os<<serializeString32(state);
} else {
- os<<serializeLongString(m_init_state);
+ os<<serializeString32(m_init_state);
}
writeU16(os, m_hp);
writeV3F1000(os, m_velocity);
@@ -436,7 +436,7 @@ std::string LuaEntitySAO::generateSetTextureModCommand() const
// command
writeU8(os, AO_CMD_SET_TEXTURE_MOD);
// parameters
- os << serializeString(m_current_texture_modifier);
+ os << serializeString16(m_current_texture_modifier);
return os.str();
}
diff --git a/src/server/luaentity_sao.h b/src/server/luaentity_sao.h
index 2520c8f5d..e060aa06d 100644
--- a/src/server/luaentity_sao.h
+++ b/src/server/luaentity_sao.h
@@ -42,6 +42,7 @@ public:
void step(float dtime, bool send_recommended);
std::string getClientInitializationData(u16 protocol_version);
bool isStaticAllowed() const { return m_prop.static_save; }
+ bool shouldUnload() const { return true; }
void getStaticData(std::string *result) const;
u16 punch(v3f dir, const ToolCapabilities *toolcap = nullptr,
ServerActiveObject *puncher = nullptr,
diff --git a/src/server/mods.cpp b/src/server/mods.cpp
index 6ac530739..cf1467648 100644
--- a/src/server/mods.cpp
+++ b/src/server/mods.cpp
@@ -99,10 +99,10 @@ void ServerModManager::getModNames(std::vector<std::string> &modlist) const
void ServerModManager::getModsMediaPaths(std::vector<std::string> &paths) const
{
for (const ModSpec &spec : m_sorted_mods) {
- paths.push_back(spec.path + DIR_DELIM + "textures");
- paths.push_back(spec.path + DIR_DELIM + "sounds");
- paths.push_back(spec.path + DIR_DELIM + "media");
- paths.push_back(spec.path + DIR_DELIM + "models");
- paths.push_back(spec.path + DIR_DELIM + "locale");
+ fs::GetRecursiveDirs(paths, spec.path + DIR_DELIM + "textures");
+ fs::GetRecursiveDirs(paths, spec.path + DIR_DELIM + "sounds");
+ fs::GetRecursiveDirs(paths, spec.path + DIR_DELIM + "media");
+ fs::GetRecursiveDirs(paths, spec.path + DIR_DELIM + "models");
+ fs::GetRecursiveDirs(paths, spec.path + DIR_DELIM + "locale");
}
}
diff --git a/src/server/player_sao.cpp b/src/server/player_sao.cpp
index 3d9f08bfa..62515d1c9 100644
--- a/src/server/player_sao.cpp
+++ b/src/server/player_sao.cpp
@@ -55,6 +55,7 @@ PlayerSAO::PlayerSAO(ServerEnvironment *env_, RemotePlayer *player_, session_t p
m_prop.backface_culling = false;
m_prop.makes_footstep_sound = true;
m_prop.stepheight = PLAYER_DEFAULT_STEPHEIGHT * BS;
+ m_prop.show_on_minimap = true;
m_hp = m_prop.hp_max;
m_breath = m_prop.breath_max;
// Disable zoom in survival mode using a value of 0
@@ -109,7 +110,7 @@ std::string PlayerSAO::getClientInitializationData(u16 protocol_version)
// Protocol >= 15
writeU8(os, 1); // version
- os << serializeString(m_player->getName()); // name
+ os << serializeString16(m_player->getName()); // name
writeU8(os, 1); // is_player
writeS16(os, getId()); // id
writeV3F32(os, m_base_position);
@@ -117,22 +118,22 @@ std::string PlayerSAO::getClientInitializationData(u16 protocol_version)
writeU16(os, getHP());
std::ostringstream msg_os(std::ios::binary);
- msg_os << serializeLongString(getPropertyPacket()); // message 1
- msg_os << serializeLongString(generateUpdateArmorGroupsCommand()); // 2
- msg_os << serializeLongString(generateUpdateAnimationCommand()); // 3
+ msg_os << serializeString32(getPropertyPacket()); // message 1
+ msg_os << serializeString32(generateUpdateArmorGroupsCommand()); // 2
+ msg_os << serializeString32(generateUpdateAnimationCommand()); // 3
for (const auto &bone_pos : m_bone_position) {
- msg_os << serializeLongString(generateUpdateBonePositionCommand(
- bone_pos.first, bone_pos.second.X, bone_pos.second.Y)); // m_bone_position.size
+ msg_os << serializeString32(generateUpdateBonePositionCommand(
+ bone_pos.first, bone_pos.second.X, bone_pos.second.Y)); // 3 + N
}
- msg_os << serializeLongString(generateUpdateAttachmentCommand()); // 4
- msg_os << serializeLongString(generateUpdatePhysicsOverrideCommand()); // 5
+ msg_os << serializeString32(generateUpdateAttachmentCommand()); // 4 + m_bone_position.size
+ msg_os << serializeString32(generateUpdatePhysicsOverrideCommand()); // 5 + m_bone_position.size
int message_count = 5 + m_bone_position.size();
for (const auto &id : getAttachmentChildIds()) {
if (ServerActiveObject *obj = m_env->getActiveObject(id)) {
message_count++;
- msg_os << serializeLongString(obj->generateUpdateInfantCommand(
+ msg_os << serializeString32(obj->generateUpdateInfantCommand(
id, protocol_version));
}
}
@@ -457,20 +458,25 @@ u16 PlayerSAO::punch(v3f dir,
void PlayerSAO::setHP(s32 hp, const PlayerHPChangeReason &reason)
{
- s32 oldhp = m_hp;
+ if (hp == (s32)m_hp)
+ return; // Nothing to do
- //hp = rangelim(hp, 0, m_prop.hp_max);
+ if (m_hp <= 0 && hp < (s32)m_hp)
+ return; // Cannot take more damage
- if (oldhp != hp) {
- s32 hp_change = m_env->getScriptIface()->on_player_hpchange(this, hp - oldhp, reason);
+ {
+ s32 hp_change = m_env->getScriptIface()->on_player_hpchange(this, hp - m_hp, reason);
if (hp_change == 0)
return;
- hp = rangelim(oldhp + hp_change, 0, m_prop.hp_max);
+ hp = m_hp + hp_change;
}
+ s32 oldhp = m_hp;
+ hp = rangelim(hp, 0, m_prop.hp_max);
+
if (hp < oldhp && isImmortal())
- return;
+ return; // Do not allow immortal players to be damaged
m_hp = hp;
@@ -558,11 +564,35 @@ void PlayerSAO::setMaxSpeedOverride(const v3f &vel)
bool PlayerSAO::checkMovementCheat()
{
- if (isAttached() || m_is_singleplayer ||
+ if (m_is_singleplayer ||
g_settings->getBool("disable_anticheat")) {
m_last_good_position = m_base_position;
return false;
}
+ if (UnitSAO *parent = dynamic_cast<UnitSAO *>(getParent())) {
+ v3f attachment_pos;
+ {
+ int parent_id;
+ std::string bone;
+ v3f attachment_rot;
+ bool force_visible;
+ getAttachment(&parent_id, &bone, &attachment_pos, &attachment_rot, &force_visible);
+ }
+
+ v3f parent_pos = parent->getBasePosition();
+ f32 diff = m_base_position.getDistanceFromSQ(parent_pos) - attachment_pos.getLengthSQ();
+ const f32 maxdiff = 4.0f * BS; // fair trade-off value for various latencies
+
+ if (diff > maxdiff * maxdiff) {
+ setBasePosition(parent_pos);
+ actionstream << "Server: " << m_player->getName()
+ << " moved away from parent; diff=" << sqrtf(diff) / BS
+ << " resetting position." << std::endl;
+ return true;
+ }
+ // Player movement is locked to the entity. Skip further checks
+ return false;
+ }
bool cheated = false;
/*
diff --git a/src/server/player_sao.h b/src/server/player_sao.h
index 8571bd4f9..3e178d4fc 100644
--- a/src/server/player_sao.h
+++ b/src/server/player_sao.h
@@ -83,6 +83,7 @@ public:
void addedToEnvironment(u32 dtime_s);
void removingFromEnvironment();
bool isStaticAllowed() const { return false; }
+ bool shouldUnload() const { return false; }
std::string getClientInitializationData(u16 protocol_version);
void getStaticData(std::string *result) const;
void step(float dtime, bool send_recommended);
diff --git a/src/server/serveractiveobject.cpp b/src/server/serveractiveobject.cpp
index 3341dc008..8cb59b2d6 100644
--- a/src/server/serveractiveobject.cpp
+++ b/src/server/serveractiveobject.cpp
@@ -61,7 +61,7 @@ std::string ServerActiveObject::generateUpdateInfantCommand(u16 infant_id, u16 p
// Clients since 4aa9a66 so no longer need this data
// Version 38 is the first bump after that commit.
// See also: ClientEnvironment::addActiveObject
- os << serializeLongString(getClientInitializationData(protocol_version));
+ os << serializeString32(getClientInitializationData(protocol_version));
}
return os.str();
}
diff --git a/src/server/serveractiveobject.h b/src/server/serveractiveobject.h
index 927009aef..2764d159e 100644
--- a/src/server/serveractiveobject.h
+++ b/src/server/serveractiveobject.h
@@ -125,6 +125,7 @@ public:
assert(isStaticAllowed());
*result = "";
}
+
/*
Return false in here to never save and instead remove object
on unload. getStaticData() will not be called in that case.
@@ -132,6 +133,14 @@ public:
virtual bool isStaticAllowed() const
{return true;}
+ /*
+ Return false here to never unload the object.
+ isStaticAllowed && shouldUnload -> unload when out of active block range
+ !isStaticAllowed && shouldUnload -> unload when block is unloaded
+ */
+ virtual bool shouldUnload() const
+ { return true; }
+
// Returns tool wear
virtual u16 punch(v3f dir,
const ToolCapabilities *toolcap = nullptr,
diff --git a/src/server/unit_sao.cpp b/src/server/unit_sao.cpp
index ef0e87f2c..2371640ca 100644
--- a/src/server/unit_sao.cpp
+++ b/src/server/unit_sao.cpp
@@ -121,8 +121,8 @@ void UnitSAO::sendOutdatedData()
}
// clang-format on
-void UnitSAO::setAttachment(
- int parent_id, const std::string &bone, v3f position, v3f rotation)
+void UnitSAO::setAttachment(int parent_id, const std::string &bone, v3f position,
+ v3f rotation, bool force_visible)
{
// Attachments need to be handled on both the server and client.
// If we just attach on the server, we can only copy the position of the parent.
@@ -137,6 +137,7 @@ void UnitSAO::setAttachment(
m_attachment_bone = bone;
m_attachment_position = position;
m_attachment_rotation = rotation;
+ m_force_visible = force_visible;
m_attachment_sent = false;
if (parent_id != old_parent) {
@@ -145,13 +146,14 @@ void UnitSAO::setAttachment(
}
}
-void UnitSAO::getAttachment(
- int *parent_id, std::string *bone, v3f *position, v3f *rotation) const
+void UnitSAO::getAttachment(int *parent_id, std::string *bone, v3f *position,
+ v3f *rotation, bool *force_visible) const
{
*parent_id = m_attachment_parent_id;
*bone = m_attachment_bone;
*position = m_attachment_position;
*rotation = m_attachment_rotation;
+ *force_visible = m_force_visible;
}
void UnitSAO::clearChildAttachments()
@@ -159,7 +161,7 @@ void UnitSAO::clearChildAttachments()
for (int child_id : m_attachment_child_ids) {
// Child can be NULL if it was deleted earlier
if (ServerActiveObject *child = m_env->getActiveObject(child_id))
- child->setAttachment(0, "", v3f(0, 0, 0), v3f(0, 0, 0));
+ child->setAttachment(0, "", v3f(0, 0, 0), v3f(0, 0, 0), false);
}
m_attachment_child_ids.clear();
}
@@ -169,9 +171,9 @@ void UnitSAO::clearParentAttachment()
ServerActiveObject *parent = nullptr;
if (m_attachment_parent_id) {
parent = m_env->getActiveObject(m_attachment_parent_id);
- setAttachment(0, "", m_attachment_position, m_attachment_rotation);
+ setAttachment(0, "", m_attachment_position, m_attachment_rotation, false);
} else {
- setAttachment(0, "", v3f(0, 0, 0), v3f(0, 0, 0));
+ setAttachment(0, "", v3f(0, 0, 0), v3f(0, 0, 0), false);
}
// Do it
if (parent)
@@ -242,9 +244,10 @@ std::string UnitSAO::generateUpdateAttachmentCommand() const
writeU8(os, AO_CMD_ATTACH_TO);
// parameters
writeS16(os, m_attachment_parent_id);
- os << serializeString(m_attachment_bone);
+ os << serializeString16(m_attachment_bone);
writeV3F32(os, m_attachment_position);
writeV3F32(os, m_attachment_rotation);
+ writeU8(os, m_force_visible);
return os.str();
}
@@ -255,7 +258,7 @@ std::string UnitSAO::generateUpdateBonePositionCommand(
// command
writeU8(os, AO_CMD_SET_BONE_POSITION);
// parameters
- os << serializeString(bone);
+ os << serializeString16(bone);
writeV3F32(os, position);
writeV3F32(os, rotation);
return os.str();
@@ -291,7 +294,7 @@ std::string UnitSAO::generateUpdateArmorGroupsCommand() const
writeU8(os, AO_CMD_UPDATE_ARMOR_GROUPS);
writeU16(os, m_armor_groups.size());
for (const auto &armor_group : m_armor_groups) {
- os << serializeString(armor_group.first);
+ os << serializeString16(armor_group.first);
writeS16(os, armor_group.second);
}
return os.str();
diff --git a/src/server/unit_sao.h b/src/server/unit_sao.h
index 3cb7f0ad5..a21e055c5 100644
--- a/src/server/unit_sao.h
+++ b/src/server/unit_sao.h
@@ -64,9 +64,9 @@ public:
ServerActiveObject *getParent() const;
inline bool isAttached() const { return getParent(); }
void setAttachment(int parent_id, const std::string &bone, v3f position,
- v3f rotation);
+ v3f rotation, bool force_visible);
void getAttachment(int *parent_id, std::string *bone, v3f *position,
- v3f *rotation) const;
+ v3f *rotation, bool *force_visible) const;
void clearChildAttachments();
void clearParentAttachment();
void addAttachmentChild(int child_id);
@@ -133,4 +133,5 @@ private:
v3f m_attachment_position;
v3f m_attachment_rotation;
bool m_attachment_sent = false;
+ bool m_force_visible = false;
};
diff --git a/src/serverenvironment.cpp b/src/serverenvironment.cpp
index ad2ffc9a4..d044b003d 100644
--- a/src/serverenvironment.cpp
+++ b/src/serverenvironment.cpp
@@ -1066,6 +1066,91 @@ bool ServerEnvironment::swapNode(v3s16 p, const MapNode &n)
return true;
}
+u8 ServerEnvironment::findSunlight(v3s16 pos) const
+{
+ // Directions for neighbouring nodes with specified order
+ static const v3s16 dirs[] = {
+ v3s16(-1, 0, 0), v3s16(1, 0, 0), v3s16(0, 0, -1), v3s16(0, 0, 1),
+ v3s16(0, -1, 0), v3s16(0, 1, 0)
+ };
+
+ const NodeDefManager *ndef = m_server->ndef();
+
+ // found_light remembers the highest known sunlight value at pos
+ u8 found_light = 0;
+
+ struct stack_entry {
+ v3s16 pos;
+ s16 dist;
+ };
+ std::stack<stack_entry> stack;
+ stack.push({pos, 0});
+
+ std::unordered_map<s64, s8> dists;
+ dists[MapDatabase::getBlockAsInteger(pos)] = 0;
+
+ while (!stack.empty()) {
+ struct stack_entry e = stack.top();
+ stack.pop();
+
+ v3s16 currentPos = e.pos;
+ s8 dist = e.dist + 1;
+
+ for (const v3s16& off : dirs) {
+ v3s16 neighborPos = currentPos + off;
+ s64 neighborHash = MapDatabase::getBlockAsInteger(neighborPos);
+
+ // Do not walk neighborPos multiple times unless the distance to the start
+ // position is shorter
+ auto it = dists.find(neighborHash);
+ if (it != dists.end() && dist >= it->second)
+ continue;
+
+ // Position to walk
+ bool is_position_ok;
+ MapNode node = m_map->getNode(neighborPos, &is_position_ok);
+ if (!is_position_ok) {
+ // This happens very rarely because the map at currentPos is loaded
+ m_map->emergeBlock(neighborPos, false);
+ node = m_map->getNode(neighborPos, &is_position_ok);
+ if (!is_position_ok)
+ continue; // not generated
+ }
+
+ const ContentFeatures &def = ndef->get(node);
+ if (!def.sunlight_propagates) {
+ // Do not test propagation here again
+ dists[neighborHash] = -1;
+ continue;
+ }
+
+ // Sunlight could have come from here
+ dists[neighborHash] = dist;
+ u8 daylight = node.param1 & 0x0f;
+
+ // In the special case where sunlight shines from above and thus
+ // does not decrease with upwards distance, daylight is always
+ // bigger than nightlight, which never reaches 15
+ int possible_finlight = daylight - dist;
+ if (possible_finlight <= found_light) {
+ // Light from here cannot make a brighter light at currentPos than
+ // found_light
+ continue;
+ }
+
+ u8 nightlight = node.param1 >> 4;
+ if (daylight > nightlight) {
+ // Found a valid daylight
+ found_light = possible_finlight;
+ } else {
+ // Sunlight may be darker, so walk the neighbours
+ stack.push({neighborPos, dist});
+ }
+ }
+ }
+ return found_light;
+}
+
void ServerEnvironment::clearObjects(ClearObjectsMode mode)
{
infostream << "ServerEnvironment::clearObjects(): "
@@ -1354,8 +1439,8 @@ void ServerEnvironment::step(float dtime)
std::shuffle(output.begin(), output.end(), m_rgen);
int i = 0;
- // The time budget for ABMs is 20%.
- u32 max_time_ms = m_cache_abm_interval * 1000 / 5;
+ // determine the time budget for ABMs
+ u32 max_time_ms = m_cache_abm_interval * 1000 * m_cache_abm_time_budget;
for (const v3s16 &p : output) {
MapBlock *block = m_map->getBlockNoCreateNoEx(p);
if (!block)
@@ -1887,8 +1972,8 @@ void ServerEnvironment::deactivateFarObjects(bool _force_delete)
// force_delete might be overriden per object
bool force_delete = _force_delete;
- // Do not deactivate if static data creation not allowed
- if (!force_delete && !obj->isStaticAllowed())
+ // Do not deactivate if disallowed
+ if (!force_delete && !obj->shouldUnload())
return false;
// removeRemovedObjects() is responsible for these
@@ -1917,7 +2002,10 @@ void ServerEnvironment::deactivateFarObjects(bool _force_delete)
}
// If block is still active, don't remove
- if (!force_delete && m_active_blocks.contains(blockpos_o))
+ bool still_active = obj->isStaticAllowed() ?
+ m_active_blocks.contains(blockpos_o) :
+ getMap().getBlockNoCreateNoEx(blockpos_o) != nullptr;
+ if (!force_delete && still_active)
return false;
verbosestream << "ServerEnvironment::deactivateFarObjects(): "
diff --git a/src/serverenvironment.h b/src/serverenvironment.h
index af742e290..cfd5b8f3e 100644
--- a/src/serverenvironment.h
+++ b/src/serverenvironment.h
@@ -322,6 +322,9 @@ public:
bool removeNode(v3s16 p);
bool swapNode(v3s16 p, const MapNode &n);
+ // Find the daylight value at pos with a Depth First Search
+ u8 findSunlight(v3s16 pos) const;
+
// Find all active objects inside a radius around a point
void getObjectsInsideRadius(std::vector<ServerActiveObject *> &objects, const v3f &pos, float radius,
std::function<bool(ServerActiveObject *obj)> include_obj_cb)
diff --git a/src/serverlist.cpp b/src/serverlist.cpp
index 18264e933..80a8c2f1a 100644
--- a/src/serverlist.cpp
+++ b/src/serverlist.cpp
@@ -52,15 +52,7 @@ std::vector<ServerListSpec> getLocal()
{
std::string path = ServerList::getFilePath();
std::string liststring;
- if (fs::PathExists(path)) {
- std::ifstream istream(path.c_str());
- if (istream.is_open()) {
- std::ostringstream ostream;
- ostream << istream.rdbuf();
- liststring = ostream.str();
- istream.close();
- }
- }
+ fs::ReadFile(path, liststring);
return deSerialize(liststring);
}
@@ -253,19 +245,26 @@ void sendAnnounce(AnnounceAction action,
for (const ModSpec &mod : mods) {
server["mods"].append(mod.name);
}
- actionstream << "Announcing to " << g_settings->get("serverlist_url") << std::endl;
} else if (action == AA_UPDATE) {
if (lag)
server["lag"] = lag;
}
+ if (action == AA_START) {
+ actionstream << "Announcing " << aa_names[action] << " to " <<
+ g_settings->get("serverlist_url") << std::endl;
+ } else {
+ infostream << "Announcing " << aa_names[action] << " to " <<
+ g_settings->get("serverlist_url") << std::endl;
+ }
+
HTTPFetchRequest fetch_request;
fetch_request.url = g_settings->get("serverlist_url") + std::string("/announce");
- fetch_request.post_fields["json"] = fastWriteJson(server);
+ fetch_request.method = HTTP_POST;
+ fetch_request.fields["json"] = fastWriteJson(server);
fetch_request.multipart = true;
httpfetch_async(fetch_request);
}
#endif
} // namespace ServerList
-
diff --git a/src/settings.cpp b/src/settings.cpp
index 55404319e..f30ef34e9 100644
--- a/src/settings.cpp
+++ b/src/settings.cpp
@@ -45,7 +45,13 @@ Settings::~Settings()
Settings & Settings::operator += (const Settings &other)
{
- update(other);
+ if (&other == this)
+ return *this;
+
+ MutexAutoLock lock(m_mutex);
+ MutexAutoLock lock2(other.m_mutex);
+
+ updateNoLock(other);
return *this;
}
@@ -512,25 +518,6 @@ u32 Settings::getFlagStr(const std::string &name, const FlagDesc *flagdesc,
return flags;
}
-// N.B. if getStruct() is used to read a non-POD aggregate type,
-// the behavior is undefined.
-bool Settings::getStruct(const std::string &name, const std::string &format,
- void *out, size_t olen) const
-{
- std::string valstr;
-
- try {
- valstr = get(name);
- } catch (SettingNotFoundException &e) {
- return false;
- }
-
- if (!deSerializeStringToStruct(valstr, format, out, olen))
- return false;
-
- return true;
-}
-
bool Settings::getNoiseParams(const std::string &name, NoiseParams &np) const
{
@@ -546,6 +533,7 @@ bool Settings::getNoiseParamsFromValue(const std::string &name,
if (!getNoEx(name, value))
return false;
+ // Format: f32,f32,(f32,f32,f32),s32,s32,f32[,f32]
Strfnd f(value);
np.offset = stof(f.next(","));
@@ -615,28 +603,6 @@ std::vector<std::string> Settings::getNames() const
* Getters that don't throw exceptions *
***************************************/
-bool Settings::getEntryNoEx(const std::string &name, SettingsEntry &val) const
-{
- try {
- val = getEntry(name);
- return true;
- } catch (SettingNotFoundException &e) {
- return false;
- }
-}
-
-
-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 {
@@ -822,15 +788,23 @@ bool Settings::setDefault(const std::string &name, const std::string &value)
}
-bool Settings::setGroup(const std::string &name, Settings *group)
+bool Settings::setGroup(const std::string &name, const Settings &group)
{
- return setEntry(name, &group, true, false);
+ // Settings must own the group pointer
+ // avoid double-free by copying the source
+ Settings *copy = new Settings();
+ *copy = group;
+ return setEntry(name, &copy, true, false);
}
-bool Settings::setGroupDefault(const std::string &name, Settings *group)
+bool Settings::setGroupDefault(const std::string &name, const Settings &group)
{
- return setEntry(name, &group, true, true);
+ // Settings must own the group pointer
+ // avoid double-free by copying the source
+ Settings *copy = new Settings();
+ *copy = group;
+ return setEntry(name, &copy, true, true);
}
@@ -900,17 +874,6 @@ bool Settings::setFlagStr(const std::string &name, u32 flags,
}
-bool Settings::setStruct(const std::string &name, const std::string &format,
- void *value)
-{
- std::string structstr;
- if (!serializeStructToString(&structstr, format, value))
- return false;
-
- return set(name, structstr);
-}
-
-
bool Settings::setNoiseParams(const std::string &name,
const NoiseParams &np, bool set_default)
{
@@ -961,32 +924,6 @@ void Settings::clearDefaults()
clearDefaultsNoLock();
}
-void Settings::updateValue(const Settings &other, const std::string &name)
-{
- if (&other == this)
- return;
-
- MutexAutoLock lock(m_mutex);
-
- try {
- m_settings[name] = other.get(name);
- } catch (SettingNotFoundException &e) {
- }
-}
-
-
-void Settings::update(const Settings &other)
-{
- if (&other == this)
- return;
-
- MutexAutoLock lock(m_mutex);
- MutexAutoLock lock2(other.m_mutex);
-
- updateNoLock(other);
-}
-
-
SettingsParseEvent Settings::parseConfigObject(const std::string &line,
const std::string &end, std::string &name, std::string &value)
{
@@ -1052,7 +989,7 @@ void Settings::overrideDefaults(Settings *other)
{
for (const auto &setting : other->m_settings) {
if (setting.second.is_group) {
- setGroupDefault(setting.first, setting.second.group);
+ setGroupDefault(setting.first, *setting.second.group);
continue;
}
const FlagDesc *flagdesc = getFlagDescFallback(setting.first);
@@ -1102,6 +1039,19 @@ void Settings::deregisterChangedCallback(const std::string &name,
}
}
+void Settings::removeSecureSettings()
+{
+ for (const auto &name : getNames()) {
+ if (name.compare(0, 7, "secure.") != 0)
+ continue;
+
+ errorstream << "Secure setting " << name
+ << " isn't allowed, so was ignored."
+ << std::endl;
+ remove(name);
+ }
+}
+
void Settings::doCallbacks(const std::string &name) const
{
MutexAutoLock lock(m_callback_mutex);
diff --git a/src/settings.h b/src/settings.h
index 0c9a155db..6db2f9481 100644
--- a/src/settings.h
+++ b/src/settings.h
@@ -113,23 +113,10 @@ public:
bool parseConfigLines(std::istream &is, const std::string &end = "");
void writeLines(std::ostream &os, u32 tab_depth=0) const;
- SettingsParseEvent parseConfigObject(const std::string &line,
- const std::string &end, std::string &name, std::string &value);
- bool updateConfigObject(std::istream &is, std::ostream &os,
- const std::string &end, u32 tab_depth=0);
-
- static bool checkNameValid(const std::string &name);
- static bool checkValueValid(const std::string &value);
- static std::string getMultiline(std::istream &is, size_t *num_lines=NULL);
- static void printEntry(std::ostream &os, const std::string &name,
- const SettingsEntry &entry, u32 tab_depth=0);
-
/***********
* Getters *
***********/
- 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;
@@ -144,10 +131,6 @@ public:
v3f getV3F(const std::string &name) const;
u32 getFlagStr(const std::string &name, const FlagDesc *flagdesc,
u32 *flagmask) const;
- // N.B. if getStruct() is used to read a non-POD aggregate type,
- // the behavior is undefined.
- bool getStruct(const std::string &name, const std::string &format,
- void *out, size_t olen) const;
bool getNoiseParams(const std::string &name, NoiseParams &np) const;
bool getNoiseParamsFromValue(const std::string &name, NoiseParams &np) const;
bool getNoiseParamsFromGroup(const std::string &name, NoiseParams &np) const;
@@ -161,8 +144,6 @@ public:
* Getters that don't throw exceptions *
***************************************/
- 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;
@@ -192,8 +173,8 @@ public:
bool set_group, bool set_default);
bool set(const std::string &name, const std::string &value);
bool setDefault(const std::string &name, const std::string &value);
- bool setGroup(const std::string &name, Settings *group);
- bool setGroupDefault(const std::string &name, Settings *group);
+ bool setGroup(const std::string &name, const Settings &group);
+ bool setGroupDefault(const std::string &name, const Settings &group);
bool setBool(const std::string &name, bool value);
bool setS16(const std::string &name, s16 value);
bool setU16(const std::string &name, u16 value);
@@ -206,16 +187,11 @@ public:
const FlagDesc *flagdesc = nullptr, u32 flagmask = U32_MAX);
bool setNoiseParams(const std::string &name, const NoiseParams &np,
bool set_default=false);
- // N.B. if setStruct() is used to write a non-POD aggregate type,
- // the behavior is undefined.
- bool setStruct(const std::string &name, const std::string &format, void *value);
// remove a setting
bool remove(const std::string &name);
void clear();
void clearDefaults();
- void updateValue(const Settings &other, const std::string &name);
- void update(const Settings &other);
/**************
* Miscellany *
@@ -231,7 +207,34 @@ public:
void deregisterChangedCallback(const std::string &name,
SettingsChangedCallback cbf, void *userdata = NULL);
+ void removeSecureSettings();
+
private:
+ /***********************
+ * Reading and writing *
+ ***********************/
+
+ SettingsParseEvent parseConfigObject(const std::string &line,
+ const std::string &end, std::string &name, std::string &value);
+ bool updateConfigObject(std::istream &is, std::ostream &os,
+ const std::string &end, u32 tab_depth=0);
+
+ static bool checkNameValid(const std::string &name);
+ static bool checkValueValid(const std::string &value);
+ static std::string getMultiline(std::istream &is, size_t *num_lines=NULL);
+ static void printEntry(std::ostream &os, const std::string &name,
+ const SettingsEntry &entry, u32 tab_depth=0);
+
+ /***********
+ * Getters *
+ ***********/
+
+ const SettingsEntry &getEntry(const std::string &name) const;
+ const SettingsEntry &getEntryDefault(const std::string &name) const;
+
+ // Allow TestSettings to run sanity checks using private functions.
+ friend class TestSettings;
+
void updateNoLock(const Settings &other);
void clearNoLock();
void clearDefaultsNoLock();
diff --git a/src/settings_translation_file.cpp b/src/settings_translation_file.cpp
index febfbb9d3..0cd772337 100644
--- a/src/settings_translation_file.cpp
+++ b/src/settings_translation_file.cpp
@@ -242,13 +242,7 @@ fake_function() {
gettext("Enables Hable's 'Uncharted 2' filmic tone mapping.\nSimulates the tone curve of photographic film and how this approximates the\nappearance of high dynamic range images. Mid-range contrast is slightly\nenhanced, highlights and shadows are gradually compressed.");
gettext("Bumpmapping");
gettext("Bumpmapping");
- gettext("Enables bumpmapping for textures. Normalmaps need to be supplied by the texture pack\nor need to be auto-generated.\nRequires shaders to be enabled.");
- gettext("Generate normalmaps");
- gettext("Enables on the fly normalmap generation (Emboss effect).\nRequires bumpmapping to be enabled.");
- gettext("Normalmaps strength");
- gettext("Strength of generated normalmaps.");
- gettext("Normalmaps sampling");
- gettext("Defines sampling step of texture.\nA higher value results in smoother normal maps.");
+ gettext("Enables bumpmapping for textures. Normalmaps need to be supplied by the texture pack.\nRequires shaders to be enabled.");
gettext("Parallax Occlusion");
gettext("Parallax occlusion");
gettext("Enables parallax occlusion mapping.\nRequires shaders to be enabled.");
diff --git a/src/sound.h b/src/sound.h
index 6cbd55e8f..6f7b0a1af 100644
--- a/src/sound.h
+++ b/src/sound.h
@@ -39,7 +39,7 @@ struct SimpleSoundSpec
// keep in sync with item definitions
void serialize(std::ostream &os, u8 cf_version) const
{
- os << serializeString(name);
+ os << serializeString16(name);
writeF32(os, gain);
writeF32(os, pitch);
writeF32(os, fade);
@@ -49,7 +49,7 @@ struct SimpleSoundSpec
void deSerialize(std::istream &is, u8 cf_version)
{
- name = deSerializeString(is);
+ name = deSerializeString16(is);
gain = readF32(is);
pitch = readF32(is);
fade = readF32(is);
diff --git a/src/staticobject.cpp b/src/staticobject.cpp
index 5ccb7baf5..86e455b9f 100644
--- a/src/staticobject.cpp
+++ b/src/staticobject.cpp
@@ -35,7 +35,7 @@ void StaticObject::serialize(std::ostream &os)
// pos
writeV3F1000(os, pos);
// data
- os<<serializeString(data);
+ os<<serializeString16(data);
}
void StaticObject::deSerialize(std::istream &is, u8 version)
{
@@ -44,7 +44,7 @@ void StaticObject::deSerialize(std::istream &is, u8 version)
// pos
pos = readV3F1000(is);
// data
- data = deSerializeString(is);
+ data = deSerializeString16(is);
}
void StaticObjectList::serialize(std::ostream &os)
diff --git a/src/texture_override.cpp b/src/texture_override.cpp
index 10d129b87..effdb0efd 100644
--- a/src/texture_override.cpp
+++ b/src/texture_override.cpp
@@ -24,6 +24,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <algorithm>
#include <fstream>
+#define override_cast static_cast<override_t>
+
TextureOverrideSource::TextureOverrideSource(std::string filepath)
{
std::ifstream infile(filepath.c_str());
@@ -56,25 +58,37 @@ TextureOverrideSource::TextureOverrideSource(std::string filepath)
std::vector<std::string> targets = str_split(splitted[1], ',');
for (const std::string &target : targets) {
if (target == "top")
- texture_override.target |= static_cast<u8>(OverrideTarget::TOP);
+ texture_override.target |= override_cast(OverrideTarget::TOP);
else if (target == "bottom")
- texture_override.target |= static_cast<u8>(OverrideTarget::BOTTOM);
+ texture_override.target |= override_cast(OverrideTarget::BOTTOM);
else if (target == "left")
- texture_override.target |= static_cast<u8>(OverrideTarget::LEFT);
+ texture_override.target |= override_cast(OverrideTarget::LEFT);
else if (target == "right")
- texture_override.target |= static_cast<u8>(OverrideTarget::RIGHT);
+ texture_override.target |= override_cast(OverrideTarget::RIGHT);
else if (target == "front")
- texture_override.target |= static_cast<u8>(OverrideTarget::FRONT);
+ texture_override.target |= override_cast(OverrideTarget::FRONT);
else if (target == "back")
- texture_override.target |= static_cast<u8>(OverrideTarget::BACK);
+ texture_override.target |= override_cast(OverrideTarget::BACK);
else if (target == "inventory")
- texture_override.target |= static_cast<u8>(OverrideTarget::INVENTORY);
+ texture_override.target |= override_cast(OverrideTarget::INVENTORY);
else if (target == "wield")
- texture_override.target |= static_cast<u8>(OverrideTarget::WIELD);
+ texture_override.target |= override_cast(OverrideTarget::WIELD);
+ else if (target == "special1")
+ texture_override.target |= override_cast(OverrideTarget::SPECIAL_1);
+ else if (target == "special2")
+ texture_override.target |= override_cast(OverrideTarget::SPECIAL_2);
+ else if (target == "special3")
+ texture_override.target |= override_cast(OverrideTarget::SPECIAL_3);
+ else if (target == "special4")
+ texture_override.target |= override_cast(OverrideTarget::SPECIAL_4);
+ else if (target == "special5")
+ texture_override.target |= override_cast(OverrideTarget::SPECIAL_5);
+ else if (target == "special6")
+ texture_override.target |= override_cast(OverrideTarget::SPECIAL_6);
else if (target == "sides")
- texture_override.target |= static_cast<u8>(OverrideTarget::SIDES);
+ texture_override.target |= override_cast(OverrideTarget::SIDES);
else if (target == "all" || target == "*")
- texture_override.target |= static_cast<u8>(OverrideTarget::ALL_FACES);
+ texture_override.target |= override_cast(OverrideTarget::ALL_FACES);
else {
// Report invalid target
warningstream << filepath << ":" << line_index
@@ -85,7 +99,7 @@ TextureOverrideSource::TextureOverrideSource(std::string filepath)
}
// If there are no valid targets, skip adding this override
- if (texture_override.target == static_cast<u8>(OverrideTarget::INVALID)) {
+ if (texture_override.target == override_cast(OverrideTarget::INVALID)) {
continue;
}
@@ -112,7 +126,7 @@ std::vector<TextureOverride> TextureOverrideSource::getNodeTileOverrides()
std::vector<TextureOverride> found_overrides;
for (const TextureOverride &texture_override : m_overrides) {
- if (texture_override.hasTarget(OverrideTarget::ALL_FACES))
+ if (texture_override.hasTarget(OverrideTarget::NODE_TARGETS))
found_overrides.push_back(texture_override);
}
diff --git a/src/texture_override.h b/src/texture_override.h
index db03bd014..bdc95e732 100644
--- a/src/texture_override.h
+++ b/src/texture_override.h
@@ -23,8 +23,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <string>
#include <vector>
+typedef u16 override_t;
+
//! Bitmask enum specifying what a texture override should apply to
-enum class OverrideTarget : u8
+enum class OverrideTarget : override_t
{
INVALID = 0,
TOP = 1 << 0,
@@ -35,23 +37,33 @@ enum class OverrideTarget : u8
BACK = 1 << 5,
INVENTORY = 1 << 6,
WIELD = 1 << 7,
+ SPECIAL_1 = 1 << 8,
+ SPECIAL_2 = 1 << 9,
+ SPECIAL_3 = 1 << 10,
+ SPECIAL_4 = 1 << 11,
+ SPECIAL_5 = 1 << 12,
+ SPECIAL_6 = 1 << 13,
+ // clang-format off
SIDES = LEFT | RIGHT | FRONT | BACK,
ALL_FACES = TOP | BOTTOM | SIDES,
+ ALL_SPECIAL = SPECIAL_1 | SPECIAL_2 | SPECIAL_3 | SPECIAL_4 | SPECIAL_5 | SPECIAL_6,
+ NODE_TARGETS = ALL_FACES | ALL_SPECIAL,
ITEM_TARGETS = INVENTORY | WIELD,
+ // clang-format on
};
struct TextureOverride
{
std::string id;
std::string texture;
- u8 target;
+ override_t target;
// Helper function for checking if an OverrideTarget is found in
// a TextureOverride without casting
inline bool hasTarget(OverrideTarget overrideTarget) const
{
- return (target & static_cast<u8>(overrideTarget)) != 0;
+ return (target & static_cast<override_t>(overrideTarget)) != 0;
}
};
diff --git a/src/threading/thread.cpp b/src/threading/thread.cpp
index e0f808c4d..5cfc60995 100644
--- a/src/threading/thread.cpp
+++ b/src/threading/thread.cpp
@@ -33,6 +33,8 @@ DEALINGS IN THE SOFTWARE.
#include <sys/prctl.h>
#elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)
#include <pthread_np.h>
+#elif defined(__NetBSD__)
+ #include <sched.h>
#elif defined(_MSC_VER)
struct THREADNAME_INFO {
DWORD dwType; // Must be 0x1000
@@ -71,7 +73,28 @@ Thread::Thread(const std::string &name) :
Thread::~Thread()
{
- kill();
+ // kill the thread if running
+ if (!m_running) {
+ wait();
+ } else {
+
+ m_running = false;
+
+#if defined(_WIN32)
+ // See https://msdn.microsoft.com/en-us/library/hh920601.aspx#thread__native_handle_method
+ TerminateThread((HANDLE) m_thread_obj->native_handle(), 0);
+ CloseHandle((HANDLE) m_thread_obj->native_handle());
+#else
+ // We need to pthread_kill instead on Android since NDKv5's pthread
+ // implementation is incomplete.
+# ifdef __ANDROID__
+ pthread_kill(getThreadHandle(), SIGKILL);
+# else
+ pthread_cancel(getThreadHandle());
+# endif
+ wait();
+#endif
+ }
// Make sure start finished mutex is unlocked before it's destroyed
if (m_start_finished_mutex.try_lock())
@@ -136,37 +159,6 @@ bool Thread::wait()
}
-bool Thread::kill()
-{
- if (!m_running) {
- wait();
- return false;
- }
-
- m_running = false;
-
-#if defined(_WIN32)
- // See https://msdn.microsoft.com/en-us/library/hh920601.aspx#thread__native_handle_method
- TerminateThread((HANDLE) m_thread_obj->native_handle(), 0);
- CloseHandle((HANDLE) m_thread_obj->native_handle());
-#else
- // We need to pthread_kill instead on Android since NDKv5's pthread
- // implementation is incomplete.
-# ifdef __ANDROID__
- pthread_kill(getThreadHandle(), SIGKILL);
-# else
- pthread_cancel(getThreadHandle());
-# endif
- wait();
-#endif
-
- m_retval = nullptr;
- m_joinable = false;
- m_request_stop = false;
-
- return true;
-}
-
bool Thread::getReturnValue(void **ret)
{
@@ -219,12 +211,16 @@ void Thread::setName(const std::string &name)
#elif defined(__NetBSD__)
- pthread_setname_np(pthread_self(), name.c_str());
+ pthread_setname_np(pthread_self(), "%s", const_cast<char*>(name.c_str()));
#elif defined(__APPLE__)
pthread_setname_np(name.c_str());
+#elif defined(__HAIKU__)
+
+ rename_thread(find_thread(NULL), name.c_str());
+
#elif defined(_MSC_VER)
// Windows itself doesn't support thread names,
@@ -281,7 +277,14 @@ bool Thread::bindToProcessor(unsigned int proc_number)
CPU_SET(proc_number, &cpuset);
return pthread_setaffinity_np(getThreadHandle(), sizeof(cpuset), &cpuset) == 0;
+#elif defined(__NetBSD__)
+ cpuset_t *cpuset = cpuset_create();
+ if (cpuset == NULL)
+ return false;
+ int r = pthread_setaffinity_np(getThreadHandle(), cpuset_size(cpuset), cpuset);
+ cpuset_destroy(cpuset);
+ return r == 0;
#elif defined(__sun) || defined(sun)
return processor_bind(P_LWPID, P_MYID, proc_number, NULL) == 0
diff --git a/src/threading/thread.h b/src/threading/thread.h
index cea92226f..45fb171da 100644
--- a/src/threading/thread.h
+++ b/src/threading/thread.h
@@ -36,6 +36,10 @@ DEALINGS IN THE SOFTWARE.
#include <sys/thread.h> // for tid_t
#endif
+#ifdef __HAIKU__
+ #include <kernel/OS.h>
+#endif
+
/*
* On platforms using pthreads, these five priority classes correlate to
* even divisions between the minimum and maximum reported thread priority.
@@ -71,14 +75,6 @@ public:
bool stop();
/*
- * Immediately terminates the thread.
- * This should be used with extreme caution, as the thread will not have
- * any opportunity to release resources it may be holding (such as memory
- * or locks).
- */
- bool kill();
-
- /*
* Waits for thread to finish.
* Note: This does not stop a thread, you have to do this on your own.
* Returns false immediately if the thread is not started or has been waited
diff --git a/src/tool.cpp b/src/tool.cpp
index 22e41d28e..90f4f9c12 100644
--- a/src/tool.cpp
+++ b/src/tool.cpp
@@ -66,7 +66,7 @@ void ToolCapabilities::serialize(std::ostream &os, u16 protocol_version) const
for (const auto &groupcap : groupcaps) {
const std::string *name = &groupcap.first;
const ToolGroupCap *cap = &groupcap.second;
- os << serializeString(*name);
+ os << serializeString16(*name);
writeS16(os, cap->uses);
writeS16(os, cap->maxlevel);
writeU32(os, cap->times.size());
@@ -79,7 +79,7 @@ void ToolCapabilities::serialize(std::ostream &os, u16 protocol_version) const
writeU32(os, damageGroups.size());
for (const auto &damageGroup : damageGroups) {
- os << serializeString(damageGroup.first);
+ os << serializeString16(damageGroup.first);
writeS16(os, damageGroup.second);
}
@@ -98,7 +98,7 @@ void ToolCapabilities::deSerialize(std::istream &is)
groupcaps.clear();
u32 groupcaps_size = readU32(is);
for (u32 i = 0; i < groupcaps_size; i++) {
- std::string name = deSerializeString(is);
+ std::string name = deSerializeString16(is);
ToolGroupCap cap;
cap.uses = readS16(is);
cap.maxlevel = readS16(is);
@@ -113,7 +113,7 @@ void ToolCapabilities::deSerialize(std::istream &is)
u32 damage_groups_size = readU32(is);
for (u32 i = 0; i < damage_groups_size; i++) {
- std::string name = deSerializeString(is);
+ std::string name = deSerializeString16(is);
s16 rating = readS16(is);
damageGroups[name] = rating;
}
diff --git a/src/translation.cpp b/src/translation.cpp
index 8bbaee0a3..82e813a5d 100644
--- a/src/translation.cpp
+++ b/src/translation.cpp
@@ -29,14 +29,6 @@ Translations client_translations;
Translations *g_client_translations = &client_translations;
#endif
-// Per language server translations
-std::unordered_map<std::string,Translations> server_translations;
-std::unordered_map<std::string,Translations> *g_server_translations = &server_translations;
-
-Translations::~Translations()
-{
- clear();
-}
void Translations::clear()
{
diff --git a/src/translation.h b/src/translation.h
index 71423b15e..f1a336fca 100644
--- a/src/translation.h
+++ b/src/translation.h
@@ -23,7 +23,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <string>
class Translations;
-extern std::unordered_map<std::string, Translations> *g_server_translations;
#ifndef SERVER
extern Translations *g_client_translations;
#endif
@@ -31,10 +30,6 @@ extern Translations *g_client_translations;
class Translations
{
public:
- Translations() = default;
-
- ~Translations();
-
void loadTranslation(const std::string &data);
void clear();
const std::wstring &getTranslation(
diff --git a/src/unittest/test_map_settings_manager.cpp b/src/unittest/test_map_settings_manager.cpp
index 0a5c971b9..3d642a9b4 100644
--- a/src/unittest/test_map_settings_manager.cpp
+++ b/src/unittest/test_map_settings_manager.cpp
@@ -65,31 +65,6 @@ void check_noise_params(const NoiseParams *np1, const NoiseParams *np2)
}
-std::string read_file_to_string(const std::string &filepath)
-{
- std::string buf;
- FILE *f = fopen(filepath.c_str(), "rb");
- if (!f)
- return "";
-
- fseek(f, 0, SEEK_END);
-
- long filesize = ftell(f);
- if (filesize == -1) {
- fclose(f);
- return "";
- }
- rewind(f);
-
- buf.resize(filesize);
-
- UASSERTEQ(size_t, fread(&buf[0], 1, filesize, f), 1);
-
- fclose(f);
- return buf;
-}
-
-
void TestMapSettingsManager::makeUserConfig(Settings *conf)
{
conf->set("mg_name", "v7");
@@ -199,7 +174,8 @@ void TestMapSettingsManager::testMapSettingsManager()
};
SHA1 ctx;
- std::string metafile_contents = read_file_to_string(test_mapmeta_path);
+ std::string metafile_contents;
+ UASSERT(fs::ReadFile(test_mapmeta_path, metafile_contents));
ctx.addBytes(&metafile_contents[0], metafile_contents.size());
unsigned char *sha1_result = ctx.getDigest();
int resultdiff = memcmp(sha1_result, expected_contents_hash, 20);
diff --git a/src/unittest/test_serialization.cpp b/src/unittest/test_serialization.cpp
index 51e28f144..660d77d02 100644
--- a/src/unittest/test_serialization.cpp
+++ b/src/unittest/test_serialization.cpp
@@ -32,25 +32,19 @@ public:
void buildTestStrings();
void testSerializeString();
- void testSerializeWideString();
void testSerializeLongString();
void testSerializeJsonString();
- void testSerializeHex();
void testDeSerializeString();
- void testDeSerializeWideString();
void testDeSerializeLongString();
void testStreamRead();
void testStreamWrite();
- void testVecPut();
- void testStringLengthLimits();
- void testBufReader();
void testFloatFormat();
std::string teststring2;
std::wstring teststring2_w;
std::string teststring2_w_encoded;
- static const u8 test_serialized_data[12 * 13 - 8];
+ static const u8 test_serialized_data[12 * 11 - 2];
};
static TestSerialization g_test_instance;
@@ -61,17 +55,11 @@ void TestSerialization::runTests(IGameDef *gamedef)
TEST(testSerializeString);
TEST(testDeSerializeString);
- TEST(testSerializeWideString);
- TEST(testDeSerializeWideString);
TEST(testSerializeLongString);
TEST(testDeSerializeLongString);
TEST(testSerializeJsonString);
- TEST(testSerializeHex);
TEST(testStreamRead);
TEST(testStreamWrite);
- TEST(testVecPut);
- TEST(testStringLengthLimits);
- TEST(testBufReader);
TEST(testFloatFormat);
}
@@ -103,21 +91,21 @@ void TestSerialization::buildTestStrings()
void TestSerialization::testSerializeString()
{
// Test blank string
- UASSERT(serializeString("") == mkstr("\0\0"));
+ UASSERT(serializeString16("") == mkstr("\0\0"));
// Test basic string
- UASSERT(serializeString("Hello world!") == mkstr("\0\14Hello world!"));
+ UASSERT(serializeString16("Hello world!") == mkstr("\0\14Hello world!"));
// Test character range
- UASSERT(serializeString(teststring2) == mkstr("\1\0") + teststring2);
+ UASSERT(serializeString16(teststring2) == mkstr("\1\0") + teststring2);
}
void TestSerialization::testDeSerializeString()
{
// Test deserialize
{
- std::istringstream is(serializeString(teststring2), std::ios::binary);
- UASSERT(deSerializeString(is) == teststring2);
+ std::istringstream is(serializeString16(teststring2), std::ios::binary);
+ UASSERT(deSerializeString16(is) == teststring2);
UASSERT(!is.eof());
is.get();
UASSERT(is.eof());
@@ -126,78 +114,34 @@ void TestSerialization::testDeSerializeString()
// Test deserialize an incomplete length specifier
{
std::istringstream is(mkstr("\x53"), std::ios::binary);
- EXCEPTION_CHECK(SerializationError, deSerializeString(is));
+ EXCEPTION_CHECK(SerializationError, deSerializeString16(is));
}
// Test deserialize a string with incomplete data
{
std::istringstream is(mkstr("\x00\x55 abcdefg"), std::ios::binary);
- EXCEPTION_CHECK(SerializationError, deSerializeString(is));
- }
-}
-
-void TestSerialization::testSerializeWideString()
-{
- // Test blank string
- UASSERT(serializeWideString(L"") == mkstr("\0\0"));
-
- // Test basic string
- UASSERT(serializeWideString(utf8_to_wide("Hello world!")) ==
- mkstr("\0\14\0H\0e\0l\0l\0o\0 \0w\0o\0r\0l\0d\0!"));
-
- // Test character range
- UASSERT(serializeWideString(teststring2_w) ==
- mkstr("\1\0") + teststring2_w_encoded);
-}
-
-void TestSerialization::testDeSerializeWideString()
-{
- // Test deserialize
- {
- std::istringstream is(serializeWideString(teststring2_w), std::ios::binary);
- UASSERT(deSerializeWideString(is) == teststring2_w);
- UASSERT(!is.eof());
- is.get();
- UASSERT(is.eof());
- }
-
- // Test deserialize an incomplete length specifier
- {
- std::istringstream is(mkstr("\x53"), std::ios::binary);
- EXCEPTION_CHECK(SerializationError, deSerializeWideString(is));
- }
-
- // Test deserialize a string with an incomplete character
- {
- std::istringstream is(mkstr("\x00\x07\0a\0b\0c\0d\0e\0f\0"), std::ios::binary);
- EXCEPTION_CHECK(SerializationError, deSerializeWideString(is));
- }
-
- // Test deserialize a string with incomplete data
- {
- std::istringstream is(mkstr("\x00\x08\0a\0b\0c\0d\0e\0f"), std::ios::binary);
- EXCEPTION_CHECK(SerializationError, deSerializeWideString(is));
+ EXCEPTION_CHECK(SerializationError, deSerializeString16(is));
}
}
void TestSerialization::testSerializeLongString()
{
// Test blank string
- UASSERT(serializeLongString("") == mkstr("\0\0\0\0"));
+ UASSERT(serializeString32("") == mkstr("\0\0\0\0"));
// Test basic string
- UASSERT(serializeLongString("Hello world!") == mkstr("\0\0\0\14Hello world!"));
+ UASSERT(serializeString32("Hello world!") == mkstr("\0\0\0\14Hello world!"));
// Test character range
- UASSERT(serializeLongString(teststring2) == mkstr("\0\0\1\0") + teststring2);
+ UASSERT(serializeString32(teststring2) == mkstr("\0\0\1\0") + teststring2);
}
void TestSerialization::testDeSerializeLongString()
{
// Test deserialize
{
- std::istringstream is(serializeLongString(teststring2), std::ios::binary);
- UASSERT(deSerializeLongString(is) == teststring2);
+ std::istringstream is(serializeString32(teststring2), std::ios::binary);
+ UASSERT(deSerializeString32(is) == teststring2);
UASSERT(!is.eof());
is.get();
UASSERT(is.eof());
@@ -206,19 +150,19 @@ void TestSerialization::testDeSerializeLongString()
// Test deserialize an incomplete length specifier
{
std::istringstream is(mkstr("\x53"), std::ios::binary);
- EXCEPTION_CHECK(SerializationError, deSerializeLongString(is));
+ EXCEPTION_CHECK(SerializationError, deSerializeString32(is));
}
// Test deserialize a string with incomplete data
{
std::istringstream is(mkstr("\x00\x00\x00\x05 abc"), std::ios::binary);
- EXCEPTION_CHECK(SerializationError, deSerializeLongString(is));
+ EXCEPTION_CHECK(SerializationError, deSerializeString32(is));
}
// Test deserialize a string with a length too large
{
std::istringstream is(mkstr("\xFF\xFF\xFF\xFF blah"), std::ios::binary);
- EXCEPTION_CHECK(SerializationError, deSerializeLongString(is));
+ EXCEPTION_CHECK(SerializationError, deSerializeString32(is));
}
}
@@ -268,25 +212,6 @@ void TestSerialization::testSerializeJsonString()
UASSERT(is.eof());
}
-void TestSerialization::testSerializeHex()
-{
- // Test blank string
- UASSERT(serializeHexString("") == "");
- UASSERT(serializeHexString("", true) == "");
-
- // Test basic string
- UASSERT(serializeHexString("Hello world!") ==
- "48656c6c6f20776f726c6421");
- UASSERT(serializeHexString("Hello world!", true) ==
- "48 65 6c 6c 6f 20 77 6f 72 6c 64 21");
-
- // Test binary string
- UASSERT(serializeHexString(mkstr("\x00\x0a\xb0\x63\x1f\x00\xff")) ==
- "000ab0631f00ff");
- UASSERT(serializeHexString(mkstr("\x00\x0a\xb0\x63\x1f\x00\xff"), true) ==
- "00 0a b0 63 1f 00 ff");
-}
-
void TestSerialization::testStreamRead()
{
@@ -310,19 +235,17 @@ void TestSerialization::testStreamRead()
UASSERT(readF1000(is) == F1000_MIN);
UASSERT(readF1000(is) == F1000_MAX);
- UASSERT(deSerializeString(is) == "foobar!");
+ UASSERT(deSerializeString16(is) == "foobar!");
UASSERT(readV2S16(is) == v2s16(500, 500));
UASSERT(readV3S16(is) == v3s16(4207, 604, -30));
UASSERT(readV2S32(is) == v2s32(1920, 1080));
UASSERT(readV3S32(is) == v3s32(-400, 6400054, 290549855));
- UASSERT(deSerializeWideString(is) == L"\x02~woof~\x5455");
-
UASSERT(readV3F1000(is) == v3f(500, 10024.2f, -192.54f));
UASSERT(readARGB8(is) == video::SColor(255, 128, 50, 128));
- UASSERT(deSerializeLongString(is) == "some longer string here");
+ UASSERT(deSerializeString32(is) == "some longer string here");
UASSERT(is.rdbuf()->in_avail() == 2);
UASSERT(readU16(is) == 0xF00D);
@@ -350,7 +273,7 @@ void TestSerialization::testStreamWrite()
writeF1000(os, F1000_MIN);
writeF1000(os, F1000_MAX);
- os << serializeString("foobar!");
+ os << serializeString16("foobar!");
data = os.str();
UASSERT(data.size() < sizeof(test_serialized_data));
@@ -361,12 +284,10 @@ void TestSerialization::testStreamWrite()
writeV2S32(os, v2s32(1920, 1080));
writeV3S32(os, v3s32(-400, 6400054, 290549855));
- os << serializeWideString(L"\x02~woof~\x5455");
-
writeV3F1000(os, v3f(500, 10024.2f, -192.54f));
writeARGB8(os, video::SColor(255, 128, 50, 128));
- os << serializeLongString("some longer string here");
+ os << serializeString32("some longer string here");
writeU16(os, 0xF00D);
@@ -376,255 +297,6 @@ void TestSerialization::testStreamWrite()
}
-void TestSerialization::testVecPut()
-{
- std::vector<u8> buf;
-
- putU8(&buf, 0x11);
- putU16(&buf, 0x2233);
- putU32(&buf, 0x44556677);
- putU64(&buf, 0x8899AABBCCDDEEFFLL);
-
- putS8(&buf, -128);
- putS16(&buf, 30000);
- putS32(&buf, -6);
- putS64(&buf, -43);
-
- putF1000(&buf, 53.53467f);
- putF1000(&buf, -300000.32f);
- putF1000(&buf, F1000_MIN);
- putF1000(&buf, F1000_MAX);
-
- putString(&buf, "foobar!");
-
- putV2S16(&buf, v2s16(500, 500));
- putV3S16(&buf, v3s16(4207, 604, -30));
- putV2S32(&buf, v2s32(1920, 1080));
- putV3S32(&buf, v3s32(-400, 6400054, 290549855));
-
- putWideString(&buf, L"\x02~woof~\x5455");
-
- putV3F1000(&buf, v3f(500, 10024.2f, -192.54f));
- putARGB8(&buf, video::SColor(255, 128, 50, 128));
-
- putLongString(&buf, "some longer string here");
-
- putU16(&buf, 0xF00D);
-
- UASSERT(buf.size() == sizeof(test_serialized_data));
- UASSERT(!memcmp(&buf[0], test_serialized_data, sizeof(test_serialized_data)));
-}
-
-
-void TestSerialization::testStringLengthLimits()
-{
- std::vector<u8> buf;
- std::string too_long(STRING_MAX_LEN + 1, 'A');
- std::string way_too_large(LONG_STRING_MAX_LEN + 1, 'B');
- std::wstring too_long_wide(WIDE_STRING_MAX_LEN + 1, L'C');
-
- EXCEPTION_CHECK(SerializationError, putString(&buf, too_long));
-
- putLongString(&buf, too_long);
- too_long.resize(too_long.size() - 1);
- putString(&buf, too_long);
-
- EXCEPTION_CHECK(SerializationError, putWideString(&buf, too_long_wide));
- too_long_wide.resize(too_long_wide.size() - 1);
- putWideString(&buf, too_long_wide);
-}
-
-
-void TestSerialization::testBufReader()
-{
- u8 u8_data;
- u16 u16_data;
- u32 u32_data;
- u64 u64_data;
- s8 s8_data;
- s16 s16_data;
- s32 s32_data;
- s64 s64_data;
- f32 f32_data, f32_data2, f32_data3, f32_data4;
- video::SColor scolor_data;
- v2s16 v2s16_data;
- v3s16 v3s16_data;
- v2s32 v2s32_data;
- v3s32 v3s32_data;
- v3f v3f_data;
- std::string string_data;
- std::wstring widestring_data;
- std::string longstring_data;
- u8 raw_data[10] = {0};
-
- BufReader buf(test_serialized_data, sizeof(test_serialized_data));
-
- // Try reading data like normal
- UASSERT(buf.getU8() == 0x11);
- UASSERT(buf.getU16() == 0x2233);
- UASSERT(buf.getU32() == 0x44556677);
- UASSERT(buf.getU64() == 0x8899AABBCCDDEEFFLL);
- UASSERT(buf.getS8() == -128);
- UASSERT(buf.getS16() == 30000);
- UASSERT(buf.getS32() == -6);
- UASSERT(buf.getS64() == -43);
- UASSERT(buf.getF1000() == 53.534f);
- UASSERT(buf.getF1000() == -300000.32f);
- UASSERT(buf.getF1000() == F1000_MIN);
- UASSERT(buf.getF1000() == F1000_MAX);
- UASSERT(buf.getString() == "foobar!");
- UASSERT(buf.getV2S16() == v2s16(500, 500));
- UASSERT(buf.getV3S16() == v3s16(4207, 604, -30));
- UASSERT(buf.getV2S32() == v2s32(1920, 1080));
- UASSERT(buf.getV3S32() == v3s32(-400, 6400054, 290549855));
- UASSERT(buf.getWideString() == L"\x02~woof~\x5455");
- UASSERT(buf.getV3F1000() == v3f(500, 10024.2f, -192.54f));
- UASSERT(buf.getARGB8() == video::SColor(255, 128, 50, 128));
- UASSERT(buf.getLongString() == "some longer string here");
-
- // Verify the offset and data is unchanged after a failed read
- size_t orig_pos = buf.pos;
- u32_data = 0;
- UASSERT(buf.getU32NoEx(&u32_data) == false);
- UASSERT(buf.pos == orig_pos);
- UASSERT(u32_data == 0);
-
- // Now try the same for a failed string read
- UASSERT(buf.getStringNoEx(&string_data) == false);
- UASSERT(buf.pos == orig_pos);
- UASSERT(string_data == "");
-
- // Now try the same for a failed string read
- UASSERT(buf.getWideStringNoEx(&widestring_data) == false);
- UASSERT(buf.pos == orig_pos);
- UASSERT(widestring_data == L"");
-
- UASSERT(buf.getU16() == 0xF00D);
-
- UASSERT(buf.remaining() == 0);
-
- // Check to make sure these each blow exceptions as they're supposed to
- EXCEPTION_CHECK(SerializationError, buf.getU8());
- EXCEPTION_CHECK(SerializationError, buf.getU16());
- EXCEPTION_CHECK(SerializationError, buf.getU32());
- EXCEPTION_CHECK(SerializationError, buf.getU64());
-
- EXCEPTION_CHECK(SerializationError, buf.getS8());
- EXCEPTION_CHECK(SerializationError, buf.getS16());
- EXCEPTION_CHECK(SerializationError, buf.getS32());
- EXCEPTION_CHECK(SerializationError, buf.getS64());
-
- EXCEPTION_CHECK(SerializationError, buf.getF1000());
- EXCEPTION_CHECK(SerializationError, buf.getARGB8());
-
- EXCEPTION_CHECK(SerializationError, buf.getV2S16());
- EXCEPTION_CHECK(SerializationError, buf.getV3S16());
- EXCEPTION_CHECK(SerializationError, buf.getV2S32());
- EXCEPTION_CHECK(SerializationError, buf.getV3S32());
- EXCEPTION_CHECK(SerializationError, buf.getV3F1000());
-
- EXCEPTION_CHECK(SerializationError, buf.getString());
- EXCEPTION_CHECK(SerializationError, buf.getWideString());
- EXCEPTION_CHECK(SerializationError, buf.getLongString());
- EXCEPTION_CHECK(SerializationError,
- buf.getRawData(raw_data, sizeof(raw_data)));
-
- // See if we can skip backwards
- buf.pos = 5;
- UASSERT(buf.getRawDataNoEx(raw_data, 3) == true);
- UASSERT(raw_data[0] == 0x66);
- UASSERT(raw_data[1] == 0x77);
- UASSERT(raw_data[2] == 0x88);
-
- UASSERT(buf.getU32() == 0x99AABBCC);
- UASSERT(buf.pos == 12);
-
- // Now let's try it all over again using the NoEx variants
- buf.pos = 0;
-
- UASSERT(buf.getU8NoEx(&u8_data));
- UASSERT(buf.getU16NoEx(&u16_data));
- UASSERT(buf.getU32NoEx(&u32_data));
- UASSERT(buf.getU64NoEx(&u64_data));
-
- UASSERT(buf.getS8NoEx(&s8_data));
- UASSERT(buf.getS16NoEx(&s16_data));
- UASSERT(buf.getS32NoEx(&s32_data));
- UASSERT(buf.getS64NoEx(&s64_data));
-
- UASSERT(buf.getF1000NoEx(&f32_data));
- UASSERT(buf.getF1000NoEx(&f32_data2));
- UASSERT(buf.getF1000NoEx(&f32_data3));
- UASSERT(buf.getF1000NoEx(&f32_data4));
-
- UASSERT(buf.getStringNoEx(&string_data));
- UASSERT(buf.getV2S16NoEx(&v2s16_data));
- UASSERT(buf.getV3S16NoEx(&v3s16_data));
- UASSERT(buf.getV2S32NoEx(&v2s32_data));
- UASSERT(buf.getV3S32NoEx(&v3s32_data));
- UASSERT(buf.getWideStringNoEx(&widestring_data));
- UASSERT(buf.getV3F1000NoEx(&v3f_data));
- UASSERT(buf.getARGB8NoEx(&scolor_data));
-
- UASSERT(buf.getLongStringNoEx(&longstring_data));
-
- // and make sure we got the correct data
- UASSERT(u8_data == 0x11);
- UASSERT(u16_data == 0x2233);
- UASSERT(u32_data == 0x44556677);
- UASSERT(u64_data == 0x8899AABBCCDDEEFFLL);
- UASSERT(s8_data == -128);
- UASSERT(s16_data == 30000);
- UASSERT(s32_data == -6);
- UASSERT(s64_data == -43);
- UASSERT(f32_data == 53.534f);
- UASSERT(f32_data2 == -300000.32f);
- UASSERT(f32_data3 == F1000_MIN);
- UASSERT(f32_data4 == F1000_MAX);
- UASSERT(string_data == "foobar!");
- UASSERT(v2s16_data == v2s16(500, 500));
- UASSERT(v3s16_data == v3s16(4207, 604, -30));
- UASSERT(v2s32_data == v2s32(1920, 1080));
- UASSERT(v3s32_data == v3s32(-400, 6400054, 290549855));
- UASSERT(widestring_data == L"\x02~woof~\x5455");
- UASSERT(v3f_data == v3f(500, 10024.2f, -192.54f));
- UASSERT(scolor_data == video::SColor(255, 128, 50, 128));
- UASSERT(longstring_data == "some longer string here");
-
- UASSERT(buf.remaining() == 2);
- UASSERT(buf.getRawDataNoEx(raw_data, 3) == false);
- UASSERT(buf.remaining() == 2);
- UASSERT(buf.getRawDataNoEx(raw_data, 2) == true);
- UASSERT(raw_data[0] == 0xF0);
- UASSERT(raw_data[1] == 0x0D);
- UASSERT(buf.remaining() == 0);
-
- // Make sure no more available data causes a failure
- UASSERT(!buf.getU8NoEx(&u8_data));
- UASSERT(!buf.getU16NoEx(&u16_data));
- UASSERT(!buf.getU32NoEx(&u32_data));
- UASSERT(!buf.getU64NoEx(&u64_data));
-
- UASSERT(!buf.getS8NoEx(&s8_data));
- UASSERT(!buf.getS16NoEx(&s16_data));
- UASSERT(!buf.getS32NoEx(&s32_data));
- UASSERT(!buf.getS64NoEx(&s64_data));
-
- UASSERT(!buf.getF1000NoEx(&f32_data));
- UASSERT(!buf.getARGB8NoEx(&scolor_data));
-
- UASSERT(!buf.getV2S16NoEx(&v2s16_data));
- UASSERT(!buf.getV3S16NoEx(&v3s16_data));
- UASSERT(!buf.getV2S32NoEx(&v2s32_data));
- UASSERT(!buf.getV3S32NoEx(&v3s32_data));
- UASSERT(!buf.getV3F1000NoEx(&v3f_data));
-
- UASSERT(!buf.getStringNoEx(&string_data));
- UASSERT(!buf.getWideStringNoEx(&widestring_data));
- UASSERT(!buf.getLongStringNoEx(&longstring_data));
- UASSERT(!buf.getRawDataNoEx(raw_data, sizeof(raw_data)));
-}
-
void TestSerialization::testFloatFormat()
{
FloatType type = getFloatSerializationType();
@@ -708,7 +380,7 @@ void TestSerialization::testFloatFormat()
UASSERT(test_single(i));
}
-const u8 TestSerialization::test_serialized_data[12 * 13 - 8] = {
+const u8 TestSerialization::test_serialized_data[12 * 11 - 2] = {
0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc,
0xdd, 0xee, 0xff, 0x80, 0x75, 0x30, 0xff, 0xff, 0xff, 0xfa, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xd5, 0x00, 0x00, 0xd1, 0x1e, 0xee, 0x1e,
@@ -716,9 +388,7 @@ const u8 TestSerialization::test_serialized_data[12 * 13 - 8] = {
0x66, 0x6f, 0x6f, 0x62, 0x61, 0x72, 0x21, 0x01, 0xf4, 0x01, 0xf4, 0x10,
0x6f, 0x02, 0x5c, 0xff, 0xe2, 0x00, 0x00, 0x07, 0x80, 0x00, 0x00, 0x04,
0x38, 0xff, 0xff, 0xfe, 0x70, 0x00, 0x61, 0xa8, 0x36, 0x11, 0x51, 0x70,
- 0x5f, 0x00, 0x08, 0x00,
- 0x02, 0x00, 0x7e, 0x00, 'w', 0x00, 'o', 0x00, 'o', 0x00, 'f', 0x00, // \x02~woof~\x5455
- 0x7e, 0x54, 0x55, 0x00, 0x07, 0xa1, 0x20, 0x00, 0x98, 0xf5, 0x08, 0xff,
+ 0x5f, 0x00, 0x07, 0xa1, 0x20, 0x00, 0x98, 0xf5, 0x08, 0xff,
0xfd, 0x0f, 0xe4, 0xff, 0x80, 0x32, 0x80, 0x00, 0x00, 0x00, 0x17, 0x73,
0x6f, 0x6d, 0x65, 0x20, 0x6c, 0x6f, 0x6e, 0x67, 0x65, 0x72, 0x20, 0x73,
0x74, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x68, 0x65, 0x72, 0x65, 0xF0, 0x0D,
diff --git a/src/unittest/test_servermodmanager.cpp b/src/unittest/test_servermodmanager.cpp
index 799936757..e3edb0c32 100644
--- a/src/unittest/test_servermodmanager.cpp
+++ b/src/unittest/test_servermodmanager.cpp
@@ -169,6 +169,4 @@ void TestServerModManager::testGetModMediaPaths()
std::vector<std::string> result;
sm.getModsMediaPaths(result);
UASSERTEQ(bool, result.empty(), false);
- // We should have 5 folders for each mod (textures, media, locale, model, sounds)
- UASSERTEQ(unsigned long, result.size() % 5, 0);
}
diff --git a/src/unittest/test_settings.cpp b/src/unittest/test_settings.cpp
index aa56f3e06..f91ba5b67 100644
--- a/src/unittest/test_settings.cpp
+++ b/src/unittest/test_settings.cpp
@@ -149,15 +149,15 @@ void TestSettings::testAllSettings()
UASSERT(group->getS16("a") == 5);
UASSERT(fabs(group->getFloat("bb") - 2.5) < 0.001);
- Settings *group3 = new Settings;
- group3->set("cat", "meow");
- group3->set("dog", "woof");
-
- Settings *group2 = new Settings;
- group2->setS16("num_apples", 4);
- group2->setS16("num_oranges", 53);
- group2->setGroup("animals", group3);
- group2->set("animals", "cute"); //destroys group 3
+ Settings group3;
+ group3.set("cat", "meow");
+ group3.set("dog", "woof");
+
+ Settings group2;
+ group2.setS16("num_apples", 4);
+ group2.setS16("num_oranges", 53);
+ group2.setGroup("animals", group3);
+ group2.set("animals", "cute"); //destroys group 3
s.setGroup("groupy_thing", group2);
// Test set failure conditions
diff --git a/src/unittest/test_threading.cpp b/src/unittest/test_threading.cpp
index 8d4d814fd..65ef7c02d 100644
--- a/src/unittest/test_threading.cpp
+++ b/src/unittest/test_threading.cpp
@@ -31,7 +31,6 @@ public:
void runTests(IGameDef *gamedef);
void testStartStopWait();
- void testThreadKill();
void testAtomicSemaphoreThread();
};
@@ -40,7 +39,6 @@ static TestThreading g_test_instance;
void TestThreading::runTests(IGameDef *gamedef)
{
TEST(testStartStopWait);
- TEST(testThreadKill);
TEST(testAtomicSemaphoreThread);
}
@@ -111,29 +109,6 @@ void TestThreading::testStartStopWait()
}
-void TestThreading::testThreadKill()
-{
- SimpleTestThread *thread = new SimpleTestThread(300);
-
- UASSERT(thread->start() == true);
-
- // kill()ing is quite violent, so let's make sure our victim is sleeping
- // before we do this... so we don't corrupt the rest of the program's state
- sleep_ms(100);
- UASSERT(thread->kill() == true);
-
- // The state of the thread object should be reset if all went well
- UASSERT(thread->isRunning() == false);
- UASSERT(thread->start() == true);
- UASSERT(thread->stop() == true);
- UASSERT(thread->wait() == true);
-
- // kill() after already waiting should fail.
- UASSERT(thread->kill() == false);
-
- delete thread;
-}
-
class AtomicTestThread : public Thread {
public:
diff --git a/src/util/base64.cpp b/src/util/base64.cpp
index c75f98598..6e1584410 100644
--- a/src/util/base64.cpp
+++ b/src/util/base64.cpp
@@ -34,8 +34,9 @@ static const std::string base64_chars =
"0123456789+/";
-static inline bool is_base64(unsigned char c) {
- return (isalnum(c) || (c == '+') || (c == '/'));
+static inline bool is_base64(unsigned char c)
+{
+ return isalnum(c) || c == '+' || c == '/' || c == '=';
}
bool base64_is_valid(std::string const& s)
diff --git a/src/util/md32_common.h b/src/util/md32_common.h
index a4c2099c9..2c050b72a 100644
--- a/src/util/md32_common.h
+++ b/src/util/md32_common.h
@@ -301,7 +301,7 @@
int HASH_UPDATE(HASH_CTX *c, const void *data_, size_t len)
{
- const unsigned char *data = data_;
+ const unsigned char *data = (const unsigned char *)data_;
unsigned char *p;
HASH_LONG l;
size_t n;
diff --git a/src/util/serialize.cpp b/src/util/serialize.cpp
index 5b276668d..d770101f2 100644
--- a/src/util/serialize.cpp
+++ b/src/util/serialize.cpp
@@ -30,86 +30,18 @@ with this program; if not, write to the Free Software Foundation, Inc.,
FloatType g_serialize_f32_type = FLOATTYPE_UNKNOWN;
-////
-//// BufReader
-////
-
-bool BufReader::getStringNoEx(std::string *val)
-{
- u16 num_chars;
- if (!getU16NoEx(&num_chars))
- return false;
-
- if (pos + num_chars > size) {
- pos -= sizeof(num_chars);
- return false;
- }
-
- val->assign((const char *)data + pos, num_chars);
- pos += num_chars;
-
- return true;
-}
-
-bool BufReader::getWideStringNoEx(std::wstring *val)
-{
- u16 num_chars;
- if (!getU16NoEx(&num_chars))
- return false;
-
- if (pos + num_chars * 2 > size) {
- pos -= sizeof(num_chars);
- return false;
- }
-
- for (size_t i = 0; i != num_chars; i++) {
- val->push_back(readU16(data + pos));
- pos += 2;
- }
-
- return true;
-}
-
-bool BufReader::getLongStringNoEx(std::string *val)
-{
- u32 num_chars;
- if (!getU32NoEx(&num_chars))
- return false;
-
- if (pos + num_chars > size) {
- pos -= sizeof(num_chars);
- return false;
- }
-
- val->assign((const char *)data + pos, num_chars);
- pos += num_chars;
-
- return true;
-}
-
-bool BufReader::getRawDataNoEx(void *val, size_t len)
-{
- if (pos + len > size)
- return false;
-
- memcpy(val, data + pos, len);
- pos += len;
-
- return true;
-}
-
////
//// String
////
-std::string serializeString(const std::string &plain)
+std::string serializeString16(const std::string &plain)
{
std::string s;
char buf[2];
if (plain.size() > STRING_MAX_LEN)
- throw SerializationError("String too long for serializeString");
+ throw SerializationError("String too long for serializeString16");
s.reserve(2 + plain.size());
writeU16((u8 *)&buf[0], plain.size());
@@ -119,14 +51,14 @@ std::string serializeString(const std::string &plain)
return s;
}
-std::string deSerializeString(std::istream &is)
+std::string deSerializeString16(std::istream &is)
{
std::string s;
char buf[2];
is.read(buf, 2);
if (is.gcount() != 2)
- throw SerializationError("deSerializeString: size not read");
+ throw SerializationError("deSerializeString16: size not read");
u16 s_size = readU16((u8 *)buf);
if (s_size == 0)
@@ -135,66 +67,17 @@ std::string deSerializeString(std::istream &is)
s.resize(s_size);
is.read(&s[0], s_size);
if (is.gcount() != s_size)
- throw SerializationError("deSerializeString: couldn't read all chars");
+ throw SerializationError("deSerializeString16: couldn't read all chars");
return s;
}
-////
-//// Wide String
-////
-
-std::string serializeWideString(const std::wstring &plain)
-{
- std::string s;
- char buf[2];
-
- if (plain.size() > WIDE_STRING_MAX_LEN)
- throw SerializationError("String too long for serializeWideString");
- s.reserve(2 + 2 * plain.size());
-
- writeU16((u8 *)buf, plain.size());
- s.append(buf, 2);
-
- for (wchar_t i : plain) {
- writeU16((u8 *)buf, i);
- s.append(buf, 2);
- }
- return s;
-}
-
-std::wstring deSerializeWideString(std::istream &is)
-{
- std::wstring s;
- char buf[2];
-
- is.read(buf, 2);
- if (is.gcount() != 2)
- throw SerializationError("deSerializeWideString: size not read");
-
- u16 s_size = readU16((u8 *)buf);
- if (s_size == 0)
- return s;
-
- s.reserve(s_size);
- for (u32 i = 0; i < s_size; i++) {
- is.read(&buf[0], 2);
- if (is.gcount() != 2) {
- throw SerializationError(
- "deSerializeWideString: couldn't read all chars");
- }
-
- wchar_t c16 = readU16((u8 *)buf);
- s.append(&c16, 1);
- }
- return s;
-}
////
//// Long String
////
-std::string serializeLongString(const std::string &plain)
+std::string serializeString32(const std::string &plain)
{
std::string s;
char buf[4];
@@ -209,7 +92,7 @@ std::string serializeLongString(const std::string &plain)
return s;
}
-std::string deSerializeLongString(std::istream &is)
+std::string deSerializeString32(std::istream &is)
{
std::string s;
char buf[4];
@@ -404,284 +287,3 @@ std::string deSerializeJsonStringIfNeeded(std::istream &is)
return tmp_os.str();
}
-////
-//// String/Struct conversions
-////
-
-bool deSerializeStringToStruct(std::string valstr,
- std::string format, void *out, size_t olen)
-{
- size_t len = olen;
- std::vector<std::string *> strs_alloced;
- std::string *str;
- char *f, *snext;
- size_t pos;
-
- char *s = &valstr[0];
- char *buf = new char[len];
- char *bufpos = buf;
-
- char *fmtpos, *fmt = &format[0];
- while ((f = strtok_r(fmt, ",", &fmtpos)) && s) {
- fmt = nullptr;
-
- bool is_unsigned = false;
- int width = 0;
- char valtype = *f;
-
- width = (int)strtol(f + 1, &f, 10);
- if (width && valtype == 's')
- valtype = 'i';
-
- switch (valtype) {
- case 'u':
- is_unsigned = true;
- /* FALLTHROUGH */
- case 'i':
- if (width == 16) {
- bufpos += PADDING(bufpos, u16);
- if ((bufpos - buf) + sizeof(u16) <= len) {
- if (is_unsigned)
- *(u16 *)bufpos = (u16)strtoul(s, &s, 10);
- else
- *(s16 *)bufpos = (s16)strtol(s, &s, 10);
- }
- bufpos += sizeof(u16);
- } else if (width == 32) {
- bufpos += PADDING(bufpos, u32);
- if ((bufpos - buf) + sizeof(u32) <= len) {
- if (is_unsigned)
- *(u32 *)bufpos = (u32)strtoul(s, &s, 10);
- else
- *(s32 *)bufpos = (s32)strtol(s, &s, 10);
- }
- bufpos += sizeof(u32);
- } else if (width == 64) {
- bufpos += PADDING(bufpos, u64);
- if ((bufpos - buf) + sizeof(u64) <= len) {
- if (is_unsigned)
- *(u64 *)bufpos = (u64)strtoull(s, &s, 10);
- else
- *(s64 *)bufpos = (s64)strtoll(s, &s, 10);
- }
- bufpos += sizeof(u64);
- }
- s = strchr(s, ',');
- break;
- case 'b':
- snext = strchr(s, ',');
- if (snext)
- *snext++ = 0;
-
- bufpos += PADDING(bufpos, bool);
- if ((bufpos - buf) + sizeof(bool) <= len)
- *(bool *)bufpos = is_yes(std::string(s));
- bufpos += sizeof(bool);
-
- s = snext;
- break;
- case 'f':
- bufpos += PADDING(bufpos, float);
- if ((bufpos - buf) + sizeof(float) <= len)
- *(float *)bufpos = strtof(s, &s);
- bufpos += sizeof(float);
-
- s = strchr(s, ',');
- break;
- case 's':
- while (*s == ' ' || *s == '\t')
- s++;
- if (*s++ != '"') //error, expected string
- goto fail;
- snext = s;
-
- while (snext[0] && !(snext[-1] != '\\' && snext[0] == '"'))
- snext++;
- *snext++ = 0;
-
- bufpos += PADDING(bufpos, std::string *);
-
- str = new std::string(s);
- pos = 0;
- while ((pos = str->find("\\\"", pos)) != std::string::npos)
- str->erase(pos, 1);
-
- if ((bufpos - buf) + sizeof(std::string *) <= len)
- *(std::string **)bufpos = str;
- bufpos += sizeof(std::string *);
- strs_alloced.push_back(str);
-
- s = *snext ? snext + 1 : nullptr;
- break;
- case 'v':
- while (*s == ' ' || *s == '\t')
- s++;
- if (*s++ != '(') //error, expected vector
- goto fail;
-
- if (width == 2) {
- bufpos += PADDING(bufpos, v2f);
-
- if ((bufpos - buf) + sizeof(v2f) <= len) {
- v2f *v = (v2f *)bufpos;
- v->X = strtof(s, &s);
- s++;
- v->Y = strtof(s, &s);
- }
-
- bufpos += sizeof(v2f);
- } else if (width == 3) {
- bufpos += PADDING(bufpos, v3f);
- if ((bufpos - buf) + sizeof(v3f) <= len) {
- v3f *v = (v3f *)bufpos;
- v->X = strtof(s, &s);
- s++;
- v->Y = strtof(s, &s);
- s++;
- v->Z = strtof(s, &s);
- }
-
- bufpos += sizeof(v3f);
- }
- s = strchr(s, ',');
- break;
- default: //error, invalid format specifier
- goto fail;
- }
-
- if (s && *s == ',')
- s++;
-
- if ((size_t)(bufpos - buf) > len) //error, buffer too small
- goto fail;
- }
-
- if (f && *f) { //error, mismatched number of fields and values
-fail:
- for (size_t i = 0; i != strs_alloced.size(); i++)
- delete strs_alloced[i];
- delete[] buf;
- return false;
- }
-
- memcpy(out, buf, olen);
- delete[] buf;
- return true;
-}
-
-// Casts *buf to a signed or unsigned fixed-width integer of 'w' width
-#define SIGN_CAST(w, buf) (is_unsigned ? *((u##w *) buf) : *((s##w *) buf))
-
-bool serializeStructToString(std::string *out,
- std::string format, void *value)
-{
- std::ostringstream os;
- std::string str;
- char *f;
- size_t strpos;
-
- char *bufpos = (char *) value;
- char *fmtpos, *fmt = &format[0];
- while ((f = strtok_r(fmt, ",", &fmtpos))) {
- fmt = nullptr;
- bool is_unsigned = false;
- int width = 0;
- char valtype = *f;
-
- width = (int)strtol(f + 1, &f, 10);
- if (width && valtype == 's')
- valtype = 'i';
-
- switch (valtype) {
- case 'u':
- is_unsigned = true;
- /* FALLTHROUGH */
- case 'i':
- if (width == 16) {
- bufpos += PADDING(bufpos, u16);
- os << SIGN_CAST(16, bufpos);
- bufpos += sizeof(u16);
- } else if (width == 32) {
- bufpos += PADDING(bufpos, u32);
- os << SIGN_CAST(32, bufpos);
- bufpos += sizeof(u32);
- } else if (width == 64) {
- bufpos += PADDING(bufpos, u64);
- os << SIGN_CAST(64, bufpos);
- bufpos += sizeof(u64);
- }
- break;
- case 'b':
- bufpos += PADDING(bufpos, bool);
- os << std::boolalpha << *((bool *) bufpos);
- bufpos += sizeof(bool);
- break;
- case 'f':
- bufpos += PADDING(bufpos, float);
- os << *((float *) bufpos);
- bufpos += sizeof(float);
- break;
- case 's':
- bufpos += PADDING(bufpos, std::string *);
- str = **((std::string **) bufpos);
-
- strpos = 0;
- while ((strpos = str.find('"', strpos)) != std::string::npos) {
- str.insert(strpos, 1, '\\');
- strpos += 2;
- }
-
- os << str;
- bufpos += sizeof(std::string *);
- break;
- case 'v':
- if (width == 2) {
- bufpos += PADDING(bufpos, v2f);
- v2f *v = (v2f *) bufpos;
- os << '(' << v->X << ", " << v->Y << ')';
- bufpos += sizeof(v2f);
- } else {
- bufpos += PADDING(bufpos, v3f);
- v3f *v = (v3f *) bufpos;
- os << '(' << v->X << ", " << v->Y << ", " << v->Z << ')';
- bufpos += sizeof(v3f);
- }
- break;
- default:
- return false;
- }
- os << ", ";
- }
- *out = os.str();
-
- // Trim off the trailing comma and space
- if (out->size() >= 2)
- out->resize(out->size() - 2);
-
- return true;
-}
-
-#undef SIGN_CAST
-
-////
-//// Other
-////
-
-std::string serializeHexString(const std::string &data, bool insert_spaces)
-{
- std::string result;
- result.reserve(data.size() * (2 + insert_spaces));
-
- static const char hex_chars[] = "0123456789abcdef";
-
- const size_t len = data.size();
- for (size_t i = 0; i != len; i++) {
- u8 byte = data[i];
- result.push_back(hex_chars[(byte >> 4) & 0x0F]);
- result.push_back(hex_chars[(byte >> 0) & 0x0F]);
- if (insert_spaces && i != len - 1)
- result.push_back(' ');
- }
-
- return result;
-}
diff --git a/src/util/serialize.h b/src/util/serialize.h
index a4b5a234a..b3ec28eab 100644
--- a/src/util/serialize.h
+++ b/src/util/serialize.h
@@ -440,22 +440,16 @@ MAKE_STREAM_WRITE_FXN(video::SColor, ARGB8, 4);
////
// Creates a string with the length as the first two bytes
-std::string serializeString(const std::string &plain);
-
-// Creates a string with the length as the first two bytes from wide string
-std::string serializeWideString(const std::wstring &plain);
+std::string serializeString16(const std::string &plain);
// Reads a string with the length as the first two bytes
-std::string deSerializeString(std::istream &is);
-
-// Reads a wide string with the length as the first two bytes
-std::wstring deSerializeWideString(std::istream &is);
+std::string deSerializeString16(std::istream &is);
// Creates a string with the length as the first four bytes
-std::string serializeLongString(const std::string &plain);
+std::string serializeString32(const std::string &plain);
// Reads a string with the length as the first four bytes
-std::string deSerializeLongString(std::istream &is);
+std::string deSerializeString32(std::istream &is);
// Creates a string encoded in JSON format (almost equivalent to a C string literal)
std::string serializeJsonString(const std::string &plain);
@@ -469,239 +463,3 @@ std::string serializeJsonStringIfNeeded(const std::string &s);
// Parses a string serialized by serializeJsonStringIfNeeded.
std::string deSerializeJsonStringIfNeeded(std::istream &is);
-
-// Creates a string consisting of the hexadecimal representation of `data`
-std::string serializeHexString(const std::string &data, bool insert_spaces=false);
-
-// Creates a string containing comma delimited values of a struct whose layout is
-// described by the parameter format
-bool serializeStructToString(std::string *out,
- std::string format, void *value);
-
-// Reads a comma delimited string of values into a struct whose layout is
-// decribed by the parameter format
-bool deSerializeStringToStruct(std::string valstr,
- std::string format, void *out, size_t olen);
-
-////
-//// BufReader
-////
-
-#define MAKE_BUFREADER_GETNOEX_FXN(T, N, S) \
- inline bool get ## N ## NoEx(T *val) \
- { \
- if (pos + S > size) \
- return false; \
- *val = read ## N(data + pos); \
- pos += S; \
- return true; \
- }
-
-#define MAKE_BUFREADER_GET_FXN(T, N) \
- inline T get ## N() \
- { \
- T val; \
- if (!get ## N ## NoEx(&val)) \
- throw SerializationError("Attempted read past end of data"); \
- return val; \
- }
-
-class BufReader {
-public:
- BufReader(const u8 *data_, size_t size_) :
- data(data_),
- size(size_)
- {
- }
-
- MAKE_BUFREADER_GETNOEX_FXN(u8, U8, 1);
- MAKE_BUFREADER_GETNOEX_FXN(u16, U16, 2);
- MAKE_BUFREADER_GETNOEX_FXN(u32, U32, 4);
- MAKE_BUFREADER_GETNOEX_FXN(u64, U64, 8);
- MAKE_BUFREADER_GETNOEX_FXN(s8, S8, 1);
- MAKE_BUFREADER_GETNOEX_FXN(s16, S16, 2);
- MAKE_BUFREADER_GETNOEX_FXN(s32, S32, 4);
- MAKE_BUFREADER_GETNOEX_FXN(s64, S64, 8);
- MAKE_BUFREADER_GETNOEX_FXN(f32, F1000, 4);
- MAKE_BUFREADER_GETNOEX_FXN(v2s16, V2S16, 4);
- MAKE_BUFREADER_GETNOEX_FXN(v3s16, V3S16, 6);
- MAKE_BUFREADER_GETNOEX_FXN(v2s32, V2S32, 8);
- MAKE_BUFREADER_GETNOEX_FXN(v3s32, V3S32, 12);
- MAKE_BUFREADER_GETNOEX_FXN(v3f, V3F1000, 12);
- MAKE_BUFREADER_GETNOEX_FXN(video::SColor, ARGB8, 4);
-
- bool getStringNoEx(std::string *val);
- bool getWideStringNoEx(std::wstring *val);
- bool getLongStringNoEx(std::string *val);
- bool getRawDataNoEx(void *data, size_t len);
-
- MAKE_BUFREADER_GET_FXN(u8, U8);
- MAKE_BUFREADER_GET_FXN(u16, U16);
- MAKE_BUFREADER_GET_FXN(u32, U32);
- MAKE_BUFREADER_GET_FXN(u64, U64);
- MAKE_BUFREADER_GET_FXN(s8, S8);
- MAKE_BUFREADER_GET_FXN(s16, S16);
- MAKE_BUFREADER_GET_FXN(s32, S32);
- MAKE_BUFREADER_GET_FXN(s64, S64);
- MAKE_BUFREADER_GET_FXN(f32, F1000);
- MAKE_BUFREADER_GET_FXN(v2s16, V2S16);
- MAKE_BUFREADER_GET_FXN(v3s16, V3S16);
- MAKE_BUFREADER_GET_FXN(v2s32, V2S32);
- MAKE_BUFREADER_GET_FXN(v3s32, V3S32);
- MAKE_BUFREADER_GET_FXN(v3f, V3F1000);
- MAKE_BUFREADER_GET_FXN(video::SColor, ARGB8);
- MAKE_BUFREADER_GET_FXN(std::string, String);
- MAKE_BUFREADER_GET_FXN(std::wstring, WideString);
- MAKE_BUFREADER_GET_FXN(std::string, LongString);
-
- inline void getRawData(void *val, size_t len)
- {
- if (!getRawDataNoEx(val, len))
- throw SerializationError("Attempted read past end of data");
- }
-
- inline size_t remaining()
- {
- assert(pos <= size);
- return size - pos;
- }
-
- const u8 *data;
- size_t size;
- size_t pos = 0;
-};
-
-#undef MAKE_BUFREADER_GET_FXN
-#undef MAKE_BUFREADER_GETNOEX_FXN
-
-
-////
-//// Vector-based write routines
-////
-
-inline void putU8(std::vector<u8> *dest, u8 val)
-{
- dest->push_back((val >> 0) & 0xFF);
-}
-
-inline void putU16(std::vector<u8> *dest, u16 val)
-{
- dest->push_back((val >> 8) & 0xFF);
- dest->push_back((val >> 0) & 0xFF);
-}
-
-inline void putU32(std::vector<u8> *dest, u32 val)
-{
- dest->push_back((val >> 24) & 0xFF);
- dest->push_back((val >> 16) & 0xFF);
- dest->push_back((val >> 8) & 0xFF);
- dest->push_back((val >> 0) & 0xFF);
-}
-
-inline void putU64(std::vector<u8> *dest, u64 val)
-{
- dest->push_back((val >> 56) & 0xFF);
- dest->push_back((val >> 48) & 0xFF);
- dest->push_back((val >> 40) & 0xFF);
- dest->push_back((val >> 32) & 0xFF);
- dest->push_back((val >> 24) & 0xFF);
- dest->push_back((val >> 16) & 0xFF);
- dest->push_back((val >> 8) & 0xFF);
- dest->push_back((val >> 0) & 0xFF);
-}
-
-inline void putS8(std::vector<u8> *dest, s8 val)
-{
- putU8(dest, val);
-}
-
-inline void putS16(std::vector<u8> *dest, s16 val)
-{
- putU16(dest, val);
-}
-
-inline void putS32(std::vector<u8> *dest, s32 val)
-{
- putU32(dest, val);
-}
-
-inline void putS64(std::vector<u8> *dest, s64 val)
-{
- putU64(dest, val);
-}
-
-inline void putF1000(std::vector<u8> *dest, f32 val)
-{
- putS32(dest, val * FIXEDPOINT_FACTOR);
-}
-
-inline void putV2S16(std::vector<u8> *dest, v2s16 val)
-{
- putS16(dest, val.X);
- putS16(dest, val.Y);
-}
-
-inline void putV3S16(std::vector<u8> *dest, v3s16 val)
-{
- putS16(dest, val.X);
- putS16(dest, val.Y);
- putS16(dest, val.Z);
-}
-
-inline void putV2S32(std::vector<u8> *dest, v2s32 val)
-{
- putS32(dest, val.X);
- putS32(dest, val.Y);
-}
-
-inline void putV3S32(std::vector<u8> *dest, v3s32 val)
-{
- putS32(dest, val.X);
- putS32(dest, val.Y);
- putS32(dest, val.Z);
-}
-
-inline void putV3F1000(std::vector<u8> *dest, v3f val)
-{
- putF1000(dest, val.X);
- putF1000(dest, val.Y);
- putF1000(dest, val.Z);
-}
-
-inline void putARGB8(std::vector<u8> *dest, video::SColor val)
-{
- putU32(dest, val.color);
-}
-
-inline void putString(std::vector<u8> *dest, const std::string &val)
-{
- if (val.size() > STRING_MAX_LEN)
- throw SerializationError("String too long");
-
- putU16(dest, val.size());
- dest->insert(dest->end(), val.begin(), val.end());
-}
-
-inline void putWideString(std::vector<u8> *dest, const std::wstring &val)
-{
- if (val.size() > WIDE_STRING_MAX_LEN)
- throw SerializationError("String too long");
-
- putU16(dest, val.size());
- for (size_t i = 0; i != val.size(); i++)
- putU16(dest, val[i]);
-}
-
-inline void putLongString(std::vector<u8> *dest, const std::string &val)
-{
- if (val.size() > LONG_STRING_MAX_LEN)
- throw SerializationError("String too long");
-
- putU32(dest, val.size());
- dest->insert(dest->end(), val.begin(), val.end());
-}
-
-inline void putRawData(std::vector<u8> *dest, const void *src, size_t len)
-{
- dest->insert(dest->end(), (u8 *)src, (u8 *)src + len);
-}
diff --git a/src/util/sha256.c b/src/util/sha256.c
index 4241f31f3..5c8266f9a 100644
--- a/src/util/sha256.c
+++ b/src/util/sha256.c
@@ -18,13 +18,13 @@ const char SHA256_version[] = "SHA-256" OPENSSL_VERSION_PTEXT;
unsigned static char cleanse_ctr = 0;
static void OPENSSL_cleanse(void *ptr, size_t len)
{
- unsigned char *p = ptr;
+ unsigned char *p = (unsigned char *)ptr;
size_t loop = len, ctr = cleanse_ctr;
while (loop--) {
*(p++) = (unsigned char)ctr;
ctr += (17 + ((size_t)p & 0xF));
}
- p = memchr(ptr, (unsigned char)ctr, len);
+ p = (unsigned char *)memchr(ptr, (unsigned char)ctr, len);
if (p)
ctr += (63 + (size_t)p);
cleanse_ctr = (unsigned char)ctr;
@@ -262,7 +262,7 @@ static void sha256_block_data_order(SHA256_CTX *ctx, const void *in,
unsigned MD32_REG_T a, b, c, d, e, f, g, h, s0, s1, T1;
SHA_LONG X[16];
int i;
- const unsigned char *data = in;
+ const unsigned char *data = (const unsigned char *)in;
const union {
long one;
char little;
diff --git a/src/util/string.cpp b/src/util/string.cpp
index 6e1db798c..8381a29c5 100644
--- a/src/util/string.cpp
+++ b/src/util/string.cpp
@@ -27,6 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "translation.h"
#include <algorithm>
+#include <array>
#include <sstream>
#include <iomanip>
#include <map>
@@ -889,3 +890,70 @@ std::wstring translate_string(const std::wstring &s)
return translate_string(s, g_client_translations);
#endif
}
+
+static const std::array<std::wstring, 22> disallowed_dir_names = {
+ // Problematic filenames from here:
+ // https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file#file-and-directory-names
+ L"CON",
+ L"PRN",
+ L"AUX",
+ L"NUL",
+ L"COM1",
+ L"COM2",
+ L"COM3",
+ L"COM4",
+ L"COM5",
+ L"COM6",
+ L"COM7",
+ L"COM8",
+ L"COM9",
+ L"LPT1",
+ L"LPT2",
+ L"LPT3",
+ L"LPT4",
+ L"LPT5",
+ L"LPT6",
+ L"LPT7",
+ L"LPT8",
+ L"LPT9",
+};
+
+/**
+ * List of characters that are blacklisted from created directories
+ */
+static const std::wstring disallowed_path_chars = L"<>:\"/\\|?*.";
+
+/**
+ * Sanitize the name of a new directory. This consists of two stages:
+ * 1. Check for 'reserved filenames' that can't be used on some filesystems
+ * and add a prefix to them
+ * 2. Remove 'unsafe' characters from the name by replacing them with '_'
+ */
+std::string sanitizeDirName(const std::string &str, const std::string &optional_prefix)
+{
+ std::wstring safe_name = utf8_to_wide(str);
+
+ for (std::wstring disallowed_name : disallowed_dir_names) {
+ if (str_equal(safe_name, disallowed_name, true)) {
+ safe_name = utf8_to_wide(optional_prefix) + safe_name;
+ break;
+ }
+ }
+
+ for (unsigned long i = 0; i < safe_name.length(); i++) {
+ bool is_valid = true;
+
+ // Unlikely, but control characters should always be blacklisted
+ if (safe_name[i] < 32) {
+ is_valid = false;
+ } else if (safe_name[i] < 128) {
+ is_valid = disallowed_path_chars.find_first_of(safe_name[i])
+ == std::wstring::npos;
+ }
+
+ if (!is_valid)
+ safe_name[i] = '_';
+ }
+
+ return wide_to_utf8(safe_name);
+}
diff --git a/src/util/string.h b/src/util/string.h
index 185fb55e2..6fd11fadc 100644
--- a/src/util/string.h
+++ b/src/util/string.h
@@ -746,3 +746,11 @@ inline irr::core::stringw utf8_to_stringw(const std::string &input)
std::wstring str = utf8_to_wide(input);
return irr::core::stringw(str.c_str());
}
+
+/**
+ * Sanitize the name of a new directory. This consists of two stages:
+ * 1. Check for 'reserved filenames' that can't be used on some filesystems
+ * and prefix them
+ * 2. Remove 'unsafe' characters from the name by replacing them with '_'
+ */
+std::string sanitizeDirName(const std::string &str, const std::string &optional_prefix);