summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt63
-rw-r--r--src/ban.cpp22
-rw-r--r--src/ban.h8
-rw-r--r--src/camera.cpp211
-rw-r--r--src/camera.h46
-rw-r--r--src/cavegen.cpp19
-rw-r--r--src/cavegen.h6
-rw-r--r--src/cguittfont/CMakeLists.txt2
-rw-r--r--src/chat.cpp86
-rw-r--r--src/chat.h27
-rw-r--r--src/chat_interface.h82
-rw-r--r--src/client.cpp191
-rw-r--r--src/client.h33
-rw-r--r--src/client/clientlauncher.cpp49
-rw-r--r--src/client/clientlauncher.h4
-rw-r--r--src/client/inputhandler.h20
-rw-r--r--src/client/tile.cpp153
-rw-r--r--src/client/tile.h1
-rw-r--r--src/clientiface.cpp52
-rw-r--r--src/clientiface.h28
-rw-r--r--src/clientmap.cpp280
-rw-r--r--src/clientmap.h13
-rw-r--r--src/clientmedia.cpp2
-rw-r--r--src/clientobject.cpp2
-rw-r--r--src/clientobject.h2
-rw-r--r--src/clouds.cpp2
-rw-r--r--src/clouds.h6
-rw-r--r--src/cmake_config.h.in8
-rw-r--r--src/collision.cpp319
-rw-r--r--src/collision.h10
-rw-r--r--src/content_cao.cpp121
-rw-r--r--src/content_cao.h17
-rw-r--r--src/content_mapblock.cpp383
-rw-r--r--src/content_mapnode.cpp66
-rw-r--r--src/content_mapnode.h4
-rw-r--r--src/content_nodemeta.cpp14
-rw-r--r--src/content_nodemeta.h4
-rw-r--r--src/content_sao.cpp52
-rw-r--r--src/content_sao.h11
-rw-r--r--src/convert_json.cpp8
-rw-r--r--src/craftdef.cpp14
-rw-r--r--src/database-dummy.cpp1
-rw-r--r--src/database-leveldb.cpp4
-rw-r--r--src/database-redis.cpp37
-rw-r--r--src/database-sqlite3.cpp111
-rw-r--r--src/database-sqlite3.h4
-rw-r--r--src/debug.cpp170
-rw-r--r--src/debug.h57
-rw-r--r--src/defaultsettings.cpp48
-rw-r--r--src/drawscene.cpp192
-rw-r--r--src/drawscene.h8
-rw-r--r--src/dungeongen.cpp31
-rw-r--r--src/dungeongen.h1
-rw-r--r--src/emerge.cpp778
-rw-r--r--src/emerge.h123
-rw-r--r--src/environment.cpp644
-rw-r--r--src/environment.h161
-rw-r--r--src/event_manager.h8
-rw-r--r--src/exceptions.h6
-rw-r--r--src/filesys.cpp40
-rw-r--r--src/filesys.h4
-rw-r--r--src/fontengine.cpp2
-rw-r--r--src/game.cpp594
-rw-r--r--src/gamedef.h6
-rw-r--r--src/gameparams.h9
-rw-r--r--src/genericobject.cpp2
-rw-r--r--src/gettext.cpp133
-rw-r--r--src/gettext.h14
-rw-r--r--src/guiChatConsole.cpp171
-rw-r--r--src/guiChatConsole.h17
-rw-r--r--src/guiEngine.cpp12
-rw-r--r--src/guiFileSelectMenu.cpp1
-rw-r--r--src/guiFormSpecMenu.cpp262
-rw-r--r--src/guiFormSpecMenu.h87
-rw-r--r--src/guiKeyChangeMenu.cpp6
-rw-r--r--src/guiTable.cpp51
-rw-r--r--src/guiscalingfilter.cpp4
-rw-r--r--src/httpfetch.cpp81
-rw-r--r--src/httpfetch.h3
-rw-r--r--src/hud.cpp311
-rw-r--r--src/hud.h58
-rw-r--r--src/inventory.cpp24
-rw-r--r--src/inventorymanager.cpp10
-rw-r--r--src/irrlichttypes.h15
-rw-r--r--src/itemdef.cpp125
-rw-r--r--src/itemdef.h1
-rw-r--r--src/jthread/CMakeLists.txt14
-rw-r--r--src/jthread/LICENSE.MIT20
-rw-r--r--src/jthread/jevent.h59
-rw-r--r--src/jthread/jmutex.h79
-rw-r--r--src/jthread/jmutexautolock.h43
-rw-r--r--src/jthread/jthread.h117
-rw-r--r--src/jthread/pthread/jevent.cpp67
-rw-r--r--src/jthread/pthread/jmutex.cpp58
-rw-r--r--src/jthread/pthread/jsemaphore.cpp156
-rw-r--r--src/jthread/pthread/jthread.cpp168
-rw-r--r--src/jthread/win32/jevent.cpp43
-rw-r--r--src/jthread/win32/jmutex.cpp68
-rw-r--r--src/jthread/win32/jsemaphore.cpp104
-rwxr-xr-xsrc/jthread/win32/jthread.cpp146
-rw-r--r--src/localplayer.cpp63
-rw-r--r--src/localplayer.h5
-rw-r--r--src/log.cpp407
-rw-r--r--src/log.h216
-rw-r--r--src/luaentity_common.h30
-rw-r--r--src/main.cpp274
-rw-r--r--src/mainmenumanager.h21
-rw-r--r--src/map.cpp431
-rw-r--r--src/map.h10
-rw-r--r--src/mapblock.cpp140
-rw-r--r--src/mapblock.h14
-rw-r--r--src/mapblock_mesh.cpp272
-rw-r--r--src/mapblock_mesh.h32
-rw-r--r--src/mapgen.cpp132
-rw-r--r--src/mapgen.h37
-rw-r--r--src/mapgen_flat.cpp622
-rw-r--r--src/mapgen_flat.h124
-rw-r--r--src/mapgen_fractal.cpp746
-rw-r--r--src/mapgen_fractal.h134
-rw-r--r--src/mapgen_singlenode.cpp28
-rw-r--r--src/mapgen_singlenode.h5
-rw-r--r--src/mapgen_v5.cpp197
-rw-r--r--src/mapgen_v5.h13
-rw-r--r--src/mapgen_v6.cpp78
-rw-r--r--src/mapgen_v6.h19
-rw-r--r--src/mapgen_v7.cpp400
-rw-r--r--src/mapgen_v7.h18
-rw-r--r--src/mapgen_valleys.cpp1017
-rw-r--r--src/mapgen_valleys.h187
-rw-r--r--src/mapnode.cpp115
-rw-r--r--src/mapnode.h6
-rw-r--r--src/mapsector.cpp2
-rw-r--r--src/mesh.cpp347
-rw-r--r--src/mesh.h8
-rw-r--r--src/mg_biome.cpp4
-rw-r--r--src/mg_decoration.cpp48
-rw-r--r--src/mg_decoration.h11
-rw-r--r--src/mg_ore.cpp134
-rw-r--r--src/mg_ore.h33
-rw-r--r--src/mg_schematic.cpp82
-rw-r--r--src/mg_schematic.h5
-rw-r--r--src/minimap.cpp94
-rw-r--r--src/minimap.h23
-rw-r--r--src/modalMenu.h6
-rw-r--r--src/mods.cpp9
-rw-r--r--src/mods.h18
-rw-r--r--src/nameidmapping.cpp2
-rw-r--r--src/network/clientpackethandler.cpp31
-rw-r--r--src/network/connection.cpp145
-rw-r--r--src/network/connection.h62
-rw-r--r--src/network/networkpacket.cpp3
-rw-r--r--src/network/networkprotocol.h59
-rw-r--r--src/network/serverpackethandler.cpp181
-rw-r--r--src/nodedef.cpp240
-rw-r--r--src/nodedef.h74
-rw-r--r--src/nodemetadata.cpp31
-rw-r--r--src/nodemetadata.h6
-rw-r--r--src/nodetimer.cpp24
-rw-r--r--src/noise.cpp16
-rw-r--r--src/objdef.h4
-rw-r--r--src/object_properties.cpp22
-rw-r--r--src/object_properties.h9
-rw-r--r--src/particles.cpp182
-rw-r--r--src/particles.h10
-rw-r--r--src/pathfinder.cpp906
-rw-r--r--src/pathfinder.h298
-rw-r--r--src/player.cpp63
-rw-r--r--src/player.h18
-rw-r--r--src/porting.cpp257
-rw-r--r--src/porting.h170
-rw-r--r--src/porting_android.cpp114
-rw-r--r--src/porting_android.h6
-rw-r--r--src/profiler.h20
-rw-r--r--src/quicktune.cpp14
-rw-r--r--src/rollback.cpp10
-rw-r--r--src/rollback_interface.cpp2
-rw-r--r--src/script/common/c_content.cpp179
-rw-r--r--src/script/common/c_converter.cpp9
-rw-r--r--src/script/common/c_converter.h2
-rw-r--r--src/script/common/c_internal.cpp49
-rw-r--r--src/script/common/c_internal.h25
-rw-r--r--src/script/common/c_types.h4
-rw-r--r--src/script/cpp_api/s_async.cpp95
-rw-r--r--src/script/cpp_api/s_async.h30
-rw-r--r--src/script/cpp_api/s_base.cpp64
-rw-r--r--src/script/cpp_api/s_base.h20
-rw-r--r--src/script/cpp_api/s_entity.cpp31
-rw-r--r--src/script/cpp_api/s_env.cpp195
-rw-r--r--src/script/cpp_api/s_env.h20
-rw-r--r--src/script/cpp_api/s_internal.h42
-rw-r--r--src/script/cpp_api/s_inventory.cpp33
-rw-r--r--src/script/cpp_api/s_item.cpp56
-rw-r--r--src/script/cpp_api/s_item.h2
-rw-r--r--src/script/cpp_api/s_mainmenu.cpp10
-rw-r--r--src/script/cpp_api/s_node.cpp47
-rw-r--r--src/script/cpp_api/s_nodemeta.cpp33
-rw-r--r--src/script/cpp_api/s_player.cpp6
-rw-r--r--src/script/cpp_api/s_security.cpp13
-rw-r--r--src/script/cpp_api/s_security.h5
-rw-r--r--src/script/cpp_api/s_server.cpp12
-rw-r--r--src/script/lua_api/CMakeLists.txt1
-rw-r--r--src/script/lua_api/l_areastore.cpp72
-rw-r--r--src/script/lua_api/l_areastore.h18
-rw-r--r--src/script/lua_api/l_base.cpp4
-rw-r--r--src/script/lua_api/l_env.cpp166
-rw-r--r--src/script/lua_api/l_env.h50
-rw-r--r--src/script/lua_api/l_http.cpp193
-rw-r--r--src/script/lua_api/l_http.h (renamed from src/logoutputbuffer.h)58
-rw-r--r--src/script/lua_api/l_internal.h10
-rw-r--r--src/script/lua_api/l_inventory.cpp3
-rw-r--r--src/script/lua_api/l_item.cpp2
-rw-r--r--src/script/lua_api/l_mainmenu.cpp53
-rw-r--r--src/script/lua_api/l_mainmenu.h2
-rw-r--r--src/script/lua_api/l_mapgen.cpp181
-rw-r--r--src/script/lua_api/l_mapgen.h10
-rw-r--r--src/script/lua_api/l_nodemeta.cpp21
-rw-r--r--src/script/lua_api/l_nodetimer.cpp6
-rw-r--r--src/script/lua_api/l_noise.cpp126
-rw-r--r--src/script/lua_api/l_noise.h33
-rw-r--r--src/script/lua_api/l_object.cpp132
-rw-r--r--src/script/lua_api/l_particles.cpp6
-rw-r--r--src/script/lua_api/l_rollback.cpp4
-rw-r--r--src/script/lua_api/l_server.cpp25
-rw-r--r--src/script/lua_api/l_server.h3
-rw-r--r--src/script/lua_api/l_util.cpp140
-rw-r--r--src/script/lua_api/l_util.h9
-rw-r--r--src/script/lua_api/l_vmanip.cpp35
-rw-r--r--src/script/scripting_game.cpp3
-rw-r--r--src/script/scripting_mainmenu.cpp2
-rw-r--r--src/serialization.cpp4
-rw-r--r--src/serialization.h12
-rw-r--r--src/server.cpp578
-rw-r--r--src/server.h33
-rw-r--r--src/serverlist.cpp10
-rw-r--r--src/serverobject.cpp2
-rw-r--r--src/settings.cpp50
-rw-r--r--src/settings.h6
-rw-r--r--src/settings_translation_file.cpp620
-rw-r--r--src/shader.cpp65
-rw-r--r--src/shader.h3
-rw-r--r--src/sky.cpp9
-rw-r--r--src/sky.h5
-rw-r--r--src/socket.cpp16
-rw-r--r--src/socket.h6
-rw-r--r--src/sound_openal.cpp172
-rw-r--r--src/staticobject.cpp14
-rw-r--r--src/staticobject.h2
-rw-r--r--src/strfnd.h176
-rw-r--r--src/subgame.cpp52
-rw-r--r--src/terminal_chat_console.cpp453
-rw-r--r--src/terminal_chat_console.h131
-rw-r--r--src/threading/CMakeLists.txt7
-rw-r--r--src/threading/atomic.h139
-rw-r--r--src/threading/event.cpp89
-rw-r--r--src/threading/event.h72
-rw-r--r--src/threading/mutex.cpp108
-rw-r--r--src/threading/mutex.h81
-rw-r--r--src/threading/mutex_auto_lock.h60
-rw-r--r--src/threading/semaphore.cpp162
-rw-r--r--src/threading/semaphore.h (renamed from src/jthread/jsemaphore.h)51
-rw-r--r--src/threading/thread.cpp430
-rw-r--r--src/threading/thread.h183
-rw-r--r--src/threads.h69
-rw-r--r--src/tool.cpp10
-rw-r--r--src/touchscreengui.cpp631
-rw-r--r--src/touchscreengui.h116
-rw-r--r--src/unittest/CMakeLists.txt1
-rw-r--r--src/unittest/test.cpp18
-rw-r--r--src/unittest/test.h91
-rw-r--r--src/unittest/test_areastore.cpp69
-rw-r--r--src/unittest/test_collision.cpp32
-rw-r--r--src/unittest/test_serialization.cpp263
-rw-r--r--src/unittest/test_threading.cpp182
-rw-r--r--src/unittest/test_utilities.cpp35
-rw-r--r--src/util/CMakeLists.txt1
-rw-r--r--src/util/areastore.cpp (renamed from src/areastore.cpp)192
-rw-r--r--src/util/areastore.h (renamed from src/areastore.h)180
-rw-r--r--src/util/auth.cpp107
-rw-r--r--src/util/auth.h35
-rw-r--r--src/util/basic_macros.h53
-rw-r--r--src/util/container.h171
-rw-r--r--src/util/numeric.cpp11
-rw-r--r--src/util/numeric.h19
-rw-r--r--src/util/serialize.cpp73
-rw-r--r--src/util/serialize.h251
-rw-r--r--src/util/srp.cpp693
-rw-r--r--src/util/srp.h91
-rw-r--r--src/util/strfnd.h82
-rw-r--r--src/util/string.h81
-rw-r--r--src/util/thread.h64
-rw-r--r--src/wieldmesh.cpp116
-rw-r--r--src/wieldmesh.h8
292 files changed, 18262 insertions, 9240 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 614e81908..feca199c1 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -27,6 +27,12 @@ set(CMAKE_BUILD_TYPE "${CMAKE_BUILD_TYPE}" CACHE STRING
mark_as_advanced(EXECUTABLE_OUTPUT_PATH LIBRARY_OUTPUT_PATH)
+if(NOT (BUILD_CLIENT OR BUILD_SERVER))
+ message(WARNING "Neither BUILD_CLIENT nor BUILD_SERVER is set! Setting BUILD_SERVER=true")
+ set(BUILD_SERVER TRUE)
+endif()
+
+
option(ENABLE_CURL "Enable cURL support for fetching media" TRUE)
set(USE_CURL FALSE)
@@ -40,6 +46,15 @@ else()
mark_as_advanced(CLEAR CURL_LIBRARY CURL_INCLUDE_DIR)
endif()
+if(NOT USE_CURL)
+ if(BUILD_CLIENT)
+ message(WARNING "cURL is required to load the server list")
+ endif()
+ if(BUILD_SERVER)
+ message(WARNING "cURL is required to announce to the server list")
+ endif()
+endif()
+
option(ENABLE_GETTEXT "Use GetText for internationalization" FALSE)
set(USE_GETTEXT FALSE)
@@ -140,11 +155,40 @@ if(ENABLE_FREETYPE)
endif()
endif(ENABLE_FREETYPE)
-
-find_package(Lua REQUIRED)
+# LuaJIT
+option(ENABLE_LUAJIT "Enable LuaJIT support" TRUE)
+set(USE_LUAJIT FALSE)
+if(ENABLE_LUAJIT)
+ find_package(LuaJIT)
+ if(LUAJIT_FOUND)
+ set(USE_LUAJIT TRUE)
+ endif(LUAJIT_FOUND)
+else()
+ message (STATUS "LuaJIT detection disabled! (ENABLE_LUAJIT=0)")
+endif()
+if(NOT USE_LUAJIT)
+ message(STATUS "LuaJIT not found, using bundled Lua.")
+ set(LUA_LIBRARY "lua")
+ set(LUA_INCLUDE_DIR "${PROJECT_SOURCE_DIR}/lua/src")
+ add_subdirectory(lua)
+endif()
find_package(GMP REQUIRED)
+option(ENABLE_CURSES "Enable ncurses console" TRUE)
+set(USE_CURSES FALSE)
+
+if(ENABLE_CURSES)
+ find_package(Ncursesw)
+ if(CURSES_FOUND)
+ set(USE_CURSES TRUE)
+ message(STATUS "ncurses console enabled.")
+ include_directories(${CURSES_INCLUDE_DIRS})
+ else()
+ message(STATUS "ncurses not found!")
+ endif()
+endif(ENABLE_CURSES)
+
option(ENABLE_LEVELDB "Enable LevelDB backend" TRUE)
set(USE_LEVELDB FALSE)
@@ -297,16 +341,16 @@ add_custom_target(GenerateVersion
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}")
-add_subdirectory(jthread)
+add_subdirectory(threading)
add_subdirectory(network)
add_subdirectory(script)
add_subdirectory(unittest)
add_subdirectory(util)
set(common_SRCS
- areastore.cpp
ban.cpp
cavegen.cpp
+ chat.cpp
clientiface.cpp
collision.cpp
content_abm.cpp
@@ -337,10 +381,13 @@ set(common_SRCS
map.cpp
mapblock.cpp
mapgen.cpp
+ mapgen_flat.cpp
+ mapgen_fractal.cpp
mapgen_singlenode.cpp
mapgen_v5.cpp
mapgen_v6.cpp
mapgen_v7.cpp
+ mapgen_valleys.cpp
mapnode.cpp
mapsector.cpp
mg_biome.cpp
@@ -371,6 +418,7 @@ set(common_SRCS
sound.cpp
staticobject.cpp
subgame.cpp
+ terminal_chat_console.cpp
tool.cpp
treegen.cpp
version.cpp
@@ -415,7 +463,6 @@ set(client_SRCS
${sound_SRCS}
${client_network_SRCS}
camera.cpp
- chat.cpp
client.cpp
clientmap.cpp
clientmedia.cpp
@@ -542,6 +589,9 @@ if(BUILD_CLIENT)
${CGUITTFONT_LIBRARY}
)
endif()
+ if (USE_CURSES)
+ target_link_libraries(${PROJECT_NAME} ${CURSES_LIBRARIES})
+ endif()
if (USE_LEVELDB)
target_link_libraries(${PROJECT_NAME} ${LEVELDB_LIBRARY})
endif()
@@ -569,6 +619,9 @@ if(BUILD_SERVER)
)
set_target_properties(${PROJECT_NAME}server PROPERTIES
COMPILE_DEFINITIONS "SERVER")
+ if (USE_CURSES)
+ target_link_libraries(${PROJECT_NAME}server ${CURSES_LIBRARIES})
+ endif()
if (USE_LEVELDB)
target_link_libraries(${PROJECT_NAME}server ${LEVELDB_LIBRARY})
endif()
diff --git a/src/ban.cpp b/src/ban.cpp
index 7c1a68d45..5fa430702 100644
--- a/src/ban.cpp
+++ b/src/ban.cpp
@@ -19,10 +19,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "ban.h"
#include <fstream>
-#include "jthread/jmutexautolock.h"
+#include "threading/mutex_auto_lock.h"
#include <sstream>
#include <set>
-#include "strfnd.h"
+#include "util/strfnd.h"
#include "util/string.h"
#include "log.h"
#include "filesys.h"
@@ -36,7 +36,7 @@ BanManager::BanManager(const std::string &banfilepath):
}
catch(SerializationError &e)
{
- infostream<<"WARNING: BanManager: creating "
+ warningstream<<"BanManager: creating "
<<m_banfilepath<<std::endl;
}
}
@@ -48,7 +48,7 @@ BanManager::~BanManager()
void BanManager::load()
{
- JMutexAutoLock lock(m_mutex);
+ MutexAutoLock lock(m_mutex);
infostream<<"BanManager: loading from "<<m_banfilepath<<std::endl;
std::ifstream is(m_banfilepath.c_str(), std::ios::binary);
if(is.good() == false)
@@ -73,7 +73,7 @@ void BanManager::load()
void BanManager::save()
{
- JMutexAutoLock lock(m_mutex);
+ MutexAutoLock lock(m_mutex);
infostream << "BanManager: saving to " << m_banfilepath << std::endl;
std::ostringstream ss(std::ios_base::binary);
@@ -90,13 +90,13 @@ void BanManager::save()
bool BanManager::isIpBanned(const std::string &ip)
{
- JMutexAutoLock lock(m_mutex);
+ MutexAutoLock lock(m_mutex);
return m_ips.find(ip) != m_ips.end();
}
std::string BanManager::getBanDescription(const std::string &ip_or_name)
{
- JMutexAutoLock lock(m_mutex);
+ MutexAutoLock lock(m_mutex);
std::string s = "";
for (StringMap::iterator it = m_ips.begin(); it != m_ips.end(); ++it) {
if (it->first == ip_or_name || it->second == ip_or_name
@@ -110,7 +110,7 @@ std::string BanManager::getBanDescription(const std::string &ip_or_name)
std::string BanManager::getBanName(const std::string &ip)
{
- JMutexAutoLock lock(m_mutex);
+ MutexAutoLock lock(m_mutex);
StringMap::iterator it = m_ips.find(ip);
if (it == m_ips.end())
return "";
@@ -119,14 +119,14 @@ std::string BanManager::getBanName(const std::string &ip)
void BanManager::add(const std::string &ip, const std::string &name)
{
- JMutexAutoLock lock(m_mutex);
+ MutexAutoLock lock(m_mutex);
m_ips[ip] = name;
m_modified = true;
}
void BanManager::remove(const std::string &ip_or_name)
{
- JMutexAutoLock lock(m_mutex);
+ MutexAutoLock lock(m_mutex);
for (StringMap::iterator it = m_ips.begin(); it != m_ips.end();) {
if ((it->first == ip_or_name) || (it->second == ip_or_name)) {
m_ips.erase(it++);
@@ -140,7 +140,7 @@ void BanManager::remove(const std::string &ip_or_name)
bool BanManager::isModified()
{
- JMutexAutoLock lock(m_mutex);
+ MutexAutoLock lock(m_mutex);
return m_modified;
}
diff --git a/src/ban.h b/src/ban.h
index 5db7179de..d1a49cb15 100644
--- a/src/ban.h
+++ b/src/ban.h
@@ -21,9 +21,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#define BAN_HEADER
#include "util/string.h"
-#include "jthread/jthread.h"
-#include "jthread/jmutex.h"
+#include "threading/thread.h"
+#include "threading/mutex.h"
#include "exceptions.h"
+#include <map>
+#include <string>
class BanManager
{
@@ -40,7 +42,7 @@ public:
void remove(const std::string &ip_or_name);
bool isModified();
private:
- JMutex m_mutex;
+ Mutex m_mutex;
std::string m_banfilepath;
StringMap m_ips;
bool m_modified;
diff --git a/src/camera.cpp b/src/camera.cpp
index 0c6d03e4e..6893b8cbf 100644
--- a/src/camera.cpp
+++ b/src/camera.cpp
@@ -34,6 +34,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "util/numeric.h"
#include "util/mathconstants.h"
#include "constants.h"
+#include "fontengine.h"
#define CAMERA_OFFSET_STEP 200
@@ -59,13 +60,6 @@ Camera::Camera(scene::ISceneManager* smgr, MapDrawControl& draw_control,
m_fov_x(1.0),
m_fov_y(1.0),
- m_added_busytime(0),
- m_added_frames(0),
- m_range_old(0),
- m_busytime_old(0),
- m_frametime_counter(0),
- m_time_per_range(30. / 50), // a sane default of 30ms per 50 nodes of range
-
m_view_bobbing_anim(0),
m_view_bobbing_state(0),
m_view_bobbing_speed(0),
@@ -79,8 +73,9 @@ Camera::Camera(scene::ISceneManager* smgr, MapDrawControl& draw_control,
m_camera_mode(CAMERA_MODE_FIRST)
{
- //dstream<<__FUNCTION_NAME<<std::endl;
+ //dstream<<FUNCTION_NAME<<std::endl;
+ m_driver = smgr->getVideoDriver();
// note: making the camera node a child of the player node
// would lead to unexpected behaviour, so we don't do that.
m_playernode = smgr->addEmptySceneNode(smgr->getRootSceneNode());
@@ -107,9 +102,9 @@ Camera::Camera(scene::ISceneManager* smgr, MapDrawControl& draw_control,
*/
m_cache_fall_bobbing_amount = g_settings->getFloat("fall_bobbing_amount");
m_cache_view_bobbing_amount = g_settings->getFloat("view_bobbing_amount");
- m_cache_wanted_fps = g_settings->getFloat("wanted_fps");
m_cache_fov = g_settings->getFloat("fov");
m_cache_view_bobbing = g_settings->getBool("view_bobbing");
+ m_nametags.clear();
}
Camera::~Camera()
@@ -452,15 +447,15 @@ void Camera::update(LocalPlayer* player, f32 frametime, f32 busytime,
m_wieldnode->setColor(player->light_color);
- // Render distance feedback loop
- updateViewingRange(frametime, busytime);
+ // Set render distance
+ updateViewingRange();
// If the player is walking, swimming, or climbing,
// view bobbing is enabled and free_move is off,
// start (or continue) the view bobbing animation.
v3f speed = player->getSpeed();
const bool movement_XZ = hypot(speed.X, speed.Z) > BS;
- const bool movement_Y = abs(speed.Y) > BS;
+ const bool movement_Y = fabs(speed.Y) > BS;
const bool walking = movement_XZ && player->touching_ground;
const bool swimming = (movement_XZ || player->swimming_vertical) && player->in_liquid;
@@ -481,143 +476,16 @@ void Camera::update(LocalPlayer* player, f32 frametime, f32 busytime,
}
}
-void Camera::updateViewingRange(f32 frametime_in, f32 busytime_in)
+void Camera::updateViewingRange()
{
- if (m_draw_control.range_all)
- return;
-
- m_added_busytime += busytime_in;
- m_added_frames += 1;
-
- m_frametime_counter -= frametime_in;
- if (m_frametime_counter > 0)
+ if (m_draw_control.range_all) {
+ m_cameranode->setFarValue(100000.0);
return;
- m_frametime_counter = 0.2; // Same as ClientMap::updateDrawList interval
-
- /*dstream<<__FUNCTION_NAME
- <<": Collected "<<m_added_frames<<" frames, total of "
- <<m_added_busytime<<"s."<<std::endl;
-
- dstream<<"m_draw_control.blocks_drawn="
- <<m_draw_control.blocks_drawn
- <<", m_draw_control.blocks_would_have_drawn="
- <<m_draw_control.blocks_would_have_drawn
- <<std::endl;*/
-
- // Get current viewing range and FPS settings
- f32 viewing_range_min = g_settings->getFloat("viewing_range_nodes_min");
- viewing_range_min = MYMAX(15.0, viewing_range_min);
-
- f32 viewing_range_max = g_settings->getFloat("viewing_range_nodes_max");
- viewing_range_max = MYMAX(viewing_range_min, viewing_range_max);
-
- // Immediately apply hard limits
- if(m_draw_control.wanted_range < viewing_range_min)
- m_draw_control.wanted_range = viewing_range_min;
- if(m_draw_control.wanted_range > viewing_range_max)
- m_draw_control.wanted_range = viewing_range_max;
-
- // Just so big a value that everything rendered is visible
- // Some more allowance than viewing_range_max * BS because of clouds,
- // active objects, etc.
- if(viewing_range_max < 200*BS)
- m_cameranode->setFarValue(200 * BS * 10);
- else
- m_cameranode->setFarValue(viewing_range_max * BS * 10);
-
- f32 wanted_fps = m_cache_wanted_fps;
- wanted_fps = MYMAX(wanted_fps, 1.0);
- f32 wanted_frametime = 1.0 / wanted_fps;
-
- m_draw_control.wanted_min_range = viewing_range_min;
- m_draw_control.wanted_max_blocks = (2.0*m_draw_control.blocks_would_have_drawn)+1;
- if (m_draw_control.wanted_max_blocks < 10)
- m_draw_control.wanted_max_blocks = 10;
-
- f32 block_draw_ratio = 1.0;
- if (m_draw_control.blocks_would_have_drawn != 0)
- {
- block_draw_ratio = (f32)m_draw_control.blocks_drawn
- / (f32)m_draw_control.blocks_would_have_drawn;
}
- // Calculate the average frametime in the case that all wanted
- // blocks had been drawn
- f32 frametime = m_added_busytime / m_added_frames / block_draw_ratio;
-
- m_added_busytime = 0.0;
- m_added_frames = 0;
-
- f32 wanted_frametime_change = wanted_frametime - frametime;
- //dstream<<"wanted_frametime_change="<<wanted_frametime_change<<std::endl;
- g_profiler->avg("wanted_frametime_change", wanted_frametime_change);
-
- // If needed frametime change is small, just return
- // This value was 0.4 for many months until 2011-10-18 by c55;
- if (fabs(wanted_frametime_change) < wanted_frametime*0.33)
- {
- //dstream<<"ignoring small wanted_frametime_change"<<std::endl;
- return;
- }
-
- f32 range = m_draw_control.wanted_range;
- f32 new_range = range;
-
- f32 d_range = range - m_range_old;
- f32 d_busytime = busytime_in - m_busytime_old;
- if (d_range != 0)
- {
- m_time_per_range = d_busytime / d_range;
- }
- //dstream<<"time_per_range="<<m_time_per_range<<std::endl;
- g_profiler->avg("time_per_range", m_time_per_range);
-
- // The minimum allowed calculated frametime-range derivative:
- // Practically this sets the maximum speed of changing the range.
- // The lower this value, the higher the maximum changing speed.
- // A low value here results in wobbly range (0.001)
- // A low value can cause oscillation in very nonlinear time/range curves.
- // A high value here results in slow changing range (0.0025)
- // SUGG: This could be dynamically adjusted so that when
- // the camera is turning, this is lower
- //f32 min_time_per_range = 0.0010; // Up to 0.4.7
- f32 min_time_per_range = 0.0005;
- if(m_time_per_range < min_time_per_range)
- {
- m_time_per_range = min_time_per_range;
- //dstream<<"m_time_per_range="<<m_time_per_range<<" (min)"<<std::endl;
- }
- else
- {
- //dstream<<"m_time_per_range="<<m_time_per_range<<std::endl;
- }
-
- f32 wanted_range_change = wanted_frametime_change / m_time_per_range;
- // Dampen the change a bit to kill oscillations
- //wanted_range_change *= 0.9;
- //wanted_range_change *= 0.75;
- wanted_range_change *= 0.5;
- //dstream<<"wanted_range_change="<<wanted_range_change<<std::endl;
-
- // If needed range change is very small, just return
- if(fabs(wanted_range_change) < 0.001)
- {
- //dstream<<"ignoring small wanted_range_change"<<std::endl;
- return;
- }
-
- new_range += wanted_range_change;
-
- //f32 new_range_unclamped = new_range;
- new_range = MYMAX(new_range, viewing_range_min);
- new_range = MYMIN(new_range, viewing_range_max);
- /*dstream<<"new_range="<<new_range_unclamped
- <<", clamped to "<<new_range<<std::endl;*/
-
- m_range_old = m_draw_control.wanted_range;
- m_busytime_old = busytime_in;
-
- m_draw_control.wanted_range = new_range;
+ f32 viewing_range = g_settings->getFloat("viewing_range");
+ m_draw_control.wanted_range = viewing_range;
+ m_cameranode->setFarValue((viewing_range < 2000) ? 2000 * BS : viewing_range * BS);
}
void Camera::setDigging(s32 button)
@@ -646,7 +514,7 @@ void Camera::drawWieldedTool(irr::core::matrix4* translation)
scene::ICameraSceneNode* cam = m_wieldmgr->getActiveCamera();
cam->setAspectRatio(m_cameranode->getAspectRatio());
cam->setFOV(72.0*M_PI/180.0);
- cam->setNearValue(0.1);
+ cam->setNearValue(10);
cam->setFarValue(1000);
if (translation != NULL)
{
@@ -662,3 +530,54 @@ void Camera::drawWieldedTool(irr::core::matrix4* translation)
}
m_wieldmgr->drawAll();
}
+
+void Camera::drawNametags()
+{
+ core::matrix4 trans = m_cameranode->getProjectionMatrix();
+ trans *= m_cameranode->getViewMatrix();
+
+ for (std::list<Nametag *>::const_iterator
+ i = m_nametags.begin();
+ i != m_nametags.end(); ++i) {
+ Nametag *nametag = *i;
+ if (nametag->nametag_color.getAlpha() == 0) {
+ // Enforce hiding nametag,
+ // because if freetype is enabled, a grey
+ // shadow can remain.
+ continue;
+ }
+ v3f pos = nametag->parent_node->getPosition() + v3f(0.0, 1.1 * BS, 0.0);
+ f32 transformed_pos[4] = { pos.X, pos.Y, pos.Z, 1.0f };
+ trans.multiplyWith1x4Matrix(transformed_pos);
+ if (transformed_pos[3] > 0) {
+ core::dimension2d<u32> textsize =
+ g_fontengine->getFont()->getDimension(
+ utf8_to_wide(nametag->nametag_text).c_str());
+ f32 zDiv = transformed_pos[3] == 0.0f ? 1.0f :
+ core::reciprocal(transformed_pos[3]);
+ v2u32 screensize = m_driver->getScreenSize();
+ v2s32 screen_pos;
+ screen_pos.X = screensize.X *
+ (0.5 * transformed_pos[0] * zDiv + 0.5) - textsize.Width / 2;
+ screen_pos.Y = screensize.Y *
+ (0.5 - transformed_pos[1] * zDiv * 0.5) - textsize.Height / 2;
+ core::rect<s32> size(0, 0, textsize.Width, textsize.Height);
+ g_fontengine->getFont()->draw(utf8_to_wide(nametag->nametag_text).c_str(),
+ size + screen_pos, nametag->nametag_color);
+ }
+ }
+}
+
+Nametag *Camera::addNametag(scene::ISceneNode *parent_node,
+ std::string nametag_text, video::SColor nametag_color)
+{
+ Nametag *nametag = new Nametag(parent_node, nametag_text, nametag_color);
+ m_nametags.push_back(nametag);
+ return nametag;
+}
+
+void Camera::removeNametag(Nametag *nametag)
+{
+ m_nametags.remove(nametag);
+ delete nametag;
+}
diff --git a/src/camera.h b/src/camera.h
index 006f4b3ce..ce46c3190 100644
--- a/src/camera.h
+++ b/src/camera.h
@@ -26,6 +26,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "client/tile.h"
#include "util/numeric.h"
#include <ICameraSceneNode.h>
+#include <ISceneNode.h>
+#include <list>
#include "client.h"
@@ -34,6 +36,20 @@ struct MapDrawControl;
class IGameDef;
class WieldMeshSceneNode;
+struct Nametag {
+ Nametag(scene::ISceneNode *a_parent_node,
+ const std::string &a_nametag_text,
+ const video::SColor &a_nametag_color):
+ parent_node(a_parent_node),
+ nametag_text(a_nametag_text),
+ nametag_color(a_nametag_color)
+ {
+ }
+ scene::ISceneNode *parent_node;
+ std::string nametag_text;
+ video::SColor nametag_color;
+};
+
enum CameraMode {CAMERA_MODE_FIRST, CAMERA_MODE_THIRD, CAMERA_MODE_THIRD_FRONT};
/*
@@ -84,7 +100,7 @@ public:
{
return m_camera_direction;
}
-
+
// Get the camera offset
inline v3s16 getOffset() const
{
@@ -120,8 +136,8 @@ public:
void update(LocalPlayer* player, f32 frametime, f32 busytime,
f32 tool_reload_ratio, ClientEnvironment &c_env);
- // Render distance feedback loop
- void updateViewingRange(f32 frametime_in, f32 busytime_in);
+ // Update render distance
+ void updateViewingRange();
// Start digging animation
// Pass 0 for left click, 1 for right click
@@ -151,6 +167,16 @@ public:
return m_camera_mode;
}
+ Nametag *addNametag(scene::ISceneNode *parent_node,
+ std::string nametag_text, video::SColor nametag_color);
+
+ void removeNametag(Nametag *nametag);
+
+ std::list<Nametag *> *getNametags()
+ { return &m_nametags; }
+
+ void drawNametags();
+
private:
// Nodes
scene::ISceneNode* m_playernode;
@@ -162,8 +188,9 @@ private:
// draw control
MapDrawControl& m_draw_control;
-
+
IGameDef *m_gamedef;
+ video::IVideoDriver *m_driver;
// Absolute camera position
v3f m_camera_position;
@@ -177,14 +204,6 @@ private:
f32 m_fov_x;
f32 m_fov_y;
- // Stuff for viewing range calculations
- f32 m_added_busytime;
- s16 m_added_frames;
- f32 m_range_old;
- f32 m_busytime_old;
- f32 m_frametime_counter;
- f32 m_time_per_range;
-
// View bobbing animation frame (0 <= m_view_bobbing_anim < 1)
f32 m_view_bobbing_anim;
// If 0, view bobbing is off (e.g. player is standing).
@@ -211,9 +230,10 @@ private:
f32 m_cache_fall_bobbing_amount;
f32 m_cache_view_bobbing_amount;
- f32 m_cache_wanted_fps;
f32 m_cache_fov;
bool m_cache_view_bobbing;
+
+ std::list<Nametag *> m_nametags;
};
#endif
diff --git a/src/cavegen.cpp b/src/cavegen.cpp
index 8372f70b5..b8abfbca5 100644
--- a/src/cavegen.cpp
+++ b/src/cavegen.cpp
@@ -31,17 +31,21 @@ NoiseParams nparams_caveliquids(0, 1, v3f(150.0, 150.0, 150.0), 776, 3, 0.6, 2.0
///////////////////////////////////////// Caves V5
-CaveV5::CaveV5(MapgenV5 *mg, PseudoRandom *ps)
+CaveV5::CaveV5(Mapgen *mg, PseudoRandom *ps)
{
this->mg = mg;
this->vm = mg->vm;
this->ndef = mg->ndef;
this->water_level = mg->water_level;
this->ps = ps;
- this->c_water_source = mg->c_water_source;
- this->c_lava_source = mg->c_lava_source;
- this->c_ice = mg->c_ice;
+ c_water_source = ndef->getId("mapgen_water_source");
+ c_lava_source = ndef->getId("mapgen_lava_source");
+ c_ice = ndef->getId("mapgen_ice");
this->np_caveliquids = &nparams_caveliquids;
+ this->ystride = mg->csize.X;
+
+ if (c_ice == CONTENT_IGNORE)
+ c_ice = CONTENT_AIR;
dswitchint = ps->range(1, 14);
flooded = ps->range(1, 2) == 2;
@@ -149,7 +153,7 @@ void CaveV5::makeTunnel(bool dirswitch)
p = orpi + veci + of + rs / 2;
if (p.Z >= node_min.Z && p.Z <= node_max.Z &&
p.X >= node_min.X && p.X <= node_max.X) {
- u32 index = (p.Z - node_min.Z) * mg->ystride + (p.X - node_min.X);
+ u32 index = (p.Z - node_min.Z) * ystride + (p.X - node_min.X);
s16 h = mg->heightmap[index];
if (h < p.Y)
return;
@@ -160,7 +164,7 @@ void CaveV5::makeTunnel(bool dirswitch)
p = orpi + of + rs / 2;
if (p.Z >= node_min.Z && p.Z <= node_max.Z &&
p.X >= node_min.X && p.X <= node_max.X) {
- u32 index = (p.Z - node_min.Z) * mg->ystride + (p.X - node_min.X);
+ u32 index = (p.Z - node_min.Z) * ystride + (p.X - node_min.X);
s16 h = mg->heightmap[index];
if (h < p.Y)
return;
@@ -214,7 +218,8 @@ void CaveV5::carveRoute(v3f vec, float f, bool randomize_xz)
float nval = NoisePerlin3D(np_caveliquids, startp.X,
startp.Y, startp.Z, mg->seed);
- MapNode liquidnode = nval < 0.40 ? lavanode : waternode;
+ MapNode liquidnode = (nval < 0.40 && node_max.Y < MGV5_LAVA_DEPTH) ?
+ lavanode : waternode;
v3f fp = orp + vec * f;
fp.X += 0.1 * ps->range(-10, 10);
diff --git a/src/cavegen.h b/src/cavegen.h
index b9662587b..a1124711b 100644
--- a/src/cavegen.h
+++ b/src/cavegen.h
@@ -21,6 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#define CAVEGEN_HEADER
#define VMANIP_FLAG_CAVE VOXELFLAG_CHECKED1
+#define MGV5_LAVA_DEPTH -256
#define MGV7_LAVA_DEPTH -256
class MapgenV5;
@@ -29,7 +30,7 @@ class MapgenV7;
class CaveV5 {
public:
- MapgenV5 *mg;
+ Mapgen *mg;
MMVManip *vm;
INodeDefManager *ndef;
@@ -64,9 +65,10 @@ public:
content_t c_ice;
int water_level;
+ int ystride;
CaveV5() {}
- CaveV5(MapgenV5 *mg, PseudoRandom *ps);
+ CaveV5(Mapgen *mg, PseudoRandom *ps);
void makeCave(v3s16 nmin, v3s16 nmax, int max_stone_height);
void makeTunnel(bool dirswitch);
void carveRoute(v3f vec, float f, bool randomize_xz);
diff --git a/src/cguittfont/CMakeLists.txt b/src/cguittfont/CMakeLists.txt
index 7717a2f91..6cd35f310 100644
--- a/src/cguittfont/CMakeLists.txt
+++ b/src/cguittfont/CMakeLists.txt
@@ -2,7 +2,7 @@
# Do not add CGUITTFont.cpp to the line below.
# xCGUITTFont.cpp is a wrapper file that includes
# additional required headers.
-add_library(cguittfont xCGUITTFont.cpp)
+add_library(cguittfont STATIC xCGUITTFont.cpp)
if(FREETYPE_PKGCONFIG_FOUND)
set_target_properties(cguittfont
diff --git a/src/chat.cpp b/src/chat.cpp
index 50391d39b..cebe31225 100644
--- a/src/chat.cpp
+++ b/src/chat.cpp
@@ -19,7 +19,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "chat.h"
#include "debug.h"
-#include "strfnd.h"
+#include "util/strfnd.h"
#include <cctype>
#include <sstream>
#include "util/string.h"
@@ -97,6 +97,8 @@ void ChatBuffer::step(f32 dtime)
void ChatBuffer::deleteOldest(u32 count)
{
+ bool at_bottom = (m_scroll == getBottomScrollPos());
+
u32 del_unformatted = 0;
u32 del_formatted = 0;
@@ -120,6 +122,11 @@ void ChatBuffer::deleteOldest(u32 count)
m_unformatted.erase(m_unformatted.begin(), m_unformatted.begin() + del_unformatted);
m_formatted.erase(m_formatted.begin(), m_formatted.begin() + del_formatted);
+
+ if (at_bottom)
+ m_scroll = getBottomScrollPos();
+ else
+ scrollAbsolute(m_scroll - del_formatted);
}
void ChatBuffer::deleteByAge(f32 maxAge)
@@ -390,6 +397,7 @@ ChatPrompt::ChatPrompt(std::wstring prompt, u32 history_limit):
m_cols(0),
m_view(0),
m_cursor(0),
+ m_cursor_len(0),
m_nick_completion_start(0),
m_nick_completion_end(0)
{
@@ -417,20 +425,13 @@ void ChatPrompt::input(const std::wstring &str)
m_nick_completion_end = 0;
}
-std::wstring ChatPrompt::submit()
+void ChatPrompt::addToHistory(std::wstring line)
{
- std::wstring line = m_line;
- m_line.clear();
if (!line.empty())
m_history.push_back(line);
if (m_history.size() > m_history_limit)
m_history.erase(m_history.begin());
m_history_index = m_history.size();
- m_view = 0;
- m_cursor = 0;
- m_nick_completion_start = 0;
- m_nick_completion_end = 0;
- return line;
}
void ChatPrompt::clear()
@@ -442,13 +443,15 @@ void ChatPrompt::clear()
m_nick_completion_end = 0;
}
-void ChatPrompt::replace(std::wstring line)
+std::wstring ChatPrompt::replace(std::wstring line)
{
+ std::wstring old_line = m_line;
m_line = line;
m_view = m_cursor = line.size();
clampView();
m_nick_completion_start = 0;
m_nick_completion_end = 0;
+ return old_line;
}
void ChatPrompt::historyPrev()
@@ -590,14 +593,12 @@ void ChatPrompt::cursorOperation(CursorOp op, CursorOpDir dir, CursorOpScope sco
s32 length = m_line.size();
s32 increment = (dir == CURSOROP_DIR_RIGHT) ? 1 : -1;
- if (scope == CURSOROP_SCOPE_CHARACTER)
- {
+ switch (scope) {
+ case CURSOROP_SCOPE_CHARACTER:
new_cursor += increment;
- }
- else if (scope == CURSOROP_SCOPE_WORD)
- {
- if (increment > 0)
- {
+ break;
+ case CURSOROP_SCOPE_WORD:
+ if (dir == CURSOROP_DIR_RIGHT) {
// skip one word to the right
while (new_cursor < length && isspace(m_line[new_cursor]))
new_cursor++;
@@ -605,39 +606,47 @@ void ChatPrompt::cursorOperation(CursorOp op, CursorOpDir dir, CursorOpScope sco
new_cursor++;
while (new_cursor < length && isspace(m_line[new_cursor]))
new_cursor++;
- }
- else
- {
+ } else {
// skip one word to the left
while (new_cursor >= 1 && isspace(m_line[new_cursor - 1]))
new_cursor--;
while (new_cursor >= 1 && !isspace(m_line[new_cursor - 1]))
new_cursor--;
}
- }
- else if (scope == CURSOROP_SCOPE_LINE)
- {
+ break;
+ case CURSOROP_SCOPE_LINE:
new_cursor += increment * length;
+ break;
+ case CURSOROP_SCOPE_SELECTION:
+ break;
}
new_cursor = MYMAX(MYMIN(new_cursor, length), 0);
- if (op == CURSOROP_MOVE)
- {
+ switch (op) {
+ case CURSOROP_MOVE:
m_cursor = new_cursor;
- }
- else if (op == CURSOROP_DELETE)
- {
- if (new_cursor < old_cursor)
- {
- m_line.erase(new_cursor, old_cursor - new_cursor);
- m_cursor = new_cursor;
+ m_cursor_len = 0;
+ break;
+ case CURSOROP_DELETE:
+ if (m_cursor_len > 0) { // Delete selected text first
+ m_line.erase(m_cursor, m_cursor_len);
+ } else {
+ m_cursor = MYMIN(new_cursor, old_cursor);
+ m_line.erase(m_cursor, abs(new_cursor - old_cursor));
}
- else if (new_cursor > old_cursor)
- {
- m_line.erase(old_cursor, new_cursor - old_cursor);
- m_cursor = old_cursor;
+ m_cursor_len = 0;
+ break;
+ case CURSOROP_SELECT:
+ if (scope == CURSOROP_SCOPE_LINE) {
+ m_cursor = 0;
+ m_cursor_len = length;
+ } else {
+ m_cursor = MYMIN(new_cursor, old_cursor);
+ m_cursor_len += abs(new_cursor - old_cursor);
+ m_cursor_len = MYMIN(m_cursor_len, length - m_cursor);
}
+ break;
}
clampView();
@@ -677,9 +686,12 @@ ChatBackend::~ChatBackend()
void ChatBackend::addMessage(std::wstring name, std::wstring text)
{
+ name = unescape_enriched(name);
+ text = unescape_enriched(text);
+
// Note: A message may consist of multiple lines, for example the MOTD.
WStrfnd fnd(text);
- while (!fnd.atend())
+ while (!fnd.at_end())
{
std::wstring line = fnd.next(L"\n");
m_console_buffer.addLine(name, line);
diff --git a/src/chat.h b/src/chat.h
index 82ce80875..db4146d35 100644
--- a/src/chat.h
+++ b/src/chat.h
@@ -25,7 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <vector>
#include <list>
-// Chat console related classes, only used by the client
+// Chat console related classes
struct ChatLine
{
@@ -123,7 +123,7 @@ private:
u32 m_scrollback;
// Array of unformatted chat lines
std::vector<ChatLine> m_unformatted;
-
+
// Number of character columns in console
u32 m_cols;
// Number of character rows in console
@@ -146,14 +146,21 @@ public:
void input(wchar_t ch);
void input(const std::wstring &str);
- // Submit, clear and return current line
- std::wstring submit();
+ // Add a string to the history
+ void addToHistory(std::wstring line);
+
+ // Get current line
+ std::wstring getLine() const { return m_line; }
+
+ // Get section of line that is currently selected
+ std::wstring getSelection() const
+ { return m_line.substr(m_cursor, m_cursor_len); }
// Clear the current line
void clear();
// Replace the current line with the given text
- void replace(std::wstring line);
+ std::wstring replace(std::wstring line);
// Select previous command from history
void historyPrev();
@@ -169,10 +176,13 @@ public:
std::wstring getVisiblePortion() const;
// Get cursor position (relative to visible portion). -1 if invalid
s32 getVisibleCursorPosition() const;
+ // Get length of cursor selection
+ s32 getCursorLength() const { return m_cursor_len; }
// Cursor operations
enum CursorOp {
CURSOROP_MOVE,
+ CURSOROP_SELECT,
CURSOROP_DELETE
};
@@ -186,7 +196,8 @@ public:
enum CursorOpScope {
CURSOROP_SCOPE_CHARACTER,
CURSOROP_SCOPE_WORD,
- CURSOROP_SCOPE_LINE
+ CURSOROP_SCOPE_LINE,
+ CURSOROP_SCOPE_SELECTION
};
// Cursor operation
@@ -213,7 +224,7 @@ private:
std::wstring m_line;
// History buffer
std::vector<std::wstring> m_history;
- // History index (0 <= m_history_index <= m_history.size())
+ // History index (0 <= m_history_index <= m_history.size())
u32 m_history_index;
// Maximum number of history entries
u32 m_history_limit;
@@ -224,6 +235,8 @@ private:
s32 m_view;
// Cursor (index into m_line)
s32 m_cursor;
+ // Cursor length (length of selected portion of line)
+ s32 m_cursor_len;
// Last nick completion start (index into m_line)
s32 m_nick_completion_start;
diff --git a/src/chat_interface.h b/src/chat_interface.h
new file mode 100644
index 000000000..4784821fc
--- /dev/null
+++ b/src/chat_interface.h
@@ -0,0 +1,82 @@
+/*
+Minetest
+Copyright (C) 2015 est31 <MTest31@outlook.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.
+*/
+
+#ifndef CHAT_INTERFACE_H
+#define CHAT_INTERFACE_H
+
+#include "util/container.h"
+#include <string>
+#include <queue>
+#include "irrlichttypes.h"
+
+enum ChatEventType {
+ CET_CHAT,
+ CET_NICK_ADD,
+ CET_NICK_REMOVE,
+ CET_TIME_INFO,
+};
+
+class ChatEvent {
+protected:
+ ChatEvent(ChatEventType a_type) { type = a_type; }
+public:
+ ChatEventType type;
+};
+
+struct ChatEventTimeInfo : public ChatEvent {
+ ChatEventTimeInfo(
+ u64 a_game_time,
+ u32 a_time) :
+ ChatEvent(CET_TIME_INFO),
+ game_time(a_game_time),
+ time(a_time)
+ {}
+
+ u64 game_time;
+ u32 time;
+};
+
+struct ChatEventNick : public ChatEvent {
+ ChatEventNick(ChatEventType a_type,
+ const std::string &a_nick) :
+ ChatEvent(a_type), // one of CET_NICK_ADD, CET_NICK_REMOVE
+ nick(a_nick)
+ {}
+
+ std::string nick;
+};
+
+struct ChatEventChat : public ChatEvent {
+ ChatEventChat(const std::string &a_nick,
+ const std::wstring &an_evt_msg) :
+ ChatEvent(CET_CHAT),
+ nick(a_nick),
+ evt_msg(an_evt_msg)
+ {}
+
+ std::string nick;
+ std::wstring evt_msg;
+};
+
+struct ChatInterface {
+ MutexedQueue<ChatEvent *> command_queue; // chat backend --> server
+ MutexedQueue<ChatEvent *> outgoing_queue; // server --> chat backend
+};
+
+#endif
diff --git a/src/client.cpp b/src/client.cpp
index 946f4f1c4..4ffcec6ba 100644
--- a/src/client.cpp
+++ b/src/client.cpp
@@ -21,7 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <algorithm>
#include <sstream>
#include <IFileSystem.h>
-#include "jthread/jmutexautolock.h"
+#include "threading/mutex_auto_lock.h"
#include "util/auth.h"
#include "util/directiontables.h"
#include "util/pointedthing.h"
@@ -82,11 +82,11 @@ MeshUpdateQueue::MeshUpdateQueue()
MeshUpdateQueue::~MeshUpdateQueue()
{
- JMutexAutoLock lock(m_mutex);
+ MutexAutoLock lock(m_mutex);
for(std::vector<QueuedMeshUpdate*>::iterator
i = m_queue.begin();
- i != m_queue.end(); i++)
+ i != m_queue.end(); ++i)
{
QueuedMeshUpdate *q = *i;
delete q;
@@ -98,11 +98,11 @@ MeshUpdateQueue::~MeshUpdateQueue()
*/
void MeshUpdateQueue::addBlock(v3s16 p, MeshMakeData *data, bool ack_block_to_server, bool urgent)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
assert(data); // pre-condition
- JMutexAutoLock lock(m_mutex);
+ MutexAutoLock lock(m_mutex);
if(urgent)
m_urgents.insert(p);
@@ -113,7 +113,7 @@ void MeshUpdateQueue::addBlock(v3s16 p, MeshMakeData *data, bool ack_block_to_se
*/
for(std::vector<QueuedMeshUpdate*>::iterator
i = m_queue.begin();
- i != m_queue.end(); i++)
+ i != m_queue.end(); ++i)
{
QueuedMeshUpdate *q = *i;
if(q->p == p)
@@ -141,12 +141,12 @@ void MeshUpdateQueue::addBlock(v3s16 p, MeshMakeData *data, bool ack_block_to_se
// Returns NULL if queue is empty
QueuedMeshUpdate *MeshUpdateQueue::pop()
{
- JMutexAutoLock lock(m_mutex);
+ MutexAutoLock lock(m_mutex);
bool must_be_urgent = !m_urgents.empty();
for(std::vector<QueuedMeshUpdate*>::iterator
i = m_queue.begin();
- i != m_queue.end(); i++)
+ i != m_queue.end(); ++i)
{
QueuedMeshUpdate *q = *i;
if(must_be_urgent && m_urgents.count(q->p) == 0)
@@ -228,17 +228,17 @@ Client::Client(
m_particle_manager(&m_env),
m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, ipv6, this),
m_device(device),
+ m_camera(NULL),
+ m_minimap_disabled_by_server(false),
m_server_ser_ver(SER_FMT_VER_INVALID),
m_proto_ver(0),
m_playeritem(0),
m_inventory_updated(false),
m_inventory_from_server(NULL),
m_inventory_from_server_age(0.0),
- m_show_highlighted(false),
m_animation_time(0),
m_crack_level(-1),
m_crack_pos(0,0,0),
- m_highlighted_pos(0,0,0),
m_map_seed(0),
m_password(password),
m_chosen_auth_mech(AUTH_MECHANISM_NONE),
@@ -264,12 +264,15 @@ Client::Client(
m_cache_smooth_lighting = g_settings->getBool("smooth_lighting");
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"));
}
void Client::Stop()
{
//request all client managed threads to stop
- m_mesh_update_thread.Stop();
+ m_mesh_update_thread.stop();
// Save local server map
if (m_localdb) {
infostream << "Local map saving ended." << std::endl;
@@ -280,7 +283,7 @@ void Client::Stop()
bool Client::isShutdown()
{
- if (!m_mesh_update_thread.IsRunning()) return true;
+ if (!m_mesh_update_thread.isRunning()) return true;
return false;
}
@@ -289,8 +292,8 @@ Client::~Client()
{
m_con.Disconnect();
- m_mesh_update_thread.Stop();
- m_mesh_update_thread.Wait();
+ m_mesh_update_thread.stop();
+ m_mesh_update_thread.wait();
while (!m_mesh_update_thread.m_queue_out.empty()) {
MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_frontNoEx();
delete r.mesh;
@@ -322,7 +325,7 @@ void Client::connect(Address address,
const std::string &address_name,
bool is_local_server)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
initLocalMapSaving(address, address_name, is_local_server);
@@ -332,7 +335,7 @@ void Client::connect(Address address,
void Client::step(float dtime)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
// Limit a bit
if(dtime > 2.0)
@@ -383,25 +386,30 @@ void Client::step(float dtime)
Player *myplayer = m_env.getLocalPlayer();
FATAL_ERROR_IF(myplayer == NULL, "Local player not found in environment.");
- // Send TOSERVER_INIT_LEGACY
- // [0] u16 TOSERVER_INIT_LEGACY
- // [2] u8 SER_FMT_VER_HIGHEST_READ
- // [3] u8[20] player_name
- // [23] u8[28] password (new in some version)
- // [51] u16 minimum supported network protocol version (added sometime)
- // [53] u16 maximum supported network protocol version (added later than the previous one)
-
- char pName[PLAYERNAME_SIZE];
- char pPassword[PASSWORD_SIZE];
- memset(pName, 0, PLAYERNAME_SIZE * sizeof(char));
- memset(pPassword, 0, PASSWORD_SIZE * sizeof(char));
-
- std::string hashed_password = translatePassword(myplayer->getName(), m_password);
- snprintf(pName, PLAYERNAME_SIZE, "%s", myplayer->getName());
- snprintf(pPassword, PASSWORD_SIZE, "%s", hashed_password.c_str());
-
- sendLegacyInit(pName, pPassword);
- if (LATEST_PROTOCOL_VERSION >= 25)
+ u16 proto_version_min = g_settings->getFlag("send_pre_v25_init") ?
+ CLIENT_PROTOCOL_VERSION_MIN_LEGACY : CLIENT_PROTOCOL_VERSION_MIN;
+
+ if (proto_version_min < 25) {
+ // Send TOSERVER_INIT_LEGACY
+ // [0] u16 TOSERVER_INIT_LEGACY
+ // [2] u8 SER_FMT_VER_HIGHEST_READ
+ // [3] u8[20] player_name
+ // [23] u8[28] password (new in some version)
+ // [51] u16 minimum supported network protocol version (added sometime)
+ // [53] u16 maximum supported network protocol version (added later than the previous one)
+
+ char pName[PLAYERNAME_SIZE];
+ char pPassword[PASSWORD_SIZE];
+ memset(pName, 0, PLAYERNAME_SIZE * sizeof(char));
+ memset(pPassword, 0, PASSWORD_SIZE * sizeof(char));
+
+ std::string hashed_password = translate_password(myplayer->getName(), m_password);
+ snprintf(pName, PLAYERNAME_SIZE, "%s", myplayer->getName());
+ snprintf(pPassword, PASSWORD_SIZE, "%s", hashed_password.c_str());
+
+ sendLegacyInit(pName, pPassword);
+ }
+ if (CLIENT_PROTOCOL_VERSION_MAX >= 25)
sendInit(myplayer->getName());
}
@@ -617,7 +625,7 @@ void Client::step(float dtime)
{
for(std::map<int, u16>::iterator
i = m_sounds_to_objects.begin();
- i != m_sounds_to_objects.end(); i++)
+ i != m_sounds_to_objects.end(); ++i)
{
int client_id = i->first;
u16 object_id = i->second;
@@ -642,7 +650,7 @@ void Client::step(float dtime)
i != m_sounds_server_to_client.end();) {
s32 server_id = i->first;
int client_id = i->second;
- i++;
+ ++i;
if(!m_sound->soundExists(client_id)) {
m_sounds_server_to_client.erase(server_id);
m_sounds_client_to_server.erase(client_id);
@@ -823,7 +831,7 @@ void Client::initLocalMapSaving(const Address &address,
void Client::ReceiveAll()
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
u32 start_ms = porting::getTimeMs();
for(;;)
{
@@ -849,7 +857,7 @@ void Client::ReceiveAll()
void Client::Receive()
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
NetworkPacket pkt;
m_con.Receive(&pkt);
ProcessData(&pkt);
@@ -866,7 +874,7 @@ inline void Client::handleCommand(NetworkPacket* pkt)
*/
void Client::ProcessData(NetworkPacket *pkt)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
ToClientCommand command = (ToClientCommand) pkt->getCommand();
u32 sender_peer_id = pkt->getPeerId();
@@ -945,6 +953,7 @@ void Client::interact(u8 action, const PointedThing& pointed)
2: digging completed
3: place block or item (to abovesurface)
4: use item
+ 5: perform secondary action of item
*/
NetworkPacket pkt(TOSERVER_INTERACT, 1 + 2 + 0);
@@ -999,10 +1008,13 @@ void Client::sendLegacyInit(const char* playerName, const char* playerPassword)
NetworkPacket pkt(TOSERVER_INIT_LEGACY,
1 + PLAYERNAME_SIZE + PASSWORD_SIZE + 2 + 2);
+ u16 proto_version_min = g_settings->getFlag("send_pre_v25_init") ?
+ CLIENT_PROTOCOL_VERSION_MIN_LEGACY : CLIENT_PROTOCOL_VERSION_MIN;
+
pkt << (u8) SER_FMT_VER_HIGHEST_READ;
pkt.putRawString(playerName,PLAYERNAME_SIZE);
pkt.putRawString(playerPassword, PASSWORD_SIZE);
- pkt << (u16) CLIENT_PROTOCOL_VERSION_MIN << (u16) CLIENT_PROTOCOL_VERSION_MAX;
+ pkt << (u16) proto_version_min << (u16) CLIENT_PROTOCOL_VERSION_MAX;
Send(&pkt);
}
@@ -1013,8 +1025,12 @@ void Client::sendInit(const std::string &playerName)
// we don't support network compression yet
u16 supp_comp_modes = NETPROTO_COMPRESSION_NONE;
+
+ u16 proto_version_min = g_settings->getFlag("send_pre_v25_init") ?
+ CLIENT_PROTOCOL_VERSION_MIN_LEGACY : CLIENT_PROTOCOL_VERSION_MIN;
+
pkt << (u8) SER_FMT_VER_HIGHEST_READ << (u16) supp_comp_modes;
- pkt << (u16) CLIENT_PROTOCOL_VERSION_MIN << (u16) CLIENT_PROTOCOL_VERSION_MAX;
+ pkt << (u16) proto_version_min << (u16) CLIENT_PROTOCOL_VERSION_MAX;
pkt << playerName;
Send(&pkt);
@@ -1027,18 +1043,14 @@ void Client::startAuth(AuthMechanism chosen_auth_mechanism)
switch (chosen_auth_mechanism) {
case AUTH_MECHANISM_FIRST_SRP: {
// send srp verifier to server
+ std::string verifier;
+ std::string salt;
+ generate_srp_verifier_and_salt(getPlayerName(), m_password,
+ &verifier, &salt);
+
NetworkPacket resp_pkt(TOSERVER_FIRST_SRP, 0);
- char *salt, *bytes_v;
- std::size_t len_salt, len_v;
- salt = NULL;
- getSRPVerifier(getPlayerName(), m_password,
- &salt, &len_salt, &bytes_v, &len_v);
- resp_pkt
- << std::string((char*)salt, len_salt)
- << std::string((char*)bytes_v, len_v)
- << (u8)((m_password == "") ? 1 : 0);
- free(salt);
- free(bytes_v);
+ resp_pkt << salt << verifier << (u8)((m_password == "") ? 1 : 0);
+
Send(&resp_pkt);
break;
}
@@ -1047,7 +1059,7 @@ void Client::startAuth(AuthMechanism chosen_auth_mechanism)
u8 based_on = 1;
if (chosen_auth_mechanism == AUTH_MECHANISM_LEGACY_PASSWORD) {
- m_password = translatePassword(getPlayerName(), m_password);
+ m_password = translate_password(getPlayerName(), m_password);
based_on = 0;
}
@@ -1058,8 +1070,10 @@ void Client::startAuth(AuthMechanism chosen_auth_mechanism)
m_password.length(), NULL, NULL);
char *bytes_A = 0;
size_t len_A = 0;
- srp_user_start_authentication((struct SRPUser *) m_auth_data,
- NULL, NULL, 0, (unsigned char **) &bytes_A, &len_A);
+ SRP_Result res = srp_user_start_authentication(
+ (struct SRPUser *) m_auth_data, NULL, NULL, 0,
+ (unsigned char **) &bytes_A, &len_A);
+ FATAL_ERROR_IF(res != SRP_OK, "Creating local SRP user failed.");
NetworkPacket resp_pkt(TOSERVER_SRP_BYTES_A, 0);
resp_pkt << std::string(bytes_A, len_A) << based_on;
@@ -1105,7 +1119,7 @@ void Client::sendRemovedSounds(std::vector<s32> &soundList)
pkt << (u16) (server_ids & 0xFFFF);
for(std::vector<s32>::iterator i = soundList.begin();
- i != soundList.end(); i++)
+ i != soundList.end(); ++i)
pkt << *i;
Send(&pkt);
@@ -1191,8 +1205,8 @@ void Client::sendChangePassword(const std::string &oldpassword,
m_new_password = newpassword;
startAuth(choseAuthMech(m_sudo_auth_methods));
} else {
- std::string oldpwd = translatePassword(playername, oldpassword);
- std::string newpwd = translatePassword(playername, newpassword);
+ std::string oldpwd = translate_password(playername, oldpassword);
+ std::string newpwd = translate_password(playername, newpassword);
NetworkPacket pkt(TOSERVER_PASSWORD_LEGACY, 2 * PASSWORD_SIZE);
@@ -1210,7 +1224,7 @@ void Client::sendChangePassword(const std::string &oldpassword,
void Client::sendDamage(u8 damage)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
NetworkPacket pkt(TOSERVER_DAMAGE, sizeof(u8));
pkt << damage;
@@ -1219,7 +1233,7 @@ void Client::sendDamage(u8 damage)
void Client::sendBreath(u16 breath)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
NetworkPacket pkt(TOSERVER_BREATH, sizeof(u16));
pkt << breath;
@@ -1228,7 +1242,7 @@ void Client::sendBreath(u16 breath)
void Client::sendRespawn()
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
NetworkPacket pkt(TOSERVER_RESPAWN, 0);
Send(&pkt);
@@ -1236,7 +1250,7 @@ void Client::sendRespawn()
void Client::sendReady()
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
NetworkPacket pkt(TOSERVER_CLIENT_READY,
1 + 1 + 1 + 1 + 2 + sizeof(char) * strlen(g_version_hash));
@@ -1270,7 +1284,7 @@ void Client::sendPlayerPos()
u16 our_peer_id;
{
- //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
+ //MutexAutoLock lock(m_con_mutex); //bulk comment-out
our_peer_id = m_con.GetPeerID();
}
@@ -1469,13 +1483,13 @@ ClientActiveObject * Client::getSelectedActiveObject(
{
ClientActiveObject *obj = objects[i].obj;
- core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
+ aabb3f *selection_box = obj->getSelectionBox();
if(selection_box == NULL)
continue;
v3f pos = obj->getPosition();
- core::aabbox3d<f32> offsetted_box(
+ aabb3f offsetted_box(
selection_box->MinEdge + pos,
selection_box->MaxEdge + pos
);
@@ -1504,15 +1518,6 @@ int Client::getCrackLevel()
return m_crack_level;
}
-void Client::setHighlighted(v3s16 pos, bool show_highlighted)
-{
- m_show_highlighted = show_highlighted;
- v3s16 old_highlighted_pos = m_highlighted_pos;
- m_highlighted_pos = pos;
- addUpdateMeshTaskForNode(old_highlighted_pos, false, true);
- addUpdateMeshTaskForNode(m_highlighted_pos, false, true);
-}
-
void Client::setCrack(int level, v3s16 pos)
{
int old_crack_level = m_crack_level;
@@ -1589,7 +1594,8 @@ void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server, bool urgent)
Create a task to update the mesh of the block
*/
- MeshMakeData *data = new MeshMakeData(this, m_cache_enable_shaders);
+ MeshMakeData *data = new MeshMakeData(this, m_cache_enable_shaders,
+ m_cache_use_tangent_vertices);
{
//TimeTaker timer("data fill");
@@ -1597,7 +1603,6 @@ void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server, bool urgent)
// Debug: 1-6ms, avg=2ms
data->fill(b);
data->setCrack(m_crack_level, m_crack_pos);
- data->setHighlighted(m_highlighted_pos, m_show_highlighted);
data->setSmoothLighting(m_cache_smooth_lighting);
}
@@ -1769,32 +1774,9 @@ void Client::afterContentReceived(IrrlichtDevice *device)
m_nodedef->updateTextures(this, texture_update_progress, &tu_args);
delete[] tu_args.text_base;
- // Preload item textures and meshes if configured to
- if(g_settings->getBool("preload_item_visuals"))
- {
- verbosestream<<"Updating item textures and meshes"<<std::endl;
- text = wgettext("Item textures...");
- draw_load_screen(text, device, guienv, 0, 0);
- std::set<std::string> names = m_itemdef->getAll();
- size_t size = names.size();
- size_t count = 0;
- int percent = 0;
- for(std::set<std::string>::const_iterator
- i = names.begin(); i != names.end(); ++i)
- {
- // Asking for these caches the result
- m_itemdef->getInventoryTexture(*i, this);
- m_itemdef->getWieldMesh(*i, this);
- count++;
- percent = (count * 100 / size * 0.2) + 80;
- draw_load_screen(text, device, guienv, 0, percent);
- }
- delete[] text;
- }
-
// Start mesh update thread after setting up content definitions
infostream<<"- Starting mesh update thread"<<std::endl;
- m_mesh_update_thread.Start();
+ m_mesh_update_thread.start();
m_state = LC_Ready;
sendReady();
@@ -1839,9 +1821,12 @@ void Client::makeScreenshot(IrrlichtDevice *device)
+ DIR_DELIM
+ std::string("screenshot_")
+ std::string(timetstamp_c);
- std::string filename_ext = ".png";
+ std::string filename_ext = "." + g_settings->get("screenshot_format");
std::string filename;
+ u32 quality = (u32)g_settings->getS32("screenshot_quality");
+ quality = MYMIN(MYMAX(quality, 0), 100) / 100.0 * 255;
+
// Try to find a unique filename
unsigned serial = 0;
@@ -1863,7 +1848,7 @@ void Client::makeScreenshot(IrrlichtDevice *device)
raw_image->copyTo(image);
std::ostringstream sstr;
- if (driver->writeImageToFile(image, filename.c_str())) {
+ if (driver->writeImageToFile(image, filename.c_str(), quality)) {
sstr << "Saved screenshot to '" << filename << "'";
} else {
sstr << "Failed to save screenshot '" << filename << "'";
diff --git a/src/client.h b/src/client.h
index 547edfeab..cdadb9d3e 100644
--- a/src/client.h
+++ b/src/client.h
@@ -23,7 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "network/connection.h"
#include "environment.h"
#include "irrlichttypes_extrabloated.h"
-#include "jthread/jmutex.h"
+#include "threading/mutex.h"
#include <ostream>
#include <map>
#include <set>
@@ -50,6 +50,7 @@ struct PointedThing;
class Database;
class Mapper;
struct MinimapMapblock;
+class Camera;
struct QueuedMeshUpdate
{
@@ -89,14 +90,14 @@ public:
u32 size()
{
- JMutexAutoLock lock(m_mutex);
+ MutexAutoLock lock(m_mutex);
return m_queue.size();
}
private:
std::vector<QueuedMeshUpdate*> m_queue;
std::set<v3s16> m_urgents;
- JMutex m_mutex;
+ Mutex m_mutex;
};
struct MeshUpdateResult
@@ -119,19 +120,14 @@ private:
MeshUpdateQueue m_queue_in;
protected:
- const char *getName()
- { return "MeshUpdateThread"; }
virtual void doUpdate();
public:
- MeshUpdateThread()
- {
- }
+ MeshUpdateThread() : UpdateThread("Mesh") {}
void enqueueUpdate(v3s16 p, MeshMakeData *data,
bool ack_block_to_server, bool urgent);
-
MutexedQueue<MeshUpdateResult> m_queue_out;
v3s16 m_camera_offset;
@@ -461,9 +457,6 @@ public:
int getCrackLevel();
void setCrack(int level, v3s16 pos);
- void setHighlighted(v3s16 pos, bool show_higlighted);
- v3s16 getHighlighted(){ return m_highlighted_pos; }
-
u16 getHP();
u16 getBreath();
@@ -515,6 +508,15 @@ public:
Mapper* getMapper ()
{ return m_mapper; }
+ void setCamera(Camera* camera)
+ { m_camera = camera; }
+
+ Camera* getCamera ()
+ { return m_camera; }
+
+ bool isMinimapDisabledByServer()
+ { return m_minimap_disabled_by_server; }
+
// IGameDef interface
virtual IItemDefManager* getItemDefManager();
virtual INodeDefManager* getNodeDefManager();
@@ -594,7 +596,9 @@ private:
ParticleManager m_particle_manager;
con::Connection m_con;
IrrlichtDevice *m_device;
+ Camera *m_camera;
Mapper *m_mapper;
+ bool m_minimap_disabled_by_server;
// Server serialization version
u8 m_server_ser_ver;
@@ -610,12 +614,10 @@ private:
Inventory *m_inventory_from_server;
float m_inventory_from_server_age;
PacketCounter m_packetcounter;
- bool m_show_highlighted;
// Block mesh animation parameters
float m_animation_time;
int m_crack_level;
v3s16 m_crack_pos;
- v3s16 m_highlighted_pos;
// 0 <= m_daynight_i < DAYNIGHT_CACHE_COUNT
//s32 m_daynight_i;
//u32 m_daynight_ratio;
@@ -683,6 +685,9 @@ private:
// TODO: Add callback to update these when g_settings changes
bool m_cache_smooth_lighting;
bool m_cache_enable_shaders;
+ bool m_cache_use_tangent_vertices;
+
+ DISABLE_CLASS_COPY(Client);
};
#endif // !CLIENT_HEADER
diff --git a/src/client/clientlauncher.cpp b/src/client/clientlauncher.cpp
index bad5c384c..404a16310 100644
--- a/src/client/clientlauncher.cpp
+++ b/src/client/clientlauncher.cpp
@@ -89,7 +89,7 @@ bool ClientLauncher::run(GameParams &game_params, const Settings &cmd_args)
if (list_video_modes)
return print_video_modes();
- if (!init_engine(game_params.log_level)) {
+ if (!init_engine()) {
errorstream << "Could not initialize game engine." << std::endl;
return false;
}
@@ -201,6 +201,9 @@ bool ClientLauncher::run(GameParams &game_params, const Settings &cmd_args)
bool game_has_run = launch_game(error_message, reconnect_requested,
game_params, cmd_args);
+ // Reset the reconnect_requested flag
+ reconnect_requested = false;
+
// If skip_main_menu, we only want to startup once
if (skip_main_menu && !first_loop)
break;
@@ -321,10 +324,10 @@ void ClientLauncher::init_args(GameParams &game_params, const Settings &cmd_args
|| cmd_args.getFlag("random-input");
}
-bool ClientLauncher::init_engine(int log_level)
+bool ClientLauncher::init_engine()
{
receiver = new MyEventReceiver();
- create_engine_device(log_level);
+ create_engine_device();
return device != NULL;
}
@@ -336,6 +339,7 @@ bool ClientLauncher::launch_game(std::string &error_message,
MainMenuData menudata;
menudata.address = address;
menudata.name = playername;
+ menudata.password = password;
menudata.port = itos(game_params.socket_port);
menudata.script_data.errormessage = error_message;
menudata.script_data.reconnect_requested = reconnect_requested;
@@ -455,7 +459,7 @@ bool ClientLauncher::launch_game(std::string &error_message,
if (game_params.game_spec.isValid() &&
game_params.game_spec.id != worldspec.gameid) {
- errorstream << "WARNING: Overriding gamespec from \""
+ warningstream << "Overriding gamespec from \""
<< worldspec.gameid << "\" to \""
<< game_params.game_spec.id << "\"" << std::endl;
gamespec = game_params.game_spec;
@@ -500,20 +504,8 @@ void ClientLauncher::main_menu(MainMenuData *menudata)
smgr->clear(); /* leave scene manager in a clean state */
}
-bool ClientLauncher::create_engine_device(int log_level)
+bool ClientLauncher::create_engine_device()
{
- static const irr::ELOG_LEVEL irr_log_level[5] = {
- ELL_NONE,
- ELL_ERROR,
- ELL_WARNING,
- ELL_INFORMATION,
-#if (IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 8)
- ELL_INFORMATION
-#else
- ELL_DEBUG
-#endif
- };
-
// Resolution selection
bool fullscreen = g_settings->getBool("fullscreen");
u16 screenW = g_settings->getU16("screenW");
@@ -524,6 +516,9 @@ bool ClientLauncher::create_engine_device(int log_level)
u16 bits = g_settings->getU16("fullscreen_bpp");
u16 fsaa = g_settings->getU16("fsaa");
+ // stereo buffer required for pageflip stereo
+ bool stereo_buffer = g_settings->get("3d_mode") == "pageflip";
+
// Determine driver
video::E_DRIVER_TYPE driverType = video::EDT_OPENGL;
std::string driverstring = g_settings->get("video_driver");
@@ -549,9 +544,11 @@ bool ClientLauncher::create_engine_device(int log_level)
params.AntiAlias = fsaa;
params.Fullscreen = fullscreen;
params.Stencilbuffer = false;
+ params.Stereobuffer = stereo_buffer;
params.Vsync = vsync;
params.EventReceiver = receiver;
params.HighPrecisionFPU = g_settings->getBool("high_precision_fpu");
+ params.ZBufferBits = 24;
#ifdef __ANDROID__
params.PrivateData = porting::app_global;
params.OGLES2ShaderPath = std::string(porting::path_user + DIR_DELIM +
@@ -561,10 +558,6 @@ bool ClientLauncher::create_engine_device(int log_level)
device = createDeviceEx(params);
if (device) {
- // Map our log level to irrlicht engine one.
- ILogger* irr_logger = device->getLogger();
- irr_logger->setLogLevel(irr_log_level[log_level]);
-
porting::initIrrlicht(device);
}
@@ -651,14 +644,14 @@ void ClientLauncher::speed_tests()
infostream << "Around 5000/ms should do well here." << std::endl;
TimeTaker timer("Testing mutex speed");
- JMutex m;
+ Mutex m;
u32 n = 0;
u32 i = 0;
do {
n += 10000;
for (; i < n; i++) {
- m.Lock();
- m.Unlock();
+ m.lock();
+ m.unlock();
}
}
// Do at least 10ms
@@ -696,7 +689,7 @@ bool ClientLauncher::print_video_modes()
return false;
}
- dstream << _("Available video modes (WxHxD):") << std::endl;
+ std::cout << _("Available video modes (WxHxD):") << std::endl;
video::IVideoModeList *videomode_list = nulldevice->getVideoModeList();
@@ -707,14 +700,14 @@ bool ClientLauncher::print_video_modes()
for (s32 i = 0; i < videomode_count; ++i) {
videomode_res = videomode_list->getVideoModeResolution(i);
videomode_depth = videomode_list->getVideoModeDepth(i);
- dstream << videomode_res.Width << "x" << videomode_res.Height
+ std::cout << videomode_res.Width << "x" << videomode_res.Height
<< "x" << videomode_depth << std::endl;
}
- dstream << _("Active video mode (WxHxD):") << std::endl;
+ std::cout << _("Active video mode (WxHxD):") << std::endl;
videomode_res = videomode_list->getDesktopResolution();
videomode_depth = videomode_list->getDesktopDepth();
- dstream << videomode_res.Width << "x" << videomode_res.Height
+ std::cout << videomode_res.Width << "x" << videomode_res.Height
<< "x" << videomode_depth << std::endl;
}
diff --git a/src/client/clientlauncher.h b/src/client/clientlauncher.h
index 49ceefc52..b10bbebc9 100644
--- a/src/client/clientlauncher.h
+++ b/src/client/clientlauncher.h
@@ -90,13 +90,13 @@ public:
protected:
void init_args(GameParams &game_params, const Settings &cmd_args);
- bool init_engine(int log_level);
+ bool init_engine();
bool launch_game(std::string &error_message, bool reconnect_requested,
GameParams &game_params, const Settings &cmd_args);
void main_menu(MainMenuData *menudata);
- bool create_engine_device(int log_level);
+ bool create_engine_device();
void speed_tests();
bool print_video_modes();
diff --git a/src/client/inputhandler.h b/src/client/inputhandler.h
index a894e35aa..69e4b25fa 100644
--- a/src/client/inputhandler.h
+++ b/src/client/inputhandler.h
@@ -17,8 +17,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef __INPUT_HANDLER_H__
-#define __INPUT_HANDLER_H__
+#ifndef INPUT_HANDLER_H
+#define INPUT_HANDLER_H
#include "irrlichttypes_extrabloated.h"
@@ -86,16 +86,16 @@ public:
}
}
} else if (event.EventType == irr::EET_LOG_TEXT_EVENT) {
- static const enum LogMessageLevel irr_loglev_conv[] = {
- LMT_VERBOSE, // ELL_DEBUG
- LMT_INFO, // ELL_INFORMATION
- LMT_ACTION, // ELL_WARNING
- LMT_ERROR, // ELL_ERROR
- LMT_ERROR, // ELL_NONE
+ static const LogLevel irr_loglev_conv[] = {
+ LL_VERBOSE, // ELL_DEBUG
+ LL_INFO, // ELL_INFORMATION
+ LL_WARNING, // ELL_WARNING
+ LL_ERROR, // ELL_ERROR
+ LL_NONE, // ELL_NONE
};
assert(event.LogEvent.Level < ARRLEN(irr_loglev_conv));
- log_printline(irr_loglev_conv[event.LogEvent.Level],
- std::string("Irrlicht: ") + (const char *)event.LogEvent.Text);
+ g_logger.log(irr_loglev_conv[event.LogEvent.Level],
+ std::string("Irrlicht: ") + (const char*) event.LogEvent.Text);
return true;
}
/* always return false in order to continue processing events */
diff --git a/src/client/tile.cpp b/src/client/tile.cpp
index a28b40c65..ec8c95f02 100644
--- a/src/client/tile.cpp
+++ b/src/client/tile.cpp
@@ -31,7 +31,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "mesh.h"
#include "log.h"
#include "gamedef.h"
-#include "strfnd.h"
+#include "util/strfnd.h"
#include "util/string.h" // for parseColorString()
#include "imagefilters.h"
#include "guiscalingfilter.h"
@@ -194,7 +194,7 @@ class SourceImageCache
public:
~SourceImageCache() {
for (std::map<std::string, video::IImage*>::iterator iter = m_images.begin();
- iter != m_images.end(); iter++) {
+ iter != m_images.end(); ++iter) {
iter->second->drop();
}
m_images.clear();
@@ -414,7 +414,7 @@ private:
// Maps a texture name to an index in the former.
std::map<std::string, u32> m_name_to_id;
// The two former containers are behind this mutex
- JMutex m_textureinfo_cache_mutex;
+ Mutex m_textureinfo_cache_mutex;
// Queued texture fetches (to be processed by the main thread)
RequestQueue<std::string, u32, u8, u8> m_get_texture_queue;
@@ -439,7 +439,7 @@ TextureSource::TextureSource(IrrlichtDevice *device):
{
assert(m_device); // Pre-condition
- m_main_thread = get_current_thread_id();
+ m_main_thread = thr_get_current_thread_id();
// Add a NULL TextureInfo as the first index, named ""
m_textureinfo_cache.push_back(TextureInfo(""));
@@ -461,7 +461,7 @@ TextureSource::~TextureSource()
for (std::vector<TextureInfo>::iterator iter =
m_textureinfo_cache.begin();
- iter != m_textureinfo_cache.end(); iter++)
+ iter != m_textureinfo_cache.end(); ++iter)
{
//cleanup texture
if (iter->texture)
@@ -471,7 +471,7 @@ TextureSource::~TextureSource()
for (std::vector<video::ITexture*>::iterator iter =
m_texture_trash.begin(); iter != m_texture_trash.end();
- iter++) {
+ ++iter) {
video::ITexture *t = *iter;
//cleanup trashed texture
@@ -490,7 +490,7 @@ u32 TextureSource::getTextureId(const std::string &name)
/*
See if texture already exists
*/
- JMutexAutoLock lock(m_textureinfo_cache_mutex);
+ MutexAutoLock lock(m_textureinfo_cache_mutex);
std::map<std::string, u32>::iterator n;
n = m_name_to_id.find(name);
if (n != m_name_to_id.end())
@@ -502,7 +502,7 @@ u32 TextureSource::getTextureId(const std::string &name)
/*
Get texture
*/
- if (get_current_thread_id() == m_main_thread)
+ if (thr_is_current_thread(m_main_thread))
{
return generateTexture(name);
}
@@ -553,10 +553,12 @@ static void blit_with_alpha(video::IImage *src, video::IImage *dst,
static void blit_with_alpha_overlay(video::IImage *src, video::IImage *dst,
v2s32 src_pos, v2s32 dst_pos, v2u32 size);
-// Like blit_with_alpha overlay, but uses an int to calculate the ratio
-// and modifies any destination pixels that are not fully transparent
-static void blit_with_interpolate_overlay(video::IImage *src, video::IImage *dst,
- v2s32 src_pos, v2s32 dst_pos, v2u32 size, int ratio);
+// Apply a color to an image. Uses an int (0-255) to calculate the ratio.
+// If the ratio is 255 or -1 and keep_alpha is true, then it multiples the
+// color alpha with the destination alpha.
+// Otherwise, any pixels that are not fully transparent get the color alpha.
+static void apply_colorize(video::IImage *dst, v2u32 dst_pos, v2u32 size,
+ video::SColor color, int ratio, bool keep_alpha);
// Apply a mask to an image
static void apply_mask(video::IImage *mask, video::IImage *dst,
@@ -593,7 +595,7 @@ u32 TextureSource::generateTexture(const std::string &name)
/*
See if texture already exists
*/
- JMutexAutoLock lock(m_textureinfo_cache_mutex);
+ MutexAutoLock lock(m_textureinfo_cache_mutex);
std::map<std::string, u32>::iterator n;
n = m_name_to_id.find(name);
if (n != m_name_to_id.end()) {
@@ -604,7 +606,7 @@ u32 TextureSource::generateTexture(const std::string &name)
/*
Calling only allowed from main thread
*/
- if (get_current_thread_id() != m_main_thread) {
+ if (!thr_is_current_thread(m_main_thread)) {
errorstream<<"TextureSource::generateTexture() "
"called not from main thread"<<std::endl;
return 0;
@@ -631,7 +633,7 @@ u32 TextureSource::generateTexture(const std::string &name)
Add texture to caches (add NULL textures too)
*/
- JMutexAutoLock lock(m_textureinfo_cache_mutex);
+ MutexAutoLock lock(m_textureinfo_cache_mutex);
u32 id = m_textureinfo_cache.size();
TextureInfo ti(name, tex);
@@ -643,7 +645,7 @@ u32 TextureSource::generateTexture(const std::string &name)
std::string TextureSource::getTextureName(u32 id)
{
- JMutexAutoLock lock(m_textureinfo_cache_mutex);
+ MutexAutoLock lock(m_textureinfo_cache_mutex);
if (id >= m_textureinfo_cache.size())
{
@@ -658,7 +660,7 @@ std::string TextureSource::getTextureName(u32 id)
video::ITexture* TextureSource::getTexture(u32 id)
{
- JMutexAutoLock lock(m_textureinfo_cache_mutex);
+ MutexAutoLock lock(m_textureinfo_cache_mutex);
if (id >= m_textureinfo_cache.size())
return NULL;
@@ -704,7 +706,7 @@ void TextureSource::insertSourceImage(const std::string &name, video::IImage *im
{
//infostream<<"TextureSource::insertSourceImage(): name="<<name<<std::endl;
- sanity_check(get_current_thread_id() == m_main_thread);
+ sanity_check(thr_is_current_thread(m_main_thread));
m_sourcecache.insert(name, img, true, m_device->getVideoDriver());
m_source_image_existence.set(name, true);
@@ -712,7 +714,7 @@ void TextureSource::insertSourceImage(const std::string &name, video::IImage *im
void TextureSource::rebuildImagesAndTextures()
{
- JMutexAutoLock lock(m_textureinfo_cache_mutex);
+ MutexAutoLock lock(m_textureinfo_cache_mutex);
video::IVideoDriver* driver = m_device->getVideoDriver();
sanity_check(driver);
@@ -1173,7 +1175,28 @@ bool TextureSource::generateImagePart(std::string part_of_name,
core::rect<s32>(pos_from, dim),
video::SColor(255,255,255,255),
NULL);*/
- blit_with_alpha(image, baseimg, pos_from, pos_to, dim);
+
+ core::dimension2d<u32> dim_dst = baseimg->getDimension();
+ if (dim == dim_dst) {
+ blit_with_alpha(image, baseimg, pos_from, pos_to, dim);
+ } else if (dim.Width * dim.Height < dim_dst.Width * dim_dst.Height) {
+ // Upscale overlying image
+ video::IImage* scaled_image = m_device->getVideoDriver()->
+ createImage(video::ECF_A8R8G8B8, dim_dst);
+ image->copyToScaling(scaled_image);
+
+ blit_with_alpha(scaled_image, baseimg, pos_from, pos_to, dim_dst);
+ scaled_image->drop();
+ } else {
+ // Upscale base image
+ video::IImage* scaled_base = m_device->getVideoDriver()->
+ createImage(video::ECF_A8R8G8B8, dim);
+ baseimg->copyToScaling(scaled_base);
+ baseimg->drop();
+ baseimg = scaled_base;
+
+ blit_with_alpha(image, baseimg, pos_from, pos_to, dim);
+ }
}
//cleanup
image->drop();
@@ -1242,7 +1265,7 @@ bool TextureSource::generateImagePart(std::string part_of_name,
baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
baseimg->fill(video::SColor(0,0,0,0));
}
- while (sf.atend() == false) {
+ while (sf.at_end() == false) {
u32 x = stoi(sf.next(","));
u32 y = stoi(sf.next("="));
std::string filename = sf.next(":");
@@ -1329,7 +1352,6 @@ bool TextureSource::generateImagePart(std::string part_of_name,
u32 r1 = stoi(sf.next(","));
u32 g1 = stoi(sf.next(","));
u32 b1 = stoi(sf.next(""));
- std::string filename = sf.next("");
core::dimension2d<u32> dim = baseimg->getDimension();
@@ -1639,27 +1661,17 @@ bool TextureSource::generateImagePart(std::string part_of_name,
video::SColor color;
int ratio = -1;
+ bool keep_alpha = false;
if (!parseColorString(color_str, color, false))
return false;
if (is_number(ratio_str))
ratio = mystoi(ratio_str, 0, 255);
+ else if (ratio_str == "alpha")
+ keep_alpha = true;
- core::dimension2d<u32> dim = baseimg->getDimension();
- video::IImage *img = driver->createImage(video::ECF_A8R8G8B8, dim);
-
- if (!img) {
- errorstream << "generateImagePart(): Could not create image "
- << "for part_of_name=\"" << part_of_name
- << "\", cancelling." << std::endl;
- return false;
- }
-
- img->fill(video::SColor(color));
- // Overlay the colored image
- blit_with_interpolate_overlay(img, baseimg, v2s32(0,0), v2s32(0,0), dim, ratio);
- img->drop();
+ apply_colorize(baseimg, v2u32(0, 0), baseimg->getDimension(), color, ratio, keep_alpha);
}
else if (str_starts_with(part_of_name, "[applyfiltersformesh"))
{
@@ -1698,6 +1710,31 @@ bool TextureSource::generateImagePart(std::string part_of_name,
}
}
}
+ /*
+ [resize:WxH
+ Resizes the base image to the given dimensions
+ */
+ else if (str_starts_with(part_of_name, "[resize"))
+ {
+ if (baseimg == NULL) {
+ errorstream << "generateImagePart(): baseimg == NULL "
+ << "for part_of_name=\""<< part_of_name
+ << "\", cancelling." << std::endl;
+ return false;
+ }
+
+ Strfnd sf(part_of_name);
+ sf.next(":");
+ u32 width = stoi(sf.next("x"));
+ u32 height = stoi(sf.next(""));
+ core::dimension2d<u32> dim(width, height);
+
+ video::IImage* image = m_device->getVideoDriver()->
+ createImage(video::ECF_A8R8G8B8, dim);
+ baseimg->copyToScaling(image);
+ baseimg->drop();
+ baseimg = image;
+ }
else
{
errorstream << "generateImagePart(): Invalid "
@@ -1756,6 +1793,9 @@ static void blit_with_alpha_overlay(video::IImage *src, video::IImage *dst,
}
}
+// This function has been disabled because it is currently unused.
+// Feel free to re-enable if you find it handy.
+#if 0
/*
Draw an image on top of an another one, using the specified ratio
modify all partially-opaque pixels in the destination.
@@ -1782,6 +1822,45 @@ static void blit_with_interpolate_overlay(video::IImage *src, video::IImage *dst
}
}
}
+#endif
+
+/*
+ Apply color to destination
+*/
+static void apply_colorize(video::IImage *dst, v2u32 dst_pos, v2u32 size,
+ video::SColor color, int ratio, bool keep_alpha)
+{
+ u32 alpha = color.getAlpha();
+ video::SColor dst_c;
+ if ((ratio == -1 && alpha == 255) || ratio == 255) { // full replacement of color
+ if (keep_alpha) { // replace the color with alpha = dest alpha * color alpha
+ dst_c = color;
+ for (u32 y = dst_pos.Y; y < dst_pos.Y + size.Y; y++)
+ for (u32 x = dst_pos.X; x < dst_pos.X + size.X; x++) {
+ u32 dst_alpha = dst->getPixel(x, y).getAlpha();
+ if (dst_alpha > 0) {
+ dst_c.setAlpha(dst_alpha * alpha / 255);
+ dst->setPixel(x, y, dst_c);
+ }
+ }
+ } else { // replace the color including the alpha
+ for (u32 y = dst_pos.Y; y < dst_pos.Y + size.Y; y++)
+ for (u32 x = dst_pos.X; x < dst_pos.X + size.X; x++)
+ if (dst->getPixel(x, y).getAlpha() > 0)
+ dst->setPixel(x, y, color);
+ }
+ } else { // interpolate between the color and destination
+ float interp = (ratio == -1 ? color.getAlpha() / 255.0f : ratio / 255.0f);
+ for (u32 y = dst_pos.Y; y < dst_pos.Y + size.Y; y++)
+ for (u32 x = dst_pos.X; x < dst_pos.X + size.X; x++) {
+ dst_c = dst->getPixel(x, y);
+ if (dst_c.getAlpha() > 0) {
+ dst_c = color.getInterpolated(dst_c, interp);
+ dst->setPixel(x, y, dst_c);
+ }
+ }
+ }
+}
/*
Apply mask to destination
@@ -2057,7 +2136,7 @@ video::ITexture *TextureSource::getShaderFlagsTexture(bool normalmap_present)
{
std::string tname = "__shaderFlagsTexture";
tname += normalmap_present ? "1" : "0";
-
+
if (isKnownSourceImage(tname)) {
return getTexture(tname);
} else {
diff --git a/src/client/tile.h b/src/client/tile.h
index 7796e801d..b75916841 100644
--- a/src/client/tile.h
+++ b/src/client/tile.h
@@ -21,7 +21,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#define TILE_HEADER
#include "irrlichttypes.h"
-#include "irr_v2d.h"
#include "irr_v3d.h"
#include <ITexture.h>
#include <IrrlichtDevice.h>
diff --git a/src/clientiface.cpp b/src/clientiface.cpp
index 3330e0af9..a3a17d435 100644
--- a/src/clientiface.cpp
+++ b/src/clientiface.cpp
@@ -38,10 +38,12 @@ const char *ClientInterface::statenames[] = {
"Disconnecting",
"Denied",
"Created",
- "InitSent",
+ "AwaitingInit2",
+ "HelloSent",
"InitDone",
"DefinitionsSent",
- "Active"
+ "Active",
+ "SudoMode",
};
@@ -65,7 +67,7 @@ void RemoteClient::GetNextBlocks (
float dtime,
std::vector<PrioritySortedBlockTransfer> &dest)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
// Increment timers
@@ -368,17 +370,21 @@ queue_full_break:
void RemoteClient::GotBlock(v3s16 p)
{
- if(m_blocks_sending.find(p) != m_blocks_sending.end())
- m_blocks_sending.erase(p);
- else
- {
- m_excess_gotblocks++;
+ 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++;
+
+ m_blocks_sent.insert(p);
}
- m_blocks_sent.insert(p);
}
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.0;
else
@@ -389,22 +395,26 @@ 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);
}
void RemoteClient::SetBlocksNotSent(std::map<v3s16, MapBlock*> &blocks)
{
m_nearest_unsent_d = 0;
+ m_nothing_to_send_pause_timer = 0;
for(std::map<v3s16, MapBlock*>::iterator
i = blocks.begin();
i != blocks.end(); ++i)
{
v3s16 p = i->first;
+ m_blocks_modified.insert(p);
if(m_blocks_sending.find(p) != m_blocks_sending.end())
m_blocks_sending.erase(p);
@@ -593,7 +603,7 @@ ClientInterface::~ClientInterface()
Delete clients
*/
{
- JMutexAutoLock clientslock(m_clients_mutex);
+ MutexAutoLock clientslock(m_clients_mutex);
for(std::map<u16, RemoteClient*>::iterator
i = m_clients.begin();
@@ -609,7 +619,7 @@ ClientInterface::~ClientInterface()
std::vector<u16> ClientInterface::getClientIDs(ClientState min_state)
{
std::vector<u16> reply;
- JMutexAutoLock clientslock(m_clients_mutex);
+ MutexAutoLock clientslock(m_clients_mutex);
for(std::map<u16, RemoteClient*>::iterator
i = m_clients.begin();
@@ -660,7 +670,7 @@ void ClientInterface::UpdatePlayerList()
infostream << "* " << player->getName() << "\t";
{
- JMutexAutoLock clientslock(m_clients_mutex);
+ MutexAutoLock clientslock(m_clients_mutex);
RemoteClient* client = lockedGetClientNoEx(*i);
if(client != NULL)
client->PrintInfo(infostream);
@@ -680,7 +690,7 @@ void ClientInterface::send(u16 peer_id, u8 channelnum,
void ClientInterface::sendToAll(u16 channelnum,
NetworkPacket* pkt, bool reliable)
{
- JMutexAutoLock clientslock(m_clients_mutex);
+ MutexAutoLock clientslock(m_clients_mutex);
for(std::map<u16, RemoteClient*>::iterator
i = m_clients.begin();
i != m_clients.end(); ++i) {
@@ -694,7 +704,7 @@ void ClientInterface::sendToAll(u16 channelnum,
RemoteClient* ClientInterface::getClientNoEx(u16 peer_id, ClientState state_min)
{
- JMutexAutoLock clientslock(m_clients_mutex);
+ MutexAutoLock clientslock(m_clients_mutex);
std::map<u16, RemoteClient*>::iterator n;
n = m_clients.find(peer_id);
// The client may not exist; clients are immediately removed if their
@@ -725,7 +735,7 @@ RemoteClient* ClientInterface::lockedGetClientNoEx(u16 peer_id, ClientState stat
ClientState ClientInterface::getClientState(u16 peer_id)
{
- JMutexAutoLock clientslock(m_clients_mutex);
+ MutexAutoLock clientslock(m_clients_mutex);
std::map<u16, RemoteClient*>::iterator n;
n = m_clients.find(peer_id);
// The client may not exist; clients are immediately removed if their
@@ -738,7 +748,7 @@ ClientState ClientInterface::getClientState(u16 peer_id)
void ClientInterface::setPlayerName(u16 peer_id,std::string name)
{
- JMutexAutoLock clientslock(m_clients_mutex);
+ MutexAutoLock clientslock(m_clients_mutex);
std::map<u16, RemoteClient*>::iterator n;
n = m_clients.find(peer_id);
// The client may not exist; clients are immediately removed if their
@@ -749,7 +759,7 @@ void ClientInterface::setPlayerName(u16 peer_id,std::string name)
void ClientInterface::DeleteClient(u16 peer_id)
{
- JMutexAutoLock conlock(m_clients_mutex);
+ MutexAutoLock conlock(m_clients_mutex);
// Error check
std::map<u16, RemoteClient*>::iterator n;
@@ -784,7 +794,7 @@ void ClientInterface::DeleteClient(u16 peer_id)
void ClientInterface::CreateClient(u16 peer_id)
{
- JMutexAutoLock conlock(m_clients_mutex);
+ MutexAutoLock conlock(m_clients_mutex);
// Error check
std::map<u16, RemoteClient*>::iterator n;
@@ -801,7 +811,7 @@ void ClientInterface::CreateClient(u16 peer_id)
void ClientInterface::event(u16 peer_id, ClientStateEvent event)
{
{
- JMutexAutoLock clientlock(m_clients_mutex);
+ MutexAutoLock clientlock(m_clients_mutex);
// Error check
std::map<u16, RemoteClient*>::iterator n;
@@ -823,7 +833,7 @@ void ClientInterface::event(u16 peer_id, ClientStateEvent event)
u16 ClientInterface::getProtocolVersion(u16 peer_id)
{
- JMutexAutoLock conlock(m_clients_mutex);
+ MutexAutoLock conlock(m_clients_mutex);
// Error check
std::map<u16, RemoteClient*>::iterator n;
@@ -838,7 +848,7 @@ u16 ClientInterface::getProtocolVersion(u16 peer_id)
void ClientInterface::setClientVersion(u16 peer_id, u8 major, u8 minor, u8 patch, std::string full)
{
- JMutexAutoLock conlock(m_clients_mutex);
+ MutexAutoLock conlock(m_clients_mutex);
// Error check
std::map<u16, RemoteClient*>::iterator n;
diff --git a/src/clientiface.h b/src/clientiface.h
index f6c4294e2..c09942909 100644
--- a/src/clientiface.h
+++ b/src/clientiface.h
@@ -23,7 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "constants.h"
#include "serialization.h" // for SER_FMT_VER_INVALID
-#include "jthread/jmutex.h"
+#include "threading/mutex.h"
#include "network/networkpacket.h"
#include <list>
@@ -167,6 +167,9 @@ namespace con {
#define CI_ARRAYSIZE(a) (sizeof(a) / sizeof((a)[0]))
+// Also make sure to update the ClientInterface::statenames
+// array when modifying these enums
+
enum ClientState
{
CS_Invalid,
@@ -319,7 +322,6 @@ public:
/*
List of active objects that the client knows of.
- Value is dummy.
*/
std::set<u16> m_known_objects;
@@ -374,7 +376,7 @@ private:
- A block is cleared from here when client says it has
deleted it from it's memory
- Key is position, value is dummy.
+ List of block positions.
No MapBlock* is stored here because the blocks can get deleted.
*/
std::set<v3s16> m_blocks_sent;
@@ -393,6 +395,16 @@ 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.
+
+ List of block positions.
+ */
+ std::set<v3s16> m_blocks_modified;
+
+ /*
Count of excess GotBlocks().
There is an excess amount because the client sometimes
gets a block so late that the server sends it again,
@@ -487,10 +499,8 @@ public:
protected:
//TODO find way to avoid this functions
- void Lock()
- { m_clients_mutex.Lock(); }
- void Unlock()
- { m_clients_mutex.Unlock(); }
+ void lock() { m_clients_mutex.lock(); }
+ void unlock() { m_clients_mutex.unlock(); }
std::map<u16, RemoteClient*>& getClientList()
{ return m_clients; }
@@ -501,14 +511,14 @@ private:
// Connection
con::Connection* m_con;
- JMutex m_clients_mutex;
+ Mutex m_clients_mutex;
// Connected clients (behind the con mutex)
std::map<u16, RemoteClient*> m_clients;
std::vector<std::string> m_clients_names; //for announcing masterserver
// Environment
ServerEnvironment *m_env;
- JMutex m_env_mutex;
+ Mutex m_env_mutex;
float m_print_info_timer;
diff --git a/src/clientmap.cpp b/src/clientmap.cpp
index 288a12135..a0a780250 100644
--- a/src/clientmap.cpp
+++ b/src/clientmap.cpp
@@ -50,7 +50,7 @@ ClientMap::ClientMap(
m_camera_direction(0,0,1),
m_camera_fov(M_PI)
{
- m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
+ m_box = aabb3f(-BS*1000000,-BS*1000000,-BS*1000000,
BS*1000000,BS*1000000,BS*1000000);
/* TODO: Add a callback function so these can be updated when a setting
@@ -70,7 +70,7 @@ ClientMap::ClientMap(
ClientMap::~ClientMap()
{
- /*JMutexAutoLock lock(mesh_mutex);
+ /*MutexAutoLock lock(mesh_mutex);
if(mesh != NULL)
{
@@ -81,7 +81,7 @@ ClientMap::~ClientMap()
MapSector * ClientMap::emergeSector(v2s16 p2d)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
// Check that it doesn't exist already
try{
return getSectorNoGenerate(p2d);
@@ -94,7 +94,7 @@ MapSector * ClientMap::emergeSector(v2s16 p2d)
ClientMapSector *sector = new ClientMapSector(this, p2d, m_gamedef);
{
- //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
+ //MutexAutoLock lock(m_sector_mutex); // Bulk comment-out
m_sectors[p2d] = sector;
}
@@ -141,6 +141,33 @@ static bool isOccluded(Map *map, v3s16 p0, v3s16 p1, float step, float stepfac,
return false;
}
+void ClientMap::getBlocksInViewRange(v3s16 cam_pos_nodes,
+ v3s16 *p_blocks_min, v3s16 *p_blocks_max)
+{
+ v3s16 box_nodes_d = m_control.wanted_range * v3s16(1, 1, 1);
+ // Define p_nodes_min/max as v3s32 because 'cam_pos_nodes -/+ box_nodes_d'
+ // can exceed the range of v3s16 when a large view range is used near the
+ // world edges.
+ v3s32 p_nodes_min(
+ cam_pos_nodes.X - box_nodes_d.X,
+ cam_pos_nodes.Y - box_nodes_d.Y,
+ cam_pos_nodes.Z - box_nodes_d.Z);
+ v3s32 p_nodes_max(
+ cam_pos_nodes.X + box_nodes_d.X,
+ cam_pos_nodes.Y + box_nodes_d.Y,
+ cam_pos_nodes.Z + box_nodes_d.Z);
+ // Take a fair amount as we will be dropping more out later
+ // Umm... these additions are a bit strange but they are needed.
+ *p_blocks_min = v3s16(
+ p_nodes_min.X / MAP_BLOCKSIZE - 3,
+ p_nodes_min.Y / MAP_BLOCKSIZE - 3,
+ p_nodes_min.Z / MAP_BLOCKSIZE - 3);
+ *p_blocks_max = v3s16(
+ p_nodes_max.X / MAP_BLOCKSIZE + 1,
+ p_nodes_max.Y / MAP_BLOCKSIZE + 1,
+ p_nodes_max.Z / MAP_BLOCKSIZE + 1);
+}
+
void ClientMap::updateDrawList(video::IVideoDriver* driver)
{
ScopeProfiler sp(g_profiler, "CM::updateDrawList()", SPT_AVG);
@@ -148,21 +175,16 @@ void ClientMap::updateDrawList(video::IVideoDriver* driver)
INodeDefManager *nodemgr = m_gamedef->ndef();
- for(std::map<v3s16, MapBlock*>::iterator
- i = m_drawlist.begin();
- i != m_drawlist.end(); ++i)
- {
+ for (std::map<v3s16, MapBlock*>::iterator i = m_drawlist.begin();
+ i != m_drawlist.end(); ++i) {
MapBlock *block = i->second;
block->refDrop();
}
m_drawlist.clear();
- m_camera_mutex.Lock();
v3f camera_position = m_camera_position;
v3f camera_direction = m_camera_direction;
f32 camera_fov = m_camera_fov;
- //v3s16 camera_offset = m_camera_offset;
- m_camera_mutex.Unlock();
// Use a higher fov to accomodate faster camera movements.
// Blocks are cropped better when they are drawn.
@@ -170,19 +192,9 @@ void ClientMap::updateDrawList(video::IVideoDriver* driver)
camera_fov *= 1.2;
v3s16 cam_pos_nodes = floatToInt(camera_position, BS);
- v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
- v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
- v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
- // Take a fair amount as we will be dropping more out later
- // Umm... these additions are a bit strange but they are needed.
- v3s16 p_blocks_min(
- p_nodes_min.X / MAP_BLOCKSIZE - 3,
- p_nodes_min.Y / MAP_BLOCKSIZE - 3,
- p_nodes_min.Z / MAP_BLOCKSIZE - 3);
- v3s16 p_blocks_max(
- p_nodes_max.X / MAP_BLOCKSIZE + 1,
- p_nodes_max.Y / MAP_BLOCKSIZE + 1,
- p_nodes_max.Z / MAP_BLOCKSIZE + 1);
+ v3s16 p_blocks_min;
+ v3s16 p_blocks_max;
+ getBlocksInViewRange(cam_pos_nodes, &p_blocks_min, &p_blocks_max);
// Number of blocks in rendering range
u32 blocks_in_range = 0;
@@ -202,19 +214,14 @@ void ClientMap::updateDrawList(video::IVideoDriver* driver)
// Distance to farthest drawn block
float farthest_drawn = 0;
- for(std::map<v2s16, MapSector*>::iterator
- si = m_sectors.begin();
- si != m_sectors.end(); ++si)
- {
+ for (std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
+ si != m_sectors.end(); ++si) {
MapSector *sector = si->second;
v2s16 sp = sector->getPos();
- if(m_control.range_all == false)
- {
- 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)
+ if (m_control.range_all == false) {
+ 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)
continue;
}
@@ -227,9 +234,8 @@ void ClientMap::updateDrawList(video::IVideoDriver* driver)
u32 sector_blocks_drawn = 0;
- for(MapBlockVect::iterator i = sectorblocks.begin();
- i != sectorblocks.end(); i++)
- {
+ for (MapBlockVect::iterator i = sectorblocks.begin();
+ i != sectorblocks.end(); ++i) {
MapBlock *block = *i;
/*
@@ -241,16 +247,13 @@ void ClientMap::updateDrawList(video::IVideoDriver* driver)
block->mesh->updateCameraOffset(m_camera_offset);
float range = 100000 * BS;
- if(m_control.range_all == false)
+ if (m_control.range_all == false)
range = m_control.wanted_range * BS;
float d = 0.0;
- if(isBlockInSight(block->getPos(), camera_position,
- camera_direction, camera_fov,
- range, &d) == false)
- {
+ if (!isBlockInSight(block->getPos(), camera_position,
+ camera_direction, camera_fov, range, &d))
continue;
- }
// This is ugly (spherical distance limit?)
/*if(m_control.range_all == false &&
@@ -263,9 +266,9 @@ void ClientMap::updateDrawList(video::IVideoDriver* driver)
Ignore if mesh doesn't exist
*/
{
- //JMutexAutoLock lock(block->mesh_mutex);
+ //MutexAutoLock lock(block->mesh_mutex);
- if(block->mesh == NULL){
+ if (block->mesh == NULL) {
blocks_in_range_without_mesh++;
continue;
}
@@ -278,44 +281,41 @@ void ClientMap::updateDrawList(video::IVideoDriver* driver)
// No occlusion culling when free_move is on and camera is
// inside ground
bool occlusion_culling_enabled = true;
- if(g_settings->getBool("free_move")){
+ if (g_settings->getBool("free_move")) {
MapNode n = getNodeNoEx(cam_pos_nodes);
- if(n.getContent() == CONTENT_IGNORE ||
+ if (n.getContent() == CONTENT_IGNORE ||
nodemgr->get(n).solidness == 2)
occlusion_culling_enabled = false;
}
v3s16 cpn = block->getPos() * MAP_BLOCKSIZE;
- cpn += v3s16(MAP_BLOCKSIZE/2, MAP_BLOCKSIZE/2, MAP_BLOCKSIZE/2);
- float step = BS*1;
+ cpn += v3s16(MAP_BLOCKSIZE / 2, MAP_BLOCKSIZE / 2, MAP_BLOCKSIZE / 2);
+ float step = BS * 1;
float stepfac = 1.1;
- float startoff = BS*1;
- float endoff = -BS*MAP_BLOCKSIZE*1.42*1.42;
- v3s16 spn = cam_pos_nodes + v3s16(0,0,0);
- s16 bs2 = MAP_BLOCKSIZE/2 + 1;
+ float startoff = BS * 1;
+ float endoff = -BS*MAP_BLOCKSIZE * 1.42 * 1.42;
+ v3s16 spn = cam_pos_nodes + v3s16(0, 0, 0);
+ s16 bs2 = MAP_BLOCKSIZE / 2 + 1;
u32 needed_count = 1;
- if(
- occlusion_culling_enabled &&
- isOccluded(this, spn, cpn + v3s16(0,0,0),
- step, stepfac, startoff, endoff, needed_count, nodemgr) &&
- isOccluded(this, spn, cpn + v3s16(bs2,bs2,bs2),
- step, stepfac, startoff, endoff, needed_count, nodemgr) &&
- isOccluded(this, spn, cpn + v3s16(bs2,bs2,-bs2),
- step, stepfac, startoff, endoff, needed_count, nodemgr) &&
- isOccluded(this, spn, cpn + v3s16(bs2,-bs2,bs2),
- step, stepfac, startoff, endoff, needed_count, nodemgr) &&
- isOccluded(this, spn, cpn + v3s16(bs2,-bs2,-bs2),
- step, stepfac, startoff, endoff, needed_count, nodemgr) &&
- isOccluded(this, spn, cpn + v3s16(-bs2,bs2,bs2),
- step, stepfac, startoff, endoff, needed_count, nodemgr) &&
- isOccluded(this, spn, cpn + v3s16(-bs2,bs2,-bs2),
- step, stepfac, startoff, endoff, needed_count, nodemgr) &&
- isOccluded(this, spn, cpn + v3s16(-bs2,-bs2,bs2),
- step, stepfac, startoff, endoff, needed_count, nodemgr) &&
- isOccluded(this, spn, cpn + v3s16(-bs2,-bs2,-bs2),
- step, stepfac, startoff, endoff, needed_count, nodemgr)
- )
- {
+ if (occlusion_culling_enabled &&
+ isOccluded(this, spn, cpn + v3s16(0, 0, 0),
+ step, stepfac, startoff, endoff, needed_count, nodemgr) &&
+ isOccluded(this, spn, cpn + v3s16(bs2,bs2,bs2),
+ step, stepfac, startoff, endoff, needed_count, nodemgr) &&
+ isOccluded(this, spn, cpn + v3s16(bs2,bs2,-bs2),
+ step, stepfac, startoff, endoff, needed_count, nodemgr) &&
+ isOccluded(this, spn, cpn + v3s16(bs2,-bs2,bs2),
+ step, stepfac, startoff, endoff, needed_count, nodemgr) &&
+ isOccluded(this, spn, cpn + v3s16(bs2,-bs2,-bs2),
+ step, stepfac, startoff, endoff, needed_count, nodemgr) &&
+ isOccluded(this, spn, cpn + v3s16(-bs2,bs2,bs2),
+ step, stepfac, startoff, endoff, needed_count, nodemgr) &&
+ isOccluded(this, spn, cpn + v3s16(-bs2,bs2,-bs2),
+ step, stepfac, startoff, endoff, needed_count, nodemgr) &&
+ isOccluded(this, spn, cpn + v3s16(-bs2,-bs2,bs2),
+ step, stepfac, startoff, endoff, needed_count, nodemgr) &&
+ isOccluded(this, spn, cpn + v3s16(-bs2,-bs2,-bs2),
+ step, stepfac, startoff, endoff, needed_count, nodemgr)) {
blocks_occlusion_culled++;
continue;
}
@@ -325,9 +325,9 @@ void ClientMap::updateDrawList(video::IVideoDriver* driver)
// Limit block count in case of a sudden increase
blocks_would_have_drawn++;
- if(blocks_drawn >= m_control.wanted_max_blocks
- && m_control.range_all == false
- && d > m_control.wanted_min_range * BS)
+ if (blocks_drawn >= m_control.wanted_max_blocks &&
+ !m_control.range_all &&
+ d > m_control.wanted_range * BS)
continue;
// Add to set
@@ -336,12 +336,12 @@ void ClientMap::updateDrawList(video::IVideoDriver* driver)
sector_blocks_drawn++;
blocks_drawn++;
- if(d/BS > farthest_drawn)
- farthest_drawn = d/BS;
+ if (d / BS > farthest_drawn)
+ farthest_drawn = d / BS;
} // foreach sectorblocks
- if(sector_blocks_drawn != 0)
+ if (sector_blocks_drawn != 0)
m_last_drawn_sectors.insert(sp);
}
@@ -351,9 +351,9 @@ void ClientMap::updateDrawList(video::IVideoDriver* driver)
g_profiler->avg("CM: blocks in range", blocks_in_range);
g_profiler->avg("CM: blocks occlusion culled", blocks_occlusion_culled);
- if(blocks_in_range != 0)
+ if (blocks_in_range != 0)
g_profiler->avg("CM: blocks in range without mesh (frac)",
- (float)blocks_in_range_without_mesh/blocks_in_range);
+ (float)blocks_in_range_without_mesh / blocks_in_range);
g_profiler->avg("CM: blocks drawn", blocks_drawn);
g_profiler->avg("CM: farthest drawn", farthest_drawn);
g_profiler->avg("CM: wanted max blocks", m_control.wanted_max_blocks);
@@ -400,12 +400,12 @@ struct MeshBufListList
void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
std::string prefix;
- if(pass == scene::ESNRP_SOLID)
+ if (pass == scene::ESNRP_SOLID)
prefix = "CM: solid: ";
else
prefix = "CM: transparent: ";
@@ -413,10 +413,8 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
/*
This is called two times per frame, reset on the non-transparent one
*/
- if(pass == scene::ESNRP_SOLID)
- {
+ if (pass == scene::ESNRP_SOLID)
m_last_drawn_sectors.clear();
- }
/*
Get time for measuring timeout.
@@ -433,33 +431,18 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
int crack = m_client->getCrackLevel();
u32 daynight_ratio = m_client->getEnv().getDayNightRatio();
- m_camera_mutex.Lock();
v3f camera_position = m_camera_position;
v3f camera_direction = m_camera_direction;
f32 camera_fov = m_camera_fov;
- m_camera_mutex.Unlock();
/*
Get all blocks and draw all visible ones
*/
v3s16 cam_pos_nodes = floatToInt(camera_position, BS);
-
- v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
-
- v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
- v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
-
- // Take a fair amount as we will be dropping more out later
- // Umm... these additions are a bit strange but they are needed.
- v3s16 p_blocks_min(
- p_nodes_min.X / MAP_BLOCKSIZE - 3,
- p_nodes_min.Y / MAP_BLOCKSIZE - 3,
- p_nodes_min.Z / MAP_BLOCKSIZE - 3);
- v3s16 p_blocks_max(
- p_nodes_max.X / MAP_BLOCKSIZE + 1,
- p_nodes_max.Y / MAP_BLOCKSIZE + 1,
- p_nodes_max.Z / MAP_BLOCKSIZE + 1);
+ v3s16 p_blocks_min;
+ v3s16 p_blocks_max;
+ getBlocksInViewRange(cam_pos_nodes, &p_blocks_min, &p_blocks_max);
u32 vertex_count = 0;
u32 meshbuffer_count = 0;
@@ -480,52 +463,40 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
*/
{
- ScopeProfiler sp(g_profiler, prefix+"drawing blocks", SPT_AVG);
+ ScopeProfiler sp(g_profiler, prefix + "drawing blocks", SPT_AVG);
MeshBufListList drawbufs;
- for(std::map<v3s16, MapBlock*>::iterator
- i = m_drawlist.begin();
- i != m_drawlist.end(); ++i)
- {
+ for (std::map<v3s16, MapBlock*>::iterator i = m_drawlist.begin();
+ i != m_drawlist.end(); ++i) {
MapBlock *block = i->second;
// If the mesh of the block happened to get deleted, ignore it
- if(block->mesh == NULL)
+ if (block->mesh == NULL)
continue;
float d = 0.0;
- if(isBlockInSight(block->getPos(), camera_position,
- camera_direction, camera_fov,
- 100000*BS, &d) == false)
- {
+ if (!isBlockInSight(block->getPos(), camera_position,
+ camera_direction, camera_fov, 100000 * BS, &d))
continue;
- }
// Mesh animation
{
- //JMutexAutoLock lock(block->mesh_mutex);
+ //MutexAutoLock lock(block->mesh_mutex);
MapBlockMesh *mapBlockMesh = block->mesh;
assert(mapBlockMesh);
// Pretty random but this should work somewhat nicely
- bool faraway = d >= BS*50;
+ bool faraway = d >= BS * 50;
//bool faraway = d >= m_control.wanted_range * BS;
- if(mapBlockMesh->isAnimationForced() ||
- !faraway ||
- mesh_animate_count_far < (m_control.range_all ? 200 : 50))
- {
- bool animated = mapBlockMesh->animate(
- faraway,
- animation_time,
- crack,
- daynight_ratio);
- if(animated)
+ if (mapBlockMesh->isAnimationForced() || !faraway ||
+ mesh_animate_count_far < (m_control.range_all ? 200 : 50)) {
+ bool animated = mapBlockMesh->animate(faraway, animation_time,
+ crack, daynight_ratio);
+ if (animated)
mesh_animate_count++;
- if(animated && faraway)
+ if (animated && faraway)
mesh_animate_count_far++;
- }
- else
- {
+ } else {
mapBlockMesh->decreaseAnimationForceTimer();
}
}
@@ -534,7 +505,7 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
Get the meshbuffers of the block
*/
{
- //JMutexAutoLock lock(block->mesh_mutex);
+ //MutexAutoLock lock(block->mesh_mutex);
MapBlockMesh *mapBlockMesh = block->mesh;
assert(mapBlockMesh);
@@ -543,7 +514,7 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
assert(mesh);
u32 c = mesh->getMeshBufferCount();
- for(u32 i=0; i<c; i++)
+ for (u32 i = 0; i < c; i++)
{
scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
@@ -555,11 +526,10 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
video::IMaterialRenderer* rnd =
driver->getMaterialRenderer(material.MaterialType);
bool transparent = (rnd && rnd->isTransparent());
- if(transparent == is_transparent_pass)
- {
- if(buf->getVertexCount() == 0)
- errorstream<<"Block ["<<analyze_block(block)
- <<"] contains an empty meshbuf"<<std::endl;
+ if (transparent == is_transparent_pass) {
+ if (buf->getVertexCount() == 0)
+ errorstream << "Block [" << analyze_block(block)
+ << "] contains an empty meshbuf" << std::endl;
drawbufs.add(buf);
}
}
@@ -569,13 +539,13 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
std::vector<MeshBufList> &lists = drawbufs.lists;
int timecheck_counter = 0;
- for(std::vector<MeshBufList>::iterator i = lists.begin();
+ for (std::vector<MeshBufList>::iterator i = lists.begin();
i != lists.end(); ++i) {
timecheck_counter++;
- if(timecheck_counter > 50) {
+ if (timecheck_counter > 50) {
timecheck_counter = 0;
int time2 = time(0);
- if(time2 > time1 + 4) {
+ if (time2 > time1 + 4) {
infostream << "ClientMap::renderMap(): "
"Rendering takes ages, returning."
<< std::endl;
@@ -587,7 +557,7 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
driver->setMaterial(list.m);
- for(std::vector<scene::IMeshBuffer*>::iterator j = list.bufs.begin();
+ for (std::vector<scene::IMeshBuffer*>::iterator j = list.bufs.begin();
j != list.bufs.end(); ++j) {
scene::IMeshBuffer *buf = *j;
driver->drawMeshBuffer(buf);
@@ -599,18 +569,18 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
} // ScopeProfiler
// Log only on solid pass because values are the same
- if(pass == scene::ESNRP_SOLID){
+ if (pass == scene::ESNRP_SOLID) {
g_profiler->avg("CM: animated meshes", mesh_animate_count);
g_profiler->avg("CM: animated meshes (far)", mesh_animate_count_far);
}
- g_profiler->avg(prefix+"vertices drawn", vertex_count);
- if(blocks_had_pass_meshbuf != 0)
- g_profiler->avg(prefix+"meshbuffers per block",
- (float)meshbuffer_count / (float)blocks_had_pass_meshbuf);
- if(blocks_drawn != 0)
- g_profiler->avg(prefix+"empty blocks (frac)",
- (float)blocks_without_stuff / blocks_drawn);
+ g_profiler->avg(prefix + "vertices drawn", vertex_count);
+ if (blocks_had_pass_meshbuf != 0)
+ g_profiler->avg(prefix + "meshbuffers per block",
+ (float)meshbuffer_count / (float)blocks_had_pass_meshbuf);
+ if (blocks_drawn != 0)
+ g_profiler->avg(prefix + "empty blocks (frac)",
+ (float)blocks_without_stuff / blocks_drawn);
/*infostream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
<<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
@@ -799,11 +769,7 @@ void ClientMap::renderPostFx(CameraMode cam_mode)
// Sadly ISceneManager has no "post effects" render pass, in that case we
// could just register for that and handle it in renderMap().
- m_camera_mutex.Lock();
- v3f camera_position = m_camera_position;
- m_camera_mutex.Unlock();
-
- MapNode n = getNodeNoEx(floatToInt(camera_position, BS));
+ MapNode n = getNodeNoEx(floatToInt(m_camera_position, BS));
// - If the player is in a solid node, make everything black.
// - If the player is in liquid, draw a semi-transparent overlay.
diff --git a/src/clientmap.h b/src/clientmap.h
index 492e23fa5..396e68f64 100644
--- a/src/clientmap.h
+++ b/src/clientmap.h
@@ -30,9 +30,8 @@ struct MapDrawControl
{
MapDrawControl():
range_all(false),
- wanted_range(50),
+ wanted_range(0),
wanted_max_blocks(0),
- wanted_min_range(0),
blocks_drawn(0),
blocks_would_have_drawn(0),
farthest_drawn(0)
@@ -44,8 +43,6 @@ struct MapDrawControl
float wanted_range;
// Maximum number of blocks to draw
u32 wanted_max_blocks;
- // Blocks in this range are drawn regardless of number of blocks drawn
- float wanted_min_range;
// Number of blocks rendered is written here by the renderer
u32 blocks_drawn;
// Number of blocks that would have been drawn in wanted_range
@@ -89,7 +86,6 @@ public:
void updateCamera(v3f pos, v3f dir, f32 fov, v3s16 offset)
{
- JMutexAutoLock lock(m_camera_mutex);
m_camera_position = pos;
m_camera_direction = dir;
m_camera_fov = fov;
@@ -116,11 +112,13 @@ public:
renderMap(driver, SceneManager->getSceneNodeRenderPass());
}
- virtual const core::aabbox3d<f32>& getBoundingBox() const
+ virtual const aabb3f &getBoundingBox() const
{
return m_box;
}
+ void getBlocksInViewRange(v3s16 cam_pos_nodes,
+ v3s16 *p_blocks_min, v3s16 *p_blocks_max);
void updateDrawList(video::IVideoDriver* driver);
void renderMap(video::IVideoDriver* driver, s32 pass);
@@ -141,7 +139,7 @@ public:
private:
Client *m_client;
- core::aabbox3d<f32> m_box;
+ aabb3f m_box;
MapDrawControl &m_control;
@@ -149,7 +147,6 @@ private:
v3f m_camera_direction;
f32 m_camera_fov;
v3s16 m_camera_offset;
- JMutex m_camera_mutex;
std::map<v3s16, MapBlock*> m_drawlist;
diff --git a/src/clientmedia.cpp b/src/clientmedia.cpp
index ea11ad239..bca3f67c2 100644
--- a/src/clientmedia.cpp
+++ b/src/clientmedia.cpp
@@ -34,7 +34,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
static std::string getMediaCacheDir()
{
- return porting::path_user + DIR_DELIM + "cache" + DIR_DELIM + "media";
+ return porting::path_cache + DIR_DELIM + "media";
}
/*
diff --git a/src/clientobject.cpp b/src/clientobject.cpp
index ae1be092f..a11757ea6 100644
--- a/src/clientobject.cpp
+++ b/src/clientobject.cpp
@@ -47,7 +47,7 @@ ClientActiveObject* ClientActiveObject::create(ActiveObjectType type,
n = m_types.find(type);
if(n == m_types.end()) {
// If factory is not found, just return.
- dstream<<"WARNING: ClientActiveObject: No factory for type="
+ warningstream<<"ClientActiveObject: No factory for type="
<<(int)type<<std::endl;
return NULL;
}
diff --git a/src/clientobject.h b/src/clientobject.h
index be24e1388..3cc7c2391 100644
--- a/src/clientobject.h
+++ b/src/clientobject.h
@@ -56,7 +56,7 @@ public:
virtual void updateLight(u8 light_at_pos){}
virtual void updateLightNoCheck(u8 light_at_pos){}
virtual v3s16 getLightPosition(){return v3s16(0,0,0);}
- virtual core::aabbox3d<f32>* getSelectionBox(){return NULL;}
+ virtual aabb3f *getSelectionBox() { return NULL; }
virtual bool getCollisionBox(aabb3f *toset){return false;}
virtual bool collideWithObjects(){return false;}
virtual v3f getPosition(){return v3f(0,0,0);}
diff --git a/src/clouds.cpp b/src/clouds.cpp
index 29210e2b4..82b63b6b3 100644
--- a/src/clouds.cpp
+++ b/src/clouds.cpp
@@ -62,7 +62,7 @@ Clouds::Clouds(
g_settings->registerChangedCallback("enable_3d_clouds",
&cloud_3d_setting_changed, this);
- m_box = core::aabbox3d<f32>(-BS*1000000,m_cloud_y-BS,-BS*1000000,
+ m_box = aabb3f(-BS*1000000,m_cloud_y-BS,-BS*1000000,
BS*1000000,m_cloud_y+BS,BS*1000000);
}
diff --git a/src/clouds.h b/src/clouds.h
index 195f48de0..9c6b41786 100644
--- a/src/clouds.h
+++ b/src/clouds.h
@@ -53,7 +53,7 @@ public:
virtual void render();
- virtual const core::aabbox3d<f32>& getBoundingBox() const
+ virtual const aabb3f &getBoundingBox() const
{
return m_box;
}
@@ -79,7 +79,7 @@ public:
void updateCameraOffset(v3s16 camera_offset)
{
m_camera_offset = camera_offset;
- m_box = core::aabbox3d<f32>(-BS * 1000000, m_cloud_y - BS - BS * camera_offset.Y, -BS * 1000000,
+ m_box = aabb3f(-BS * 1000000, m_cloud_y - BS - BS * camera_offset.Y, -BS * 1000000,
BS * 1000000, m_cloud_y + BS - BS * camera_offset.Y, BS * 1000000);
}
@@ -87,7 +87,7 @@ public:
private:
video::SMaterial m_material;
- core::aabbox3d<f32> m_box;
+ aabb3f m_box;
s16 m_passed_cloud_y;
float m_cloud_y;
u16 m_cloud_radius_i;
diff --git a/src/cmake_config.h.in b/src/cmake_config.h.in
index 04f368594..018532d13 100644
--- a/src/cmake_config.h.in
+++ b/src/cmake_config.h.in
@@ -12,18 +12,26 @@
#define VERSION_STRING "@VERSION_STRING@"
#define PRODUCT_VERSION_STRING "@VERSION_MAJOR@.@VERSION_MINOR@"
#define STATIC_SHAREDIR "@SHAREDIR@"
+#define STATIC_LOCALEDIR "@LOCALEDIR@"
#define BUILD_TYPE "@CMAKE_BUILD_TYPE@"
#cmakedefine01 RUN_IN_PLACE
#cmakedefine01 USE_GETTEXT
#cmakedefine01 USE_CURL
#cmakedefine01 USE_SOUND
#cmakedefine01 USE_FREETYPE
+#cmakedefine01 USE_CURSES
#cmakedefine01 USE_LEVELDB
#cmakedefine01 USE_LUAJIT
#cmakedefine01 USE_SPATIAL
#cmakedefine01 USE_SYSTEM_GMP
#cmakedefine01 USE_REDIS
#cmakedefine01 HAVE_ENDIAN_H
+#cmakedefine01 CURSES_HAVE_CURSES_H
+#cmakedefine01 CURSES_HAVE_NCURSES_H
+#cmakedefine01 CURSES_HAVE_NCURSES_NCURSES_H
+#cmakedefine01 CURSES_HAVE_NCURSES_CURSES_H
+#cmakedefine01 CURSES_HAVE_NCURSESW_NCURSES_H
+#cmakedefine01 CURSES_HAVE_NCURSESW_CURSES_H
#endif
diff --git a/src/collision.cpp b/src/collision.cpp
index 6afc79ab7..74c0c25c3 100644
--- a/src/collision.cpp
+++ b/src/collision.cpp
@@ -40,7 +40,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
// The time after which the collision occurs is stored in dtime.
int axisAlignedCollision(
const aabb3f &staticbox, const aabb3f &movingbox,
- const v3f &speed, f32 d, f32 &dtime)
+ const v3f &speed, f32 d, f32 *dtime)
{
//TimeTaker tt("axisAlignedCollision");
@@ -59,13 +59,12 @@ int axisAlignedCollision(
if(speed.X > 0) // Check for collision with X- plane
{
- if(relbox.MaxEdge.X <= d)
- {
- dtime = - relbox.MaxEdge.X / speed.X;
- if((relbox.MinEdge.Y + speed.Y * dtime < ysize) &&
- (relbox.MaxEdge.Y + speed.Y * dtime > COLL_ZERO) &&
- (relbox.MinEdge.Z + speed.Z * dtime < zsize) &&
- (relbox.MaxEdge.Z + speed.Z * dtime > COLL_ZERO))
+ if (relbox.MaxEdge.X <= d) {
+ *dtime = -relbox.MaxEdge.X / speed.X;
+ if ((relbox.MinEdge.Y + speed.Y * (*dtime) < ysize) &&
+ (relbox.MaxEdge.Y + speed.Y * (*dtime) > COLL_ZERO) &&
+ (relbox.MinEdge.Z + speed.Z * (*dtime) < zsize) &&
+ (relbox.MaxEdge.Z + speed.Z * (*dtime) > COLL_ZERO))
return 0;
}
else if(relbox.MinEdge.X > xsize)
@@ -75,13 +74,12 @@ int axisAlignedCollision(
}
else if(speed.X < 0) // Check for collision with X+ plane
{
- if(relbox.MinEdge.X >= xsize - d)
- {
- dtime = (xsize - relbox.MinEdge.X) / speed.X;
- if((relbox.MinEdge.Y + speed.Y * dtime < ysize) &&
- (relbox.MaxEdge.Y + speed.Y * dtime > COLL_ZERO) &&
- (relbox.MinEdge.Z + speed.Z * dtime < zsize) &&
- (relbox.MaxEdge.Z + speed.Z * dtime > COLL_ZERO))
+ if (relbox.MinEdge.X >= xsize - d) {
+ *dtime = (xsize - relbox.MinEdge.X) / speed.X;
+ if ((relbox.MinEdge.Y + speed.Y * (*dtime) < ysize) &&
+ (relbox.MaxEdge.Y + speed.Y * (*dtime) > COLL_ZERO) &&
+ (relbox.MinEdge.Z + speed.Z * (*dtime) < zsize) &&
+ (relbox.MaxEdge.Z + speed.Z * (*dtime) > COLL_ZERO))
return 0;
}
else if(relbox.MaxEdge.X < 0)
@@ -94,13 +92,12 @@ int axisAlignedCollision(
if(speed.Y > 0) // Check for collision with Y- plane
{
- if(relbox.MaxEdge.Y <= d)
- {
- dtime = - relbox.MaxEdge.Y / speed.Y;
- if((relbox.MinEdge.X + speed.X * dtime < xsize) &&
- (relbox.MaxEdge.X + speed.X * dtime > COLL_ZERO) &&
- (relbox.MinEdge.Z + speed.Z * dtime < zsize) &&
- (relbox.MaxEdge.Z + speed.Z * dtime > COLL_ZERO))
+ if (relbox.MaxEdge.Y <= d) {
+ *dtime = -relbox.MaxEdge.Y / speed.Y;
+ if ((relbox.MinEdge.X + speed.X * (*dtime) < xsize) &&
+ (relbox.MaxEdge.X + speed.X * (*dtime) > COLL_ZERO) &&
+ (relbox.MinEdge.Z + speed.Z * (*dtime) < zsize) &&
+ (relbox.MaxEdge.Z + speed.Z * (*dtime) > COLL_ZERO))
return 1;
}
else if(relbox.MinEdge.Y > ysize)
@@ -110,13 +107,12 @@ int axisAlignedCollision(
}
else if(speed.Y < 0) // Check for collision with Y+ plane
{
- if(relbox.MinEdge.Y >= ysize - d)
- {
- dtime = (ysize - relbox.MinEdge.Y) / speed.Y;
- if((relbox.MinEdge.X + speed.X * dtime < xsize) &&
- (relbox.MaxEdge.X + speed.X * dtime > COLL_ZERO) &&
- (relbox.MinEdge.Z + speed.Z * dtime < zsize) &&
- (relbox.MaxEdge.Z + speed.Z * dtime > COLL_ZERO))
+ if (relbox.MinEdge.Y >= ysize - d) {
+ *dtime = (ysize - relbox.MinEdge.Y) / speed.Y;
+ if ((relbox.MinEdge.X + speed.X * (*dtime) < xsize) &&
+ (relbox.MaxEdge.X + speed.X * (*dtime) > COLL_ZERO) &&
+ (relbox.MinEdge.Z + speed.Z * (*dtime) < zsize) &&
+ (relbox.MaxEdge.Z + speed.Z * (*dtime) > COLL_ZERO))
return 1;
}
else if(relbox.MaxEdge.Y < 0)
@@ -129,13 +125,12 @@ int axisAlignedCollision(
if(speed.Z > 0) // Check for collision with Z- plane
{
- if(relbox.MaxEdge.Z <= d)
- {
- dtime = - relbox.MaxEdge.Z / speed.Z;
- if((relbox.MinEdge.X + speed.X * dtime < xsize) &&
- (relbox.MaxEdge.X + speed.X * dtime > COLL_ZERO) &&
- (relbox.MinEdge.Y + speed.Y * dtime < ysize) &&
- (relbox.MaxEdge.Y + speed.Y * dtime > COLL_ZERO))
+ if (relbox.MaxEdge.Z <= d) {
+ *dtime = -relbox.MaxEdge.Z / speed.Z;
+ if ((relbox.MinEdge.X + speed.X * (*dtime) < xsize) &&
+ (relbox.MaxEdge.X + speed.X * (*dtime) > COLL_ZERO) &&
+ (relbox.MinEdge.Y + speed.Y * (*dtime) < ysize) &&
+ (relbox.MaxEdge.Y + speed.Y * (*dtime) > COLL_ZERO))
return 2;
}
//else if(relbox.MinEdge.Z > zsize)
@@ -145,13 +140,12 @@ int axisAlignedCollision(
}
else if(speed.Z < 0) // Check for collision with Z+ plane
{
- if(relbox.MinEdge.Z >= zsize - d)
- {
- dtime = (zsize - relbox.MinEdge.Z) / speed.Z;
- if((relbox.MinEdge.X + speed.X * dtime < xsize) &&
- (relbox.MaxEdge.X + speed.X * dtime > COLL_ZERO) &&
- (relbox.MinEdge.Y + speed.Y * dtime < ysize) &&
- (relbox.MaxEdge.Y + speed.Y * dtime > COLL_ZERO))
+ if (relbox.MinEdge.Z >= zsize - d) {
+ *dtime = (zsize - relbox.MinEdge.Z) / speed.Z;
+ if ((relbox.MinEdge.X + speed.X * (*dtime) < xsize) &&
+ (relbox.MaxEdge.X + speed.X * (*dtime) > COLL_ZERO) &&
+ (relbox.MinEdge.Y + speed.Y * (*dtime) < ysize) &&
+ (relbox.MaxEdge.Y + speed.Y * (*dtime) > COLL_ZERO))
return 2;
}
//else if(relbox.MaxEdge.Z < 0)
@@ -176,7 +170,7 @@ bool wouldCollideWithCeiling(
for(std::vector<aabb3f>::const_iterator
i = staticboxes.begin();
- i != staticboxes.end(); i++)
+ i != staticboxes.end(); ++i)
{
const aabb3f& staticbox = *i;
if((movingbox.MaxEdge.Y - d <= staticbox.MinEdge.Y) &&
@@ -191,37 +185,51 @@ bool wouldCollideWithCeiling(
return false;
}
+static inline void getNeighborConnectingFace(v3s16 p, INodeDefManager *nodedef,
+ Map *map, MapNode n, int v, int *neighbors)
+{
+ MapNode n2 = map->getNodeNoEx(p);
+ if (nodedef->nodeboxConnects(n, n2, v))
+ *neighbors |= v;
+}
collisionMoveResult collisionMoveSimple(Environment *env, IGameDef *gamedef,
f32 pos_max_d, const aabb3f &box_0,
f32 stepheight, f32 dtime,
- v3f &pos_f, v3f &speed_f,
- v3f &accel_f,ActiveObject* self,
+ v3f *pos_f, v3f *speed_f,
+ v3f accel_f, ActiveObject *self,
bool collideWithObjects)
{
+ static bool time_notification_done = false;
Map *map = &env->getMap();
//TimeTaker tt("collisionMoveSimple");
- ScopeProfiler sp(g_profiler, "collisionMoveSimple avg", SPT_AVG);
+ ScopeProfiler sp(g_profiler, "collisionMoveSimple avg", SPT_AVG);
collisionMoveResult result;
/*
Calculate new velocity
*/
- if( dtime > 0.5 ) {
- infostream<<"collisionMoveSimple: WARNING: maximum step interval exceeded, lost movement details!"<<std::endl;
+ if (dtime > 0.5) {
+ if (!time_notification_done) {
+ time_notification_done = true;
+ infostream << "collisionMoveSimple: maximum step interval exceeded,"
+ " lost movement details!"<<std::endl;
+ }
dtime = 0.5;
+ } else {
+ time_notification_done = false;
}
- speed_f += accel_f * dtime;
+ *speed_f += accel_f * dtime;
// If there is no speed, there are no collisions
- if(speed_f.getLength() == 0)
+ if (speed_f->getLength() == 0)
return result;
// Limit speed for avoiding hangs
- speed_f.Y=rangelim(speed_f.Y,-5000,5000);
- speed_f.X=rangelim(speed_f.X,-5000,5000);
- speed_f.Z=rangelim(speed_f.Z,-5000,5000);
+ speed_f->Y = rangelim(speed_f->Y, -5000, 5000);
+ speed_f->X = rangelim(speed_f->X, -5000, 5000);
+ speed_f->Z = rangelim(speed_f->Z, -5000, 5000);
/*
Collect node boxes in movement range
@@ -234,10 +242,10 @@ collisionMoveResult collisionMoveSimple(Environment *env, IGameDef *gamedef,
std::vector<v3s16> node_positions;
{
//TimeTaker tt2("collisionMoveSimple collect boxes");
- ScopeProfiler sp(g_profiler, "collisionMoveSimple collect boxes avg", SPT_AVG);
+ ScopeProfiler sp(g_profiler, "collisionMoveSimple collect boxes avg", SPT_AVG);
- v3s16 oldpos_i = floatToInt(pos_f, BS);
- v3s16 newpos_i = floatToInt(pos_f + speed_f * dtime, BS);
+ v3s16 oldpos_i = floatToInt(*pos_f, BS);
+ v3s16 newpos_i = floatToInt(*pos_f + *speed_f * dtime, BS);
s16 min_x = MYMIN(oldpos_i.X, newpos_i.X) + (box_0.MinEdge.X / BS) - 1;
s16 min_y = MYMIN(oldpos_i.Y, newpos_i.Y) + (box_0.MinEdge.Y / BS) - 1;
s16 min_z = MYMIN(oldpos_i.Z, newpos_i.Z) + (box_0.MinEdge.Z / BS) - 1;
@@ -245,6 +253,8 @@ collisionMoveResult collisionMoveSimple(Environment *env, IGameDef *gamedef,
s16 max_y = MYMAX(oldpos_i.Y, newpos_i.Y) + (box_0.MaxEdge.Y / BS) + 1;
s16 max_z = MYMAX(oldpos_i.Z, newpos_i.Z) + (box_0.MaxEdge.Z / BS) + 1;
+ bool any_position_valid = false;
+
for(s16 x = min_x; x <= max_x; x++)
for(s16 y = min_y; y <= max_y; y++)
for(s16 z = min_z; z <= max_z; z++)
@@ -257,15 +267,45 @@ collisionMoveResult collisionMoveSimple(Environment *env, IGameDef *gamedef,
if (is_position_valid) {
// Object collides into walkable nodes
- const ContentFeatures &f = gamedef->getNodeDefManager()->get(n);
+ any_position_valid = true;
+ INodeDefManager *nodedef = gamedef->getNodeDefManager();
+ const ContentFeatures &f = nodedef->get(n);
if(f.walkable == false)
continue;
int n_bouncy_value = itemgroup_get(f.groups, "bouncy");
- std::vector<aabb3f> nodeboxes = n.getCollisionBoxes(gamedef->ndef());
+ int neighbors = 0;
+ if (f.drawtype == NDT_NODEBOX && f.node_box.type == NODEBOX_CONNECTED) {
+ v3s16 p2 = p;
+
+ p2.Y++;
+ getNeighborConnectingFace(p2, nodedef, map, n, 1, &neighbors);
+
+ p2 = p;
+ p2.Y--;
+ getNeighborConnectingFace(p2, nodedef, map, n, 2, &neighbors);
+
+ p2 = p;
+ p2.Z--;
+ getNeighborConnectingFace(p2, nodedef, map, n, 4, &neighbors);
+
+ p2 = p;
+ p2.X--;
+ getNeighborConnectingFace(p2, nodedef, map, n, 8, &neighbors);
+
+ p2 = p;
+ p2.Z++;
+ getNeighborConnectingFace(p2, nodedef, map, n, 16, &neighbors);
+
+ p2 = p;
+ p2.X++;
+ getNeighborConnectingFace(p2, nodedef, map, n, 32, &neighbors);
+ }
+ std::vector<aabb3f> nodeboxes;
+ n.getCollisionBoxes(gamedef->ndef(), &nodeboxes, neighbors);
for(std::vector<aabb3f>::iterator
i = nodeboxes.begin();
- i != nodeboxes.end(); i++)
+ i != nodeboxes.end(); ++i)
{
aabb3f box = *i;
box.MinEdge += v3f(x, y, z)*BS;
@@ -289,6 +329,14 @@ collisionMoveResult collisionMoveSimple(Environment *env, IGameDef *gamedef,
is_object.push_back(false);
}
}
+
+ // Do not move if world has not loaded yet, since custom node boxes
+ // are not available for collision detection.
+ if (!any_position_valid) {
+ *speed_f = v3f(0, 0, 0);
+ return result;
+ }
+
} // tt2
if(collideWithObjects)
@@ -298,14 +346,13 @@ collisionMoveResult collisionMoveSimple(Environment *env, IGameDef *gamedef,
/* add object boxes to cboxes */
-
std::vector<ActiveObject*> objects;
#ifndef SERVER
ClientEnvironment *c_env = dynamic_cast<ClientEnvironment*>(env);
if (c_env != 0) {
- f32 distance = speed_f.getLength();
+ f32 distance = speed_f->getLength();
std::vector<DistanceSortedActiveObject> clientobjects;
- c_env->getActiveObjects(pos_f,distance * 1.5,clientobjects);
+ c_env->getActiveObjects(*pos_f, distance * 1.5, clientobjects);
for (size_t i=0; i < clientobjects.size(); i++) {
if ((self == 0) || (self != clientobjects[i].obj)) {
objects.push_back((ActiveObject*)clientobjects[i].obj);
@@ -317,10 +364,10 @@ collisionMoveResult collisionMoveSimple(Environment *env, IGameDef *gamedef,
{
ServerEnvironment *s_env = dynamic_cast<ServerEnvironment*>(env);
if (s_env != 0) {
- f32 distance = speed_f.getLength();
+ f32 distance = speed_f->getLength();
std::vector<u16> s_objects;
- s_env->getObjectsInsideRadius(s_objects, pos_f, distance * 1.5);
- for (std::vector<u16>::iterator iter = s_objects.begin(); iter != s_objects.end(); iter++) {
+ s_env->getObjectsInsideRadius(s_objects, *pos_f, distance * 1.5);
+ for (std::vector<u16>::iterator iter = s_objects.begin(); iter != s_objects.end(); ++iter) {
ServerActiveObject *current = s_env->getActiveObject(*iter);
if ((self == 0) || (self != current)) {
objects.push_back((ActiveObject*)current);
@@ -371,33 +418,29 @@ collisionMoveResult collisionMoveSimple(Environment *env, IGameDef *gamedef,
int loopcount = 0;
- while(dtime > BS*1e-10)
- {
+ while(dtime > BS * 1e-10) {
//TimeTaker tt3("collisionMoveSimple dtime loop");
- ScopeProfiler sp(g_profiler, "collisionMoveSimple dtime loop avg", SPT_AVG);
+ ScopeProfiler sp(g_profiler, "collisionMoveSimple dtime loop avg", SPT_AVG);
// Avoid infinite loop
loopcount++;
- if(loopcount >= 100)
- {
- infostream<<"collisionMoveSimple: WARNING: Loop count exceeded, aborting to avoid infiniite loop"<<std::endl;
- dtime = 0;
+ if (loopcount >= 100) {
+ warningstream << "collisionMoveSimple: Loop count exceeded, aborting to avoid infiniite loop" << std::endl;
break;
}
aabb3f movingbox = box_0;
- movingbox.MinEdge += pos_f;
- movingbox.MaxEdge += pos_f;
+ movingbox.MinEdge += *pos_f;
+ movingbox.MaxEdge += *pos_f;
int nearest_collided = -1;
f32 nearest_dtime = dtime;
- u32 nearest_boxindex = -1;
+ int nearest_boxindex = -1;
/*
Go through every nodebox, find nearest collision
*/
- for(u32 boxindex = 0; boxindex < cboxes.size(); boxindex++)
- {
+ for (u32 boxindex = 0; boxindex < cboxes.size(); boxindex++) {
// Ignore if already stepped up this nodebox.
if(is_step_up[boxindex])
continue;
@@ -405,9 +448,9 @@ collisionMoveResult collisionMoveSimple(Environment *env, IGameDef *gamedef,
// Find nearest collision of the two boxes (raytracing-like)
f32 dtime_tmp;
int collided = axisAlignedCollision(
- cboxes[boxindex], movingbox, speed_f, d, dtime_tmp);
+ cboxes[boxindex], movingbox, *speed_f, d, &dtime_tmp);
- if(collided == -1 || dtime_tmp >= nearest_dtime)
+ if (collided == -1 || dtime_tmp >= nearest_dtime)
continue;
nearest_dtime = dtime_tmp;
@@ -415,18 +458,14 @@ collisionMoveResult collisionMoveSimple(Environment *env, IGameDef *gamedef,
nearest_boxindex = boxindex;
}
- if(nearest_collided == -1)
- {
+ if (nearest_collided == -1) {
// No collision with any collision box.
- pos_f += speed_f * dtime;
+ *pos_f += *speed_f * dtime;
dtime = 0; // Set to 0 to avoid "infinite" loop due to small FP numbers
- }
- else
- {
+ } else {
// Otherwise, a collision occurred.
const aabb3f& cbox = cboxes[nearest_boxindex];
-
// Check for stairs.
bool step_up = (nearest_collided != 1) && // must not be Y direction
(movingbox.MinEdge.Y < cbox.MaxEdge.Y) &&
@@ -440,79 +479,68 @@ collisionMoveResult collisionMoveSimple(Environment *env, IGameDef *gamedef,
float bounce = -(float)bouncy_values[nearest_boxindex] / 100.0;
// Move to the point of collision and reduce dtime by nearest_dtime
- if(nearest_dtime < 0)
- {
+ if (nearest_dtime < 0) {
// Handle negative nearest_dtime (can be caused by the d allowance)
- if(!step_up)
- {
- if(nearest_collided == 0)
- pos_f.X += speed_f.X * nearest_dtime;
- if(nearest_collided == 1)
- pos_f.Y += speed_f.Y * nearest_dtime;
- if(nearest_collided == 2)
- pos_f.Z += speed_f.Z * nearest_dtime;
+ if (!step_up) {
+ if (nearest_collided == 0)
+ pos_f->X += speed_f->X * nearest_dtime;
+ if (nearest_collided == 1)
+ pos_f->Y += speed_f->Y * nearest_dtime;
+ if (nearest_collided == 2)
+ pos_f->Z += speed_f->Z * nearest_dtime;
}
- }
- else
- {
- pos_f += speed_f * nearest_dtime;
+ } else {
+ *pos_f += *speed_f * nearest_dtime;
dtime -= nearest_dtime;
}
bool is_collision = true;
- if(is_unloaded[nearest_boxindex])
+ if (is_unloaded[nearest_boxindex])
is_collision = false;
CollisionInfo info;
- if (is_object[nearest_boxindex]) {
+ if (is_object[nearest_boxindex])
info.type = COLLISION_OBJECT;
- }
- else {
+ else
info.type = COLLISION_NODE;
- }
+
info.node_p = node_positions[nearest_boxindex];
info.bouncy = bouncy;
- info.old_speed = speed_f;
+ info.old_speed = *speed_f;
// Set the speed component that caused the collision to zero
- if(step_up)
- {
+ if (step_up) {
// Special case: Handle stairs
is_step_up[nearest_boxindex] = true;
is_collision = false;
- }
- else if(nearest_collided == 0) // X
- {
- if(fabs(speed_f.X) > BS*3)
- speed_f.X *= bounce;
+ } else if(nearest_collided == 0) { // X
+ if (fabs(speed_f->X) > BS * 3)
+ speed_f->X *= bounce;
else
- speed_f.X = 0;
+ speed_f->X = 0;
result.collides = true;
result.collides_xz = true;
}
- else if(nearest_collided == 1) // Y
- {
- if(fabs(speed_f.Y) > BS*3)
- speed_f.Y *= bounce;
+ else if(nearest_collided == 1) { // Y
+ if (fabs(speed_f->Y) > BS * 3)
+ speed_f->Y *= bounce;
else
- speed_f.Y = 0;
+ speed_f->Y = 0;
result.collides = true;
- }
- else if(nearest_collided == 2) // Z
- {
- if(fabs(speed_f.Z) > BS*3)
- speed_f.Z *= bounce;
+ } else if(nearest_collided == 2) { // Z
+ if (fabs(speed_f->Z) > BS * 3)
+ speed_f->Z *= bounce;
else
- speed_f.Z = 0;
+ speed_f->Z = 0;
result.collides = true;
result.collides_xz = true;
}
- info.new_speed = speed_f;
- if(info.new_speed.getDistanceFrom(info.old_speed) < 0.1*BS)
+ info.new_speed = *speed_f;
+ if (info.new_speed.getDistanceFrom(info.old_speed) < 0.1 * BS)
is_collision = false;
- if(is_collision){
+ if (is_collision) {
result.collisions.push_back(info);
}
}
@@ -522,10 +550,9 @@ collisionMoveResult collisionMoveSimple(Environment *env, IGameDef *gamedef,
Final touches: Check if standing on ground, step up stairs.
*/
aabb3f box = box_0;
- box.MinEdge += pos_f;
- box.MaxEdge += pos_f;
- for(u32 boxindex = 0; boxindex < cboxes.size(); boxindex++)
- {
+ box.MinEdge += *pos_f;
+ box.MaxEdge += *pos_f;
+ for (u32 boxindex = 0; boxindex < cboxes.size(); boxindex++) {
const aabb3f& cbox = cboxes[boxindex];
/*
@@ -537,23 +564,21 @@ collisionMoveResult collisionMoveSimple(Environment *env, IGameDef *gamedef,
Use 0.15*BS so that it is easier to get on a node.
*/
- if(
- cbox.MaxEdge.X-d > box.MinEdge.X &&
- cbox.MinEdge.X+d < box.MaxEdge.X &&
- cbox.MaxEdge.Z-d > box.MinEdge.Z &&
- cbox.MinEdge.Z+d < box.MaxEdge.Z
- ){
- if(is_step_up[boxindex])
- {
- pos_f.Y += (cbox.MaxEdge.Y - box.MinEdge.Y);
+ if (cbox.MaxEdge.X - d > box.MinEdge.X && cbox.MinEdge.X + d < box.MaxEdge.X &&
+ cbox.MaxEdge.Z - d > box.MinEdge.Z &&
+ cbox.MinEdge.Z + d < box.MaxEdge.Z) {
+ if (is_step_up[boxindex]) {
+ pos_f->Y += (cbox.MaxEdge.Y - box.MinEdge.Y);
box = box_0;
- box.MinEdge += pos_f;
- box.MaxEdge += pos_f;
+ box.MinEdge += *pos_f;
+ box.MaxEdge += *pos_f;
}
- if(fabs(cbox.MaxEdge.Y-box.MinEdge.Y) < 0.15*BS)
- {
+ if (fabs(cbox.MaxEdge.Y - box.MinEdge.Y) < 0.15 * BS) {
result.touching_ground = true;
- if(is_unloaded[boxindex])
+
+ if (is_object[boxindex])
+ result.standing_on_object = true;
+ if (is_unloaded[boxindex])
result.standing_on_unloaded = true;
}
}
diff --git a/src/collision.h b/src/collision.h
index fc4187eda..1ceaba81c 100644
--- a/src/collision.h
+++ b/src/collision.h
@@ -57,13 +57,15 @@ struct collisionMoveResult
bool collides;
bool collides_xz;
bool standing_on_unloaded;
+ bool standing_on_object;
std::vector<CollisionInfo> collisions;
collisionMoveResult():
touching_ground(false),
collides(false),
collides_xz(false),
- standing_on_unloaded(false)
+ standing_on_unloaded(false),
+ standing_on_object(false)
{}
};
@@ -71,8 +73,8 @@ struct collisionMoveResult
collisionMoveResult collisionMoveSimple(Environment *env,IGameDef *gamedef,
f32 pos_max_d, const aabb3f &box_0,
f32 stepheight, f32 dtime,
- v3f &pos_f, v3f &speed_f,
- v3f &accel_f,ActiveObject* self=0,
+ v3f *pos_f, v3f *speed_f,
+ v3f accel_f, ActiveObject *self=NULL,
bool collideWithObjects=true);
// Helper function:
@@ -81,7 +83,7 @@ collisionMoveResult collisionMoveSimple(Environment *env,IGameDef *gamedef,
// dtime receives time until first collision, invalid if -1 is returned
int axisAlignedCollision(
const aabb3f &staticbox, const aabb3f &movingbox,
- const v3f &speed, f32 d, f32 &dtime);
+ const v3f &speed, f32 d, f32 *dtime);
// Helper function:
// Checks if moving the movingbox up by the given distance would hit a ceiling.
diff --git a/src/content_cao.cpp b/src/content_cao.cpp
index 0293b7983..d701e4f72 100644
--- a/src/content_cao.cpp
+++ b/src/content_cao.cpp
@@ -309,7 +309,7 @@ public:
void initialize(const std::string &data);
- core::aabbox3d<f32>* getSelectionBox()
+ aabb3f *getSelectionBox()
{return &m_selection_box;}
v3f getPosition()
{return m_position;}
@@ -319,7 +319,7 @@ public:
bool getCollisionBox(aabb3f *toset) { return false; }
private:
- core::aabbox3d<f32> m_selection_box;
+ aabb3f m_selection_box;
scene::IMeshSceneNode *m_node;
v3f m_position;
std::string m_itemstring;
@@ -465,7 +465,7 @@ void ItemCAO::updateTexture()
}
catch(SerializationError &e)
{
- infostream<<"WARNING: "<<__FUNCTION_NAME
+ warningstream<<FUNCTION_NAME
<<": error deSerializing itemstring \""
<<m_itemstring<<std::endl;
}
@@ -546,13 +546,14 @@ GenericCAO::GenericCAO(IGameDef *gamedef, ClientEnvironment *env):
//
m_smgr(NULL),
m_irr(NULL),
+ m_camera(NULL),
+ m_gamedef(NULL),
m_selection_box(-BS/3.,-BS/3.,-BS/3., BS/3.,BS/3.,BS/3.),
m_meshnode(NULL),
m_animated_meshnode(NULL),
m_wield_meshnode(NULL),
m_spritenode(NULL),
- m_nametag_color(video::SColor(255, 255, 255, 255)),
- m_textnode(NULL),
+ m_nametag(NULL),
m_position(v3f(0,10*BS,0)),
m_velocity(v3f(0,0,0)),
m_acceleration(v3f(0,0,0)),
@@ -581,8 +582,11 @@ GenericCAO::GenericCAO(IGameDef *gamedef, ClientEnvironment *env):
m_last_light(255),
m_is_visible(false)
{
- if(gamedef == NULL)
+ if (gamedef == NULL) {
ClientActiveObject::registerType(getType(), create);
+ } else {
+ m_gamedef = gamedef;
+ }
}
bool GenericCAO::getCollisionBox(aabb3f *toset)
@@ -668,14 +672,13 @@ void GenericCAO::initialize(const std::string &data)
GenericCAO::~GenericCAO()
{
- if(m_is_player)
- {
+ if (m_is_player) {
m_env->removePlayerName(m_name.c_str());
}
removeFromScene(true);
}
-core::aabbox3d<f32>* GenericCAO::getSelectionBox()
+aabb3f *GenericCAO::getSelectionBox()
{
if(!m_prop.is_visible || !m_is_visible || m_is_local_player || getParent() != NULL)
return NULL;
@@ -696,14 +699,15 @@ v3f GenericCAO::getPosition()
scene::ISceneNode* GenericCAO::getSceneNode()
{
- if (m_meshnode)
+ if (m_meshnode) {
return m_meshnode;
- if (m_animated_meshnode)
+ } else if (m_animated_meshnode) {
return m_animated_meshnode;
- if (m_wield_meshnode)
+ } else if (m_wield_meshnode) {
return m_wield_meshnode;
- if (m_spritenode)
+ } else if (m_spritenode) {
return m_spritenode;
+ }
return NULL;
}
@@ -776,56 +780,47 @@ void GenericCAO::removeFromScene(bool permanent)
}
}
- if(m_meshnode)
- {
+ if (m_meshnode) {
m_meshnode->remove();
m_meshnode->drop();
m_meshnode = NULL;
- }
- if(m_animated_meshnode)
- {
+ } else if (m_animated_meshnode) {
m_animated_meshnode->remove();
m_animated_meshnode->drop();
m_animated_meshnode = NULL;
- }
- if(m_wield_meshnode)
- {
+ } else if (m_wield_meshnode) {
m_wield_meshnode->remove();
m_wield_meshnode->drop();
m_wield_meshnode = NULL;
- }
- if(m_spritenode)
- {
+ } else if (m_spritenode) {
m_spritenode->remove();
m_spritenode->drop();
m_spritenode = NULL;
}
- if (m_textnode)
- {
- m_textnode->remove();
- m_textnode->drop();
- m_textnode = NULL;
+
+ if (m_nametag) {
+ m_gamedef->getCamera()->removeNametag(m_nametag);
+ m_nametag = NULL;
}
}
-void GenericCAO::addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc,
- IrrlichtDevice *irr)
+void GenericCAO::addToScene(scene::ISceneManager *smgr,
+ ITextureSource *tsrc, IrrlichtDevice *irr)
{
m_smgr = smgr;
m_irr = irr;
- if (getSceneNode() != NULL)
+ if (getSceneNode() != NULL) {
return;
+ }
m_visuals_expired = false;
- if(!m_prop.is_visible)
+ if (!m_prop.is_visible) {
return;
+ }
- //video::IVideoDriver* driver = smgr->getVideoDriver();
-
- if(m_prop.visual == "sprite")
- {
+ if (m_prop.visual == "sprite") {
infostream<<"GenericCAO::addToScene(): single_sprite"<<std::endl;
m_spritenode = smgr->addBillboardSceneNode(
NULL, v2f(1, 1), v3f(0,0,0), -1);
@@ -934,10 +929,15 @@ void GenericCAO::addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc,
u8 li = m_last_light;
setMeshColor(m_animated_meshnode->getMesh(), video::SColor(255,li,li,li));
+ bool backface_culling = m_prop.backface_culling;
+ if (m_is_player)
+ backface_culling = false;
+
m_animated_meshnode->setMaterialFlag(video::EMF_LIGHTING, false);
m_animated_meshnode->setMaterialFlag(video::EMF_BILINEAR_FILTER, false);
m_animated_meshnode->setMaterialType(video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF);
m_animated_meshnode->setMaterialFlag(video::EMF_FOG_ENABLE, true);
+ m_animated_meshnode->setMaterialFlag(video::EMF_BACK_FACE_CULLING, backface_culling);
}
else
errorstream<<"GenericCAO::addToScene(): Could not load mesh "<<m_prop.mesh<<std::endl;
@@ -967,14 +967,10 @@ void GenericCAO::addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc,
updateTextures("");
scene::ISceneNode *node = getSceneNode();
- if (node && m_is_player && !m_is_local_player) {
- // Add a text node for showing the name
- gui::IGUIEnvironment* gui = irr->getGUIEnvironment();
- std::wstring wname = utf8_to_wide(m_name);
- m_textnode = smgr->addTextSceneNode(gui->getSkin()->getFont(),
- wname.c_str(), m_nametag_color, node);
- m_textnode->grab();
- m_textnode->setPosition(v3f(0, BS*1.1, 0));
+ if (node && m_prop.nametag != "" && !m_is_local_player) {
+ // Add nametag
+ m_nametag = m_gamedef->getCamera()->addNametag(node,
+ m_prop.nametag, m_prop.nametag_color);
}
updateNodePos();
@@ -1176,22 +1172,20 @@ void GenericCAO::step(float dtime, ClientEnvironment *env)
if(m_prop.physical)
{
- core::aabbox3d<f32> box = m_prop.collisionbox;
+ aabb3f box = m_prop.collisionbox;
box.MinEdge *= BS;
box.MaxEdge *= BS;
collisionMoveResult moveresult;
f32 pos_max_d = BS*0.125; // Distance per iteration
v3f p_pos = m_position;
v3f p_velocity = m_velocity;
- v3f p_acceleration = m_acceleration;
moveresult = collisionMoveSimple(env,env->getGameDef(),
pos_max_d, box, m_prop.stepheight, dtime,
- p_pos, p_velocity, p_acceleration,
+ &p_pos, &p_velocity, m_acceleration,
this, m_prop.collideWithObjects);
// Apply results
m_position = p_pos;
m_velocity = p_velocity;
- m_acceleration = p_acceleration;
bool is_end_position = moveresult.collides;
pos_translator.update(m_position, is_end_position, dtime);
@@ -1251,8 +1245,18 @@ void GenericCAO::step(float dtime, ClientEnvironment *env)
if (getParent() == NULL && m_prop.automatic_face_movement_dir &&
(fabs(m_velocity.Z) > 0.001 || fabs(m_velocity.X) > 0.001))
{
- m_yaw = atan2(m_velocity.Z,m_velocity.X) * 180 / M_PI
+ float optimal_yaw = atan2(m_velocity.Z,m_velocity.X) * 180 / M_PI
+ m_prop.automatic_face_movement_dir_offset;
+ float max_rotation_delta =
+ dtime * m_prop.automatic_face_movement_max_rotation_per_sec;
+
+ if ((m_prop.automatic_face_movement_max_rotation_per_sec > 0) &&
+ (fabs(m_yaw - optimal_yaw) > max_rotation_delta)) {
+
+ m_yaw = optimal_yaw < m_yaw ? m_yaw - max_rotation_delta : m_yaw + max_rotation_delta;
+ } else {
+ m_yaw = optimal_yaw;
+ }
updateNodePos();
}
}
@@ -1584,6 +1588,9 @@ void GenericCAO::processMessage(const std::string &data)
m_tx_basepos = m_prop.initial_sprite_basepos;
}
+ if ((m_is_player && !m_is_local_player) && m_prop.nametag == "")
+ m_prop.nametag = m_name;
+
expireVisuals();
}
else if(cmd == GENERIC_CMD_UPDATE_POSITION)
@@ -1762,15 +1769,11 @@ void GenericCAO::processMessage(const std::string &data)
m_armor_groups[name] = rating;
}
} else if (cmd == GENERIC_CMD_UPDATE_NAMETAG_ATTRIBUTES) {
+ // Deprecated, for backwards compatibility only.
readU8(is); // version
- m_nametag_color = readARGB8(is);
- if (m_textnode != NULL) {
- m_textnode->setTextColor(m_nametag_color);
-
- // Enforce hiding nametag,
- // because if freetype is enabled, a grey
- // shadow can remain.
- m_textnode->setVisible(m_nametag_color.getAlpha() > 0);
+ m_prop.nametag_color = readARGB8(is);
+ if (m_nametag != NULL) {
+ m_nametag->nametag_color = m_prop.nametag_color;
}
}
}
@@ -1820,7 +1823,7 @@ std::string GenericCAO::debugInfoText()
os<<"GenericCAO hp="<<m_hp<<"\n";
os<<"armor={";
for(ItemGroupList::const_iterator i = m_armor_groups.begin();
- i != m_armor_groups.end(); i++)
+ i != m_armor_groups.end(); ++i)
{
os<<i->first<<"="<<i->second<<", ";
}
diff --git a/src/content_cao.h b/src/content_cao.h
index 299d6c73e..a166ff494 100644
--- a/src/content_cao.h
+++ b/src/content_cao.h
@@ -26,6 +26,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "object_properties.h"
#include "itemgroup.h"
+class Camera;
+struct Nametag;
+
/*
SmoothTranslator
*/
@@ -65,13 +68,14 @@ private:
//
scene::ISceneManager *m_smgr;
IrrlichtDevice *m_irr;
- core::aabbox3d<f32> m_selection_box;
+ Camera* m_camera;
+ IGameDef *m_gamedef;
+ aabb3f m_selection_box;
scene::IMeshSceneNode *m_meshnode;
scene::IAnimatedMeshSceneNode *m_animated_meshnode;
WieldMeshSceneNode *m_wield_meshnode;
scene::IBillboardSceneNode *m_spritenode;
- video::SColor m_nametag_color;
- scene::ITextSceneNode* m_textnode;
+ Nametag* m_nametag;
v3f m_position;
v3f m_velocity;
v3f m_acceleration;
@@ -128,7 +132,7 @@ public:
bool collideWithObjects();
- core::aabbox3d<f32>* getSelectionBox();
+ aabb3f *getSelectionBox();
v3f getPosition();
@@ -202,6 +206,11 @@ public:
float time_from_last_punch=1000000);
std::string debugInfoText();
+
+ std::string infoText()
+ {
+ return m_prop.infotext;
+ }
};
diff --git a/src/content_mapblock.cpp b/src/content_mapblock.cpp
index 8fa041312..6a83bd8f3 100644
--- a/src/content_mapblock.cpp
+++ b/src/content_mapblock.cpp
@@ -50,9 +50,9 @@ void makeCuboid(MeshCollector *collector, const aabb3f &box,
v3f min = box.MinEdge;
v3f max = box.MaxEdge;
-
-
-
+
+
+
if(txc == NULL) {
static const f32 txc_default[24] = {
0,0,1,1,
@@ -163,6 +163,14 @@ void makeCuboid(MeshCollector *collector, const aabb3f &box,
}
}
+static inline void getNeighborConnectingFace(v3s16 p, INodeDefManager *nodedef,
+ MeshMakeData *data, MapNode n, int v, int *neighbors)
+{
+ MapNode n2 = data->m_vmanip.getNodeNoEx(p);
+ if (nodedef->nodeboxConnects(n, n2, v))
+ *neighbors |= v;
+}
+
/*
TODO: Fix alpha blending for special nodes
Currently only the last element rendered is blended correct
@@ -171,7 +179,6 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
MeshCollector &collector)
{
INodeDefManager *nodedef = data->m_gamedef->ndef();
- ITextureSource *tsrc = data->m_gamedef->tsrc();
scene::ISceneManager* smgr = data->m_gamedef->getSceneManager();
scene::IMeshManipulator* meshmanip = smgr->getMeshManipulator();
@@ -182,11 +189,6 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
Some settings
*/
bool enable_mesh_cache = g_settings->getBool("enable_mesh_cache");
- bool new_style_water = g_settings->getBool("new_style_water");
-
- float node_liquid_level = 1.0;
- if (new_style_water)
- node_liquid_level = 0.85;
v3s16 blockpos_nodes = data->m_blockpos*MAP_BLOCKSIZE;
@@ -288,35 +290,29 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
If our topside is liquid, set upper border of face
at upper border of node
*/
- if(top_is_same_liquid)
- {
- vertices[2].Pos.Y = 0.5*BS;
- vertices[3].Pos.Y = 0.5*BS;
- }
+ if (top_is_same_liquid) {
+ vertices[2].Pos.Y = 0.5 * BS;
+ vertices[3].Pos.Y = 0.5 * BS;
+ } else {
/*
Otherwise upper position of face is liquid level
*/
- else
- {
- vertices[2].Pos.Y = (node_liquid_level-0.5)*BS;
- vertices[3].Pos.Y = (node_liquid_level-0.5)*BS;
+ vertices[2].Pos.Y = 0.5 * BS;
+ vertices[3].Pos.Y = 0.5 * BS;
}
/*
If neighbor is liquid, lower border of face is liquid level
*/
- if(neighbor_is_same_liquid)
- {
- vertices[0].Pos.Y = (node_liquid_level-0.5)*BS;
- vertices[1].Pos.Y = (node_liquid_level-0.5)*BS;
- }
+ if (neighbor_is_same_liquid) {
+ vertices[0].Pos.Y = 0.5 * BS;
+ vertices[1].Pos.Y = 0.5 * BS;
+ } else {
/*
If neighbor is not liquid, lower border of face is
lower border of node
*/
- else
- {
- vertices[0].Pos.Y = -0.5*BS;
- vertices[1].Pos.Y = -0.5*BS;
+ vertices[0].Pos.Y = -0.5 * BS;
+ vertices[1].Pos.Y = -0.5 * BS;
}
for(s32 j=0; j<4; j++)
@@ -350,7 +346,7 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
*/
if(top_is_same_liquid)
continue;
-
+
video::S3DVertex vertices[4] =
{
video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,1),
@@ -359,7 +355,7 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,0),
};
- v3f offset(p.X*BS, p.Y*BS + (-0.5+node_liquid_level)*BS, p.Z*BS);
+ v3f offset(p.X * BS, (p.Y + 0.5) * BS, p.Z * BS);
for(s32 i=0; i<4; i++)
{
vertices[i].Pos += offset;
@@ -383,7 +379,7 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
content_t c_source = nodedef->getId(f.liquid_alternative_source);
if(ntop.getContent() == c_flowing || ntop.getContent() == c_source)
top_is_same_liquid = true;
-
+
u16 l = 0;
// If this liquid emits light and doesn't contain light, draw
// it at what it emits, for an increased effect
@@ -399,7 +395,7 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
else
l = getInteriorLight(n, 0, nodedef);
video::SColor c = MapBlock_LightColor(f.alpha, l, f.light_source);
-
+
u8 range = rangelim(nodedef->get(c_flowing).liquid_range, 1, 8);
// Neighbor liquid levels (key = relative position)
@@ -432,14 +428,14 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
content = n2.getContent();
if(n2.getContent() == c_source)
- level = (-0.5+node_liquid_level) * BS;
+ level = 0.5 * BS;
else if(n2.getContent() == c_flowing){
u8 liquid_level = (n2.param2&LIQUID_LEVEL_MASK);
if (liquid_level <= LIQUID_LEVEL_MAX+1-range)
liquid_level = 0;
else
liquid_level -= (LIQUID_LEVEL_MAX+1-range);
- level = (-0.5 + ((float)liquid_level+ 0.5) / (float)range * node_liquid_level) * BS;
+ level = (-0.5 + ((float)liquid_level + 0.5) / (float)range) * BS;
}
// Check node above neighbor.
@@ -451,7 +447,7 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
n2.getContent() == c_flowing)
flags |= neighborflag_top_is_same_liquid;
}
-
+
neighbor_levels[neighbor_dirs[i]] = level;
neighbor_contents[neighbor_dirs[i]] = content;
neighbor_flags[neighbor_dirs[i]] = flags;
@@ -459,7 +455,7 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
// Corner heights (average between four liquids)
f32 corner_levels[4];
-
+
v3s16 halfdirs[4] = {
v3s16(0,0,0),
v3s16(1,0,0),
@@ -487,7 +483,7 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
// Source is always the same height
else if(content == c_source)
{
- cornerlevel = (-0.5+node_liquid_level)*BS;
+ cornerlevel = 0.5 * BS;
valid_count = 1;
break;
}
@@ -539,14 +535,14 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
content_t neighbor_content = neighbor_contents[dir];
const ContentFeatures &n_feat = nodedef->get(neighbor_content);
-
+
// Don't draw face if neighbor is blocking the view
if(n_feat.solidness == 2)
continue;
-
+
bool neighbor_is_same_liquid = (neighbor_content == c_source
|| neighbor_content == c_flowing);
-
+
// Don't draw any faces if neighbor same is liquid and top is
// same liquid
if(neighbor_is_same_liquid == true
@@ -558,7 +554,7 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
const TileSpec *current_tile = &tile_liquid;
if(n_feat.solidness != 0 || n_feat.visual_solidness != 0)
current_tile = &tile_liquid_bfculled;
-
+
video::S3DVertex vertices[4] =
{
video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,1),
@@ -566,7 +562,7 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),
};
-
+
/*
If our topside is liquid, set upper border of face
at upper border of node
@@ -584,7 +580,7 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
vertices[2].Pos.Y = corner_levels[side_corners[i][0]];
vertices[3].Pos.Y = corner_levels[side_corners[i][1]];
}
-
+
/*
If neighbor is liquid, lower border of face is corner
liquid levels
@@ -603,7 +599,7 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
vertices[0].Pos.Y = -0.5*BS;
vertices[1].Pos.Y = -0.5*BS;
}
-
+
for(s32 j=0; j<4; j++)
{
if(dir == v3s16(0,0,1))
@@ -614,7 +610,7 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
vertices[j].Pos.rotateXZBy(90);
if(dir == v3s16(1,0,-0))
vertices[j].Pos.rotateXZBy(-90);
-
+
// Do this to not cause glitches when two liquids are
// side-by-side
/*if(neighbor_is_same_liquid == false){
@@ -629,11 +625,11 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
// Add to mesh collector
collector.append(*current_tile, vertices, 4, indices, 6);
}
-
+
/*
Generate top side, if appropriate
*/
-
+
if(top_is_same_liquid == false)
{
video::S3DVertex vertices[4] =
@@ -643,7 +639,7 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,0),
video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,0),
};
-
+
// To get backface culling right, the vertices need to go
// clockwise around the front of the face. And we happened to
// calculate corner levels in exact reverse order.
@@ -657,8 +653,8 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
vertices[i].Pos.Y += corner_levels[j];
vertices[i].Pos += intToFloat(p, BS);
}
-
- // Default downwards-flowing texture animation goes from
+
+ // Default downwards-flowing texture animation goes from
// -Z towards +Z, thus the direction is +Z.
// Rotate texture to make animation go in flow direction
// Positive if liquid moves towards +Z
@@ -721,7 +717,7 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
video::S3DVertex(BS/2,BS/2,BS/2, dir.X,dir.Y,dir.Z, c, 0,0),
video::S3DVertex(-BS/2,BS/2,BS/2, dir.X,dir.Y,dir.Z, c, 1,0),
};
-
+
// Rotations in the g_6dirs format
if(j == 0) // Z+
for(u16 i=0; i<4; i++)
@@ -770,7 +766,7 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
TileSpec tiles[6];
for (i = 0; i < 6; i++)
tiles[i] = getNodeTile(n, p, dirs[i], data);
-
+
TileSpec glass_tiles[6];
if (tiles[1].texture && tiles[2].texture && tiles[3].texture) {
glass_tiles[0] = tiles[2];
@@ -781,21 +777,21 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
glass_tiles[5] = tiles[1];
} else {
for (i = 0; i < 6; i++)
- glass_tiles[i] = tiles[1];
+ glass_tiles[i] = tiles[1];
}
-
+
u8 param2 = n.getParam2();
bool H_merge = ! bool(param2 & 128);
bool V_merge = ! bool(param2 & 64);
param2 = param2 & 63;
-
+
u16 l = getInteriorLight(n, 1, nodedef);
video::SColor c = MapBlock_LightColor(255, l, f.light_source);
v3f pos = intToFloat(p, BS);
static const float a = BS / 2;
static const float g = a - 0.003;
static const float b = .876 * ( BS / 2 );
-
+
static const aabb3f frame_edges[12] = {
aabb3f( b, b,-a, a, a, a), // y+
aabb3f(-a, b,-a,-b, a, a), // y+
@@ -818,16 +814,16 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
aabb3f(-g,-g, g, g, g, g), // z+
aabb3f(-g,-g,-g, g, g,-g) // z-
};
-
+
// table of node visible faces, 0 = invisible
int visible_faces[6] = {0,0,0,0,0,0};
-
+
// table of neighbours, 1 = same type, checked with g_26dirs
int nb[18] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
-
+
// g_26dirs to check when only horizontal merge is allowed
int nb_H_dirs[8] = {0,2,3,5,10,11,12,13};
-
+
content_t current = n.getContent();
content_t n2c;
MapNode n2;
@@ -845,14 +841,14 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
n2 = data->m_vmanip.getNodeNoEx(n2p);
n2c = n2.getContent();
if (n2c == current || n2c == CONTENT_IGNORE)
- nb[4] = 1;
+ nb[4] = 1;
} else if (H_merge && !V_merge) {
for(i = 0; i < 8; i++) {
n2p = blockpos_nodes + p + g_26dirs[nb_H_dirs[i]];
n2 = data->m_vmanip.getNodeNoEx(n2p);
n2c = n2.getContent();
if (n2c == current || n2c == CONTENT_IGNORE)
- nb[nb_H_dirs[i]] = 1;
+ nb[nb_H_dirs[i]] = 1;
}
} else if (H_merge && V_merge) {
for(i = 0; i < 18; i++) {
@@ -878,7 +874,7 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
visible_faces[i] = 1;
}
}
-
+
if (!H_merge) {
visible_faces[2] = 1;
visible_faces[3] = 1;
@@ -893,7 +889,7 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
visible_faces[i] = 1;
}
}
-
+
static const u8 nb_triplet[12*3] = {
1,2, 7, 1,5, 6, 4,2,15, 4,5,14,
2,0,11, 2,3,13, 5,0,10, 5,3,12,
@@ -1010,7 +1006,7 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
case NDT_TORCHLIKE:
{
v3s16 dir = n.getWallMountedDir(nodedef);
-
+
u8 tileindex = 0;
if(dir == v3s16(0,-1,0)){
tileindex = 0; // floor
@@ -1070,7 +1066,7 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
u16 l = getInteriorLight(n, 0, nodedef);
video::SColor c = MapBlock_LightColor(255, l, f.light_source);
-
+
float d = (float)BS/16;
float s = BS/2*f.visual_scale;
// Wall at X+ of node
@@ -1113,7 +1109,7 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
u16 l = getInteriorLight(n, 1, nodedef);
video::SColor c = MapBlock_LightColor(255, l, f.light_source);
-
+
float s = BS / 2 * f.visual_scale;
for (int j = 0; j < 2; j++)
@@ -1125,16 +1121,16 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
video::S3DVertex( s,-BS/2 + s*2,0, 0,0,0, c, 1,0),
video::S3DVertex(-s,-BS/2 + s*2,0, 0,0,0, c, 0,0),
};
+ float rotate_degree = 0;
+ if (f.param_type_2 == CPT2_DEGROTATE)
+ rotate_degree = n.param2 * 2;
- if(j == 0)
- {
+ if (j == 0) {
for(u16 i = 0; i < 4; i++)
- vertices[i].Pos.rotateXZBy(46 + n.param2 * 2);
- }
- else if(j == 1)
- {
+ vertices[i].Pos.rotateXZBy(46 + rotate_degree);
+ } else if (j == 1) {
for(u16 i = 0; i < 4; i++)
- vertices[i].Pos.rotateXZBy(-44 + n.param2 * 2);
+ vertices[i].Pos.rotateXZBy(-44 + rotate_degree);
}
for (int i = 0; i < 4; i++)
@@ -1157,7 +1153,7 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
u16 l = getInteriorLight(n, 1, nodedef);
video::SColor c = MapBlock_LightColor(255, l, f.light_source);
- float s = BS/2*f.visual_scale;
+ float s = BS / 2 * f.visual_scale;
content_t current = n.getContent();
content_t n2c;
@@ -1165,148 +1161,116 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
v3s16 n2p;
static const v3s16 dirs[6] = {
- v3s16( 0, 1, 0),
- v3s16( 0,-1, 0),
- v3s16( 1, 0, 0),
- v3s16(-1, 0, 0),
- v3s16( 0, 0, 1),
- v3s16( 0, 0,-1)
+ v3s16( 0, 1, 0),
+ v3s16( 0, -1, 0),
+ v3s16( 1, 0, 0),
+ v3s16(-1, 0, 0),
+ v3s16( 0, 0, 1),
+ v3s16( 0, 0, -1)
};
- int doDraw[6] = {0,0,0,0,0,0};
+ int doDraw[6] = {0, 0, 0, 0, 0, 0};
bool drawAllFaces = true;
- bool drawBottomFacesOnly = false; // Currently unused
-
// Check for adjacent nodes
- for(int i = 0; i < 6; i++)
- {
+ for (int i = 0; i < 6; i++) {
n2p = blockpos_nodes + p + dirs[i];
n2 = data->m_vmanip.getNodeNoEx(n2p);
n2c = n2.getContent();
if (n2c != CONTENT_IGNORE && n2c != CONTENT_AIR && n2c != current) {
doDraw[i] = 1;
- if(drawAllFaces)
+ if (drawAllFaces)
drawAllFaces = false;
}
}
- for(int j = 0; j < 6; j++)
- {
- int vOffset = 0; // Vertical offset of faces after rotation
- int hOffset = 4; // Horizontal offset of faces to reach the edge
+ for (int j = 0; j < 6; j++) {
- video::S3DVertex vertices[4] =
- {
- video::S3DVertex(-s,-BS/2, 0, 0,0,0, c, 0,1),
- video::S3DVertex( s,-BS/2, 0, 0,0,0, c, 1,1),
- video::S3DVertex( s,-BS/2 + s*2,0, 0,0,0, c, 1,0),
- video::S3DVertex(-s,-BS/2 + s*2,0, 0,0,0, c, 0,0),
+ video::S3DVertex vertices[4] = {
+ video::S3DVertex(-s, -BS / 2, 0, 0, 0, 0, c, 0, 1),
+ video::S3DVertex( s, -BS / 2, 0, 0, 0, 0, c, 1, 1),
+ video::S3DVertex( s, -BS / 2 + s * 2, 0, 0, 0, 0, c, 1, 0),
+ video::S3DVertex(-s, -BS / 2 + s * 2, 0, 0, 0, 0, c, 0, 0),
};
// Calculate which faces should be drawn, (top or sides)
- if(j == 0 && (drawAllFaces || (doDraw[3] == 1 || doDraw[1] == 1)))
- {
- for(int i = 0; i < 4; i++) {
- vertices[i].Pos.rotateXZBy(90 + n.param2 * 2);
+ if (j == 0 && (drawAllFaces ||
+ (doDraw[3] == 1 || doDraw[1] == 1))) {
+ for (int i = 0; i < 4; i++) {
+ vertices[i].Pos.rotateXZBy(90);
vertices[i].Pos.rotateXYBy(-10);
- vertices[i].Pos.Y -= vOffset;
- vertices[i].Pos.X -= hOffset;
+ vertices[i].Pos.X -= 4.0;
}
- }
- else if(j == 1 && (drawAllFaces || (doDraw[5] == 1 || doDraw[1] == 1)))
- {
- for(int i = 0; i < 4; i++) {
- vertices[i].Pos.rotateXZBy(180 + n.param2 * 2);
+ } else if (j == 1 && (drawAllFaces ||
+ (doDraw[5] == 1 || doDraw[1] == 1))) {
+ for (int i = 0; i < 4; i++) {
+ vertices[i].Pos.rotateXZBy(180);
vertices[i].Pos.rotateYZBy(10);
- vertices[i].Pos.Y -= vOffset;
- vertices[i].Pos.Z -= hOffset;
+ vertices[i].Pos.Z -= 4.0;
}
- }
- else if(j == 2 && (drawAllFaces || (doDraw[2] == 1 || doDraw[1] == 1)))
- {
- for(int i = 0; i < 4; i++) {
- vertices[i].Pos.rotateXZBy(270 + n.param2 * 2);
+ } else if (j == 2 && (drawAllFaces ||
+ (doDraw[2] == 1 || doDraw[1] == 1))) {
+ for (int i = 0; i < 4; i++) {
+ vertices[i].Pos.rotateXZBy(270);
vertices[i].Pos.rotateXYBy(10);
- vertices[i].Pos.Y -= vOffset;
- vertices[i].Pos.X += hOffset;
+ vertices[i].Pos.X += 4.0;
}
- }
- else if(j == 3 && (drawAllFaces || (doDraw[4] == 1 || doDraw[1] == 1)))
- {
- for(int i = 0; i < 4; i++) {
+ } else if (j == 3 && (drawAllFaces ||
+ (doDraw[4] == 1 || doDraw[1] == 1))) {
+ for (int i = 0; i < 4; i++) {
vertices[i].Pos.rotateYZBy(-10);
- vertices[i].Pos.Y -= vOffset;
- vertices[i].Pos.Z += hOffset;
+ vertices[i].Pos.Z += 4.0;
}
- }
-
// Center cross-flames
- else if(j == 4 && (drawAllFaces || doDraw[1] == 1))
- {
- for(int i=0; i<4; i++) {
- vertices[i].Pos.rotateXZBy(45 + n.param2 * 2);
- vertices[i].Pos.Y -= vOffset;
+ } else if (j == 4 && (drawAllFaces || doDraw[1] == 1)) {
+ for (int i = 0; i < 4; i++) {
+ vertices[i].Pos.rotateXZBy(45);
}
- }
- else if(j == 5 && (drawAllFaces || doDraw[1] == 1))
- {
- for(int i=0; i<4; i++) {
- vertices[i].Pos.rotateXZBy(-45 + n.param2 * 2);
- vertices[i].Pos.Y -= vOffset;
+ } else if (j == 5 && (drawAllFaces || doDraw[1] == 1)) {
+ for (int i = 0; i < 4; i++) {
+ vertices[i].Pos.rotateXZBy(-45);
}
- }
-
- // Render flames on bottom
- else if(j == 0 && (drawBottomFacesOnly || (doDraw[0] == 1 && doDraw[1] == 0)))
- {
- for(int i = 0; i < 4; i++) {
+ // Render flames on bottom of node above
+ } else if (j == 0 && doDraw[0] == 1 && doDraw[1] == 0) {
+ for (int i = 0; i < 4; i++) {
vertices[i].Pos.rotateYZBy(70);
- vertices[i].Pos.rotateXZBy(90 + n.param2 * 2);
+ vertices[i].Pos.rotateXZBy(90);
vertices[i].Pos.Y += 4.84;
- vertices[i].Pos.X -= hOffset+0.7;
+ vertices[i].Pos.X -= 4.7;
}
- }
- else if(j == 1 && (drawBottomFacesOnly || (doDraw[0] == 1 && doDraw[1] == 0)))
- {
- for(int i = 0; i < 4; i++) {
+ } else if (j == 1 && doDraw[0] == 1 && doDraw[1] == 0) {
+ for (int i = 0; i < 4; i++) {
vertices[i].Pos.rotateYZBy(70);
- vertices[i].Pos.rotateXZBy(180 + n.param2 * 2);
+ vertices[i].Pos.rotateXZBy(180);
vertices[i].Pos.Y += 4.84;
- vertices[i].Pos.Z -= hOffset+0.7;
+ vertices[i].Pos.Z -= 4.7;
}
- }
- else if(j == 2 && (drawBottomFacesOnly || (doDraw[0] == 1 && doDraw[1] == 0)))
- {
- for(int i = 0; i < 4; i++) {
+ } else if (j == 2 && doDraw[0] == 1 && doDraw[1] == 0) {
+ for (int i = 0; i < 4; i++) {
vertices[i].Pos.rotateYZBy(70);
- vertices[i].Pos.rotateXZBy(270 + n.param2 * 2);
+ vertices[i].Pos.rotateXZBy(270);
vertices[i].Pos.Y += 4.84;
- vertices[i].Pos.X += hOffset+0.7;
+ vertices[i].Pos.X += 4.7;
}
- }
- else if(j == 3 && (drawBottomFacesOnly || (doDraw[0] == 1 && doDraw[1] == 0)))
- {
- for(int i = 0; i < 4; i++) {
+ } else if (j == 3 && doDraw[0] == 1 && doDraw[1] == 0) {
+ for (int i = 0; i < 4; i++) {
vertices[i].Pos.rotateYZBy(70);
vertices[i].Pos.Y += 4.84;
- vertices[i].Pos.Z += hOffset+0.7;
+ vertices[i].Pos.Z += 4.7;
}
- }
- else {
+ } else {
// Skip faces that aren't adjacent to a node
continue;
}
- for(int i=0; i<4; i++)
- {
+ for (int i = 0; i < 4; i++) {
vertices[i].Pos *= f.visual_scale;
vertices[i].Pos += intToFloat(p, BS);
}
- u16 indices[] = {0,1,2,2,3,0};
+ u16 indices[] = {0, 1, 2, 2, 3, 0};
// Add to mesh collector
collector.append(tile, vertices, 4, indices, 6);
}
@@ -1539,16 +1503,47 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
v3s16(0, 0, -1)
};
TileSpec tiles[6];
-
+
u16 l = getInteriorLight(n, 1, nodedef);
video::SColor c = MapBlock_LightColor(255, l, f.light_source);
v3f pos = intToFloat(p, BS);
- std::vector<aabb3f> boxes = n.getNodeBoxes(nodedef);
+ int neighbors = 0;
+
+ // locate possible neighboring nodes to connect to
+ if (f.node_box.type == NODEBOX_CONNECTED) {
+ v3s16 p2 = p;
+
+ p2.Y++;
+ getNeighborConnectingFace(blockpos_nodes + p2, nodedef, data, n, 1, &neighbors);
+
+ p2 = p;
+ p2.Y--;
+ getNeighborConnectingFace(blockpos_nodes + p2, nodedef, data, n, 2, &neighbors);
+
+ p2 = p;
+ p2.Z--;
+ getNeighborConnectingFace(blockpos_nodes + p2, nodedef, data, n, 4, &neighbors);
+
+ p2 = p;
+ p2.X--;
+ getNeighborConnectingFace(blockpos_nodes + p2, nodedef, data, n, 8, &neighbors);
+
+ p2 = p;
+ p2.Z++;
+ getNeighborConnectingFace(blockpos_nodes + p2, nodedef, data, n, 16, &neighbors);
+
+ p2 = p;
+ p2.X++;
+ getNeighborConnectingFace(blockpos_nodes + p2, nodedef, data, n, 32, &neighbors);
+ }
+
+ std::vector<aabb3f> boxes;
+ n.getNodeBoxes(nodedef, &boxes, neighbors);
for(std::vector<aabb3f>::iterator
i = boxes.begin();
- i != boxes.end(); i++)
+ i != boxes.end(); ++i)
{
for(int j = 0; j < 6; j++)
{
@@ -1558,7 +1553,7 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
aabb3f box = *i;
box.MinEdge += pos;
box.MaxEdge += pos;
-
+
f32 temp;
if (box.MinEdge.X > box.MaxEdge.X)
{
@@ -1647,55 +1642,5 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
break;}
}
}
-
- /*
- Caused by incorrect alpha blending, selection mesh needs to be created as
- last element to ensure it gets blended correct over nodes with alpha channel
- */
- // Create selection mesh
- v3s16 p = data->m_highlighted_pos_relative;
- if (data->m_show_hud &&
- (p.X >= 0) && (p.X < MAP_BLOCKSIZE) &&
- (p.Y >= 0) && (p.Y < MAP_BLOCKSIZE) &&
- (p.Z >= 0) && (p.Z < MAP_BLOCKSIZE)) {
-
- MapNode n = data->m_vmanip.getNodeNoEx(blockpos_nodes + p);
- if(n.getContent() != CONTENT_AIR) {
- // Get selection mesh light level
- static const v3s16 dirs[7] = {
- v3s16( 0, 0, 0),
- v3s16( 0, 1, 0),
- v3s16( 0,-1, 0),
- v3s16( 1, 0, 0),
- v3s16(-1, 0, 0),
- v3s16( 0, 0, 1),
- v3s16( 0, 0,-1)
- };
-
- u16 l = 0;
- u16 l1 = 0;
- for (u8 i = 0; i < 7; i++) {
- MapNode n1 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p + dirs[i]);
- l1 = getInteriorLight(n1, -4, nodedef);
- if (l1 > l)
- l = l1;
- }
- video::SColor c = MapBlock_LightColor(255, l, 0);
- data->m_highlight_mesh_color = c;
- std::vector<aabb3f> boxes = n.getSelectionBoxes(nodedef);
- TileSpec h_tile;
- h_tile.material_flags |= MATERIAL_FLAG_HIGHLIGHTED;
- h_tile.texture = tsrc->getTextureForMesh("halo.png",&h_tile.texture_id);
- v3f pos = intToFloat(p, BS);
- f32 d = 0.05 * BS;
- for (std::vector<aabb3f>::iterator i = boxes.begin();
- i != boxes.end(); i++) {
- aabb3f box = *i;
- box.MinEdge += v3f(-d, -d, -d) + pos;
- box.MaxEdge += v3f(d, d, d) + pos;
- makeCuboid(&collector, box, &h_tile, 1, c, NULL);
- }
- }
- }
}
diff --git a/src/content_mapnode.cpp b/src/content_mapnode.cpp
index 3a4a4652a..a6bf0a82d 100644
--- a/src/content_mapnode.cpp
+++ b/src/content_mapnode.cpp
@@ -167,69 +167,3 @@ void content_mapnode_get_name_id_mapping(NameIdMapping *nimap)
nimap->set(CONTENT_AIR, "air");
}
-class NewNameGetter
-{
-public:
- NewNameGetter()
- {
- old_to_new["CONTENT_STONE"] = "default:stone";
- old_to_new["CONTENT_WATER"] = "default:water_flowing";
- old_to_new["CONTENT_TORCH"] = "default:torch";
- old_to_new["CONTENT_WATERSOURCE"] = "default:water_source";
- old_to_new["CONTENT_SIGN_WALL"] = "default:sign_wall";
- old_to_new["CONTENT_CHEST"] = "default:chest";
- old_to_new["CONTENT_FURNACE"] = "default:furnace";
- old_to_new["CONTENT_LOCKABLE_CHEST"] = "default:locked_chest";
- old_to_new["CONTENT_FENCE"] = "default:wooden_fence";
- old_to_new["CONTENT_RAIL"] = "default:rail";
- old_to_new["CONTENT_LADDER"] = "default:ladder";
- old_to_new["CONTENT_LAVA"] = "default:lava_flowing";
- old_to_new["CONTENT_LAVASOURCE"] = "default:lava_source";
- old_to_new["CONTENT_GRASS"] = "default:dirt_with_grass";
- old_to_new["CONTENT_TREE"] = "default:tree";
- old_to_new["CONTENT_LEAVES"] = "default:leaves";
- old_to_new["CONTENT_GRASS_FOOTSTEPS"] = "default:dirt_with_grass_footsteps";
- old_to_new["CONTENT_MESE"] = "default:mese";
- old_to_new["CONTENT_MUD"] = "default:dirt";
- old_to_new["CONTENT_CLOUD"] = "default:cloud";
- old_to_new["CONTENT_COALSTONE"] = "default:coalstone";
- old_to_new["CONTENT_WOOD"] = "default:wood";
- old_to_new["CONTENT_SAND"] = "default:sand";
- old_to_new["CONTENT_COBBLE"] = "default:cobble";
- old_to_new["CONTENT_STEEL"] = "default:steel";
- old_to_new["CONTENT_GLASS"] = "default:glass";
- old_to_new["CONTENT_MOSSYCOBBLE"] = "default:mossycobble";
- old_to_new["CONTENT_GRAVEL"] = "default:gravel";
- old_to_new["CONTENT_SANDSTONE"] = "default:sandstone";
- old_to_new["CONTENT_CACTUS"] = "default:cactus";
- old_to_new["CONTENT_BRICK"] = "default:brick";
- old_to_new["CONTENT_CLAY"] = "default:clay";
- old_to_new["CONTENT_PAPYRUS"] = "default:papyrus";
- old_to_new["CONTENT_BOOKSHELF"] = "default:bookshelf";
- old_to_new["CONTENT_JUNGLETREE"] = "default:jungletree";
- old_to_new["CONTENT_JUNGLEGRASS"] = "default:junglegrass";
- old_to_new["CONTENT_NC"] = "default:nyancat";
- old_to_new["CONTENT_NC_RB"] = "default:nyancat_rainbow";
- old_to_new["CONTENT_APPLE"] = "default:apple";
- old_to_new["CONTENT_SAPLING"] = "default:sapling";
- // Just in case
- old_to_new["CONTENT_IGNORE"] = "ignore";
- old_to_new["CONTENT_AIR"] = "air";
- }
- std::string get(const std::string &old)
- {
- StringMap::const_iterator it = old_to_new.find(old);
- if (it == old_to_new.end())
- return "";
- return it->second;
- }
-private:
- StringMap old_to_new;
-};
-
-NewNameGetter newnamegetter;
-
-std::string content_mapnode_get_new_name(const std::string &oldname)
-{
- return newnamegetter.get(oldname);
-}
diff --git a/src/content_mapnode.h b/src/content_mapnode.h
index 5d68afe59..9fa4e6d13 100644
--- a/src/content_mapnode.h
+++ b/src/content_mapnode.h
@@ -34,8 +34,4 @@ MapNode mapnode_translate_to_internal(MapNode n_from, u8 version);
class NameIdMapping;
void content_mapnode_get_name_id_mapping(NameIdMapping *nimap);
-// Convert "CONTENT_STONE"-style names to dynamic ids
-std::string content_mapnode_get_new_name(const std::string &oldname);
-class INodeDefManager;
-
#endif
diff --git a/src/content_nodemeta.cpp b/src/content_nodemeta.cpp
index f504924f9..7f4264d8e 100644
--- a/src/content_nodemeta.cpp
+++ b/src/content_nodemeta.cpp
@@ -79,7 +79,7 @@ static bool content_nodemeta_deserialize_legacy_body(
inv->getList("0")->setName("main");
}
assert(inv->getList("main") && !inv->getList("0"));
-
+
meta->setString("formspec","size[8,9]"
"list[current_name;main;0,0;8,4;]"
"list[current_player;main;0,5;8,4;]");
@@ -96,7 +96,7 @@ static bool content_nodemeta_deserialize_legacy_body(
inv->getList("0")->setName("main");
}
assert(inv->getList("main") && !inv->getList("0"));
-
+
meta->setString("formspec","size[8,9]"
"list[current_name;main;0,0;8,4;]"
"list[current_player;main;0,5;8,4;]");
@@ -145,7 +145,7 @@ static bool content_nodemeta_deserialize_legacy_meta(
void content_nodemeta_deserialize_legacy(std::istream &is,
NodeMetadataList *meta, NodeTimerList *timers,
- IGameDef *gamedef)
+ IItemDefManager *item_def_mgr)
{
meta->clear();
timers->clear();
@@ -154,9 +154,9 @@ void content_nodemeta_deserialize_legacy(std::istream &is,
if(version > 1)
{
- infostream<<__FUNCTION_NAME<<": version "<<version<<" not supported"
+ infostream<<FUNCTION_NAME<<": version "<<version<<" not supported"
<<std::endl;
- throw SerializationError(__FUNCTION_NAME);
+ throw SerializationError(FUNCTION_NAME);
}
u16 count = readU16(is);
@@ -174,14 +174,14 @@ void content_nodemeta_deserialize_legacy(std::istream &is,
if(meta->get(p) != NULL)
{
- infostream<<"WARNING: "<<__FUNCTION_NAME<<": "
+ warningstream<<FUNCTION_NAME<<": "
<<"already set data at position"
<<"("<<p.X<<","<<p.Y<<","<<p.Z<<"): Ignoring."
<<std::endl;
continue;
}
- NodeMetadata *data = new NodeMetadata(gamedef);
+ NodeMetadata *data = new NodeMetadata(item_def_mgr);
bool need_timer = content_nodemeta_deserialize_legacy_meta(is, data);
meta->set(p, data);
diff --git a/src/content_nodemeta.h b/src/content_nodemeta.h
index 0b08bc6a1..ebc01d9a8 100644
--- a/src/content_nodemeta.h
+++ b/src/content_nodemeta.h
@@ -24,7 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
class NodeMetadataList;
class NodeTimerList;
-class IGameDef;
+class IItemDefManager;
/*
Legacy nodemeta definitions
@@ -32,7 +32,7 @@ class IGameDef;
void content_nodemeta_deserialize_legacy(std::istream &is,
NodeMetadataList *meta, NodeTimerList *timers,
- IGameDef *gamedef);
+ IItemDefManager *item_def_mgr);
void content_nodemeta_serialize_legacy(std::ostream &os, NodeMetadataList *meta);
diff --git a/src/content_sao.cpp b/src/content_sao.cpp
index add1726fc..53bf3154f 100644
--- a/src/content_sao.cpp
+++ b/src/content_sao.cpp
@@ -259,7 +259,7 @@ void LuaEntitySAO::step(float dtime, bool send_recommended)
else
{
if(m_prop.physical){
- core::aabbox3d<f32> box = m_prop.collisionbox;
+ aabb3f box = m_prop.collisionbox;
box.MinEdge *= BS;
box.MaxEdge *= BS;
collisionMoveResult moveresult;
@@ -269,7 +269,7 @@ void LuaEntitySAO::step(float dtime, bool send_recommended)
v3f p_acceleration = m_acceleration;
moveresult = collisionMoveSimple(m_env,m_env->getGameDef(),
pos_max_d, box, m_prop.stepheight, dtime,
- p_pos, p_velocity, p_acceleration,
+ &p_pos, &p_velocity, p_acceleration,
this, m_prop.collideWithObjects);
// Apply results
@@ -283,8 +283,20 @@ void LuaEntitySAO::step(float dtime, bool send_recommended)
}
if((m_prop.automatic_face_movement_dir) &&
- (fabs(m_velocity.Z) > 0.001 || fabs(m_velocity.X) > 0.001)){
- m_yaw = atan2(m_velocity.Z,m_velocity.X) * 180 / M_PI + m_prop.automatic_face_movement_dir_offset;
+ (fabs(m_velocity.Z) > 0.001 || fabs(m_velocity.X) > 0.001))
+ {
+ float optimal_yaw = atan2(m_velocity.Z,m_velocity.X) * 180 / M_PI
+ + m_prop.automatic_face_movement_dir_offset;
+ float max_rotation_delta =
+ dtime * m_prop.automatic_face_movement_max_rotation_per_sec;
+
+ if ((m_prop.automatic_face_movement_max_rotation_per_sec > 0) &&
+ (fabs(m_yaw - optimal_yaw) > max_rotation_delta)) {
+
+ m_yaw = optimal_yaw < m_yaw ? m_yaw - max_rotation_delta : m_yaw + max_rotation_delta;
+ } else {
+ m_yaw = optimal_yaw;
+ }
}
}
@@ -393,7 +405,7 @@ std::string LuaEntitySAO::getClientInitializationData(u16 protocol_version)
std::string LuaEntitySAO::getStaticData()
{
- verbosestream<<__FUNCTION_NAME<<std::endl;
+ verbosestream<<FUNCTION_NAME<<std::endl;
std::ostringstream os(std::ios::binary);
// version
writeU8(os, 1);
@@ -757,8 +769,6 @@ PlayerSAO::PlayerSAO(ServerEnvironment *env_, Player *player_, u16 peer_id_,
m_bone_position_sent(false),
m_attachment_parent_id(0),
m_attachment_sent(false),
- m_nametag_color(video::SColor(255, 255, 255, 255)),
- m_nametag_sent(false),
// public
m_physics_override_speed(1),
m_physics_override_jump(1),
@@ -776,7 +786,7 @@ PlayerSAO::PlayerSAO(ServerEnvironment *env_, Player *player_, u16 peer_id_,
m_prop.hp_max = PLAYER_MAX_HP;
m_prop.physical = false;
m_prop.weight = 75;
- m_prop.collisionbox = core::aabbox3d<f32>(-1/3.,-1.0,-1/3., 1/3.,1.0,1/3.);
+ m_prop.collisionbox = aabb3f(-1/3.,-1.0,-1/3., 1/3.,1.0,1/3.);
// start of default appearance, this should be overwritten by LUA
m_prop.visual = "upright_sprite";
m_prop.visual_size = v2f(1, 2);
@@ -821,8 +831,8 @@ void PlayerSAO::removingFromEnvironment()
{
m_player->setPlayerSAO(NULL);
m_player->peer_id = 0;
- m_env->savePlayer(m_player->getName());
- m_env->removePlayer(m_player->getName());
+ m_env->savePlayer((RemotePlayer*)m_player);
+ m_env->removePlayer(m_player);
}
}
@@ -857,7 +867,7 @@ std::string PlayerSAO::getClientInitializationData(u16 protocol_version)
os<<serializeLongString(gob_cmd_update_physics_override(m_physics_override_speed,
m_physics_override_jump, m_physics_override_gravity, m_physics_override_sneak,
m_physics_override_sneak_glitch)); // 5
- os << serializeLongString(gob_cmd_update_nametag_attributes(m_nametag_color)); // 6
+ os << serializeLongString(gob_cmd_update_nametag_attributes(m_prop.nametag_color)); // 6 (GENERIC_CMD_UPDATE_NAMETAG_ATTRIBUTES) : Deprecated, for backwards compatibility only.
}
else
{
@@ -1012,14 +1022,6 @@ void PlayerSAO::step(float dtime, bool send_recommended)
ActiveObjectMessage aom(getId(), true, str);
m_messages_out.push(aom);
}
-
- if (m_nametag_sent == false) {
- m_nametag_sent = true;
- std::string str = gob_cmd_update_nametag_attributes(m_nametag_color);
- // create message and add to list
- ActiveObjectMessage aom(getId(), true, str);
- m_messages_out.push(aom);
- }
}
void PlayerSAO::setBasePosition(const v3f &position)
@@ -1270,17 +1272,6 @@ void PlayerSAO::notifyObjectPropertiesModified()
m_properties_sent = false;
}
-void PlayerSAO::setNametagColor(video::SColor color)
-{
- m_nametag_color = color;
- m_nametag_sent = false;
-}
-
-video::SColor PlayerSAO::getNametagColor()
-{
- return m_nametag_color;
-}
-
Inventory* PlayerSAO::getInventory()
{
return m_inventory;
@@ -1396,4 +1387,3 @@ bool PlayerSAO::getCollisionBox(aabb3f *toset) {
bool PlayerSAO::collideWithObjects(){
return true;
}
-
diff --git a/src/content_sao.h b/src/content_sao.h
index 1f0a68cd8..44d40d332 100644
--- a/src/content_sao.h
+++ b/src/content_sao.h
@@ -91,13 +91,13 @@ private:
std::string m_init_state;
bool m_registered;
struct ObjectProperties m_prop;
-
+
s16 m_hp;
v3f m_velocity;
v3f m_acceleration;
float m_yaw;
ItemGroupList m_armor_groups;
-
+
bool m_properties_sent;
float m_last_sent_yaw;
v3f m_last_sent_position;
@@ -213,8 +213,6 @@ public:
std::set<int> getAttachmentChildIds();
ObjectProperties* accessObjectProperties();
void notifyObjectPropertiesModified();
- void setNametagColor(video::SColor color);
- video::SColor getNametagColor();
/*
Inventory interface
@@ -292,7 +290,7 @@ public:
private:
std::string getPropertyPacket();
-
+
Player *m_player;
u16 m_peer_id;
Inventory *m_inventory;
@@ -333,8 +331,6 @@ private:
v3f m_attachment_rotation;
bool m_attachment_sent;
- video::SColor m_nametag_color;
- bool m_nametag_sent;
public:
float m_physics_override_speed;
@@ -346,4 +342,3 @@ public:
};
#endif
-
diff --git a/src/convert_json.cpp b/src/convert_json.cpp
index e03508e21..e548c45f5 100644
--- a/src/convert_json.cpp
+++ b/src/convert_json.cpp
@@ -52,7 +52,13 @@ Json::Value fetchJsonValue(const std::string &url,
if (!reader.parse(stream, root)) {
errorstream << "URL: " << url << std::endl;
errorstream << "Failed to parse json data " << reader.getFormattedErrorMessages();
- errorstream << "data: \"" << fetch_result.data << "\"" << std::endl;
+ if (fetch_result.data.size() > 100) {
+ errorstream << "Data (" << fetch_result.data.size()
+ << " bytes) printed to warningstream." << std::endl;
+ warningstream << "data: \"" << fetch_result.data << "\"" << std::endl;
+ } else {
+ errorstream << "data: \"" << fetch_result.data << "\"" << std::endl;
+ }
return Json::Value();
}
diff --git a/src/craftdef.cpp b/src/craftdef.cpp
index 409481e64..d3f1edaf9 100644
--- a/src/craftdef.cpp
+++ b/src/craftdef.cpp
@@ -29,7 +29,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "util/serialize.h"
#include "util/string.h"
#include "util/numeric.h"
-#include "strfnd.h"
+#include "util/strfnd.h"
#include "exceptions.h"
inline bool isGroupRecipeStr(const std::string &rec_name)
@@ -90,7 +90,7 @@ static bool inputItemMatchesRecipe(const std::string &inp_name,
all_groups_match = false;
break;
}
- } while (!f.atend());
+ } while (!f.at_end());
if (all_groups_match)
return true;
}
@@ -214,7 +214,7 @@ static void craftDecrementOrReplaceInput(CraftInput &input,
for (std::vector<std::pair<std::string, std::string> >::iterator
j = pairs.begin();
j != pairs.end(); ++j) {
- if (item.name == craftGetItemName(j->first, gamedef)) {
+ if (inputItemMatchesRecipe(item.name, j->first, gamedef->idef())) {
if (item.count == 1) {
item.deSerialize(j->second, gamedef->idef());
found_replacement = true;
@@ -964,10 +964,10 @@ public:
{
std::ostringstream os(std::ios::binary);
os << "Crafting definitions:\n";
- for (int type = 0; type <= craft_hash_type_max; type++) {
+ for (int type = 0; type <= craft_hash_type_max; ++type) {
for (std::map<u64, std::vector<CraftDefinition*> >::const_iterator
it = (m_craft_defs[type]).begin();
- it != (m_craft_defs[type]).end(); it++) {
+ it != (m_craft_defs[type]).end(); ++it) {
for (std::vector<CraftDefinition*>::size_type i = 0;
i < it->second.size(); i++) {
os << "type " << type
@@ -992,10 +992,10 @@ public:
}
virtual void clear()
{
- for (int type = 0; type <= craft_hash_type_max; type++) {
+ for (int type = 0; type <= craft_hash_type_max; ++type) {
for (std::map<u64, std::vector<CraftDefinition*> >::iterator
it = m_craft_defs[type].begin();
- it != m_craft_defs[type].end(); it++) {
+ it != m_craft_defs[type].end(); ++it) {
for (std::vector<CraftDefinition*>::iterator
iit = it->second.begin();
iit != it->second.end(); ++iit) {
diff --git a/src/database-dummy.cpp b/src/database-dummy.cpp
index 2e5de5ed1..b38db1fb9 100644
--- a/src/database-dummy.cpp
+++ b/src/database-dummy.cpp
@@ -47,6 +47,7 @@ bool Database_Dummy::deleteBlock(const v3s16 &pos)
void Database_Dummy::listAllLoadableBlocks(std::vector<v3s16> &dst)
{
+ dst.reserve(m_database.size());
for (std::map<s64, std::string>::const_iterator x = m_database.begin();
x != m_database.end(); ++x) {
dst.push_back(getIntegerAsBlock(x->first));
diff --git a/src/database-leveldb.cpp b/src/database-leveldb.cpp
index e895354a4..acd0fd1eb 100644
--- a/src/database-leveldb.cpp
+++ b/src/database-leveldb.cpp
@@ -57,7 +57,7 @@ bool Database_LevelDB::saveBlock(const v3s16 &pos, const std::string &data)
leveldb::Status status = m_database->Put(leveldb::WriteOptions(),
i64tos(getBlockAsInteger(pos)), data);
if (!status.ok()) {
- errorstream << "WARNING: saveBlock: LevelDB error saving block "
+ warningstream << "saveBlock: LevelDB error saving block "
<< PP(pos) << ": " << status.ToString() << std::endl;
return false;
}
@@ -82,7 +82,7 @@ bool Database_LevelDB::deleteBlock(const v3s16 &pos)
leveldb::Status status = m_database->Delete(leveldb::WriteOptions(),
i64tos(getBlockAsInteger(pos)));
if (!status.ok()) {
- errorstream << "WARNING: deleteBlock: LevelDB error deleting block "
+ warningstream << "deleteBlock: LevelDB error deleting block "
<< PP(pos) << ": " << status.ToString() << std::endl;
return false;
}
diff --git a/src/database-redis.cpp b/src/database-redis.cpp
index cc4e5bade..498e9d39a 100644
--- a/src/database-redis.cpp
+++ b/src/database-redis.cpp
@@ -84,15 +84,15 @@ bool Database_Redis::saveBlock(const v3s16 &pos, const std::string &data)
redisReply *reply = static_cast<redisReply *>(redisCommand(ctx, "HSET %s %s %b",
hash.c_str(), tmp.c_str(), data.c_str(), data.size()));
if (!reply) {
- errorstream << "WARNING: saveBlock: redis command 'HSET' failed on "
+ warningstream << "saveBlock: redis command 'HSET' failed on "
"block " << PP(pos) << ": " << ctx->errstr << std::endl;
freeReplyObject(reply);
return false;
}
if (reply->type == REDIS_REPLY_ERROR) {
- errorstream << "WARNING: saveBlock: saving block " << PP(pos)
- << " failed: " << reply->str << std::endl;
+ warningstream << "saveBlock: saving block " << PP(pos)
+ << " failed: " << std::string(reply->str, reply->len) << std::endl;
freeReplyObject(reply);
return false;
}
@@ -118,12 +118,26 @@ std::string Database_Redis::loadBlock(const v3s16 &pos)
freeReplyObject(reply);
return str;
}
- case REDIS_REPLY_ERROR:
- errorstream << "WARNING: loadBlock: loading block " << PP(pos)
- << " failed: " << reply->str << std::endl;
+ case REDIS_REPLY_ERROR: {
+ std::string errstr(reply->str, reply->len);
+ freeReplyObject(reply);
+ errorstream << "loadBlock: loading block " << PP(pos)
+ << " failed: " << errstr << std::endl;
+ throw FileNotGoodException(std::string(
+ "Redis command 'HGET %s %s' errored: ") + errstr);
+ }
+ case REDIS_REPLY_NIL: {
+ // block not found in database
+ freeReplyObject(reply);
+ return "";
+ }
}
+ errorstream << "loadBlock: loading block " << PP(pos)
+ << " returned invalid reply type " << reply->type
+ << ": " << std::string(reply->str, reply->len) << std::endl;
freeReplyObject(reply);
- return "";
+ throw FileNotGoodException(std::string(
+ "Redis command 'HGET %s %s' gave invalid reply."));
}
bool Database_Redis::deleteBlock(const v3s16 &pos)
@@ -136,8 +150,8 @@ bool Database_Redis::deleteBlock(const v3s16 &pos)
throw FileNotGoodException(std::string(
"Redis command 'HDEL %s %s' failed: ") + ctx->errstr);
} else if (reply->type == REDIS_REPLY_ERROR) {
- errorstream << "WARNING: deleteBlock: deleting block " << PP(pos)
- << " failed: " << reply->str << std::endl;
+ warningstream << "deleteBlock: deleting block " << PP(pos)
+ << " failed: " << std::string(reply->str, reply->len) << std::endl;
freeReplyObject(reply);
return false;
}
@@ -155,13 +169,16 @@ void Database_Redis::listAllLoadableBlocks(std::vector<v3s16> &dst)
}
switch (reply->type) {
case REDIS_REPLY_ARRAY:
+ dst.reserve(reply->elements);
for (size_t i = 0; i < reply->elements; i++) {
assert(reply->element[i]->type == REDIS_REPLY_STRING);
dst.push_back(getIntegerAsBlock(stoi64(reply->element[i]->str)));
}
+ break;
case REDIS_REPLY_ERROR:
throw FileNotGoodException(std::string(
- "Failed to get keys from database: ") + reply->str);
+ "Failed to get keys from database: ") +
+ std::string(reply->str, reply->len));
}
freeReplyObject(reply);
}
diff --git a/src/database-sqlite3.cpp b/src/database-sqlite3.cpp
index 84b1a7122..56f937bf2 100644
--- a/src/database-sqlite3.cpp
+++ b/src/database-sqlite3.cpp
@@ -31,31 +31,79 @@ SQLite format specification:
#include "filesys.h"
#include "exceptions.h"
#include "settings.h"
+#include "porting.h"
#include "util/string.h"
#include <cassert>
+// When to print messages when the database is being held locked by another process
+// Note: I've seen occasional delays of over 250ms while running minetestmapper.
+#define BUSY_INFO_TRESHOLD 100 // Print first informational message after 100ms.
+#define BUSY_WARNING_TRESHOLD 250 // Print warning message after 250ms. Lag is increased.
+#define BUSY_ERROR_TRESHOLD 1000 // Print error message after 1000ms. Significant lag.
+#define BUSY_FATAL_TRESHOLD 3000 // Allow SQLITE_BUSY to be returned, which will cause a minetest crash.
+#define BUSY_ERROR_INTERVAL 10000 // Safety net: report again every 10 seconds
-#define SQLRES(s, r) \
+
+#define SQLRES(s, r, m) \
if ((s) != (r)) { \
- throw FileNotGoodException(std::string(\
- "SQLite3 database error (" \
- __FILE__ ":" TOSTRING(__LINE__) \
- "): ") +\
+ throw FileNotGoodException(std::string(m) + ": " +\
sqlite3_errmsg(m_database)); \
}
-#define SQLOK(s) SQLRES(s, SQLITE_OK)
+#define SQLOK(s, m) SQLRES(s, SQLITE_OK, m)
#define PREPARE_STATEMENT(name, query) \
- SQLOK(sqlite3_prepare_v2(m_database, query, -1, &m_stmt_##name, NULL))
+ SQLOK(sqlite3_prepare_v2(m_database, query, -1, &m_stmt_##name, NULL),\
+ "Failed to prepare query '" query "'")
#define FINALIZE_STATEMENT(statement) \
- if (sqlite3_finalize(statement) != SQLITE_OK) { \
- throw FileNotGoodException(std::string( \
- "SQLite3: Failed to finalize " #statement ": ") + \
- sqlite3_errmsg(m_database)); \
+ SQLOK(sqlite3_finalize(statement), "Failed to finalize " #statement)
+
+int Database_SQLite3::busyHandler(void *data, int count)
+{
+ s64 &first_time = reinterpret_cast<s64 *>(data)[0];
+ s64 &prev_time = reinterpret_cast<s64 *>(data)[1];
+ s64 cur_time = getTimeMs();
+
+ if (count == 0) {
+ first_time = cur_time;
+ prev_time = first_time;
+ } else {
+ while (cur_time < prev_time)
+ cur_time += s64(1)<<32;
}
+ if (cur_time - first_time < BUSY_INFO_TRESHOLD) {
+ ; // do nothing
+ } else if (cur_time - first_time >= BUSY_INFO_TRESHOLD &&
+ prev_time - first_time < BUSY_INFO_TRESHOLD) {
+ infostream << "SQLite3 database has been locked for "
+ << cur_time - first_time << " ms." << std::endl;
+ } else if (cur_time - first_time >= BUSY_WARNING_TRESHOLD &&
+ prev_time - first_time < BUSY_WARNING_TRESHOLD) {
+ warningstream << "SQLite3 database has been locked for "
+ << cur_time - first_time << " ms." << std::endl;
+ } else if (cur_time - first_time >= BUSY_ERROR_TRESHOLD &&
+ prev_time - first_time < BUSY_ERROR_TRESHOLD) {
+ errorstream << "SQLite3 database has been locked for "
+ << cur_time - first_time << " ms; this causes lag." << std::endl;
+ } else if (cur_time - first_time >= BUSY_FATAL_TRESHOLD &&
+ prev_time - first_time < BUSY_FATAL_TRESHOLD) {
+ errorstream << "SQLite3 database has been locked for "
+ << cur_time - first_time << " ms - giving up!" << std::endl;
+ } else if ((cur_time - first_time) / BUSY_ERROR_INTERVAL !=
+ (prev_time - first_time) / BUSY_ERROR_INTERVAL) {
+ // Safety net: keep reporting every BUSY_ERROR_INTERVAL
+ errorstream << "SQLite3 database has been locked for "
+ << (cur_time - first_time) / 1000 << " seconds!" << std::endl;
+ }
+
+ prev_time = cur_time;
+
+ // Make sqlite transaction fail if delay exceeds BUSY_FATAL_TRESHOLD
+ return cur_time - first_time < BUSY_FATAL_TRESHOLD;
+}
+
Database_SQLite3::Database_SQLite3(const std::string &savedir) :
m_initialized(false),
@@ -72,13 +120,15 @@ Database_SQLite3::Database_SQLite3(const std::string &savedir) :
void Database_SQLite3::beginSave() {
verifyDatabase();
- SQLRES(sqlite3_step(m_stmt_begin), SQLITE_DONE);
+ SQLRES(sqlite3_step(m_stmt_begin), SQLITE_DONE,
+ "Failed to start SQLite3 transaction");
sqlite3_reset(m_stmt_begin);
}
void Database_SQLite3::endSave() {
verifyDatabase();
- SQLRES(sqlite3_step(m_stmt_end), SQLITE_DONE);
+ SQLRES(sqlite3_step(m_stmt_end), SQLITE_DONE,
+ "Failed to commit SQLite3 transaction");
sqlite3_reset(m_stmt_end);
}
@@ -99,13 +149,12 @@ void Database_SQLite3::openDatabase()
bool needs_create = !fs::PathExists(dbp);
- if (sqlite3_open_v2(dbp.c_str(), &m_database,
- SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
- NULL) != SQLITE_OK) {
- errorstream << "SQLite3 database failed to open: "
- << sqlite3_errmsg(m_database) << std::endl;
- throw FileNotGoodException("Cannot open database file");
- }
+ SQLOK(sqlite3_open_v2(dbp.c_str(), &m_database,
+ SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL),
+ std::string("Failed to open SQLite3 database file ") + dbp);
+
+ SQLOK(sqlite3_busy_handler(m_database, Database_SQLite3::busyHandler,
+ m_busy_handler_data), "Failed to set SQLite3 busy handler");
if (needs_create) {
createDatabase();
@@ -113,7 +162,8 @@ void Database_SQLite3::openDatabase()
std::string query_str = std::string("PRAGMA synchronous = ")
+ itos(g_settings->getU16("sqlite_synchronous"));
- SQLOK(sqlite3_exec(m_database, query_str.c_str(), NULL, NULL, NULL));
+ SQLOK(sqlite3_exec(m_database, query_str.c_str(), NULL, NULL, NULL),
+ "Failed to modify sqlite3 synchronous mode");
}
void Database_SQLite3::verifyDatabase()
@@ -140,7 +190,8 @@ void Database_SQLite3::verifyDatabase()
inline void Database_SQLite3::bindPos(sqlite3_stmt *stmt, const v3s16 &pos, int index)
{
- SQLOK(sqlite3_bind_int64(stmt, index, getBlockAsInteger(pos)));
+ SQLOK(sqlite3_bind_int64(stmt, index, getBlockAsInteger(pos)),
+ "Internal error: failed to bind query at " __FILE__ ":" TOSTRING(__LINE__));
}
bool Database_SQLite3::deleteBlock(const v3s16 &pos)
@@ -153,7 +204,7 @@ bool Database_SQLite3::deleteBlock(const v3s16 &pos)
sqlite3_reset(m_stmt_delete);
if (!good) {
- errorstream << "WARNING: deleteBlock: Block failed to delete "
+ warningstream << "deleteBlock: Block failed to delete "
<< PP(pos) << ": " << sqlite3_errmsg(m_database) << std::endl;
}
return good;
@@ -177,9 +228,10 @@ bool Database_SQLite3::saveBlock(const v3s16 &pos, const std::string &data)
#endif
bindPos(m_stmt_write, pos);
- SQLOK(sqlite3_bind_blob(m_stmt_write, 2, data.data(), data.size(), NULL));
+ SQLOK(sqlite3_bind_blob(m_stmt_write, 2, data.data(), data.size(), NULL),
+ "Internal error: failed to bind query at " __FILE__ ":" TOSTRING(__LINE__));
- SQLRES(sqlite3_step(m_stmt_write), SQLITE_DONE)
+ SQLRES(sqlite3_step(m_stmt_write), SQLITE_DONE, "Failed to save block")
sqlite3_reset(m_stmt_write);
return true;
@@ -217,7 +269,8 @@ void Database_SQLite3::createDatabase()
" `pos` INT PRIMARY KEY,\n"
" `data` BLOB\n"
");\n",
- NULL, NULL, NULL));
+ NULL, NULL, NULL),
+ "Failed to create database table");
}
void Database_SQLite3::listAllLoadableBlocks(std::vector<v3s16> &dst)
@@ -239,10 +292,6 @@ Database_SQLite3::~Database_SQLite3()
FINALIZE_STATEMENT(m_stmt_end)
FINALIZE_STATEMENT(m_stmt_delete)
- if (sqlite3_close(m_database) != SQLITE_OK) {
- errorstream << "Database_SQLite3::~Database_SQLite3(): "
- << "Failed to close database: "
- << sqlite3_errmsg(m_database) << std::endl;
- }
+ SQLOK(sqlite3_close(m_database), "Failed to close database");
}
diff --git a/src/database-sqlite3.h b/src/database-sqlite3.h
index a775742be..04a1825d9 100644
--- a/src/database-sqlite3.h
+++ b/src/database-sqlite3.h
@@ -63,6 +63,10 @@ private:
sqlite3_stmt *m_stmt_delete;
sqlite3_stmt *m_stmt_begin;
sqlite3_stmt *m_stmt_end;
+
+ s64 m_busy_handler_data[2];
+
+ static int busyHandler(void *data, int count);
};
#endif
diff --git a/src/debug.cpp b/src/debug.cpp
index ae2ffadc3..8647160b1 100644
--- a/src/debug.cpp
+++ b/src/debug.cpp
@@ -26,8 +26,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <stdlib.h>
#include <cstring>
#include <map>
-#include "jthread/jmutex.h"
-#include "jthread/jmutexautolock.h"
+#include <sstream>
+#include "threading/mutex.h"
+#include "threading/mutex_auto_lock.h"
#include "config.h"
#ifdef _MSC_VER
@@ -36,98 +37,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "filesys.h"
#endif
-/*
- Debug output
-*/
-
-#define DEBUGSTREAM_COUNT 2
-
-FILE *g_debugstreams[DEBUGSTREAM_COUNT] = {stderr, NULL};
-
-#define DEBUGPRINT(...)\
-{\
- for(int i=0; i<DEBUGSTREAM_COUNT; i++)\
- {\
- if(g_debugstreams[i] != NULL){\
- fprintf(g_debugstreams[i], __VA_ARGS__);\
- fflush(g_debugstreams[i]);\
- }\
- }\
-}
-
-void debugstreams_init(bool disable_stderr, const char *filename)
-{
- if(disable_stderr)
- g_debugstreams[0] = NULL;
- else
- g_debugstreams[0] = stderr;
-
- if(filename)
- g_debugstreams[1] = fopen(filename, "a");
-
- if(g_debugstreams[1])
- {
- fprintf(g_debugstreams[1], "\n\n-------------\n");
- fprintf(g_debugstreams[1], " Separator \n");
- fprintf(g_debugstreams[1], "-------------\n\n");
- }
-}
-
-void debugstreams_deinit()
-{
- if(g_debugstreams[1] != NULL)
- fclose(g_debugstreams[1]);
-}
-
-class Debugbuf : public std::streambuf
-{
-public:
- Debugbuf(bool disable_stderr)
- {
- m_disable_stderr = disable_stderr;
- }
-
- int overflow(int c)
- {
- for(int i=0; i<DEBUGSTREAM_COUNT; i++)
- {
- if(g_debugstreams[i] == stderr && m_disable_stderr)
- continue;
- if(g_debugstreams[i] != NULL)
- (void)fwrite(&c, 1, 1, g_debugstreams[i]);
- //TODO: Is this slow?
- fflush(g_debugstreams[i]);
- }
-
- return c;
- }
- std::streamsize xsputn(const char *s, std::streamsize n)
- {
-#ifdef __ANDROID__
- __android_log_print(ANDROID_LOG_VERBOSE, PROJECT_NAME, "%s", s);
+#if USE_CURSES
+ #include "terminal_chat_console.h"
#endif
- for(int i=0; i<DEBUGSTREAM_COUNT; i++)
- {
- if(g_debugstreams[i] == stderr && m_disable_stderr)
- continue;
- if(g_debugstreams[i] != NULL)
- (void)fwrite(s, 1, n, g_debugstreams[i]);
- //TODO: Is this slow?
- fflush(g_debugstreams[i]);
- }
-
- return n;
- }
-
-private:
- bool m_disable_stderr;
-};
-
-Debugbuf debugbuf(false);
-std::ostream dstream(&debugbuf);
-Debugbuf debugbuf_no_stderr(true);
-std::ostream dstream_no_stderr(&debugbuf_no_stderr);
-Nullstream dummyout;
/*
Assert
@@ -136,15 +48,16 @@ Nullstream dummyout;
void sanity_check_fn(const char *assertion, const char *file,
unsigned int line, const char *function)
{
- DEBUGPRINT("\nIn thread %lx:\n"
- "%s:%u: %s: An engine assumption '%s' failed.\n",
- (unsigned long)get_current_thread_id(),
- file, line, function, assertion);
+#if USE_CURSES
+ g_term_console.stopAndWaitforThread();
+#endif
- debug_stacks_print();
+ errorstream << std::endl << "In thread " << std::hex
+ << thr_get_current_thread_id() << ":" << std::endl;
+ errorstream << file << ":" << line << ": " << function
+ << ": An engine assumption '" << assertion << "' failed." << std::endl;
- if(g_debugstreams[1])
- fclose(g_debugstreams[1]);
+ debug_stacks_print_to(errorstream);
abort();
}
@@ -152,15 +65,16 @@ void sanity_check_fn(const char *assertion, const char *file,
void fatal_error_fn(const char *msg, const char *file,
unsigned int line, const char *function)
{
- DEBUGPRINT("\nIn thread %lx:\n"
- "%s:%u: %s: A fatal error occurred: %s\n",
- (unsigned long)get_current_thread_id(),
- file, line, function, msg);
+#if USE_CURSES
+ g_term_console.stopAndWaitforThread();
+#endif
- debug_stacks_print();
+ errorstream << std::endl << "In thread " << std::hex
+ << thr_get_current_thread_id() << ":" << std::endl;
+ errorstream << file << ":" << line << ": " << function
+ << ": A fatal error occured: " << msg << std::endl;
- if(g_debugstreams[1])
- fclose(g_debugstreams[1]);
+ debug_stacks_print_to(errorstream);
abort();
}
@@ -191,8 +105,10 @@ DebugStack::DebugStack(threadid_t id)
void DebugStack::print(FILE *file, bool everything)
{
- fprintf(file, "DEBUG STACK FOR THREAD %lx:\n",
- (unsigned long)threadid);
+ std::ostringstream os;
+ os << threadid;
+ fprintf(file, "DEBUG STACK FOR THREAD %s:\n",
+ os.str().c_str());
for(int i=0; i<stack_max_i; i++)
{
@@ -211,7 +127,7 @@ void DebugStack::print(FILE *file, bool everything)
void DebugStack::print(std::ostream &os, bool everything)
{
- os<<"DEBUG STACK FOR THREAD "<<(unsigned long)threadid<<": "<<std::endl;
+ os<<"DEBUG STACK FOR THREAD "<<threadid<<": "<<std::endl;
for(int i=0; i<stack_max_i; i++)
{
@@ -228,8 +144,15 @@ void DebugStack::print(std::ostream &os, bool everything)
os<<"Probably overflown."<<std::endl;
}
+// Note: Using pthread_t (that is, threadid_t on POSIX platforms) as the key to
+// a std::map is naughty. Formally, a pthread_t may only be compared using
+// pthread_equal() - pthread_t lacks the well-ordered property needed for
+// comparisons in binary searches. This should be fixed at some point by
+// defining a custom comparator with an arbitrary but stable ordering of
+// pthread_t, but it isn't too important since none of our supported platforms
+// implement pthread_t as a non-ordinal type.
std::map<threadid_t, DebugStack*> g_debug_stacks;
-JMutex g_debug_stacks_mutex;
+Mutex g_debug_stacks_mutex;
void debug_stacks_init()
{
@@ -237,7 +160,7 @@ void debug_stacks_init()
void debug_stacks_print_to(std::ostream &os)
{
- JMutexAutoLock lock(g_debug_stacks_mutex);
+ MutexAutoLock lock(g_debug_stacks_mutex);
os<<"Debug stacks:"<<std::endl;
@@ -251,29 +174,14 @@ void debug_stacks_print_to(std::ostream &os)
void debug_stacks_print()
{
- JMutexAutoLock lock(g_debug_stacks_mutex);
-
- DEBUGPRINT("Debug stacks:\n");
-
- for(std::map<threadid_t, DebugStack*>::iterator
- i = g_debug_stacks.begin();
- i != g_debug_stacks.end(); ++i)
- {
- DebugStack *stack = i->second;
-
- for(int i=0; i<DEBUGSTREAM_COUNT; i++)
- {
- if(g_debugstreams[i] != NULL)
- stack->print(g_debugstreams[i], true);
- }
- }
+ debug_stacks_print_to(errorstream);
}
DebugStacker::DebugStacker(const char *text)
{
- threadid_t threadid = get_current_thread_id();
+ threadid_t threadid = thr_get_current_thread_id();
- JMutexAutoLock lock(g_debug_stacks_mutex);
+ MutexAutoLock lock(g_debug_stacks_mutex);
std::map<threadid_t, DebugStack*>::iterator n;
n = g_debug_stacks.find(threadid);
@@ -307,7 +215,7 @@ DebugStacker::DebugStacker(const char *text)
DebugStacker::~DebugStacker()
{
- JMutexAutoLock lock(g_debug_stacks_mutex);
+ MutexAutoLock lock(g_debug_stacks_mutex);
if(m_overflowed == true)
return;
diff --git a/src/debug.h b/src/debug.h
index 9684aa2df..2098942db 100644
--- a/src/debug.h
+++ b/src/debug.h
@@ -24,6 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <exception>
#include <assert.h>
#include "gettime.h"
+#include "log.h"
#if (defined(WIN32) || defined(_WIN32_WCE))
#define WIN32_LEAN_AND_MEAN
@@ -34,15 +35,15 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#ifdef _MSC_VER
#include <eh.h>
#endif
- #define __NORETURN __declspec(noreturn)
- #define __FUNCTION_NAME __FUNCTION__
+ #define NORETURN __declspec(noreturn)
+ #define FUNCTION_NAME __FUNCTION__
#else
- #define __NORETURN __attribute__ ((__noreturn__))
- #define __FUNCTION_NAME __PRETTY_FUNCTION__
+ #define NORETURN __attribute__ ((__noreturn__))
+ #define FUNCTION_NAME __PRETTY_FUNCTION__
#endif
// Whether to catch all std::exceptions.
-// Assert will be called on such an event.
+// When "catching", the program will abort with an error message.
// In debug mode, leave these for the debugger and don't catch them.
#ifdef NDEBUG
#define CATCH_UNHANDLED_EXCEPTIONS 1
@@ -50,42 +51,18 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#define CATCH_UNHANDLED_EXCEPTIONS 0
#endif
-/*
- Debug output
-*/
-
-#define DTIME (getTimestamp()+": ")
-
-extern void debugstreams_init(bool disable_stderr, const char *filename);
-extern void debugstreams_deinit();
-
-// This is used to redirect output to /dev/null
-class Nullstream : public std::ostream {
-public:
- Nullstream():
- std::ostream(0)
- {
- }
-private:
-};
-
-extern std::ostream dstream;
-extern std::ostream dstream_no_stderr;
-extern Nullstream dummyout;
-
-
/* Abort program execution immediately
*/
-__NORETURN extern void fatal_error_fn(
+NORETURN extern void fatal_error_fn(
const char *msg, const char *file,
unsigned int line, const char *function);
#define FATAL_ERROR(msg) \
- fatal_error_fn((msg), __FILE__, __LINE__, __FUNCTION_NAME)
+ fatal_error_fn((msg), __FILE__, __LINE__, FUNCTION_NAME)
#define FATAL_ERROR_IF(expr, msg) \
((expr) \
- ? fatal_error_fn((msg), __FILE__, __LINE__, __FUNCTION_NAME) \
+ ? fatal_error_fn((msg), __FILE__, __LINE__, FUNCTION_NAME) \
: (void)(0))
/*
@@ -94,14 +71,14 @@ __NORETURN extern void fatal_error_fn(
defined)
*/
-__NORETURN extern void sanity_check_fn(
+NORETURN extern void sanity_check_fn(
const char *assertion, const char *file,
unsigned int line, const char *function);
#define SANITY_CHECK(expr) \
((expr) \
? (void)(0) \
- : sanity_check_fn(#expr, __FILE__, __LINE__, __FUNCTION_NAME))
+ : sanity_check_fn(#expr, __FILE__, __LINE__, FUNCTION_NAME))
#define sanity_check(expr) SANITY_CHECK(expr)
@@ -145,16 +122,16 @@ private:
#if CATCH_UNHANDLED_EXCEPTIONS == 1
#define BEGIN_DEBUG_EXCEPTION_HANDLER try {
- #define END_DEBUG_EXCEPTION_HANDLER(logstream) \
- } catch (std::exception &e) { \
- logstream << "ERROR: An unhandled exception occurred: " \
- << e.what() << std::endl; \
- assert(0); \
+ #define END_DEBUG_EXCEPTION_HANDLER \
+ } catch (std::exception &e) { \
+ errorstream << "An unhandled exception occurred: " \
+ << e.what() << std::endl; \
+ FATAL_ERROR(e.what()); \
}
#else
// Dummy ones
#define BEGIN_DEBUG_EXCEPTION_HANDLER
- #define END_DEBUG_EXCEPTION_HANDLER(logstream)
+ #define END_DEBUG_EXCEPTION_HANDLER
#endif
#endif // DEBUG_HEADER
diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp
index f0b02b2d9..e8b652c17 100644
--- a/src/defaultsettings.cpp
+++ b/src/defaultsettings.cpp
@@ -33,6 +33,7 @@ void set_default_settings(Settings *settings)
// Client stuff
settings->setDefault("remote_port", "30000");
settings->setDefault("keymap_forward", "KEY_KEY_W");
+ settings->setDefault("keymap_autorun", "");
settings->setDefault("keymap_backward", "KEY_KEY_S");
settings->setDefault("keymap_left", "KEY_KEY_A");
settings->setDefault("keymap_right", "KEY_KEY_D");
@@ -88,12 +89,9 @@ void set_default_settings(Settings *settings)
settings->setDefault("show_debug", "true");
#endif
- settings->setDefault("wanted_fps", "30");
settings->setDefault("fps_max", "60");
settings->setDefault("pause_fps_max", "20");
- // A bit more than the server will send around the player, to make fog blend well
- settings->setDefault("viewing_range_nodes_max", "240");
- settings->setDefault("viewing_range_nodes_min", "35");
+ settings->setDefault("viewing_range", "100");
settings->setDefault("map_generation_limit", "31000");
settings->setDefault("screenW", "800");
settings->setDefault("screenH", "600");
@@ -108,7 +106,6 @@ void set_default_settings(Settings *settings)
settings->setDefault("enable_fog", "true");
settings->setDefault("fov", "72");
settings->setDefault("view_bobbing", "true");
- settings->setDefault("new_style_water", "false");
settings->setDefault("leaves_style", "fancy");
settings->setDefault("connected_glass", "false");
settings->setDefault("smooth_lighting", "true");
@@ -126,6 +123,8 @@ void set_default_settings(Settings *settings)
settings->setDefault("invert_mouse", "false");
settings->setDefault("enable_clouds", "true");
settings->setDefault("screenshot_path", ".");
+ settings->setDefault("screenshot_format", "png");
+ settings->setDefault("screenshot_quality", "0");
settings->setDefault("view_bobbing_amount", "1.0");
settings->setDefault("fall_bobbing_amount", "0.0");
settings->setDefault("enable_3d_clouds", "true");
@@ -136,7 +135,9 @@ void set_default_settings(Settings *settings)
settings->setDefault("console_color", "(0,0,0)");
settings->setDefault("console_alpha", "200");
settings->setDefault("selectionbox_color", "(0,0,0)");
- settings->setDefault("enable_node_highlighting", "false");
+ settings->setDefault("selectionbox_width", "2");
+ settings->setDefault("inventory_items_animations", "false");
+ settings->setDefault("node_highlighting", "box");
settings->setDefault("crosshair_color", "(255,255,255)");
settings->setDefault("crosshair_alpha", "255");
settings->setDefault("hud_scaling", "1.0");
@@ -147,9 +148,9 @@ void set_default_settings(Settings *settings)
settings->setDefault("enable_sound", "true");
settings->setDefault("sound_volume", "0.8");
settings->setDefault("desynchronize_mapblock_texture_animation", "true");
- settings->setDefault("selectionbox_width","2");
- settings->setDefault("hud_hotbar_max_width","1.0");
+ settings->setDefault("hud_hotbar_max_width", "1.0");
settings->setDefault("enable_local_map_saving", "false");
+ settings->setDefault("show_entity_selectionbox", "true");
settings->setDefault("mip_map", "false");
settings->setDefault("anisotropic_filter", "false");
@@ -157,7 +158,7 @@ void set_default_settings(Settings *settings)
settings->setDefault("trilinear_filter", "false");
settings->setDefault("texture_clean_transparent", "false");
settings->setDefault("texture_min_size", "64");
- settings->setDefault("preload_item_visuals", "false");
+ settings->setDefault("tone_mapping", "false");
settings->setDefault("enable_bumpmapping", "false");
settings->setDefault("enable_parallax_occlusion", "false");
settings->setDefault("generate_normalmaps", "false");
@@ -178,11 +179,14 @@ void set_default_settings(Settings *settings)
settings->setDefault("repeat_rightclick_time", "0.25");
settings->setDefault("enable_particles", "true");
settings->setDefault("enable_mesh_cache", "false");
+ settings->setDefault("enable_vbo", "true");
settings->setDefault("enable_minimap", "true");
settings->setDefault("minimap_shape_round", "true");
settings->setDefault("minimap_double_scan_height", "true");
+ settings->setDefault("send_pre_v25_init", "true");
+
settings->setDefault("curl_timeout", "5000");
settings->setDefault("curl_parallel_limit", "8");
settings->setDefault("curl_file_download_timeout", "300000");
@@ -277,15 +281,19 @@ void set_default_settings(Settings *settings)
settings->setDefault("sqlite_synchronous", "2");
settings->setDefault("full_block_send_enable_min_time_from_building", "2.0");
settings->setDefault("dedicated_server_step", "0.1");
+ settings->setDefault("active_block_mgmt_interval", "2.0");
+ settings->setDefault("abm_interval", "1.0");
+ settings->setDefault("nodetimer_interval", "1.0");
settings->setDefault("ignore_world_load_errors", "false");
settings->setDefault("remote_media", "");
- settings->setDefault("debug_log_level", "2");
+ settings->setDefault("debug_log_level", "action");
settings->setDefault("emergequeue_limit_total", "256");
settings->setDefault("emergequeue_limit_diskonly", "32");
settings->setDefault("emergequeue_limit_generate", "32");
settings->setDefault("num_emerge_threads", "1");
settings->setDefault("secure.enable_security", "false");
settings->setDefault("secure.trusted_mods", "");
+ settings->setDefault("secure.http_mods", "");
// physics stuff
settings->setDefault("movement_acceleration_default", "3");
@@ -311,8 +319,6 @@ void set_default_settings(Settings *settings)
settings->setDefault("water_level", "1");
settings->setDefault("chunksize", "5");
settings->setDefault("mg_flags", "dungeons");
- settings->setDefault("mgv6_spflags", "jungles, snowbiomes");
- settings->setDefault("enable_floating_dungeons", "true");
// IPv6
settings->setDefault("enable_ipv6", "true");
@@ -334,7 +340,6 @@ void set_default_settings(Settings *settings)
settings->setDefault("screenH", "0");
settings->setDefault("enable_shaders", "false");
settings->setDefault("fullscreen", "true");
- settings->setDefault("enable_particles", "false");
settings->setDefault("video_driver", "ogles1");
settings->setDefault("touchtarget", "true");
settings->setDefault("TMPFolder","/sdcard/" PROJECT_NAME_C "/tmp/");
@@ -343,10 +348,19 @@ void set_default_settings(Settings *settings)
settings->setDefault("max_simultaneous_block_sends_per_client", "3");
settings->setDefault("emergequeue_limit_diskonly", "8");
settings->setDefault("emergequeue_limit_generate", "8");
- settings->setDefault("preload_item_visuals", "false");
-
- settings->setDefault("viewing_range_nodes_max", "50");
- settings->setDefault("viewing_range_nodes_min", "20");
+ settings->setDefault("max_block_generate_distance", "3");
+ settings->setDefault("enable_3d_clouds", "false");
+ settings->setDefault("fps_max", "30");
+ settings->setDefault("pause_fps_max", "10");
+ settings->setDefault("max_objects_per_block", "20");
+ settings->setDefault("sqlite_synchronous", "1");
+ settings->setDefault("gui_scaling", "1.1");
+ settings->setDefault("server_map_save_interval", "15");
+ settings->setDefault("client_mapblock_limit", "500");
+ settings->setDefault("active_block_range", "1");
+ settings->setDefault("chunksize", "3");
+
+ settings->setDefault("viewing_range", "25");
settings->setDefault("inventory_image_hack", "false");
//check for device with small screen
diff --git a/src/drawscene.cpp b/src/drawscene.cpp
index 509f341d5..c6abda4ac 100644
--- a/src/drawscene.cpp
+++ b/src/drawscene.cpp
@@ -31,27 +31,9 @@ typedef enum {
EYECOUNT = 2
} paralax_sign;
-
-void draw_selectionbox(video::IVideoDriver* driver, Hud& hud,
- std::vector<aabb3f>& hilightboxes, bool show_hud)
-{
- static const s16 selectionbox_width = rangelim(g_settings->getS16("selectionbox_width"), 1, 5);
-
- if (!show_hud)
- return;
-
- video::SMaterial oldmaterial = driver->getMaterial2D();
- video::SMaterial m;
- m.Thickness = selectionbox_width;
- m.Lighting = false;
- driver->setMaterial(m);
- hud.drawSelectionBoxes(hilightboxes);
- driver->setMaterial(oldmaterial);
-}
-
void draw_anaglyph_3d_mode(Camera& camera, bool show_hud, Hud& hud,
- std::vector<aabb3f> hilightboxes, video::IVideoDriver* driver,
- scene::ISceneManager* smgr, bool draw_wield_tool, Client& client,
+ video::IVideoDriver* driver, scene::ISceneManager* smgr,
+ bool draw_wield_tool, Client& client,
gui::IGUIEnvironment* guienv )
{
@@ -85,10 +67,8 @@ void draw_anaglyph_3d_mode(Camera& camera, bool show_hud, Hud& hud,
camera.getCameraNode()->setTarget(focusPoint);
smgr->drawAll();
driver->setTransform(video::ETS_WORLD, core::IdentityMatrix);
- if (show_hud)
- {
- draw_selectionbox(driver, hud, hilightboxes, show_hud);
-
+ if (show_hud) {
+ hud.drawSelectionMesh();
if (draw_wield_tool)
camera.drawWieldedTool(&leftMove);
}
@@ -115,10 +95,8 @@ void draw_anaglyph_3d_mode(Camera& camera, bool show_hud, Hud& hud,
camera.getCameraNode()->setTarget(focusPoint);
smgr->drawAll();
driver->setTransform(video::ETS_WORLD, core::IdentityMatrix);
- if (show_hud)
- {
- draw_selectionbox(driver, hud, hilightboxes, show_hud);
-
+ if (show_hud) {
+ hud.drawSelectionMesh();
if (draw_wield_tool)
camera.drawWieldedTool(&rightMove);
}
@@ -144,16 +122,15 @@ void init_texture(video::IVideoDriver* driver, const v2u32& screensize,
irr::video::ECF_A8R8G8B8);
}
-video::ITexture* draw_image(const v2u32& screensize,
- paralax_sign psign, const irr::core::matrix4& startMatrix,
- const irr::core::vector3df& focusPoint, bool show_hud,
- video::IVideoDriver* driver, Camera& camera, scene::ISceneManager* smgr,
- Hud& hud, std::vector<aabb3f>& hilightboxes,
- bool draw_wield_tool, Client& client, gui::IGUIEnvironment* guienv,
- video::SColor skycolor )
+video::ITexture* draw_image(const v2u32 &screensize,
+ paralax_sign psign, const irr::core::matrix4 &startMatrix,
+ const irr::core::vector3df &focusPoint, bool show_hud,
+ video::IVideoDriver *driver, Camera &camera, scene::ISceneManager *smgr,
+ Hud &hud, bool draw_wield_tool, Client &client,
+ gui::IGUIEnvironment *guienv, const video::SColor &skycolor)
{
static video::ITexture* images[2] = { NULL, NULL };
- static v2u32 last_screensize = v2u32(0,0);
+ static v2u32 last_screensize = v2u32(0, 0);
video::ITexture* image = NULL;
@@ -187,10 +164,8 @@ video::ITexture* draw_image(const v2u32& screensize,
driver->setTransform(video::ETS_WORLD, core::IdentityMatrix);
- if (show_hud)
- {
- draw_selectionbox(driver, hud, hilightboxes, show_hud);
-
+ if (show_hud) {
+ hud.drawSelectionMesh();
if (draw_wield_tool)
camera.drawWieldedTool(&movement);
}
@@ -220,7 +195,7 @@ video::ITexture* draw_hud(video::IVideoDriver* driver, const v2u32& screensize,
hud.drawCrosshair();
hud.drawHotbar(client.getPlayerItem());
hud.drawLuaElements(camera.getOffset());
-
+ camera.drawNametags();
guienv->drawAll();
}
@@ -232,7 +207,7 @@ video::ITexture* draw_hud(video::IVideoDriver* driver, const v2u32& screensize,
}
void draw_interlaced_3d_mode(Camera& camera, bool show_hud,
- Hud& hud, std::vector<aabb3f> hilightboxes, video::IVideoDriver* driver,
+ Hud& hud, video::IVideoDriver* driver,
scene::ISceneManager* smgr, const v2u32& screensize,
bool draw_wield_tool, Client& client, gui::IGUIEnvironment* guienv,
video::SColor skycolor )
@@ -248,7 +223,7 @@ void draw_interlaced_3d_mode(Camera& camera, bool show_hud,
/* create left view */
video::ITexture* left_image = draw_image(screensize, LEFT, startMatrix,
- focusPoint, show_hud, driver, camera, smgr, hud, hilightboxes,
+ focusPoint, show_hud, driver, camera, smgr, hud,
draw_wield_tool, client, guienv, skycolor);
//Right eye...
@@ -267,10 +242,8 @@ void draw_interlaced_3d_mode(Camera& camera, bool show_hud,
driver->setTransform(video::ETS_WORLD, core::IdentityMatrix);
- if (show_hud)
- {
- draw_selectionbox(driver, hud, hilightboxes, show_hud);
-
+ if (show_hud) {
+ hud.drawSelectionMesh();
if(draw_wield_tool)
camera.drawWieldedTool(&rightMove);
}
@@ -293,7 +266,7 @@ void draw_interlaced_3d_mode(Camera& camera, bool show_hud,
}
void draw_sidebyside_3d_mode(Camera& camera, bool show_hud,
- Hud& hud, std::vector<aabb3f> hilightboxes, video::IVideoDriver* driver,
+ Hud& hud, video::IVideoDriver* driver,
scene::ISceneManager* smgr, const v2u32& screensize,
bool draw_wield_tool, Client& client, gui::IGUIEnvironment* guienv,
video::SColor skycolor )
@@ -309,12 +282,12 @@ void draw_sidebyside_3d_mode(Camera& camera, bool show_hud,
/* create left view */
video::ITexture* left_image = draw_image(screensize, LEFT, startMatrix,
- focusPoint, show_hud, driver, camera, smgr, hud, hilightboxes,
+ focusPoint, show_hud, driver, camera, smgr, hud,
draw_wield_tool, client, guienv, skycolor);
/* create right view */
video::ITexture* right_image = draw_image(screensize, RIGHT, startMatrix,
- focusPoint, show_hud, driver, camera, smgr, hud, hilightboxes,
+ focusPoint, show_hud, driver, camera, smgr, hud,
draw_wield_tool, client, guienv, skycolor);
/* create hud overlay */
@@ -349,7 +322,7 @@ void draw_sidebyside_3d_mode(Camera& camera, bool show_hud,
}
void draw_top_bottom_3d_mode(Camera& camera, bool show_hud,
- Hud& hud, std::vector<aabb3f> hilightboxes, video::IVideoDriver* driver,
+ Hud& hud, video::IVideoDriver* driver,
scene::ISceneManager* smgr, const v2u32& screensize,
bool draw_wield_tool, Client& client, gui::IGUIEnvironment* guienv,
video::SColor skycolor )
@@ -365,12 +338,12 @@ void draw_top_bottom_3d_mode(Camera& camera, bool show_hud,
/* create left view */
video::ITexture* left_image = draw_image(screensize, LEFT, startMatrix,
- focusPoint, show_hud, driver, camera, smgr, hud, hilightboxes,
+ focusPoint, show_hud, driver, camera, smgr, hud,
draw_wield_tool, client, guienv, skycolor);
/* create right view */
video::ITexture* right_image = draw_image(screensize, RIGHT, startMatrix,
- focusPoint, show_hud, driver, camera, smgr, hud, hilightboxes,
+ focusPoint, show_hud, driver, camera, smgr, hud,
draw_wield_tool, client, guienv, skycolor);
/* create hud overlay */
@@ -404,23 +377,100 @@ void draw_top_bottom_3d_mode(Camera& camera, bool show_hud,
camera.getCameraNode()->setTarget(oldTarget);
}
-void draw_plain(Camera& camera, bool show_hud, Hud& hud,
- std::vector<aabb3f> hilightboxes, video::IVideoDriver* driver,
- bool draw_wield_tool, Client& client, gui::IGUIEnvironment* guienv)
+void draw_pageflip_3d_mode(Camera& camera, bool show_hud,
+ Hud& hud, video::IVideoDriver* driver,
+ scene::ISceneManager* smgr, const v2u32& screensize,
+ bool draw_wield_tool, Client& client, gui::IGUIEnvironment* guienv,
+ video::SColor skycolor)
{
+ /* preserve old setup*/
+ irr::core::vector3df oldPosition = camera.getCameraNode()->getPosition();
+ irr::core::vector3df oldTarget = camera.getCameraNode()->getTarget();
+
+ irr::core::matrix4 startMatrix =
+ camera.getCameraNode()->getAbsoluteTransformation();
+ irr::core::vector3df focusPoint = (camera.getCameraNode()->getTarget()
+ - camera.getCameraNode()->getAbsolutePosition()).setLength(1)
+ + camera.getCameraNode()->getAbsolutePosition();
+
+ //Left eye...
+ driver->setRenderTarget(irr::video::ERT_STEREO_LEFT_BUFFER);
+
+ irr::core::vector3df leftEye;
+ irr::core::matrix4 leftMove;
+ leftMove.setTranslation(
+ irr::core::vector3df(-g_settings->getFloat("3d_paralax_strength"),
+ 0.0f, 0.0f));
+ leftEye = (startMatrix * leftMove).getTranslation();
+
+ //clear the depth buffer, and color
+ driver->beginScene(true, true, irr::video::SColor(200, 200, 200, 255));
+ camera.getCameraNode()->setPosition(leftEye);
+ camera.getCameraNode()->setTarget(focusPoint);
+ smgr->drawAll();
driver->setTransform(video::ETS_WORLD, core::IdentityMatrix);
- draw_selectionbox(driver, hud, hilightboxes, show_hud);
+ if (show_hud) {
+ hud.drawSelectionMesh();
+ if (draw_wield_tool)
+ camera.drawWieldedTool(&leftMove);
+ hud.drawHotbar(client.getPlayerItem());
+ hud.drawLuaElements(camera.getOffset());
+ camera.drawNametags();
+ }
+
+ guienv->drawAll();
+
+ //Right eye...
+ driver->setRenderTarget(irr::video::ERT_STEREO_RIGHT_BUFFER);
+
+ irr::core::vector3df rightEye;
+ irr::core::matrix4 rightMove;
+ rightMove.setTranslation(
+ irr::core::vector3df(g_settings->getFloat("3d_paralax_strength"),
+ 0.0f, 0.0f));
+ rightEye = (startMatrix * rightMove).getTranslation();
+
+ //clear the depth buffer, and color
+ driver->beginScene(true, true, irr::video::SColor(200, 200, 200, 255));
+ camera.getCameraNode()->setPosition(rightEye);
+ camera.getCameraNode()->setTarget(focusPoint);
+ smgr->drawAll();
+ driver->setTransform(video::ETS_WORLD, core::IdentityMatrix);
- if(draw_wield_tool)
- camera.drawWieldedTool();
+ if (show_hud) {
+ hud.drawSelectionMesh();
+ if (draw_wield_tool)
+ camera.drawWieldedTool(&rightMove);
+ hud.drawHotbar(client.getPlayerItem());
+ hud.drawLuaElements(camera.getOffset());
+ camera.drawNametags();
+ }
+
+ guienv->drawAll();
+
+ camera.getCameraNode()->setPosition(oldPosition);
+ camera.getCameraNode()->setTarget(oldTarget);
+}
+
+void draw_plain(Camera &camera, bool show_hud, Hud &hud,
+ video::IVideoDriver *driver, bool draw_wield_tool,
+ Client &client, gui::IGUIEnvironment *guienv)
+{
+ driver->setTransform(video::ETS_WORLD, core::IdentityMatrix);
+ if (show_hud) {
+ hud.drawSelectionMesh();
+ if (draw_wield_tool) {
+ camera.drawWieldedTool();
+ }
+ }
}
void draw_scene(video::IVideoDriver *driver, scene::ISceneManager *smgr,
Camera &camera, Client& client, LocalPlayer *player, Hud &hud,
Mapper &mapper, gui::IGUIEnvironment *guienv,
- std::vector<aabb3f> hilightboxes, const v2u32 &screensize,
- video::SColor skycolor, bool show_hud, bool show_minimap)
+ const v2u32 &screensize, const video::SColor &skycolor,
+ bool show_hud, bool show_minimap)
{
TimeTaker timer("smgr");
@@ -444,30 +494,37 @@ void draw_scene(video::IVideoDriver *driver, scene::ISceneManager *smgr,
if (draw_mode == "anaglyph")
{
- draw_anaglyph_3d_mode(camera, show_hud, hud, hilightboxes, driver,
+ draw_anaglyph_3d_mode(camera, show_hud, hud, driver,
smgr, draw_wield_tool, client, guienv);
draw_crosshair = false;
}
else if (draw_mode == "interlaced")
{
- draw_interlaced_3d_mode(camera, show_hud, hud, hilightboxes, driver,
+ draw_interlaced_3d_mode(camera, show_hud, hud, driver,
smgr, screensize, draw_wield_tool, client, guienv, skycolor);
draw_crosshair = false;
}
else if (draw_mode == "sidebyside")
{
- draw_sidebyside_3d_mode(camera, show_hud, hud, hilightboxes, driver,
+ draw_sidebyside_3d_mode(camera, show_hud, hud, driver,
smgr, screensize, draw_wield_tool, client, guienv, skycolor);
show_hud = false;
}
else if (draw_mode == "topbottom")
{
- draw_top_bottom_3d_mode(camera, show_hud, hud, hilightboxes, driver,
+ draw_top_bottom_3d_mode(camera, show_hud, hud, driver,
smgr, screensize, draw_wield_tool, client, guienv, skycolor);
show_hud = false;
}
+ else if (draw_mode == "pageflip")
+ {
+ draw_pageflip_3d_mode(camera, show_hud, hud, driver,
+ smgr, screensize, draw_wield_tool, client, guienv, skycolor);
+ draw_crosshair = false;
+ show_hud = false;
+ }
else {
- draw_plain(camera, show_hud, hud, hilightboxes, driver,
+ draw_plain(camera, show_hud, hud, driver,
draw_wield_tool, client, guienv);
}
@@ -483,8 +540,11 @@ void draw_scene(video::IVideoDriver *driver, scene::ISceneManager *smgr,
{
if (draw_crosshair)
hud.drawCrosshair();
+
hud.drawHotbar(client.getPlayerItem());
hud.drawLuaElements(camera.getOffset());
+ camera.drawNametags();
+
if (show_minimap)
mapper.drawMinimap();
}
diff --git a/src/drawscene.h b/src/drawscene.h
index 0630f2970..364fcd499 100644
--- a/src/drawscene.h
+++ b/src/drawscene.h
@@ -31,9 +31,9 @@ void draw_load_screen(const std::wstring &text, IrrlichtDevice *device,
bool clouds = true);
void draw_scene(video::IVideoDriver *driver, scene::ISceneManager *smgr,
- Camera &camera, Client &client, LocalPlayer *player, Hud &hud,
- Mapper &mapper, gui::IGUIEnvironment *guienv,
- std::vector<aabb3f> hilightboxes, const v2u32 &screensize,
- video::SColor skycolor, bool show_hud, bool show_minimap);
+ Camera &camera, Client &client, LocalPlayer *player,
+ Hud &hud, Mapper &mapper, gui::IGUIEnvironment *guienv,
+ const v2u32 &screensize, const video::SColor &skycolor,
+ bool show_hud, bool show_minimap);
#endif /* DRAWSCENE_H_ */
diff --git a/src/dungeongen.cpp b/src/dungeongen.cpp
index 8ce64e1e1..5bfc320c9 100644
--- a/src/dungeongen.cpp
+++ b/src/dungeongen.cpp
@@ -44,7 +44,7 @@ DungeonGen::DungeonGen(Mapgen *mapgen, DungeonParams *dparams)
this->vm = mapgen->vm;
#ifdef DGEN_USE_TORCHES
- c_torch = ndef->getId("default:torch");
+ c_torch = mg->ndef->getId("default:torch");
#endif
if (dparams) {
@@ -65,6 +65,11 @@ DungeonGen::DungeonGen(Mapgen *mapgen, DungeonParams *dparams)
dp.np_wetness = nparams_dungeon_wetness;
dp.np_density = nparams_dungeon_density;
}
+
+ // For mapgens using river water
+ dp.c_river_water = mg->ndef->getId("mapgen_river_water_source");
+ if (dp.c_river_water == CONTENT_IGNORE)
+ dp.c_river_water = mg->ndef->getId("mapgen_water_source");
}
@@ -80,17 +85,14 @@ void DungeonGen::generate(u32 bseed, v3s16 nmin, v3s16 nmax)
// Dungeon generator doesn't modify places which have this set
vm->clearFlag(VMANIP_FLAG_DUNGEON_INSIDE | VMANIP_FLAG_DUNGEON_PRESERVE);
- bool no_float = !g_settings->getBool("enable_floating_dungeons");
-
- // Set all air and water (and optionally ignore) to be untouchable
+ // Set all air and water to be untouchable
// to make dungeons open to caves and open air
for (s16 z = nmin.Z; z <= nmax.Z; z++) {
for (s16 y = nmin.Y; y <= nmax.Y; y++) {
u32 i = vm->m_area.index(nmin.X, y, z);
for (s16 x = nmin.X; x <= nmax.X; x++) {
content_t c = vm->m_data[i].getContent();
- if (c == CONTENT_AIR || c == dp.c_water ||
- (no_float && c == CONTENT_IGNORE))
+ if (c == CONTENT_AIR || c == dp.c_water || c == dp.c_river_water)
vm->m_flags[i] |= VMANIP_FLAG_DUNGEON_PRESERVE;
i++;
}
@@ -141,21 +143,21 @@ void DungeonGen::makeDungeon(v3s16 start_padding)
// start_padding is used to disallow starting the generation of
// a dungeon in a neighboring generation chunk
roomplace = vm->m_area.MinEdge + start_padding + v3s16(
- random.range(0, areasize.X - roomsize.X - 1 - start_padding.X),
- random.range(0, areasize.Y - roomsize.Y - 1 - start_padding.Y),
- random.range(0, areasize.Z - roomsize.Z - 1 - start_padding.Z));
+ random.range(0, areasize.X - roomsize.X - start_padding.X),
+ random.range(0, areasize.Y - roomsize.Y - start_padding.Y),
+ random.range(0, areasize.Z - roomsize.Z - start_padding.Z));
/*
Check that we're not putting the room to an unknown place,
otherwise it might end up floating in the air
*/
fits = true;
- for (s16 z = 1; z < roomsize.Z - 1; z++)
- for (s16 y = 1; y < roomsize.Y - 1; y++)
- for (s16 x = 1; x < roomsize.X - 1; x++) {
+ for (s16 z = 0; z < roomsize.Z; z++)
+ for (s16 y = 0; y < roomsize.Y; y++)
+ for (s16 x = 0; x < roomsize.X; x++) {
v3s16 p = roomplace + v3s16(x, y, z);
u32 vi = vm->m_area.index(p);
- if ((vm->m_flags[vi] & VMANIP_FLAG_DUNGEON_INSIDE) ||
+ if ((vm->m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE) ||
vm->m_data[vi].getContent() == CONTENT_IGNORE) {
fits = false;
break;
@@ -392,7 +394,8 @@ void DungeonGen::makeCorridor(v3s16 doorplace, v3s16 doordir,
if (partcount != 0)
p.Y += make_stairs;
- if (vm->m_area.contains(p) && vm->m_area.contains(p + v3s16(0, 1, 0))) {
+ if (vm->m_area.contains(p) && vm->m_area.contains(p + v3s16(0, 1, 0)) &&
+ vm->m_area.contains(v3s16(p.X - dir.X, p.Y - 1, p.Z - dir.Z))) {
if (make_stairs) {
makeFill(p + v3s16(-1, -1, -1),
dp.holesize + v3s16(2, 3, 2),
diff --git a/src/dungeongen.h b/src/dungeongen.h
index 4e1201d82..d209dd4bf 100644
--- a/src/dungeongen.h
+++ b/src/dungeongen.h
@@ -40,6 +40,7 @@ int dir_to_facedir(v3s16 d);
struct DungeonParams {
content_t c_water;
+ content_t c_river_water;
content_t c_cobble;
content_t c_moss;
content_t c_stair;
diff --git a/src/emerge.cpp b/src/emerge.cpp
index d6bda731a..93e8f2b30 100644
--- a/src/emerge.cpp
+++ b/src/emerge.cpp
@@ -20,77 +20,102 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "emerge.h"
-#include "server.h"
+
#include <iostream>
#include <queue>
-#include "jthread/jevent.h"
-#include "map.h"
-#include "environment.h"
+
#include "util/container.h"
#include "util/thread.h"
-#include "constants.h"
-#include "voxel.h"
+#include "threading/event.h"
+
#include "config.h"
-#include "mapblock.h"
-#include "serverobject.h"
-#include "settings.h"
-#include "scripting_game.h"
-#include "profiler.h"
+#include "constants.h"
+#include "environment.h"
#include "log.h"
-#include "nodedef.h"
-#include "mg_biome.h"
-#include "mg_ore.h"
-#include "mg_decoration.h"
-#include "mg_schematic.h"
+#include "map.h"
+#include "mapblock.h"
+#include "mapgen_flat.h"
+#include "mapgen_fractal.h"
#include "mapgen_v5.h"
#include "mapgen_v6.h"
#include "mapgen_v7.h"
+#include "mapgen_valleys.h"
#include "mapgen_singlenode.h"
+#include "mg_biome.h"
+#include "mg_ore.h"
+#include "mg_decoration.h"
+#include "mg_schematic.h"
+#include "nodedef.h"
+#include "profiler.h"
+#include "scripting_game.h"
+#include "server.h"
+#include "serverobject.h"
+#include "settings.h"
+#include "voxel.h"
+
struct MapgenDesc {
const char *name;
MapgenFactory *factory;
+ bool is_user_visible;
};
-MapgenDesc reg_mapgens[] = {
- {"v5", new MapgenFactoryV5},
- {"v6", new MapgenFactoryV6},
- {"v7", new MapgenFactoryV7},
- {"singlenode", new MapgenFactorySinglenode},
-};
-
-class EmergeThread : public JThread
-{
+class EmergeThread : public Thread {
public:
- Server *m_server;
- ServerMap *map;
- EmergeManager *emerge;
- Mapgen *mapgen;
bool enable_mapgen_debug_info;
int id;
- Event qevent;
- std::queue<v3s16> blockqueue;
-
- EmergeThread(Server *server, int ethreadid):
- JThread(),
- m_server(server),
- map(NULL),
- emerge(NULL),
- mapgen(NULL),
- enable_mapgen_debug_info(false),
- id(ethreadid)
- {
- }
+ EmergeThread(Server *server, int ethreadid);
+ ~EmergeThread();
+
+ void *run();
+ void signal();
+
+ // Requires queue mutex held
+ bool pushBlock(v3s16 pos);
+
+ void cancelPendingItems();
+
+ static void runCompletionCallbacks(
+ v3s16 pos, EmergeAction action,
+ const EmergeCallbackList &callbacks);
+
+private:
+ Server *m_server;
+ ServerMap *m_map;
+ EmergeManager *m_emerge;
+ Mapgen *m_mapgen;
+
+ Event m_queue_event;
+ std::queue<v3s16> m_block_queue;
+
+ bool popBlockEmerge(v3s16 *pos, BlockEmergeData *bedata);
+
+ EmergeAction getBlockOrStartGen(
+ v3s16 pos, bool allow_gen, MapBlock **block, BlockMakeData *data);
+ MapBlock *finishGen(v3s16 pos, BlockMakeData *bmdata,
+ std::map<v3s16, MapBlock *> *modified_blocks);
- void *Thread();
- bool popBlockEmerge(v3s16 *pos, u8 *flags);
- bool getBlockOrStartGen(v3s16 p, MapBlock **b,
- BlockMakeData *data, bool allow_generate);
+ friend class EmergeManager;
};
+////
+//// Built-in mapgens
+////
+
+MapgenDesc g_reg_mapgens[] = {
+ {"v5", new MapgenFactoryV5, true},
+ {"v6", new MapgenFactoryV6, true},
+ {"v7", new MapgenFactoryV7, true},
+ {"flat", new MapgenFactoryFlat, true},
+ {"fractal", new MapgenFactoryFractal, true},
+ {"valleys", new MapgenFactoryValleys, true},
+ {"singlenode", new MapgenFactorySinglenode, false},
+};
-/////////////////////////////// Emerge Manager ////////////////////////////////
+////
+//// EmergeManager
+////
EmergeManager::EmergeManager(IGameDef *gamedef)
{
@@ -104,34 +129,34 @@ EmergeManager::EmergeManager(IGameDef *gamedef)
// Note that accesses to this variable are not synchronized.
// This is because the *only* thread ever starting or stopping
// EmergeThreads should be the ServerThread.
- this->threads_active = false;
+ this->m_threads_active = false;
- mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
+ enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
- // if unspecified, leave a proc for the main thread and one for
+ // If unspecified, leave a proc for the main thread and one for
// some other misc thread
s16 nthreads = 0;
if (!g_settings->getS16NoEx("num_emerge_threads", nthreads))
- nthreads = porting::getNumberOfProcessors() - 2;
+ nthreads = Thread::getNumberOfProcessors() - 2;
if (nthreads < 1)
nthreads = 1;
- qlimit_total = g_settings->getU16("emergequeue_limit_total");
- if (!g_settings->getU16NoEx("emergequeue_limit_diskonly", qlimit_diskonly))
- qlimit_diskonly = nthreads * 5 + 1;
- if (!g_settings->getU16NoEx("emergequeue_limit_generate", qlimit_generate))
- qlimit_generate = nthreads + 1;
+ m_qlimit_total = g_settings->getU16("emergequeue_limit_total");
+ if (!g_settings->getU16NoEx("emergequeue_limit_diskonly", m_qlimit_diskonly))
+ m_qlimit_diskonly = nthreads * 5 + 1;
+ if (!g_settings->getU16NoEx("emergequeue_limit_generate", m_qlimit_generate))
+ m_qlimit_generate = nthreads + 1;
// don't trust user input for something very important like this
- if (qlimit_total < 1)
- qlimit_total = 1;
- if (qlimit_diskonly < 1)
- qlimit_diskonly = 1;
- if (qlimit_generate < 1)
- qlimit_generate = 1;
+ if (m_qlimit_total < 1)
+ m_qlimit_total = 1;
+ if (m_qlimit_diskonly < 1)
+ m_qlimit_diskonly = 1;
+ if (m_qlimit_generate < 1)
+ m_qlimit_generate = 1;
for (s16 i = 0; i < nthreads; i++)
- emergethread.push_back(new EmergeThread((Server *) gamedef, i));
+ m_threads.push_back(new EmergeThread((Server *)gamedef, i));
infostream << "EmergeManager: using " << nthreads << " threads" << std::endl;
}
@@ -139,27 +164,25 @@ EmergeManager::EmergeManager(IGameDef *gamedef)
EmergeManager::~EmergeManager()
{
- for (u32 i = 0; i != emergethread.size(); i++) {
- if (threads_active) {
- emergethread[i]->Stop();
- emergethread[i]->qevent.signal();
- emergethread[i]->Wait();
+ for (u32 i = 0; i != m_threads.size(); i++) {
+ EmergeThread *thread = m_threads[i];
+
+ if (m_threads_active) {
+ thread->stop();
+ thread->signal();
+ thread->wait();
}
- delete emergethread[i];
- delete mapgen[i];
+
+ delete thread;
+ delete m_mapgens[i];
}
- emergethread.clear();
- mapgen.clear();
delete biomemgr;
delete oremgr;
delete decomgr;
delete schemmgr;
- if (params.sparams) {
- delete params.sparams;
- params.sparams = NULL;
- }
+ delete params.sparams;
}
@@ -171,33 +194,37 @@ void EmergeManager::loadMapgenParams()
void EmergeManager::initMapgens()
{
- if (mapgen.size())
+ if (m_mapgens.size())
return;
+ MapgenFactory *mgfactory = getMapgenFactory(params.mg_name);
+ if (!mgfactory) {
+ errorstream << "EmergeManager: mapgen " << params.mg_name <<
+ " not registered; falling back to " << DEFAULT_MAPGEN << std::endl;
+
+ params.mg_name = DEFAULT_MAPGEN;
+
+ mgfactory = getMapgenFactory(params.mg_name);
+ FATAL_ERROR_IF(mgfactory == NULL, "Couldn't use any mapgen!");
+ }
+
if (!params.sparams) {
- params.sparams = createMapgenParams(params.mg_name);
- if (!params.sparams) {
- params.mg_name = DEFAULT_MAPGEN;
- params.sparams = createMapgenParams(params.mg_name);
- assert(params.sparams);
- }
+ params.sparams = mgfactory->createMapgenParams();
params.sparams->readParams(g_settings);
}
- // Create the mapgens
- for (u32 i = 0; i != emergethread.size(); i++) {
- Mapgen *mg = createMapgen(params.mg_name, i, &params);
- assert(mg);
- mapgen.push_back(mg);
+ for (u32 i = 0; i != m_threads.size(); i++) {
+ Mapgen *mg = mgfactory->createMapgen(i, &params, this);
+ m_mapgens.push_back(mg);
}
}
Mapgen *EmergeManager::getCurrentMapgen()
{
- for (u32 i = 0; i != emergethread.size(); i++) {
- if (emergethread[i]->IsSameThread())
- return emergethread[i]->mapgen;
+ for (u32 i = 0; i != m_threads.size(); i++) {
+ if (m_threads[i]->isCurrentThread())
+ return m_threads[i]->m_mapgen;
}
return NULL;
@@ -206,329 +233,486 @@ Mapgen *EmergeManager::getCurrentMapgen()
void EmergeManager::startThreads()
{
- if (threads_active)
+ if (m_threads_active)
return;
- for (u32 i = 0; i != emergethread.size(); i++)
- emergethread[i]->Start();
+ for (u32 i = 0; i != m_threads.size(); i++)
+ m_threads[i]->start();
- threads_active = true;
+ m_threads_active = true;
}
void EmergeManager::stopThreads()
{
- if (!threads_active)
+ if (!m_threads_active)
return;
// Request thread stop in parallel
- for (u32 i = 0; i != emergethread.size(); i++) {
- emergethread[i]->Stop();
- emergethread[i]->qevent.signal();
+ for (u32 i = 0; i != m_threads.size(); i++) {
+ m_threads[i]->stop();
+ m_threads[i]->signal();
}
// Then do the waiting for each
- for (u32 i = 0; i != emergethread.size(); i++)
- emergethread[i]->Wait();
+ for (u32 i = 0; i != m_threads.size(); i++)
+ m_threads[i]->wait();
- threads_active = false;
+ m_threads_active = false;
}
-bool EmergeManager::enqueueBlockEmerge(u16 peer_id, v3s16 p, bool allow_generate)
+bool EmergeManager::isRunning()
{
- std::map<v3s16, BlockEmergeData *>::const_iterator iter;
- BlockEmergeData *bedata;
- u16 count;
- u8 flags = 0;
- int idx = 0;
+ return m_threads_active;
+}
+
+bool EmergeManager::enqueueBlockEmerge(
+ u16 peer_id,
+ v3s16 blockpos,
+ bool allow_generate,
+ bool ignore_queue_limits)
+{
+ u16 flags = 0;
if (allow_generate)
- flags |= BLOCK_EMERGE_ALLOWGEN;
+ flags |= BLOCK_EMERGE_ALLOW_GEN;
+ if (ignore_queue_limits)
+ flags |= BLOCK_EMERGE_FORCE_QUEUE;
- {
- JMutexAutoLock queuelock(queuemutex);
+ return enqueueBlockEmergeEx(blockpos, peer_id, flags, NULL, NULL);
+}
- count = blocks_enqueued.size();
- if (count >= qlimit_total)
- return false;
- count = peer_queue_count[peer_id];
- u16 qlimit_peer = allow_generate ? qlimit_generate : qlimit_diskonly;
- if (count >= qlimit_peer)
+bool EmergeManager::enqueueBlockEmergeEx(
+ v3s16 blockpos,
+ u16 peer_id,
+ u16 flags,
+ EmergeCompletionCallback callback,
+ void *callback_param)
+{
+ EmergeThread *thread = NULL;
+ bool entry_already_exists = false;
+
+ {
+ MutexAutoLock queuelock(m_queue_mutex);
+
+ if (!pushBlockEmergeData(blockpos, peer_id, flags,
+ callback, callback_param, &entry_already_exists))
return false;
- iter = blocks_enqueued.find(p);
- if (iter != blocks_enqueued.end()) {
- bedata = iter->second;
- bedata->flags |= flags;
+ if (entry_already_exists)
return true;
- }
- bedata = new BlockEmergeData;
- bedata->flags = flags;
- bedata->peer_requested = peer_id;
- blocks_enqueued.insert(std::make_pair(p, bedata));
+ thread = getOptimalThread();
+ thread->pushBlock(blockpos);
+ }
- peer_queue_count[peer_id] = count + 1;
+ thread->signal();
- // insert into the EmergeThread queue with the least items
- int lowestitems = emergethread[0]->blockqueue.size();
- for (u32 i = 1; i != emergethread.size(); i++) {
- int nitems = emergethread[i]->blockqueue.size();
- if (nitems < lowestitems) {
- idx = i;
- lowestitems = nitems;
- }
- }
+ return true;
+}
- emergethread[idx]->blockqueue.push(p);
+
+//
+// Mapgen-related helper functions
+//
+
+v3s16 EmergeManager::getContainingChunk(v3s16 blockpos)
+{
+ return getContainingChunk(blockpos, params.chunksize);
+}
+
+
+v3s16 EmergeManager::getContainingChunk(v3s16 blockpos, s16 chunksize)
+{
+ s16 coff = -chunksize / 2;
+ v3s16 chunk_offset(coff, coff, coff);
+
+ return getContainerPos(blockpos - chunk_offset, chunksize)
+ * chunksize + chunk_offset;
+}
+
+
+int EmergeManager::getSpawnLevelAtPoint(v2s16 p)
+{
+ if (m_mapgens.size() == 0 || !m_mapgens[0]) {
+ errorstream << "EmergeManager: getSpawnLevelAtPoint() called"
+ " before mapgen init" << std::endl;
+ return 0;
}
- emergethread[idx]->qevent.signal();
- return true;
+ return m_mapgens[0]->getSpawnLevelAtPoint(p);
}
int EmergeManager::getGroundLevelAtPoint(v2s16 p)
{
- if (mapgen.size() == 0 || !mapgen[0]) {
+ if (m_mapgens.size() == 0 || !m_mapgens[0]) {
errorstream << "EmergeManager: getGroundLevelAtPoint() called"
- " before mapgen initialized" << std::endl;
+ " before mapgen init" << std::endl;
return 0;
}
- return mapgen[0]->getGroundLevelAtPoint(p);
+ return m_mapgens[0]->getGroundLevelAtPoint(p);
}
bool EmergeManager::isBlockUnderground(v3s16 blockpos)
{
- /*
+#if 0
v2s16 p = v2s16((blockpos.X * MAP_BLOCKSIZE) + MAP_BLOCKSIZE / 2,
(blockpos.Y * MAP_BLOCKSIZE) + MAP_BLOCKSIZE / 2);
int ground_level = getGroundLevelAtPoint(p);
return blockpos.Y * (MAP_BLOCKSIZE + 1) <= min(water_level, ground_level);
- */
+#endif
- //yuck, but then again, should i bother being accurate?
- //the height of the nodes in a single block is quite variable
+ // Use a simple heuristic; the above method is wildly inaccurate anyway.
return blockpos.Y * (MAP_BLOCKSIZE + 1) <= params.water_level;
}
-void EmergeManager::getMapgenNames(std::list<const char *> &mgnames)
+void EmergeManager::getMapgenNames(
+ std::vector<const char *> *mgnames, bool include_hidden)
{
- for (u32 i = 0; i != ARRLEN(reg_mapgens); i++)
- mgnames.push_back(reg_mapgens[i].name);
+ for (u32 i = 0; i != ARRLEN(g_reg_mapgens); i++) {
+ if (include_hidden || g_reg_mapgens[i].is_user_visible)
+ mgnames->push_back(g_reg_mapgens[i].name);
+ }
}
-Mapgen *EmergeManager::createMapgen(const std::string &mgname, int mgid,
- MapgenParams *mgparams)
+MapgenFactory *EmergeManager::getMapgenFactory(const std::string &mgname)
{
- u32 i;
- for (i = 0; i != ARRLEN(reg_mapgens) && mgname != reg_mapgens[i].name; i++);
- if (i == ARRLEN(reg_mapgens)) {
- errorstream << "EmergeManager; mapgen " << mgname <<
- " not registered" << std::endl;
- return NULL;
+ for (u32 i = 0; i != ARRLEN(g_reg_mapgens); i++) {
+ if (mgname == g_reg_mapgens[i].name)
+ return g_reg_mapgens[i].factory;
}
- MapgenFactory *mgfactory = reg_mapgens[i].factory;
- return mgfactory->createMapgen(mgid, mgparams, this);
+ return NULL;
}
-MapgenSpecificParams *EmergeManager::createMapgenParams(const std::string &mgname)
+bool EmergeManager::pushBlockEmergeData(
+ v3s16 pos,
+ u16 peer_requested,
+ u16 flags,
+ EmergeCompletionCallback callback,
+ void *callback_param,
+ bool *entry_already_exists)
{
- u32 i;
- for (i = 0; i < ARRLEN(reg_mapgens) && mgname != reg_mapgens[i].name; i++);
- if (i == ARRLEN(reg_mapgens)) {
- errorstream << "EmergeManager: Mapgen " << mgname <<
- " not registered" << std::endl;
- return NULL;
+ u16 &count_peer = m_peer_queue_count[peer_requested];
+
+ if ((flags & BLOCK_EMERGE_FORCE_QUEUE) == 0) {
+ if (m_blocks_enqueued.size() >= m_qlimit_total)
+ return false;
+
+ if (peer_requested != PEER_ID_INEXISTENT) {
+ u16 qlimit_peer = (flags & BLOCK_EMERGE_ALLOW_GEN) ?
+ m_qlimit_generate : m_qlimit_diskonly;
+ if (count_peer >= qlimit_peer)
+ return false;
+ }
}
- MapgenFactory *mgfactory = reg_mapgens[i].factory;
- return mgfactory->createMapgenParams();
-}
+ std::pair<std::map<v3s16, BlockEmergeData>::iterator, bool> findres;
+ findres = m_blocks_enqueued.insert(std::make_pair(pos, BlockEmergeData()));
+
+ BlockEmergeData &bedata = findres.first->second;
+ *entry_already_exists = !findres.second;
+ if (callback)
+ bedata.callbacks.push_back(std::make_pair(callback, callback_param));
-////////////////////////////// Emerge Thread //////////////////////////////////
+ if (*entry_already_exists) {
+ bedata.flags |= flags;
+ } else {
+ bedata.flags = flags;
+ bedata.peer_requested = peer_requested;
+
+ count_peer++;
+ }
+
+ return true;
+}
-bool EmergeThread::popBlockEmerge(v3s16 *pos, u8 *flags)
+
+bool EmergeManager::popBlockEmergeData(
+ v3s16 pos,
+ BlockEmergeData *bedata)
{
- std::map<v3s16, BlockEmergeData *>::iterator iter;
- JMutexAutoLock queuelock(emerge->queuemutex);
+ std::map<v3s16, BlockEmergeData>::iterator it;
+ std::map<u16, u16>::iterator it2;
+
+ it = m_blocks_enqueued.find(pos);
+ if (it == m_blocks_enqueued.end())
+ return false;
- if (blockqueue.empty())
+ *bedata = it->second;
+
+ it2 = m_peer_queue_count.find(bedata->peer_requested);
+ if (it2 == m_peer_queue_count.end())
return false;
- v3s16 p = blockqueue.front();
- blockqueue.pop();
- *pos = p;
+ u16 &count_peer = it2->second;
+ assert(count_peer != 0);
+ count_peer--;
+
+ m_blocks_enqueued.erase(it);
+
+ return true;
+}
+
+
+EmergeThread *EmergeManager::getOptimalThread()
+{
+ size_t nthreads = m_threads.size();
+
+ FATAL_ERROR_IF(nthreads == 0, "No emerge threads!");
+
+ size_t index = 0;
+ size_t nitems_lowest = m_threads[0]->m_block_queue.size();
+
+ for (size_t i = 1; i < nthreads; i++) {
+ size_t nitems = m_threads[i]->m_block_queue.size();
+ if (nitems < nitems_lowest) {
+ index = i;
+ nitems_lowest = nitems;
+ }
+ }
+
+ return m_threads[index];
+}
+
+
+////
+//// EmergeThread
+////
+
+EmergeThread::EmergeThread(Server *server, int ethreadid) :
+ enable_mapgen_debug_info(false),
+ id(ethreadid),
+ m_server(server),
+ m_map(NULL),
+ m_emerge(NULL),
+ m_mapgen(NULL)
+{
+ m_name = "Emerge-" + itos(ethreadid);
+}
+
+
+EmergeThread::~EmergeThread()
+{
+ //cancelPendingItems();
+}
+
+
+void EmergeThread::signal()
+{
+ m_queue_event.signal();
+}
+
+
+bool EmergeThread::pushBlock(v3s16 pos)
+{
+ m_block_queue.push(pos);
+ return true;
+}
+
+
+void EmergeThread::cancelPendingItems()
+{
+ MutexAutoLock queuelock(m_emerge->m_queue_mutex);
+
+ while (!m_block_queue.empty()) {
+ BlockEmergeData bedata;
+ v3s16 pos;
+
+ pos = m_block_queue.front();
+ m_block_queue.pop();
+
+ m_emerge->popBlockEmergeData(pos, &bedata);
- iter = emerge->blocks_enqueued.find(p);
- if (iter == emerge->blocks_enqueued.end())
- return false; //uh oh, queue and map out of sync!!
+ runCompletionCallbacks(pos, EMERGE_CANCELLED, bedata.callbacks);
+ }
+}
- BlockEmergeData *bedata = iter->second;
- *flags = bedata->flags;
- emerge->peer_queue_count[bedata->peer_requested]--;
+void EmergeThread::runCompletionCallbacks(
+ v3s16 pos,
+ EmergeAction action,
+ const EmergeCallbackList &callbacks)
+{
+ for (size_t i = 0; i != callbacks.size(); i++) {
+ EmergeCompletionCallback callback;
+ void *param;
+
+ callback = callbacks[i].first;
+ param = callbacks[i].second;
+
+ callback(pos, action, param);
+ }
+}
+
+
+bool EmergeThread::popBlockEmerge(v3s16 *pos, BlockEmergeData *bedata)
+{
+ MutexAutoLock queuelock(m_emerge->m_queue_mutex);
+
+ if (m_block_queue.empty())
+ return false;
+
+ *pos = m_block_queue.front();
+ m_block_queue.pop();
- delete bedata;
- emerge->blocks_enqueued.erase(iter);
+ m_emerge->popBlockEmergeData(*pos, bedata);
return true;
}
-bool EmergeThread::getBlockOrStartGen(v3s16 p, MapBlock **b,
- BlockMakeData *data, bool allow_gen)
+EmergeAction EmergeThread::getBlockOrStartGen(
+ v3s16 pos, bool allow_gen, MapBlock **block, BlockMakeData *bmdata)
{
- v2s16 p2d(p.X, p.Z);
- //envlock: usually takes <=1ms, sometimes 90ms or ~400ms to acquire
- JMutexAutoLock envlock(m_server->m_env_mutex);
-
- // Load sector if it isn't loaded
- if (map->getSectorNoGenerateNoEx(p2d) == NULL)
- map->loadSectorMeta(p2d);
-
- // Attempt to load block
- MapBlock *block = map->getBlockNoCreateNoEx(p);
- if (!block || block->isDummy() || !block->isGenerated()) {
- EMERGE_DBG_OUT("not in memory, attempting to load from disk");
- block = map->loadBlock(p);
- if (block && block->isGenerated())
- map->prepareBlock(block);
+ MutexAutoLock envlock(m_server->m_env_mutex);
+
+ // 1). Attempt to fetch block from memory
+ *block = m_map->getBlockNoCreateNoEx(pos);
+ if (*block && !(*block)->isDummy() && (*block)->isGenerated())
+ return EMERGE_FROM_MEMORY;
+
+ // 2). Attempt to load block from disk
+ *block = m_map->loadBlock(pos);
+ if (*block && (*block)->isGenerated())
+ return EMERGE_FROM_DISK;
+
+ // 3). Attempt to start generation
+ if (allow_gen && m_map->initBlockMake(pos, bmdata))
+ return EMERGE_GENERATED;
+
+ // All attempts failed; cancel this block emerge
+ return EMERGE_CANCELLED;
+}
+
+
+MapBlock *EmergeThread::finishGen(v3s16 pos, BlockMakeData *bmdata,
+ std::map<v3s16, MapBlock *> *modified_blocks)
+{
+ MutexAutoLock envlock(m_server->m_env_mutex);
+ ScopeProfiler sp(g_profiler,
+ "EmergeThread: after Mapgen::makeChunk", SPT_AVG);
+
+ /*
+ Perform post-processing on blocks (invalidate lighting, queue liquid
+ transforms, etc.) to finish block make
+ */
+ m_map->finishBlockMake(bmdata, modified_blocks);
+
+ MapBlock *block = m_map->getBlockNoCreateNoEx(pos);
+ if (!block) {
+ errorstream << "EmergeThread::finishGen: Couldn't grab block we "
+ "just generated: " << PP(pos) << std::endl;
+ return NULL;
}
- // If could not load and allowed to generate,
- // start generation inside this same envlock
- if (allow_gen && (block == NULL || !block->isGenerated())) {
- EMERGE_DBG_OUT("generating");
- *b = block;
- return map->initBlockMake(data, p);
+ v3s16 minp = bmdata->blockpos_min * MAP_BLOCKSIZE;
+ v3s16 maxp = bmdata->blockpos_max * MAP_BLOCKSIZE +
+ v3s16(1,1,1) * (MAP_BLOCKSIZE - 1);
+
+ // Ignore map edit events, they will not need to be sent
+ // to anybody because the block hasn't been sent to anybody
+ MapEditEventAreaIgnorer ign(
+ &m_server->m_ignore_map_edit_events_area,
+ VoxelArea(minp, maxp));
+
+ /*
+ Run Lua on_generated callbacks
+ */
+ try {
+ m_server->getScriptIface()->environment_OnGenerated(
+ minp, maxp, m_mapgen->blockseed);
+ } catch (LuaError &e) {
+ m_server->setAsyncFatalError("Lua: " + std::string(e.what()));
}
- *b = block;
- return false;
+ EMERGE_DBG_OUT("ended up with: " << analyze_block(block));
+
+ /*
+ Activate the block
+ */
+ m_server->m_env->activateBlock(block, 0);
+
+ return block;
}
-void *EmergeThread::Thread()
+void *EmergeThread::run()
{
- ThreadStarted();
- log_register_thread("EmergeThread" + itos(id));
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
BEGIN_DEBUG_EXCEPTION_HANDLER
- v3s16 last_tried_pos(-32768,-32768,-32768); // For error output
- v3s16 p;
- u8 flags = 0;
-
- map = (ServerMap *)&(m_server->m_env->getMap());
- emerge = m_server->m_emerge;
- mapgen = emerge->mapgen[id];
- enable_mapgen_debug_info = emerge->mapgen_debug_info;
+ v3s16 pos;
- porting::setThreadName("EmergeThread");
+ m_map = (ServerMap *)&(m_server->m_env->getMap());
+ m_emerge = m_server->m_emerge;
+ m_mapgen = m_emerge->m_mapgens[id];
+ enable_mapgen_debug_info = m_emerge->enable_mapgen_debug_info;
- while (!StopRequested())
try {
- if (!popBlockEmerge(&p, &flags)) {
- qevent.wait();
+ while (!stopRequested()) {
+ std::map<v3s16, MapBlock *> modified_blocks;
+ BlockEmergeData bedata;
+ BlockMakeData bmdata;
+ EmergeAction action;
+ MapBlock *block;
+
+ if (!popBlockEmerge(&pos, &bedata)) {
+ m_queue_event.wait();
continue;
}
- last_tried_pos = p;
- if (blockpos_over_limit(p))
+ if (blockpos_over_limit(pos))
continue;
- bool allow_generate = flags & BLOCK_EMERGE_ALLOWGEN;
- EMERGE_DBG_OUT("p=" PP(p) " allow_generate=" << allow_generate);
-
- /*
- Try to fetch block from memory or disk.
- If not found and asked to generate, initialize generator.
- */
- BlockMakeData data;
- MapBlock *block = NULL;
- std::map<v3s16, MapBlock *> modified_blocks;
+ bool allow_gen = bedata.flags & BLOCK_EMERGE_ALLOW_GEN;
+ EMERGE_DBG_OUT("pos=" PP(pos) " allow_gen=" << allow_gen);
- if (getBlockOrStartGen(p, &block, &data, allow_generate) && mapgen) {
+ action = getBlockOrStartGen(pos, allow_gen, &block, &bmdata);
+ if (action == EMERGE_GENERATED) {
{
- ScopeProfiler sp(g_profiler, "EmergeThread: Mapgen::makeChunk", SPT_AVG);
+ ScopeProfiler sp(g_profiler,
+ "EmergeThread: Mapgen::makeChunk", SPT_AVG);
TimeTaker t("mapgen::make_block()");
- mapgen->makeChunk(&data);
+ m_mapgen->makeChunk(&bmdata);
if (enable_mapgen_debug_info == false)
t.stop(true); // Hide output
}
- {
- //envlock: usually 0ms, but can take either 30 or 400ms to acquire
- JMutexAutoLock envlock(m_server->m_env_mutex);
- ScopeProfiler sp(g_profiler, "EmergeThread: after "
- "Mapgen::makeChunk (envlock)", SPT_AVG);
-
- map->finishBlockMake(&data, modified_blocks);
-
- block = map->getBlockNoCreateNoEx(p);
- if (block) {
- /*
- Do some post-generate stuff
- */
- v3s16 minp = data.blockpos_min * MAP_BLOCKSIZE;
- v3s16 maxp = data.blockpos_max * MAP_BLOCKSIZE +
- v3s16(1,1,1) * (MAP_BLOCKSIZE - 1);
-
- // Ignore map edit events, they will not need to be sent
- // to anybody because the block hasn't been sent to anybody
- MapEditEventAreaIgnorer
- ign(&m_server->m_ignore_map_edit_events_area,
- VoxelArea(minp, maxp));
- try { // takes about 90ms with -O1 on an e3-1230v2
- m_server->getScriptIface()->environment_OnGenerated(
- minp, maxp, mapgen->blockseed);
- } catch (LuaError &e) {
- m_server->setAsyncFatalError("Lua: " + std::string(e.what()));
- }
-
- EMERGE_DBG_OUT("ended up with: " << analyze_block(block));
-
- m_server->m_env->activateBlock(block, 0);
- }
- }
+ block = finishGen(pos, &bmdata, &modified_blocks);
}
- /*
- Set sent status of modified blocks on clients
- */
- // Add the originally fetched block to the modified list
+ runCompletionCallbacks(pos, action, bedata.callbacks);
+
if (block)
- modified_blocks[p] = block;
+ modified_blocks[pos] = block;
- if (modified_blocks.size() > 0) {
+ if (modified_blocks.size() > 0)
m_server->SetBlocksNotSent(modified_blocks);
- }
}
- catch (VersionMismatchException &e) {
+ } catch (VersionMismatchException &e) {
std::ostringstream err;
- err << "World data version mismatch in MapBlock " << PP(last_tried_pos) << std::endl
+ err << "World data version mismatch in MapBlock " << PP(pos) << std::endl
<< "----" << std::endl
<< "\"" << e.what() << "\"" << std::endl
<< "See debug.txt." << std::endl
<< "World probably saved by a newer version of " PROJECT_NAME_C "."
<< std::endl;
m_server->setAsyncFatalError(err.str());
- }
- catch (SerializationError &e) {
+ } catch (SerializationError &e) {
std::ostringstream err;
- err << "Invalid data in MapBlock " << PP(last_tried_pos) << std::endl
+ err << "Invalid data in MapBlock " << PP(pos) << std::endl
<< "----" << std::endl
<< "\"" << e.what() << "\"" << std::endl
<< "See debug.txt." << std::endl
@@ -537,24 +721,6 @@ void *EmergeThread::Thread()
m_server->setAsyncFatalError(err.str());
}
- {
- JMutexAutoLock queuelock(emerge->queuemutex);
- while (!blockqueue.empty())
- {
- v3s16 p = blockqueue.front();
- blockqueue.pop();
-
- std::map<v3s16, BlockEmergeData *>::iterator iter;
- iter = emerge->blocks_enqueued.find(p);
- if (iter == emerge->blocks_enqueued.end())
- continue; //uh oh, queue and map out of sync!!
-
- BlockEmergeData *bedata = iter->second;
- delete bedata;
- }
- }
-
- END_DEBUG_EXCEPTION_HANDLER(errorstream)
- log_deregister_thread();
+ END_DEBUG_EXCEPTION_HANDLER
return NULL;
}
diff --git a/src/emerge.h b/src/emerge.h
index 1653199ec..825ac1c0f 100644
--- a/src/emerge.h
+++ b/src/emerge.h
@@ -26,13 +26,13 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "mapgen.h" // for MapgenParams
#include "map.h"
-#define BLOCK_EMERGE_ALLOWGEN (1<<0)
+#define BLOCK_EMERGE_ALLOW_GEN (1 << 0)
+#define BLOCK_EMERGE_FORCE_QUEUE (1 << 1)
-#define EMERGE_DBG_OUT(x) \
- do { \
- if (enable_mapgen_debug_info) \
- infostream << "EmergeThread: " x << std::endl; \
- } while (0)
+#define EMERGE_DBG_OUT(x) do { \
+ if (enable_mapgen_debug_info) \
+ infostream << "EmergeThread: " x << std::endl; \
+} while (0)
class EmergeThread;
class INodeDefManager;
@@ -43,6 +43,7 @@ class OreManager;
class DecorationManager;
class SchematicManager;
+// Structure containing inputs/outputs for chunk generation
struct BlockMakeData {
MMVManip *vmanip;
u64 seed;
@@ -61,60 +62,118 @@ struct BlockMakeData {
~BlockMakeData() { delete vmanip; }
};
+// Result from processing an item on the emerge queue
+enum EmergeAction {
+ EMERGE_CANCELLED,
+ EMERGE_ERRORED,
+ EMERGE_FROM_MEMORY,
+ EMERGE_FROM_DISK,
+ EMERGE_GENERATED,
+};
+
+// Callback
+typedef void (*EmergeCompletionCallback)(
+ v3s16 blockpos, EmergeAction action, void *param);
+
+typedef std::vector<
+ std::pair<
+ EmergeCompletionCallback,
+ void *
+ >
+> EmergeCallbackList;
+
struct BlockEmergeData {
u16 peer_requested;
- u8 flags;
+ u16 flags;
+ EmergeCallbackList callbacks;
};
class EmergeManager {
public:
INodeDefManager *ndef;
+ bool enable_mapgen_debug_info;
- std::vector<Mapgen *> mapgen;
- std::vector<EmergeThread *> emergethread;
-
- bool threads_active;
-
- //settings
- MapgenParams params;
- bool mapgen_debug_info;
- u16 qlimit_total;
- u16 qlimit_diskonly;
- u16 qlimit_generate;
-
+ // Generation Notify
u32 gen_notify_on;
std::set<u32> gen_notify_on_deco_ids;
- //// Block emerge queue data structures
- JMutex queuemutex;
- std::map<v3s16, BlockEmergeData *> blocks_enqueued;
- std::map<u16, u16> peer_queue_count;
+ // Map generation parameters
+ MapgenParams params;
- //// Managers of map generation-related components
+ // Managers of various map generation-related components
BiomeManager *biomemgr;
OreManager *oremgr;
DecorationManager *decomgr;
SchematicManager *schemmgr;
- //// Methods
+ // Methods
EmergeManager(IGameDef *gamedef);
~EmergeManager();
void loadMapgenParams();
- static MapgenSpecificParams *createMapgenParams(const std::string &mgname);
void initMapgens();
- Mapgen *getCurrentMapgen();
- Mapgen *createMapgen(const std::string &mgname, int mgid,
- MapgenParams *mgparams);
- static void getMapgenNames(std::list<const char *> &mgnames);
+
void startThreads();
void stopThreads();
- bool enqueueBlockEmerge(u16 peer_id, v3s16 p, bool allow_generate);
+ bool isRunning();
+
+ bool enqueueBlockEmerge(
+ u16 peer_id,
+ v3s16 blockpos,
+ bool allow_generate,
+ bool ignore_queue_limits=false);
+
+ bool enqueueBlockEmergeEx(
+ v3s16 blockpos,
+ u16 peer_id,
+ u16 flags,
+ EmergeCompletionCallback callback,
+ void *callback_param);
- //mapgen helper methods
+ v3s16 getContainingChunk(v3s16 blockpos);
+
+ Mapgen *getCurrentMapgen();
+
+ // Mapgen helpers methods
Biome *getBiomeAtPoint(v3s16 p);
+ int getSpawnLevelAtPoint(v2s16 p);
int getGroundLevelAtPoint(v2s16 p);
bool isBlockUnderground(v3s16 blockpos);
+
+ static MapgenFactory *getMapgenFactory(const std::string &mgname);
+ static void getMapgenNames(
+ std::vector<const char *> *mgnames, bool include_hidden);
+ static v3s16 getContainingChunk(v3s16 blockpos, s16 chunksize);
+
+private:
+ std::vector<Mapgen *> m_mapgens;
+ std::vector<EmergeThread *> m_threads;
+ bool m_threads_active;
+
+ Mutex m_queue_mutex;
+ std::map<v3s16, BlockEmergeData> m_blocks_enqueued;
+ std::map<u16, u16> m_peer_queue_count;
+
+ u16 m_qlimit_total;
+ u16 m_qlimit_diskonly;
+ u16 m_qlimit_generate;
+
+ // Requires m_queue_mutex held
+ EmergeThread *getOptimalThread();
+
+ bool pushBlockEmergeData(
+ v3s16 pos,
+ u16 peer_requested,
+ u16 flags,
+ EmergeCompletionCallback callback,
+ void *callback_param,
+ bool *entry_already_exists);
+
+ bool popBlockEmergeData(v3s16 pos, BlockEmergeData *bedata);
+
+ friend class EmergeThread;
+
+ DISABLE_CLASS_COPY(EmergeManager);
};
#endif
diff --git a/src/environment.cpp b/src/environment.cpp
index dbbfc6f1f..413bc7ff1 100644
--- a/src/environment.cpp
+++ b/src/environment.cpp
@@ -44,19 +44,27 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "map.h"
#include "emerge.h"
#include "util/serialize.h"
-#include "jthread/jmutexautolock.h"
+#include "threading/mutex_auto_lock.h"
#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
+#define LBM_NAME_ALLOWED_CHARS "abcdefghijklmnopqrstuvwxyz0123456789_:"
+
+// A number that is much smaller than the timeout for particle spawners should/could ever be
+#define PARTICLE_SPAWNER_NO_EXPIRY -1024.f
+
Environment::Environment():
+ m_time_of_day_speed(0),
m_time_of_day(9000),
m_time_of_day_f(9000./24000),
- m_time_of_day_speed(0),
- m_time_counter(0),
+ m_time_conversion_skew(0.0f),
m_enable_day_night_ratio_override(false),
m_day_night_ratio_override(0.0f)
{
m_cache_enable_shaders = g_settings->getBool("enable_shaders");
+ 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");
}
Environment::~Environment()
@@ -70,7 +78,7 @@ Environment::~Environment()
void Environment::addPlayer(Player *player)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
/*
Check that peer_ids are unique.
Also check that names are unique.
@@ -85,28 +93,11 @@ void Environment::addPlayer(Player *player)
m_players.push_back(player);
}
-void Environment::removePlayer(u16 peer_id)
-{
- DSTACK(__FUNCTION_NAME);
-
- for(std::vector<Player*>::iterator i = m_players.begin();
- i != m_players.end();)
- {
- Player *player = *i;
- if(player->peer_id == peer_id) {
- delete player;
- i = m_players.erase(i);
- } else {
- ++i;
- }
- }
-}
-
-void Environment::removePlayer(const char *name)
+void Environment::removePlayer(Player* player)
{
for (std::vector<Player*>::iterator it = m_players.begin();
it != m_players.end(); ++it) {
- if (strcmp((*it)->getName(), name) == 0) {
+ if ((*it) == player) {
delete *it;
m_players.erase(it);
return;
@@ -197,74 +188,91 @@ std::vector<Player*> Environment::getPlayers(bool ignore_disconnected)
u32 Environment::getDayNightRatio()
{
- if(m_enable_day_night_ratio_override)
+ MutexAutoLock lock(this->m_time_lock);
+ if (m_enable_day_night_ratio_override)
return m_day_night_ratio_override;
- return time_to_daynight_ratio(m_time_of_day_f*24000, m_cache_enable_shaders);
+ return time_to_daynight_ratio(m_time_of_day_f * 24000, m_cache_enable_shaders);
}
void Environment::setTimeOfDaySpeed(float speed)
{
- JMutexAutoLock(this->m_timeofday_lock);
m_time_of_day_speed = speed;
}
float Environment::getTimeOfDaySpeed()
{
- JMutexAutoLock(this->m_timeofday_lock);
- float retval = m_time_of_day_speed;
- return retval;
+ return m_time_of_day_speed;
+}
+
+void Environment::setDayNightRatioOverride(bool enable, u32 value)
+{
+ MutexAutoLock lock(this->m_time_lock);
+ m_enable_day_night_ratio_override = enable;
+ m_day_night_ratio_override = value;
}
void Environment::setTimeOfDay(u32 time)
{
- JMutexAutoLock(this->m_time_lock);
+ MutexAutoLock lock(this->m_time_lock);
+ if (m_time_of_day > time)
+ m_day_count++;
m_time_of_day = time;
m_time_of_day_f = (float)time / 24000.0;
}
u32 Environment::getTimeOfDay()
{
- JMutexAutoLock(this->m_time_lock);
- u32 retval = m_time_of_day;
- return retval;
+ MutexAutoLock lock(this->m_time_lock);
+ return m_time_of_day;
}
float Environment::getTimeOfDayF()
{
- JMutexAutoLock(this->m_time_lock);
- float retval = m_time_of_day_f;
- return retval;
+ MutexAutoLock lock(this->m_time_lock);
+ return m_time_of_day_f;
}
void Environment::stepTimeOfDay(float dtime)
{
- // getTimeOfDaySpeed lock the value we need to prevent MT problems
- float day_speed = getTimeOfDaySpeed();
+ MutexAutoLock lock(this->m_time_lock);
+
+ // Cached in order to prevent the two reads we do to give
+ // different results (can be written by code not under the lock)
+ f32 cached_time_of_day_speed = m_time_of_day_speed;
- m_time_counter += dtime;
- f32 speed = day_speed * 24000./(24.*3600);
- u32 units = (u32)(m_time_counter*speed);
+ f32 speed = cached_time_of_day_speed * 24000. / (24. * 3600);
+ m_time_conversion_skew += dtime;
+ u32 units = (u32)(m_time_conversion_skew * speed);
bool sync_f = false;
- if(units > 0){
+ if (units > 0) {
// Sync at overflow
- if(m_time_of_day + units >= 24000)
+ if (m_time_of_day + units >= 24000) {
sync_f = true;
+ m_day_count++;
+ }
m_time_of_day = (m_time_of_day + units) % 24000;
- if(sync_f)
+ if (sync_f)
m_time_of_day_f = (float)m_time_of_day / 24000.0;
}
if (speed > 0) {
- m_time_counter -= (f32)units / speed;
+ m_time_conversion_skew -= (f32)units / speed;
}
- if(!sync_f){
- m_time_of_day_f += day_speed/24/3600*dtime;
- if(m_time_of_day_f > 1.0)
+ if (!sync_f) {
+ m_time_of_day_f += cached_time_of_day_speed / 24 / 3600 * dtime;
+ if (m_time_of_day_f > 1.0)
m_time_of_day_f -= 1.0;
- if(m_time_of_day_f < 0.0)
+ if (m_time_of_day_f < 0.0)
m_time_of_day_f += 1.0;
}
}
+u32 Environment::getDayCount()
+{
+ // Atomic<u32> counter
+ return m_day_count;
+}
+
+
/*
ABMWithState
*/
@@ -282,6 +290,223 @@ ABMWithState::ABMWithState(ActiveBlockModifier *abm_):
}
/*
+ LBMManager
+*/
+
+void LBMContentMapping::deleteContents()
+{
+ for (std::vector<LoadingBlockModifierDef *>::iterator it = lbm_list.begin();
+ it != lbm_list.end(); ++it) {
+ delete *it;
+ }
+}
+
+void LBMContentMapping::addLBM(LoadingBlockModifierDef *lbm_def, IGameDef *gamedef)
+{
+ // Add the lbm_def to the LBMContentMapping.
+ // Unknown names get added to the global NameIdMapping.
+ INodeDefManager *nodedef = gamedef->ndef();
+
+ lbm_list.push_back(lbm_def);
+
+ for (std::set<std::string>::const_iterator it = lbm_def->trigger_contents.begin();
+ it != lbm_def->trigger_contents.end(); ++it) {
+ std::set<content_t> c_ids;
+ bool found = nodedef->getIds(*it, c_ids);
+ if (!found) {
+ content_t c_id = gamedef->allocateUnknownNodeId(*it);
+ if (c_id == CONTENT_IGNORE) {
+ // Seems it can't be allocated.
+ warningstream << "Could not internalize node name \"" << *it
+ << "\" while loading LBM \"" << lbm_def->name << "\"." << std::endl;
+ continue;
+ }
+ c_ids.insert(c_id);
+ }
+
+ for (std::set<content_t>::const_iterator iit =
+ c_ids.begin(); iit != c_ids.end(); ++iit) {
+ content_t c_id = *iit;
+ map[c_id].push_back(lbm_def);
+ }
+ }
+}
+
+const std::vector<LoadingBlockModifierDef *> *
+ LBMContentMapping::lookup(content_t c) const
+{
+ container_map::const_iterator it = map.find(c);
+ if (it == map.end())
+ return NULL;
+ // This first dereferences the iterator, returning
+ // a std::vector<LoadingBlockModifierDef *>
+ // reference, then we convert it to a pointer.
+ return &(it->second);
+}
+
+LBMManager::~LBMManager()
+{
+ for (std::map<std::string, LoadingBlockModifierDef *>::iterator it =
+ m_lbm_defs.begin(); it != m_lbm_defs.end(); ++it) {
+ delete it->second;
+ }
+ for (lbm_lookup_map::iterator it = m_lbm_lookup.begin();
+ it != m_lbm_lookup.end(); ++it) {
+ (it->second).deleteContents();
+ }
+}
+
+void LBMManager::addLBMDef(LoadingBlockModifierDef *lbm_def)
+{
+ // Precondition, in query mode the map isn't used anymore
+ FATAL_ERROR_IF(m_query_mode == true,
+ "attempted to modify LBMManager in query mode");
+
+ if (!string_allowed(lbm_def->name, LBM_NAME_ALLOWED_CHARS)) {
+ throw ModError("Error adding LBM \"" + lbm_def->name +
+ "\": Does not follow naming conventions: "
+ "Only chararacters [a-z0-9_:] are allowed.");
+ }
+
+ m_lbm_defs[lbm_def->name] = lbm_def;
+}
+
+void LBMManager::loadIntroductionTimes(const std::string &times,
+ IGameDef *gamedef, u32 now)
+{
+ m_query_mode = true;
+
+ // name -> time map.
+ // Storing it in a map first instead of
+ // handling the stuff directly in the loop
+ // removes all duplicate entries.
+ // TODO make this std::unordered_map
+ std::map<std::string, u32> introduction_times;
+
+ /*
+ The introduction times string consists of name~time entries,
+ with each entry terminated by a semicolon. The time is decimal.
+ */
+
+ size_t idx = 0;
+ size_t idx_new;
+ while ((idx_new = times.find(";", idx)) != std::string::npos) {
+ std::string entry = times.substr(idx, idx_new - idx);
+ std::vector<std::string> components = str_split(entry, '~');
+ if (components.size() != 2)
+ throw SerializationError("Introduction times entry \""
+ + entry + "\" requires exactly one '~'!");
+ const std::string &name = components[0];
+ u32 time = from_string<u32>(components[1]);
+ introduction_times[name] = time;
+ idx = idx_new + 1;
+ }
+
+ // Put stuff from introduction_times into m_lbm_lookup
+ for (std::map<std::string, u32>::const_iterator it = introduction_times.begin();
+ it != introduction_times.end(); ++it) {
+ const std::string &name = it->first;
+ u32 time = it->second;
+
+ std::map<std::string, LoadingBlockModifierDef *>::iterator def_it =
+ m_lbm_defs.find(name);
+ if (def_it == m_lbm_defs.end()) {
+ // This seems to be an LBM entry for
+ // an LBM we haven't loaded. Discard it.
+ continue;
+ }
+ LoadingBlockModifierDef *lbm_def = def_it->second;
+ if (lbm_def->run_at_every_load) {
+ // This seems to be an LBM entry for
+ // an LBM that runs at every load.
+ // Don't add it just yet.
+ continue;
+ }
+
+ m_lbm_lookup[time].addLBM(lbm_def, gamedef);
+
+ // Erase the entry so that we know later
+ // what elements didn't get put into m_lbm_lookup
+ m_lbm_defs.erase(name);
+ }
+
+ // Now also add the elements from m_lbm_defs to m_lbm_lookup
+ // that weren't added in the previous step.
+ // They are introduced first time to this world,
+ // or are run at every load (introducement time hardcoded to U32_MAX).
+
+ LBMContentMapping &lbms_we_introduce_now = m_lbm_lookup[now];
+ LBMContentMapping &lbms_running_always = m_lbm_lookup[U32_MAX];
+
+ for (std::map<std::string, LoadingBlockModifierDef *>::iterator it =
+ m_lbm_defs.begin(); it != m_lbm_defs.end(); ++it) {
+ if (it->second->run_at_every_load) {
+ lbms_running_always.addLBM(it->second, gamedef);
+ } else {
+ lbms_we_introduce_now.addLBM(it->second, gamedef);
+ }
+ }
+
+ // Clear the list, so that we don't delete remaining elements
+ // twice in the destructor
+ m_lbm_defs.clear();
+}
+
+std::string LBMManager::createIntroductionTimesString()
+{
+ // Precondition, we must be in query mode
+ FATAL_ERROR_IF(m_query_mode == false,
+ "attempted to query on non fully set up LBMManager");
+
+ std::ostringstream oss;
+ for (lbm_lookup_map::iterator it = m_lbm_lookup.begin();
+ it != m_lbm_lookup.end(); ++it) {
+ u32 time = it->first;
+ std::vector<LoadingBlockModifierDef *> &lbm_list = it->second.lbm_list;
+ for (std::vector<LoadingBlockModifierDef *>::iterator iit = lbm_list.begin();
+ iit != lbm_list.end(); ++iit) {
+ // Don't add if the LBM runs at every load,
+ // then introducement time is hardcoded
+ // and doesn't need to be stored
+ if ((*iit)->run_at_every_load)
+ continue;
+ oss << (*iit)->name << "~" << time << ";";
+ }
+ }
+ return oss.str();
+}
+
+void LBMManager::applyLBMs(ServerEnvironment *env, MapBlock *block, u32 stamp)
+{
+ // Precondition, we need m_lbm_lookup to be initialized
+ FATAL_ERROR_IF(m_query_mode == false,
+ "attempted to query on non fully set up LBMManager");
+ v3s16 pos_of_block = block->getPosRelative();
+ v3s16 pos;
+ MapNode n;
+ content_t c;
+ lbm_lookup_map::const_iterator it = getLBMsIntroducedAfter(stamp);
+ for (pos.X = 0; pos.X < MAP_BLOCKSIZE; pos.X++)
+ for (pos.Y = 0; pos.Y < MAP_BLOCKSIZE; pos.Y++)
+ for (pos.Z = 0; pos.Z < MAP_BLOCKSIZE; pos.Z++)
+ {
+ n = block->getNodeNoEx(pos);
+ c = n.getContent();
+ for (LBMManager::lbm_lookup_map::const_iterator iit = it;
+ iit != m_lbm_lookup.end(); ++iit) {
+ const std::vector<LoadingBlockModifierDef *> *lbm_list =
+ iit->second.lookup(c);
+ if (!lbm_list)
+ continue;
+ for (std::vector<LoadingBlockModifierDef *>::const_iterator iit =
+ lbm_list->begin(); iit != lbm_list->end(); ++iit) {
+ (*iit)->trigger(env, pos + pos_of_block, n);
+ }
+ }
+ }
+}
+
+/*
ActiveBlockList
*/
@@ -365,6 +590,7 @@ ServerEnvironment::ServerEnvironment(ServerMap *map,
m_active_block_interval_overload_skip(0),
m_game_time(0),
m_game_time_fraction_counter(0),
+ m_last_clear_objects_time(0),
m_recommended_send_interval(0.1),
m_max_lag_estimate(0.1)
{
@@ -453,15 +679,12 @@ void ServerEnvironment::saveLoadedPlayers()
}
}
-void ServerEnvironment::savePlayer(const std::string &playername)
+void ServerEnvironment::savePlayer(RemotePlayer *player)
{
std::string players_path = m_path_world + DIR_DELIM "players";
fs::CreateDir(players_path);
- RemotePlayer *player = static_cast<RemotePlayer*>(getPlayer(playername.c_str()));
- if (player) {
- player->save(players_path);
- }
+ player->save(players_path);
}
Player *ServerEnvironment::loadPlayer(const std::string &playername)
@@ -517,6 +740,11 @@ void ServerEnvironment::saveMeta()
Settings args;
args.setU64("game_time", m_game_time);
args.setU64("time_of_day", getTimeOfDay());
+ args.setU64("last_clear_objects_time", m_last_clear_objects_time);
+ args.setU64("lbm_introduction_times_version", 1);
+ args.set("lbm_introduction_times",
+ m_lbm_mgr.createIntroductionTimesString());
+ args.setU64("day_count", m_day_count);
args.writeLines(ss);
ss<<"EnvArgsEnd\n";
@@ -554,12 +782,35 @@ void ServerEnvironment::loadMeta()
throw SerializationError("Couldn't load env meta game_time");
}
+ setTimeOfDay(args.exists("time_of_day") ?
+ // set day to morning by default
+ args.getU64("time_of_day") : 9000);
+
+ m_last_clear_objects_time = args.exists("last_clear_objects_time") ?
+ // If missing, do as if clearObjects was never called
+ args.getU64("last_clear_objects_time") : 0;
+
+ std::string lbm_introduction_times = "";
try {
- m_time_of_day = args.getU64("time_of_day");
+ u64 ver = args.getU64("lbm_introduction_times_version");
+ if (ver == 1) {
+ lbm_introduction_times = args.get("lbm_introduction_times");
+ } else {
+ infostream << "ServerEnvironment::loadMeta(): Non-supported"
+ << " introduction time version " << ver << std::endl;
+ }
} catch (SettingNotFoundException &e) {
- // This is not as important
- m_time_of_day = 9000;
+ // No problem, this is expected. Just continue with an empty string
}
+ m_lbm_mgr.loadIntroductionTimes(lbm_introduction_times, m_gamedef, m_game_time);
+
+ m_day_count = args.exists("day_count") ?
+ args.getU64("day_count") : 0;
+}
+
+void ServerEnvironment::loadDefaultMeta()
+{
+ m_lbm_mgr.loadIntroductionTimes("", m_gamedef, m_game_time);
}
struct ActiveABM
@@ -597,35 +848,39 @@ public:
i->timer -= trigger_interval;
actual_interval = trigger_interval;
}
- float intervals = actual_interval / trigger_interval;
- if(intervals == 0)
- continue;
float chance = abm->getTriggerChance();
if(chance == 0)
chance = 1;
ActiveABM aabm;
aabm.abm = abm;
- aabm.chance = chance / intervals;
- if(aabm.chance == 0)
- aabm.chance = 1;
+ if(abm->getSimpleCatchUp()) {
+ float intervals = actual_interval / trigger_interval;
+ if(intervals == 0)
+ continue;
+ aabm.chance = chance / intervals;
+ if(aabm.chance == 0)
+ aabm.chance = 1;
+ } else {
+ aabm.chance = chance;
+ }
// Trigger neighbors
std::set<std::string> required_neighbors_s
= abm->getRequiredNeighbors();
for(std::set<std::string>::iterator
i = required_neighbors_s.begin();
- i != required_neighbors_s.end(); i++)
+ i != required_neighbors_s.end(); ++i)
{
ndef->getIds(*i, aabm.required_neighbors);
}
// Trigger contents
std::set<std::string> contents_s = abm->getTriggerContents();
for(std::set<std::string>::iterator
- i = contents_s.begin(); i != contents_s.end(); i++)
+ i = contents_s.begin(); i != contents_s.end(); ++i)
{
std::set<content_t> ids;
ndef->getIds(*i, ids);
for(std::set<content_t>::const_iterator k = ids.begin();
- k != ids.end(); k++)
+ k != ids.end(); ++k)
{
content_t c = *k;
std::map<content_t, std::vector<ActiveABM> >::iterator j;
@@ -694,7 +949,7 @@ public:
continue;
for(std::vector<ActiveABM>::iterator
- i = j->second.begin(); i != j->second.end(); i++) {
+ i = j->second.begin(); i != j->second.end(); ++i) {
if(myrand() % i->chance != 0)
continue;
@@ -749,13 +1004,19 @@ void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
// Get time difference
u32 dtime_s = 0;
u32 stamp = block->getTimestamp();
- if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
- dtime_s = m_game_time - block->getTimestamp();
+ if (m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
+ dtime_s = m_game_time - stamp;
dtime_s += additional_dtime;
/*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
<<stamp<<", game time: "<<m_game_time<<std::endl;*/
+ // Remove stored static objects if clearObjects was called since block's timestamp
+ if (stamp == BLOCK_TIMESTAMP_UNDEFINED || stamp < m_last_clear_objects_time) {
+ block->m_static_objects.m_stored.clear();
+ // do not set changed flag to avoid unnecessary mapblock writes
+ }
+
// Set current time as timestamp
block->setTimestampNoChangedFlag(m_game_time);
@@ -765,6 +1026,9 @@ void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
// Activate stored objects
activateObjects(block, dtime_s);
+ /* Handle LoadingBlockModifiers */
+ m_lbm_mgr.applyLBMs(this, block, stamp);
+
// Run node timers
std::map<v3s16, NodeTimer> elapsed_timers =
block->m_node_timers.step((float)dtime_s);
@@ -772,7 +1036,7 @@ void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
MapNode n;
for(std::map<v3s16, NodeTimer>::iterator
i = elapsed_timers.begin();
- i != elapsed_timers.end(); i++){
+ i != elapsed_timers.end(); ++i){
n = block->getNodeNoEx(i->first);
v3s16 p = i->first + block->getPosRelative();
if(m_script->node_on_timer(p,n,i->second.elapsed))
@@ -790,6 +1054,11 @@ void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
m_abms.push_back(ABMWithState(abm));
}
+void ServerEnvironment::addLoadingBlockModifierDef(LoadingBlockModifierDef *lbm)
+{
+ m_lbm_mgr.addLBMDef(lbm);
+}
+
bool ServerEnvironment::setNode(v3s16 p, const MapNode &n)
{
INodeDefManager *ndef = m_gamedef->ndef();
@@ -868,22 +1137,22 @@ void ServerEnvironment::getObjectsInsideRadius(std::vector<u16> &objects, v3f po
}
}
-void ServerEnvironment::clearAllObjects()
+void ServerEnvironment::clearObjects(ClearObjectsMode mode)
{
- infostream<<"ServerEnvironment::clearAllObjects(): "
- <<"Removing all active objects"<<std::endl;
+ infostream << "ServerEnvironment::clearObjects(): "
+ << "Removing all active objects" << std::endl;
std::vector<u16> objects_to_remove;
- for(std::map<u16, ServerActiveObject*>::iterator
+ for (std::map<u16, ServerActiveObject*>::iterator
i = m_active_objects.begin();
i != m_active_objects.end(); ++i) {
ServerActiveObject* obj = i->second;
- if(obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
+ if (obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
continue;
u16 id = i->first;
// Delete static object if block is loaded
- if(obj->m_static_exists){
+ if (obj->m_static_exists) {
MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
- if(block){
+ if (block) {
block->m_static_objects.remove(id);
block->raiseModified(MOD_STATE_WRITE_NEEDED,
MOD_REASON_CLEAR_ALL_OBJECTS);
@@ -891,7 +1160,7 @@ void ServerEnvironment::clearAllObjects()
}
}
// If known by some client, don't delete immediately
- if(obj->m_known_by_count > 0){
+ if (obj->m_known_by_count > 0) {
obj->m_pending_deactivation = true;
obj->m_removed = true;
continue;
@@ -903,39 +1172,46 @@ void ServerEnvironment::clearAllObjects()
m_script->removeObjectReference(obj);
// Delete active object
- if(obj->environmentDeletes())
+ if (obj->environmentDeletes())
delete obj;
// Id to be removed from m_active_objects
objects_to_remove.push_back(id);
}
// Remove references from m_active_objects
- for(std::vector<u16>::iterator i = objects_to_remove.begin();
+ for (std::vector<u16>::iterator i = objects_to_remove.begin();
i != objects_to_remove.end(); ++i) {
m_active_objects.erase(*i);
}
// Get list of loaded blocks
std::vector<v3s16> loaded_blocks;
- infostream<<"ServerEnvironment::clearAllObjects(): "
- <<"Listing all loaded blocks"<<std::endl;
+ infostream << "ServerEnvironment::clearObjects(): "
+ << "Listing all loaded blocks" << std::endl;
m_map->listAllLoadedBlocks(loaded_blocks);
- infostream<<"ServerEnvironment::clearAllObjects(): "
- <<"Done listing all loaded blocks: "
- <<loaded_blocks.size()<<std::endl;
+ infostream << "ServerEnvironment::clearObjects(): "
+ << "Done listing all loaded blocks: "
+ << loaded_blocks.size()<<std::endl;
// Get list of loadable blocks
std::vector<v3s16> loadable_blocks;
- infostream<<"ServerEnvironment::clearAllObjects(): "
- <<"Listing all loadable blocks"<<std::endl;
- m_map->listAllLoadableBlocks(loadable_blocks);
- infostream<<"ServerEnvironment::clearAllObjects(): "
- <<"Done listing all loadable blocks: "
- <<loadable_blocks.size()
- <<", now clearing"<<std::endl;
+ if (mode == CLEAR_OBJECTS_MODE_FULL) {
+ infostream << "ServerEnvironment::clearObjects(): "
+ << "Listing all loadable blocks" << std::endl;
+ m_map->listAllLoadableBlocks(loadable_blocks);
+ infostream << "ServerEnvironment::clearObjects(): "
+ << "Done listing all loadable blocks: "
+ << loadable_blocks.size() << std::endl;
+ } else {
+ loadable_blocks = loaded_blocks;
+ }
+
+ infostream << "ServerEnvironment::clearObjects(): "
+ << "Now clearing objects in " << loadable_blocks.size()
+ << " blocks" << std::endl;
// Grab a reference on each loaded block to avoid unloading it
- for(std::vector<v3s16>::iterator i = loaded_blocks.begin();
+ for (std::vector<v3s16>::iterator i = loaded_blocks.begin();
i != loaded_blocks.end(); ++i) {
v3s16 p = *i;
MapBlock *block = m_map->getBlockNoCreateNoEx(p);
@@ -944,24 +1220,27 @@ void ServerEnvironment::clearAllObjects()
}
// Remove objects in all loadable blocks
- u32 unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks");
- unload_interval = MYMAX(unload_interval, 1);
+ u32 unload_interval = U32_MAX;
+ if (mode == CLEAR_OBJECTS_MODE_FULL) {
+ unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks");
+ unload_interval = MYMAX(unload_interval, 1);
+ }
u32 report_interval = loadable_blocks.size() / 10;
u32 num_blocks_checked = 0;
u32 num_blocks_cleared = 0;
u32 num_objs_cleared = 0;
- for(std::vector<v3s16>::iterator i = loadable_blocks.begin();
+ for (std::vector<v3s16>::iterator i = loadable_blocks.begin();
i != loadable_blocks.end(); ++i) {
v3s16 p = *i;
MapBlock *block = m_map->emergeBlock(p, false);
- if(!block){
- errorstream<<"ServerEnvironment::clearAllObjects(): "
- <<"Failed to emerge block "<<PP(p)<<std::endl;
+ if (!block) {
+ errorstream << "ServerEnvironment::clearObjects(): "
+ << "Failed to emerge block " << PP(p) << std::endl;
continue;
}
u32 num_stored = block->m_static_objects.m_stored.size();
u32 num_active = block->m_static_objects.m_active.size();
- if(num_stored != 0 || num_active != 0){
+ if (num_stored != 0 || num_active != 0) {
block->m_static_objects.m_stored.clear();
block->m_static_objects.m_active.clear();
block->raiseModified(MOD_STATE_WRITE_NEEDED,
@@ -971,23 +1250,23 @@ void ServerEnvironment::clearAllObjects()
}
num_blocks_checked++;
- if(report_interval != 0 &&
- num_blocks_checked % report_interval == 0){
+ if (report_interval != 0 &&
+ num_blocks_checked % report_interval == 0) {
float percent = 100.0 * (float)num_blocks_checked /
- loadable_blocks.size();
- infostream<<"ServerEnvironment::clearAllObjects(): "
- <<"Cleared "<<num_objs_cleared<<" objects"
- <<" in "<<num_blocks_cleared<<" blocks ("
- <<percent<<"%)"<<std::endl;
+ loadable_blocks.size();
+ infostream << "ServerEnvironment::clearObjects(): "
+ << "Cleared " << num_objs_cleared << " objects"
+ << " in " << num_blocks_cleared << " blocks ("
+ << percent << "%)" << std::endl;
}
- if(num_blocks_checked % unload_interval == 0){
+ if (num_blocks_checked % unload_interval == 0) {
m_map->unloadUnreferencedBlocks();
}
}
m_map->unloadUnreferencedBlocks();
// Drop references that were added above
- for(std::vector<v3s16>::iterator i = loaded_blocks.begin();
+ for (std::vector<v3s16>::iterator i = loaded_blocks.begin();
i != loaded_blocks.end(); ++i) {
v3s16 p = *i;
MapBlock *block = m_map->getBlockNoCreateNoEx(p);
@@ -995,14 +1274,16 @@ void ServerEnvironment::clearAllObjects()
block->refDrop();
}
- infostream<<"ServerEnvironment::clearAllObjects(): "
- <<"Finished: Cleared "<<num_objs_cleared<<" objects"
- <<" in "<<num_blocks_cleared<<" blocks"<<std::endl;
+ m_last_clear_objects_time = m_game_time;
+
+ infostream << "ServerEnvironment::clearObjects(): "
+ << "Finished: Cleared " << num_objs_cleared << " objects"
+ << " in " << num_blocks_cleared << " blocks" << std::endl;
}
void ServerEnvironment::step(float dtime)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
//TimeTaker timer("ServerEnv step");
@@ -1012,7 +1293,8 @@ void ServerEnvironment::step(float dtime)
// Update this one
// NOTE: This is kind of funny on a singleplayer game, but doesn't
// really matter that much.
- m_recommended_send_interval = g_settings->getFloat("dedicated_server_step");
+ static const float server_step = g_settings->getFloat("dedicated_server_step");
+ m_recommended_send_interval = server_step;
/*
Increment game time
@@ -1046,9 +1328,8 @@ void ServerEnvironment::step(float dtime)
/*
Manage active block list
*/
- if(m_active_blocks_management_interval.step(dtime, 2.0))
- {
- ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg /2s", SPT_AVG);
+ if (m_active_blocks_management_interval.step(dtime, m_cache_active_block_mgmt_interval)) {
+ ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg per interval", SPT_AVG);
/*
Get player block positions
*/
@@ -1069,7 +1350,7 @@ void ServerEnvironment::step(float dtime)
/*
Update list of active blocks, collecting changes
*/
- const s16 active_block_range = g_settings->getS16("active_block_range");
+ static const s16 active_block_range = g_settings->getS16("active_block_range");
std::set<v3s16> blocks_removed;
std::set<v3s16> blocks_added;
m_active_blocks.update(players_blockpos, active_block_range,
@@ -1084,8 +1365,7 @@ void ServerEnvironment::step(float dtime)
for(std::set<v3s16>::iterator
i = blocks_removed.begin();
- i != blocks_removed.end(); ++i)
- {
+ i != blocks_removed.end(); ++i) {
v3s16 p = *i;
/* infostream<<"Server: Block " << PP(p)
@@ -1124,11 +1404,10 @@ void ServerEnvironment::step(float dtime)
/*
Mess around in active blocks
*/
- if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
- {
- ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg /1s", SPT_AVG);
+ if (m_active_blocks_nodemetadata_interval.step(dtime, m_cache_nodetimer_interval)) {
+ ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg per interval", SPT_AVG);
- float dtime = 1.0;
+ float dtime = m_cache_nodetimer_interval;
for(std::set<v3s16>::iterator
i = m_active_blocks.m_list.begin();
@@ -1161,7 +1440,7 @@ void ServerEnvironment::step(float dtime)
MapNode n;
for(std::map<v3s16, NodeTimer>::iterator
i = elapsed_timers.begin();
- i != elapsed_timers.end(); i++){
+ i != elapsed_timers.end(); ++i){
n = block->getNodeNoEx(i->first);
p = i->first + block->getPosRelative();
if(m_script->node_on_timer(p,n,i->second.elapsed))
@@ -1171,19 +1450,18 @@ void ServerEnvironment::step(float dtime)
}
}
- const float abm_interval = 1.0;
- if(m_active_block_modifier_interval.step(dtime, abm_interval))
+ if (m_active_block_modifier_interval.step(dtime, m_cache_abm_interval))
do{ // breakable
if(m_active_block_interval_overload_skip > 0){
ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
m_active_block_interval_overload_skip--;
break;
}
- ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /1s", SPT_AVG);
- TimeTaker timer("modify in active blocks");
+ ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg per interval", SPT_AVG);
+ TimeTaker timer("modify in active blocks per interval");
// Initialize handling of ActiveBlockModifiers
- ABMHandler abmhandler(m_abms, abm_interval, this, true);
+ ABMHandler abmhandler(m_abms, m_cache_abm_interval, this, true);
for(std::set<v3s16>::iterator
i = m_active_blocks.m_list.begin();
@@ -1208,7 +1486,7 @@ void ServerEnvironment::step(float dtime)
u32 time_ms = timer.stop(true);
u32 max_time_ms = 200;
if(time_ms > max_time_ms){
- infostream<<"WARNING: active block modifiers took "
+ warningstream<<"active block modifiers took "
<<time_ms<<"ms (longer than "
<<max_time_ms<<"ms)"<<std::endl;
m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
@@ -1269,6 +1547,49 @@ void ServerEnvironment::step(float dtime)
*/
removeRemovedObjects();
}
+
+ /*
+ Manage particle spawner expiration
+ */
+ if (m_particle_management_interval.step(dtime, 1.0)) {
+ for (std::map<u32, float>::iterator i = m_particle_spawners.begin();
+ i != m_particle_spawners.end(); ) {
+ //non expiring spawners
+ if (i->second == PARTICLE_SPAWNER_NO_EXPIRY) {
+ ++i;
+ continue;
+ }
+
+ i->second -= 1.0f;
+ if (i->second <= 0.f)
+ m_particle_spawners.erase(i++);
+ else
+ ++i;
+ }
+ }
+}
+
+u32 ServerEnvironment::addParticleSpawner(float exptime)
+{
+ // Timers with lifetime 0 do not expire
+ float time = exptime > 0.f ? exptime : PARTICLE_SPAWNER_NO_EXPIRY;
+
+ u32 id = 0;
+ for (;;) { // look for unused particlespawner id
+ id++;
+ std::map<u32, float>::iterator f;
+ f = m_particle_spawners.find(id);
+ if (f == m_particle_spawners.end()) {
+ m_particle_spawners[id] = time;
+ break;
+ }
+ }
+ return id;
+}
+
+void ServerEnvironment::deleteParticleSpawner(u32 id)
+{
+ m_particle_spawners.erase(id);
}
ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
@@ -1318,12 +1639,11 @@ u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
Finds out what new objects have been added to
inside a radius around a position
*/
-void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
+void ServerEnvironment::getAddedActiveObjects(Player *player, s16 radius,
s16 player_radius,
std::set<u16> &current_objects,
- std::set<u16> &added_objects)
+ std::queue<u16> &added_objects)
{
- v3f pos_f = intToFloat(pos, BS);
f32 radius_f = radius * BS;
f32 player_radius_f = player_radius * BS;
@@ -1339,18 +1659,19 @@ void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
*/
for(std::map<u16, ServerActiveObject*>::iterator
i = m_active_objects.begin();
- i != m_active_objects.end(); ++i)
- {
+ i != m_active_objects.end(); ++i) {
u16 id = i->first;
+
// Get object
ServerActiveObject *object = i->second;
if(object == NULL)
continue;
+
// Discard if removed or deactivating
if(object->m_removed || object->m_pending_deactivation)
continue;
- f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
+ f32 distance_f = object->getBasePosition().getDistanceFrom(player->getPosition());
if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
// Discard if too far
if (distance_f > player_radius_f && player_radius_f != 0)
@@ -1364,7 +1685,7 @@ void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
if(n != current_objects.end())
continue;
// Add to added_objects
- added_objects.insert(id);
+ added_objects.push(id);
}
}
@@ -1372,12 +1693,11 @@ void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
Finds out what objects have been removed from
inside a radius around a position
*/
-void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
+void ServerEnvironment::getRemovedActiveObjects(Player *player, s16 radius,
s16 player_radius,
std::set<u16> &current_objects,
- std::set<u16> &removed_objects)
+ std::queue<u16> &removed_objects)
{
- v3f pos_f = intToFloat(pos, BS);
f32 radius_f = radius * BS;
f32 player_radius_f = player_radius * BS;
@@ -1399,20 +1719,19 @@ void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
u16 id = *i;
ServerActiveObject *object = getActiveObject(id);
- if(object == NULL){
- infostream<<"ServerEnvironment::getRemovedActiveObjects():"
- <<" object in current_objects is NULL"<<std::endl;
- removed_objects.insert(id);
+ if (object == NULL) {
+ infostream << "ServerEnvironment::getRemovedActiveObjects():"
+ << " object in current_objects is NULL" << std::endl;
+ removed_objects.push(id);
continue;
}
- if(object->m_removed || object->m_pending_deactivation)
- {
- removed_objects.insert(id);
+ if (object->m_removed || object->m_pending_deactivation) {
+ removed_objects.push(id);
continue;
}
- f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
+ f32 distance_f = object->getBasePosition().getDistanceFrom(player->getPosition());
if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
if (distance_f <= player_radius_f || player_radius_f == 0)
continue;
@@ -1420,7 +1739,7 @@ void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
continue;
// Object is no longer visible
- removed_objects.insert(id);
+ removed_objects.push(id);
}
}
@@ -1493,6 +1812,17 @@ u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
delete object;
return 0;
}
+
+ if (objectpos_over_limit(object->getBasePosition())) {
+ v3f p = object->getBasePosition();
+ errorstream << "ServerEnvironment::addActiveObjectRaw(): "
+ << "object position (" << p.X << "," << p.Y << "," << p.Z
+ << ") outside maximum range" << std::endl;
+ if (object->environmentDeletes())
+ delete object;
+ return 0;
+ }
+
/*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
<<"added (id="<<object->getId()<<")"<<std::endl;*/
@@ -1921,7 +2251,7 @@ void ServerEnvironment::deactivateFarObjects(bool force_delete)
// reason. Unsuccessful attempts have been made to find
// said reason.
if(id && block->m_static_objects.m_active.find(id) != block->m_static_objects.m_active.end()){
- infostream<<"ServerEnv: WARNING: Performing hack #83274"
+ warningstream<<"ServerEnv: Performing hack #83274"
<<std::endl;
block->m_static_objects.remove(id);
}
@@ -2039,7 +2369,7 @@ ClientMap & ClientEnvironment::getClientMap()
void ClientEnvironment::addPlayer(Player *player)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
/*
It is a failure if player is local and there already is a local
player
@@ -2063,7 +2393,7 @@ LocalPlayer * ClientEnvironment::getLocalPlayer()
void ClientEnvironment::step(float dtime)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
/* Step time of day */
stepTimeOfDay(dtime);
@@ -2562,7 +2892,7 @@ void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
event.type = CEE_PLAYER_DAMAGE;
event.player_damage.amount = damage;
event.player_damage.send_to_server = handle_hp;
- m_client_event_queue.push_back(event);
+ m_client_event_queue.push(event);
}
void ClientEnvironment::updateLocalPlayerBreath(u16 breath)
@@ -2570,7 +2900,7 @@ void ClientEnvironment::updateLocalPlayerBreath(u16 breath)
ClientEnvEvent event;
event.type = CEE_PLAYER_BREATH;
event.player_breath.amount = breath;
- m_client_event_queue.push_back(event);
+ m_client_event_queue.push(event);
}
/*
@@ -2604,11 +2934,9 @@ ClientEnvEvent ClientEnvironment::getClientEvent()
event.type = CEE_NONE;
else {
event = m_client_event_queue.front();
- m_client_event_queue.pop_front();
+ m_client_event_queue.pop();
}
return event;
}
#endif // #ifndef SERVER
-
-
diff --git a/src/environment.h b/src/environment.h
index c70694316..c6786faed 100644
--- a/src/environment.h
+++ b/src/environment.h
@@ -39,7 +39,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "util/numeric.h"
#include "mapnode.h"
#include "mapblock.h"
-#include "jthread/jmutex.h"
+#include "threading/mutex.h"
+#include "threading/atomic.h"
#include "network/networkprotocol.h" // for AccessDeniedCode
class ServerEnvironment;
@@ -52,6 +53,7 @@ class ServerMap;
class ClientMap;
class GameScripting;
class Player;
+class RemotePlayer;
class Environment
{
@@ -71,8 +73,7 @@ public:
virtual Map & getMap() = 0;
virtual void addPlayer(Player *player);
- void removePlayer(u16 peer_id);
- void removePlayer(const char *name);
+ void removePlayer(Player *player);
Player * getPlayer(u16 peer_id);
Player * getPlayer(const char *name);
Player * getRandomConnectedPlayer();
@@ -92,11 +93,9 @@ public:
void setTimeOfDaySpeed(float speed);
float getTimeOfDaySpeed();
- void setDayNightRatioOverride(bool enable, u32 value)
- {
- m_enable_day_night_ratio_override = enable;
- m_day_night_ratio_override = value;
- }
+ void setDayNightRatioOverride(bool enable, u32 value);
+
+ u32 getDayCount();
// counter used internally when triggering ABMs
u32 m_added_objects;
@@ -104,16 +103,28 @@ public:
protected:
// peer_ids in here should be unique, except that there may be many 0s
std::vector<Player*> m_players;
+
+ GenericAtomic<float> m_time_of_day_speed;
+
+ /*
+ * Below: values managed by m_time_lock
+ */
// Time of day in milli-hours (0-23999); determines day and night
u32 m_time_of_day;
// Time of day in 0...1
float m_time_of_day_f;
- float m_time_of_day_speed;
- // Used to buffer dtime for adding to m_time_of_day
- float m_time_counter;
+ // Stores the skew created by the float -> u32 conversion
+ // to be applied at next conversion, so that there is no real skew.
+ float m_time_conversion_skew;
// Overriding the day-night ratio is useful for custom sky visuals
bool m_enable_day_night_ratio_override;
u32 m_day_night_ratio_override;
+ // Days from the server start, accounts for time shift
+ // in game (e.g. /time or bed usage)
+ Atomic<u32> m_day_count;
+ /*
+ * Above: values managed by m_time_lock
+ */
/* TODO: Add a callback function so these can be updated when a setting
* changes. At this point in time it doesn't matter (e.g. /set
@@ -125,15 +136,18 @@ protected:
* a later release.
*/
bool m_cache_enable_shaders;
+ float m_cache_active_block_mgmt_interval;
+ float m_cache_abm_interval;
+ float m_cache_nodetimer_interval;
private:
- JMutex m_timeofday_lock;
- JMutex m_time_lock;
+ Mutex m_time_lock;
+ DISABLE_CLASS_COPY(Environment);
};
/*
- Active block modifier interface.
+ {Active, Loading} block modifier interface.
These are fed into ServerEnvironment at initialization time;
ServerEnvironment handles deleting them.
@@ -155,6 +169,8 @@ public:
virtual float getTriggerInterval() = 0;
// Random chance of (1 / return value), 0 is disallowed
virtual u32 getTriggerChance() = 0;
+ // Whether to modify chance to simulate time lost by an unnattended block
+ virtual bool getSimpleCatchUp() = 0;
// This is called usually at interval for 1/chance of the nodes
virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n){};
virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n,
@@ -169,6 +185,77 @@ struct ABMWithState
ABMWithState(ActiveBlockModifier *abm_);
};
+struct LoadingBlockModifierDef
+{
+ // Set of contents to trigger on
+ std::set<std::string> trigger_contents;
+ std::string name;
+ bool run_at_every_load;
+
+ virtual ~LoadingBlockModifierDef() {}
+ virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n){};
+};
+
+struct LBMContentMapping
+{
+ typedef std::map<content_t, std::vector<LoadingBlockModifierDef *> > container_map;
+ container_map map;
+
+ std::vector<LoadingBlockModifierDef *> lbm_list;
+
+ // Needs to be separate method (not inside destructor),
+ // because the LBMContentMapping may be copied and destructed
+ // many times during operation in the lbm_lookup_map.
+ void deleteContents();
+ void addLBM(LoadingBlockModifierDef *lbm_def, IGameDef *gamedef);
+ const std::vector<LoadingBlockModifierDef *> *lookup(content_t c) const;
+};
+
+class LBMManager
+{
+public:
+ LBMManager():
+ m_query_mode(false)
+ {}
+
+ ~LBMManager();
+
+ // Don't call this after loadIntroductionTimes() ran.
+ void addLBMDef(LoadingBlockModifierDef *lbm_def);
+
+ void loadIntroductionTimes(const std::string &times,
+ IGameDef *gamedef, u32 now);
+
+ // Don't call this before loadIntroductionTimes() ran.
+ std::string createIntroductionTimesString();
+
+ // Don't call this before loadIntroductionTimes() ran.
+ void applyLBMs(ServerEnvironment *env, MapBlock *block, u32 stamp);
+
+ // Warning: do not make this std::unordered_map, order is relevant here
+ typedef std::map<u32, LBMContentMapping> lbm_lookup_map;
+
+private:
+ // Once we set this to true, we can only query,
+ // not modify
+ bool m_query_mode;
+
+ // For m_query_mode == false:
+ // The key of the map is the LBM def's name.
+ // TODO make this std::unordered_map
+ std::map<std::string, LoadingBlockModifierDef *> m_lbm_defs;
+
+ // For m_query_mode == true:
+ // The key of the map is the LBM def's first introduction time.
+ lbm_lookup_map m_lbm_lookup;
+
+ // Returns an iterator to the LBMs that were introduced
+ // after the given time. This is guaranteed to return
+ // valid values for everything
+ lbm_lookup_map::const_iterator getLBMsIntroducedAfter(u32 time)
+ { return m_lbm_lookup.lower_bound(time); }
+};
+
/*
List of active blocks, used by ServerEnvironment
*/
@@ -196,6 +283,18 @@ private:
};
/*
+ Operation mode for ServerEnvironment::clearObjects()
+*/
+enum ClearObjectsMode {
+ // Load and go through every mapblock, clearing objects
+ CLEAR_OBJECTS_MODE_FULL,
+
+ // Clear objects immediately in loaded mapblocks;
+ // clear objects in unloaded mapblocks only when the mapblocks are next activated.
+ CLEAR_OBJECTS_MODE_QUICK,
+};
+
+/*
The server-side environment.
This is not thread-safe. Server uses an environment mutex.
@@ -226,7 +325,7 @@ public:
const std::string &str_reason, bool reconnect);
// Save players
void saveLoadedPlayers();
- void savePlayer(const std::string &playername);
+ void savePlayer(RemotePlayer *player);
Player *loadPlayer(const std::string &playername);
/*
@@ -234,6 +333,12 @@ public:
*/
void saveMeta();
void loadMeta();
+ // to be called instead of loadMeta if
+ // env_meta.txt doesn't exist (e.g. new world)
+ void loadDefaultMeta();
+
+ u32 addParticleSpawner(float exptime);
+ void deleteParticleSpawner(u32 id);
/*
External ActiveObject interface
@@ -265,19 +370,19 @@ public:
Find out what new objects have been added to
inside a radius around a position
*/
- void getAddedActiveObjects(v3s16 pos, s16 radius,
+ void getAddedActiveObjects(Player *player, s16 radius,
s16 player_radius,
std::set<u16> &current_objects,
- std::set<u16> &added_objects);
+ std::queue<u16> &added_objects);
/*
Find out what new objects have been removed from
inside a radius around a position
*/
- void getRemovedActiveObjects(v3s16 pos, s16 radius,
+ void getRemovedActiveObjects(Player* player, s16 radius,
s16 player_radius,
std::set<u16> &current_objects,
- std::set<u16> &removed_objects);
+ std::queue<u16> &removed_objects);
/*
Get the next message emitted by some active object.
@@ -292,11 +397,12 @@ public:
void activateBlock(MapBlock *block, u32 additional_dtime=0);
/*
- ActiveBlockModifiers
+ {Active,Loading}BlockModifiers
-------------------------------------------
*/
void addActiveBlockModifier(ActiveBlockModifier *abm);
+ void addLoadingBlockModifierDef(LoadingBlockModifierDef *lbm);
/*
Other stuff
@@ -311,8 +417,8 @@ public:
// Find all active objects inside a radius around a point
void getObjectsInsideRadius(std::vector<u16> &objects, v3f pos, float radius);
- // Clear all objects, loading and going through every MapBlock
- void clearAllObjects();
+ // Clear objects, loading and going through every MapBlock
+ void clearObjects(ClearObjectsMode mode);
// This makes stuff happen
void step(f32 dtime);
@@ -402,12 +508,21 @@ private:
u32 m_game_time;
// A helper variable for incrementing the latter
float m_game_time_fraction_counter;
+ // Time of last clearObjects call (game time).
+ // When a mapblock older than this is loaded, its objects are cleared.
+ u32 m_last_clear_objects_time;
+ // Active block modifiers
std::vector<ABMWithState> m_abms;
+ LBMManager m_lbm_mgr;
// An interval for generally sending object positions and stuff
float m_recommended_send_interval;
// Estimate for general maximum lag as determined by server.
// Can raise to high values like 15s with eg. map generation mods.
float m_max_lag_estimate;
+
+ // Particles
+ IntervalLimiter m_particle_management_interval;
+ std::map<u32, float> m_particle_spawners;
};
#ifndef SERVER
@@ -534,7 +649,7 @@ private:
IrrlichtDevice *m_irr;
std::map<u16, ClientActiveObject*> m_active_objects;
std::vector<ClientSimpleObject*> m_simple_objects;
- std::list<ClientEnvEvent> m_client_event_queue;
+ std::queue<ClientEnvEvent> m_client_event_queue;
IntervalLimiter m_active_object_light_update_interval;
IntervalLimiter m_lava_hurt_interval;
IntervalLimiter m_drowning_interval;
diff --git a/src/event_manager.h b/src/event_manager.h
index 33d99b28c..f926922f5 100644
--- a/src/event_manager.h
+++ b/src/event_manager.h
@@ -53,7 +53,7 @@ public:
if(i != m_dest.end()){
std::list<FuncSpec> &funcs = i->second.funcs;
for(std::list<FuncSpec>::iterator i = funcs.begin();
- i != funcs.end(); i++){
+ i != funcs.end(); ++i){
(*(i->f))(e, i->d);
}
}
@@ -83,12 +83,12 @@ public:
if(remove)
funcs.erase(j++);
else
- j++;
+ ++j;
}
}
} else{
for(std::map<std::string, Dest>::iterator
- i = m_dest.begin(); i != m_dest.end(); i++){
+ i = m_dest.begin(); i != m_dest.end(); ++i){
std::list<FuncSpec> &funcs = i->second.funcs;
std::list<FuncSpec>::iterator j = funcs.begin();
while(j != funcs.end()){
@@ -96,7 +96,7 @@ public:
if(remove)
funcs.erase(j++);
else
- j++;
+ ++j;
}
}
}
diff --git a/src/exceptions.h b/src/exceptions.h
index 6bf832828..4f18f70d9 100644
--- a/src/exceptions.h
+++ b/src/exceptions.h
@@ -125,6 +125,12 @@ public:
PrngException(std::string s): BaseException(s) {}
};
+class ModError : public BaseException {
+public:
+ ModError(const std::string &s): BaseException(s) {}
+};
+
+
/*
Some "old-style" interrupts:
*/
diff --git a/src/filesys.cpp b/src/filesys.cpp
index 4cefdb807..b4c52ab79 100644
--- a/src/filesys.cpp
+++ b/src/filesys.cpp
@@ -26,6 +26,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <fstream>
#include "log.h"
#include "config.h"
+#include "porting.h"
namespace fs
{
@@ -693,18 +694,47 @@ bool safeWriteToFile(const std::string &path, const std::string &content)
os.flush();
os.close();
if (os.fail()) {
+ // Remove the temporary file because writing it failed and it's useless.
remove(tmp_file.c_str());
return false;
}
- // Copy file
- remove(path.c_str());
- if(rename(tmp_file.c_str(), path.c_str())) {
+ bool rename_success = false;
+
+ // Move the finished temporary file over the real file
+#ifdef _WIN32
+ // When creating the file, it can cause Windows Search indexer, virus scanners and other apps
+ // to query the file. This can make the move file call below fail.
+ // We retry up to 5 times, with a 1ms sleep between, before we consider the whole operation failed
+ int number_attempts = 0;
+ while (number_attempts < 5) {
+ rename_success = MoveFileEx(tmp_file.c_str(), path.c_str(),
+ MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH);
+ if (rename_success)
+ break;
+ sleep_ms(1);
+ ++number_attempts;
+ }
+#else
+ // On POSIX compliant systems rename() is specified to be able to swap the
+ // file in place of the destination file, making this a truly error-proof
+ // transaction.
+ rename_success = rename(tmp_file.c_str(), path.c_str()) == 0;
+#endif
+ if (!rename_success) {
+ warningstream << "Failed to write to file: " << path.c_str() << std::endl;
+ // Remove the temporary file because moving it over the target file
+ // failed.
remove(tmp_file.c_str());
return false;
- } else {
- return true;
}
+
+ return true;
+}
+
+bool Rename(const std::string &from, const std::string &to)
+{
+ return rename(from.c_str(), to.c_str()) == 0;
}
} // namespace fs
diff --git a/src/filesys.h b/src/filesys.h
index 19fcbb673..94d0c874d 100644
--- a/src/filesys.h
+++ b/src/filesys.h
@@ -28,10 +28,12 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#define DIR_DELIM "\\"
#define DIR_DELIM_CHAR '\\'
#define FILESYS_CASE_INSENSITIVE 1
+#define PATH_DELIM ";"
#else // POSIX
#define DIR_DELIM "/"
#define DIR_DELIM_CHAR '/'
#define FILESYS_CASE_INSENSITIVE 0
+#define PATH_DELIM ":"
#endif
namespace fs
@@ -113,6 +115,8 @@ const char *GetFilenameFromPath(const char *path);
bool safeWriteToFile(const std::string &path, const std::string &content);
+bool Rename(const std::string &from, const std::string &to);
+
} // namespace fs
#endif
diff --git a/src/fontengine.cpp b/src/fontengine.cpp
index f881701e0..55ff001e7 100644
--- a/src/fontengine.cpp
+++ b/src/fontengine.cpp
@@ -122,7 +122,7 @@ void FontEngine::cleanCache()
for (std::map<unsigned int, irr::gui::IGUIFont*>::iterator iter
= m_font_cache[i].begin();
- iter != m_font_cache[i].end(); iter++) {
+ iter != m_font_cache[i].end(); ++iter) {
iter->second->drop();
iter->second = NULL;
}
diff --git a/src/game.cpp b/src/game.cpp
index 11e868a80..23f261cfd 100644
--- a/src/game.cpp
+++ b/src/game.cpp
@@ -40,7 +40,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "guiPasswordChange.h"
#include "guiVolumeChange.h"
#include "hud.h"
-#include "logoutputbuffer.h"
#include "mainmenumanager.h"
#include "mapblock.h"
#include "nodedef.h" // Needed for determining pointing to nodes
@@ -58,6 +57,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "util/pointedthing.h"
#include "version.h"
#include "minimap.h"
+#include "mapblock_mesh.h"
#include "sound.h"
@@ -176,19 +176,6 @@ struct LocalFormspecHandler : public TextDest {
}
}
- if (m_formname == "MT_CHAT_MENU") {
- assert(m_client != 0);
-
- if ((fields.find("btn_send") != fields.end()) ||
- (fields.find("quit") != fields.end())) {
- StringMap::const_iterator it = fields.find("f_text");
- if (it != fields.end())
- m_client->typeChatMessage(utf8_to_wide(it->second));
-
- return;
- }
- }
-
if (m_formname == "MT_DEATH_SCREEN") {
assert(m_client != 0);
@@ -286,23 +273,70 @@ inline bool isPointableNode(const MapNode &n,
(liquids_pointable && features.isLiquid());
}
+static inline void getNeighborConnectingFace(v3s16 p, INodeDefManager *nodedef,
+ ClientMap *map, MapNode n, u8 bitmask, u8 *neighbors)
+{
+ MapNode n2 = map->getNodeNoEx(p);
+ if (nodedef->nodeboxConnects(n, n2, bitmask))
+ *neighbors |= bitmask;
+}
+
+static inline u8 getNeighbors(v3s16 p, INodeDefManager *nodedef, ClientMap *map, MapNode n)
+{
+ u8 neighbors = 0;
+ const ContentFeatures &f = nodedef->get(n);
+ // locate possible neighboring nodes to connect to
+ if (f.drawtype == NDT_NODEBOX && f.node_box.type == NODEBOX_CONNECTED) {
+ v3s16 p2 = p;
+
+ p2.Y++;
+ getNeighborConnectingFace(p2, nodedef, map, n, 1, &neighbors);
+
+ p2 = p;
+ p2.Y--;
+ getNeighborConnectingFace(p2, nodedef, map, n, 2, &neighbors);
+
+ p2 = p;
+ p2.Z--;
+ getNeighborConnectingFace(p2, nodedef, map, n, 4, &neighbors);
+
+ p2 = p;
+ p2.X--;
+ getNeighborConnectingFace(p2, nodedef, map, n, 8, &neighbors);
+
+ p2 = p;
+ p2.Z++;
+ getNeighborConnectingFace(p2, nodedef, map, n, 16, &neighbors);
+
+ p2 = p;
+ p2.X++;
+ getNeighborConnectingFace(p2, nodedef, map, n, 32, &neighbors);
+ }
+
+ return neighbors;
+}
+
/*
Find what the player is pointing at
*/
-PointedThing getPointedThing(Client *client, v3f player_position,
- v3f camera_direction, v3f camera_position, core::line3d<f32> shootline,
- f32 d, bool liquids_pointable, bool look_for_object, v3s16 camera_offset,
- std::vector<aabb3f> &hilightboxes, ClientActiveObject *&selected_object)
+PointedThing getPointedThing(Client *client, Hud *hud, const v3f &player_position,
+ const v3f &camera_direction, const v3f &camera_position,
+ core::line3d<f32> shootline, f32 d, bool liquids_pointable,
+ bool look_for_object, const v3s16 &camera_offset,
+ ClientActiveObject *&selected_object)
{
PointedThing result;
- hilightboxes.clear();
+ std::vector<aabb3f> *selectionboxes = hud->getSelectionBoxes();
+ selectionboxes->clear();
+ static const bool show_entity_selectionbox = g_settings->getBool("show_entity_selectionbox");
+
selected_object = NULL;
INodeDefManager *nodedef = client->getNodeDefManager();
ClientMap &map = client->getEnv().getClientMap();
- f32 mindistance = BS * 1001;
+ f32 min_distance = BS * 1001;
// First try to find a pointed at active object
if (look_for_object) {
@@ -310,19 +344,20 @@ PointedThing getPointedThing(Client *client, v3f player_position,
camera_position, shootline);
if (selected_object != NULL) {
- if (selected_object->doShowSelectionBox()) {
+ if (show_entity_selectionbox &&
+ selected_object->doShowSelectionBox()) {
aabb3f *selection_box = selected_object->getSelectionBox();
// Box should exist because object was
// returned in the first place
assert(selection_box);
v3f pos = selected_object->getPosition();
- hilightboxes.push_back(aabb3f(
- selection_box->MinEdge + pos - intToFloat(camera_offset, BS),
- selection_box->MaxEdge + pos - intToFloat(camera_offset, BS)));
+ selectionboxes->push_back(aabb3f(
+ selection_box->MinEdge, selection_box->MaxEdge));
+ hud->setSelectionPos(pos, camera_offset);
}
- mindistance = (selected_object->getPosition() - camera_position).getLength();
+ min_distance = (selected_object->getPosition() - camera_position).getLength();
result.type = POINTEDTHING_OBJECT;
result.object_id = selected_object->getId();
@@ -331,14 +366,13 @@ PointedThing getPointedThing(Client *client, v3f player_position,
// That didn't work, try to find a pointed at node
-
v3s16 pos_i = floatToInt(player_position, BS);
/*infostream<<"pos_i=("<<pos_i.X<<","<<pos_i.Y<<","<<pos_i.Z<<")"
<<std::endl;*/
s16 a = d;
- s16 ystart = pos_i.Y + 0 - (camera_direction.Y < 0 ? a : 1);
+ s16 ystart = pos_i.Y - (camera_direction.Y < 0 ? a : 1);
s16 zstart = pos_i.Z - (camera_direction.Z < 0 ? a : 1);
s16 xstart = pos_i.X - (camera_direction.X < 0 ? a : 1);
s16 yend = pos_i.Y + 1 + (camera_direction.Y > 0 ? a : 1);
@@ -355,82 +389,139 @@ PointedThing getPointedThing(Client *client, v3f player_position,
if (xend == 32767)
xend = 32766;
- for (s16 y = ystart; y <= yend; y++)
- for (s16 z = zstart; z <= zend; z++)
+ v3s16 pointed_pos(0, 0, 0);
+
+ for (s16 y = ystart; y <= yend; y++) {
+ for (s16 z = zstart; z <= zend; z++) {
for (s16 x = xstart; x <= xend; x++) {
MapNode n;
bool is_valid_position;
+ v3s16 p(x, y, z);
- n = map.getNodeNoEx(v3s16(x, y, z), &is_valid_position);
- if (!is_valid_position)
+ n = map.getNodeNoEx(p, &is_valid_position);
+ if (!is_valid_position) {
continue;
-
- if (!isPointableNode(n, client, liquids_pointable))
+ }
+ if (!isPointableNode(n, client, liquids_pointable)) {
continue;
+ }
- std::vector<aabb3f> boxes = n.getSelectionBoxes(nodedef);
+ std::vector<aabb3f> boxes;
+ n.getSelectionBoxes(nodedef, &boxes, getNeighbors(p, nodedef, &map, n));
v3s16 np(x, y, z);
v3f npf = intToFloat(np, BS);
-
for (std::vector<aabb3f>::const_iterator
i = boxes.begin();
- i != boxes.end(); i++) {
+ i != boxes.end(); ++i) {
aabb3f box = *i;
box.MinEdge += npf;
box.MaxEdge += npf;
- for (u16 j = 0; j < 6; j++) {
- v3s16 facedir = g_6dirs[j];
- aabb3f facebox = box;
-
- f32 d = 0.001 * BS;
-
- if (facedir.X > 0)
- facebox.MinEdge.X = facebox.MaxEdge.X - d;
- else if (facedir.X < 0)
- facebox.MaxEdge.X = facebox.MinEdge.X + d;
- else if (facedir.Y > 0)
- facebox.MinEdge.Y = facebox.MaxEdge.Y - d;
- else if (facedir.Y < 0)
- facebox.MaxEdge.Y = facebox.MinEdge.Y + d;
- else if (facedir.Z > 0)
- facebox.MinEdge.Z = facebox.MaxEdge.Z - d;
- else if (facedir.Z < 0)
- facebox.MaxEdge.Z = facebox.MinEdge.Z + d;
-
- v3f centerpoint = facebox.getCenter();
- f32 distance = (centerpoint - camera_position).getLength();
-
- if (distance >= mindistance)
- continue;
-
- if (!facebox.intersectsWithLine(shootline))
- continue;
-
- v3s16 np_above = np + facedir;
-
- result.type = POINTEDTHING_NODE;
- result.node_undersurface = np;
- result.node_abovesurface = np_above;
- mindistance = distance;
-
- hilightboxes.clear();
-
- if (!g_settings->getBool("enable_node_highlighting")) {
- for (std::vector<aabb3f>::const_iterator
- i2 = boxes.begin();
- i2 != boxes.end(); i2++) {
- aabb3f box = *i2;
- box.MinEdge += npf + v3f(-d, -d, -d) - intToFloat(camera_offset, BS);
- box.MaxEdge += npf + v3f(d, d, d) - intToFloat(camera_offset, BS);
- hilightboxes.push_back(box);
- }
- }
+ v3f centerpoint = box.getCenter();
+ f32 distance = (centerpoint - camera_position).getLength();
+ if (distance >= min_distance) {
+ continue;
+ }
+ if (!box.intersectsWithLine(shootline)) {
+ continue;
}
+ result.type = POINTEDTHING_NODE;
+ min_distance = distance;
+ pointed_pos = np;
+ }
+ }
+ }
+ }
+
+ if (result.type == POINTEDTHING_NODE) {
+ f32 d = 0.001 * BS;
+ MapNode n = map.getNodeNoEx(pointed_pos);
+ v3f npf = intToFloat(pointed_pos, BS);
+ std::vector<aabb3f> boxes;
+ n.getSelectionBoxes(nodedef, &boxes, getNeighbors(pointed_pos, nodedef, &map, n));
+ f32 face_min_distance = 1000 * BS;
+ for (std::vector<aabb3f>::const_iterator
+ i = boxes.begin();
+ i != boxes.end(); ++i) {
+ aabb3f box = *i;
+ box.MinEdge += npf;
+ box.MaxEdge += npf;
+ for (u16 j = 0; j < 6; j++) {
+ v3s16 facedir = g_6dirs[j];
+ aabb3f facebox = box;
+ if (facedir.X > 0) {
+ facebox.MinEdge.X = facebox.MaxEdge.X - d;
+ } else if (facedir.X < 0) {
+ facebox.MaxEdge.X = facebox.MinEdge.X + d;
+ } else if (facedir.Y > 0) {
+ facebox.MinEdge.Y = facebox.MaxEdge.Y - d;
+ } else if (facedir.Y < 0) {
+ facebox.MaxEdge.Y = facebox.MinEdge.Y + d;
+ } else if (facedir.Z > 0) {
+ facebox.MinEdge.Z = facebox.MaxEdge.Z - d;
+ } else if (facedir.Z < 0) {
+ facebox.MaxEdge.Z = facebox.MinEdge.Z + d;
}
- } // for coords
+ v3f centerpoint = facebox.getCenter();
+ f32 distance = (centerpoint - camera_position).getLength();
+ if (distance >= face_min_distance)
+ continue;
+ if (!facebox.intersectsWithLine(shootline))
+ continue;
+ result.node_abovesurface = pointed_pos + facedir;
+ face_min_distance = distance;
+ }
+ }
+ selectionboxes->clear();
+ for (std::vector<aabb3f>::const_iterator
+ i = boxes.begin();
+ i != boxes.end(); ++i) {
+ aabb3f box = *i;
+ box.MinEdge += v3f(-d, -d, -d);
+ box.MaxEdge += v3f(d, d, d);
+ selectionboxes->push_back(box);
+ }
+ hud->setSelectionPos(intToFloat(pointed_pos, BS), camera_offset);
+ result.node_undersurface = pointed_pos;
+ }
+
+ // Update selection mesh light level and vertex colors
+ if (selectionboxes->size() > 0) {
+ v3f pf = hud->getSelectionPos();
+ v3s16 p = floatToInt(pf, BS);
+
+ // Get selection mesh light level
+ MapNode n = map.getNodeNoEx(p);
+ u16 node_light = getInteriorLight(n, -1, nodedef);
+ u16 light_level = node_light;
+ for (u8 i = 0; i < 6; i++) {
+ n = map.getNodeNoEx(p + g_6dirs[i]);
+ node_light = getInteriorLight(n, -1, nodedef);
+ if (node_light > light_level)
+ light_level = node_light;
+ }
+
+ video::SColor c = MapBlock_LightColor(255, light_level, 0);
+ u8 day = c.getRed();
+ u8 night = c.getGreen();
+ u32 daynight_ratio = client->getEnv().getDayNightRatio();
+ finalColorBlend(c, day, night, daynight_ratio);
+
+ // Modify final color a bit with time
+ u32 timer = porting::getTimeMs() % 5000;
+ float timerf = (float)(irr::core::PI * ((timer / 2500.0) - 0.5));
+ float sin_r = 0.08 * sin(timerf);
+ float sin_g = 0.08 * sin(timerf + irr::core::PI * 0.5);
+ float sin_b = 0.08 * sin(timerf + irr::core::PI);
+ c.setRed(core::clamp(core::round32(c.getRed() * (0.8 + sin_r)), 0, 255));
+ c.setGreen(core::clamp(core::round32(c.getGreen() * (0.8 + sin_g)), 0, 255));
+ c.setBlue(core::clamp(core::round32(c.getBlue() * (0.8 + sin_b)), 0, 255));
+
+ // Set mesh final color
+ hud->setSelectionMeshColor(c);
+ }
return result;
}
@@ -490,7 +581,7 @@ private:
color(color)
{}
};
- std::vector<Piece> m_log;
+ std::deque<Piece> m_log;
public:
u32 m_log_max_size;
@@ -513,12 +604,12 @@ public:
{
std::map<std::string, Meta> m_meta;
- for (std::vector<Piece>::const_iterator k = m_log.begin();
- k != m_log.end(); k++) {
+ for (std::deque<Piece>::const_iterator k = m_log.begin();
+ k != m_log.end(); ++k) {
const Piece &piece = *k;
for (Profiler::GraphValues::const_iterator i = piece.values.begin();
- i != piece.values.end(); i++) {
+ i != piece.values.end(); ++i) {
const std::string &id = i->first;
const float &value = i->second;
std::map<std::string, Meta>::iterator j =
@@ -550,7 +641,7 @@ public:
u32 next_color_i = 0;
for (std::map<std::string, Meta>::iterator i = m_meta.begin();
- i != m_meta.end(); i++) {
+ i != m_meta.end(); ++i) {
Meta &meta = i->second;
video::SColor color(255, 200, 200, 200);
@@ -566,7 +657,7 @@ public:
s32 meta_i = 0;
for (std::map<std::string, Meta>::const_iterator i = m_meta.begin();
- i != m_meta.end(); i++) {
+ i != m_meta.end(); ++i) {
const std::string &id = i->first;
const Meta &meta = i->second;
s32 x = x_left;
@@ -601,8 +692,8 @@ public:
float lastscaledvalue = 0.0;
bool lastscaledvalue_exists = false;
- for (std::vector<Piece>::const_iterator j = m_log.begin();
- j != m_log.end(); j++) {
+ for (std::deque<Piece>::const_iterator j = m_log.begin();
+ j != m_log.end(); ++j) {
const Piece &piece = *j;
float value = 0;
bool value_exists = false;
@@ -871,15 +962,18 @@ public:
services->setPixelShaderConstant("yawVec", (irr::f32 *)&minimap_yaw_vec, 3);
// Uniform sampler layers
- int layer0 = 0;
- int layer1 = 1;
- int layer2 = 2;
// before 1.8 there isn't a "integer interface", only float
#if (IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 8)
+ f32 layer0 = 0;
+ f32 layer1 = 1;
+ f32 layer2 = 2;
services->setPixelShaderConstant("baseTexture" , (irr::f32 *)&layer0, 1);
services->setPixelShaderConstant("normalTexture" , (irr::f32 *)&layer1, 1);
services->setPixelShaderConstant("textureFlags" , (irr::f32 *)&layer2, 1);
#else
+ s32 layer0 = 0;
+ s32 layer1 = 1;
+ s32 layer2 = 2;
services->setPixelShaderConstant("baseTexture" , (irr::s32 *)&layer0, 1);
services->setPixelShaderConstant("normalTexture" , (irr::s32 *)&layer1, 1);
services->setPixelShaderConstant("textureFlags" , (irr::s32 *)&layer2, 1);
@@ -1040,27 +1134,6 @@ static inline void create_formspec_menu(GUIFormSpecMenu **cur_formspec,
#define SIZE_TAG "size[11,5.5,true]" // Fixed size on desktop
#endif
-static void show_chat_menu(GUIFormSpecMenu **cur_formspec,
- InventoryManager *invmgr, IGameDef *gamedef,
- IWritableTextureSource *tsrc, IrrlichtDevice *device,
- Client *client, std::string text)
-{
- std::string formspec =
- FORMSPEC_VERSION_STRING
- SIZE_TAG
- "field[3,2.35;6,0.5;f_text;;" + text + "]"
- "button_exit[4,3;3,0.5;btn_send;" + wide_to_utf8(wstrgettext("Proceed")) + "]"
- ;
-
- /* Create menu */
- /* Note: FormspecFormSource and LocalFormspecHandler
- * are deleted by guiFormSpecMenu */
- FormspecFormSource *fs_src = new FormspecFormSource(formspec);
- LocalFormspecHandler *txt_dst = new LocalFormspecHandler("MT_CHAT_MENU", client);
-
- create_formspec_menu(cur_formspec, invmgr, gamedef, tsrc, device, fs_src, txt_dst, NULL);
-}
-
static void show_deathscreen(GUIFormSpecMenu **cur_formspec,
InventoryManager *invmgr, IGameDef *gamedef,
IWritableTextureSource *tsrc, IrrlichtDevice *device, Client *client)
@@ -1089,32 +1162,32 @@ static void show_pause_menu(GUIFormSpecMenu **cur_formspec,
bool singleplayermode)
{
#ifdef __ANDROID__
- std::string control_text = wide_to_utf8(wstrgettext("Default Controls:\n"
- "No menu visible:\n"
- "- single tap: button activate\n"
- "- double tap: place/use\n"
- "- slide finger: look around\n"
- "Menu/Inventory visible:\n"
- "- double tap (outside):\n"
- " -->close\n"
- "- touch stack, touch slot:\n"
- " --> move stack\n"
- "- touch&drag, tap 2nd finger\n"
- " --> place single item to slot\n"
- ));
+ std::string control_text = strgettext("Default Controls:\n"
+ "No menu visible:\n"
+ "- single tap: button activate\n"
+ "- double tap: place/use\n"
+ "- slide finger: look around\n"
+ "Menu/Inventory visible:\n"
+ "- double tap (outside):\n"
+ " -->close\n"
+ "- touch stack, touch slot:\n"
+ " --> move stack\n"
+ "- touch&drag, tap 2nd finger\n"
+ " --> place single item to slot\n"
+ );
#else
- std::string control_text = wide_to_utf8(wstrgettext("Default Controls:\n"
- "- WASD: move\n"
- "- Space: jump/climb\n"
- "- Shift: sneak/go down\n"
- "- Q: drop item\n"
- "- I: inventory\n"
- "- Mouse: turn/look\n"
- "- Mouse left: dig/punch\n"
- "- Mouse right: place/use\n"
- "- Mouse wheel: select item\n"
- "- T: chat\n"
- ));
+ std::string control_text = strgettext("Default Controls:\n"
+ "- WASD: move\n"
+ "- Space: jump/climb\n"
+ "- Shift: sneak/go down\n"
+ "- Q: drop item\n"
+ "- I: inventory\n"
+ "- Mouse: turn/look\n"
+ "- Mouse left: dig/punch\n"
+ "- Mouse right: place/use\n"
+ "- Mouse wheel: select item\n"
+ "- T: chat\n"
+ );
#endif
float ypos = singleplayermode ? 0.5 : 0.1;
@@ -1122,23 +1195,23 @@ static void show_pause_menu(GUIFormSpecMenu **cur_formspec,
os << FORMSPEC_VERSION_STRING << SIZE_TAG
<< "button_exit[4," << (ypos++) << ";3,0.5;btn_continue;"
- << wide_to_utf8(wstrgettext("Continue")) << "]";
+ << strgettext("Continue") << "]";
if (!singleplayermode) {
os << "button_exit[4," << (ypos++) << ";3,0.5;btn_change_password;"
- << wide_to_utf8(wstrgettext("Change Password")) << "]";
+ << strgettext("Change Password") << "]";
}
#ifndef __ANDROID__
os << "button_exit[4," << (ypos++) << ";3,0.5;btn_sound;"
- << wide_to_utf8(wstrgettext("Sound Volume")) << "]";
+ << strgettext("Sound Volume") << "]";
os << "button_exit[4," << (ypos++) << ";3,0.5;btn_key_config;"
- << wide_to_utf8(wstrgettext("Change Keys")) << "]";
+ << strgettext("Change Keys") << "]";
#endif
os << "button_exit[4," << (ypos++) << ";3,0.5;btn_exit_menu;"
- << wide_to_utf8(wstrgettext("Exit to Menu")) << "]";
+ << strgettext("Exit to Menu") << "]";
os << "button_exit[4," << (ypos++) << ";3,0.5;btn_exit_os;"
- << wide_to_utf8(wstrgettext("Exit to OS")) << "]"
+ << strgettext("Exit to OS") << "]"
<< "textarea[7.5,0.25;3.9,6.25;;" << control_text << ";]"
<< "textarea[0.4,0.25;3.5,6;;" << PROJECT_NAME_C "\n"
<< g_build_info << "\n"
@@ -1163,7 +1236,7 @@ static void updateChat(Client &client, f32 dtime, bool show_debug,
ChatBackend &chat_backend, gui::IGUIStaticText *guitext_chat)
{
// Add chat log output for errors to be shown in chat
- static LogOutputBuffer chat_log_error_buf(LMT_ERROR);
+ static LogOutputBuffer chat_log_error_buf(g_logger, LL_ERROR);
// Get new messages from error log buffer
while (!chat_log_error_buf.empty()) {
@@ -1235,6 +1308,7 @@ struct KeyCache {
KEYMAP_ID_JUMP,
KEYMAP_ID_SPECIAL1,
KEYMAP_ID_SNEAK,
+ KEYMAP_ID_AUTORUN,
// Other
KEYMAP_ID_DROP,
@@ -1287,6 +1361,8 @@ void KeyCache::populate()
key[KEYMAP_ID_SPECIAL1] = getKeySetting("keymap_special1");
key[KEYMAP_ID_SNEAK] = getKeySetting("keymap_sneak");
+ key[KEYMAP_ID_AUTORUN] = getKeySetting("keymap_autorun");
+
key[KEYMAP_ID_DROP] = getKeySetting("keymap_drop");
key[KEYMAP_ID_INVENTORY] = getKeySetting("keymap_inventory");
key[KEYMAP_ID_CHAT] = getKeySetting("keymap_chat");
@@ -1488,12 +1564,13 @@ protected:
void dropSelectedItem();
void openInventory();
- void openConsole();
+ void openConsole(float height, const wchar_t *line=NULL);
void toggleFreeMove(float *statustext_time);
void toggleFreeMoveAlt(float *statustext_time, float *jump_timer);
void toggleFast(float *statustext_time);
void toggleNoClip(float *statustext_time);
void toggleCinematic(float *statustext_time);
+ void toggleAutorun(float *statustext_time);
void toggleChat(float *statustext_time, bool *flag);
void toggleHud(float *statustext_time, bool *flag);
@@ -1519,9 +1596,9 @@ protected:
void updateCamera(VolatileRunFlags *flags, u32 busy_time, f32 dtime,
float time_from_last_punch);
void updateSound(f32 dtime);
- void processPlayerInteraction(std::vector<aabb3f> &highlight_boxes,
- GameRunData *runData, f32 dtime, bool show_hud,
+ void processPlayerInteraction(GameRunData *runData, f32 dtime, bool show_hud,
bool show_debug);
+ void handlePointingAtNothing(GameRunData *runData, const ItemStack &playerItem);
void handlePointingAtNode(GameRunData *runData,
const PointedThing &pointed, const ItemDefinition &playeritem_def,
const ToolCapabilities &playeritem_toolcap, f32 dtime);
@@ -1531,8 +1608,7 @@ protected:
void handleDigging(GameRunData *runData, const PointedThing &pointed,
const v3s16 &nodepos, const ToolCapabilities &playeritem_toolcap,
f32 dtime);
- void updateFrame(std::vector<aabb3f> &highlight_boxes, ProfilerGraph *graph,
- RunStats *stats, GameRunData *runData,
+ void updateFrame(ProfilerGraph *graph, RunStats *stats, GameRunData *runData,
f32 dtime, const VolatileRunFlags &flags, const CameraOrientation &cam);
void updateGui(float *statustext_time, const RunStats &stats,
const GameRunData& runData, f32 dtime, const VolatileRunFlags &flags,
@@ -1548,6 +1624,10 @@ protected:
static void settingChangedCallback(const std::string &setting_name, void *data);
void readSettings();
+#ifdef __ANDROID__
+ void handleAndroidChatInput();
+#endif
+
private:
InputHandler *input;
@@ -1626,7 +1706,6 @@ private:
* a later release.
*/
bool m_cache_doubletap_jump;
- bool m_cache_enable_node_highlighting;
bool m_cache_enable_clouds;
bool m_cache_enable_particles;
bool m_cache_enable_fog;
@@ -1635,8 +1714,8 @@ private:
#ifdef __ANDROID__
bool m_cache_hold_aux1;
+ bool m_android_chat_open;
#endif
-
};
Game::Game() :
@@ -1664,8 +1743,6 @@ Game::Game() :
{
g_settings->registerChangedCallback("doubletap_jump",
&settingChangedCallback, this);
- g_settings->registerChangedCallback("enable_node_highlighting",
- &settingChangedCallback, this);
g_settings->registerChangedCallback("enable_clouds",
&settingChangedCallback, this);
g_settings->registerChangedCallback("enable_particles",
@@ -1715,8 +1792,6 @@ Game::~Game()
g_settings->deregisterChangedCallback("doubletap_jump",
&settingChangedCallback, this);
- g_settings->deregisterChangedCallback("enable_node_highlighting",
- &settingChangedCallback, this);
g_settings->deregisterChangedCallback("enable_clouds",
&settingChangedCallback, this);
g_settings->deregisterChangedCallback("enable_particles",
@@ -1803,8 +1878,6 @@ void Game::run()
&runData.fog_range,
client));
- std::vector<aabb3f> highlight_boxes;
-
set_light_table(g_settings->getFloat("display_gamma"));
#ifdef __ANDROID__
@@ -1812,7 +1885,9 @@ void Game::run()
&& client->checkPrivilege("fast");
#endif
- while (device->run() && !(*kill || g_gamecallback->shutdown_requested)) {
+ while (device->run()
+ && !(*kill || g_gamecallback->shutdown_requested
+ || (server && server->getShutdownRequested()))) {
/* Must be called immediately after a device->run() call because it
* uses device->getTimer()->getTime()
@@ -1852,17 +1927,23 @@ void Game::run()
updateCamera(&flags, draw_times.busy_time, dtime,
runData.time_from_last_punch);
updateSound(dtime);
- processPlayerInteraction(highlight_boxes, &runData, dtime,
- flags.show_hud, flags.show_debug);
- updateFrame(highlight_boxes, &graph, &stats, &runData, dtime,
- flags, cam_view);
+ processPlayerInteraction(&runData, dtime, flags.show_hud,
+ flags.show_debug);
+ updateFrame(&graph, &stats, &runData, dtime, flags, cam_view);
updateProfilerGraphs(&graph);
+
+ // Update if minimap has been disabled by the server
+ flags.show_minimap &= !client->isMinimapDisabledByServer();
}
}
void Game::shutdown()
{
+ if (g_settings->get("3d_mode") == "pageflip") {
+ driver->setRenderTarget(irr::video::ERT_STEREO_BOTH_BUFFERS);
+ }
+
showOverlayMessage(wgettext("Shutting down..."), 0, 0, false);
if (clouds)
@@ -2044,6 +2125,7 @@ bool Game::createClient(const std::string &playername,
camera = new Camera(smgr, *draw_control, gamedef);
if (!camera || !camera->successfullyCreated(*error_message))
return false;
+ client->setCamera(camera);
/* Clouds
*/
@@ -2147,7 +2229,7 @@ bool Game::initGui()
// Chat backend and console
gui_chat_console = new GUIChatConsole(guienv, guienv->getRootGUIElement(),
- -1, chat_backend, client);
+ -1, chat_backend, client, &g_menumgr);
if (!gui_chat_console) {
*error_message = "Could not allocate memory for chat console";
errorstream << *error_message << std::endl;
@@ -2166,7 +2248,7 @@ bool Game::initGui()
#ifdef HAVE_TOUCHSCREENGUI
if (g_touchscreengui)
- g_touchscreengui->init(texture_src, porting::getDisplayDensity());
+ g_touchscreengui->init(texture_src);
#endif
@@ -2562,7 +2644,17 @@ void Game::processUserInput(VolatileRunFlags *flags,
|| noMenuActive() == false
|| guienv->hasFocus(gui_chat_console)) {
input->clear();
+#ifdef HAVE_TOUCHSCREENGUI
+ g_touchscreengui->hide();
+#endif
+ }
+#ifdef HAVE_TOUCHSCREENGUI
+ else if (g_touchscreengui) {
+ /* on touchscreengui step may generate own input events which ain't
+ * what we want in case we just did clear them */
+ g_touchscreengui->step(dtime);
}
+#endif
if (!guienv->hasFocus(gui_chat_console) && gui_chat_console->isOpen()) {
gui_chat_console->closeConsoleAtOnce();
@@ -2571,18 +2663,11 @@ void Game::processUserInput(VolatileRunFlags *flags,
// Input handler step() (used by the random input generator)
input->step(dtime);
-#ifdef HAVE_TOUCHSCREENGUI
-
- if (g_touchscreengui) {
- g_touchscreengui->step(dtime);
- }
-
-#endif
#ifdef __ANDROID__
-
- if (current_formspec != 0)
+ if (current_formspec != NULL)
current_formspec->getAndroidUIInput();
-
+ else
+ handleAndroidChatInput();
#endif
// Increase timer for double tap of "keymap_jump"
@@ -2613,19 +2698,21 @@ void Game::processKeyboardInput(VolatileRunFlags *flags,
if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_DROP])) {
dropSelectedItem();
+ } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_AUTORUN])) {
+ toggleAutorun(statustext_time);
} else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_INVENTORY])) {
openInventory();
} else if (input->wasKeyDown(EscapeKey) || input->wasKeyDown(CancelKey)) {
- show_pause_menu(&current_formspec, client, gamedef, texture_src, device,
- simple_singleplayer_mode);
+ if (!gui_chat_console->isOpenInhibited()) {
+ show_pause_menu(&current_formspec, client, gamedef,
+ texture_src, device, simple_singleplayer_mode);
+ }
} else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_CHAT])) {
- show_chat_menu(&current_formspec, client, gamedef, texture_src, device,
- client, "");
+ openConsole(0.2, L"");
} else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_CMD])) {
- show_chat_menu(&current_formspec, client, gamedef, texture_src, device,
- client, "/");
+ openConsole(0.2, L"/");
} else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_CONSOLE])) {
- openConsole();
+ openConsole(1);
} else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_FREEMOVE])) {
toggleFreeMove(statustext_time);
} else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_JUMP])) {
@@ -2660,19 +2747,19 @@ void Game::processKeyboardInput(VolatileRunFlags *flags,
decreaseViewRange(statustext_time);
} else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_RANGESELECT])) {
toggleFullViewRange(statustext_time);
- } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_QUICKTUNE_NEXT]))
+ } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_QUICKTUNE_NEXT])) {
quicktune->next();
- else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_QUICKTUNE_PREV]))
+ } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_QUICKTUNE_PREV])) {
quicktune->prev();
- else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_QUICKTUNE_INC]))
+ } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_QUICKTUNE_INC])) {
quicktune->inc();
- else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_QUICKTUNE_DEC]))
+ } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_QUICKTUNE_DEC])) {
quicktune->dec();
- else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_DEBUG_STACKS])) {
+ } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_DEBUG_STACKS])) {
// Print debug stacks
dstream << "-----------------------------------------"
<< std::endl;
- dstream << DTIME << "Printing debug stacks:" << std::endl;
+ dstream << "Printing debug stacks:" << std::endl;
dstream << "-----------------------------------------"
<< std::endl;
debug_stacks_print();
@@ -2768,15 +2855,32 @@ void Game::openInventory()
}
-void Game::openConsole()
+void Game::openConsole(float height, const wchar_t *line)
{
- if (!gui_chat_console->isOpenInhibited()) {
- // Open up to over half of the screen
- gui_chat_console->openConsole(0.6);
- guienv->setFocus(gui_chat_console);
+#ifdef __ANDROID__
+ porting::showInputDialog(gettext("ok"), "", "", 2);
+ m_android_chat_open = true;
+#else
+ if (gui_chat_console->isOpenInhibited())
+ return;
+ gui_chat_console->openConsole(height);
+ if (line) {
+ gui_chat_console->setCloseOnEnter(true);
+ gui_chat_console->replaceAndAddToHistory(line);
}
+#endif
}
+#ifdef __ANDROID__
+void Game::handleAndroidChatInput()
+{
+ if (m_android_chat_open && porting::getInputDialogState() == 0) {
+ std::string text = porting::getInputDialogValue();
+ client->typeChatMessage(utf8_to_wide(text));
+ }
+}
+#endif
+
void Game::toggleFreeMove(float *statustext_time)
{
@@ -2842,6 +2946,16 @@ void Game::toggleCinematic(float *statustext_time)
statustext = msg[cinematic];
}
+// Add WoW-style autorun by toggling continuous forward.
+void Game::toggleAutorun(float *statustext_time)
+{
+ static const wchar_t *msg[] = { L"autorun disabled", L"autorun enabled" };
+ bool autorun_enabled = !g_settings->getBool("continuous_forward");
+ g_settings->set("continuous_forward", bool_to_cstr(autorun_enabled));
+
+ *statustext_time = 0;
+ statustext = msg[autorun_enabled ? 1 : 0];
+}
void Game::toggleChat(float *statustext_time, bool *flag)
{
@@ -2860,8 +2974,6 @@ void Game::toggleHud(float *statustext_time, bool *flag)
*flag = !*flag;
*statustext_time = 0;
statustext = msg[*flag];
- if (g_settings->getBool("enable_node_highlighting"))
- client->setHighlighted(client->getHighlighted(), *flag);
}
void Game::toggleMinimap(float *statustext_time, bool *flag,
@@ -2982,10 +3094,10 @@ void Game::toggleProfiler(float *statustext_time, u32 *profiler_current_page,
void Game::increaseViewRange(float *statustext_time)
{
- s16 range = g_settings->getS16("viewing_range_nodes_min");
+ s16 range = g_settings->getS16("viewing_range");
s16 range_new = range + 10;
- g_settings->set("viewing_range_nodes_min", itos(range_new));
- statustext = utf8_to_wide("Minimum viewing range changed to "
+ g_settings->set("viewing_range", itos(range_new));
+ statustext = utf8_to_wide("Viewing range changed to "
+ itos(range_new));
*statustext_time = 0;
}
@@ -2993,14 +3105,14 @@ void Game::increaseViewRange(float *statustext_time)
void Game::decreaseViewRange(float *statustext_time)
{
- s16 range = g_settings->getS16("viewing_range_nodes_min");
+ s16 range = g_settings->getS16("viewing_range");
s16 range_new = range - 10;
- if (range_new < 0)
- range_new = range;
+ if (range_new < 20)
+ range_new = 20;
- g_settings->set("viewing_range_nodes_min", itos(range_new));
- statustext = utf8_to_wide("Minimum viewing range changed to "
+ g_settings->set("viewing_range", itos(range_new));
+ statustext = utf8_to_wide("Viewing range changed to "
+ itos(range_new));
*statustext_time = 0;
}
@@ -3447,8 +3559,8 @@ void Game::updateSound(f32 dtime)
}
-void Game::processPlayerInteraction(std::vector<aabb3f> &highlight_boxes,
- GameRunData *runData, f32 dtime, bool show_hud, bool show_debug)
+void Game::processPlayerInteraction(GameRunData *runData,
+ f32 dtime, bool show_hud, bool show_debug)
{
LocalPlayer *player = client->getEnv().getLocalPlayer();
@@ -3506,25 +3618,17 @@ void Game::processPlayerInteraction(std::vector<aabb3f> &highlight_boxes,
PointedThing pointed = getPointedThing(
// input
- client, player_position, camera_direction,
+ client, hud, player_position, camera_direction,
camera_position, shootline, d,
playeritem_def.liquids_pointable,
!runData->ldown_for_dig,
camera_offset,
// output
- highlight_boxes,
runData->selected_object);
if (pointed != runData->pointed_old) {
infostream << "Pointing at " << pointed.dump() << std::endl;
-
- if (m_cache_enable_node_highlighting) {
- if (pointed.type == POINTEDTHING_NODE) {
- client->setHighlighted(pointed.node_undersurface, show_hud);
- } else {
- client->setHighlighted(pointed.node_undersurface, false);
- }
- }
+ hud->updateSelectionMesh(camera_offset);
}
/*
@@ -3548,6 +3652,7 @@ void Game::processPlayerInteraction(std::vector<aabb3f> &highlight_boxes,
infostream << "Pointing away from node"
<< " (stopped digging)" << std::endl;
runData->digging = false;
+ hud->updateSelectionMesh(camera_offset);
}
}
@@ -3585,6 +3690,8 @@ void Game::processPlayerInteraction(std::vector<aabb3f> &highlight_boxes,
} else if (input->getLeftState()) {
// When button is held down in air, show continuous animation
runData->left_punch = true;
+ } else if (input->getRightClicked()) {
+ handlePointingAtNothing(runData, playeritem);
}
runData->pointed_old = pointed;
@@ -3600,6 +3707,15 @@ void Game::processPlayerInteraction(std::vector<aabb3f> &highlight_boxes,
}
+void Game::handlePointingAtNothing(GameRunData *runData, const ItemStack &playerItem)
+{
+ infostream << "Right Clicked in Air" << std::endl;
+ PointedThing fauxPointed;
+ fauxPointed.type = POINTEDTHING_NOTHING;
+ client->interact(5, fauxPointed);
+}
+
+
void Game::handlePointingAtNode(GameRunData *runData,
const PointedThing &pointed, const ItemDefinition &playeritem_def,
const ToolCapabilities &playeritem_toolcap, f32 dtime)
@@ -3615,7 +3731,7 @@ void Game::handlePointingAtNode(GameRunData *runData,
NodeMetadata *meta = map.getNodeMetadata(nodepos);
if (meta) {
- infotext = utf8_to_wide(meta->getString("infotext"));
+ infotext = unescape_enriched(utf8_to_wide(meta->getString("infotext")));
} else {
MapNode n = map.getNodeNoEx(nodepos);
@@ -3671,11 +3787,15 @@ void Game::handlePointingAtNode(GameRunData *runData,
} else {
soundmaker->m_player_rightpunch_sound =
SimpleSoundSpec();
- }
- if (playeritem_def.node_placement_prediction == "" ||
- nodedef_manager->get(map.getNodeNoEx(nodepos)).rightclickable)
- client->interact(3, pointed); // Report to server
+ if (playeritem_def.node_placement_prediction == "" ||
+ nodedef_manager->get(map.getNodeNoEx(nodepos)).rightclickable) {
+ client->interact(3, pointed); // Report to server
+ } else {
+ soundmaker->m_player_rightpunch_sound =
+ playeritem_def.sound_place_failed;
+ }
+ }
}
}
}
@@ -3687,10 +3807,15 @@ void Game::handlePointingAtObject(GameRunData *runData,
const v3f &player_position,
bool show_debug)
{
- infotext = utf8_to_wide(runData->selected_object->infoText());
+ infotext = unescape_enriched(
+ utf8_to_wide(runData->selected_object->infoText()));
- if (infotext == L"" && show_debug) {
- infotext = utf8_to_wide(runData->selected_object->debugInfoText());
+ if (show_debug) {
+ if (infotext != L"") {
+ infotext += L"\n";
+ }
+ infotext += unescape_enriched(utf8_to_wide(
+ runData->selected_object->debugInfoText()));
}
if (input->getLeftState()) {
@@ -3854,9 +3979,9 @@ void Game::handleDigging(GameRunData *runData,
}
-void Game::updateFrame(std::vector<aabb3f> &highlight_boxes,
- ProfilerGraph *graph, RunStats *stats, GameRunData *runData,
- f32 dtime, const VolatileRunFlags &flags, const CameraOrientation &cam)
+void Game::updateFrame(ProfilerGraph *graph, RunStats *stats,
+ GameRunData *runData, f32 dtime, const VolatileRunFlags &flags,
+ const CameraOrientation &cam)
{
LocalPlayer *player = client->getEnv().getLocalPlayer();
@@ -3867,12 +3992,7 @@ void Game::updateFrame(std::vector<aabb3f> &highlight_boxes,
if (draw_control->range_all) {
runData->fog_range = 100000 * BS;
} else {
- runData->fog_range = draw_control->wanted_range * BS
- + 0.0 * MAP_BLOCKSIZE * BS;
- runData->fog_range = MYMIN(
- runData->fog_range,
- (draw_control->farthest_drawn + 20) * BS);
- runData->fog_range *= 0.9;
+ runData->fog_range = 0.9 * draw_control->wanted_range * BS;
}
/*
@@ -4048,7 +4168,7 @@ void Game::updateFrame(std::vector<aabb3f> &highlight_boxes,
}
draw_scene(driver, smgr, *camera, *client, player, *hud, *mapper,
- guienv, highlight_boxes, screensize, skycolor, flags.show_hud,
+ guienv, screensize, skycolor, flags.show_hud,
flags.show_minimap);
/*
@@ -4328,7 +4448,6 @@ void Game::settingChangedCallback(const std::string &setting_name, void *data)
void Game::readSettings()
{
m_cache_doubletap_jump = g_settings->getBool("doubletap_jump");
- m_cache_enable_node_highlighting = g_settings->getBool("enable_node_highlighting");
m_cache_enable_clouds = g_settings->getBool("enable_clouds");
m_cache_enable_particles = g_settings->getBool("enable_particles");
m_cache_enable_fog = g_settings->getBool("enable_fog");
@@ -4420,4 +4539,3 @@ void the_game(bool *kill,
errorstream << "ModError: " << error_message << std::endl;
}
}
-
diff --git a/src/gamedef.h b/src/gamedef.h
index a5f6b5968..7e3da4cac 100644
--- a/src/gamedef.h
+++ b/src/gamedef.h
@@ -32,6 +32,8 @@ class IShaderSource;
class MtEventManager;
class IRollbackManager;
class EmergeManager;
+class Camera;
+
namespace irr { namespace scene {
class IAnimatedMesh;
class ISceneManager;
@@ -67,6 +69,10 @@ public:
{ return NULL; }
virtual scene::ISceneManager* getSceneManager()=0;
+ virtual Camera* getCamera()
+ { return NULL; }
+ virtual void setCamera(Camera *camera) {}
+
// Only usable on the server, and NOT thread-safe. It is usable from the
// environment thread.
virtual IRollbackManager* getRollbackManager(){return NULL;}
diff --git a/src/gameparams.h b/src/gameparams.h
index b50e943d2..bf9953c39 100644
--- a/src/gameparams.h
+++ b/src/gameparams.h
@@ -17,17 +17,18 @@ with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef __GAME_PARAMS_H__
-#define __GAME_PARAMS_H__
+#ifndef GAME_PARAMS_H
+#define GAME_PARAMS_H
-#include "irrlichttypes_extrabloated.h"
+#include "irrlichttypes.h"
+
+struct SubgameSpec;
struct GameParams {
u16 socket_port;
std::string world_path;
SubgameSpec game_spec;
bool is_dedicated_server;
- int log_level;
};
#endif
diff --git a/src/genericobject.cpp b/src/genericobject.cpp
index 90e8cf3d3..368cae1ff 100644
--- a/src/genericobject.cpp
+++ b/src/genericobject.cpp
@@ -110,7 +110,7 @@ std::string gob_cmd_update_armor_groups(const ItemGroupList &armor_groups)
writeU8(os, GENERIC_CMD_UPDATE_ARMOR_GROUPS);
writeU16(os, armor_groups.size());
for(ItemGroupList::const_iterator i = armor_groups.begin();
- i != armor_groups.end(); i++){
+ i != armor_groups.end(); ++i){
os<<serializeString(i->first);
writeS16(os, i->second);
}
diff --git a/src/gettext.cpp b/src/gettext.cpp
index 0fd6b574e..81d93fef3 100644
--- a/src/gettext.cpp
+++ b/src/gettext.cpp
@@ -117,107 +117,87 @@ const char* MSVC_LocaleLookup(const char* raw_shortname) {
#endif
/******************************************************************************/
-#ifdef _MSC_VER
-void init_gettext(const char *path, const std::string &configured_language, int argc, char** argv) {
-#else
-void init_gettext(const char *path, const std::string &configured_language) {
-#endif
+void init_gettext(const char *path, const std::string &configured_language,
+ int argc, char *argv[])
+{
#if USE_GETTEXT
- /** first try to set user override environment **/
- if (configured_language.length() != 0) {
+ // First, try to set user override environment
+ if (!configured_language.empty()) {
#ifndef _WIN32
- /* add user specified locale to environment */
+ // Add user specified locale to environment
setenv("LANGUAGE", configured_language.c_str(), 1);
- /* reload locale with changed environment */
+ // Reload locale with changed environment
setlocale(LC_ALL, "");
#elif defined(_MSC_VER)
- std::string current_language_var("");
- if (getenv("LANGUAGE") != 0) {
- current_language_var = std::string(getenv("LANGUAGE"));
- }
-
- char *lang_str = (char*)calloc(10 + configured_language.length(), sizeof(char));
- strcat(lang_str, "LANGUAGE=");
- strcat(lang_str, configured_language.c_str());
- putenv(lang_str);
+ std::string current_language;
+ const char *env_lang = getenv("LANGUAGE");
+ if (env_lang)
+ current_language = env_lang;
- SetEnvironmentVariableA("LANGUAGE",configured_language.c_str());
+ _putenv(("LANGUAGE=" + configured_language).c_str());
+ SetEnvironmentVariableA("LANGUAGE", configured_language.c_str());
#ifndef SERVER
- //very very dirty workaround to force gettext to see the right environment
- if (current_language_var != configured_language) {
- STARTUPINFO startupinfo;
- PROCESS_INFORMATION processinfo;
- memset(&startupinfo, 0, sizeof(startupinfo));
- memset(&processinfo, 0, sizeof(processinfo));
- errorstream << "MSVC localization workaround active restating minetest in new environment!" << std::endl;
-
- std::string parameters = "";
-
- for (unsigned int i=1;i < argc; i++) {
- if (parameters != "") {
- parameters += " ";
- }
+ // Hack to force gettext to see the right environment
+ if (current_language != configured_language) {
+ errorstream << "MSVC localization workaround active. "
+ "Restarting " PROJECT_NAME_C " in a new environment!" << std::endl;
+
+ std::string parameters;
+
+ for (unsigned int i = 1; i < argc; i++) {
+ if (!parameters.empty())
+ parameters += ' ';
+
parameters += argv[i];
}
- const char* ptr_parameters = 0;
+ const char *ptr_parameters = NULL;
- if (parameters != "") {
+ if (!parameters.empty())
ptr_parameters = parameters.c_str();
- }
-
- /** users may start by short name in commandline without extention **/
- std::string appname = argv[0];
- if (appname.substr(appname.length() - 4) != ".exe") {
- appname += ".exe";
- }
- if (!CreateProcess(appname.c_str(),
- (char*) ptr_parameters,
- NULL,
- NULL,
- false,
- DETACHED_PROCESS | CREATE_UNICODE_ENVIRONMENT,
- NULL,
- NULL,
- &startupinfo,
- &processinfo)) {
- char buffer[1024];
- FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
- NULL,
- GetLastError(),
- MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),
- buffer,
- sizeof(buffer)-1,
- NULL);
+ // Allow calling without an extension
+ std::string app_name = argv[0];
+ if (app_name.compare(app_name.size() - 4, 4, ".exe") != 0)
+ app_name += ".exe";
+
+ STARTUPINFO startup_info = {0};
+ PROCESS_INFORMATION process_info = {0};
+
+ bool success = CreateProcess(app_name.c_str(), (char *)ptr_parameters,
+ NULL, NULL, false, DETACHED_PROCESS | CREATE_UNICODE_ENVIRONMENT,
+ NULL, NULL, &startup_info, &process_info);
+
+ if (success) {
+ exit(0);
+ // NOTREACHED
+ } else {
+ char buffer[1024];
+
+ FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(),
+ MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT), buffer,
+ sizeof(buffer) - 1, NULL);
+
errorstream << "*******************************************************" << std::endl;
- errorstream << "CMD: " << appname << std::endl;
+ errorstream << "CMD: " << app_name << std::endl;
errorstream << "Failed to restart with current locale: " << std::endl;
errorstream << buffer;
errorstream << "Expect language to be broken!" << std::endl;
errorstream << "*******************************************************" << std::endl;
}
- else {
- exit(0);
- }
+ }
#else
- errorstream << "*******************************************************" << std::endl;
- errorstream << "Can't apply locale workaround for server!" << std::endl;
- errorstream << "Expect language to be broken!" << std::endl;
- errorstream << "*******************************************************" << std::endl;
-
+ errorstream << "*******************************************************" << std::endl;
+ errorstream << "Can't apply locale workaround for server!" << std::endl;
+ errorstream << "Expect language to be broken!" << std::endl;
+ errorstream << "*******************************************************" << std::endl;
#endif
- }
- setlocale(LC_ALL,configured_language.c_str());
+ setlocale(LC_ALL, configured_language.c_str());
#else // Mingw
- char *lang_str = (char*)calloc(10 + configured_language.length(), sizeof(char));
- strcat(lang_str, "LANGUAGE=");
- strcat(lang_str, configured_language.c_str());
- putenv(lang_str);
-
+ _putenv(("LANGUAGE=" + configured_language).c_str());
setlocale(LC_ALL, "");
#endif // ifndef _WIN32
}
@@ -266,4 +246,3 @@ void init_gettext(const char *path, const std::string &configured_language) {
infostream << "Message locale is now set to: "
<< setlocale(LC_ALL, 0) << std::endl;
}
-
diff --git a/src/gettext.h b/src/gettext.h
index 9b4e801f7..b8d27f77c 100644
--- a/src/gettext.h
+++ b/src/gettext.h
@@ -32,12 +32,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#define gettext_noop(String) (String)
#define N_(String) gettext_noop((String))
-#ifdef _MSC_VER
void init_gettext(const char *path, const std::string &configured_language,
- int argc, char** argv);
-#else
-void init_gettext(const char *path, const std::string &configured_language);
-#endif
+ int argc, char *argv[]);
extern wchar_t *utf8_to_wide_c(const char *str);
@@ -48,14 +44,6 @@ inline const wchar_t *wgettext(const char *str)
return utf8_to_wide_c(gettext(str));
}
-inline std::wstring wstrgettext(const std::string &text)
-{
- const wchar_t *tmp = wgettext(text.c_str());
- std::wstring retval = (std::wstring)tmp;
- delete[] tmp;
- return retval;
-}
-
inline std::string strgettext(const std::string &text)
{
return gettext(text.c_str());
diff --git a/src/guiChatConsole.cpp b/src/guiChatConsole.cpp
index 3937e405c..17a1689c7 100644
--- a/src/guiChatConsole.cpp
+++ b/src/guiChatConsole.cpp
@@ -46,15 +46,18 @@ GUIChatConsole::GUIChatConsole(
gui::IGUIElement* parent,
s32 id,
ChatBackend* backend,
- Client* client
+ Client* client,
+ IMenuManager* menumgr
):
IGUIElement(gui::EGUIET_ELEMENT, env, parent, id,
core::rect<s32>(0,0,100,100)),
m_chat_backend(backend),
m_client(client),
+ m_menumgr(menumgr),
m_screensize(v2u32(0,0)),
m_animate_time_old(0),
m_open(false),
+ m_close_on_enter(false),
m_height(0),
m_desired_height(0),
m_desired_height_fraction(0.0),
@@ -119,6 +122,10 @@ void GUIChatConsole::openConsole(f32 height)
m_desired_height_fraction = height;
m_desired_height = height * m_screensize.Y;
reformatConsole();
+ m_animate_time_old = getTimeMs();
+ IGUIElement::setVisible(true);
+ Environment->setFocus(this);
+ m_menumgr->createdMenu(this);
}
bool GUIChatConsole::isOpen() const
@@ -134,11 +141,13 @@ bool GUIChatConsole::isOpenInhibited() const
void GUIChatConsole::closeConsole()
{
m_open = false;
+ Environment->removeFocus(this);
+ m_menumgr->deletingMenu(this);
}
void GUIChatConsole::closeConsoleAtOnce()
{
- m_open = false;
+ closeConsole();
m_height = 0;
recalculateConsolePosition();
}
@@ -148,6 +157,14 @@ f32 GUIChatConsole::getDesiredHeight() const
return m_desired_height_fraction;
}
+void GUIChatConsole::replaceAndAddToHistory(std::wstring line)
+{
+ ChatPrompt& prompt = m_chat_backend->getPrompt();
+ prompt.addToHistory(prompt.getLine());
+ prompt.replace(line);
+}
+
+
void GUIChatConsole::setCursor(
bool visible, bool blinking, f32 blink_speed, f32 relative_height)
{
@@ -228,6 +245,13 @@ void GUIChatConsole::animate(u32 msec)
{
// animate the console height
s32 goal = m_open ? m_desired_height : 0;
+
+ // Set invisible if close animation finished (reset by openConsole)
+ // This function (animate()) is never called once its visibility becomes false so do not
+ // actually set visible to false before the inhibited period is over
+ if (!m_open && m_height == 0 && m_open_inhibited == 0)
+ IGUIElement::setVisible(false);
+
if (m_height != goal)
{
s32 max_change = msec * m_screensize.Y * (m_height_speed / 1000.0);
@@ -362,13 +386,15 @@ void GUIChatConsole::drawPrompt()
s32 cursor_pos = prompt.getVisibleCursorPosition();
if (cursor_pos >= 0)
{
+ s32 cursor_len = prompt.getCursorLength();
video::IVideoDriver* driver = Environment->getVideoDriver();
s32 x = (1 + cursor_pos) * m_fontsize.X;
core::rect<s32> destrect(
x,
- y + (1.0-m_cursor_height) * m_fontsize.Y,
- x + m_fontsize.X,
- y + m_fontsize.Y);
+ y + m_fontsize.Y * (1.0 - m_cursor_height),
+ x + m_fontsize.X * MYMAX(cursor_len, 1),
+ y + m_fontsize.Y * (cursor_len ? m_cursor_height+1 : 1)
+ );
video::SColor cursor_color(255,255,255,255);
driver->draw2DRectangle(
cursor_color,
@@ -381,23 +407,27 @@ void GUIChatConsole::drawPrompt()
bool GUIChatConsole::OnEvent(const SEvent& event)
{
+
+ ChatPrompt &prompt = m_chat_backend->getPrompt();
+
if(event.EventType == EET_KEY_INPUT_EVENT && event.KeyInput.PressedDown)
{
// Key input
if(KeyPress(event.KeyInput) == getKeySetting("keymap_console"))
{
closeConsole();
- Environment->removeFocus(this);
// inhibit open so the_game doesn't reopen immediately
m_open_inhibited = 50;
+ m_close_on_enter = false;
return true;
}
else if(event.KeyInput.Key == KEY_ESCAPE)
{
closeConsoleAtOnce();
- Environment->removeFocus(this);
- // the_game will open the pause menu
+ m_close_on_enter = false;
+ // inhibit open so the_game doesn't reopen immediately
+ m_open_inhibited = 1; // so the ESCAPE button doesn't open the "pause menu"
return true;
}
else if(event.KeyInput.Key == KEY_PRIOR)
@@ -412,57 +442,50 @@ bool GUIChatConsole::OnEvent(const SEvent& event)
}
else if(event.KeyInput.Key == KEY_RETURN)
{
- std::wstring text = m_chat_backend->getPrompt().submit();
+ prompt.addToHistory(prompt.getLine());
+ std::wstring text = prompt.replace(L"");
m_client->typeChatMessage(text);
+ if (m_close_on_enter) {
+ closeConsoleAtOnce();
+ m_close_on_enter = false;
+ }
return true;
}
else if(event.KeyInput.Key == KEY_UP)
{
// Up pressed
// Move back in history
- m_chat_backend->getPrompt().historyPrev();
+ prompt.historyPrev();
return true;
}
else if(event.KeyInput.Key == KEY_DOWN)
{
// Down pressed
// Move forward in history
- m_chat_backend->getPrompt().historyNext();
- return true;
- }
- else if(event.KeyInput.Key == KEY_LEFT)
- {
- // Left or Ctrl-Left pressed
- // move character / word to the left
- ChatPrompt::CursorOpScope scope =
- event.KeyInput.Control ?
- ChatPrompt::CURSOROP_SCOPE_WORD :
- ChatPrompt::CURSOROP_SCOPE_CHARACTER;
- m_chat_backend->getPrompt().cursorOperation(
- ChatPrompt::CURSOROP_MOVE,
- ChatPrompt::CURSOROP_DIR_LEFT,
- scope);
+ prompt.historyNext();
return true;
}
- else if(event.KeyInput.Key == KEY_RIGHT)
+ else if(event.KeyInput.Key == KEY_LEFT || event.KeyInput.Key == KEY_RIGHT)
{
- // Right or Ctrl-Right pressed
- // move character / word to the right
- ChatPrompt::CursorOpScope scope =
- event.KeyInput.Control ?
+ // Left/right pressed
+ // Move/select character/word to the left depending on control and shift keys
+ ChatPrompt::CursorOp op = event.KeyInput.Shift ?
+ ChatPrompt::CURSOROP_SELECT :
+ ChatPrompt::CURSOROP_MOVE;
+ ChatPrompt::CursorOpDir dir = event.KeyInput.Key == KEY_LEFT ?
+ ChatPrompt::CURSOROP_DIR_LEFT :
+ ChatPrompt::CURSOROP_DIR_RIGHT;
+ ChatPrompt::CursorOpScope scope = event.KeyInput.Control ?
ChatPrompt::CURSOROP_SCOPE_WORD :
ChatPrompt::CURSOROP_SCOPE_CHARACTER;
- m_chat_backend->getPrompt().cursorOperation(
- ChatPrompt::CURSOROP_MOVE,
- ChatPrompt::CURSOROP_DIR_RIGHT,
- scope);
+ prompt.cursorOperation(op, dir, scope);
return true;
}
else if(event.KeyInput.Key == KEY_HOME)
{
// Home pressed
// move to beginning of line
- m_chat_backend->getPrompt().cursorOperation(
+ prompt.cursorOperation(
ChatPrompt::CURSOROP_MOVE,
ChatPrompt::CURSOROP_DIR_LEFT,
ChatPrompt::CURSOROP_SCOPE_LINE);
@@ -472,7 +495,7 @@ bool GUIChatConsole::OnEvent(const SEvent& event)
{
// End pressed
// move to end of line
- m_chat_backend->getPrompt().cursorOperation(
+ prompt.cursorOperation(
ChatPrompt::CURSOROP_MOVE,
ChatPrompt::CURSOROP_DIR_RIGHT,
ChatPrompt::CURSOROP_SCOPE_LINE);
@@ -486,7 +509,7 @@ bool GUIChatConsole::OnEvent(const SEvent& event)
event.KeyInput.Control ?
ChatPrompt::CURSOROP_SCOPE_WORD :
ChatPrompt::CURSOROP_SCOPE_CHARACTER;
- m_chat_backend->getPrompt().cursorOperation(
+ prompt.cursorOperation(
ChatPrompt::CURSOROP_DELETE,
ChatPrompt::CURSOROP_DIR_LEFT,
scope);
@@ -500,30 +523,72 @@ bool GUIChatConsole::OnEvent(const SEvent& event)
event.KeyInput.Control ?
ChatPrompt::CURSOROP_SCOPE_WORD :
ChatPrompt::CURSOROP_SCOPE_CHARACTER;
- m_chat_backend->getPrompt().cursorOperation(
+ prompt.cursorOperation(
ChatPrompt::CURSOROP_DELETE,
ChatPrompt::CURSOROP_DIR_RIGHT,
scope);
return true;
}
+ else if(event.KeyInput.Key == KEY_KEY_A && event.KeyInput.Control)
+ {
+ // Ctrl-A pressed
+ // Select all text
+ prompt.cursorOperation(
+ ChatPrompt::CURSOROP_SELECT,
+ ChatPrompt::CURSOROP_DIR_LEFT, // Ignored
+ ChatPrompt::CURSOROP_SCOPE_LINE);
+ return true;
+ }
+ else if(event.KeyInput.Key == KEY_KEY_C && event.KeyInput.Control)
+ {
+ // Ctrl-C pressed
+ // Copy text to clipboard
+ if (prompt.getCursorLength() <= 0)
+ return true;
+ std::wstring wselected = prompt.getSelection();
+ std::string selected(wselected.begin(), wselected.end());
+ Environment->getOSOperator()->copyToClipboard(selected.c_str());
+ return true;
+ }
else if(event.KeyInput.Key == KEY_KEY_V && event.KeyInput.Control)
{
// Ctrl-V pressed
// paste text from clipboard
+ if (prompt.getCursorLength() > 0) {
+ // Delete selected section of text
+ prompt.cursorOperation(
+ ChatPrompt::CURSOROP_DELETE,
+ ChatPrompt::CURSOROP_DIR_LEFT, // Ignored
+ ChatPrompt::CURSOROP_SCOPE_SELECTION);
+ }
IOSOperator *os_operator = Environment->getOSOperator();
const c8 *text = os_operator->getTextFromClipboard();
- if (text)
- {
- std::wstring wtext = narrow_to_wide(text);
- m_chat_backend->getPrompt().input(wtext);
- }
+ if (!text)
+ return true;
+ std::basic_string<unsigned char> str((const unsigned char*)text);
+ prompt.input(std::wstring(str.begin(), str.end()));
+ return true;
+ }
+ else if(event.KeyInput.Key == KEY_KEY_X && event.KeyInput.Control)
+ {
+ // Ctrl-X pressed
+ // Cut text to clipboard
+ if (prompt.getCursorLength() <= 0)
+ return true;
+ std::wstring wselected = prompt.getSelection();
+ std::string selected(wselected.begin(), wselected.end());
+ Environment->getOSOperator()->copyToClipboard(selected.c_str());
+ prompt.cursorOperation(
+ ChatPrompt::CURSOROP_DELETE,
+ ChatPrompt::CURSOROP_DIR_LEFT, // Ignored
+ ChatPrompt::CURSOROP_SCOPE_SELECTION);
return true;
}
else if(event.KeyInput.Key == KEY_KEY_U && event.KeyInput.Control)
{
// Ctrl-U pressed
// kill line to left end
- m_chat_backend->getPrompt().cursorOperation(
+ prompt.cursorOperation(
ChatPrompt::CURSOROP_DELETE,
ChatPrompt::CURSOROP_DIR_LEFT,
ChatPrompt::CURSOROP_SCOPE_LINE);
@@ -533,7 +598,7 @@ bool GUIChatConsole::OnEvent(const SEvent& event)
{
// Ctrl-K pressed
// kill line to right end
- m_chat_backend->getPrompt().cursorOperation(
+ prompt.cursorOperation(
ChatPrompt::CURSOROP_DELETE,
ChatPrompt::CURSOROP_DIR_RIGHT,
ChatPrompt::CURSOROP_SCOPE_LINE);
@@ -545,7 +610,7 @@ bool GUIChatConsole::OnEvent(const SEvent& event)
// Nick completion
std::list<std::string> names = m_client->getConnectedPlayerNames();
bool backwards = event.KeyInput.Shift;
- m_chat_backend->getPrompt().nickCompletion(names, backwards);
+ prompt.nickCompletion(names, backwards);
return true;
}
else if(event.KeyInput.Char != 0 && !event.KeyInput.Control)
@@ -553,9 +618,9 @@ bool GUIChatConsole::OnEvent(const SEvent& event)
#if (defined(linux) || defined(__linux))
wchar_t wc = L'_';
mbtowc( &wc, (char *) &event.KeyInput.Char, sizeof(event.KeyInput.Char) );
- m_chat_backend->getPrompt().input(wc);
+ prompt.input(wc);
#else
- m_chat_backend->getPrompt().input(event.KeyInput.Char);
+ prompt.input(event.KeyInput.Char);
#endif
return true;
}
@@ -572,3 +637,13 @@ bool GUIChatConsole::OnEvent(const SEvent& event)
return Parent ? Parent->OnEvent(event) : false;
}
+void GUIChatConsole::setVisible(bool visible)
+{
+ m_open = visible;
+ IGUIElement::setVisible(visible);
+ if (!visible) {
+ m_height = 0;
+ recalculateConsolePosition();
+ }
+}
+
diff --git a/src/guiChatConsole.h b/src/guiChatConsole.h
index 652b265a4..3013a1d31 100644
--- a/src/guiChatConsole.h
+++ b/src/guiChatConsole.h
@@ -21,6 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#define GUICHATCONSOLE_HEADER
#include "irrlichttypes_extrabloated.h"
+#include "modalMenu.h"
#include "chat.h"
#include "config.h"
@@ -33,7 +34,8 @@ public:
gui::IGUIElement* parent,
s32 id,
ChatBackend* backend,
- Client* client);
+ Client* client,
+ IMenuManager* menumgr);
virtual ~GUIChatConsole();
// Open the console (height = desired fraction of screen size)
@@ -51,11 +53,16 @@ public:
void closeConsole();
// Close the console immediately, without animation.
void closeConsoleAtOnce();
+ // 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(std::wstring line);
+
// Change how the cursor looks
void setCursor(
bool visible,
@@ -70,6 +77,8 @@ public:
virtual bool OnEvent(const SEvent& event);
+ virtual void setVisible(bool visible);
+
private:
void reformatConsole();
void recalculateConsolePosition();
@@ -81,11 +90,9 @@ private:
void drawPrompt();
private:
- // pointer to the chat backend
ChatBackend* m_chat_backend;
-
- // pointer to the client
Client* m_client;
+ IMenuManager* m_menumgr;
// current screen size
v2u32 m_screensize;
@@ -95,6 +102,8 @@ private:
// should the console be opened or closed?
bool m_open;
+ // should it close after you press enter?
+ bool m_close_on_enter;
// current console height [pixels]
s32 m_height;
// desired height [pixels]
diff --git a/src/guiEngine.cpp b/src/guiEngine.cpp
index c616bc322..ba286a91c 100644
--- a/src/guiEngine.cpp
+++ b/src/guiEngine.cpp
@@ -238,13 +238,13 @@ bool GUIEngine::loadMainMenuScript()
}
std::string script = porting::path_share + DIR_DELIM "builtin" + DIR_DELIM "init.lua";
- if (m_script->loadScript(script)) {
+ try {
+ m_script->loadScript(script);
// Menu script loaded
return true;
- } else {
- infostream
- << "GUIEngine: execution of menu script in: \""
- << m_scriptdir << "\" failed!" << std::endl;
+ } catch (const ModError &e) {
+ errorstream << "GUIEngine: execution of menu script failed: "
+ << e.what() << std::endl;
}
return false;
@@ -361,7 +361,7 @@ void GUIEngine::cloudPreProcess()
/******************************************************************************/
void GUIEngine::cloudPostProcess()
{
- float fps_max = g_settings->getFloat("fps_max");
+ float fps_max = g_settings->getFloat("pause_fps_max");
// Time of frame without fps limit
u32 busytime_u32;
diff --git a/src/guiFileSelectMenu.cpp b/src/guiFileSelectMenu.cpp
index e02407427..0bb02f8a6 100644
--- a/src/guiFileSelectMenu.cpp
+++ b/src/guiFileSelectMenu.cpp
@@ -36,6 +36,7 @@ GUIModalMenu(env, parent, id, menumgr)
GUIFileSelectMenu::~GUIFileSelectMenu()
{
removeChildren();
+ setlocale(LC_NUMERIC, "C");
}
void GUIFileSelectMenu::removeChildren()
diff --git a/src/guiFormSpecMenu.cpp b/src/guiFormSpecMenu.cpp
index 62a84460f..2bf06c1d6 100644
--- a/src/guiFormSpecMenu.cpp
+++ b/src/guiFormSpecMenu.cpp
@@ -28,7 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "constants.h"
#include "gamedef.h"
#include "keycode.h"
-#include "strfnd.h"
+#include "util/strfnd.h"
#include <IGUICheckBox.h>
#include <IGUIEditBox.h>
#include <IGUIButton.h>
@@ -240,6 +240,15 @@ GUITable* GUIFormSpecMenu::getTable(const std::string &tablename)
return 0;
}
+std::vector<std::string>* GUIFormSpecMenu::getDropDownValues(const std::string &name)
+{
+ for (u32 i = 0; i < m_dropdowns.size(); ++i) {
+ if (name == m_dropdowns[i].first.fname)
+ return &m_dropdowns[i].second;
+ }
+ return NULL;
+}
+
static std::vector<std::string> split(const std::string &s, char delim)
{
std::vector<std::string> tokens;
@@ -300,7 +309,7 @@ void GUIFormSpecMenu::parseSize(parserData* data,std::string element)
void GUIFormSpecMenu::parseList(parserData* data,std::string element)
{
if (m_gamedef == 0) {
- errorstream<<"WARNING: invalid use of 'list' with m_gamedef==0"<<std::endl;
+ warningstream<<"invalid use of 'list' with m_gamedef==0"<<std::endl;
return;
}
@@ -345,7 +354,7 @@ void GUIFormSpecMenu::parseList(parserData* data,std::string element)
}
if(!data->explicit_size)
- errorstream<<"WARNING: invalid use of list without a size[] element"<<std::endl;
+ warningstream<<"invalid use of list without a size[] element"<<std::endl;
m_inventorylists.push_back(ListDrawSpec(loc, listname, pos, geom, start_i));
return;
}
@@ -413,7 +422,7 @@ void GUIFormSpecMenu::parseCheckbox(parserData* data,std::string element)
if (selected == "true")
fselected = true;
- std::wstring wlabel = utf8_to_wide(label);
+ std::wstring wlabel = utf8_to_wide(unescape_string(label));
core::rect<s32> rect = core::rect<s32>(
pos.X, pos.Y + ((imgsize.Y/2) - m_btn_height),
@@ -450,7 +459,7 @@ void GUIFormSpecMenu::parseScrollBar(parserData* data, std::string element)
if (parts.size() >= 5) {
std::vector<std::string> v_pos = split(parts[0],',');
std::vector<std::string> v_dim = split(parts[1],',');
- std::string name = parts[2];
+ std::string name = parts[3];
std::string value = parts[4];
MY_CHECKPOS("scrollbar",0);
@@ -525,7 +534,7 @@ void GUIFormSpecMenu::parseImage(parserData* data,std::string element)
geom.Y = stof(v_geom[1]) * (float)imgsize.Y;
if(!data->explicit_size)
- errorstream<<"WARNING: invalid use of image without a size[] element"<<std::endl;
+ warningstream<<"invalid use of image without a size[] element"<<std::endl;
m_images.push_back(ImageDrawSpec(name, pos, geom));
return;
}
@@ -541,7 +550,7 @@ void GUIFormSpecMenu::parseImage(parserData* data,std::string element)
pos.Y += stof(v_pos[1]) * (float) spacing.Y;
if(!data->explicit_size)
- errorstream<<"WARNING: invalid use of image without a size[] element"<<std::endl;
+ warningstream<<"invalid use of image without a size[] element"<<std::endl;
m_images.push_back(ImageDrawSpec(name, pos));
return;
}
@@ -571,8 +580,8 @@ void GUIFormSpecMenu::parseItemImage(parserData* data,std::string element)
geom.Y = stof(v_geom[1]) * (float)imgsize.Y;
if(!data->explicit_size)
- errorstream<<"WARNING: invalid use of item_image without a size[] element"<<std::endl;
- m_itemimages.push_back(ImageDrawSpec(name, pos, geom));
+ warningstream<<"invalid use of item_image without a size[] element"<<std::endl;
+ m_itemimages.push_back(ImageDrawSpec("", name, pos, geom));
return;
}
errorstream<< "Invalid ItemImage element(" << parts.size() << "): '" << element << "'" << std::endl;
@@ -607,11 +616,9 @@ void GUIFormSpecMenu::parseButton(parserData* data,std::string element,
pos.X + geom.X, pos.Y + m_btn_height);
if(!data->explicit_size)
- errorstream<<"WARNING: invalid use of button without a size[] element"<<std::endl;
-
- label = unescape_string(label);
+ warningstream<<"invalid use of button without a size[] element"<<std::endl;
- std::wstring wlabel = utf8_to_wide(label);
+ std::wstring wlabel = utf8_to_wide(unescape_string(label));
FieldSpec spec(
name,
@@ -666,7 +673,7 @@ void GUIFormSpecMenu::parseBackground(parserData* data,std::string element)
}
if(!data->explicit_size)
- errorstream<<"WARNING: invalid use of background without a size[] element"<<std::endl;
+ warningstream<<"invalid use of background without a size[] element"<<std::endl;
m_backgrounds.push_back(ImageDrawSpec(name, pos, geom));
return;
}
@@ -733,7 +740,6 @@ void GUIFormSpecMenu::parseTable(parserData* data,std::string element)
geom.X = stof(v_geom[0]) * (float)spacing.X;
geom.Y = stof(v_geom[1]) * (float)spacing.Y;
-
core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
FieldSpec spec(
@@ -746,7 +752,7 @@ void GUIFormSpecMenu::parseTable(parserData* data,std::string element)
spec.ftype = f_Table;
for (unsigned int i = 0; i < items.size(); ++i) {
- items[i] = unescape_string(items[i]);
+ items[i] = unescape_enriched(unescape_string(items[i]));
}
//now really show table
@@ -818,7 +824,7 @@ void GUIFormSpecMenu::parseTextList(parserData* data,std::string element)
spec.ftype = f_Table;
for (unsigned int i = 0; i < items.size(); ++i) {
- items[i] = unescape_string(items[i]);
+ items[i] = unescape_enriched(unescape_string(items[i]));
}
//now really show list
@@ -889,13 +895,22 @@ void GUIFormSpecMenu::parseDropDown(parserData* data,std::string element)
}
for (unsigned int i=0; i < items.size(); i++) {
- e->addItem(utf8_to_wide(items[i]).c_str());
+ e->addItem(unescape_enriched(unescape_string(
+ utf8_to_wide(items[i]))).c_str());
}
if (str_initial_selection != "")
e->setSelected(stoi(str_initial_selection.c_str())-1);
m_fields.push_back(spec);
+
+ m_dropdowns.push_back(std::pair<FieldSpec,
+ std::vector<std::string> >(spec, std::vector<std::string>()));
+ std::vector<std::string> &values = m_dropdowns.back().second;
+ for (unsigned int i = 0; i < items.size(); i++) {
+ values.push_back(unescape_string(items[i]));
+ }
+
return;
}
errorstream << "Invalid dropdown element(" << parts.size() << "): '"
@@ -930,9 +945,7 @@ void GUIFormSpecMenu::parsePwdField(parserData* data,std::string element)
core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
- label = unescape_string(label);
-
- std::wstring wlabel = utf8_to_wide(label);
+ std::wstring wlabel = utf8_to_wide(unescape_string(label));
FieldSpec spec(
name,
@@ -982,7 +995,7 @@ void GUIFormSpecMenu::parseSimpleField(parserData* data,
core::rect<s32> rect;
if(data->explicit_size)
- errorstream<<"WARNING: invalid use of unpositioned \"field\" in inventory"<<std::endl;
+ warningstream<<"invalid use of unpositioned \"field\" in inventory"<<std::endl;
v2s32 pos = padding + AbsoluteRect.UpperLeftCorner;
pos.Y = ((m_fields.size()+2)*60);
@@ -995,15 +1008,13 @@ void GUIFormSpecMenu::parseSimpleField(parserData* data,
if(m_form_src)
default_val = m_form_src->resolveText(default_val);
- default_val = unescape_string(default_val);
- label = unescape_string(label);
- std::wstring wlabel = utf8_to_wide(label);
+ std::wstring wlabel = utf8_to_wide(unescape_string(label));
FieldSpec spec(
name,
wlabel,
- utf8_to_wide(default_val),
+ utf8_to_wide(unescape_string(default_val)),
258+m_fields.size()
);
@@ -1088,21 +1099,18 @@ void GUIFormSpecMenu::parseTextArea(parserData* data,
core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
if(!data->explicit_size)
- errorstream<<"WARNING: invalid use of positioned "<<type<<" without a size[] element"<<std::endl;
+ warningstream<<"invalid use of positioned "<<type<<" without a size[] element"<<std::endl;
if(m_form_src)
default_val = m_form_src->resolveText(default_val);
- default_val = unescape_string(default_val);
- label = unescape_string(label);
-
- std::wstring wlabel = utf8_to_wide(label);
+ std::wstring wlabel = utf8_to_wide(unescape_string(label));
FieldSpec spec(
name,
wlabel,
- utf8_to_wide(default_val),
+ utf8_to_wide(unescape_string(default_val)),
258+m_fields.size()
);
@@ -1195,9 +1203,8 @@ void GUIFormSpecMenu::parseLabel(parserData* data,std::string element)
pos.Y += (stof(v_pos[1]) + 7.0/30.0) * (float)spacing.Y;
if(!data->explicit_size)
- errorstream<<"WARNING: invalid use of label without a size[] element"<<std::endl;
+ warningstream<<"invalid use of label without a size[] element"<<std::endl;
- text = unescape_string(text);
std::vector<std::string> lines = split(text, '\n');
for (unsigned int i = 0; i != lines.size(); i++) {
@@ -1211,7 +1218,7 @@ void GUIFormSpecMenu::parseLabel(parserData* data,std::string element)
// in the integer cases: 0.4 is not exactly
// representable in binary floating point.
s32 posy = pos.Y + ((float)i) * spacing.Y * 2.0 / 5.0;
- std::wstring wlabel = utf8_to_wide(lines[i]);
+ std::wstring wlabel = utf8_to_wide(unescape_string(lines[i]));
core::rect<s32> rect = core::rect<s32>(
pos.X, posy - m_btn_height,
pos.X + m_font->getDimension(wlabel.c_str()).Width,
@@ -1243,7 +1250,8 @@ void GUIFormSpecMenu::parseVertLabel(parserData* data,std::string element)
((parts.size() > 2) && (m_formspec_version > FORMSPEC_API_VERSION)))
{
std::vector<std::string> v_pos = split(parts[0],',');
- std::wstring text = utf8_to_wide(unescape_string(parts[1]));
+ std::wstring text = unescape_enriched(
+ unescape_string(utf8_to_wide(parts[1])));
MY_CHECKPOS("vertlabel",1);
@@ -1260,7 +1268,7 @@ void GUIFormSpecMenu::parseVertLabel(parserData* data,std::string element)
//actually text.length() would be correct but adding +1 avoids to break all mods
if(!data->explicit_size)
- errorstream<<"WARNING: invalid use of label without a size[] element"<<std::endl;
+ warningstream<<"invalid use of label without a size[] element"<<std::endl;
std::wstring label = L"";
@@ -1326,13 +1334,12 @@ void GUIFormSpecMenu::parseImageButton(parserData* data,std::string element,
core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
if(!data->explicit_size)
- errorstream<<"WARNING: invalid use of image_button without a size[] element"<<std::endl;
+ warningstream<<"invalid use of image_button without a size[] element"<<std::endl;
image_name = unescape_string(image_name);
pressed_image_name = unescape_string(pressed_image_name);
- label = unescape_string(label);
- std::wstring wlabel = utf8_to_wide(label);
+ std::wstring wlabel = utf8_to_wide(unescape_string(label));
FieldSpec spec(
name,
@@ -1430,7 +1437,8 @@ void GUIFormSpecMenu::parseTabHeader(parserData* data,std::string element)
e->setNotClipped(true);
for (unsigned int i = 0; i < buttons.size(); i++) {
- e->addTab(utf8_to_wide(buttons[i]).c_str(), -1);
+ e->addTab(unescape_enriched(unescape_string(
+ utf8_to_wide(buttons[i]))).c_str(), -1);
}
if ((tab_index >= 0) &&
@@ -1449,9 +1457,8 @@ void GUIFormSpecMenu::parseItemImageButton(parserData* data,std::string element)
{
if (m_gamedef == 0) {
- errorstream <<
- "WARNING: invalid use of item_image_button with m_gamedef==0"
- << std::endl;
+ warningstream << "invalid use of item_image_button with m_gamedef==0"
+ << std::endl;
return;
}
@@ -1466,6 +1473,9 @@ void GUIFormSpecMenu::parseItemImageButton(parserData* data,std::string element)
std::string name = parts[3];
std::string label = parts[4];
+ label = unescape_string(label);
+ item_name = unescape_string(item_name);
+
MY_CHECKPOS("itemimagebutton",0);
MY_CHECKGEOM("itemimagebutton",1);
@@ -1479,19 +1489,17 @@ void GUIFormSpecMenu::parseItemImageButton(parserData* data,std::string element)
core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
if(!data->explicit_size)
- errorstream<<"WARNING: invalid use of item_image_button without a size[] element"<<std::endl;
+ warningstream<<"invalid use of item_image_button without a size[] element"<<std::endl;
IItemDefManager *idef = m_gamedef->idef();
ItemStack item;
item.deSerialize(item_name, idef);
- video::ITexture *texture = idef->getInventoryTexture(item.getDefinition(idef).name, m_gamedef);
m_tooltips[name] =
TooltipSpec(item.getDefinition(idef).description,
m_default_tooltip_bgcolor,
m_default_tooltip_color);
- label = unescape_string(label);
FieldSpec spec(
name,
utf8_to_wide(label),
@@ -1499,20 +1507,21 @@ void GUIFormSpecMenu::parseItemImageButton(parserData* data,std::string element)
258 + m_fields.size()
);
- gui::IGUIButton *e = Environment->addButton(rect, this, spec.fid, spec.flabel.c_str());
+ gui::IGUIButton *e = Environment->addButton(rect, this, spec.fid, L"");
if (spec.fname == data->focused_fieldname) {
Environment->setFocus(e);
}
- e->setUseAlphaChannel(true);
- e->setImage(guiScalingImageButton(Environment->getVideoDriver(), texture, geom.X, geom.Y));
- e->setPressedImage(guiScalingImageButton(Environment->getVideoDriver(), texture, geom.X, geom.Y));
- e->setScaleImage(true);
spec.ftype = f_Button;
rect+=data->basepos-padding;
spec.rect=rect;
m_fields.push_back(spec);
+ pos = padding + AbsoluteRect.UpperLeftCorner;
+ pos.X += stof(v_pos[0]) * (float) spacing.X;
+ pos.Y += stof(v_pos[1]) * (float) spacing.Y;
+ m_itemimages.push_back(ImageDrawSpec("", item_name, e, pos, geom));
+ m_static_texts.push_back(StaticTextSpec(utf8_to_wide(label), rect, e));
return;
}
errorstream<< "Invalid ItemImagebutton element(" << parts.size() << "): '" << element << "'" << std::endl;
@@ -1880,6 +1889,8 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
m_fields.clear();
m_boxes.clear();
m_tooltips.clear();
+ m_inventory_rings.clear();
+ m_static_texts.clear();
// Set default values (fits old formspec values)
m_bgcolor = video::SColor(140,0,0,0);
@@ -2103,7 +2114,7 @@ bool GUIFormSpecMenu::getAndroidUIInput()
}
for(std::vector<FieldSpec>::iterator iter = m_fields.begin();
- iter != m_fields.end(); iter++) {
+ iter != m_fields.end(); ++iter) {
if (iter->fname != fieldname) {
continue;
@@ -2151,13 +2162,14 @@ GUIFormSpecMenu::ItemSpec GUIFormSpecMenu::getItemAtPos(v2s32 p) const
return ItemSpec(InventoryLocation(), "", -1);
}
-void GUIFormSpecMenu::drawList(const ListDrawSpec &s, int phase)
+void GUIFormSpecMenu::drawList(const ListDrawSpec &s, int phase,
+ bool &item_hovered)
{
video::IVideoDriver* driver = Environment->getVideoDriver();
Inventory *inv = m_invmgr->getInventory(s.inventoryloc);
if(!inv){
- infostream<<"GUIFormSpecMenu::drawList(): WARNING: "
+ warningstream<<"GUIFormSpecMenu::drawList(): "
<<"The inventory location "
<<"\""<<s.inventoryloc.dump()<<"\" doesn't exist"
<<std::endl;
@@ -2165,7 +2177,7 @@ void GUIFormSpecMenu::drawList(const ListDrawSpec &s, int phase)
}
InventoryList *ilist = inv->getList(s.listname);
if(!ilist){
- infostream<<"GUIFormSpecMenu::drawList(): WARNING: "
+ warningstream<<"GUIFormSpecMenu::drawList(): "
<<"The inventory list \""<<s.listname<<"\" @ \""
<<s.inventoryloc.dump()<<"\" doesn't exist"
<<std::endl;
@@ -2192,13 +2204,16 @@ void GUIFormSpecMenu::drawList(const ListDrawSpec &s, int phase)
&& m_selected_item->listname == s.listname
&& m_selected_item->i == item_i;
bool hovering = rect.isPointInside(m_pointer);
+ ItemRotationKind rotation_kind = selected ? IT_ROT_SELECTED :
+ (hovering ? IT_ROT_HOVERED : IT_ROT_NONE);
- if(phase == 0)
- {
- if(hovering)
+ if (phase == 0) {
+ if (hovering) {
+ item_hovered = true;
driver->draw2DRectangle(m_slotbg_h, rect, &AbsoluteClippingRect);
- else
+ } else {
driver->draw2DRectangle(m_slotbg_n, rect, &AbsoluteClippingRect);
+ }
}
//Draw inv slot borders
@@ -2232,22 +2247,25 @@ void GUIFormSpecMenu::drawList(const ListDrawSpec &s, int phase)
if(!item.empty())
{
drawItemStack(driver, m_font, item,
- rect, &AbsoluteClippingRect, m_gamedef);
+ rect, &AbsoluteClippingRect, m_gamedef,
+ rotation_kind);
}
// Draw tooltip
- std::string tooltip_text = "";
- if (hovering && !m_selected_item)
- tooltip_text = item.getDefinition(m_gamedef->idef()).description;
- if (tooltip_text != "") {
- std::vector<std::string> tt_rows = str_split(tooltip_text, '\n');
+ std::wstring tooltip_text = L"";
+ if (hovering && !m_selected_item) {
+ tooltip_text = utf8_to_wide(item.getDefinition(m_gamedef->idef()).description);
+ tooltip_text = unescape_enriched(tooltip_text);
+ }
+ if (tooltip_text != L"") {
+ std::vector<std::wstring> tt_rows = str_split(tooltip_text, L'\n');
m_tooltip_element->setBackgroundColor(m_default_tooltip_bgcolor);
m_tooltip_element->setOverrideColor(m_default_tooltip_color);
m_tooltip_element->setVisible(true);
this->bringToFront(m_tooltip_element);
- m_tooltip_element->setText(utf8_to_wide(tooltip_text).c_str());
+ m_tooltip_element->setText(tooltip_text.c_str());
s32 tooltip_width = m_tooltip_element->getTextWidth() + m_btn_height;
- s32 tooltip_height = m_tooltip_element->getTextHeight() * tt_rows.size() + 5;
+ s32 tooltip_height = m_tooltip_element->getTextHeight() + 5;
v2u32 screenSize = driver->getScreenSize();
int tooltip_offset_x = m_btn_height;
int tooltip_offset_y = m_btn_height;
@@ -2273,11 +2291,15 @@ void GUIFormSpecMenu::drawList(const ListDrawSpec &s, int phase)
void GUIFormSpecMenu::drawSelectedItem()
{
- if(!m_selected_item)
- return;
-
video::IVideoDriver* driver = Environment->getVideoDriver();
+ if (!m_selected_item) {
+ drawItemStack(driver, m_font, ItemStack(),
+ core::rect<s32>(v2s32(0, 0), v2s32(0, 0)),
+ NULL, m_gamedef, IT_ROT_DRAGGED);
+ return;
+ }
+
Inventory *inv = m_invmgr->getInventory(m_selected_item->inventoryloc);
sanity_check(inv);
InventoryList *list = inv->getList(m_selected_item->listname);
@@ -2287,7 +2309,7 @@ void GUIFormSpecMenu::drawSelectedItem()
core::rect<s32> imgrect(0,0,imgsize.X,imgsize.Y);
core::rect<s32> rect = imgrect + (m_pointer - imgrect.getCenter());
- drawItemStack(driver, m_font, stack, rect, NULL, m_gamedef);
+ drawItemStack(driver, m_font, stack, rect, NULL, m_gamedef, IT_ROT_DRAGGED);
}
void GUIFormSpecMenu::drawMenu()
@@ -2369,6 +2391,12 @@ void GUIFormSpecMenu::drawMenu()
driver->draw2DRectangle(todraw, rect, 0);
}
+
+ /*
+ Call base class
+ */
+ gui::IGUIElement::draw();
+
/*
Draw images
*/
@@ -2413,18 +2441,22 @@ void GUIFormSpecMenu::drawMenu()
const ImageDrawSpec &spec = m_itemimages[i];
IItemDefManager *idef = m_gamedef->idef();
ItemStack item;
- item.deSerialize(spec.name, idef);
- video::ITexture *texture = idef->getInventoryTexture(item.getDefinition(idef).name, m_gamedef);
- // Image size on screen
+ item.deSerialize(spec.item_name, idef);
core::rect<s32> imgrect(0, 0, spec.geom.X, spec.geom.Y);
- // Image rectangle on screen
+ // Viewport rectangle on screen
core::rect<s32> rect = imgrect + spec.pos;
- const video::SColor color(255,255,255,255);
- const video::SColor colors[] = {color,color,color,color};
- draw2DImageFilterScaled(driver, texture, rect,
- core::rect<s32>(core::position2d<s32>(0,0),
- core::dimension2di(texture->getOriginalSize())),
- NULL/*&AbsoluteClippingRect*/, colors, true);
+ if (spec.parent_button && spec.parent_button->isPressed()) {
+#if (IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 8)
+ rect += core::dimension2d<s32>(
+ 0.05 * (float)rect.getWidth(), 0.05 * (float)rect.getHeight());
+#else
+ rect += core::dimension2d<s32>(
+ skin->getSize(irr::gui::EGDS_BUTTON_PRESSED_IMAGE_OFFSET_X),
+ skin->getSize(irr::gui::EGDS_BUTTON_PRESSED_IMAGE_OFFSET_Y));
+#endif
+ }
+ drawItemStack(driver, m_font, item, rect, &AbsoluteClippingRect,
+ m_gamedef, IT_ROT_NONE);
}
/*
@@ -2432,17 +2464,18 @@ void GUIFormSpecMenu::drawMenu()
Phase 0: Item slot rectangles
Phase 1: Item images; prepare tooltip
*/
- int start_phase=0;
- for(int phase=start_phase; phase<=1; phase++)
- for(u32 i=0; i<m_inventorylists.size(); i++)
- {
- drawList(m_inventorylists[i], phase);
+ bool item_hovered = false;
+ int start_phase = 0;
+ for (int phase = start_phase; phase <= 1; phase++) {
+ for (u32 i = 0; i < m_inventorylists.size(); i++) {
+ drawList(m_inventorylists[i], phase, item_hovered);
+ }
+ }
+ if (!item_hovered) {
+ drawItemStack(driver, m_font, ItemStack(),
+ core::rect<s32>(v2s32(0, 0), v2s32(0, 0)),
+ NULL, m_gamedef, IT_ROT_HOVERED);
}
-
- /*
- Call base class
- */
- gui::IGUIElement::draw();
/* TODO find way to show tooltips on touchscreen */
#ifndef HAVE_TOUCHSCREENGUI
@@ -2450,6 +2483,28 @@ void GUIFormSpecMenu::drawMenu()
#endif
/*
+ Draw static text elements
+ */
+ for (u32 i = 0; i < m_static_texts.size(); i++) {
+ const StaticTextSpec &spec = m_static_texts[i];
+ core::rect<s32> rect = spec.rect;
+ if (spec.parent_button && spec.parent_button->isPressed()) {
+#if (IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 8)
+ rect += core::dimension2d<s32>(
+ 0.05 * (float)rect.getWidth(), 0.05 * (float)rect.getHeight());
+#else
+ // Use image offset instead of text's because its a bit smaller
+ // and fits better, also TEXT_OFFSET_X is always 0
+ rect += core::dimension2d<s32>(
+ skin->getSize(irr::gui::EGDS_BUTTON_PRESSED_IMAGE_OFFSET_X),
+ skin->getSize(irr::gui::EGDS_BUTTON_PRESSED_IMAGE_OFFSET_Y));
+#endif
+ }
+ video::SColor color(255, 255, 255, 255);
+ m_font->draw(spec.text.c_str(), rect, color, true, true, &rect);
+ }
+
+ /*
Draw fields/buttons tooltips
*/
gui::IGUIElement *hovered =
@@ -2461,7 +2516,7 @@ void GUIFormSpecMenu::drawMenu()
u32 delta = 0;
if (id == -1) {
m_old_tooltip_id = id;
- m_old_tooltip = "";
+ m_old_tooltip = L"";
} else {
if (id == m_old_tooltip_id) {
delta = porting::getDeltaMs(m_hovered_time, getTimeMs());
@@ -2473,12 +2528,12 @@ void GUIFormSpecMenu::drawMenu()
if (id != -1 && delta >= m_tooltip_show_delay) {
for(std::vector<FieldSpec>::iterator iter = m_fields.begin();
- iter != m_fields.end(); iter++) {
- if ( (iter->fid == id) && (m_tooltips[iter->fname].tooltip != "") ){
+ iter != m_fields.end(); ++iter) {
+ if (iter->fid == id && m_tooltips[iter->fname].tooltip != L"") {
if (m_old_tooltip != m_tooltips[iter->fname].tooltip) {
m_old_tooltip = m_tooltips[iter->fname].tooltip;
- m_tooltip_element->setText(utf8_to_wide(m_tooltips[iter->fname].tooltip).c_str());
- std::vector<std::string> tt_rows = str_split(m_tooltips[iter->fname].tooltip, '\n');
+ m_tooltip_element->setText(m_tooltips[iter->fname].tooltip.c_str());
+ std::vector<std::wstring> tt_rows = str_split(m_tooltips[iter->fname].tooltip, L'\n');
s32 tooltip_width = m_tooltip_element->getTextWidth() + m_btn_height;
s32 tooltip_height = m_tooltip_element->getTextHeight() * tt_rows.size() + 5;
int tooltip_offset_x = m_btn_height;
@@ -2690,8 +2745,11 @@ void GUIFormSpecMenu::acceptInput(FormspecQuitMode quitmode=quit_mode_no)
}
s32 selected = e->getSelected();
if (selected >= 0) {
- fields[name] =
- wide_to_utf8(e->getItem(selected));
+ std::vector<std::string> *dropdown_values =
+ getDropDownValues(s.fname);
+ if (dropdown_values && selected < (s32)dropdown_values->size()) {
+ fields[name] = (*dropdown_values)[selected];
+ }
}
}
else if (s.ftype == f_TabHeader) {
@@ -2832,7 +2890,7 @@ bool GUIFormSpecMenu::preprocessEvent(const SEvent& event)
core::position2d<s32>(x, y));
if (event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN) {
m_old_tooltip_id = -1;
- m_old_tooltip = "";
+ m_old_tooltip = L"";
}
if (!isChild(hovered,this)) {
if (DoubleClickDetection(event)) {
@@ -3609,7 +3667,7 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
std::string GUIFormSpecMenu::getNameByID(s32 id)
{
for(std::vector<FieldSpec>::iterator iter = m_fields.begin();
- iter != m_fields.end(); iter++) {
+ iter != m_fields.end(); ++iter) {
if (iter->fid == id) {
return iter->fname;
}
@@ -3625,7 +3683,7 @@ std::string GUIFormSpecMenu::getNameByID(s32 id)
std::wstring GUIFormSpecMenu::getLabelByID(s32 id)
{
for(std::vector<FieldSpec>::iterator iter = m_fields.begin();
- iter != m_fields.end(); iter++) {
+ iter != m_fields.end(); ++iter) {
if (iter->fid == id) {
return iter->flabel;
}
diff --git a/src/guiFormSpecMenu.h b/src/guiFormSpecMenu.h
index 2ba47f7ff..ef230c81c 100644
--- a/src/guiFormSpecMenu.h
+++ b/src/guiFormSpecMenu.h
@@ -29,6 +29,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "modalMenu.h"
#include "guiTable.h"
#include "network/networkprotocol.h"
+#include "util/string.h"
class IGameDef;
class InventoryManager;
@@ -139,25 +140,53 @@ class GUIFormSpecMenu : public GUIModalMenu
struct ImageDrawSpec
{
- ImageDrawSpec()
+ ImageDrawSpec():
+ parent_button(NULL)
{
}
ImageDrawSpec(const std::string &a_name,
- v2s32 a_pos, v2s32 a_geom):
+ const std::string &a_item_name,
+ gui::IGUIButton *a_parent_button,
+ const v2s32 &a_pos, const v2s32 &a_geom):
name(a_name),
+ item_name(a_item_name),
+ parent_button(a_parent_button),
pos(a_pos),
- geom(a_geom)
+ geom(a_geom),
+ scale(true)
+ {
+ }
+ ImageDrawSpec(const std::string &a_name,
+ const std::string &a_item_name,
+ const v2s32 &a_pos, const v2s32 &a_geom):
+ name(a_name),
+ item_name(a_item_name),
+ parent_button(NULL),
+ pos(a_pos),
+ geom(a_geom),
+ scale(true)
{
- scale = true;
}
ImageDrawSpec(const std::string &a_name,
- v2s32 a_pos):
+ const v2s32 &a_pos, const v2s32 &a_geom):
name(a_name),
- pos(a_pos)
+ parent_button(NULL),
+ pos(a_pos),
+ geom(a_geom),
+ scale(true)
+ {
+ }
+ ImageDrawSpec(const std::string &a_name,
+ const v2s32 &a_pos):
+ name(a_name),
+ parent_button(NULL),
+ pos(a_pos),
+ scale(false)
{
- scale = false;
}
std::string name;
+ std::string item_name;
+ gui::IGUIButton *parent_button;
v2s32 pos;
v2s32 geom;
bool scale;
@@ -169,12 +198,12 @@ class GUIFormSpecMenu : public GUIModalMenu
{
}
FieldSpec(const std::string &name, const std::wstring &label,
- const std::wstring &fdeflt, int id) :
+ const std::wstring &default_text, int id) :
fname(name),
- flabel(label),
- fdefault(fdeflt),
fid(id)
{
+ flabel = unescape_enriched(label);
+ fdefault = unescape_enriched(default_text);
send = false;
ftype = f_Unknown;
is_exit = false;
@@ -207,16 +236,41 @@ class GUIFormSpecMenu : public GUIModalMenu
}
TooltipSpec(std::string a_tooltip, irr::video::SColor a_bgcolor,
irr::video::SColor a_color):
- tooltip(a_tooltip),
bgcolor(a_bgcolor),
color(a_color)
{
+ tooltip = unescape_enriched(utf8_to_wide(a_tooltip));
}
- std::string tooltip;
+ std::wstring tooltip;
irr::video::SColor bgcolor;
irr::video::SColor color;
};
+ struct StaticTextSpec {
+ StaticTextSpec():
+ parent_button(NULL)
+ {
+ }
+ StaticTextSpec(const std::wstring &a_text,
+ const core::rect<s32> &a_rect):
+ rect(a_rect),
+ parent_button(NULL)
+ {
+ text = unescape_enriched(a_text);
+ }
+ StaticTextSpec(const std::wstring &a_text,
+ const core::rect<s32> &a_rect,
+ gui::IGUIButton *a_parent_button):
+ rect(a_rect),
+ parent_button(a_parent_button)
+ {
+ text = unescape_enriched(a_text);
+ }
+ std::wstring text;
+ core::rect<s32> rect;
+ gui::IGUIButton *parent_button;
+ };
+
public:
GUIFormSpecMenu(irr::IrrlichtDevice* dev,
gui::IGUIElement* parent, s32 id,
@@ -282,7 +336,7 @@ public:
void regenerateGui(v2u32 screensize);
ItemSpec getItemAtPos(v2s32 p) const;
- void drawList(const ListDrawSpec &s, int phase);
+ void drawList(const ListDrawSpec &s, int phase, bool &item_hovered);
void drawSelectedItem();
void drawMenu();
void updateSelectedItem();
@@ -295,6 +349,7 @@ public:
bool pausesGame() { return doPause; }
GUITable* getTable(const std::string &tablename);
+ std::vector<std::string>* getDropDownValues(const std::string &name);
#ifdef __ANDROID__
bool getAndroidUIInput();
@@ -328,12 +383,16 @@ protected:
std::vector<ImageDrawSpec> m_itemimages;
std::vector<BoxDrawSpec> m_boxes;
std::vector<FieldSpec> m_fields;
+ std::vector<StaticTextSpec> m_static_texts;
std::vector<std::pair<FieldSpec,GUITable*> > m_tables;
std::vector<std::pair<FieldSpec,gui::IGUICheckBox*> > m_checkboxes;
std::map<std::string, TooltipSpec> m_tooltips;
std::vector<std::pair<FieldSpec,gui::IGUIScrollBar*> > m_scrollbars;
+ std::vector<std::pair<FieldSpec, std::vector<std::string> > > m_dropdowns;
ItemSpec *m_selected_item;
+ f32 m_timer1;
+ f32 m_timer2;
u32 m_selected_amount;
bool m_selected_dragging;
@@ -350,7 +409,7 @@ protected:
u32 m_tooltip_show_delay;
s32 m_hovered_time;
s32 m_old_tooltip_id;
- std::string m_old_tooltip;
+ std::wstring m_old_tooltip;
bool m_rmouse_auto_place;
diff --git a/src/guiKeyChangeMenu.cpp b/src/guiKeyChangeMenu.cpp
index 261592394..b05818256 100644
--- a/src/guiKeyChangeMenu.cpp
+++ b/src/guiKeyChangeMenu.cpp
@@ -81,7 +81,7 @@ GUIKeyChangeMenu::~GUIKeyChangeMenu()
removeChildren();
for (std::vector<key_setting*>::iterator iter = key_settings.begin();
- iter != key_settings.end(); iter ++) {
+ iter != key_settings.end(); ++iter) {
delete[] (*iter)->button_name;
delete (*iter);
}
@@ -187,7 +187,7 @@ void GUIKeyChangeMenu::regenerateGui(v2u32 screensize)
{
core::rect < s32 > rect(0, 0, 100, 30);
- rect += topleft + v2s32(size.X - 100 - 20, size.Y - 40);
+ rect += topleft + v2s32(size.X / 2 - 105, size.Y - 40);
const wchar_t *text = wgettext("Save");
Environment->addButton(rect, this, GUI_ID_BACK_BUTTON,
text);
@@ -195,7 +195,7 @@ void GUIKeyChangeMenu::regenerateGui(v2u32 screensize)
}
{
core::rect < s32 > rect(0, 0, 100, 30);
- rect += topleft + v2s32(size.X - 100 - 20 - 100 - 20, size.Y - 40);
+ rect += topleft + v2s32(size.X / 2 + 5, size.Y - 40);
const wchar_t *text = wgettext("Cancel");
Environment->addButton(rect, this, GUI_ID_ABORT_BUTTON,
text);
diff --git a/src/guiTable.cpp b/src/guiTable.cpp
index ed5b0d87b..3cc95ce4f 100644
--- a/src/guiTable.cpp
+++ b/src/guiTable.cpp
@@ -556,24 +556,46 @@ s32 GUITable::getSelected() const
void GUITable::setSelected(s32 index)
{
+ s32 old_selected = m_selected;
+
m_selected = -1;
m_sel_column = 0;
m_sel_doubleclick = false;
- --index;
+ --index; // Switch from 1-based indexing to 0-based indexing
s32 rowcount = m_rows.size();
-
- if (index >= rowcount)
+ if (rowcount == 0) {
+ return;
+ } else if (index < 0) {
+ index = 0;
+ } else if (index >= rowcount) {
index = rowcount - 1;
- while (index >= 0 && m_rows[index].visible_index < 0)
- --index;
+ }
+
+ // If the selected row is not visible, open its ancestors to make it visible
+ bool selection_invisible = m_rows[index].visible_index < 0;
+ if (selection_invisible) {
+ std::set<s32> opened_trees;
+ getOpenedTrees(opened_trees);
+ s32 indent = m_rows[index].indent;
+ for (s32 j = index - 1; j >= 0; --j) {
+ if (m_rows[j].indent < indent) {
+ opened_trees.insert(j);
+ indent = m_rows[j].indent;
+ }
+ }
+ setOpenedTrees(opened_trees);
+ }
+
if (index >= 0) {
m_selected = m_rows[index].visible_index;
assert(m_selected >= 0 && m_selected < (s32) m_visible_rows.size());
}
- autoScroll();
+ if (m_selected != old_selected || selection_invisible) {
+ autoScroll();
+ }
}
GUITable::DynamicData GUITable::getDynamicData() const
@@ -596,11 +618,11 @@ void GUITable::setDynamicData(const DynamicData &dyndata)
m_keynav_time = dyndata.keynav_time;
m_keynav_buffer = dyndata.keynav_buffer;
- m_scrollbar->setPos(dyndata.scrollpos);
-
setSelected(dyndata.selected);
m_sel_column = 0;
m_sel_doubleclick = false;
+
+ m_scrollbar->setPos(dyndata.scrollpos);
}
const c8* GUITable::getTypeName() const
@@ -906,6 +928,11 @@ bool GUITable::OnEvent(const SEvent &event)
sel_doubleclick) {
sendTableEvent(sel_column, sel_doubleclick);
}
+
+ // Treeview: double click opens/closes trees
+ if (m_has_tree_column && sel_doubleclick) {
+ toggleVisibleTree(m_selected, 0, false);
+ }
}
}
return true;
@@ -1091,7 +1118,9 @@ void GUITable::getOpenedTrees(std::set<s32> &opened_trees) const
void GUITable::setOpenedTrees(const std::set<s32> &opened_trees)
{
- s32 old_selected = getSelected();
+ s32 old_selected = -1;
+ if (m_selected >= 0)
+ old_selected = m_visible_rows[m_selected];
std::vector<s32> parents;
std::vector<s32> closed_parents;
@@ -1143,7 +1172,9 @@ void GUITable::setOpenedTrees(const std::set<s32> &opened_trees)
updateScrollBar();
- setSelected(old_selected);
+ // m_selected must be updated since it is a visible row index
+ if (old_selected >= 0)
+ m_selected = m_rows[old_selected].visible_index;
}
void GUITable::openTree(s32 to_open)
diff --git a/src/guiscalingfilter.cpp b/src/guiscalingfilter.cpp
index 26a2265a8..41cc72836 100644
--- a/src/guiscalingfilter.cpp
+++ b/src/guiscalingfilter.cpp
@@ -51,13 +51,13 @@ void guiScalingCache(io::path key, video::IVideoDriver *driver, video::IImage *v
void guiScalingCacheClear(video::IVideoDriver *driver)
{
for (std::map<io::path, video::IImage *>::iterator it = g_imgCache.begin();
- it != g_imgCache.end(); it++) {
+ it != g_imgCache.end(); ++it) {
if (it->second != NULL)
it->second->drop();
}
g_imgCache.clear();
for (std::map<io::path, video::ITexture *>::iterator it = g_txrCache.begin();
- it != g_txrCache.end(); it++) {
+ it != g_txrCache.end(); ++it) {
if (it->second != NULL)
driver->removeTexture(it->second);
}
diff --git a/src/httpfetch.cpp b/src/httpfetch.cpp
index 56cdad2b1..f64c9f717 100644
--- a/src/httpfetch.cpp
+++ b/src/httpfetch.cpp
@@ -18,14 +18,14 @@ with this program; if not, write to the Free Software Foundation, Inc.,
*/
#include "socket.h" // for select()
-#include "porting.h" // for sleep_ms(), get_sysinfo()
+#include "porting.h" // for sleep_ms(), get_sysinfo(), secure_rand_fill_buf()
#include "httpfetch.h"
#include <iostream>
#include <sstream>
#include <list>
#include <map>
#include <errno.h>
-#include "jthread/jevent.h"
+#include "threading/event.h"
#include "config.h"
#include "exceptions.h"
#include "debug.h"
@@ -34,9 +34,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "util/thread.h"
#include "version.h"
#include "settings.h"
+#include "noise.h"
-JMutex g_httpfetch_mutex;
+Mutex g_httpfetch_mutex;
std::map<unsigned long, std::queue<HTTPFetchResult> > g_httpfetch_results;
+PcgRandom g_callerid_randomness;
HTTPFetchRequest::HTTPFetchRequest()
{
@@ -55,7 +57,7 @@ static void httpfetch_deliver_result(const HTTPFetchResult &fetch_result)
{
unsigned long caller = fetch_result.caller;
if (caller != HTTPFETCH_DISCARD) {
- JMutexAutoLock lock(g_httpfetch_mutex);
+ MutexAutoLock lock(g_httpfetch_mutex);
g_httpfetch_results[caller].push(fetch_result);
}
}
@@ -64,7 +66,7 @@ static void httpfetch_request_clear(unsigned long caller);
unsigned long httpfetch_caller_alloc()
{
- JMutexAutoLock lock(g_httpfetch_mutex);
+ MutexAutoLock lock(g_httpfetch_mutex);
// Check each caller ID except HTTPFETCH_DISCARD
const unsigned long discard = HTTPFETCH_DISCARD;
@@ -84,6 +86,34 @@ unsigned long httpfetch_caller_alloc()
return discard;
}
+unsigned long httpfetch_caller_alloc_secure()
+{
+ MutexAutoLock lock(g_httpfetch_mutex);
+
+ // Generate random caller IDs and make sure they're not
+ // already used or equal to HTTPFETCH_DISCARD
+ // Give up after 100 tries to prevent infinite loop
+ u8 tries = 100;
+ unsigned long caller;
+
+ do {
+ caller = (((u64) g_callerid_randomness.next()) << 32) |
+ g_callerid_randomness.next();
+
+ if (--tries < 1) {
+ FATAL_ERROR("httpfetch_caller_alloc_secure: ran out of caller IDs");
+ return HTTPFETCH_DISCARD;
+ }
+ } while (g_httpfetch_results.find(caller) != g_httpfetch_results.end());
+
+ verbosestream << "httpfetch_caller_alloc_secure: allocating "
+ << caller << std::endl;
+
+ // Access element to create it
+ g_httpfetch_results[caller];
+ return caller;
+}
+
void httpfetch_caller_free(unsigned long caller)
{
verbosestream<<"httpfetch_caller_free: freeing "
@@ -91,14 +121,14 @@ void httpfetch_caller_free(unsigned long caller)
httpfetch_request_clear(caller);
if (caller != HTTPFETCH_DISCARD) {
- JMutexAutoLock lock(g_httpfetch_mutex);
+ MutexAutoLock lock(g_httpfetch_mutex);
g_httpfetch_results.erase(caller);
}
}
bool httpfetch_async_get(unsigned long caller, HTTPFetchResult &fetch_result)
{
- JMutexAutoLock lock(g_httpfetch_mutex);
+ MutexAutoLock lock(g_httpfetch_mutex);
// Check that caller exists
std::map<unsigned long, std::queue<HTTPFetchResult> >::iterator
@@ -262,7 +292,7 @@ HTTPFetchOngoing::HTTPFetchOngoing(HTTPFetchRequest request_, CurlHandlePool *po
}
// Set POST (or GET) data
- if (request.post_fields.empty()) {
+ if (request.post_fields.empty() && request.post_data.empty()) {
curl_easy_setopt(curl, CURLOPT_HTTPGET, 1);
} else if (request.multipart) {
curl_httppost *last = NULL;
@@ -390,7 +420,7 @@ HTTPFetchOngoing::~HTTPFetchOngoing()
}
-class CurlFetchThread : public JThread
+class CurlFetchThread : public Thread
{
protected:
enum RequestType {
@@ -414,7 +444,8 @@ protected:
std::list<HTTPFetchRequest> m_queued_fetches;
public:
- CurlFetchThread(int parallel_limit)
+ CurlFetchThread(int parallel_limit) :
+ Thread("CurlFetch")
{
if (parallel_limit >= 1)
m_parallel_limit = parallel_limit;
@@ -613,13 +644,9 @@ protected:
}
}
- void * Thread()
+ void *run()
{
- ThreadStarted();
- log_register_thread("CurlFetchThread");
- DSTACK(__FUNCTION_NAME);
-
- porting::setThreadName("CurlFetchThread");
+ DSTACK(FUNCTION_NAME);
CurlHandlePool pool;
@@ -631,7 +658,7 @@ protected:
FATAL_ERROR_IF(!m_all_ongoing.empty(), "Expected empty");
- while (!StopRequested()) {
+ while (!stopRequested()) {
BEGIN_DEBUG_EXCEPTION_HANDLER
/*
@@ -680,7 +707,7 @@ protected:
else
waitForIO(100);
- END_DEBUG_EXCEPTION_HANDLER(errorstream)
+ END_DEBUG_EXCEPTION_HANDLER
}
// Call curl_multi_remove_handle and cleanup easy handles
@@ -713,15 +740,20 @@ void httpfetch_init(int parallel_limit)
FATAL_ERROR_IF(res != CURLE_OK, "CURL init failed");
g_httpfetch_thread = new CurlFetchThread(parallel_limit);
+
+ // Initialize g_callerid_randomness for httpfetch_caller_alloc_secure
+ u64 randbuf[2];
+ porting::secure_rand_fill_buf(randbuf, sizeof(u64) * 2);
+ g_callerid_randomness = PcgRandom(randbuf[0], randbuf[1]);
}
void httpfetch_cleanup()
{
verbosestream<<"httpfetch_cleanup: cleaning up"<<std::endl;
- g_httpfetch_thread->Stop();
+ g_httpfetch_thread->stop();
g_httpfetch_thread->requestWakeUp();
- g_httpfetch_thread->Wait();
+ g_httpfetch_thread->wait();
delete g_httpfetch_thread;
curl_global_cleanup();
@@ -730,18 +762,17 @@ void httpfetch_cleanup()
void httpfetch_async(const HTTPFetchRequest &fetch_request)
{
g_httpfetch_thread->requestFetch(fetch_request);
- if (!g_httpfetch_thread->IsRunning())
- g_httpfetch_thread->Start();
+ if (!g_httpfetch_thread->isRunning())
+ g_httpfetch_thread->start();
}
static void httpfetch_request_clear(unsigned long caller)
{
- if (g_httpfetch_thread->IsRunning()) {
+ if (g_httpfetch_thread->isRunning()) {
Event event;
g_httpfetch_thread->requestClear(caller, &event);
event.wait();
- }
- else {
+ } else {
g_httpfetch_thread->requestClear(caller, NULL);
}
}
diff --git a/src/httpfetch.h b/src/httpfetch.h
index c44c8d2d3..f57ed8789 100644
--- a/src/httpfetch.h
+++ b/src/httpfetch.h
@@ -116,6 +116,9 @@ bool httpfetch_async_get(unsigned long caller, HTTPFetchResult &fetch_result);
// Not required if you want to set caller = HTTPFETCH_DISCARD
unsigned long httpfetch_caller_alloc();
+// Allocates a non-predictable caller ID for httpfetch_async
+unsigned long httpfetch_caller_alloc_secure();
+
// Frees a caller ID allocated with httpfetch_caller_alloc
// Note: This can be expensive, because the httpfetch thread is told
// to stop any ongoing fetches for the given caller.
diff --git a/src/hud.cpp b/src/hud.cpp
index dbc4a01a3..19feaef7b 100644
--- a/src/hud.cpp
+++ b/src/hud.cpp
@@ -22,6 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "hud.h"
#include "settings.h"
#include "util/numeric.h"
+#include "util/string.h"
#include "log.h"
#include "gamedef.h"
#include "itemdef.h"
@@ -32,6 +33,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "porting.h"
#include "fontengine.h"
#include "guiscalingfilter.h"
+#include "mesh.h"
#include <IGUIStaticText.h>
#ifdef HAVE_TOUCHSCREENGUI
@@ -40,7 +42,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
Hud::Hud(video::IVideoDriver *driver, scene::ISceneManager* smgr,
gui::IGUIEnvironment* guienv, IGameDef *gamedef, LocalPlayer *player,
- Inventory *inventory) {
+ Inventory *inventory)
+{
this->driver = driver;
this->smgr = smgr;
this->guienv = guienv;
@@ -48,16 +51,15 @@ Hud::Hud(video::IVideoDriver *driver, scene::ISceneManager* smgr,
this->player = player;
this->inventory = inventory;
+ m_hud_scaling = g_settings->getFloat("hud_scaling");
m_screensize = v2u32(0, 0);
m_displaycenter = v2s32(0, 0);
m_hotbar_imagesize = floor(HOTBAR_IMAGE_SIZE * porting::getDisplayDensity() + 0.5);
- m_hotbar_imagesize *= g_settings->getFloat("hud_scaling");
+ m_hotbar_imagesize *= m_hud_scaling;
m_padding = m_hotbar_imagesize / 12;
- const video::SColor hbar_color(255, 255, 255, 255);
- for (unsigned int i=0; i < 4; i++ ){
- hbar_colors[i] = hbar_color;
- }
+ for (unsigned int i = 0; i < 4; i++)
+ hbar_colors[i] = video::SColor(255, 255, 255, 255);
tsrc = gamedef->getTextureSource();
@@ -80,10 +82,46 @@ Hud::Hud(video::IVideoDriver *driver, scene::ISceneManager* smgr,
use_hotbar_image = false;
hotbar_selected_image = "";
use_hotbar_selected_image = false;
+
+ m_selection_mesh = NULL;
+ m_selection_boxes.clear();
+ m_halo_boxes.clear();
+
+ m_selection_pos = v3f(0.0, 0.0, 0.0);
+ std::string mode = g_settings->get("node_highlighting");
+ m_selection_material.Lighting = false;
+
+ if (g_settings->getBool("enable_shaders")) {
+ IShaderSource *shdrsrc = gamedef->getShaderSource();
+ u16 shader_id = shdrsrc->getShader(
+ mode == "halo" ? "selection_shader" : "default_shader", 1, 1);
+ m_selection_material.MaterialType = shdrsrc->getShaderInfo(shader_id).material;
+ } else {
+ m_selection_material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
+ }
+
+ if (mode == "box") {
+ m_use_selection_mesh = false;
+ m_selection_material.Thickness =
+ rangelim(g_settings->getS16("selectionbox_width"), 1, 5);
+ } else if (mode == "halo") {
+ m_use_selection_mesh = true;
+ m_selection_material.setTexture(0, tsrc->getTextureForMesh("halo.png"));
+ m_selection_material.setFlag(video::EMF_BACK_FACE_CULLING, true);
+ } else {
+ m_selection_material.MaterialType = video::EMT_SOLID;
+ }
}
-void Hud::drawItem(const ItemStack &item, const core::rect<s32>& rect, bool selected) {
+Hud::~Hud()
+{
+ if (m_selection_mesh)
+ m_selection_mesh->drop();
+}
+void Hud::drawItem(const ItemStack &item, const core::rect<s32>& rect,
+ bool selected)
+{
if (selected) {
/* draw hihlighting around selected item */
if (use_hotbar_selected_image) {
@@ -154,29 +192,35 @@ void Hud::drawItem(const ItemStack &item, const core::rect<s32>& rect, bool sele
video::SColor bgcolor2(128, 0, 0, 0);
if (!use_hotbar_image)
driver->draw2DRectangle(bgcolor2, rect, NULL);
- drawItemStack(driver, g_fontengine->getFont(), item, rect, NULL, gamedef);
+ drawItemStack(driver, g_fontengine->getFont(), item, rect, NULL,
+ gamedef, selected ? IT_ROT_SELECTED : IT_ROT_NONE);
}
//NOTE: selectitem = 0 -> no selected; selectitem 1-based
-void Hud::drawItems(v2s32 upperleftpos, s32 itemcount, s32 offset,
- InventoryList *mainlist, u16 selectitem, u16 direction)
+void Hud::drawItems(v2s32 upperleftpos, v2s32 screen_offset, s32 itemcount,
+ s32 inv_offset, InventoryList *mainlist, u16 selectitem, u16 direction)
{
#ifdef HAVE_TOUCHSCREENGUI
- if ( (g_touchscreengui) && (offset == 0))
+ if (g_touchscreengui && inv_offset == 0)
g_touchscreengui->resetHud();
#endif
s32 height = m_hotbar_imagesize + m_padding * 2;
- s32 width = (itemcount - offset) * (m_hotbar_imagesize + m_padding * 2);
+ s32 width = (itemcount - inv_offset) * (m_hotbar_imagesize + m_padding * 2);
if (direction == HUD_DIR_TOP_BOTTOM || direction == HUD_DIR_BOTTOM_TOP) {
- width = m_hotbar_imagesize + m_padding * 2;
- height = (itemcount - offset) * (m_hotbar_imagesize + m_padding * 2);
+ s32 tmp = height;
+ height = width;
+ width = tmp;
}
// Position of upper left corner of bar
- v2s32 pos = upperleftpos;
+ v2s32 pos = screen_offset;
+ pos.X *= m_hud_scaling * porting::getDisplayDensity();
+ pos.Y *= m_hud_scaling * porting::getDisplayDensity();
+ pos += upperleftpos;
+ // Store hotbar_image in member variable, used by drawItem()
if (hotbar_image != player->hotbar_image) {
hotbar_image = player->hotbar_image;
if (hotbar_image != "")
@@ -185,6 +229,7 @@ void Hud::drawItems(v2s32 upperleftpos, s32 itemcount, s32 offset,
use_hotbar_image = false;
}
+ // Store hotbar_selected_image in member variable, used by drawItem()
if (hotbar_selected_image != player->hotbar_selected_image) {
hotbar_selected_image = player->hotbar_selected_image;
if (hotbar_selected_image != "")
@@ -193,10 +238,10 @@ void Hud::drawItems(v2s32 upperleftpos, s32 itemcount, s32 offset,
use_hotbar_selected_image = false;
}
- /* draw customized item background */
+ // draw customized item background
if (use_hotbar_image) {
core::rect<s32> imgrect2(-m_padding/2, -m_padding/2,
- width+m_padding/2, height+m_padding/2);
+ width+m_padding/2, height+m_padding/2);
core::rect<s32> rect2 = imgrect2 + pos;
video::ITexture *texture = tsrc->getTexture(hotbar_image);
core::dimension2di imgsize(texture->getOriginalSize());
@@ -205,29 +250,28 @@ void Hud::drawItems(v2s32 upperleftpos, s32 itemcount, s32 offset,
NULL, hbar_colors, true);
}
- for (s32 i = offset; i < itemcount && (size_t)i < mainlist->getSize(); i++)
- {
- v2s32 steppos;
+ // Draw items
+ core::rect<s32> imgrect(0, 0, m_hotbar_imagesize, m_hotbar_imagesize);
+ for (s32 i = inv_offset; i < itemcount && (size_t)i < mainlist->getSize(); i++) {
s32 fullimglen = m_hotbar_imagesize + m_padding * 2;
- core::rect<s32> imgrect(0, 0, m_hotbar_imagesize, m_hotbar_imagesize);
-
+ v2s32 steppos;
switch (direction) {
- case HUD_DIR_RIGHT_LEFT:
- steppos = v2s32(-(m_padding + (i - offset) * fullimglen), m_padding);
- break;
- case HUD_DIR_TOP_BOTTOM:
- steppos = v2s32(m_padding, m_padding + (i - offset) * fullimglen);
- break;
- case HUD_DIR_BOTTOM_TOP:
- steppos = v2s32(m_padding, -(m_padding + (i - offset) * fullimglen));
- break;
- default:
- steppos = v2s32(m_padding + (i - offset) * fullimglen, m_padding);
- break;
+ case HUD_DIR_RIGHT_LEFT:
+ steppos = v2s32(-(m_padding + (i - inv_offset) * fullimglen), m_padding);
+ break;
+ case HUD_DIR_TOP_BOTTOM:
+ steppos = v2s32(m_padding, m_padding + (i - inv_offset) * fullimglen);
+ break;
+ case HUD_DIR_BOTTOM_TOP:
+ steppos = v2s32(m_padding, -(m_padding + (i - inv_offset) * fullimglen));
+ break;
+ default:
+ steppos = v2s32(m_padding + (i - inv_offset) * fullimglen, m_padding);
+ break;
}
- drawItem(mainlist->getItem(i), (imgrect + pos + steppos), (i +1) == selectitem );
+ drawItem(mainlist->getItem(i), (imgrect + pos + steppos), (i + 1) == selectitem);
#ifdef HAVE_TOUCHSCREENGUI
if (g_touchscreengui)
@@ -237,7 +281,8 @@ void Hud::drawItems(v2s32 upperleftpos, s32 itemcount, s32 offset,
}
-void Hud::drawLuaElements(v3s16 camera_offset) {
+void Hud::drawLuaElements(const v3s16 &camera_offset)
+{
u32 text_height = g_fontengine->getTextHeight();
irr::gui::IGUIFont* font = g_fontengine->getFont();
for (size_t i = 0; i != player->maxHudId(); i++) {
@@ -275,7 +320,7 @@ void Hud::drawLuaElements(v3s16 camera_offset) {
(e->number >> 8) & 0xFF,
(e->number >> 0) & 0xFF);
core::rect<s32> size(0, 0, e->scale.X, text_height * e->scale.Y);
- std::wstring text = utf8_to_wide(e->text);
+ std::wstring text = unescape_enriched(utf8_to_wide(e->text));
core::dimension2d<u32> textsize = font->getDimension(text.c_str());
v2s32 offset((e->align.X - 1.0) * (textsize.Width / 2),
(e->align.Y - 1.0) * (textsize.Height / 2));
@@ -288,7 +333,8 @@ void Hud::drawLuaElements(v3s16 camera_offset) {
break; }
case HUD_ELEM_INVENTORY: {
InventoryList *inv = inventory->getList(e->text);
- drawItems(pos, e->number, 0, inv, e->item, e->dir);
+ drawItems(pos, v2s32(e->offset.X, e->offset.Y), e->number, 0,
+ inv, e->item, e->dir);
break; }
case HUD_ELEM_WAYPOINT: {
v3f p_pos = player->getPosition() / BS;
@@ -310,11 +356,11 @@ void Hud::drawLuaElements(v3s16 camera_offset) {
(e->number >> 8) & 0xFF,
(e->number >> 0) & 0xFF);
core::rect<s32> size(0, 0, 200, 2 * text_height);
- std::wstring text = utf8_to_wide(e->name);
+ std::wstring text = unescape_enriched(utf8_to_wide(e->name));
font->draw(text.c_str(), size + pos, color);
std::ostringstream os;
os << distance << e->text;
- text = utf8_to_wide(os.str());
+ text = unescape_enriched(utf8_to_wide(os.str()));
pos.Y += text_height;
font->draw(text.c_str(), size + pos, color);
break; }
@@ -341,8 +387,7 @@ void Hud::drawStatbar(v2s32 pos, u16 corner, u16 drawdir, std::string texture,
if (size == v2s32()) {
dstd = srcd;
} else {
- double size_factor = g_settings->getFloat("hud_scaling") *
- porting::getDisplayDensity();
+ float size_factor = m_hud_scaling * porting::getDisplayDensity();
dstd.Height = size.Y * size_factor;
dstd.Width = size.X * size_factor;
offset.X *= size_factor;
@@ -410,18 +455,19 @@ void Hud::drawHotbar(u16 playeritem) {
if ( (float) width / (float) porting::getWindowSize().X <=
g_settings->getFloat("hud_hotbar_max_width")) {
if (player->hud_flags & HUD_FLAG_HOTBAR_VISIBLE) {
- drawItems(pos, hotbar_itemcount, 0, mainlist, playeritem + 1, 0);
+ drawItems(pos, v2s32(0, 0), hotbar_itemcount, 0, mainlist, playeritem + 1, 0);
}
- }
- else {
+ } else {
pos.X += width/4;
v2s32 secondpos = pos;
pos = pos - v2s32(0, m_hotbar_imagesize + m_padding);
if (player->hud_flags & HUD_FLAG_HOTBAR_VISIBLE) {
- drawItems(pos, hotbar_itemcount/2, 0, mainlist, playeritem + 1, 0);
- drawItems(secondpos, hotbar_itemcount, hotbar_itemcount/2, mainlist, playeritem + 1, 0);
+ drawItems(pos, v2s32(0, 0), hotbar_itemcount / 2, 0,
+ mainlist, playeritem + 1, 0);
+ drawItems(secondpos, v2s32(0, 0), hotbar_itemcount,
+ hotbar_itemcount / 2, mainlist, playeritem + 1, 0);
}
}
@@ -446,8 +492,8 @@ void Hud::drawHotbar(u16 playeritem) {
}
-void Hud::drawCrosshair() {
-
+void Hud::drawCrosshair()
+{
if (use_crosshair_image) {
video::ITexture *crosshair = tsrc->getTexture("crosshair.png");
v2u32 size = crosshair->getOriginalSize();
@@ -464,48 +510,179 @@ void Hud::drawCrosshair() {
}
}
+void Hud::setSelectionPos(const v3f &pos, const v3s16 &camera_offset)
+{
+ m_camera_offset = camera_offset;
+ m_selection_pos = pos;
+ m_selection_pos_with_offset = pos - intToFloat(camera_offset, BS);
+}
-void Hud::drawSelectionBoxes(std::vector<aabb3f> &hilightboxes) {
- for (std::vector<aabb3f>::const_iterator
- i = hilightboxes.begin();
- i != hilightboxes.end(); i++) {
- driver->draw3DBox(*i, selectionbox_argb);
+void Hud::drawSelectionMesh()
+{
+ if (!m_use_selection_mesh) {
+ // Draw 3D selection boxes
+ video::SMaterial oldmaterial = driver->getMaterial2D();
+ driver->setMaterial(m_selection_material);
+ for (std::vector<aabb3f>::const_iterator
+ i = m_selection_boxes.begin();
+ i != m_selection_boxes.end(); ++i) {
+ aabb3f box = aabb3f(
+ i->MinEdge + m_selection_pos_with_offset,
+ i->MaxEdge + m_selection_pos_with_offset);
+
+ u32 r = (selectionbox_argb.getRed() *
+ m_selection_mesh_color.getRed() / 255);
+ u32 g = (selectionbox_argb.getGreen() *
+ m_selection_mesh_color.getGreen() / 255);
+ u32 b = (selectionbox_argb.getBlue() *
+ m_selection_mesh_color.getBlue() / 255);
+ driver->draw3DBox(box, video::SColor(255, r, g, b));
+ }
+ driver->setMaterial(oldmaterial);
+ } else if (m_selection_mesh) {
+ // Draw selection mesh
+ video::SMaterial oldmaterial = driver->getMaterial2D();
+ driver->setMaterial(m_selection_material);
+ setMeshColor(m_selection_mesh, m_selection_mesh_color);
+ scene::IMesh* mesh = cloneMesh(m_selection_mesh);
+ translateMesh(mesh, m_selection_pos_with_offset);
+ u32 mc = m_selection_mesh->getMeshBufferCount();
+ for (u32 i = 0; i < mc; i++) {
+ scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
+ driver->drawMeshBuffer(buf);
+ }
+ mesh->drop();
+ driver->setMaterial(oldmaterial);
}
}
+void Hud::updateSelectionMesh(const v3s16 &camera_offset)
+{
+ m_camera_offset = camera_offset;
+ if (!m_use_selection_mesh)
+ return;
+
+ if (m_selection_mesh) {
+ m_selection_mesh->drop();
+ m_selection_mesh = NULL;
+ }
+
+ if (!m_selection_boxes.size()) {
+ // No pointed object
+ return;
+ }
+
+ // New pointed object, create new mesh.
+
+ // Texture UV coordinates for selection boxes
+ static f32 texture_uv[24] = {
+ 0,0,1,1,
+ 0,0,1,1,
+ 0,0,1,1,
+ 0,0,1,1,
+ 0,0,1,1,
+ 0,0,1,1
+ };
+
+ // Use single halo box instead of multiple overlapping boxes.
+ // Temporary solution - problem can be solved with multiple
+ // rendering targets, or some method to remove inner surfaces.
+ // Thats because of halo transparency.
+
+ aabb3f halo_box(100.0, 100.0, 100.0, -100.0, -100.0, -100.0);
+ m_halo_boxes.clear();
+
+ for (std::vector<aabb3f>::iterator
+ i = m_selection_boxes.begin();
+ i != m_selection_boxes.end(); ++i) {
+ halo_box.addInternalBox(*i);
+ }
+
+ m_halo_boxes.push_back(halo_box);
+ m_selection_mesh = convertNodeboxesToMesh(
+ m_halo_boxes, texture_uv, 0.5);
+}
void Hud::resizeHotbar() {
if (m_screensize != porting::getWindowSize()) {
m_hotbar_imagesize = floor(HOTBAR_IMAGE_SIZE * porting::getDisplayDensity() + 0.5);
- m_hotbar_imagesize *= g_settings->getFloat("hud_scaling");
+ m_hotbar_imagesize *= m_hud_scaling;
m_padding = m_hotbar_imagesize / 12;
m_screensize = porting::getWindowSize();
m_displaycenter = v2s32(m_screensize.X/2,m_screensize.Y/2);
}
}
+struct MeshTimeInfo {
+ s32 time;
+ scene::IMesh *mesh;
+};
+
void drawItemStack(video::IVideoDriver *driver,
gui::IGUIFont *font,
const ItemStack &item,
const core::rect<s32> &rect,
const core::rect<s32> *clip,
- IGameDef *gamedef)
+ IGameDef *gamedef,
+ ItemRotationKind rotation_kind)
{
- if(item.empty())
+ static MeshTimeInfo rotation_time_infos[IT_ROT_NONE];
+ static bool enable_animations =
+ g_settings->getBool("inventory_items_animations");
+
+ if (item.empty()) {
+ if (rotation_kind < IT_ROT_NONE) {
+ rotation_time_infos[rotation_kind].mesh = NULL;
+ }
return;
+ }
const ItemDefinition &def = item.getDefinition(gamedef->idef());
- video::ITexture *texture = gamedef->idef()->getInventoryTexture(def.name, gamedef);
+ scene::IMesh* mesh = gamedef->idef()->getWieldMesh(def.name, gamedef);
+
+ if (mesh) {
+ driver->clearZBuffer();
+ s32 delta = 0;
+ if (rotation_kind < IT_ROT_NONE) {
+ MeshTimeInfo &ti = rotation_time_infos[rotation_kind];
+ if (mesh != ti.mesh) {
+ ti.mesh = mesh;
+ ti.time = getTimeMs();
+ } else {
+ delta = porting::getDeltaMs(ti.time, getTimeMs()) % 100000;
+ }
+ }
+ core::rect<s32> oldViewPort = driver->getViewPort();
+ core::matrix4 oldProjMat = driver->getTransform(video::ETS_PROJECTION);
+ core::matrix4 oldViewMat = driver->getTransform(video::ETS_VIEW);
+ core::matrix4 ProjMatrix;
+ ProjMatrix.buildProjectionMatrixOrthoLH(2, 2, -1, 100);
+ driver->setTransform(video::ETS_PROJECTION, ProjMatrix);
+ driver->setTransform(video::ETS_VIEW, ProjMatrix);
+ core::matrix4 matrix;
+ matrix.makeIdentity();
+
+ if (enable_animations) {
+ float timer_f = (float)delta / 5000.0;
+ matrix.setRotationDegrees(core::vector3df(0, 360 * timer_f, 0));
+ }
- // Draw the inventory texture
- if(texture != NULL)
- {
- const video::SColor color(255,255,255,255);
- const video::SColor colors[] = {color,color,color,color};
- draw2DImageFilterScaled(driver, texture, rect,
- core::rect<s32>(core::position2d<s32>(0,0),
- core::dimension2di(texture->getOriginalSize())),
- clip, colors, true);
+ driver->setTransform(video::ETS_WORLD, matrix);
+ driver->setViewPort(rect);
+
+ u32 mc = mesh->getMeshBufferCount();
+ for (u32 j = 0; j < mc; ++j) {
+ scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
+ video::SMaterial &material = buf->getMaterial();
+ material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
+ material.Lighting = false;
+ driver->setMaterial(material);
+ driver->drawMeshBuffer(buf);
+ }
+
+ driver->setTransform(video::ETS_VIEW, oldViewMat);
+ driver->setTransform(video::ETS_PROJECTION, oldProjMat);
+ driver->setViewPort(oldViewPort);
}
if(def.type == ITEM_TOOL && item.wear != 0)
diff --git a/src/hud.h b/src/hud.h
index 614e7c92d..7f0fbe7b3 100644
--- a/src/hud.h
+++ b/src/hud.h
@@ -33,8 +33,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#define HUD_CORNER_CENTER 2
// Note that these visibility flags do not determine if the hud items are
-// actually drawn, but rather, allows the item to be drawn should the rest of
-// the game state permit it.
+// actually drawn, but rather, whether to draw the item should the rest
+// of the game state permit it.
#define HUD_FLAG_HOTBAR_VISIBLE (1 << 0)
#define HUD_FLAG_HEALTHBAR_VISIBLE (1 << 1)
#define HUD_FLAG_CROSSHAIR_VISIBLE (1 << 2)
@@ -119,31 +119,65 @@ public:
bool use_hotbar_image;
std::string hotbar_selected_image;
bool use_hotbar_selected_image;
- v3s16 camera_offset;
Hud(video::IVideoDriver *driver,scene::ISceneManager* smgr,
gui::IGUIEnvironment* guienv, IGameDef *gamedef, LocalPlayer *player,
Inventory *inventory);
+ ~Hud();
void drawHotbar(u16 playeritem);
void resizeHotbar();
void drawCrosshair();
- void drawSelectionBoxes(std::vector<aabb3f> &hilightboxes);
- void drawLuaElements(v3s16 camera_offset);
+ void drawSelectionMesh();
+ void updateSelectionMesh(const v3s16 &camera_offset);
+
+ std::vector<aabb3f> *getSelectionBoxes()
+ { return &m_selection_boxes; }
+
+ void setSelectionPos(const v3f &pos, const v3s16 &camera_offset);
+
+ v3f getSelectionPos() const
+ { return m_selection_pos; }
+
+ void setSelectionMeshColor(const video::SColor &c)
+ { m_selection_mesh_color = c; }
+
+ void drawLuaElements(const v3s16 &camera_offset);
+
private:
void drawStatbar(v2s32 pos, u16 corner, u16 drawdir, std::string texture,
s32 count, v2s32 offset, v2s32 size=v2s32());
- void drawItems(v2s32 upperleftpos, s32 itemcount, s32 offset,
- InventoryList *mainlist, u16 selectitem, u16 direction);
+ void drawItems(v2s32 upperleftpos, v2s32 screen_offset, s32 itemcount,
+ s32 inv_offset, InventoryList *mainlist, u16 selectitem, u16 direction);
- void drawItem(const ItemStack &item, const core::rect<s32>& rect, bool selected);
+ void drawItem(const ItemStack &item, const core::rect<s32>& rect,
+ bool selected);
+ float m_hud_scaling; // cached minetest setting
+ v3s16 m_camera_offset;
v2u32 m_screensize;
v2s32 m_displaycenter;
- s32 m_hotbar_imagesize;
- s32 m_padding;
+ s32 m_hotbar_imagesize; // Takes hud_scaling into account, updated by resizeHotbar()
+ s32 m_padding; // Takes hud_scaling into account, updated by resizeHotbar()
video::SColor hbar_colors[4];
+
+ std::vector<aabb3f> m_selection_boxes;
+ std::vector<aabb3f> m_halo_boxes;
+ v3f m_selection_pos;
+ v3f m_selection_pos_with_offset;
+
+ scene::IMesh* m_selection_mesh;
+ video::SColor m_selection_mesh_color;
+ video::SMaterial m_selection_material;
+ bool m_use_selection_mesh;
+};
+
+enum ItemRotationKind {
+ IT_ROT_SELECTED,
+ IT_ROT_HOVERED,
+ IT_ROT_DRAGGED,
+ IT_ROT_NONE, // Must be last, also serves as number
};
void drawItemStack(video::IVideoDriver *driver,
@@ -151,8 +185,8 @@ void drawItemStack(video::IVideoDriver *driver,
const ItemStack &item,
const core::rect<s32> &rect,
const core::rect<s32> *clip,
- IGameDef *gamedef);
-
+ IGameDef *gamedef,
+ ItemRotationKind rotation_kind);
#endif
diff --git a/src/inventory.cpp b/src/inventory.cpp
index af8b1b301..cb8faecbc 100644
--- a/src/inventory.cpp
+++ b/src/inventory.cpp
@@ -23,7 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <sstream>
#include "log.h"
#include "itemdef.h"
-#include "strfnd.h"
+#include "util/strfnd.h"
#include "content_mapnode.h" // For loading legacy MaterialItems
#include "nameidmapping.h" // For loading legacy MaterialItems
#include "util/serialize.h"
@@ -126,7 +126,7 @@ ItemStack::ItemStack(std::string name_, u16 count_,
void ItemStack::serialize(std::ostream &os) const
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
if(empty())
return;
@@ -151,7 +151,7 @@ void ItemStack::serialize(std::ostream &os) const
void ItemStack::deSerialize(std::istream &is, IItemDefManager *itemdef)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
clear();
@@ -218,7 +218,7 @@ void ItemStack::deSerialize(std::istream &is, IItemDefManager *itemdef)
Strfnd fnd(all);
fnd.next("\"");
// If didn't skip to end, we have ""s
- if(!fnd.atend()){
+ if(!fnd.at_end()){
name = fnd.next("\"");
} else { // No luck, just read a word then
fnd.start(all);
@@ -246,7 +246,7 @@ void ItemStack::deSerialize(std::istream &is, IItemDefManager *itemdef)
Strfnd fnd(all);
fnd.next("\"");
// If didn't skip to end, we have ""s
- if(!fnd.atend()){
+ if(!fnd.at_end()){
name = fnd.next("\"");
} else { // No luck, just read a word then
fnd.start(all);
@@ -335,8 +335,9 @@ ItemStack ItemStack::addItem(const ItemStack &newitem_,
*this = newitem;
newitem.clear();
}
- // If item name differs, bail out
- else if(name != newitem.name)
+ // If item name or metadata differs, bail out
+ else if (name != newitem.name
+ || metadata != newitem.metadata)
{
// cannot be added
}
@@ -374,8 +375,9 @@ bool ItemStack::itemFits(const ItemStack &newitem_,
{
newitem.clear();
}
- // If item name differs, bail out
- else if(name != newitem.name)
+ // If item name or metadata differs, bail out
+ else if (name != newitem.name
+ || metadata != newitem.metadata)
{
// cannot be added
}
@@ -730,7 +732,7 @@ bool InventoryList::containsItem(const ItemStack &item) const
return true;
for(std::vector<ItemStack>::const_reverse_iterator
i = m_items.rbegin();
- i != m_items.rend(); i++)
+ i != m_items.rend(); ++i)
{
if(count == 0)
break;
@@ -750,7 +752,7 @@ ItemStack InventoryList::removeItem(const ItemStack &item)
ItemStack removed;
for(std::vector<ItemStack>::reverse_iterator
i = m_items.rbegin();
- i != m_items.rend(); i++)
+ i != m_items.rend(); ++i)
{
if(i->name == item.name)
{
diff --git a/src/inventorymanager.cpp b/src/inventorymanager.cpp
index 476768b8c..3d8513492 100644
--- a/src/inventorymanager.cpp
+++ b/src/inventorymanager.cpp
@@ -25,7 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "settings.h"
#include "craftdef.h"
#include "rollback_interface.h"
-#include "strfnd.h"
+#include "util/strfnd.h"
#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
@@ -818,9 +818,9 @@ void ICraftAction::apply(InventoryManager *mgr,
// Add the new replacements to the list
IItemDefManager *itemdef = gamedef->getItemDefManager();
for (std::vector<ItemStack>::iterator it = temp.begin();
- it != temp.end(); it++) {
+ it != temp.end(); ++it) {
for (std::vector<ItemStack>::iterator jt = output_replacements.begin();
- jt != output_replacements.end(); jt++) {
+ jt != output_replacements.end(); ++jt) {
if (it->name == jt->name) {
*it = jt->addItem(*it, itemdef);
if (it->empty())
@@ -850,7 +850,7 @@ void ICraftAction::apply(InventoryManager *mgr,
// Put the replacements in the inventory or drop them on the floor, if
// the invenotry is full
for (std::vector<ItemStack>::iterator it = output_replacements.begin();
- it != output_replacements.end(); it++) {
+ it != output_replacements.end(); ++it) {
if (list_main)
*it = list_main->addItem(*it);
if (it->empty())
@@ -886,7 +886,7 @@ bool getCraftingResult(Inventory *inv, ItemStack& result,
std::vector<ItemStack> &output_replacements,
bool decrementInput, IGameDef *gamedef)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
result.clear();
diff --git a/src/irrlichttypes.h b/src/irrlichttypes.h
index bead64407..dedebd45b 100644
--- a/src/irrlichttypes.h
+++ b/src/irrlichttypes.h
@@ -48,4 +48,19 @@ using namespace irr;
#endif
#endif
+#define S8_MIN (-0x7F - 1)
+#define S16_MIN (-0x7FFF - 1)
+#define S32_MIN (-0x7FFFFFFF - 1)
+#define S64_MIN (-0x7FFFFFFFFFFFFFFF - 1)
+
+#define S8_MAX 0x7F
+#define S16_MAX 0x7FFF
+#define S32_MAX 0x7FFFFFFF
+#define S64_MAX 0x7FFFFFFFFFFFFFFF
+
+#define U8_MAX 0xFF
+#define U16_MAX 0xFFFF
+#define U32_MAX 0xFFFFFFFF
+#define U64_MAX 0xFFFFFFFFFFFFFFFF
+
#endif
diff --git a/src/itemdef.cpp b/src/itemdef.cpp
index 0133b1b3f..a618ad631 100644
--- a/src/itemdef.cpp
+++ b/src/itemdef.cpp
@@ -80,6 +80,7 @@ ItemDefinition& ItemDefinition::operator=(const ItemDefinition &def)
groups = def.groups;
node_placement_prediction = def.node_placement_prediction;
sound_place = def.sound_place;
+ sound_place_failed = def.sound_place_failed;
range = def.range;
return *this;
}
@@ -114,6 +115,7 @@ void ItemDefinition::reset()
}
groups.clear();
sound_place = SimpleSoundSpec();
+ sound_place_failed = SimpleSoundSpec();
range = -1;
node_placement_prediction = "";
@@ -145,7 +147,7 @@ void ItemDefinition::serialize(std::ostream &os, u16 protocol_version) const
os<<serializeString(tool_capabilities_s);
writeU16(os, groups.size());
for(std::map<std::string, int>::const_iterator
- i = groups.begin(); i != groups.end(); i++){
+ i = groups.begin(); i != groups.end(); ++i){
os<<serializeString(i->first);
writeS16(os, i->second);
}
@@ -155,8 +157,10 @@ void ItemDefinition::serialize(std::ostream &os, u16 protocol_version) const
os<<serializeString(sound_place.name);
writeF1000(os, sound_place.gain);
}
- if(protocol_version > 20){
+ if (protocol_version > 20) {
writeF1000(os, range);
+ os << serializeString(sound_place_failed.name);
+ writeF1000(os, sound_place_failed.gain);
}
}
@@ -211,8 +215,10 @@ void ItemDefinition::deSerialize(std::istream &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 {
+ sound_place_failed.name = deSerializeString(is);
+ sound_place_failed.gain = readF1000(is);
+ } catch(SerializationError &e) {};
}
/*
@@ -241,7 +247,7 @@ public:
{
#ifndef SERVER
- m_main_thread = get_current_thread_id();
+ m_main_thread = thr_get_current_thread_id();
#endif
clear();
}
@@ -261,7 +267,7 @@ public:
#endif
for (std::map<std::string, ItemDefinition*>::iterator iter =
m_item_definitions.begin(); iter != m_item_definitions.end();
- iter ++) {
+ ++iter) {
delete iter->second;
}
m_item_definitions.clear();
@@ -317,7 +323,7 @@ public:
<<name<<"\""<<std::endl;
// This is not thread-safe
- sanity_check(get_current_thread_id() == m_main_thread);
+ sanity_check(thr_is_current_thread(m_main_thread));
// Skip if already in cache
ClientCached *cc = NULL;
@@ -326,7 +332,6 @@ public:
return cc;
ITextureSource *tsrc = gamedef->getTextureSource();
- INodeDefManager *nodedef = gamedef->getNodeDefManager();
const ItemDefinition &def = get(name);
// Create new ClientCached
@@ -337,103 +342,11 @@ public:
if(def.inventory_image != "")
cc->inventory_texture = tsrc->getTexture(def.inventory_image);
- // Additional processing for nodes:
- // - Create a wield mesh if WieldMeshSceneNode can't render
- // the node on its own.
- // - If inventory_texture isn't set yet, create one using
- // render-to-texture.
- if (def.type == ITEM_NODE) {
- // Get node properties
- content_t id = nodedef->getId(name);
- const ContentFeatures &f = nodedef->get(id);
-
- bool need_rtt_mesh = cc->inventory_texture == NULL;
-
- // Keep this in sync with WieldMeshSceneNode::setItem()
- bool need_wield_mesh =
- !(f.mesh_ptr[0] ||
- f.drawtype == NDT_NORMAL ||
- f.drawtype == NDT_ALLFACES ||
- f.drawtype == NDT_AIRLIKE);
-
- scene::IMesh *node_mesh = NULL;
-
- if (need_rtt_mesh || need_wield_mesh) {
- u8 param1 = 0;
- if (f.param_type == CPT_LIGHT)
- param1 = 0xee;
-
- /*
- Make a mesh from the node
- */
- MeshMakeData mesh_make_data(gamedef, false);
- u8 param2 = 0;
- if (f.param_type_2 == CPT2_WALLMOUNTED)
- param2 = 1;
- MapNode mesh_make_node(id, param1, param2);
- mesh_make_data.fillSingleNode(&mesh_make_node);
- MapBlockMesh mapblock_mesh(&mesh_make_data, v3s16(0, 0, 0));
- node_mesh = mapblock_mesh.getMesh();
- node_mesh->grab();
- video::SColor c(255, 255, 255, 255);
- setMeshColor(node_mesh, c);
-
- // scale and translate the mesh so it's a
- // unit cube centered on the origin
- scaleMesh(node_mesh, v3f(1.0/BS, 1.0/BS, 1.0/BS));
- translateMesh(node_mesh, v3f(-1.0, -1.0, -1.0));
- }
-
- /*
- Draw node mesh into a render target texture
- */
- if (need_rtt_mesh) {
- TextureFromMeshParams params;
- params.mesh = node_mesh;
- params.dim.set(64, 64);
- params.rtt_texture_name = "INVENTORY_"
- + def.name + "_RTT";
- params.delete_texture_on_shutdown = true;
- params.camera_position.set(0, 1.0, -1.5);
- params.camera_position.rotateXZBy(45);
- params.camera_lookat.set(0, 0, 0);
- // Set orthogonal projection
- params.camera_projection_matrix.buildProjectionMatrixOrthoLH(
- 1.65, 1.65, 0, 100);
- params.ambient_light.set(1.0, 0.2, 0.2, 0.2);
- params.light_position.set(10, 100, -50);
- params.light_color.set(1.0, 0.5, 0.5, 0.5);
- params.light_radius = 1000;
-
-#ifdef __ANDROID__
- params.camera_position.set(0, -1.0, -1.5);
- params.camera_position.rotateXZBy(45);
- params.light_position.set(10, -100, -50);
-#endif
- cc->inventory_texture =
- tsrc->generateTextureFromMesh(params);
-
- // render-to-target didn't work
- if (cc->inventory_texture == NULL) {
- cc->inventory_texture =
- tsrc->getTexture(f.tiledef[0].name);
- }
- }
-
- /*
- Use the node mesh as the wield mesh
- */
- if (need_wield_mesh) {
- cc->wield_mesh = node_mesh;
- cc->wield_mesh->grab();
+ ItemStack item = ItemStack();
+ item.name = def.name;
- // no way reference count can be smaller than 2 in this place!
- assert(cc->wield_mesh->getReferenceCount() >= 2);
- }
-
- if (node_mesh)
- node_mesh->drop();
- }
+ scene::IMesh *mesh = getItemMesh(gamedef, item);
+ cc->wield_mesh = mesh;
// Put in cache
m_clientcached.set(name, cc);
@@ -448,7 +361,7 @@ public:
if(cc)
return cc;
- if(get_current_thread_id() == m_main_thread)
+ if(thr_is_current_thread(m_main_thread))
{
return createClientCachedDirect(name, gamedef);
}
@@ -500,7 +413,7 @@ public:
{
for(std::map<std::string, ItemDefinition*>::const_iterator
i = m_item_definitions.begin();
- i != m_item_definitions.end(); i++)
+ i != m_item_definitions.end(); ++i)
{
delete i->second;
}
diff --git a/src/itemdef.h b/src/itemdef.h
index f993fdbac..805b4aa5d 100644
--- a/src/itemdef.h
+++ b/src/itemdef.h
@@ -68,6 +68,7 @@ struct ItemDefinition
ToolCapabilities *tool_capabilities;
ItemGroupList groups;
SimpleSoundSpec sound_place;
+ SimpleSoundSpec sound_place_failed;
f32 range;
// Client shall immediately place this node when player places the item.
diff --git a/src/jthread/CMakeLists.txt b/src/jthread/CMakeLists.txt
deleted file mode 100644
index cebb35caa..000000000
--- a/src/jthread/CMakeLists.txt
+++ /dev/null
@@ -1,14 +0,0 @@
-if(UNIX)
- set(THREAD_SYS_DIR pthread)
-else()
- set(THREAD_SYS_DIR win32)
-endif()
-
-set(SRC_PREFIX ${CMAKE_CURRENT_SOURCE_DIR}/${THREAD_SYS_DIR})
-set(JTHREAD_SRCS
- ${SRC_PREFIX}/jmutex.cpp
- ${SRC_PREFIX}/jthread.cpp
- ${SRC_PREFIX}/jsemaphore.cpp
- ${SRC_PREFIX}/jevent.cpp
- PARENT_SCOPE)
-
diff --git a/src/jthread/LICENSE.MIT b/src/jthread/LICENSE.MIT
deleted file mode 100644
index 2aa4fd57b..000000000
--- a/src/jthread/LICENSE.MIT
+++ /dev/null
@@ -1,20 +0,0 @@
-The license of JThread:
-
-Permission is hereby granted, free of charge, to any person obtaining a
-copy of this software and associated documentation files (the "Software"),
-to deal in the Software without restriction, including without limitation
-the rights to use, copy, modify, merge, publish, distribute, sublicense,
-and/or sell copies of the Software, and to permit persons to whom the
-Software is furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included
-in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
-IN THE SOFTWARE.
-
diff --git a/src/jthread/jevent.h b/src/jthread/jevent.h
deleted file mode 100644
index 9ea7ebde8..000000000
--- a/src/jthread/jevent.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
-
- This file is a part of the JThread package, which contains some object-
- oriented thread wrappers for different thread implementations.
-
- Copyright (c) 2000-2006 Jori Liesenborgs (jori.liesenborgs@gmail.com)
-
- Permission is hereby granted, free of charge, to any person obtaining a
- copy of this software and associated documentation files (the "Software"),
- to deal in the Software without restriction, including without limitation
- the rights to use, copy, modify, merge, publish, distribute, sublicense,
- and/or sell copies of the Software, and to permit persons to whom the
- Software is furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- DEALINGS IN THE SOFTWARE.
-
-*/
-
-#ifndef JEVENT_H_
-#define JEVENT_H_
-
-#ifdef _WIN32
-#include <windows.h>
-#elif defined(__MACH__) && defined(__APPLE__)
-#include <mach/mach.h>
-#include <mach/task.h>
-#include <mach/semaphore.h>
-#include <sys/semaphore.h>
-#else
-#include <semaphore.h>
-#endif
-
-
-class Event {
-#ifdef _WIN32
- HANDLE hEvent;
-#elif defined(__MACH__) && defined(__APPLE__)
- semaphore_t sem;
-#else
- sem_t sem;
-#endif
-
-public:
- Event();
- ~Event();
- void wait();
- void signal();
-};
-
-#endif /* JEVENT_H_ */
diff --git a/src/jthread/jmutex.h b/src/jthread/jmutex.h
deleted file mode 100644
index e57cd8a43..000000000
--- a/src/jthread/jmutex.h
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
-
- This file is a part of the JThread package, which contains some object-
- oriented thread wrappers for different thread implementations.
-
- Copyright (c) 2000-2006 Jori Liesenborgs (jori.liesenborgs@gmail.com)
-
- Permission is hereby granted, free of charge, to any person obtaining a
- copy of this software and associated documentation files (the "Software"),
- to deal in the Software without restriction, including without limitation
- the rights to use, copy, modify, merge, publish, distribute, sublicense,
- and/or sell copies of the Software, and to permit persons to whom the
- Software is furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- DEALINGS IN THE SOFTWARE.
-
-*/
-
-#ifndef JMUTEX_H
-
-#define JMUTEX_H
-
-#if (defined(WIN32) || defined(_WIN32_WCE))
- #ifndef _WIN32_WINNT
- #define _WIN32_WINNT 0x0501
- #endif
- #ifndef _WIN32_WCE
- #include <process.h>
- #endif // _WIN32_WCE
- #include <winsock2.h>
- #include <windows.h>
- // CriticalSection is way faster than the alternative
- #define JMUTEX_CRITICALSECTION
-#else // using pthread
- #include <pthread.h>
-#endif // WIN32
-
-#define ERR_JMUTEX_ALREADYINIT -1
-#define ERR_JMUTEX_NOTINIT -2
-#define ERR_JMUTEX_CANTCREATEMUTEX -3
-
-class JMutex
-{
-public:
- JMutex();
- ~JMutex();
- int Lock();
- int Unlock();
-
-private:
-#if (defined(WIN32) || defined(_WIN32_WCE))
-#ifdef JMUTEX_CRITICALSECTION
- CRITICAL_SECTION mutex;
-#else // Use standard mutex
- HANDLE mutex;
-#endif // JMUTEX_CRITICALSECTION
-#else // pthread mutex
- pthread_mutex_t mutex;
-
- bool IsLocked() {
- if (pthread_mutex_trylock(&mutex)) {
- pthread_mutex_unlock(&mutex);
- return true;
- }
- return false;
- }
-#endif // WIN32
-};
-
-#endif // JMUTEX_H
diff --git a/src/jthread/jmutexautolock.h b/src/jthread/jmutexautolock.h
deleted file mode 100644
index 6020a5c33..000000000
--- a/src/jthread/jmutexautolock.h
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
-
- This file is a part of the JThread package, which contains some object-
- oriented thread wrappers for different thread implementations.
-
- Copyright (c) 2000-2006 Jori Liesenborgs (jori.liesenborgs@gmail.com)
-
- Permission is hereby granted, free of charge, to any person obtaining a
- copy of this software and associated documentation files (the "Software"),
- to deal in the Software without restriction, including without limitation
- the rights to use, copy, modify, merge, publish, distribute, sublicense,
- and/or sell copies of the Software, and to permit persons to whom the
- Software is furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- DEALINGS IN THE SOFTWARE.
-
-*/
-
-#ifndef JMUTEXAUTOLOCK_H
-
-#define JMUTEXAUTOLOCK_H
-
-#include "jmutex.h"
-
-class JMutexAutoLock
-{
-public:
- JMutexAutoLock(JMutex &m) : mutex(m) { mutex.Lock(); }
- ~JMutexAutoLock() { mutex.Unlock(); }
-private:
- JMutex &mutex;
-};
-
-#endif // JMUTEXAUTOLOCK_H
diff --git a/src/jthread/jthread.h b/src/jthread/jthread.h
deleted file mode 100644
index 89743a3e3..000000000
--- a/src/jthread/jthread.h
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
-
- This file is a part of the JThread package, which contains some object-
- oriented thread wrappers for different thread implementations.
-
- Copyright (c) 2000-2006 Jori Liesenborgs (jori.liesenborgs@gmail.com)
-
- Permission is hereby granted, free of charge, to any person obtaining a
- copy of this software and associated documentation files (the "Software"),
- to deal in the Software without restriction, including without limitation
- the rights to use, copy, modify, merge, publish, distribute, sublicense,
- and/or sell copies of the Software, and to permit persons to whom the
- Software is furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- DEALINGS IN THE SOFTWARE.
-
-*/
-
-#ifndef JTHREAD_H
-#define JTHREAD_H
-
-#if __cplusplus >= 201103L
-#include <atomic>
-#endif
-
-#include "jthread/jmutex.h"
-
-#define ERR_JTHREAD_CANTINITMUTEX -1
-#define ERR_JTHREAD_CANTSTARTTHREAD -2
-#define ERR_JTHREAD_THREADFUNCNOTSET -3
-#define ERR_JTHREAD_NOTRUNNING -4
-#define ERR_JTHREAD_ALREADYRUNNING -5
-
-class JThread
-{
-public:
- JThread();
- virtual ~JThread();
- int Start();
- inline void Stop()
- { requeststop = true; }
- int Kill();
- virtual void *Thread() = 0;
- inline bool IsRunning()
- { return running; }
- inline bool StopRequested()
- { return requeststop; }
- void *GetReturnValue();
- bool IsSameThread();
-
- /*
- * Wait for thread to finish
- * Note: this does not stop a thread you have to do this on your own
- * WARNING: never ever call this on a thread not started or already killed!
- */
- void Wait();
-protected:
- void ThreadStarted();
-private:
-
-#if (defined(WIN32) || defined(_WIN32_WCE))
-#ifdef _WIN32_WCE
- DWORD threadid;
- static DWORD WINAPI TheThread(void *param);
-#else
- static UINT __stdcall TheThread(void *param);
- UINT threadid;
-#endif // _WIN32_WCE
- HANDLE threadhandle;
-#else // pthread type threads
- static void *TheThread(void *param);
-
- pthread_t threadid;
-
- /*
- * reading and writing bool values is atomic on all relevant architectures
- * ( x86 + arm ). No need to waste time for locking here.
- * once C++11 is supported we can tell compiler to handle cpu caches correct
- * too. This should cause additional improvement (and silence thread
- * concurrency check tools.
- */
-#if __cplusplus >= 201103L
- std::atomic_bool started;
-#else
- bool started;
-#endif
-#endif // WIN32
- void *retval;
- /*
- * reading and writing bool values is atomic on all relevant architectures
- * ( x86 + arm ). No need to waste time for locking here.
- * once C++11 is supported we can tell compiler to handle cpu caches correct
- * too. This should cause additional improvement (and silence thread
- * concurrency check tools.
- */
-#if __cplusplus >= 201103L
- std::atomic_bool running;
- std::atomic_bool requeststop;
-#else
- bool running;
- bool requeststop;
-#endif
-
- JMutex continuemutex,continuemutex2;
-};
-
-#endif // JTHREAD_H
-
diff --git a/src/jthread/pthread/jevent.cpp b/src/jthread/pthread/jevent.cpp
deleted file mode 100644
index e1d40f4c1..000000000
--- a/src/jthread/pthread/jevent.cpp
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
-
- This file is a part of the JThread package, which contains some object-
- oriented thread wrappers for different thread implementations.
-
- Copyright (c) 2000-2006 Jori Liesenborgs (jori.liesenborgs@gmail.com)
-
- Permission is hereby granted, free of charge, to any person obtaining a
- copy of this software and associated documentation files (the "Software"),
- to deal in the Software without restriction, including without limitation
- the rights to use, copy, modify, merge, publish, distribute, sublicense,
- and/or sell copies of the Software, and to permit persons to whom the
- Software is furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- DEALINGS IN THE SOFTWARE.
-
-*/
-#include <assert.h>
-#include "jthread/jevent.h"
-
-#define UNUSED(expr) do { (void)(expr); } while (0)
-
-#if defined(__MACH__) && defined(__APPLE__)
-#undef sem_t
-#define sem_t semaphore_t
-#undef sem_init
-#define sem_init(s, p, c) semaphore_create(mach_task_self(), (s), 0, (c))
-#undef sem_wait
-#define sem_wait(s) semaphore_wait(*(s))
-#undef sem_post
-#define sem_post(s) semaphore_signal(*(s))
-#undef sem_destroy
-#define sem_destroy(s) semaphore_destroy(mach_task_self(), *(s))
-#endif
-
-Event::Event() {
- int sem_init_retval = sem_init(&sem, 0, 0);
- assert(sem_init_retval == 0);
- UNUSED(sem_init_retval);
-}
-
-Event::~Event() {
- int sem_destroy_retval = sem_destroy(&sem);
- assert(sem_destroy_retval == 0);
- UNUSED(sem_destroy_retval);
-}
-
-void Event::wait() {
- int sem_wait_retval = sem_wait(&sem);
- assert(sem_wait_retval == 0);
- UNUSED(sem_wait_retval);
-}
-
-void Event::signal() {
- int sem_post_retval = sem_post(&sem);
- assert(sem_post_retval == 0);
- UNUSED(sem_post_retval);
-}
diff --git a/src/jthread/pthread/jmutex.cpp b/src/jthread/pthread/jmutex.cpp
deleted file mode 100644
index 0551b9728..000000000
--- a/src/jthread/pthread/jmutex.cpp
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
-
- This file is a part of the JThread package, which contains some object-
- oriented thread wrappers for different thread implementations.
-
- Copyright (c) 2000-2006 Jori Liesenborgs (jori.liesenborgs@gmail.com)
-
- Permission is hereby granted, free of charge, to any person obtaining a
- copy of this software and associated documentation files (the "Software"),
- to deal in the Software without restriction, including without limitation
- the rights to use, copy, modify, merge, publish, distribute, sublicense,
- and/or sell copies of the Software, and to permit persons to whom the
- Software is furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- DEALINGS IN THE SOFTWARE.
-
-*/
-#include <assert.h>
-#include "jthread/jmutex.h"
-#define UNUSED(expr) do { (void)(expr); } while (0)
-JMutex::JMutex()
-{
- int mutex_init_retval = pthread_mutex_init(&mutex,NULL);
- assert( mutex_init_retval == 0 );
- UNUSED(mutex_init_retval);
-}
-
-JMutex::~JMutex()
-{
- int mutex_dextroy_retval = pthread_mutex_destroy(&mutex);
- assert( mutex_dextroy_retval == 0 );
- UNUSED(mutex_dextroy_retval);
-}
-
-int JMutex::Lock()
-{
- int mutex_lock_retval = pthread_mutex_lock(&mutex);
- assert( mutex_lock_retval == 0 );
- return mutex_lock_retval;
- UNUSED(mutex_lock_retval);
-}
-
-int JMutex::Unlock()
-{
- int mutex_unlock_retval = pthread_mutex_unlock(&mutex);
- assert( mutex_unlock_retval == 0 );
- return mutex_unlock_retval;
- UNUSED(mutex_unlock_retval);
-}
diff --git a/src/jthread/pthread/jsemaphore.cpp b/src/jthread/pthread/jsemaphore.cpp
deleted file mode 100644
index 15281ba64..000000000
--- a/src/jthread/pthread/jsemaphore.cpp
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
-Minetest
-Copyright (C) 2013 sapier, < sapier AT gmx DOT 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
-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 <assert.h>
-#include <errno.h>
-#include <sys/time.h>
-#include "jthread/jsemaphore.h"
-#if defined(__MACH__) && defined(__APPLE__)
-#include <unistd.h>
-#endif
-
-#define UNUSED(expr) do { (void)(expr); } while (0)
-
-#if defined(__MACH__) && defined(__APPLE__)
-#undef sem_t
-#undef sem_init
-#undef sem_wait
-#undef sem_post
-#undef sem_destroy
-#define sem_t semaphore_t
-#define sem_init(s, p, c) semaphore_create(mach_task_self(), (s), 0, (c))
-#define sem_wait(s) semaphore_wait(*(s))
-#define sem_post(s) semaphore_signal(*(s))
-#define sem_destroy(s) semaphore_destroy(mach_task_self(), *(s))
-pthread_mutex_t semcount_mutex;
-#endif
-
-JSemaphore::JSemaphore() {
- int sem_init_retval = sem_init(&m_semaphore,0,0);
- assert(sem_init_retval == 0);
- UNUSED(sem_init_retval);
-#if defined(__MACH__) && defined(__APPLE__)
- semcount = 0;
-#endif
-}
-
-JSemaphore::~JSemaphore() {
- int sem_destroy_retval = sem_destroy(&m_semaphore);
-#ifdef __ANDROID__
-// WORKAROUND for broken bionic semaphore implementation!
- assert(
- (sem_destroy_retval == 0) ||
- (errno == EBUSY)
- );
-#else
- assert(sem_destroy_retval == 0);
-#endif
- UNUSED(sem_destroy_retval);
-}
-
-JSemaphore::JSemaphore(int initval) {
- int sem_init_retval = sem_init(&m_semaphore,0,initval);
- assert(sem_init_retval == 0);
- UNUSED(sem_init_retval);
-}
-
-void JSemaphore::Post() {
- int sem_post_retval = sem_post(&m_semaphore);
- assert(sem_post_retval == 0);
- UNUSED(sem_post_retval);
-#if defined(__MACH__) && defined(__APPLE__)
- pthread_mutex_lock(&semcount_mutex);
- semcount++;
- pthread_mutex_unlock(&semcount_mutex);
-#endif
-}
-
-void JSemaphore::Wait() {
- int sem_wait_retval = sem_wait(&m_semaphore);
- assert(sem_wait_retval == 0);
- UNUSED(sem_wait_retval);
-#if defined(__MACH__) && defined(__APPLE__)
- pthread_mutex_lock(&semcount_mutex);
- semcount--;
- pthread_mutex_unlock(&semcount_mutex);
-#endif
-}
-
-bool JSemaphore::Wait(unsigned int time_ms) {
-#if defined(__MACH__) && defined(__APPLE__)
- mach_timespec_t waittime;
- waittime.tv_sec = time_ms / 1000;
- waittime.tv_nsec = 1000000 * (time_ms % 1000);
-#else
- struct timespec waittime;
-#endif
- struct timeval now;
-
- if (gettimeofday(&now, NULL) == -1) {
- assert("Unable to get time by clock_gettime!" == 0);
- return false;
- }
-
-#if !(defined(__MACH__) && defined(__APPLE__))
- waittime.tv_nsec = ((time_ms % 1000) * 1000 * 1000) + (now.tv_usec * 1000);
- waittime.tv_sec = (time_ms / 1000) + (waittime.tv_nsec / (1000*1000*1000)) + now.tv_sec;
- waittime.tv_nsec %= 1000*1000*1000;
-#endif
-
- errno = 0;
-#if defined(__MACH__) && defined(__APPLE__)
- int sem_wait_retval = semaphore_timedwait(m_semaphore, waittime);
- if (sem_wait_retval == KERN_OPERATION_TIMED_OUT) {
- errno = ETIMEDOUT;
- } else if (sem_wait_retval == KERN_ABORTED) {
- errno = EINTR;
- } else if (sem_wait_retval != 0) {
- errno = EINVAL;
- }
-#else
- int sem_wait_retval = sem_timedwait(&m_semaphore, &waittime);
-#endif
-
- if (sem_wait_retval == 0)
- {
-#if defined(__MACH__) && defined(__APPLE__)
- pthread_mutex_lock(&semcount_mutex);
- semcount--;
- pthread_mutex_unlock(&semcount_mutex);
-#endif
- return true;
- }
- else {
- assert((errno == ETIMEDOUT) || (errno == EINTR));
- return false;
- }
- return sem_wait_retval == 0 ? true : false;
-}
-
-int JSemaphore::GetValue() {
- int retval = 0;
-#if defined(__MACH__) && defined(__APPLE__)
- pthread_mutex_lock(&semcount_mutex);
- retval = semcount;
- pthread_mutex_unlock(&semcount_mutex);
-#else
- sem_getvalue(&m_semaphore, &retval);
-#endif
- return retval;
-}
-
diff --git a/src/jthread/pthread/jthread.cpp b/src/jthread/pthread/jthread.cpp
deleted file mode 100644
index 414d8fde5..000000000
--- a/src/jthread/pthread/jthread.cpp
+++ /dev/null
@@ -1,168 +0,0 @@
-/*
-
- This file is a part of the JThread package, which contains some object-
- oriented thread wrappers for different thread implementations.
-
- Copyright (c) 2000-2006 Jori Liesenborgs (jori.liesenborgs@gmail.com)
-
- Permission is hereby granted, free of charge, to any person obtaining a
- copy of this software and associated documentation files (the "Software"),
- to deal in the Software without restriction, including without limitation
- the rights to use, copy, modify, merge, publish, distribute, sublicense,
- and/or sell copies of the Software, and to permit persons to whom the
- Software is furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- DEALINGS IN THE SOFTWARE.
-
-*/
-
-#include "jthread/jthread.h"
-#include <assert.h>
-#include <sys/time.h>
-#include <time.h>
-#include <stdlib.h>
-
-#define UNUSED(expr) do { (void)(expr); } while (0)
-
-JThread::JThread()
-{
- retval = NULL;
- requeststop = false;
- running = false;
- started = false;
-}
-
-JThread::~JThread()
-{
- Kill();
-}
-
-void JThread::Wait() {
- void* status;
- if (started) {
- int pthread_join_retval = pthread_join(threadid,&status);
- assert(pthread_join_retval == 0);
- UNUSED(pthread_join_retval);
- started = false;
- }
-}
-
-int JThread::Start()
-{
- int status;
-
- if (running)
- {
- return ERR_JTHREAD_ALREADYRUNNING;
- }
- requeststop = false;
-
- pthread_attr_t attr;
- pthread_attr_init(&attr);
- //pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
-
- continuemutex.Lock();
- status = pthread_create(&threadid,&attr,TheThread,this);
- pthread_attr_destroy(&attr);
- if (status != 0)
- {
- continuemutex.Unlock();
- return ERR_JTHREAD_CANTSTARTTHREAD;
- }
-
- /* Wait until 'running' is set */
-
- while (!running)
- {
- struct timespec req,rem;
-
- req.tv_sec = 0;
- req.tv_nsec = 1000000;
- nanosleep(&req,&rem);
- }
- started = true;
-
- continuemutex.Unlock();
-
- continuemutex2.Lock();
- continuemutex2.Unlock();
- return 0;
-}
-
-int JThread::Kill()
-{
- void* status;
- if (!running)
- {
- if (started) {
- int pthread_join_retval = pthread_join(threadid,&status);
- assert(pthread_join_retval == 0);
- UNUSED(pthread_join_retval);
- started = false;
- }
- return ERR_JTHREAD_NOTRUNNING;
- }
-#ifdef __ANDROID__
- pthread_kill(threadid, SIGKILL);
-#else
- pthread_cancel(threadid);
-#endif
- if (started) {
- int pthread_join_retval = pthread_join(threadid,&status);
- assert(pthread_join_retval == 0);
- UNUSED(pthread_join_retval);
- started = false;
- }
- running = false;
- return 0;
-}
-
-void *JThread::GetReturnValue()
-{
- void *val;
-
- if (running) {
- val = NULL;
- } else {
- val = retval;
- }
-
- return val;
-}
-
-bool JThread::IsSameThread()
-{
- return pthread_equal(pthread_self(), threadid);
-}
-
-void *JThread::TheThread(void *param)
-{
- JThread *jthread = (JThread *)param;
-
- jthread->continuemutex2.Lock();
- jthread->running = true;
-
- jthread->continuemutex.Lock();
- jthread->continuemutex.Unlock();
-
- jthread->Thread();
-
- jthread->running = false;
-
- return NULL;
-}
-
-void JThread::ThreadStarted()
-{
- continuemutex2.Unlock();
-}
-
diff --git a/src/jthread/win32/jevent.cpp b/src/jthread/win32/jevent.cpp
deleted file mode 100644
index 67b468f01..000000000
--- a/src/jthread/win32/jevent.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
-
- This file is a part of the JThread package, which contains some object-
- oriented thread wrappers for different thread implementations.
-
- Copyright (c) 2000-2006 Jori Liesenborgs (jori.liesenborgs@gmail.com)
-
- Permission is hereby granted, free of charge, to any person obtaining a
- copy of this software and associated documentation files (the "Software"),
- to deal in the Software without restriction, including without limitation
- the rights to use, copy, modify, merge, publish, distribute, sublicense,
- and/or sell copies of the Software, and to permit persons to whom the
- Software is furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- DEALINGS IN THE SOFTWARE.
-
-*/
-#include "jthread/jevent.h"
-
-Event::Event() {
- hEvent = CreateEvent(NULL, 0, 0, NULL);
-}
-
-Event::~Event() {
- CloseHandle(hEvent);
-}
-
-void Event::wait() {
- WaitForSingleObject(hEvent, INFINITE);
-}
-
-void Event::signal() {
- SetEvent(hEvent);
-}
diff --git a/src/jthread/win32/jmutex.cpp b/src/jthread/win32/jmutex.cpp
deleted file mode 100644
index b9f5e0e73..000000000
--- a/src/jthread/win32/jmutex.cpp
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
-
- This file is a part of the JThread package, which contains some object-
- oriented thread wrappers for different thread implementations.
-
- Copyright (c) 2000-2006 Jori Liesenborgs (jori.liesenborgs@gmail.com)
-
- Permission is hereby granted, free of charge, to any person obtaining a
- copy of this software and associated documentation files (the "Software"),
- to deal in the Software without restriction, including without limitation
- the rights to use, copy, modify, merge, publish, distribute, sublicense,
- and/or sell copies of the Software, and to permit persons to whom the
- Software is furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- DEALINGS IN THE SOFTWARE.
-
-*/
-#include <assert.h>
-#include "jthread/jmutex.h"
-
-JMutex::JMutex()
-{
-#ifdef JMUTEX_CRITICALSECTION
- InitializeCriticalSection(&mutex);
-#else
- mutex = CreateMutex(NULL,FALSE,NULL);
- assert(mutex != NULL);
-#endif // JMUTEX_CRITICALSECTION
-}
-
-JMutex::~JMutex()
-{
-#ifdef JMUTEX_CRITICALSECTION
- DeleteCriticalSection(&mutex);
-#else
- CloseHandle(mutex);
-#endif // JMUTEX_CRITICALSECTION
-}
-
-int JMutex::Lock()
-{
-#ifdef JMUTEX_CRITICALSECTION
- EnterCriticalSection(&mutex);
-#else
- WaitForSingleObject(mutex,INFINITE);
-#endif // JMUTEX_CRITICALSECTION
- return 0;
-}
-
-int JMutex::Unlock()
-{
-#ifdef JMUTEX_CRITICALSECTION
- LeaveCriticalSection(&mutex);
-#else
- ReleaseMutex(mutex);
-#endif // JMUTEX_CRITICALSECTION
- return 0;
-}
-
diff --git a/src/jthread/win32/jsemaphore.cpp b/src/jthread/win32/jsemaphore.cpp
deleted file mode 100644
index 27a11e819..000000000
--- a/src/jthread/win32/jsemaphore.cpp
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
-Minetest
-Copyright (C) 2013 sapier, < sapier AT gmx DOT 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
-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 "jthread/jsemaphore.h"
-
-JSemaphore::JSemaphore() {
- m_hSemaphore = CreateSemaphore(
- 0,
- 0,
- MAX_SEMAPHORE_COUNT,
- 0);
-}
-
-JSemaphore::~JSemaphore() {
- CloseHandle(m_hSemaphore);
-}
-
-JSemaphore::JSemaphore(int initval) {
- m_hSemaphore = CreateSemaphore(
- 0,
- initval,
- MAX_SEMAPHORE_COUNT,
- 0);
-}
-
-void JSemaphore::Post() {
- ReleaseSemaphore(
- m_hSemaphore,
- 1,
- 0);
-}
-
-void JSemaphore::Wait() {
- WaitForSingleObject(
- m_hSemaphore,
- INFINITE);
-}
-
-bool JSemaphore::Wait(unsigned int time_ms) {
- unsigned int retval = WaitForSingleObject(
- m_hSemaphore,
- time_ms);
-
- if (retval == WAIT_OBJECT_0)
- {
- return true;
- }
- else {
- assert(retval == WAIT_TIMEOUT);
- return false;
- }
-}
-
-typedef LONG (NTAPI *_NtQuerySemaphore)(
- HANDLE SemaphoreHandle,
- DWORD SemaphoreInformationClass,
- PVOID SemaphoreInformation,
- ULONG SemaphoreInformationLength,
- PULONG ReturnLength OPTIONAL
-);
-
-typedef struct _SEMAPHORE_BASIC_INFORMATION {
- ULONG CurrentCount;
- ULONG MaximumCount;
-} SEMAPHORE_BASIC_INFORMATION;
-
-/* Note: this will only work as long as jthread is directly linked to application */
-/* it's gonna fail if someone tries to build jthread as dll */
-static _NtQuerySemaphore NtQuerySemaphore =
- (_NtQuerySemaphore)
- GetProcAddress
- (GetModuleHandle ("ntdll.dll"), "NtQuerySemaphore");
-
-int JSemaphore::GetValue() {
- SEMAPHORE_BASIC_INFORMATION BasicInfo;
- LONG retval;
-
- assert(NtQuerySemaphore);
-
- retval = NtQuerySemaphore (m_hSemaphore, 0,
- &BasicInfo, sizeof (SEMAPHORE_BASIC_INFORMATION), NULL);
-
- if (retval == ERROR_SUCCESS)
- return BasicInfo.CurrentCount;
-
- assert("unable to read semaphore count" == 0);
- return 0;
-}
-
diff --git a/src/jthread/win32/jthread.cpp b/src/jthread/win32/jthread.cpp
deleted file mode 100755
index b523d664c..000000000
--- a/src/jthread/win32/jthread.cpp
+++ /dev/null
@@ -1,146 +0,0 @@
-/*
-
- This file is a part of the JThread package, which contains some object-
- oriented thread wrappers for different thread implementations.
-
- Copyright (c) 2000-2006 Jori Liesenborgs (jori.liesenborgs@gmail.com)
-
- Permission is hereby granted, free of charge, to any person obtaining a
- copy of this software and associated documentation files (the "Software"),
- to deal in the Software without restriction, including without limitation
- the rights to use, copy, modify, merge, publish, distribute, sublicense,
- and/or sell copies of the Software, and to permit persons to whom the
- Software is furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- DEALINGS IN THE SOFTWARE.
-
-*/
-
-#include "jthread/jthread.h"
-#include <assert.h>
-#define UNUSED(expr) do { (void)(expr); } while (0)
-#ifndef _WIN32_WCE
- #include <process.h>
-#endif // _WIN32_WCE
-
-JThread::JThread()
-{
- retval = NULL;
- requeststop = false;
- running = false;
-}
-
-JThread::~JThread()
-{
- Kill();
-}
-
-void JThread::Wait() {
- if (running)
- {
- WaitForSingleObject(threadhandle, INFINITE);
- }
-}
-
-int JThread::Start()
-{
- if (running)
- {
- return ERR_JTHREAD_ALREADYRUNNING;
- }
- requeststop = false;
-
- continuemutex.Lock();
-#ifndef _WIN32_WCE
- threadhandle = (HANDLE)_beginthreadex(NULL,0,TheThread,this,0,&threadid);
-#else
- threadhandle = CreateThread(NULL,0,TheThread,this,0,&threadid);
-#endif // _WIN32_WCE
- if (threadhandle == NULL)
- {
- continuemutex.Unlock();
- return ERR_JTHREAD_CANTSTARTTHREAD;
- }
-
- /* Wait until 'running' is set */
- while (!running)
- {
- Sleep(1);
- }
-
- continuemutex.Unlock();
-
- continuemutex2.Lock();
- continuemutex2.Unlock();
-
- return 0;
-}
-
-int JThread::Kill()
-{
- if (!running)
- {
- return ERR_JTHREAD_NOTRUNNING;
- }
- TerminateThread(threadhandle,0);
- CloseHandle(threadhandle);
- running = false;
- return 0;
-}
-
-void *JThread::GetReturnValue()
-{
- void *val;
-
- if (running) {
- val = NULL;
- } else {
- val = retval;
- }
- return val;
-}
-
-bool JThread::IsSameThread()
-{
- return GetCurrentThreadId() == threadid;
-}
-
-#ifndef _WIN32_WCE
-UINT __stdcall JThread::TheThread(void *param)
-#else
-DWORD WINAPI JThread::TheThread(void *param)
-#endif // _WIN32_WCE
-{
- JThread *jthread;
- void *ret;
-
- jthread = (JThread *)param;
-
- jthread->continuemutex2.Lock();
- jthread->running = true;
-
- jthread->continuemutex.Lock();
- jthread->continuemutex.Unlock();
-
- ret = jthread->Thread();
-
- jthread->running = false;
- jthread->retval = ret;
- CloseHandle(jthread->threadhandle);
- return 0;
-}
-
-void JThread::ThreadStarted()
-{
- continuemutex2.Unlock();
-}
-
diff --git a/src/localplayer.cpp b/src/localplayer.cpp
index 77e7a9e16..507f31980 100644
--- a/src/localplayer.cpp
+++ b/src/localplayer.cpp
@@ -42,6 +42,7 @@ LocalPlayer::LocalPlayer(IGameDef *gamedef, const char *name):
last_pitch(0),
last_yaw(0),
last_keyPressed(0),
+ camera_impact(0.f),
last_animation(NO_ANIM),
hotbar_image(""),
hotbar_selected_image(""),
@@ -182,7 +183,7 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d,
*/
if (control.sneak && m_sneak_node_exists &&
!(fly_allowed && g_settings->getBool("free_move")) && !in_liquid &&
- physics_override_sneak) {
+ physics_override_sneak && !got_teleported) {
f32 maxd = 0.5 * BS + sneak_max;
v3f lwn_f = intToFloat(m_sneak_node, BS);
position.X = rangelim(position.X, lwn_f.X-maxd, lwn_f.X+maxd);
@@ -203,18 +204,21 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d,
}
}
+ if (got_teleported)
+ got_teleported = false;
+
// this shouldn't be hardcoded but transmitted from server
float player_stepheight = touching_ground ? (BS*0.6) : (BS*0.2);
#ifdef __ANDROID__
- player_stepheight += (0.5 * BS);
+ player_stepheight += (0.6 * BS);
#endif
v3f accel_f = v3f(0,0,0);
collisionMoveResult result = collisionMoveSimple(env, m_gamedef,
- pos_max_d, m_collisionbox, player_stepheight, dtime,
- position, m_speed, accel_f);
+ pos_max_d, m_collisionbox, player_stepheight, dtime,
+ &position, &m_speed, accel_f);
/*
If the player's feet touch the topside of any node, this is
@@ -309,7 +313,8 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d,
if (sneak_node_found) {
f32 cb_max = 0;
MapNode n = map->getNodeNoEx(m_sneak_node);
- std::vector<aabb3f> nodeboxes = n.getCollisionBoxes(nodemgr);
+ std::vector<aabb3f> nodeboxes;
+ n.getCollisionBoxes(nodemgr, &nodeboxes);
for (std::vector<aabb3f>::iterator it = nodeboxes.begin();
it != nodeboxes.end(); ++it) {
aabb3f box = *it;
@@ -344,7 +349,7 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d,
}
}
- if(!touching_ground_was && touching_ground){
+ if(!result.standing_on_object && !touching_ground_was && touching_ground) {
MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround");
m_gamedef->event()->put(e);
@@ -512,15 +517,16 @@ void LocalPlayer::applyControl(float dtime)
}
}
- if(continuous_forward)
+ if (continuous_forward)
speedH += move_direction;
- if(control.up)
- {
- if(continuous_forward)
- superspeed = true;
- else
+ if (control.up) {
+ if (continuous_forward) {
+ if (fast_move)
+ superspeed = true;
+ } else {
speedH += move_direction;
+ }
}
if(control.down)
{
@@ -620,3 +626,36 @@ v3s16 LocalPlayer::getStandingNodePos()
return floatToInt(getPosition() - v3f(0, BS, 0), BS);
}
+// Horizontal acceleration (X and Z), Y direction is ignored
+void LocalPlayer::accelerateHorizontal(const v3f &target_speed, const f32 max_increase)
+{
+ if (max_increase == 0)
+ return;
+
+ v3f d_wanted = target_speed - m_speed;
+ d_wanted.Y = 0;
+ f32 dl = d_wanted.getLength();
+ if (dl > max_increase)
+ dl = max_increase;
+
+ v3f d = d_wanted.normalize() * dl;
+
+ m_speed.X += d.X;
+ m_speed.Z += d.Z;
+}
+
+// Vertical acceleration (Y), X and Z directions are ignored
+void LocalPlayer::accelerateVertical(const v3f &target_speed, const f32 max_increase)
+{
+ if (max_increase == 0)
+ return;
+
+ f32 d_wanted = target_speed.Y - m_speed.Y;
+ if (d_wanted > max_increase)
+ d_wanted = max_increase;
+ else if (d_wanted < -max_increase)
+ d_wanted = -max_increase;
+
+ m_speed.Y += d_wanted;
+}
+
diff --git a/src/localplayer.h b/src/localplayer.h
index 40a7f089e..3ae0c4e51 100644
--- a/src/localplayer.h
+++ b/src/localplayer.h
@@ -45,7 +45,7 @@ public:
bool isAttached;
v3f overridePosition;
-
+
void move(f32 dtime, Environment *env, f32 pos_max_d);
void move(f32 dtime, Environment *env, f32 pos_max_d,
std::vector<CollisionInfo> *collision_info);
@@ -81,6 +81,9 @@ public:
}
private:
+ void accelerateHorizontal(const v3f &target_speed, const f32 max_increase);
+ void accelerateVertical(const v3f &target_speed, const f32 max_increase);
+
// This is used for determining the sneaking range
v3s16 m_sneak_node;
// Whether the player is allowed to sneak
diff --git a/src/log.cpp b/src/log.cpp
index e6d80db34..589cfd909 100644
--- a/src/log.cpp
+++ b/src/log.cpp
@@ -19,19 +19,81 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "log.h"
-#include <map>
-#include <list>
-#include <sstream>
-#include <algorithm>
-#include "threads.h"
-#include "jthread/jmutexautolock.h"
+#include "threading/mutex_auto_lock.h"
#include "debug.h"
#include "gettime.h"
#include "porting.h"
#include "config.h"
+#include "exceptions.h"
+#include "util/numeric.h"
+#include "log.h"
+
+#include <sstream>
+#include <iostream>
+#include <algorithm>
+#include <cerrno>
+#include <cstring>
+
+const int BUFFER_LENGTH = 256;
+
+class StringBuffer : public std::streambuf {
+public:
+ StringBuffer() {
+ buffer_index = 0;
+ }
+
+ int overflow(int c);
+ virtual void flush(const std::string &buf) = 0;
+ std::streamsize xsputn(const char *s, std::streamsize n);
+ void push_back(char c);
+
+private:
+ char buffer[BUFFER_LENGTH];
+ int buffer_index;
+};
+
+
+class LogBuffer : public StringBuffer {
+public:
+ LogBuffer(Logger &logger, LogLevel lev) :
+ logger(logger),
+ level(lev)
+ {}
+
+ void flush(const std::string &buffer);
+
+private:
+ Logger &logger;
+ LogLevel level;
+};
+
+
+class RawLogBuffer : public StringBuffer {
+public:
+ void flush(const std::string &buffer);
+};
+
+////
+//// Globals
+////
+
+Logger g_logger;
+
+StreamLogOutput stdout_output(std::cout);
+StreamLogOutput stderr_output(std::cerr);
+std::ostream null_stream(NULL);
+
+RawLogBuffer raw_buf;
+
+LogBuffer none_buf(g_logger, LL_NONE);
+LogBuffer error_buf(g_logger, LL_ERROR);
+LogBuffer warning_buf(g_logger, LL_WARNING);
+LogBuffer action_buf(g_logger, LL_ACTION);
+LogBuffer info_buf(g_logger, LL_INFO);
+LogBuffer verbose_buf(g_logger, LL_VERBOSE);
// Connection
-std::ostream *dout_con_ptr = &dummyout;
+std::ostream *dout_con_ptr = &null_stream;
std::ostream *derr_con_ptr = &verbosestream;
// Server
@@ -44,170 +106,265 @@ std::ostream *dout_client_ptr = &infostream;
std::ostream *derr_client_ptr = &errorstream;
#endif
+std::ostream rawstream(&raw_buf);
+std::ostream dstream(&none_buf);
+std::ostream errorstream(&error_buf);
+std::ostream warningstream(&warning_buf);
+std::ostream actionstream(&action_buf);
+std::ostream infostream(&info_buf);
+std::ostream verbosestream(&verbose_buf);
+
+// Android
#ifdef __ANDROID__
-unsigned int android_log_level_mapping[] = {
- /* LMT_ERROR */ ANDROID_LOG_ERROR,
- /* LMT_ACTION */ ANDROID_LOG_WARN,
- /* LMT_INFO */ ANDROID_LOG_INFO,
- /* LMT_VERBOSE */ ANDROID_LOG_VERBOSE
- };
+
+static unsigned int g_level_to_android[] = {
+ ANDROID_LOG_INFO, // LL_NONE
+ //ANDROID_LOG_FATAL,
+ ANDROID_LOG_ERROR, // LL_ERROR
+ ANDROID_LOG_WARN, // LL_WARNING
+ ANDROID_LOG_WARN, // LL_ACTION
+ //ANDROID_LOG_INFO,
+ ANDROID_LOG_DEBUG, // LL_INFO
+ ANDROID_LOG_VERBOSE, // LL_VERBOSE
+};
+
+class AndroidSystemLogOutput : public ICombinedLogOutput {
+ public:
+ AndroidSystemLogOutput()
+ {
+ g_logger.addOutput(this);
+ }
+ ~AndroidSystemLogOutput()
+ {
+ g_logger.removeOutput(this);
+ }
+ void logRaw(LogLevel lev, const std::string &line)
+ {
+ STATIC_ASSERT(ARRLEN(g_level_to_android) == LL_MAX,
+ mismatch_between_android_and_internal_loglevels);
+ __android_log_print(g_level_to_android[lev],
+ PROJECT_NAME_C, "%s", line.c_str());
+ }
+};
+
+AndroidSystemLogOutput g_android_log_output;
+
#endif
-std::vector<ILogOutput*> log_outputs[LMT_NUM_VALUES];
-std::map<threadid_t, std::string> log_threadnames;
-JMutex log_threadnamemutex;
+///////////////////////////////////////////////////////////////////////////////
-void log_add_output(ILogOutput *out, enum LogMessageLevel lev)
+
+////
+//// Logger
+////
+
+LogLevel Logger::stringToLevel(const std::string &name)
{
- log_outputs[lev].push_back(out);
+ if (name == "none")
+ return LL_NONE;
+ else if (name == "error")
+ return LL_ERROR;
+ else if (name == "warning")
+ return LL_WARNING;
+ else if (name == "action")
+ return LL_ACTION;
+ else if (name == "info")
+ return LL_INFO;
+ else if (name == "verbose")
+ return LL_VERBOSE;
+ else
+ return LL_MAX;
}
-void log_add_output_maxlev(ILogOutput *out, enum LogMessageLevel lev)
+void Logger::addOutput(ILogOutput *out)
{
- for(int i=0; i<=lev; i++)
- log_outputs[i].push_back(out);
+ addOutputMaxLevel(out, (LogLevel)(LL_MAX - 1));
}
-void log_add_output_all_levs(ILogOutput *out)
+void Logger::addOutput(ILogOutput *out, LogLevel lev)
{
- for(int i=0; i<LMT_NUM_VALUES; i++)
- log_outputs[i].push_back(out);
+ m_outputs[lev].push_back(out);
}
-void log_remove_output(ILogOutput *out)
+void Logger::addOutputMasked(ILogOutput *out, LogLevelMask mask)
{
- for(int i=0; i<LMT_NUM_VALUES; i++){
- std::vector<ILogOutput*>::iterator it =
- std::find(log_outputs[i].begin(), log_outputs[i].end(), out);
- if(it != log_outputs[i].end())
- log_outputs[i].erase(it);
+ for (size_t i = 0; i < LL_MAX; i++) {
+ if (mask & LOGLEVEL_TO_MASKLEVEL(i))
+ m_outputs[i].push_back(out);
}
}
-void log_set_lev_silence(enum LogMessageLevel lev, bool silence)
+void Logger::addOutputMaxLevel(ILogOutput *out, LogLevel lev)
{
- JMutexAutoLock lock(log_threadnamemutex);
+ assert(lev < LL_MAX);
+ for (size_t i = 0; i <= lev; i++)
+ m_outputs[i].push_back(out);
+}
- for (std::vector<ILogOutput *>::iterator it = log_outputs[lev].begin();
- it != log_outputs[lev].end(); ++it) {
- ILogOutput *out = *it;
- out->silence = silence;
+LogLevelMask Logger::removeOutput(ILogOutput *out)
+{
+ LogLevelMask ret_mask = 0;
+ for (size_t i = 0; i < LL_MAX; i++) {
+ std::vector<ILogOutput *>::iterator it;
+
+ it = std::find(m_outputs[i].begin(), m_outputs[i].end(), out);
+ if (it != m_outputs[i].end()) {
+ ret_mask |= LOGLEVEL_TO_MASKLEVEL(i);
+ m_outputs[i].erase(it);
+ }
}
+ return ret_mask;
}
-void log_register_thread(const std::string &name)
+void Logger::setLevelSilenced(LogLevel lev, bool silenced)
{
- threadid_t id = get_current_thread_id();
- JMutexAutoLock lock(log_threadnamemutex);
+ m_silenced_levels[lev] = silenced;
+}
- log_threadnames[id] = name;
+void Logger::registerThread(const std::string &name)
+{
+ threadid_t id = thr_get_current_thread_id();
+ MutexAutoLock lock(m_mutex);
+ m_thread_names[id] = name;
}
-void log_deregister_thread()
+void Logger::deregisterThread()
{
- threadid_t id = get_current_thread_id();
- JMutexAutoLock lock(log_threadnamemutex);
+ threadid_t id = thr_get_current_thread_id();
+ MutexAutoLock lock(m_mutex);
+ m_thread_names.erase(id);
+}
- log_threadnames.erase(id);
+const std::string Logger::getLevelLabel(LogLevel lev)
+{
+ static const std::string names[] = {
+ "",
+ "ERROR",
+ "WARNING",
+ "ACTION",
+ "INFO",
+ "VERBOSE",
+ };
+ assert(lev < LL_MAX && lev >= 0);
+ STATIC_ASSERT(ARRLEN(names) == LL_MAX,
+ mismatch_between_loglevel_names_and_enum);
+ return names[lev];
}
-static std::string get_lev_string(enum LogMessageLevel lev)
+const std::string Logger::getThreadName()
{
- switch(lev){
- case LMT_ERROR:
- return "ERROR";
- case LMT_ACTION:
- return "ACTION";
- case LMT_INFO:
- return "INFO";
- case LMT_VERBOSE:
- return "VERBOSE";
- case LMT_NUM_VALUES:
- break;
- }
- return "(unknown level)";
+ std::map<threadid_t, std::string>::const_iterator it;
+
+ threadid_t id = thr_get_current_thread_id();
+ it = m_thread_names.find(id);
+ if (it != m_thread_names.end())
+ return it->second;
+
+ std::ostringstream os;
+ os << "#0x" << std::hex << id;
+ return os.str();
}
-void log_printline(enum LogMessageLevel lev, const std::string &text)
+void Logger::log(LogLevel lev, const std::string &text)
{
- JMutexAutoLock lock(log_threadnamemutex);
- std::string threadname = "(unknown thread)";
- std::map<threadid_t, std::string>::const_iterator i;
- i = log_threadnames.find(get_current_thread_id());
- if(i != log_threadnames.end())
- threadname = i->second;
- std::string levelname = get_lev_string(lev);
+ if (m_silenced_levels[lev])
+ return;
+
+ const std::string thread_name = getThreadName();
+ const std::string label = getLevelLabel(lev);
+ const std::string timestamp = getTimestamp();
std::ostringstream os(std::ios_base::binary);
- os << getTimestamp() << ": " << levelname << "["<<threadname<<"]: " << text;
+ os << timestamp << ": " << label << "[" << thread_name << "]: " << text;
- for(std::vector<ILogOutput*>::iterator i = log_outputs[lev].begin();
- i != log_outputs[lev].end(); i++) {
- ILogOutput *out = *i;
- if (out->silence)
- continue;
+ logToOutputs(lev, os.str(), timestamp, thread_name, text);
+}
- out->printLog(os.str());
- out->printLog(os.str(), lev);
- out->printLog(lev, text);
- }
+void Logger::logRaw(LogLevel lev, const std::string &text)
+{
+ if (m_silenced_levels[lev])
+ return;
+
+ logToOutputsRaw(lev, text);
}
-class Logbuf : public std::streambuf
+void Logger::logToOutputsRaw(LogLevel lev, const std::string &line)
{
-public:
- Logbuf(enum LogMessageLevel lev):
- m_lev(lev)
- {
- }
+ MutexAutoLock lock(m_mutex);
+ for (size_t i = 0; i != m_outputs[lev].size(); i++)
+ m_outputs[lev][i]->logRaw(lev, line);
+}
- ~Logbuf()
- {
- }
+void Logger::logToOutputs(LogLevel lev, const std::string &combined,
+ const std::string &time, const std::string &thread_name,
+ const std::string &payload_text)
+{
+ MutexAutoLock lock(m_mutex);
+ for (size_t i = 0; i != m_outputs[lev].size(); i++)
+ m_outputs[lev][i]->log(lev, combined, time, thread_name, payload_text);
+}
+
+
+////
+//// *LogOutput methods
+////
+
+void FileLogOutput::open(const std::string &filename)
+{
+ m_stream.open(filename.c_str(), std::ios::app | std::ios::ate);
+ if (!m_stream.good())
+ throw FileNotGoodException("Failed to open log file " +
+ filename + ": " + strerror(errno));
+ m_stream << "\n\n"
+ "-------------" << std::endl
+ << " Separator" << std::endl
+ << "-------------\n" << std::endl;
+}
- int overflow(int c)
- {
- bufchar(c);
- return c;
- }
- std::streamsize xsputn(const char *s, std::streamsize n)
- {
- for(int i=0; i<n; i++)
- bufchar(s[i]);
- return n;
- }
- void printbuf()
- {
- log_printline(m_lev, m_buf);
-#ifdef __ANDROID__
- __android_log_print(android_log_level_mapping[m_lev], PROJECT_NAME, "%s", m_buf.c_str());
-#endif
- }
- void bufchar(char c)
- {
- if(c == '\n' || c == '\r'){
- if(m_buf != "")
- printbuf();
- m_buf = "";
- return;
+////
+//// *Buffer methods
+////
+
+int StringBuffer::overflow(int c)
+{
+ push_back(c);
+ return c;
+}
+
+
+std::streamsize StringBuffer::xsputn(const char *s, std::streamsize n)
+{
+ for (int i = 0; i < n; ++i)
+ push_back(s[i]);
+ return n;
+}
+
+void StringBuffer::push_back(char c)
+{
+ if (c == '\n' || c == '\r') {
+ if (buffer_index)
+ flush(std::string(buffer, buffer_index));
+ buffer_index = 0;
+ } else {
+ int index = buffer_index;
+ buffer[index++] = c;
+ if (index >= BUFFER_LENGTH) {
+ flush(std::string(buffer, buffer_index));
+ buffer_index = 0;
+ } else {
+ buffer_index = index;
}
- m_buf += c;
}
+}
-private:
- enum LogMessageLevel m_lev;
- std::string m_buf;
-};
-
-Logbuf errorbuf(LMT_ERROR);
-Logbuf actionbuf(LMT_ACTION);
-Logbuf infobuf(LMT_INFO);
-Logbuf verbosebuf(LMT_VERBOSE);
-std::ostream errorstream(&errorbuf);
-std::ostream actionstream(&actionbuf);
-std::ostream infostream(&infobuf);
-std::ostream verbosestream(&verbosebuf);
-bool log_trace_level_enabled = false;
+void LogBuffer::flush(const std::string &buffer)
+{
+ logger.log(level, buffer);
+}
+void RawLogBuffer::flush(const std::string &buffer)
+{
+ g_logger.logRaw(LL_NONE, buffer);
+}
diff --git a/src/log.h b/src/log.h
index bd223927a..219255d9a 100644
--- a/src/log.h
+++ b/src/log.h
@@ -20,83 +20,197 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#ifndef LOG_HEADER
#define LOG_HEADER
+#include <map>
+#include <queue>
#include <string>
+#include <fstream>
+#include "threads.h"
+#include "irrlichttypes.h"
+
+class ILogOutput;
+
+enum LogLevel {
+ LL_NONE, // Special level that is always printed
+ LL_ERROR,
+ LL_WARNING,
+ LL_ACTION, // In-game actions
+ LL_INFO,
+ LL_VERBOSE,
+ LL_MAX,
+};
-/*
- Use this for logging everything.
+typedef u8 LogLevelMask;
+#define LOGLEVEL_TO_MASKLEVEL(x) (1 << x)
- If you need to explicitly print something, use dstream or cout or cerr.
-*/
+class Logger {
+public:
+ void addOutput(ILogOutput *out);
+ void addOutput(ILogOutput *out, LogLevel lev);
+ void addOutputMasked(ILogOutput *out, LogLevelMask mask);
+ void addOutputMaxLevel(ILogOutput *out, LogLevel lev);
+ LogLevelMask removeOutput(ILogOutput *out);
+ void setLevelSilenced(LogLevel lev, bool silenced);
+
+ void registerThread(const std::string &name);
+ void deregisterThread();
+
+ void log(LogLevel lev, const std::string &text);
+ // Logs without a prefix
+ void logRaw(LogLevel lev, const std::string &text);
+
+ void setTraceEnabled(bool enable) { m_trace_enabled = enable; }
+ bool getTraceEnabled() { return m_trace_enabled; }
+
+ static LogLevel stringToLevel(const std::string &name);
+ static const std::string getLevelLabel(LogLevel lev);
+
+private:
+ void logToOutputsRaw(LogLevel, const std::string &line);
+ void logToOutputs(LogLevel, const std::string &combined,
+ const std::string &time, const std::string &thread_name,
+ const std::string &payload_text);
+
+ const std::string getThreadName();
+
+ std::vector<ILogOutput *> m_outputs[LL_MAX];
+
+ // Should implement atomic loads and stores (even though it's only
+ // written to when one thread has access currently).
+ // Works on all known architectures (x86, ARM, MIPS).
+ volatile bool m_silenced_levels[LL_MAX];
+ std::map<threadid_t, std::string> m_thread_names;
+ mutable Mutex m_mutex;
+ bool m_trace_enabled;
+};
-enum LogMessageLevel {
- LMT_ERROR, /* Something failed ("invalid map data on disk, block (2,2,1)") */
- LMT_ACTION, /* In-game actions ("celeron55 placed block at (12,4,-5)") */
- LMT_INFO, /* More deep info ("saving map on disk (only_modified=true)") */
- LMT_VERBOSE, /* Flood-style ("loaded block (2,2,2) from disk") */
- LMT_NUM_VALUES,
+class ILogOutput {
+public:
+ virtual void logRaw(LogLevel, const std::string &line) = 0;
+ virtual void log(LogLevel, const std::string &combined,
+ const std::string &time, const std::string &thread_name,
+ const std::string &payload_text) = 0;
};
-class ILogOutput
-{
+class ICombinedLogOutput : public ILogOutput {
public:
- ILogOutput() :
- silence(false)
- {}
-
- /* line: Full line with timestamp, level and thread */
- virtual void printLog(const std::string &line){};
- /* line: Full line with timestamp, level and thread */
- virtual void printLog(const std::string &line, enum LogMessageLevel lev){};
- /* line: Only actual printed text */
- virtual void printLog(enum LogMessageLevel lev, const std::string &line){};
-
- bool silence;
+ void log(LogLevel lev, const std::string &combined,
+ const std::string &time, const std::string &thread_name,
+ const std::string &payload_text)
+ {
+ logRaw(lev, combined);
+ }
};
-void log_add_output(ILogOutput *out, enum LogMessageLevel lev);
-void log_add_output_maxlev(ILogOutput *out, enum LogMessageLevel lev);
-void log_add_output_all_levs(ILogOutput *out);
-void log_remove_output(ILogOutput *out);
-void log_set_lev_silence(enum LogMessageLevel lev, bool silence);
+class StreamLogOutput : public ICombinedLogOutput {
+public:
+ StreamLogOutput(std::ostream &stream) :
+ m_stream(stream)
+ {
+ }
+
+ void logRaw(LogLevel lev, const std::string &line)
+ {
+ m_stream << line << std::endl;
+ }
+
+private:
+ std::ostream &m_stream;
+};
-void log_register_thread(const std::string &name);
-void log_deregister_thread();
+class FileLogOutput : public ICombinedLogOutput {
+public:
+ void open(const std::string &filename);
-void log_printline(enum LogMessageLevel lev, const std::string &text);
+ void logRaw(LogLevel lev, const std::string &line)
+ {
+ m_stream << line << std::endl;
+ }
-#define LOGLINEF(lev, ...)\
-{\
- char buf[10000];\
- snprintf(buf, 10000, __VA_ARGS__);\
- log_printline(lev, buf);\
-}
+private:
+ std::ofstream m_stream;
+};
-extern std::ostream errorstream;
-extern std::ostream actionstream;
-extern std::ostream infostream;
-extern std::ostream verbosestream;
+class LogOutputBuffer : public ICombinedLogOutput {
+public:
+ LogOutputBuffer(Logger &logger, LogLevel lev) :
+ m_logger(logger)
+ {
+ m_logger.addOutput(this, lev);
+ }
+
+ ~LogOutputBuffer()
+ {
+ m_logger.removeOutput(this);
+ }
+
+ void logRaw(LogLevel lev, const std::string &line)
+ {
+ m_buffer.push(line);
+ }
+
+ bool empty()
+ {
+ return m_buffer.empty();
+ }
+
+ std::string get()
+ {
+ if (empty())
+ return "";
+ std::string s = m_buffer.front();
+ m_buffer.pop();
+ return s;
+ }
+
+private:
+ std::queue<std::string> m_buffer;
+ Logger &m_logger;
+};
-extern bool log_trace_level_enabled;
-#define TRACESTREAM(x){ if(log_trace_level_enabled) verbosestream x; }
-#define TRACEDO(x){ if(log_trace_level_enabled){ x ;} }
+extern StreamLogOutput stdout_output;
+extern StreamLogOutput stderr_output;
+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.
+extern std::ostream rawstream;
+
+extern std::ostream errorstream;
+extern std::ostream warningstream;
+extern std::ostream actionstream;
+extern std::ostream infostream;
+extern std::ostream verbosestream;
+extern std::ostream dstream;
+
+#define TRACEDO(x) do { \
+ if (g_logger.getTraceEnabled()) { \
+ x; \
+ } \
+} while (0)
+
+#define TRACESTREAM(x) TRACEDO(verbosestream x)
+
#define dout_con (*dout_con_ptr)
#define derr_con (*derr_con_ptr)
#define dout_server (*dout_server_ptr)
#define derr_server (*derr_server_ptr)
#ifndef SERVER
-extern std::ostream *dout_client_ptr;
-extern std::ostream *derr_client_ptr;
-#define dout_client (*dout_client_ptr)
-#define derr_client (*derr_client_ptr)
-
+ #define dout_client (*dout_client_ptr)
+ #define derr_client (*derr_client_ptr)
#endif
-#endif
+#endif
diff --git a/src/luaentity_common.h b/src/luaentity_common.h
deleted file mode 100644
index 35b079ade..000000000
--- a/src/luaentity_common.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
-Minetest
-Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@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.
-*/
-
-#ifndef LUAENTITY_COMMON_HEADER
-#define LUAENTITY_COMMON_HEADER
-
-#define LUAENTITY_CMD_UPDATE_POSITION 0
-#define LUAENTITY_CMD_SET_TEXTURE_MOD 1
-#define LUAENTITY_CMD_SET_SPRITE 2
-#define LUAENTITY_CMD_PUNCHED 3
-#define LUAENTITY_CMD_UPDATE_ARMOR_GROUPS 4
-
-#endif
-
diff --git a/src/main.cpp b/src/main.cpp
index 1d73b4025..1b95a9f1c 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -18,11 +18,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
*/
#ifdef _MSC_VER
-#ifndef SERVER // Dedicated server isn't linked with Irrlicht
- #pragma comment(lib, "Irrlicht.lib")
- // This would get rid of the console window
- //#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
-#endif
+ #ifndef SERVER // Dedicated server isn't linked with Irrlicht
+ #pragma comment(lib, "Irrlicht.lib")
+ // This would get rid of the console window
+ //#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
+ #endif
#pragma comment(lib, "zlibwapi.lib")
#pragma comment(lib, "Shell32.lib")
#endif
@@ -45,16 +45,28 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "httpfetch.h"
#include "guiEngine.h"
#include "map.h"
+#include "player.h"
#include "mapsector.h"
#include "fontengine.h"
#include "gameparams.h"
#include "database.h"
+#include "config.h"
+#if USE_CURSES
+ #include "terminal_chat_console.h"
+#endif
#ifndef SERVER
#include "client/clientlauncher.h"
#endif
#ifdef HAVE_TOUCHSCREENGUI
-#include "touchscreengui.h"
+ #include "touchscreengui.h"
+#endif
+
+#if !defined(SERVER) && \
+ (IRRLICHT_VERSION_MAJOR == 1) && \
+ (IRRLICHT_VERSION_MINOR == 8) && \
+ (IRRLICHT_VERSION_REVISION == 2)
+ #error "Irrlicht 1.8.2 is known to be broken - please update Irrlicht to version >= 1.8.3"
#endif
#define DEBUGFILE "debug.txt"
@@ -80,10 +92,10 @@ static void list_game_ids();
static void list_worlds();
static void setup_log_params(const Settings &cmd_args);
static bool create_userdata_path();
-static bool init_common(int *log_level, const Settings &cmd_args, int argc, char *argv[]);
+static bool init_common(const Settings &cmd_args, int argc, char *argv[]);
static void startup_message();
static bool read_config_file(const Settings &cmd_args);
-static void init_debug_streams(int *log_level, const Settings &cmd_args);
+static void init_log_streams(const Settings &cmd_args);
static bool game_configure(GameParams *game_params, const Settings &cmd_args);
static void game_configure_port(GameParams *game_params, const Settings &cmd_args);
@@ -122,25 +134,7 @@ u32 getTime(TimePrecision prec)
#endif
-class StderrLogOutput: public ILogOutput
-{
-public:
- /* line: Full line with timestamp, level and thread */
- void printLog(const std::string &line)
- {
- std::cerr << line << std::endl;
- }
-} main_stderr_log_out;
-
-class DstreamNoStderrLogOutput: public ILogOutput
-{
-public:
- /* line: Full line with timestamp, level and thread */
- void printLog(const std::string &line)
- {
- dstream_no_stderr << line << std::endl;
- }
-} main_dstream_no_stderr_log_out;
+FileLogOutput file_log_output;
static OptionList allowed_options;
@@ -150,10 +144,8 @@ int main(int argc, char *argv[])
debug_set_exception_handler();
- log_add_output_maxlev(&main_stderr_log_out, LMT_ACTION);
- log_add_output_all_levs(&main_dstream_no_stderr_log_out);
-
- log_register_thread("main");
+ g_logger.registerThread("Main");
+ g_logger.addOutputMaxLevel(&stderr_output, LL_ACTION);
Settings cmd_args;
bool cmd_args_ok = get_cmdline_opts(argc, argv, &cmd_args);
@@ -172,7 +164,13 @@ int main(int argc, char *argv[])
setup_log_params(cmd_args);
porting::signal_handler_init();
+
+#ifdef __ANDROID__
+ porting::initAndroid();
+ porting::initializePathsAndroid();
+#else
porting::initializePaths();
+#endif
if (!create_userdata_path()) {
errorstream << "Cannot create user data directory" << std::endl;
@@ -180,8 +178,7 @@ int main(int argc, char *argv[])
}
// Initialize debug stacks
- debug_stacks_init();
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
// Debug handler
BEGIN_DEBUG_EXCEPTION_HANDLER
@@ -198,18 +195,17 @@ int main(int argc, char *argv[])
return 0;
}
- GameParams game_params;
- if (!init_common(&game_params.log_level, cmd_args, argc, argv))
+ if (!init_common(cmd_args, argc, argv))
return 1;
#ifndef __ANDROID__
// Run unit tests
if (cmd_args.getFlag("run-unittests")) {
- run_tests();
- return 0;
+ return run_tests();
}
#endif
+ GameParams game_params;
#ifdef SERVER
game_params.is_dedicated_server = true;
#else
@@ -219,7 +215,7 @@ int main(int argc, char *argv[])
if (!game_configure(&game_params, cmd_args))
return 1;
- sanity_check(game_params.world_path != "");
+ sanity_check(!game_params.world_path.empty());
infostream << "Using commanded world path ["
<< game_params.world_path << "]" << std::endl;
@@ -247,7 +243,7 @@ int main(int argc, char *argv[])
// Stop httpfetch thread (if started)
httpfetch_cleanup();
- END_DEBUG_EXCEPTION_HANDLER(errorstream)
+ END_DEBUG_EXCEPTION_HANDLER
return retval;
}
@@ -299,6 +295,8 @@ static void set_allowed_options(OptionList *allowed_options)
_("Set gameid (\"--gameid list\" prints available ones)"))));
allowed_options->insert(std::make_pair("migrate", ValueSpec(VALUETYPE_STRING,
_("Migrate from current map backend to another (Only works when using minetestserver or with --server)"))));
+ allowed_options->insert(std::make_pair("terminal", ValueSpec(VALUETYPE_FLAG,
+ _("Feature an interactive terminal (Only works when using minetestserver or with --server)"))));
#ifndef SERVER
allowed_options->insert(std::make_pair("videomodes", ValueSpec(VALUETYPE_FLAG,
_("Show available video modes"))));
@@ -322,7 +320,7 @@ static void set_allowed_options(OptionList *allowed_options)
static void print_help(const OptionList &allowed_options)
{
- dstream << _("Allowed options:") << std::endl;
+ std::cout << _("Allowed options:") << std::endl;
print_allowed_options(allowed_options);
}
@@ -335,37 +333,38 @@ static void print_allowed_options(const OptionList &allowed_options)
if (i->second.type != VALUETYPE_FLAG)
os1 << _(" <value>");
- dstream << padStringRight(os1.str(), 24);
+ std::cout << padStringRight(os1.str(), 24);
if (i->second.help != NULL)
- dstream << i->second.help;
+ std::cout << i->second.help;
- dstream << std::endl;
+ std::cout << std::endl;
}
}
static void print_version()
{
- dstream << PROJECT_NAME_C " " << g_version_hash << std::endl;
+ std::cout << PROJECT_NAME_C " " << g_version_hash
+ << " (" << porting::getPlatformName() << ")" << std::endl;
#ifndef SERVER
- dstream << "Using Irrlicht " << IRRLICHT_SDK_VERSION << std::endl;
+ std::cout << "Using Irrlicht " << IRRLICHT_SDK_VERSION << std::endl;
#endif
- dstream << "Build info: " << g_build_info << std::endl;
+ std::cout << "Build info: " << g_build_info << std::endl;
}
static void list_game_ids()
{
std::set<std::string> gameids = getAvailableGameIds();
for (std::set<std::string>::const_iterator i = gameids.begin();
- i != gameids.end(); i++)
- dstream << (*i) <<std::endl;
+ i != gameids.end(); ++i)
+ std::cout << (*i) <<std::endl;
}
static void list_worlds()
{
- dstream << _("Available worlds:") << std::endl;
+ std::cout << _("Available worlds:") << std::endl;
std::vector<WorldSpec> worldspecs = getAvailableWorlds();
- print_worldspecs(worldspecs, dstream);
+ print_worldspecs(worldspecs, std::cout);
}
static void print_worldspecs(const std::vector<WorldSpec> &worldspecs,
@@ -403,26 +402,26 @@ static void setup_log_params(const Settings &cmd_args)
{
// Quiet mode, print errors only
if (cmd_args.getFlag("quiet")) {
- log_remove_output(&main_stderr_log_out);
- log_add_output_maxlev(&main_stderr_log_out, LMT_ERROR);
+ g_logger.removeOutput(&stderr_output);
+ g_logger.addOutputMaxLevel(&stderr_output, LL_ERROR);
}
// If trace is enabled, enable logging of certain things
if (cmd_args.getFlag("trace")) {
dstream << _("Enabling trace level debug output") << std::endl;
- log_trace_level_enabled = true;
- dout_con_ptr = &verbosestream; // this is somewhat old crap
- socket_enable_debug_output = true; // socket doesn't use log.h
+ g_logger.setTraceEnabled(true);
+ dout_con_ptr = &verbosestream; // This is somewhat old
+ socket_enable_debug_output = true; // Sockets doesn't use log.h
}
// In certain cases, output info level on stderr
if (cmd_args.getFlag("info") || cmd_args.getFlag("verbose") ||
cmd_args.getFlag("trace") || cmd_args.getFlag("speedtests"))
- log_add_output(&main_stderr_log_out, LMT_INFO);
+ g_logger.addOutput(&stderr_output, LL_INFO);
// In certain cases, output verbose level on stderr
if (cmd_args.getFlag("verbose") || cmd_args.getFlag("trace"))
- log_add_output(&main_stderr_log_out, LMT_VERBOSE);
+ g_logger.addOutput(&stderr_output, LL_VERBOSE);
}
static bool create_userdata_path()
@@ -430,9 +429,6 @@ static bool create_userdata_path()
bool success;
#ifdef __ANDROID__
- porting::initAndroid();
-
- porting::setExternalStorageDir(porting::jnienv);
if (!fs::PathExists(porting::path_user)) {
success = fs::CreateDir(porting::path_user);
} else {
@@ -444,13 +440,10 @@ static bool create_userdata_path()
success = fs::CreateDir(porting::path_user);
#endif
- infostream << "path_share = " << porting::path_share << std::endl;
- infostream << "path_user = " << porting::path_user << std::endl;
-
return success;
}
-static bool init_common(int *log_level, const Settings &cmd_args, int argc, char *argv[])
+static bool init_common(const Settings &cmd_args, int argc, char *argv[])
{
startup_message();
set_default_settings(g_settings);
@@ -462,7 +455,7 @@ static bool init_common(int *log_level, const Settings &cmd_args, int argc, char
if (!read_config_file(cmd_args))
return false;
- init_debug_streams(log_level, cmd_args);
+ init_log_streams(cmd_args);
// Initialize random seed
srand(time(0));
@@ -471,13 +464,8 @@ static bool init_common(int *log_level, const Settings &cmd_args, int argc, char
// Initialize HTTP fetcher
httpfetch_init(g_settings->getS32("curl_parallel_limit"));
-#ifdef _MSC_VER
- init_gettext((porting::path_share + DIR_DELIM + "locale").c_str(),
+ init_gettext(porting::path_locale.c_str(),
g_settings->get("language"), argc, argv);
-#else
- init_gettext((porting::path_share + DIR_DELIM + "locale").c_str(),
- g_settings->get("language"));
-#endif
return true;
}
@@ -533,38 +521,47 @@ static bool read_config_file(const Settings &cmd_args)
return true;
}
-static void init_debug_streams(int *log_level, const Settings &cmd_args)
+static void init_log_streams(const Settings &cmd_args)
{
#if RUN_IN_PLACE
- std::string logfile = DEBUGFILE;
+ std::string log_filename = DEBUGFILE;
#else
- std::string logfile = porting::path_user + DIR_DELIM + DEBUGFILE;
+ std::string log_filename = porting::path_user + DIR_DELIM + DEBUGFILE;
#endif
if (cmd_args.exists("logfile"))
- logfile = cmd_args.get("logfile");
-
- log_remove_output(&main_dstream_no_stderr_log_out);
- *log_level = g_settings->getS32("debug_log_level");
-
- if (*log_level == 0) //no logging
- logfile = "";
- if (*log_level < 0) {
- dstream << "WARNING: Supplied debug_log_level < 0; Using 0" << std::endl;
- *log_level = 0;
- } else if (*log_level > LMT_NUM_VALUES) {
- dstream << "WARNING: Supplied debug_log_level > " << LMT_NUM_VALUES
- << "; Using " << LMT_NUM_VALUES << std::endl;
- *log_level = LMT_NUM_VALUES;
+ log_filename = cmd_args.get("logfile");
+
+ g_logger.removeOutput(&file_log_output);
+ std::string conf_loglev = g_settings->get("debug_log_level");
+
+ // Old integer format
+ if (std::isdigit(conf_loglev[0])) {
+ warningstream << "Deprecated use of debug_log_level with an "
+ "integer value; please update your configuration." << std::endl;
+ static const char *lev_name[] =
+ {"", "error", "action", "info", "verbose"};
+ int lev_i = atoi(conf_loglev.c_str());
+ if (lev_i < 0 || lev_i >= (int)ARRLEN(lev_name)) {
+ warningstream << "Supplied invalid debug_log_level!"
+ " Assuming action level." << std::endl;
+ lev_i = 2;
+ }
+ conf_loglev = lev_name[lev_i];
}
- log_add_output_maxlev(&main_dstream_no_stderr_log_out,
- (LogMessageLevel)(*log_level - 1));
+ if (log_filename.empty() || conf_loglev.empty()) // No logging
+ return;
- debugstreams_init(false, logfile == "" ? NULL : logfile.c_str());
+ LogLevel log_level = Logger::stringToLevel(conf_loglev);
+ if (log_level == LL_MAX) {
+ warningstream << "Supplied unrecognized debug_log_level; "
+ "using maximum." << std::endl;
+ }
- infostream << "logfile = " << logfile << std::endl;
+ verbosestream << "log_filename = " << log_filename << std::endl;
- atexit(debugstreams_deinit);
+ file_log_output.open(log_filename.c_str());
+ g_logger.addOutputMaxLevel(&file_log_output, log_level);
}
static bool game_configure(GameParams *game_params, const Settings &cmd_args)
@@ -678,10 +675,10 @@ static bool auto_select_world(GameParams *game_params)
<< world_path << "]" << std::endl;
// If there are multiple worlds, list them
} else if (worldspecs.size() > 1 && game_params->is_dedicated_server) {
- dstream << _("Multiple worlds are available.") << std::endl;
- dstream << _("Please select one using --worldname <name>"
+ std::cerr << _("Multiple worlds are available.") << std::endl;
+ std::cerr << _("Please select one using --worldname <name>"
" or --world <path>") << std::endl;
- print_worldspecs(worldspecs, dstream);
+ print_worldspecs(worldspecs, std::cerr);
return false;
// If there are no worlds, automatically create a new one
} else {
@@ -774,7 +771,7 @@ static bool determine_subgame(GameParams *game_params)
if (game_params->game_spec.isValid()) {
gamespec = game_params->game_spec;
if (game_params->game_spec.id != world_gameid) {
- errorstream << "WARNING: Using commanded gameid ["
+ warningstream << "Using commanded gameid ["
<< gamespec.id << "]" << " instead of world gameid ["
<< world_gameid << "]" << std::endl;
}
@@ -834,15 +831,84 @@ static bool run_dedicated_server(const GameParams &game_params, const Settings &
if (cmd_args.exists("migrate"))
return migrate_database(game_params, cmd_args);
- // Create server
- Server server(game_params.world_path,
- game_params.game_spec, false, bind_addr.isIPv6());
+ if (cmd_args.exists("terminal")) {
+#if USE_CURSES
+ bool name_ok = true;
+ std::string admin_nick = g_settings->get("name");
+
+ name_ok = name_ok && !admin_nick.empty();
+ name_ok = name_ok && string_allowed(admin_nick, PLAYERNAME_ALLOWED_CHARS);
+
+ if (!name_ok) {
+ if (admin_nick.empty()) {
+ errorstream << "No name given for admin. "
+ << "Please check your minetest.conf that it "
+ << "contains a 'name = ' to your main admin account."
+ << std::endl;
+ } else {
+ errorstream << "Name for admin '"
+ << admin_nick << "' is not valid. "
+ << "Please check that it only contains allowed characters. "
+ << "Valid characters are: " << PLAYERNAME_ALLOWED_CHARS_USER_EXPL
+ << std::endl;
+ }
+ return false;
+ }
+ ChatInterface iface;
+ bool &kill = *porting::signal_handler_killstatus();
- server.start(bind_addr);
+ try {
+ // Create server
+ Server server(game_params.world_path,
+ game_params.game_spec, false, bind_addr.isIPv6(), &iface);
- // Run server
- bool &kill = *porting::signal_handler_killstatus();
- dedicated_server_loop(server, kill);
+ g_term_console.setup(&iface, &kill, admin_nick);
+
+ g_term_console.start();
+
+ server.start(bind_addr);
+ // Run server
+ dedicated_server_loop(server, kill);
+ } catch (const ModError &e) {
+ g_term_console.stopAndWaitforThread();
+ errorstream << "ModError: " << e.what() << std::endl;
+ return false;
+ } catch (const ServerError &e) {
+ g_term_console.stopAndWaitforThread();
+ errorstream << "ServerError: " << e.what() << std::endl;
+ return false;
+ }
+
+ // Tell the console to stop, and wait for it to finish,
+ // only then leave context and free iface
+ g_term_console.stop();
+ g_term_console.wait();
+
+ g_term_console.clearKillStatus();
+ } else {
+#else
+ errorstream << "Cmd arg --terminal passed, but "
+ << "compiled without ncurses. Ignoring." << std::endl;
+ } {
+#endif
+ try {
+ // Create server
+ Server server(game_params.world_path, game_params.game_spec, false,
+ bind_addr.isIPv6());
+ server.start(bind_addr);
+
+ // Run server
+ bool &kill = *porting::signal_handler_killstatus();
+ dedicated_server_loop(server, kill);
+
+ } catch (const ModError &e) {
+ errorstream << "ModError: " << e.what() << std::endl;
+ return false;
+ } catch (const ServerError &e) {
+ errorstream << "ServerError: " << e.what() << std::endl;
+ return false;
+ }
+ }
return true;
}
diff --git a/src/mainmenumanager.h b/src/mainmenumanager.h
index 6f8aa9137..17133b164 100644
--- a/src/mainmenumanager.h
+++ b/src/mainmenumanager.h
@@ -47,9 +47,9 @@ extern gui::IGUIStaticText *guiroot;
class MainMenuManager : public IMenuManager
{
public:
- virtual void createdMenu(GUIModalMenu *menu)
+ virtual void createdMenu(gui::IGUIElement *menu)
{
- for(std::list<GUIModalMenu*>::iterator
+ for(std::list<gui::IGUIElement*>::iterator
i = m_stack.begin();
i != m_stack.end(); ++i)
{
@@ -61,13 +61,13 @@ public:
m_stack.push_back(menu);
}
- virtual void deletingMenu(GUIModalMenu *menu)
+ virtual void deletingMenu(gui::IGUIElement *menu)
{
// Remove all entries if there are duplicates
bool removed_entry;
do{
removed_entry = false;
- for(std::list<GUIModalMenu*>::iterator
+ for(std::list<gui::IGUIElement*>::iterator
i = m_stack.begin();
i != m_stack.end(); ++i)
{
@@ -91,10 +91,10 @@ public:
// Returns true to prevent further processing
virtual bool preprocessEvent(const SEvent& event)
{
- if(!m_stack.empty())
- return m_stack.back()->preprocessEvent(event);
- else
+ if (m_stack.empty())
return false;
+ GUIModalMenu *mm = dynamic_cast<GUIModalMenu*>(m_stack.back());
+ return mm && mm->preprocessEvent(event);
}
u32 menuCount()
@@ -104,16 +104,17 @@ public:
bool pausesGame()
{
- for(std::list<GUIModalMenu*>::iterator
+ for(std::list<gui::IGUIElement*>::iterator
i = m_stack.begin(); i != m_stack.end(); ++i)
{
- if((*i)->pausesGame())
+ GUIModalMenu *mm = dynamic_cast<GUIModalMenu*>(*i);
+ if (mm && mm->pausesGame())
return true;
}
return false;
}
- std::list<GUIModalMenu*> m_stack;
+ std::list<gui::IGUIElement*> m_stack;
};
extern MainMenuManager g_menumgr;
diff --git a/src/map.cpp b/src/map.cpp
index 76a558d43..66fabaf87 100644
--- a/src/map.cpp
+++ b/src/map.cpp
@@ -673,7 +673,7 @@ void Map::updateLighting(enum LightBank bank,
{
INodeDefManager *nodemgr = m_gamedef->ndef();
- /*m_dout<<DTIME<<"Map::updateLighting(): "
+ /*m_dout<<"Map::updateLighting(): "
<<a_blocks.size()<<" blocks."<<std::endl;*/
//TimeTaker timer("updateLighting");
@@ -928,7 +928,7 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n,
INodeDefManager *ndef = m_gamedef->ndef();
/*PrintInfo(m_dout);
- m_dout<<DTIME<<"Map::addNodeAndUpdate(): p=("
+ m_dout<<"Map::addNodeAndUpdate(): p=("
<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
/*
@@ -1028,7 +1028,7 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n,
{
s16 y = p.Y - 1;
for(;; y--){
- //m_dout<<DTIME<<"y="<<y<<std::endl;
+ //m_dout<<"y="<<y<<std::endl;
v3s16 n2pos(p.X, y, p.Z);
MapNode n2;
@@ -1115,7 +1115,7 @@ void Map::removeNodeAndUpdate(v3s16 p,
INodeDefManager *ndef = m_gamedef->ndef();
/*PrintInfo(m_dout);
- m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
+ m_dout<<"Map::removeNodeAndUpdate(): p=("
<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
bool node_under_sunlight = true;
@@ -1197,14 +1197,14 @@ void Map::removeNodeAndUpdate(v3s16 p,
if(node_under_sunlight)
{
s16 ybottom = propagateSunlight(p, modified_blocks);
- /*m_dout<<DTIME<<"Node was under sunlight. "
+ /*m_dout<<"Node was under sunlight. "
"Propagating sunlight";
- m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
+ m_dout<<" -> ybottom="<<ybottom<<std::endl;*/
s16 y = p.Y;
for(; y >= ybottom; y--)
{
v3s16 p2(p.X, y, p.Z);
- /*m_dout<<DTIME<<"lighting neighbors of node ("
+ /*m_dout<<"lighting neighbors of node ("
<<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
<<std::endl;*/
lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
@@ -1434,7 +1434,7 @@ void Map::timerUpdate(float dtime, float unload_timeout, u32 max_loaded_blocks,
beginSave();
// If there is no practical limit, we spare creation of mapblock_queue
- if (max_loaded_blocks == (u32)-1) {
+ if (max_loaded_blocks == U32_MAX) {
for (std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
si != m_sectors.end(); ++si) {
MapSector *sector = si->second;
@@ -1614,12 +1614,12 @@ s32 Map::transforming_liquid_size() {
return m_transforming_liquid.size();
}
-void Map::transformLiquids(std::map<v3s16, MapBlock*> & modified_blocks)
+void Map::transformLiquids(std::map<v3s16, MapBlock*> &modified_blocks)
{
INodeDefManager *nodemgr = m_gamedef->ndef();
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
//TimeTaker timer("transformLiquids()");
u32 loopcount = 0;
@@ -1632,7 +1632,7 @@ void Map::transformLiquids(std::map<v3s16, MapBlock*> & modified_blocks)
std::deque<v3s16> must_reflow;
// List of MapBlocks that will require a lighting update (due to lava)
- std::map<v3s16, MapBlock*> lighting_modified_blocks;
+ std::map<v3s16, MapBlock *> lighting_modified_blocks;
u32 liquid_loop_max = g_settings->getS32("liquid_loop_max");
u32 loop_max = liquid_loop_max;
@@ -1654,10 +1654,10 @@ void Map::transformLiquids(std::map<v3s16, MapBlock*> & modified_blocks)
loop_max *= m_transforming_liquid_loop_count_multiplier;
#endif
- while(m_transforming_liquid.size() != 0)
+ while (m_transforming_liquid.size() != 0)
{
// This should be done here so that it is done when continue is used
- if(loopcount >= initial_size || loopcount >= loop_max)
+ if (loopcount >= initial_size || loopcount >= loop_max)
break;
loopcount++;
@@ -1674,21 +1674,24 @@ void Map::transformLiquids(std::map<v3s16, MapBlock*> & modified_blocks)
*/
s8 liquid_level = -1;
content_t liquid_kind = CONTENT_IGNORE;
- LiquidType liquid_type = nodemgr->get(n0).liquid_type;
+ content_t floodable_node = CONTENT_AIR;
+ const ContentFeatures &cf = nodemgr->get(n0);
+ LiquidType liquid_type = cf.liquid_type;
switch (liquid_type) {
case LIQUID_SOURCE:
liquid_level = LIQUID_LEVEL_SOURCE;
- liquid_kind = nodemgr->getId(nodemgr->get(n0).liquid_alternative_flowing);
+ liquid_kind = nodemgr->getId(cf.liquid_alternative_flowing);
break;
case LIQUID_FLOWING:
liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
liquid_kind = n0.getContent();
break;
case LIQUID_NONE:
- // if this is an air node, it *could* be transformed into a liquid. otherwise,
- // continue with the next node.
- if (n0.getContent() != CONTENT_AIR)
+ // if this node is 'floodable', it *could* be transformed
+ // into a liquid, otherwise, continue with the next node.
+ if (!cf.floodable)
continue;
+ floodable_node = n0.getContent();
liquid_kind = CONTENT_AIR;
break;
}
@@ -1718,9 +1721,10 @@ void Map::transformLiquids(std::map<v3s16, MapBlock*> & modified_blocks)
}
v3s16 npos = p0 + dirs[i];
NodeNeighbor nb(getNodeNoEx(npos), nt, npos);
+ const ContentFeatures &cfnb = nodemgr->get(nb.n);
switch (nodemgr->get(nb.n.getContent()).liquid_type) {
case LIQUID_NONE:
- if (nb.n.getContent() == CONTENT_AIR) {
+ if (cfnb.floodable) {
airs[num_airs++] = nb;
// if the current node is a water source the neighbor
// should be enqueded for transformation regardless of whether the
@@ -1728,18 +1732,21 @@ void Map::transformLiquids(std::map<v3s16, MapBlock*> & modified_blocks)
if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
m_transforming_liquid.push_back(npos);
// if the current node happens to be a flowing node, it will start to flow down here.
- if (nb.t == NEIGHBOR_LOWER) {
+ if (nb.t == NEIGHBOR_LOWER)
flowing_down = true;
- }
} else {
neutrals[num_neutrals++] = nb;
+ // If neutral below is ignore prevent water spreading outwards
+ if (nb.t == NEIGHBOR_LOWER &&
+ nb.n.getContent() == CONTENT_IGNORE)
+ flowing_down = true;
}
break;
case LIQUID_SOURCE:
// if this node is not (yet) of a liquid type, choose the first liquid type we encounter
if (liquid_kind == CONTENT_AIR)
- liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
- if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
+ liquid_kind = nodemgr->getId(cfnb.liquid_alternative_flowing);
+ if (nodemgr->getId(cfnb.liquid_alternative_flowing) != liquid_kind) {
neutrals[num_neutrals++] = nb;
} else {
// Do not count bottom source, it will screw things up
@@ -1750,8 +1757,8 @@ void Map::transformLiquids(std::map<v3s16, MapBlock*> & modified_blocks)
case LIQUID_FLOWING:
// if this node is not (yet) of a liquid type, choose the first liquid type we encounter
if (liquid_kind == CONTENT_AIR)
- liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
- if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
+ liquid_kind = nodemgr->getId(cfnb.liquid_alternative_flowing);
+ if (nodemgr->getId(cfnb.liquid_alternative_flowing) != liquid_kind) {
neutrals[num_neutrals++] = nb;
} else {
flows[num_flows++] = nb;
@@ -1770,8 +1777,8 @@ void Map::transformLiquids(std::map<v3s16, MapBlock*> & modified_blocks)
s8 max_node_level = -1;
u8 range = nodemgr->get(liquid_kind).liquid_range;
- if (range > LIQUID_LEVEL_MAX+1)
- range = LIQUID_LEVEL_MAX+1;
+ if (range > LIQUID_LEVEL_MAX + 1)
+ range = LIQUID_LEVEL_MAX + 1;
if ((num_sources >= 2 && nodemgr->get(liquid_kind).liquid_renewable) || liquid_type == LIQUID_SOURCE) {
// liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
@@ -1780,10 +1787,11 @@ void Map::transformLiquids(std::map<v3s16, MapBlock*> & modified_blocks)
new_node_content = nodemgr->getId(nodemgr->get(liquid_kind).liquid_alternative_source);
} else if (num_sources >= 1 && sources[0].t != NEIGHBOR_LOWER) {
// liquid_kind is set properly, see above
- new_node_content = liquid_kind;
max_node_level = new_node_level = LIQUID_LEVEL_MAX;
- if (new_node_level < (LIQUID_LEVEL_MAX+1-range))
- new_node_content = CONTENT_AIR;
+ if (new_node_level >= (LIQUID_LEVEL_MAX + 1 - range))
+ new_node_content = liquid_kind;
+ else
+ new_node_content = floodable_node;
} else {
// no surrounding sources, so get the maximum level that can flow into this node
for (u16 i = 0; i < num_flows; i++) {
@@ -1794,16 +1802,16 @@ void Map::transformLiquids(std::map<v3s16, MapBlock*> & modified_blocks)
max_node_level = LIQUID_LEVEL_MAX;
if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
max_node_level = nb_liquid_level + WATER_DROP_BOOST;
- } else if (nb_liquid_level > max_node_level)
+ } else if (nb_liquid_level > max_node_level) {
max_node_level = nb_liquid_level;
+ }
break;
case NEIGHBOR_LOWER:
break;
case NEIGHBOR_SAME_LEVEL:
if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
- nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level) {
+ nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level)
max_node_level = nb_liquid_level - 1;
- }
break;
}
}
@@ -1821,23 +1829,25 @@ void Map::transformLiquids(std::map<v3s16, MapBlock*> & modified_blocks)
new_node_level = liquid_level + 1;
if (new_node_level != max_node_level)
must_reflow.push_back(p0);
- } else
+ } else {
new_node_level = max_node_level;
+ }
- if (max_node_level >= (LIQUID_LEVEL_MAX+1-range))
+ if (max_node_level >= (LIQUID_LEVEL_MAX + 1 - range))
new_node_content = liquid_kind;
else
- new_node_content = CONTENT_AIR;
+ new_node_content = floodable_node;
}
/*
check if anything has changed. if not, just continue with the next node.
*/
- if (new_node_content == n0.getContent() && (nodemgr->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
- ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
- ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
- == flowing_down)))
+ if (new_node_content == n0.getContent() &&
+ (nodemgr->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
+ ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
+ ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
+ == flowing_down)))
continue;
@@ -1857,11 +1867,10 @@ void Map::transformLiquids(std::map<v3s16, MapBlock*> & modified_blocks)
// Find out whether there is a suspect for this action
std::string suspect;
- if(m_gamedef->rollback()) {
+ if (m_gamedef->rollback())
suspect = m_gamedef->rollback()->getSuspect(p0, 83, 1);
- }
- if(m_gamedef->rollback() && !suspect.empty()){
+ if (m_gamedef->rollback() && !suspect.empty()) {
// Blame suspect
RollbackScopeActor rollback_scope(m_gamedef->rollback(), suspect, true);
// Get old node for rollback
@@ -1880,10 +1889,10 @@ void Map::transformLiquids(std::map<v3s16, MapBlock*> & modified_blocks)
v3s16 blockpos = getNodeBlockPos(p0);
MapBlock *block = getBlockNoCreateNoEx(blockpos);
- if(block != NULL) {
+ if (block != NULL) {
modified_blocks[blockpos] = block;
// If new or old node emits light, MapBlock requires lighting update
- if(nodemgr->get(n0).light_source != 0 ||
+ if (nodemgr->get(n0).light_source != 0 ||
nodemgr->get(n00).light_source != 0)
lighting_modified_blocks[block->getPos()] = block;
}
@@ -2018,7 +2027,7 @@ NodeMetadata *Map::getNodeMetadata(v3s16 p)
block = emergeBlock(blockpos, false);
}
if(!block){
- infostream<<"WARNING: Map::getNodeMetadata(): Block not found"
+ warningstream<<"Map::getNodeMetadata(): Block not found"
<<std::endl;
return NULL;
}
@@ -2037,7 +2046,7 @@ bool Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
block = emergeBlock(blockpos, false);
}
if(!block){
- infostream<<"WARNING: Map::setNodeMetadata(): Block not found"
+ warningstream<<"Map::setNodeMetadata(): Block not found"
<<std::endl;
return false;
}
@@ -2052,7 +2061,7 @@ void Map::removeNodeMetadata(v3s16 p)
MapBlock *block = getBlockNoCreateNoEx(blockpos);
if(block == NULL)
{
- infostream<<"WARNING: Map::removeNodeMetadata(): Block not found"
+ warningstream<<"Map::removeNodeMetadata(): Block not found"
<<std::endl;
return;
}
@@ -2070,7 +2079,7 @@ NodeTimer Map::getNodeTimer(v3s16 p)
block = emergeBlock(blockpos, false);
}
if(!block){
- infostream<<"WARNING: Map::getNodeTimer(): Block not found"
+ warningstream<<"Map::getNodeTimer(): Block not found"
<<std::endl;
return NodeTimer();
}
@@ -2089,7 +2098,7 @@ void Map::setNodeTimer(v3s16 p, NodeTimer t)
block = emergeBlock(blockpos, false);
}
if(!block){
- infostream<<"WARNING: Map::setNodeTimer(): Block not found"
+ warningstream<<"Map::setNodeTimer(): Block not found"
<<std::endl;
return;
}
@@ -2103,7 +2112,7 @@ void Map::removeNodeTimer(v3s16 p)
MapBlock *block = getBlockNoCreateNoEx(blockpos);
if(block == NULL)
{
- infostream<<"WARNING: Map::removeNodeTimer(): Block not found"
+ warningstream<<"Map::removeNodeTimer(): Block not found"
<<std::endl;
return;
}
@@ -2118,7 +2127,7 @@ ServerMap::ServerMap(std::string savedir, IGameDef *gamedef, EmergeManager *emer
m_emerge(emerge),
m_map_metadata_changed(true)
{
- verbosestream<<__FUNCTION_NAME<<std::endl;
+ verbosestream<<FUNCTION_NAME<<std::endl;
/*
Try to load map; if not found, create a new one.
@@ -2164,7 +2173,7 @@ ServerMap::ServerMap(std::string savedir, IGameDef *gamedef, EmergeManager *emer
<<" Using default settings."<<std::endl;
}
catch(FileNotGoodException &e){
- infostream<<"WARNING: Could not load map metadata"
+ warningstream<<"Could not load map metadata"
//<<" Disabling chunk-based generator."
<<std::endl;
//m_chunksize = 0;
@@ -2188,10 +2197,10 @@ ServerMap::ServerMap(std::string savedir, IGameDef *gamedef, EmergeManager *emer
}
catch(std::exception &e)
{
- infostream<<"WARNING: ServerMap: Failed to load map from "<<savedir
+ warningstream<<"ServerMap: Failed to load map from "<<savedir
<<", exception: "<<e.what()<<std::endl;
infostream<<"Please remove the map or fix it."<<std::endl;
- infostream<<"WARNING: Map saving will be disabled."<<std::endl;
+ warningstream<<"Map saving will be disabled."<<std::endl;
}
infostream<<"Initializing new map."<<std::endl;
@@ -2205,7 +2214,7 @@ ServerMap::ServerMap(std::string savedir, IGameDef *gamedef, EmergeManager *emer
ServerMap::~ServerMap()
{
- verbosestream<<__FUNCTION_NAME<<std::endl;
+ verbosestream<<FUNCTION_NAME<<std::endl;
try
{
@@ -2254,75 +2263,51 @@ s16 ServerMap::getWaterLevel()
return m_emerge->params.water_level;
}
-bool ServerMap::initBlockMake(BlockMakeData *data, v3s16 blockpos)
+bool ServerMap::initBlockMake(v3s16 blockpos, BlockMakeData *data)
{
- bool enable_mapgen_debug_info = m_emerge->mapgen_debug_info;
- EMERGE_DBG_OUT("initBlockMake(): " PP(blockpos) " - " PP(blockpos));
+ s16 csize = m_emerge->params.chunksize;
+ v3s16 bpmin = EmergeManager::getContainingChunk(blockpos, csize);
+ v3s16 bpmax = bpmin + v3s16(1, 1, 1) * (csize - 1);
- s16 chunksize = m_emerge->params.chunksize;
- s16 coffset = -chunksize / 2;
- v3s16 chunk_offset(coffset, coffset, coffset);
- v3s16 blockpos_div = getContainerPos(blockpos - chunk_offset, chunksize);
- v3s16 blockpos_min = blockpos_div * chunksize;
- v3s16 blockpos_max = blockpos_div * chunksize + v3s16(1,1,1)*(chunksize-1);
- blockpos_min += chunk_offset;
- blockpos_max += chunk_offset;
+ bool enable_mapgen_debug_info = m_emerge->enable_mapgen_debug_info;
+ EMERGE_DBG_OUT("initBlockMake(): " PP(bpmin) " - " PP(bpmax));
- v3s16 extra_borders(1,1,1);
+ v3s16 extra_borders(1, 1, 1);
+ v3s16 full_bpmin = bpmin - extra_borders;
+ v3s16 full_bpmax = bpmax + extra_borders;
// Do nothing if not inside limits (+-1 because of neighbors)
- if(blockpos_over_limit(blockpos_min - extra_borders) ||
- blockpos_over_limit(blockpos_max + extra_borders))
+ if (blockpos_over_limit(full_bpmin) ||
+ blockpos_over_limit(full_bpmax))
return false;
data->seed = m_emerge->params.seed;
- data->blockpos_min = blockpos_min;
- data->blockpos_max = blockpos_max;
+ data->blockpos_min = bpmin;
+ data->blockpos_max = bpmax;
data->blockpos_requested = blockpos;
data->nodedef = m_gamedef->ndef();
/*
Create the whole area of this and the neighboring blocks
*/
- {
- //TimeTaker timer("initBlockMake() create area");
-
- for(s16 x=blockpos_min.X-extra_borders.X;
- x<=blockpos_max.X+extra_borders.X; x++)
- for(s16 z=blockpos_min.Z-extra_borders.Z;
- z<=blockpos_max.Z+extra_borders.Z; z++)
- {
- v2s16 sectorpos(x, z);
- // Sector metadata is loaded from disk if not already loaded.
- ServerMapSector *sector = createSector(sectorpos);
- FATAL_ERROR_IF(sector == NULL, "createSector() failed");
- (void) sector;
-
- for(s16 y=blockpos_min.Y-extra_borders.Y;
- y<=blockpos_max.Y+extra_borders.Y; y++)
- {
- v3s16 p(x,y,z);
- //MapBlock *block = createBlock(p);
- // 1) get from memory, 2) load from disk
- MapBlock *block = emergeBlock(p, false);
- // 3) create a blank one
- if(block == NULL)
- {
- block = createBlock(p);
-
- /*
- Block gets sunlight if this is true.
+ for (s16 x = full_bpmin.X; x <= full_bpmax.X; x++)
+ for (s16 z = full_bpmin.Z; z <= full_bpmax.Z; z++) {
+ v2s16 sectorpos(x, z);
+ // Sector metadata is loaded from disk if not already loaded.
+ ServerMapSector *sector = createSector(sectorpos);
+ FATAL_ERROR_IF(sector == NULL, "createSector() failed");
+
+ for (s16 y = full_bpmin.Y; y <= full_bpmax.Y; y++) {
+ v3s16 p(x, y, z);
- Refer to the map generator heuristics.
- */
- bool ug = m_emerge->isBlockUnderground(p);
- block->setIsUnderground(ug);
- }
+ MapBlock *block = emergeBlock(p, false);
+ if (block == NULL) {
+ block = createBlock(p);
- // Lighting will not be valid after make_chunk is called
- block->setLightingExpired(true);
- // Lighting will be calculated
- //block->setLightingExpired(false);
+ // Block gets sunlight if this is true.
+ // Refer to the map generator heuristics.
+ bool ug = m_emerge->isBlockUnderground(p);
+ block->setIsUnderground(ug);
}
}
}
@@ -2334,21 +2319,14 @@ bool ServerMap::initBlockMake(BlockMakeData *data, v3s16 blockpos)
neighboring blocks
*/
- // The area that contains this block and it's neighbors
- v3s16 bigarea_blocks_min = blockpos_min - extra_borders;
- v3s16 bigarea_blocks_max = blockpos_max + extra_borders;
-
data->vmanip = new MMVManip(this);
- //data->vmanip->setMap(this);
-
- // Add the area
- {
- //TimeTaker timer("initBlockMake() initialEmerge");
- data->vmanip->initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
- }
+ data->vmanip->initialEmerge(full_bpmin, full_bpmax);
- // Ensure none of the blocks to be generated were marked as containing CONTENT_IGNORE
-/* for (s16 z = blockpos_min.Z; z <= blockpos_max.Z; z++) {
+ // Note: we may need this again at some point.
+#if 0
+ // Ensure none of the blocks to be generated were marked as
+ // containing CONTENT_IGNORE
+ for (s16 z = blockpos_min.Z; z <= blockpos_max.Z; z++) {
for (s16 y = blockpos_min.Y; y <= blockpos_max.Y; y++) {
for (s16 x = blockpos_min.X; x <= blockpos_max.X; x++) {
core::map<v3s16, u8>::Node *n;
@@ -2360,124 +2338,62 @@ bool ServerMap::initBlockMake(BlockMakeData *data, v3s16 blockpos)
n->setValue(flags);
}
}
- }*/
+ }
+#endif
// Data is ready now.
return true;
}
void ServerMap::finishBlockMake(BlockMakeData *data,
- std::map<v3s16, MapBlock*> &changed_blocks)
+ std::map<v3s16, MapBlock*> *changed_blocks)
{
- v3s16 blockpos_min = data->blockpos_min;
- v3s16 blockpos_max = data->blockpos_max;
- v3s16 blockpos_requested = data->blockpos_requested;
- /*infostream<<"finishBlockMake(): ("<<blockpos_requested.X<<","
- <<blockpos_requested.Y<<","
- <<blockpos_requested.Z<<")"<<std::endl;*/
+ v3s16 bpmin = data->blockpos_min;
+ v3s16 bpmax = data->blockpos_max;
- v3s16 extra_borders(1,1,1);
+ v3s16 extra_borders(1, 1, 1);
+ v3s16 full_bpmin = bpmin - extra_borders;
+ v3s16 full_bpmax = bpmax + extra_borders;
- bool enable_mapgen_debug_info = m_emerge->mapgen_debug_info;
+ bool enable_mapgen_debug_info = m_emerge->enable_mapgen_debug_info;
+ EMERGE_DBG_OUT("finishBlockMake(): " PP(bpmin) " - " PP(bpmax));
- /*infostream<<"Resulting vmanip:"<<std::endl;
- data->vmanip.print(infostream);*/
+ /*
+ Set lighting to non-expired state in all of them.
+ This is cheating, but it is not fast enough if all of them
+ would actually be updated.
+ */
+ for (s16 x = full_bpmin.X; x <= full_bpmax.X; x++)
+ for (s16 z = full_bpmin.Z; z <= full_bpmax.Z; z++)
+ for (s16 y = full_bpmin.Y; y <= full_bpmax.Y; y++) {
+ MapBlock *block = emergeBlock(v3s16(x, y, z), false);
+ if (!block)
+ continue;
- // Make sure affected blocks are loaded
- for(s16 x=blockpos_min.X-extra_borders.X;
- x<=blockpos_max.X+extra_borders.X; x++)
- for(s16 z=blockpos_min.Z-extra_borders.Z;
- z<=blockpos_max.Z+extra_borders.Z; z++)
- for(s16 y=blockpos_min.Y-extra_borders.Y;
- y<=blockpos_max.Y+extra_borders.Y; y++)
- {
- v3s16 p(x, y, z);
- // Load from disk if not already in memory
- emergeBlock(p, false);
+ block->setLightingExpired(false);
}
/*
Blit generated stuff to map
NOTE: blitBackAll adds nearly everything to changed_blocks
*/
- {
- // 70ms @cs=8
- //TimeTaker timer("finishBlockMake() blitBackAll");
- data->vmanip->blitBackAll(&changed_blocks);
- }
+ data->vmanip->blitBackAll(changed_blocks);
- EMERGE_DBG_OUT("finishBlockMake: changed_blocks.size()=" << changed_blocks.size());
+ EMERGE_DBG_OUT("finishBlockMake: changed_blocks.size()="
+ << changed_blocks->size());
/*
Copy transforming liquid information
*/
- while(data->transforming_liquid.size() > 0)
- {
+ while (data->transforming_liquid.size()) {
m_transforming_liquid.push_back(data->transforming_liquid.front());
data->transforming_liquid.pop_front();
}
- /*
- Do stuff in central blocks
- */
-
- /*
- Update lighting
- */
- {
-#if 0
- TimeTaker t("finishBlockMake lighting update");
-
- core::map<v3s16, MapBlock*> lighting_update_blocks;
-
- // Center blocks
- for(s16 x=blockpos_min.X-extra_borders.X;
- x<=blockpos_max.X+extra_borders.X; x++)
- for(s16 z=blockpos_min.Z-extra_borders.Z;
- z<=blockpos_max.Z+extra_borders.Z; z++)
- for(s16 y=blockpos_min.Y-extra_borders.Y;
- y<=blockpos_max.Y+extra_borders.Y; y++)
- {
- v3s16 p(x, y, z);
- MapBlock *block = getBlockNoCreateNoEx(p);
- assert(block);
- lighting_update_blocks.insert(block->getPos(), block);
- }
-
- updateLighting(lighting_update_blocks, changed_blocks);
-#endif
-
- /*
- Set lighting to non-expired state in all of them.
- This is cheating, but it is not fast enough if all of them
- would actually be updated.
- */
- for(s16 x=blockpos_min.X-extra_borders.X;
- x<=blockpos_max.X+extra_borders.X; x++)
- for(s16 z=blockpos_min.Z-extra_borders.Z;
- z<=blockpos_max.Z+extra_borders.Z; z++)
- for(s16 y=blockpos_min.Y-extra_borders.Y;
- y<=blockpos_max.Y+extra_borders.Y; y++)
- {
- v3s16 p(x, y, z);
- MapBlock * block = getBlockNoCreateNoEx(p);
- if (block != NULL)
- block->setLightingExpired(false);
- }
-
-#if 0
- if(enable_mapgen_debug_info == false)
- t.stop(true); // Hide output
-#endif
- }
-
- /*
- Go through changed blocks
- */
- for(std::map<v3s16, MapBlock*>::iterator i = changed_blocks.begin();
- i != changed_blocks.end(); ++i)
- {
- MapBlock *block = i->second;
+ for (std::map<v3s16, MapBlock *>::iterator
+ it = changed_blocks->begin();
+ it != changed_blocks->end(); ++it) {
+ MapBlock *block = it->second;
if (!block)
continue;
/*
@@ -2494,14 +2410,13 @@ void ServerMap::finishBlockMake(BlockMakeData *data,
/*
Set central blocks as generated
*/
- for(s16 x=blockpos_min.X; x<=blockpos_max.X; x++)
- for(s16 z=blockpos_min.Z; z<=blockpos_max.Z; z++)
- for(s16 y=blockpos_min.Y; y<=blockpos_max.Y; y++)
- {
- v3s16 p(x, y, z);
- MapBlock *block = getBlockNoCreateNoEx(p);
+ for (s16 x = bpmin.X; x <= bpmax.X; x++)
+ for (s16 z = bpmin.Z; z <= bpmax.Z; z++)
+ for (s16 y = bpmin.Y; y <= bpmax.Y; y++) {
+ MapBlock *block = getBlockNoCreateNoEx(v3s16(x, y, z));
if (!block)
continue;
+
block->setGenerated(true);
}
@@ -2510,42 +2425,12 @@ void ServerMap::finishBlockMake(BlockMakeData *data,
NOTE: Will be saved later.
*/
//save(MOD_STATE_WRITE_AT_UNLOAD);
-
- /*infostream<<"finishBlockMake() done for ("<<blockpos_requested.X
- <<","<<blockpos_requested.Y<<","
- <<blockpos_requested.Z<<")"<<std::endl;*/
-
-
-#if 0
- if(enable_mapgen_debug_info)
- {
- /*
- Analyze resulting blocks
- */
- /*for(s16 x=blockpos_min.X-1; x<=blockpos_max.X+1; x++)
- for(s16 z=blockpos_min.Z-1; z<=blockpos_max.Z+1; z++)
- for(s16 y=blockpos_min.Y-1; y<=blockpos_max.Y+1; y++)*/
- for(s16 x=blockpos_min.X-0; x<=blockpos_max.X+0; x++)
- for(s16 z=blockpos_min.Z-0; z<=blockpos_max.Z+0; z++)
- for(s16 y=blockpos_min.Y-0; y<=blockpos_max.Y+0; y++)
- {
- v3s16 p = v3s16(x,y,z);
- MapBlock *block = getBlockNoCreateNoEx(p);
- char spos[20];
- snprintf(spos, 20, "(%2d,%2d,%2d)", x, y, z);
- infostream<<"Generated "<<spos<<": "
- <<analyze_block(block)<<std::endl;
- }
- }
-#endif
-
- getBlockNoCreateNoEx(blockpos_requested);
}
-ServerMapSector * ServerMap::createSector(v2s16 p2d)
+ServerMapSector *ServerMap::createSector(v2s16 p2d)
{
DSTACKF("%s: p2d=(%d,%d)",
- __FUNCTION_NAME,
+ FUNCTION_NAME,
p2d.X, p2d.Y);
/*
@@ -2612,7 +2497,7 @@ MapBlock * ServerMap::generateBlock(
std::map<v3s16, MapBlock*> &modified_blocks
)
{
- DSTACKF("%s: p=(%d,%d,%d)", __FUNCTION_NAME, p.X, p.Y, p.Z);
+ DSTACKF("%s: p=(%d,%d,%d)", FUNCTION_NAME, p.X, p.Y, p.Z);
/*infostream<<"generateBlock(): "
<<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
@@ -2632,7 +2517,7 @@ MapBlock * ServerMap::generateBlock(
*/
if(blockpos_over_limit(p))
{
- infostream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
+ infostream<<FUNCTION_NAME<<": Block position over limit"<<std::endl;
throw InvalidPositionException("generateBlock(): pos. over limit");
}
@@ -2722,7 +2607,7 @@ MapBlock * ServerMap::generateBlock(
MapBlock * ServerMap::createBlock(v3s16 p)
{
DSTACKF("%s: p=(%d,%d,%d)",
- __FUNCTION_NAME, p.X, p.Y, p.Z);
+ FUNCTION_NAME, p.X, p.Y, p.Z);
/*
Do not create over-limit
@@ -2781,7 +2666,7 @@ MapBlock * ServerMap::createBlock(v3s16 p)
MapBlock * ServerMap::emergeBlock(v3s16 p, bool create_blank)
{
DSTACKF("%s: p=(%d,%d,%d), create_blank=%d",
- __FUNCTION_NAME,
+ FUNCTION_NAME,
p.X, p.Y, p.Z, create_blank);
{
@@ -2922,7 +2807,7 @@ void ServerMap::createDirs(std::string path)
{
if(fs::CreateAllDirs(path) == false)
{
- m_dout<<DTIME<<"ServerMap: Failed to create directory "
+ m_dout<<"ServerMap: Failed to create directory "
<<"\""<<path<<"\""<<std::endl;
throw BaseException("ServerMap failed to create directory");
}
@@ -3004,9 +2889,9 @@ std::string ServerMap::getBlockFilename(v3s16 p)
void ServerMap::save(ModifiedState save_level)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
if(m_map_saving_enabled == false) {
- infostream<<"WARNING: Not saving map, saving disabled."<<std::endl;
+ warningstream<<"Not saving map, saving disabled."<<std::endl;
return;
}
@@ -3116,7 +3001,7 @@ void ServerMap::listAllLoadedBlocks(std::vector<v3s16> &dst)
void ServerMap::saveMapMeta()
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
createDirs(m_savedir);
@@ -3140,7 +3025,7 @@ void ServerMap::saveMapMeta()
void ServerMap::loadMapMeta()
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
Settings conf;
std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
@@ -3165,7 +3050,7 @@ void ServerMap::loadMapMeta()
void ServerMap::saveSectorMeta(ServerMapSector *sector)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
// Format used for writing
u8 version = SER_FMT_VER_HIGHEST_WRITE;
// Get destination
@@ -3186,7 +3071,7 @@ void ServerMap::saveSectorMeta(ServerMapSector *sector)
MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
// Get destination
v2s16 p2d = getSectorPos(sectordir);
@@ -3227,7 +3112,7 @@ MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load
bool ServerMap::loadSectorMeta(v2s16 p2d)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
// The directory layout we're going to load from.
// 1 - original sectors/xxxxzzzz/
@@ -3269,7 +3154,7 @@ bool ServerMap::loadSectorMeta(v2s16 p2d)
#if 0
bool ServerMap::loadSectorFull(v2s16 p2d)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
MapSector *sector = NULL;
@@ -3338,7 +3223,10 @@ bool ServerMap::loadSectorFull(v2s16 p2d)
}
#endif
-Database *ServerMap::createDatabase(const std::string &name, const std::string &savedir, Settings &conf)
+Database *ServerMap::createDatabase(
+ const std::string &name,
+ const std::string &savedir,
+ Settings &conf)
{
if (name == "sqlite3")
return new Database_SQLite3(savedir);
@@ -3377,7 +3265,7 @@ bool ServerMap::saveBlock(MapBlock *block, Database *db)
// Dummy blocks are not written
if (block->isDummy()) {
- errorstream << "WARNING: saveBlock: Not writing dummy block "
+ warningstream << "saveBlock: Not writing dummy block "
<< PP(p3d) << std::endl;
return true;
}
@@ -3405,7 +3293,7 @@ bool ServerMap::saveBlock(MapBlock *block, Database *db)
void ServerMap::loadBlock(std::string sectordir, std::string blockfile,
MapSector *sector, bool save_after_load)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
std::string fullpath = sectordir + DIR_DELIM + blockfile;
try {
@@ -3467,7 +3355,7 @@ void ServerMap::loadBlock(std::string sectordir, std::string blockfile,
}
catch(SerializationError &e)
{
- infostream<<"WARNING: Invalid block data on disk "
+ warningstream<<"Invalid block data on disk "
<<"fullpath="<<fullpath
<<" (SerializationError). "
<<"what()="<<e.what()
@@ -3481,7 +3369,7 @@ void ServerMap::loadBlock(std::string sectordir, std::string blockfile,
void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
try {
std::istringstream is(*blob, std::ios_base::binary);
@@ -3549,7 +3437,7 @@ void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool
MapBlock* ServerMap::loadBlock(v3s16 blockpos)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
v2s16 p2d(blockpos.X, blockpos.Z);
@@ -3583,6 +3471,7 @@ MapBlock* ServerMap::loadBlock(v3s16 blockpos)
/*
Make sure sector is loaded
*/
+
MapSector *sector = getSectorNoGenerateNoEx(p2d);
if(sector == NULL)
{
diff --git a/src/map.h b/src/map.h
index 2afd09639..78614d228 100644
--- a/src/map.h
+++ b/src/map.h
@@ -365,6 +365,8 @@ private:
u32 m_unprocessed_count;
u32 m_inc_trending_up_start_time; // milliseconds
bool m_queue_size_timer_started;
+
+ DISABLE_CLASS_COPY(Map);
};
/*
@@ -393,21 +395,21 @@ public:
- Check disk (doesn't load blocks)
- Create blank one
*/
- ServerMapSector * createSector(v2s16 p);
+ ServerMapSector *createSector(v2s16 p);
/*
Blocks are generated by using these and makeBlock().
*/
- bool initBlockMake(BlockMakeData *data, v3s16 blockpos);
+ bool initBlockMake(v3s16 blockpos, BlockMakeData *data);
void finishBlockMake(BlockMakeData *data,
- std::map<v3s16, MapBlock*> &changed_blocks);
+ std::map<v3s16, MapBlock*> *changed_blocks);
/*
Get a block from somewhere.
- Memory
- Create blank
*/
- MapBlock * createBlock(v3s16 p);
+ MapBlock *createBlock(v3s16 p);
/*
Forcefully get a block from somewhere.
diff --git a/src/mapblock.cpp b/src/mapblock.cpp
index 43057f3a5..f8747f52b 100644
--- a/src/mapblock.cpp
+++ b/src/mapblock.cpp
@@ -96,7 +96,7 @@ MapBlock::~MapBlock()
{
#ifndef SERVER
{
- //JMutexAutoLock lock(mesh_mutex);
+ //MutexAutoLock lock(mesh_mutex);
if(mesh)
{
@@ -501,7 +501,7 @@ static void getBlockNodeIdMapping(NameIdMapping *nimap, MapNode *nodes,
}
for(std::set<content_t>::const_iterator
i = unknown_contents.begin();
- i != unknown_contents.end(); i++){
+ i != unknown_contents.end(); ++i){
errorstream<<"getBlockNodeIdMapping(): IGNORING ERROR: "
<<"Name for node id "<<(*i)<<" not known"<<std::endl;
}
@@ -540,14 +540,14 @@ static void correctBlockNodeIds(const NameIdMapping *nimap, MapNode *nodes,
}
for(std::set<content_t>::const_iterator
i = unnamed_contents.begin();
- i != unnamed_contents.end(); i++){
+ i != unnamed_contents.end(); ++i){
errorstream<<"correctBlockNodeIds(): IGNORING ERROR: "
<<"Block contains id "<<(*i)
<<" with no name mapping"<<std::endl;
}
for(std::set<std::string>::const_iterator
i = unallocatable_contents.begin();
- i != unallocatable_contents.end(); i++){
+ i != unallocatable_contents.end(); ++i){
errorstream<<"correctBlockNodeIds(): IGNORING ERROR: "
<<"Could not allocate global id for node name \""
<<(*i)<<"\""<<std::endl;
@@ -564,7 +564,7 @@ void MapBlock::serialize(std::ostream &os, u8 version, bool disk)
throw SerializationError("ERROR: Not writing dummy block.");
}
- FATAL_ERROR_IF(version < SER_FMT_CLIENT_VER_LOWEST, "Serialize version error");
+ FATAL_ERROR_IF(version < SER_FMT_VER_LOWEST_WRITE, "Serialisation version error");
// First byte
u8 flags = 0;
@@ -696,20 +696,18 @@ void MapBlock::deSerialize(std::istream &is, u8 version, bool disk)
TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
<<": Node metadata"<<std::endl);
// Ignore errors
- try{
+ try {
std::ostringstream oss(std::ios_base::binary);
decompressZlib(is, oss);
std::istringstream iss(oss.str(), std::ios_base::binary);
- if(version >= 23)
- m_node_metadata.deSerialize(iss, m_gamedef);
+ if (version >= 23)
+ m_node_metadata.deSerialize(iss, m_gamedef->idef());
else
content_nodemeta_deserialize_legacy(iss,
- &m_node_metadata, &m_node_timers,
- m_gamedef);
- }
- catch(SerializationError &e)
- {
- errorstream<<"WARNING: MapBlock::deSerialize(): Ignoring an error"
+ &m_node_metadata, &m_node_timers,
+ m_gamedef->idef());
+ } catch(SerializationError &e) {
+ warningstream<<"MapBlock::deSerialize(): Ignoring an error"
<<" while deserializing node metadata at ("
<<PP(getPos())<<": "<<e.what()<<std::endl;
}
@@ -772,7 +770,7 @@ void MapBlock::deSerializeNetworkSpecific(std::istream &is)
}
catch(SerializationError &e)
{
- errorstream<<"WARNING: MapBlock::deSerializeNetworkSpecific(): Ignoring an error"
+ warningstream<<"MapBlock::deSerializeNetworkSpecific(): Ignoring an error"
<<": "<<e.what()<<std::endl;
}
}
@@ -794,23 +792,20 @@ void MapBlock::deSerialize_pre22(std::istream &is, u8 version, bool disk)
SharedBuffer<u8> databuf_nodelist(nodecount * ser_length);
// These have no compression
- if(version <= 3 || version == 5 || version == 6)
- {
+ if (version <= 3 || version == 5 || version == 6) {
char tmp;
is.read(&tmp, 1);
- if(is.gcount() != 1)
- throw SerializationError
- ("MapBlock::deSerialize: no enough input data");
+ if (is.gcount() != 1)
+ throw SerializationError(std::string(FUNCTION_NAME)
+ + ": not enough input data");
is_underground = tmp;
- is.read((char*)*databuf_nodelist, nodecount * ser_length);
- if((u32)is.gcount() != nodecount * ser_length)
- throw SerializationError
- ("MapBlock::deSerialize: no enough input data");
- }
- else if(version <= 10)
- {
+ is.read((char *)*databuf_nodelist, nodecount * ser_length);
+ if ((u32)is.gcount() != nodecount * ser_length)
+ throw SerializationError(std::string(FUNCTION_NAME)
+ + ": not enough input data");
+ } else if (version <= 10) {
u8 t8;
- is.read((char*)&t8, 1);
+ is.read((char *)&t8, 1);
is_underground = t8;
{
@@ -818,11 +813,10 @@ void MapBlock::deSerialize_pre22(std::istream &is, u8 version, bool disk)
std::ostringstream os(std::ios_base::binary);
decompress(is, os, version);
std::string s = os.str();
- if(s.size() != nodecount)
- throw SerializationError
- ("MapBlock::deSerialize: invalid format");
- for(u32 i=0; i<s.size(); i++)
- {
+ if (s.size() != nodecount)
+ throw SerializationError(std::string(FUNCTION_NAME)
+ + ": not enough input data");
+ for (u32 i = 0; i < s.size(); i++) {
databuf_nodelist[i*ser_length] = s[i];
}
}
@@ -831,33 +825,27 @@ void MapBlock::deSerialize_pre22(std::istream &is, u8 version, bool disk)
std::ostringstream os(std::ios_base::binary);
decompress(is, os, version);
std::string s = os.str();
- if(s.size() != nodecount)
- throw SerializationError
- ("MapBlock::deSerialize: invalid format");
- for(u32 i=0; i<s.size(); i++)
- {
+ if (s.size() != nodecount)
+ throw SerializationError(std::string(FUNCTION_NAME)
+ + ": not enough input data");
+ for (u32 i = 0; i < s.size(); i++) {
databuf_nodelist[i*ser_length + 1] = s[i];
}
}
- if(version >= 10)
- {
+ if (version >= 10) {
// Uncompress and set param2 data
std::ostringstream os(std::ios_base::binary);
decompress(is, os, version);
std::string s = os.str();
- if(s.size() != nodecount)
- throw SerializationError
- ("MapBlock::deSerialize: invalid format");
- for(u32 i=0; i<s.size(); i++)
- {
+ if (s.size() != nodecount)
+ throw SerializationError(std::string(FUNCTION_NAME)
+ + ": not enough input data");
+ for (u32 i = 0; i < s.size(); i++) {
databuf_nodelist[i*ser_length + 2] = s[i];
}
}
- }
- // All other versions (newest)
- else
- {
+ } else { // All other versions (10 to 21)
u8 flags;
is.read((char*)&flags, 1);
is_underground = (flags & 0x01) ? true : false;
@@ -870,14 +858,12 @@ void MapBlock::deSerialize_pre22(std::istream &is, u8 version, bool disk)
std::ostringstream os(std::ios_base::binary);
decompress(is, os, version);
std::string s = os.str();
- if(s.size() != nodecount*3)
- throw SerializationError
- ("MapBlock::deSerialize: decompress resulted in size"
- " other than nodecount*3");
+ if (s.size() != nodecount * 3)
+ throw SerializationError(std::string(FUNCTION_NAME)
+ + ": decompress resulted in size other than nodecount*3");
// deserialize nodes from buffer
- for(u32 i=0; i<nodecount; i++)
- {
+ for (u32 i = 0; i < nodecount; i++) {
databuf_nodelist[i*ser_length] = s[i];
databuf_nodelist[i*ser_length + 1] = s[i+nodecount];
databuf_nodelist[i*ser_length + 2] = s[i+nodecount*2];
@@ -886,53 +872,45 @@ void MapBlock::deSerialize_pre22(std::istream &is, u8 version, bool disk)
/*
NodeMetadata
*/
- if(version >= 14)
- {
+ if (version >= 14) {
// Ignore errors
- try{
- if(version <= 15)
- {
+ try {
+ if (version <= 15) {
std::string data = deSerializeString(is);
std::istringstream iss(data, std::ios_base::binary);
content_nodemeta_deserialize_legacy(iss,
- &m_node_metadata, &m_node_timers,
- m_gamedef);
- }
- else
- {
+ &m_node_metadata, &m_node_timers,
+ m_gamedef->idef());
+ } else {
//std::string data = deSerializeLongString(is);
std::ostringstream oss(std::ios_base::binary);
decompressZlib(is, oss);
std::istringstream iss(oss.str(), std::ios_base::binary);
content_nodemeta_deserialize_legacy(iss,
- &m_node_metadata, &m_node_timers,
- m_gamedef);
+ &m_node_metadata, &m_node_timers,
+ m_gamedef->idef());
}
- }
- catch(SerializationError &e)
- {
- errorstream<<"WARNING: MapBlock::deSerialize(): Ignoring an error"
+ } catch(SerializationError &e) {
+ warningstream<<"MapBlock::deSerialize(): Ignoring an error"
<<" while deserializing node metadata"<<std::endl;
}
}
}
// Deserialize node data
- for(u32 i=0; i<nodecount; i++)
- {
- data[i].deSerialize(&databuf_nodelist[i*ser_length], version);
+ for (u32 i = 0; i < nodecount; i++) {
+ data[i].deSerialize(&databuf_nodelist[i * ser_length], version);
}
- if(disk)
- {
+ if (disk) {
/*
Versions up from 9 have block objects. (DEPRECATED)
*/
- if(version >= 9){
+ if (version >= 9) {
u16 count = readU16(is);
// Not supported and length not known if count is not 0
if(count != 0){
- errorstream<<"WARNING: MapBlock::deSerialize_pre22(): "
+ warningstream<<"MapBlock::deSerialize_pre22(): "
<<"Ignoring stuff coming at and after MBOs"<<std::endl;
return;
}
@@ -941,11 +919,11 @@ void MapBlock::deSerialize_pre22(std::istream &is, u8 version, bool disk)
/*
Versions up from 15 have static objects.
*/
- if(version >= 15)
+ if (version >= 15)
m_static_objects.deSerialize(is);
// Timestamp
- if(version >= 17){
+ if (version >= 17) {
setTimestamp(readU32(is));
m_disk_timestamp = m_timestamp;
} else {
@@ -955,7 +933,7 @@ void MapBlock::deSerialize_pre22(std::istream &is, u8 version, bool disk)
// Dynamically re-set ids based on node names
NameIdMapping nimap;
// If supported, read node definition id mapping
- if(version >= 21){
+ if (version >= 21) {
nimap.deSerialize(is);
// Else set the legacy mapping
} else {
diff --git a/src/mapblock.h b/src/mapblock.h
index 334136b92..73c17ee60 100644
--- a/src/mapblock.h
+++ b/src/mapblock.h
@@ -504,7 +504,7 @@ public:
// These don't write or read version by itself
// Set disk to true for on-disk format, false for over-the-network format
- // Precondition: version >= SER_FMT_CLIENT_VER_LOWEST
+ // Precondition: version >= SER_FMT_VER_LOWEST_WRITE
void serialize(std::ostream &os, u8 version, bool disk);
// If disk == true: In addition to doing other things, will add
// unknown blocks from id-name mapping to wndef
@@ -637,6 +637,18 @@ private:
typedef std::vector<MapBlock*> MapBlockVect;
+inline bool objectpos_over_limit(v3f p)
+{
+ const static float map_gen_limit_bs = MYMIN(MAX_MAP_GENERATION_LIMIT,
+ g_settings->getU16("map_generation_limit")) * BS;
+ return (p.X < -map_gen_limit_bs
+ || p.X > map_gen_limit_bs
+ || p.Y < -map_gen_limit_bs
+ || p.Y > map_gen_limit_bs
+ || p.Z < -map_gen_limit_bs
+ || p.Z > map_gen_limit_bs);
+}
+
inline bool blockpos_over_limit(v3s16 p)
{
const static u16 map_gen_limit = MYMIN(MAX_MAP_GENERATION_LIMIT,
diff --git a/src/mapblock_mesh.cpp b/src/mapblock_mesh.cpp
index 33597b2fc..e1b044271 100644
--- a/src/mapblock_mesh.cpp
+++ b/src/mapblock_mesh.cpp
@@ -33,26 +33,26 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "util/directiontables.h"
#include <IMeshManipulator.h>
-static void applyFacesShading(video::SColor& color, float factor)
+static void applyFacesShading(video::SColor &color, const float factor)
{
- color.setRed(core::clamp(core::round32(color.getRed()*factor), 0, 255));
- color.setGreen(core::clamp(core::round32(color.getGreen()*factor), 0, 255));
+ color.setRed(core::clamp(core::round32(color.getRed() * factor), 0, 255));
+ color.setGreen(core::clamp(core::round32(color.getGreen() * factor), 0, 255));
}
/*
MeshMakeData
*/
-MeshMakeData::MeshMakeData(IGameDef *gamedef, bool use_shaders):
+MeshMakeData::MeshMakeData(IGameDef *gamedef, bool use_shaders,
+ bool use_tangent_vertices):
m_vmanip(),
m_blockpos(-1337,-1337,-1337),
m_crack_pos_relative(-1337, -1337, -1337),
- m_highlighted_pos_relative(-1337, -1337, -1337),
m_smooth_lighting(false),
m_show_hud(false),
- m_highlight_mesh_color(255, 255, 255, 255),
m_gamedef(gamedef),
- m_use_shaders(use_shaders)
+ m_use_shaders(use_shaders),
+ m_use_tangent_vertices(use_tangent_vertices)
{}
void MeshMakeData::fill(MapBlock *block)
@@ -138,12 +138,6 @@ void MeshMakeData::setCrack(int crack_level, v3s16 crack_pos)
m_crack_pos_relative = crack_pos - m_blockpos*MAP_BLOCKSIZE;
}
-void MeshMakeData::setHighlighted(v3s16 highlighted_pos, bool show_hud)
-{
- m_show_hud = show_hud;
- m_highlighted_pos_relative = highlighted_pos - m_blockpos*MAP_BLOCKSIZE;
-}
-
void MeshMakeData::setSmoothLighting(bool smooth_lighting)
{
m_smooth_lighting = smooth_lighting;
@@ -891,8 +885,9 @@ static void updateFastFaceRow(
&& next_lights[3] == lights[3]
&& next_tile == tile
&& tile.rotation == 0
- && next_light_source == light_source)
- {
+ && next_light_source == light_source
+ && (tile.material_flags & MATERIAL_FLAG_TILEABLE_HORIZONTAL)
+ && (tile.material_flags & MATERIAL_FLAG_TILEABLE_VERTICAL)) {
next_is_different = false;
}
else{
@@ -1031,18 +1026,19 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
m_mesh(new scene::SMesh()),
m_minimap_mapblock(NULL),
m_gamedef(data->m_gamedef),
+ m_driver(m_gamedef->tsrc()->getDevice()->getVideoDriver()),
m_tsrc(m_gamedef->getTextureSource()),
m_shdrsrc(m_gamedef->getShaderSource()),
m_animation_force_timer(0), // force initial animation
m_last_crack(-1),
m_crack_materials(),
- m_highlighted_materials(),
m_last_daynight_ratio((u32) -1),
m_daynight_diffs()
{
m_enable_shaders = data->m_use_shaders;
- m_enable_highlighting = g_settings->getBool("enable_node_highlighting");
-
+ m_use_tangent_vertices = data->m_use_tangent_vertices;
+ m_enable_vbo = g_settings->getBool("enable_vbo");
+
if (g_settings->getBool("enable_minimap")) {
m_minimap_mapblock = new MinimapMapblock;
m_minimap_mapblock->getMinimapNodes(
@@ -1074,15 +1070,14 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
Convert FastFaces to MeshCollector
*/
- MeshCollector collector;
+ MeshCollector collector(m_use_tangent_vertices);
{
// avg 0ms (100ms spikes when loading textures the first time)
// (NOTE: probably outdated)
//TimeTaker timer2("MeshCollector building");
- for(u32 i=0; i<fastfaces_new.size(); i++)
- {
+ for (u32 i = 0; i < fastfaces_new.size(); i++) {
FastFace &f = fastfaces_new[i];
const u16 indices[] = {0,1,2,2,3,0};
@@ -1116,8 +1111,6 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
mapblock_mesh_generate_special(data, collector);
- m_highlight_mesh_color = data->m_highlight_mesh_color;
-
/*
Convert MeshCollector to SMesh
*/
@@ -1162,38 +1155,43 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
p.tile.texture = animation_frame.texture;
}
- if(m_enable_highlighting && p.tile.material_flags & MATERIAL_FLAG_HIGHLIGHTED)
- m_highlighted_materials.push_back(i);
-
- for(u32 j = 0; j < p.vertices.size(); j++)
- {
- video::S3DVertexTangents *vertex = &p.vertices[j];
+ u32 vertex_count = m_use_tangent_vertices ?
+ p.tangent_vertices.size() : p.vertices.size();
+ for (u32 j = 0; j < vertex_count; j++) {
+ v3f *Normal;
+ video::SColor *vc;
+ if (m_use_tangent_vertices) {
+ vc = &p.tangent_vertices[j].Color;
+ Normal = &p.tangent_vertices[j].Normal;
+ } else {
+ vc = &p.vertices[j].Color;
+ Normal = &p.vertices[j].Normal;
+ }
// Note applyFacesShading second parameter is precalculated sqrt
// value for speed improvement
// Skip it for lightsources and top faces.
- video::SColor &vc = vertex->Color;
- if (!vc.getBlue()) {
- if (vertex->Normal.Y < -0.5) {
- applyFacesShading (vc, 0.447213);
- } else if (vertex->Normal.X > 0.5) {
- applyFacesShading (vc, 0.670820);
- } else if (vertex->Normal.X < -0.5) {
- applyFacesShading (vc, 0.670820);
- } else if (vertex->Normal.Z > 0.5) {
- applyFacesShading (vc, 0.836660);
- } else if (vertex->Normal.Z < -0.5) {
- applyFacesShading (vc, 0.836660);
+ if (!vc->getBlue()) {
+ if (Normal->Y < -0.5) {
+ applyFacesShading(*vc, 0.447213);
+ } else if (Normal->X > 0.5) {
+ applyFacesShading(*vc, 0.670820);
+ } else if (Normal->X < -0.5) {
+ applyFacesShading(*vc, 0.670820);
+ } else if (Normal->Z > 0.5) {
+ applyFacesShading(*vc, 0.836660);
+ } else if (Normal->Z < -0.5) {
+ applyFacesShading(*vc, 0.836660);
}
}
- if(!m_enable_shaders)
- {
+ if (!m_enable_shaders) {
// - Classic lighting (shaders handle this by themselves)
// Set initial real color and store for later updates
- u8 day = vc.getRed();
- u8 night = vc.getGreen();
- finalColorBlend(vc, day, night, 1000);
- if(day != night)
+ u8 day = vc->getRed();
+ u8 night = vc->getGreen();
+ finalColorBlend(*vc, day, night, 1000);
+ if (day != night) {
m_daynight_diffs[i][j] = std::make_pair(day, night);
+ }
}
}
@@ -1205,46 +1203,57 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
material.setFlag(video::EMF_FOG_ENABLE, true);
material.setTexture(0, p.tile.texture);
- if (p.tile.material_flags & MATERIAL_FLAG_HIGHLIGHTED) {
- material.MaterialType = video::EMT_TRANSPARENT_ADD_COLOR;
- } else {
- if (m_enable_shaders) {
- material.MaterialType = m_shdrsrc->getShaderInfo(p.tile.shader_id).material;
- p.tile.applyMaterialOptionsWithShaders(material);
- if (p.tile.normal_texture) {
- material.setTexture(1, p.tile.normal_texture);
- }
- material.setTexture(2, p.tile.flags_texture);
- } else {
- p.tile.applyMaterialOptions(material);
+ if (m_enable_shaders) {
+ material.MaterialType = m_shdrsrc->getShaderInfo(p.tile.shader_id).material;
+ p.tile.applyMaterialOptionsWithShaders(material);
+ if (p.tile.normal_texture) {
+ material.setTexture(1, p.tile.normal_texture);
}
+ material.setTexture(2, p.tile.flags_texture);
+ } else {
+ p.tile.applyMaterialOptions(material);
}
- // Create meshbuffer
- scene::SMeshBufferTangents *buf = new scene::SMeshBufferTangents();
- // Set material
- buf->Material = material;
- // Add to mesh
- m_mesh->addMeshBuffer(buf);
- // Mesh grabbed it
- buf->drop();
- buf->append(&p.vertices[0], p.vertices.size(),
- &p.indices[0], p.indices.size());
-}
- m_camera_offset = camera_offset;
+ scene::SMesh *mesh = (scene::SMesh *)m_mesh;
+
+ // Create meshbuffer, add to mesh
+ if (m_use_tangent_vertices) {
+ scene::SMeshBufferTangents *buf = new scene::SMeshBufferTangents();
+ // Set material
+ buf->Material = material;
+ // Add to mesh
+ mesh->addMeshBuffer(buf);
+ // Mesh grabbed it
+ buf->drop();
+ buf->append(&p.tangent_vertices[0], p.tangent_vertices.size(),
+ &p.indices[0], p.indices.size());
+ } else {
+ scene::SMeshBuffer *buf = new scene::SMeshBuffer();
+ // Set material
+ buf->Material = material;
+ // Add to mesh
+ mesh->addMeshBuffer(buf);
+ // Mesh grabbed it
+ buf->drop();
+ buf->append(&p.vertices[0], p.vertices.size(),
+ &p.indices[0], p.indices.size());
+ }
+ }
/*
Do some stuff to the mesh
*/
+ m_camera_offset = camera_offset;
+ translateMesh(m_mesh,
+ intToFloat(data->m_blockpos * MAP_BLOCKSIZE - camera_offset, BS));
- translateMesh(m_mesh, intToFloat(data->m_blockpos * MAP_BLOCKSIZE - camera_offset, BS));
-
- if (m_enable_shaders) {
- scene::IMeshManipulator* meshmanip = m_gamedef->getSceneManager()->getMeshManipulator();
+ if (m_use_tangent_vertices) {
+ scene::IMeshManipulator* meshmanip =
+ m_gamedef->getSceneManager()->getMeshManipulator();
meshmanip->recalculateTangents(m_mesh, true, false, false);
}
- if(m_mesh)
+ if (m_mesh)
{
#if 0
// Usually 1-700 faces and 1-7 materials
@@ -1254,14 +1263,9 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
#endif
// Use VBO for mesh (this just would set this for ever buffer)
- // This will lead to infinite memory usage because or irrlicht.
- //m_mesh->setHardwareMappingHint(scene::EHM_STATIC);
-
- /*
- NOTE: If that is enabled, some kind of a queue to the main
- thread should be made which would call irrlicht to delete
- the hardware buffer and then delete the mesh
- */
+ if (m_enable_vbo) {
+ m_mesh->setHardwareMappingHint(scene::EHM_STATIC);
+ }
}
//std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
@@ -1270,12 +1274,17 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
m_has_animation =
!m_crack_materials.empty() ||
!m_daynight_diffs.empty() ||
- !m_animation_tiles.empty() ||
- !m_highlighted_materials.empty();
+ !m_animation_tiles.empty();
}
MapBlockMesh::~MapBlockMesh()
{
+ if (m_enable_vbo && m_mesh) {
+ for (u32 i = 0; i < m_mesh->getMeshBufferCount(); i++) {
+ scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i);
+ m_driver->removeHardwareBuffer(buf);
+ }
+ }
m_mesh->drop();
m_mesh = NULL;
delete m_minimap_mapblock;
@@ -1296,7 +1305,7 @@ bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_rat
{
for(std::map<u32, std::string>::iterator
i = m_crack_materials.begin();
- i != m_crack_materials.end(); i++)
+ i != m_crack_materials.end(); ++i)
{
scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
std::string basename = i->second;
@@ -1328,7 +1337,7 @@ bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_rat
// Texture animation
for(std::map<u32, TileSpec>::iterator
i = m_animation_tiles.begin();
- i != m_animation_tiles.end(); i++)
+ i != m_animation_tiles.end(); ++i)
{
const TileSpec &tile = i->second;
// Figure out current frame
@@ -1356,15 +1365,19 @@ bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_rat
// Day-night transition
if(!m_enable_shaders && (daynight_ratio != m_last_daynight_ratio))
{
+ // Force reload mesh to VBO
+ if (m_enable_vbo) {
+ m_mesh->setDirty();
+ }
for(std::map<u32, std::map<u32, std::pair<u8, u8> > >::iterator
i = m_daynight_diffs.begin();
- i != m_daynight_diffs.end(); i++)
+ i != m_daynight_diffs.end(); ++i)
{
scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
- video::S3DVertexTangents *vertices = (video::S3DVertexTangents *)buf->getVertices();
+ video::S3DVertex *vertices = (video::S3DVertex *)buf->getVertices();
for(std::map<u32, std::pair<u8, u8 > >::iterator
j = i->second.begin();
- j != i->second.end(); j++)
+ j != i->second.end(); ++j)
{
u8 day = j->second.first;
u8 night = j->second.second;
@@ -1374,30 +1387,6 @@ bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_rat
m_last_daynight_ratio = daynight_ratio;
}
- // Node highlighting
- if (m_enable_highlighting) {
- u8 day = m_highlight_mesh_color.getRed();
- u8 night = m_highlight_mesh_color.getGreen();
- video::SColor hc;
- finalColorBlend(hc, day, night, daynight_ratio);
- float sin_r = 0.07 * sin(1.5 * time);
- float sin_g = 0.07 * sin(1.5 * time + irr::core::PI * 0.5);
- float sin_b = 0.07 * sin(1.5 * time + irr::core::PI);
- hc.setRed(core::clamp(core::round32(hc.getRed() * (0.8 + sin_r)), 0, 255));
- hc.setGreen(core::clamp(core::round32(hc.getGreen() * (0.8 + sin_g)), 0, 255));
- hc.setBlue(core::clamp(core::round32(hc.getBlue() * (0.8 + sin_b)), 0, 255));
-
- for(std::list<u32>::iterator
- i = m_highlighted_materials.begin();
- i != m_highlighted_materials.end(); i++)
- {
- scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(*i);
- video::S3DVertexTangents *vertices = (video::S3DVertexTangents*)buf->getVertices();
- for (u32 j = 0; j < buf->getVertexCount() ;j++)
- vertices[j].Color = hc;
- }
- }
-
return true;
}
@@ -1405,6 +1394,9 @@ void MapBlockMesh::updateCameraOffset(v3s16 camera_offset)
{
if (camera_offset != m_camera_offset) {
translateMesh(m_mesh, intToFloat(m_camera_offset-camera_offset, BS));
+ if (m_enable_vbo) {
+ m_mesh->setDirty();
+ }
m_camera_offset = camera_offset;
}
}
@@ -1441,17 +1433,27 @@ void MeshCollector::append(const TileSpec &tile,
p = &prebuffers[prebuffers.size() - 1];
}
- u32 vertex_count = p->vertices.size();
- for (u32 i = 0; i < numIndices; i++) {
+ u32 vertex_count;
+ if (m_use_tangent_vertices) {
+ vertex_count = p->tangent_vertices.size();
+ for (u32 i = 0; i < numVertices; i++) {
+ video::S3DVertexTangents vert(vertices[i].Pos, vertices[i].Normal,
+ vertices[i].Color, vertices[i].TCoords);
+ p->tangent_vertices.push_back(vert);
+ }
+ } else {
+ vertex_count = p->vertices.size();
+ for (u32 i = 0; i < numVertices; i++) {
+ video::S3DVertex vert(vertices[i].Pos, vertices[i].Normal,
+ vertices[i].Color, vertices[i].TCoords);
+ p->vertices.push_back(vert);
+ }
+ }
+
+ for (u32 i = 0; i < numIndices; i++) {
u32 j = indices[i] + vertex_count;
p->indices.push_back(j);
}
-
- for (u32 i = 0; i < numVertices; i++) {
- video::S3DVertexTangents vert(vertices[i].Pos, vertices[i].Normal,
- vertices[i].Color, vertices[i].TCoords);
- p->vertices.push_back(vert);
- }
}
/*
@@ -1487,15 +1489,25 @@ void MeshCollector::append(const TileSpec &tile,
p = &prebuffers[prebuffers.size() - 1];
}
- u32 vertex_count = p->vertices.size();
+ u32 vertex_count;
+ if (m_use_tangent_vertices) {
+ vertex_count = p->tangent_vertices.size();
+ for (u32 i = 0; i < numVertices; i++) {
+ video::S3DVertexTangents vert(vertices[i].Pos + pos,
+ vertices[i].Normal, c, vertices[i].TCoords);
+ p->tangent_vertices.push_back(vert);
+ }
+ } else {
+ vertex_count = p->vertices.size();
+ for (u32 i = 0; i < numVertices; i++) {
+ video::S3DVertex vert(vertices[i].Pos + pos,
+ vertices[i].Normal, c, vertices[i].TCoords);
+ p->vertices.push_back(vert);
+ }
+ }
+
for (u32 i = 0; i < numIndices; i++) {
u32 j = indices[i] + vertex_count;
p->indices.push_back(j);
}
-
- for (u32 i = 0; i < numVertices; i++) {
- video::S3DVertexTangents vert(vertices[i].Pos + pos, vertices[i].Normal,
- c, vertices[i].TCoords);
- p->vertices.push_back(vert);
- }
}
diff --git a/src/mapblock_mesh.h b/src/mapblock_mesh.h
index 8e994ec6b..f89fbe669 100644
--- a/src/mapblock_mesh.h
+++ b/src/mapblock_mesh.h
@@ -41,15 +41,15 @@ struct MeshMakeData
VoxelManipulator m_vmanip;
v3s16 m_blockpos;
v3s16 m_crack_pos_relative;
- v3s16 m_highlighted_pos_relative;
bool m_smooth_lighting;
bool m_show_hud;
- video::SColor m_highlight_mesh_color;
IGameDef *m_gamedef;
bool m_use_shaders;
+ bool m_use_tangent_vertices;
- MeshMakeData(IGameDef *gamedef, bool use_shaders);
+ MeshMakeData(IGameDef *gamedef, bool use_shaders,
+ bool use_tangent_vertices = false);
/*
Copy central data directly from block, and other data from
@@ -68,11 +68,6 @@ struct MeshMakeData
void setCrack(int crack_level, v3s16 crack_pos);
/*
- Set the highlighted node position
- */
-
- void setHighlighted(v3s16 highlighted_pos, bool show_hud);
- /*
Enable or disable smooth lighting
*/
void setSmoothLighting(bool smooth_lighting);
@@ -104,7 +99,7 @@ public:
// Returns true if anything has been changed.
bool animate(bool faraway, float time, int crack, u32 daynight_ratio);
- scene::SMesh *getMesh()
+ scene::IMesh *getMesh()
{
return m_mesh;
}
@@ -130,17 +125,17 @@ public:
void updateCameraOffset(v3s16 camera_offset);
private:
- scene::SMesh *m_mesh;
+ scene::IMesh *m_mesh;
MinimapMapblock *m_minimap_mapblock;
IGameDef *m_gamedef;
+ video::IVideoDriver *m_driver;
ITextureSource *m_tsrc;
IShaderSource *m_shdrsrc;
bool m_enable_shaders;
- bool m_enable_highlighting;
+ bool m_use_tangent_vertices;
+ bool m_enable_vbo;
- video::SColor m_highlight_mesh_color;
-
// Must animate() be called before rendering?
bool m_has_animation;
int m_animation_force_timer;
@@ -150,7 +145,6 @@ private:
int m_last_crack;
// Maps mesh buffer (i.e. material) indices to base texture names
std::map<u32, std::string> m_crack_materials;
- std::list<u32> m_highlighted_materials;
// Animation info: texture animationi
// Maps meshbuffers to TileSpecs
@@ -177,12 +171,20 @@ struct PreMeshBuffer
{
TileSpec tile;
std::vector<u16> indices;
- std::vector<video::S3DVertexTangents> vertices;
+ std::vector<video::S3DVertex> vertices;
+ std::vector<video::S3DVertexTangents> tangent_vertices;
};
struct MeshCollector
{
std::vector<PreMeshBuffer> prebuffers;
+ bool m_use_tangent_vertices;
+
+ MeshCollector(bool use_tangent_vertices):
+ m_use_tangent_vertices(use_tangent_vertices)
+ {
+ }
+
void append(const TileSpec &material,
const video::S3DVertex *vertices, u32 numVertices,
const u16 *indices, u32 numIndices);
diff --git a/src/mapgen.cpp b/src/mapgen.cpp
index f8e9477c5..b3c9380a0 100644
--- a/src/mapgen.cpp
+++ b/src/mapgen.cpp
@@ -29,7 +29,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "content_sao.h"
#include "nodedef.h"
#include "emerge.h"
-#include "content_mapnode.h" // For content_mapnode_get_new_name
#include "voxelalgorithms.h"
#include "porting.h"
#include "profiler.h"
@@ -42,11 +41,12 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "log.h"
FlagDesc flagdesc_mapgen[] = {
- {"trees", MG_TREES},
- {"caves", MG_CAVES},
- {"dungeons", MG_DUNGEONS},
- {"flat", MG_FLAT},
- {"light", MG_LIGHT},
+ {"trees", MG_TREES},
+ {"caves", MG_CAVES},
+ {"dungeons", MG_DUNGEONS},
+ {"flat", MG_FLAT},
+ {"light", MG_LIGHT},
+ {"decorations", MG_DECORATIONS},
{NULL, 0}
};
@@ -62,8 +62,9 @@ FlagDesc flagdesc_gennotify[] = {
};
-///////////////////////////////////////////////////////////////////////////////
-
+////
+//// Mapgen
+////
Mapgen::Mapgen()
{
@@ -161,6 +162,26 @@ s16 Mapgen::findGroundLevel(v2s16 p2d, s16 ymin, s16 ymax)
}
+// Returns -MAX_MAP_GENERATION_LIMIT if not found or if ground is found first
+s16 Mapgen::findLiquidSurface(v2s16 p2d, s16 ymin, s16 ymax)
+{
+ v3s16 em = vm->m_area.getExtent();
+ u32 i = vm->m_area.index(p2d.X, ymax, p2d.Y);
+ s16 y;
+
+ for (y = ymax; y >= ymin; y--) {
+ MapNode &n = vm->m_data[i];
+ if (ndef->get(n).walkable)
+ return -MAX_MAP_GENERATION_LIMIT;
+ else if (ndef->get(n).isLiquid())
+ break;
+
+ vm->m_area.add_y(em, i, -1);
+ }
+ return (y >= ymin) ? y : -MAX_MAP_GENERATION_LIMIT;
+}
+
+
void Mapgen::updateHeightmap(v3s16 nmin, v3s16 nmax)
{
if (!heightmap)
@@ -225,14 +246,31 @@ void Mapgen::lightSpread(VoxelArea &a, v3s16 p, u8 light)
return;
u32 vi = vm->m_area.index(p);
- MapNode &nn = vm->m_data[vi];
-
- light--;
- // should probably compare masked, but doesn't seem to make a difference
- if (light <= nn.param1 || !ndef->get(nn).light_propagates)
+ MapNode &n = vm->m_data[vi];
+
+ // Decay light in each of the banks separately
+ u8 light_day = light & 0x0F;
+ if (light_day > 0)
+ light_day -= 0x01;
+
+ u8 light_night = light & 0xF0;
+ if (light_night > 0)
+ light_night -= 0x10;
+
+ // Bail out only if we have no more light from either bank to propogate, or
+ // we hit a solid block that light cannot pass through.
+ if ((light_day <= (n.param1 & 0x0F) &&
+ light_night <= (n.param1 & 0xF0)) ||
+ !ndef->get(n).light_propagates)
return;
- nn.param1 = light;
+ // Since this recursive function only terminates when there is no light from
+ // either bank left, we need to take the max of both banks into account for
+ // the case where spreading has stopped for one light bank but not the other.
+ light = MYMAX(light_day, n.param1 & 0x0F) |
+ MYMAX(light_night, n.param1 & 0xF0);
+
+ n.param1 = light;
lightSpread(a, p + v3s16(0, 0, 1), light);
lightSpread(a, p + v3s16(0, 1, 0), light);
@@ -243,43 +281,29 @@ void Mapgen::lightSpread(VoxelArea &a, v3s16 p, u8 light)
}
-void Mapgen::calcLighting(v3s16 nmin, v3s16 nmax, v3s16 full_nmin, v3s16 full_nmax)
+void Mapgen::calcLighting(v3s16 nmin, v3s16 nmax, v3s16 full_nmin, v3s16 full_nmax,
+ bool propagate_shadow)
{
ScopeProfiler sp(g_profiler, "EmergeThread: mapgen lighting update", SPT_AVG);
//TimeTaker t("updateLighting");
- propagateSunlight(nmin, nmax);
+ propagateSunlight(nmin, nmax, propagate_shadow);
spreadLight(full_nmin, full_nmax);
//printf("updateLighting: %dms\n", t.stop());
}
-
-void Mapgen::calcLighting(v3s16 nmin, v3s16 nmax)
-{
- ScopeProfiler sp(g_profiler, "EmergeThread: mapgen lighting update", SPT_AVG);
- //TimeTaker t("updateLighting");
-
- propagateSunlight(
- nmin - v3s16(1, 1, 1) * MAP_BLOCKSIZE,
- nmax + v3s16(1, 0, 1) * MAP_BLOCKSIZE);
-
- spreadLight(
- nmin - v3s16(1, 1, 1) * MAP_BLOCKSIZE,
- nmax + v3s16(1, 1, 1) * MAP_BLOCKSIZE);
-
- //printf("updateLighting: %dms\n", t.stop());
-}
-
-
-void Mapgen::propagateSunlight(v3s16 nmin, v3s16 nmax)
+void Mapgen::propagateSunlight(v3s16 nmin, v3s16 nmax, bool propagate_shadow)
{
//TimeTaker t("propagateSunlight");
VoxelArea a(nmin, nmax);
bool block_is_underground = (water_level >= nmax.Y);
v3s16 em = vm->m_area.getExtent();
+ // NOTE: Direct access to the low 4 bits of param1 is okay here because,
+ // by definition, sunlight will never be in the night lightbank.
+
for (int z = a.MinEdge.Z; z <= a.MaxEdge.Z; z++) {
for (int x = a.MinEdge.X; x <= a.MaxEdge.X; x++) {
// see if we can get a light value from the overtop
@@ -287,7 +311,8 @@ void Mapgen::propagateSunlight(v3s16 nmin, v3s16 nmax)
if (vm->m_data[i].getContent() == CONTENT_IGNORE) {
if (block_is_underground)
continue;
- } else if ((vm->m_data[i].param1 & 0x0F) != LIGHT_SUN) {
+ } else if ((vm->m_data[i].param1 & 0x0F) != LIGHT_SUN &&
+ propagate_shadow) {
continue;
}
vm->m_area.add_y(em, i, -1);
@@ -305,7 +330,6 @@ void Mapgen::propagateSunlight(v3s16 nmin, v3s16 nmax)
}
-
void Mapgen::spreadLight(v3s16 nmin, v3s16 nmax)
{
//TimeTaker t("spreadLight");
@@ -316,15 +340,21 @@ void Mapgen::spreadLight(v3s16 nmin, v3s16 nmax)
u32 i = vm->m_area.index(a.MinEdge.X, y, z);
for (int x = a.MinEdge.X; x <= a.MaxEdge.X; x++, i++) {
MapNode &n = vm->m_data[i];
- if (n.getContent() == CONTENT_IGNORE ||
- !ndef->get(n).light_propagates)
+ if (n.getContent() == CONTENT_IGNORE)
continue;
- u8 light_produced = ndef->get(n).light_source & 0x0F;
+ const ContentFeatures &cf = ndef->get(n);
+ if (!cf.light_propagates)
+ continue;
+
+ // TODO(hmmmmm): Abstract away direct param1 accesses with a
+ // wrapper, but something lighter than MapNode::get/setLight
+
+ u8 light_produced = cf.light_source;
if (light_produced)
- n.param1 = light_produced;
+ n.param1 = light_produced | (light_produced << 4);
- u8 light = n.param1 & 0x0F;
+ u8 light = n.param1;
if (light) {
lightSpread(a, v3s16(x, y, z + 1), light);
lightSpread(a, v3s16(x, y + 1, z ), light);
@@ -341,8 +371,9 @@ void Mapgen::spreadLight(v3s16 nmin, v3s16 nmax)
}
-
-///////////////////////////////////////////////////////////////////////////////
+////
+//// GenerateNotifier
+////
GenerateNotifier::GenerateNotifier()
{
@@ -408,7 +439,10 @@ void GenerateNotifier::getEvents(
m_notify_events.clear();
}
-///////////////////////////////////////////////////////////////////////////////
+
+////
+//// MapgenParams
+////
void MapgenParams::load(const Settings &settings)
{
@@ -430,9 +464,11 @@ void MapgenParams::load(const Settings &settings)
settings.getNoiseParams("mg_biome_np_humidity_blend", np_biome_humidity_blend);
delete sparams;
- sparams = EmergeManager::createMapgenParams(mg_name);
- if (sparams)
+ MapgenFactory *mgfactory = EmergeManager::getMapgenFactory(mg_name);
+ if (mgfactory) {
+ sparams = mgfactory->createMapgenParams();
sparams->readParams(&settings);
+ }
}
@@ -442,7 +478,7 @@ void MapgenParams::save(Settings &settings) const
settings.setU64("seed", seed);
settings.setS16("water_level", water_level);
settings.setS16("chunksize", chunksize);
- settings.setFlagStr("mg_flags", flags, flagdesc_mapgen, (u32)-1);
+ settings.setFlagStr("mg_flags", flags, flagdesc_mapgen, U32_MAX);
settings.setNoiseParams("mg_biome_np_heat", np_biome_heat);
settings.setNoiseParams("mg_biome_np_heat_blend", np_biome_heat_blend);
settings.setNoiseParams("mg_biome_np_humidity", np_biome_humidity);
diff --git a/src/mapgen.h b/src/mapgen.h
index 46328ba92..abc3d2e89 100644
--- a/src/mapgen.h
+++ b/src/mapgen.h
@@ -29,11 +29,12 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#define DEFAULT_MAPGEN "v6"
/////////////////// Mapgen flags
-#define MG_TREES 0x01
-#define MG_CAVES 0x02
-#define MG_DUNGEONS 0x04
-#define MG_FLAT 0x08
-#define MG_LIGHT 0x10
+#define MG_TREES 0x01
+#define MG_CAVES 0x02
+#define MG_DUNGEONS 0x04
+#define MG_FLAT 0x08
+#define MG_LIGHT 0x10
+#define MG_DECORATIONS 0x20
class Settings;
class MMVManip;
@@ -126,10 +127,10 @@ struct MapgenParams {
chunksize(5),
seed(0),
water_level(1),
- flags(MG_TREES | MG_CAVES | MG_LIGHT),
- np_biome_heat(NoiseParams(50, 50, v3f(1000.0, 1000.0, 1000.0), 5349, 3, 0.5, 2.0)),
+ flags(MG_CAVES | MG_LIGHT | MG_DECORATIONS),
+ np_biome_heat(NoiseParams(50, 50, v3f(750.0, 750.0, 750.0), 5349, 3, 0.5, 2.0)),
np_biome_heat_blend(NoiseParams(0, 1.5, v3f(8.0, 8.0, 8.0), 13, 2, 1.0, 2.0)),
- np_biome_humidity(NoiseParams(50, 50, v3f(1000.0, 1000.0, 1000.0), 842, 3, 0.5, 2.0)),
+ np_biome_humidity(NoiseParams(50, 50, v3f(750.0, 750.0, 750.0), 842, 3, 0.5, 2.0)),
np_biome_humidity_blend(NoiseParams(0, 1.5, v3f(8.0, 8.0, 8.0), 90003, 2, 1.0, 2.0)),
sparams(NULL)
{}
@@ -166,21 +167,29 @@ public:
static u32 getBlockSeed2(v3s16 p, int 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);
void updateLiquid(UniqueQueue<v3s16> *trans_liquid, v3s16 nmin, v3s16 nmax);
void setLighting(u8 light, v3s16 nmin, v3s16 nmax);
void lightSpread(VoxelArea &a, v3s16 p, u8 light);
-
- void calcLighting(v3s16 nmin, v3s16 nmax);
- void calcLighting(v3s16 nmin, v3s16 nmax,
- v3s16 full_nmin, v3s16 full_nmax);
-
- void propagateSunlight(v3s16 nmin, v3s16 nmax);
+ void calcLighting(v3s16 nmin, v3s16 nmax, v3s16 full_nmin, v3s16 full_nmax,
+ bool propagate_shadow = true);
+ void propagateSunlight(v3s16 nmin, v3s16 nmax, bool propagate_shadow);
void spreadLight(v3s16 nmin, v3s16 nmax);
virtual void makeChunk(BlockMakeData *data) {}
virtual int getGroundLevelAtPoint(v2s16 p) { return 0; }
+
+ // getSpawnLevelAtPoint() is a function within each mapgen that returns a
+ // suitable y co-ordinate for player spawn ('suitable' usually meaning
+ // within 16 nodes of water_level). If a suitable spawn level cannot be
+ // found at the specified (X, Z) 'MAX_MAP_GENERATION_LIMIT' is returned to
+ // signify this and to cause Server::findSpawnPos() to try another (X, Z).
+ virtual int getSpawnLevelAtPoint(v2s16 p) { return 0; }
+
+private:
+ DISABLE_CLASS_COPY(Mapgen);
};
struct MapgenFactory {
diff --git a/src/mapgen_flat.cpp b/src/mapgen_flat.cpp
new file mode 100644
index 000000000..4669f1716
--- /dev/null
+++ b/src/mapgen_flat.cpp
@@ -0,0 +1,622 @@
+/*
+Minetest
+Copyright (C) 2010-2015 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
+Copyright (C) 2010-2015 paramat, Matt Gregory
+
+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 "mapgen.h"
+#include "voxel.h"
+#include "noise.h"
+#include "mapblock.h"
+#include "mapnode.h"
+#include "map.h"
+#include "content_sao.h"
+#include "nodedef.h"
+#include "voxelalgorithms.h"
+//#include "profiler.h" // For TimeTaker
+#include "settings.h" // For g_settings
+#include "emerge.h"
+#include "dungeongen.h"
+#include "cavegen.h"
+#include "treegen.h"
+#include "mg_biome.h"
+#include "mg_ore.h"
+#include "mg_decoration.h"
+#include "mapgen_flat.h"
+
+
+FlagDesc flagdesc_mapgen_flat[] = {
+ {"lakes", MGFLAT_LAKES},
+ {"hills", MGFLAT_HILLS},
+ {NULL, 0}
+};
+
+///////////////////////////////////////////////////////////////////////////////////////
+
+
+MapgenFlat::MapgenFlat(int mapgenid, MapgenParams *params, EmergeManager *emerge)
+ : Mapgen(mapgenid, params, emerge)
+{
+ this->m_emerge = emerge;
+ this->bmgr = emerge->biomemgr;
+
+ //// amount of elements to skip for the next index
+ //// for noise/height/biome maps (not vmanip)
+ this->ystride = csize.X;
+ // 1-down overgeneration
+ this->zstride_1d = csize.X * (csize.Y + 1);
+
+ this->biomemap = new u8[csize.X * csize.Z];
+ this->heightmap = new s16[csize.X * csize.Z];
+ this->heatmap = NULL;
+ this->humidmap = NULL;
+
+ MapgenFlatParams *sp = (MapgenFlatParams *)params->sparams;
+
+ this->spflags = sp->spflags;
+ this->ground_level = sp->ground_level;
+ this->large_cave_depth = sp->large_cave_depth;
+ this->cave_width = sp->cave_width;
+ this->lake_threshold = sp->lake_threshold;
+ this->lake_steepness = sp->lake_steepness;
+ this->hill_threshold = sp->hill_threshold;
+ this->hill_steepness = sp->hill_steepness;
+
+ //// 2D noise
+ noise_terrain = new Noise(&sp->np_terrain, seed, csize.X, csize.Z);
+ noise_filler_depth = new Noise(&sp->np_filler_depth, seed, csize.X, csize.Z);
+
+ //// 3D noise
+ // 1-down overgeneraion
+ noise_cave1 = new Noise(&sp->np_cave1, seed, csize.X, csize.Y + 1, csize.Z);
+ noise_cave2 = new Noise(&sp->np_cave2, seed, csize.X, csize.Y + 1, csize.Z);
+
+ //// Biome noise
+ noise_heat = new Noise(&params->np_biome_heat, seed, csize.X, csize.Z);
+ noise_humidity = new Noise(&params->np_biome_humidity, seed, csize.X, csize.Z);
+ noise_heat_blend = new Noise(&params->np_biome_heat_blend, seed, csize.X, csize.Z);
+ noise_humidity_blend = new Noise(&params->np_biome_humidity_blend, seed, csize.X, csize.Z);
+
+ //// Resolve nodes to be used
+ INodeDefManager *ndef = emerge->ndef;
+
+ c_stone = ndef->getId("mapgen_stone");
+ c_water_source = ndef->getId("mapgen_water_source");
+ c_lava_source = ndef->getId("mapgen_lava_source");
+ c_desert_stone = ndef->getId("mapgen_desert_stone");
+ c_ice = ndef->getId("mapgen_ice");
+ c_sandstone = ndef->getId("mapgen_sandstone");
+
+ c_cobble = ndef->getId("mapgen_cobble");
+ c_stair_cobble = ndef->getId("mapgen_stair_cobble");
+ c_mossycobble = ndef->getId("mapgen_mossycobble");
+ c_sandstonebrick = ndef->getId("mapgen_sandstonebrick");
+ c_stair_sandstonebrick = ndef->getId("mapgen_stair_sandstonebrick");
+
+ if (c_ice == CONTENT_IGNORE)
+ c_ice = CONTENT_AIR;
+ if (c_mossycobble == CONTENT_IGNORE)
+ c_mossycobble = c_cobble;
+ if (c_stair_cobble == CONTENT_IGNORE)
+ c_stair_cobble = c_cobble;
+ if (c_sandstonebrick == CONTENT_IGNORE)
+ c_sandstonebrick = c_sandstone;
+ if (c_stair_sandstonebrick == CONTENT_IGNORE)
+ c_stair_sandstonebrick = c_sandstone;
+}
+
+
+MapgenFlat::~MapgenFlat()
+{
+ delete noise_terrain;
+ delete noise_filler_depth;
+ delete noise_cave1;
+ delete noise_cave2;
+
+ delete noise_heat;
+ delete noise_humidity;
+ delete noise_heat_blend;
+ delete noise_humidity_blend;
+
+ delete[] heightmap;
+ delete[] biomemap;
+}
+
+
+MapgenFlatParams::MapgenFlatParams()
+{
+ spflags = 0;
+ ground_level = 8;
+ large_cave_depth = -33;
+ cave_width = 0.3;
+ lake_threshold = -0.45;
+ lake_steepness = 48.0;
+ hill_threshold = 0.45;
+ hill_steepness = 64.0;
+
+ np_terrain = NoiseParams(0, 1, v3f(600, 600, 600), 7244, 5, 0.6, 2.0);
+ np_filler_depth = NoiseParams(0, 1.2, v3f(150, 150, 150), 261, 3, 0.7, 2.0);
+ np_cave1 = NoiseParams(0, 12, v3f(96, 96, 96), 52534, 4, 0.5, 2.0);
+ np_cave2 = NoiseParams(0, 12, v3f(96, 96, 96), 10325, 4, 0.5, 2.0);
+}
+
+
+void MapgenFlatParams::readParams(const Settings *settings)
+{
+ settings->getFlagStrNoEx("mgflat_spflags", spflags, flagdesc_mapgen_flat);
+ settings->getS16NoEx("mgflat_ground_level", ground_level);
+ settings->getS16NoEx("mgflat_large_cave_depth", large_cave_depth);
+ settings->getFloatNoEx("mgflat_cave_width", cave_width);
+ settings->getFloatNoEx("mgflat_lake_threshold", lake_threshold);
+ settings->getFloatNoEx("mgflat_lake_steepness", lake_steepness);
+ settings->getFloatNoEx("mgflat_hill_threshold", hill_threshold);
+ settings->getFloatNoEx("mgflat_hill_steepness", hill_steepness);
+
+ settings->getNoiseParams("mgflat_np_terrain", np_terrain);
+ settings->getNoiseParams("mgflat_np_filler_depth", np_filler_depth);
+ settings->getNoiseParams("mgflat_np_cave1", np_cave1);
+ settings->getNoiseParams("mgflat_np_cave2", np_cave2);
+}
+
+
+void MapgenFlatParams::writeParams(Settings *settings) const
+{
+ settings->setFlagStr("mgflat_spflags", spflags, flagdesc_mapgen_flat, U32_MAX);
+ settings->setS16("mgflat_ground_level", ground_level);
+ settings->setS16("mgflat_large_cave_depth", large_cave_depth);
+ settings->setFloat("mgflat_cave_width", cave_width);
+ settings->setFloat("mgflat_lake_threshold", lake_threshold);
+ settings->setFloat("mgflat_lake_steepness", lake_steepness);
+ settings->setFloat("mgflat_hill_threshold", hill_threshold);
+ settings->setFloat("mgflat_hill_steepness", hill_steepness);
+
+ settings->setNoiseParams("mgflat_np_terrain", np_terrain);
+ settings->setNoiseParams("mgflat_np_filler_depth", np_filler_depth);
+ settings->setNoiseParams("mgflat_np_cave1", np_cave1);
+ settings->setNoiseParams("mgflat_np_cave2", np_cave2);
+}
+
+
+/////////////////////////////////////////////////////////////////
+
+
+int MapgenFlat::getSpawnLevelAtPoint(v2s16 p)
+{
+ s16 level_at_point = ground_level;
+ float n_terrain = NoisePerlin2D(&noise_terrain->np, p.X, p.Y, seed);
+
+ if ((spflags & MGFLAT_LAKES) && n_terrain < lake_threshold) {
+ level_at_point = ground_level -
+ (lake_threshold - n_terrain) * lake_steepness;
+ } else if ((spflags & MGFLAT_HILLS) && n_terrain > hill_threshold) {
+ level_at_point = ground_level +
+ (n_terrain - hill_threshold) * hill_steepness;
+ }
+
+ if (ground_level < water_level) // Ocean world, allow spawn in water
+ return MYMAX(level_at_point, water_level);
+ else if (level_at_point > water_level)
+ return level_at_point; // Spawn on land
+ else
+ return MAX_MAP_GENERATION_LIMIT; // Unsuitable spawn point
+}
+
+
+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;
+ this->ndef = data->nodedef;
+ //TimeTaker t("makeChunk");
+
+ v3s16 blockpos_min = data->blockpos_min;
+ v3s16 blockpos_max = data->blockpos_max;
+ node_min = blockpos_min * MAP_BLOCKSIZE;
+ node_max = (blockpos_max + v3s16(1, 1, 1)) * MAP_BLOCKSIZE - v3s16(1, 1, 1);
+ full_node_min = (blockpos_min - 1) * MAP_BLOCKSIZE;
+ full_node_max = (blockpos_max + 2) * MAP_BLOCKSIZE - v3s16(1, 1, 1);
+
+ blockseed = getBlockSeed2(full_node_min, seed);
+
+ // Make some noise
+ calculateNoise();
+
+ // Generate base terrain, mountains, and ridges with initial heightmaps
+ s16 stone_surface_max_y = generateTerrain();
+
+ // Create heightmap
+ updateHeightmap(node_min, node_max);
+
+ // Create biomemap at heightmap surface
+ bmgr->calcBiomes(csize.X, csize.Z, noise_heat->result,
+ noise_humidity->result, heightmap, biomemap);
+
+ // Actually place the biome-specific nodes
+ MgStoneType stone_type = generateBiomes(noise_heat->result, noise_humidity->result);
+
+ if (flags & MG_CAVES)
+ generateCaves(stone_surface_max_y);
+
+ if ((flags & MG_DUNGEONS) && (stone_surface_max_y >= node_min.Y)) {
+ DungeonParams dp;
+
+ dp.np_rarity = nparams_dungeon_rarity;
+ dp.np_density = nparams_dungeon_density;
+ dp.np_wetness = nparams_dungeon_wetness;
+ dp.c_water = c_water_source;
+ if (stone_type == STONE) {
+ dp.c_cobble = c_cobble;
+ dp.c_moss = c_mossycobble;
+ dp.c_stair = c_stair_cobble;
+
+ dp.diagonal_dirs = false;
+ dp.mossratio = 3.0;
+ dp.holesize = v3s16(1, 2, 1);
+ dp.roomsize = v3s16(0, 0, 0);
+ dp.notifytype = GENNOTIFY_DUNGEON;
+ } else if (stone_type == DESERT_STONE) {
+ dp.c_cobble = c_desert_stone;
+ dp.c_moss = c_desert_stone;
+ dp.c_stair = c_desert_stone;
+
+ dp.diagonal_dirs = true;
+ dp.mossratio = 0.0;
+ dp.holesize = v3s16(2, 3, 2);
+ dp.roomsize = v3s16(2, 5, 2);
+ dp.notifytype = GENNOTIFY_TEMPLE;
+ } else if (stone_type == SANDSTONE) {
+ dp.c_cobble = c_sandstonebrick;
+ dp.c_moss = c_sandstonebrick;
+ dp.c_stair = c_sandstonebrick;
+
+ dp.diagonal_dirs = false;
+ dp.mossratio = 0.0;
+ dp.holesize = v3s16(2, 2, 2);
+ dp.roomsize = v3s16(2, 0, 2);
+ dp.notifytype = GENNOTIFY_DUNGEON;
+ }
+
+ DungeonGen dgen(this, &dp);
+ dgen.generate(blockseed, full_node_min, full_node_max);
+ }
+
+ // Generate the registered decorations
+ if (flags & MG_DECORATIONS)
+ m_emerge->decomgr->placeAllDecos(this, blockseed, node_min, node_max);
+
+ // Generate the registered ores
+ m_emerge->oremgr->placeAllOres(this, blockseed, node_min, node_max);
+
+ // Sprinkle some dust on top after everything else was generated
+ dustTopNodes();
+
+ //printf("makeChunk: %dms\n", t.stop());
+
+ updateLiquid(&data->transforming_liquid, full_node_min, full_node_max);
+
+ if (flags & MG_LIGHT)
+ calcLighting(node_min - v3s16(0, 1, 0), node_max + v3s16(0, 1, 0),
+ full_node_min, full_node_max);
+
+ //setLighting(node_min - v3s16(1, 0, 1) * MAP_BLOCKSIZE,
+ // node_max + v3s16(1, 0, 1) * MAP_BLOCKSIZE, 0xFF);
+
+ this->generating = false;
+}
+
+
+void MapgenFlat::calculateNoise()
+{
+ //TimeTaker t("calculateNoise", NULL, PRECISION_MICRO);
+ s16 x = node_min.X;
+ s16 z = node_min.Z;
+
+ if ((spflags & MGFLAT_LAKES) || (spflags & MGFLAT_HILLS))
+ noise_terrain->perlinMap2D(x, z);
+
+ // Cave noises are calculated in generateCaves()
+ // only if solid terrain is present in mapchunk
+
+ noise_filler_depth->perlinMap2D(x, z);
+ noise_heat->perlinMap2D(x, z);
+ noise_humidity->perlinMap2D(x, z);
+ noise_heat_blend->perlinMap2D(x, z);
+ noise_humidity_blend->perlinMap2D(x, z);
+
+ for (s32 i = 0; i < csize.X * csize.Z; i++) {
+ noise_heat->result[i] += noise_heat_blend->result[i];
+ noise_humidity->result[i] += noise_humidity_blend->result[i];
+ }
+
+ heatmap = noise_heat->result;
+ humidmap = noise_humidity->result;
+ //printf("calculateNoise: %dus\n", t.stop());
+}
+
+
+s16 MapgenFlat::generateTerrain()
+{
+ MapNode n_air(CONTENT_AIR);
+ MapNode n_stone(c_stone);
+ MapNode n_water(c_water_source);
+
+ v3s16 em = vm->m_area.getExtent();
+ s16 stone_surface_max_y = -MAX_MAP_GENERATION_LIMIT;
+ u32 ni2d = 0;
+
+ for (s16 z = node_min.Z; z <= node_max.Z; z++)
+ for (s16 x = node_min.X; x <= node_max.X; x++, ni2d++) {
+ s16 stone_level = ground_level;
+ float n_terrain = 0.0f;
+
+ if ((spflags & MGFLAT_LAKES) || (spflags & MGFLAT_HILLS))
+ n_terrain = noise_terrain->result[ni2d];
+
+ if ((spflags & MGFLAT_LAKES) && n_terrain < lake_threshold) {
+ s16 depress = (lake_threshold - n_terrain) * lake_steepness;
+ stone_level = ground_level - depress;
+ } else if ((spflags & MGFLAT_HILLS) && n_terrain > hill_threshold) {
+ s16 rise = (n_terrain - hill_threshold) * hill_steepness;
+ stone_level = ground_level + rise;
+ }
+
+ u32 vi = vm->m_area.index(x, node_min.Y - 1, z);
+ for (s16 y = node_min.Y - 1; y <= node_max.Y + 1; y++) {
+ if (vm->m_data[vi].getContent() == CONTENT_IGNORE) {
+ if (y <= stone_level) {
+ vm->m_data[vi] = n_stone;
+ if (y > stone_surface_max_y)
+ stone_surface_max_y = y;
+ } else if (y <= water_level) {
+ vm->m_data[vi] = n_water;
+ } else {
+ vm->m_data[vi] = n_air;
+ }
+ }
+ vm->m_area.add_y(em, vi, 1);
+ }
+ }
+
+ return stone_surface_max_y;
+}
+
+
+MgStoneType MapgenFlat::generateBiomes(float *heat_map, float *humidity_map)
+{
+ v3s16 em = vm->m_area.getExtent();
+ u32 index = 0;
+ MgStoneType stone_type = STONE;
+
+ for (s16 z = node_min.Z; z <= node_max.Z; z++)
+ for (s16 x = node_min.X; x <= node_max.X; x++, index++) {
+ Biome *biome = NULL;
+ u16 depth_top = 0;
+ u16 base_filler = 0;
+ u16 depth_water_top = 0;
+ u32 vi = vm->m_area.index(x, node_max.Y, z);
+
+ // Check node at base of mapchunk above, either a node of a previously
+ // generated mapchunk or if not, a node of overgenerated base terrain.
+ content_t c_above = vm->m_data[vi + em.X].getContent();
+ bool air_above = c_above == CONTENT_AIR;
+ bool water_above = c_above == c_water_source;
+
+ // If there is air or water above enable top/filler placement, otherwise force
+ // nplaced to stone level by setting a number exceeding any possible filler depth.
+ u16 nplaced = (air_above || water_above) ? 0 : U16_MAX;
+
+
+ for (s16 y = node_max.Y; y >= node_min.Y; y--) {
+ content_t c = vm->m_data[vi].getContent();
+
+ // Biome is recalculated each time an upper surface is detected while
+ // working down a column. The selected biome then remains in effect for
+ // all nodes below until the next surface and biome recalculation.
+ // Biome is recalculated:
+ // 1. At the surface of stone below air or water.
+ // 2. At the surface of water below air.
+ // 3. When stone or water is detected but biome has not yet been calculated.
+ if ((c == c_stone && (air_above || water_above || !biome)) ||
+ (c == c_water_source && (air_above || !biome))) {
+ biome = bmgr->getBiome(heat_map[index], humidity_map[index], y);
+ depth_top = biome->depth_top;
+ base_filler = MYMAX(depth_top + biome->depth_filler
+ + noise_filler_depth->result[index], 0);
+ depth_water_top = biome->depth_water_top;
+
+ // Detect stone type for dungeons during every biome calculation.
+ // This is more efficient than detecting per-node and will not
+ // miss any desert stone or sandstone biomes.
+ if (biome->c_stone == c_desert_stone)
+ stone_type = DESERT_STONE;
+ else if (biome->c_stone == c_sandstone)
+ stone_type = SANDSTONE;
+ }
+
+ if (c == c_stone) {
+ content_t c_below = vm->m_data[vi - em.X].getContent();
+
+ // If the node below isn't solid, make this node stone, so that
+ // any top/filler nodes above are structurally supported.
+ // This is done by aborting the cycle of top/filler placement
+ // immediately by forcing nplaced to stone level.
+ if (c_below == CONTENT_AIR || c_below == c_water_source)
+ nplaced = U16_MAX;
+
+ if (nplaced < depth_top) {
+ vm->m_data[vi] = MapNode(biome->c_top);
+ nplaced++;
+ } else if (nplaced < base_filler) {
+ vm->m_data[vi] = MapNode(biome->c_filler);
+ nplaced++;
+ } else {
+ vm->m_data[vi] = MapNode(biome->c_stone);
+ }
+
+ air_above = false;
+ water_above = false;
+ } else if (c == c_water_source) {
+ vm->m_data[vi] = MapNode((y > (s32)(water_level - depth_water_top)) ?
+ biome->c_water_top : biome->c_water);
+ nplaced = 0; // Enable top/filler placement for next surface
+ air_above = false;
+ water_above = true;
+ } else if (c == CONTENT_AIR) {
+ nplaced = 0; // Enable top/filler placement for next surface
+ air_above = true;
+ water_above = false;
+ } else { // Possible various nodes overgenerated from neighbouring mapchunks
+ nplaced = U16_MAX; // Disable top/filler placement
+ air_above = false;
+ water_above = false;
+ }
+
+ vm->m_area.add_y(em, vi, -1);
+ }
+ }
+
+ return stone_type;
+}
+
+
+void MapgenFlat::dustTopNodes()
+{
+ if (node_max.Y < water_level)
+ return;
+
+ v3s16 em = vm->m_area.getExtent();
+ u32 index = 0;
+
+ for (s16 z = node_min.Z; z <= node_max.Z; z++)
+ for (s16 x = node_min.X; x <= node_max.X; x++, index++) {
+ Biome *biome = (Biome *)bmgr->getRaw(biomemap[index]);
+
+ if (biome->c_dust == CONTENT_IGNORE)
+ continue;
+
+ u32 vi = vm->m_area.index(x, full_node_max.Y, z);
+ content_t c_full_max = vm->m_data[vi].getContent();
+ s16 y_start;
+
+ if (c_full_max == CONTENT_AIR) {
+ y_start = full_node_max.Y - 1;
+ } else if (c_full_max == CONTENT_IGNORE) {
+ vi = vm->m_area.index(x, node_max.Y + 1, z);
+ content_t c_max = vm->m_data[vi].getContent();
+
+ if (c_max == CONTENT_AIR)
+ y_start = node_max.Y;
+ else
+ continue;
+ } else {
+ continue;
+ }
+
+ vi = vm->m_area.index(x, y_start, z);
+ for (s16 y = y_start; y >= node_min.Y - 1; y--) {
+ if (vm->m_data[vi].getContent() != CONTENT_AIR)
+ break;
+
+ vm->m_area.add_y(em, vi, -1);
+ }
+
+ content_t c = vm->m_data[vi].getContent();
+ if (!ndef->get(c).buildable_to && c != CONTENT_IGNORE && c != biome->c_dust) {
+ vm->m_area.add_y(em, vi, 1);
+ vm->m_data[vi] = MapNode(biome->c_dust);
+ }
+ }
+}
+
+
+void MapgenFlat::generateCaves(s16 max_stone_y)
+{
+ if (max_stone_y < node_min.Y)
+ return;
+
+ noise_cave1->perlinMap3D(node_min.X, node_min.Y - 1, node_min.Z);
+ noise_cave2->perlinMap3D(node_min.X, node_min.Y - 1, node_min.Z);
+
+ v3s16 em = vm->m_area.getExtent();
+ u32 index2d = 0;
+
+ for (s16 z = node_min.Z; z <= node_max.Z; z++)
+ for (s16 x = node_min.X; x <= node_max.X; x++, index2d++) {
+ bool column_is_open = false; // Is column open to overground
+ bool is_tunnel = false; // Is tunnel or tunnel floor
+ u32 vi = vm->m_area.index(x, node_max.Y, z);
+ u32 index3d = (z - node_min.Z) * zstride_1d + csize.Y * ystride +
+ (x - node_min.X);
+ // Biome of column
+ Biome *biome = (Biome *)bmgr->getRaw(biomemap[index2d]);
+
+ // Don't excavate the overgenerated stone at node_max.Y + 1,
+ // this creates a 'roof' over the tunnel, preventing light in
+ // tunnels at mapchunk borders when generating mapchunks upwards.
+ // This 'roof' is removed when the mapchunk above is generated.
+ for (s16 y = node_max.Y; y >= node_min.Y - 1; y--,
+ index3d -= ystride,
+ vm->m_area.add_y(em, vi, -1)) {
+
+ content_t c = vm->m_data[vi].getContent();
+ if (c == CONTENT_AIR || c == biome->c_water_top ||
+ c == biome->c_water) {
+ column_is_open = true;
+ continue;
+ }
+ // Ground
+ float d1 = contour(noise_cave1->result[index3d]);
+ float d2 = contour(noise_cave2->result[index3d]);
+
+ if (d1 * d2 > cave_width && ndef->get(c).is_ground_content) {
+ // In tunnel and ground content, excavate
+ vm->m_data[vi] = MapNode(CONTENT_AIR);
+ is_tunnel = true;
+ } else {
+ // Not in tunnel or not ground content
+ if (is_tunnel && column_is_open &&
+ (c == biome->c_filler || c == biome->c_stone))
+ // Tunnel entrance floor
+ vm->m_data[vi] = MapNode(biome->c_top);
+
+ column_is_open = false;
+ is_tunnel = false;
+ }
+ }
+ }
+
+ if (node_max.Y > large_cave_depth)
+ return;
+
+ PseudoRandom ps(blockseed + 21343);
+ u32 bruises_count = ps.range(0, 2);
+ for (u32 i = 0; i < bruises_count; i++) {
+ CaveV5 cave(this, &ps);
+ cave.makeCave(node_min, node_max, max_stone_y);
+ }
+}
diff --git a/src/mapgen_flat.h b/src/mapgen_flat.h
new file mode 100644
index 000000000..8aed09be5
--- /dev/null
+++ b/src/mapgen_flat.h
@@ -0,0 +1,124 @@
+/*
+Minetest
+Copyright (C) 2010-2015 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
+Copyright (C) 2010-2015 paramat, Matt Gregory
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef MAPGEN_FLAT_HEADER
+#define MAPGEN_FLAT_HEADER
+
+#include "mapgen.h"
+
+/////// Mapgen Flat flags
+#define MGFLAT_LAKES 0x01
+#define MGFLAT_HILLS 0x02
+
+class BiomeManager;
+
+extern FlagDesc flagdesc_mapgen_flat[];
+
+
+struct MapgenFlatParams : public MapgenSpecificParams {
+ u32 spflags;
+ s16 ground_level;
+ s16 large_cave_depth;
+ float cave_width;
+ float lake_threshold;
+ float lake_steepness;
+ float hill_threshold;
+ float hill_steepness;
+ NoiseParams np_terrain;
+ NoiseParams np_filler_depth;
+ NoiseParams np_cave1;
+ NoiseParams np_cave2;
+
+ MapgenFlatParams();
+ ~MapgenFlatParams() {}
+
+ void readParams(const Settings *settings);
+ void writeParams(Settings *settings) const;
+};
+
+class MapgenFlat : public Mapgen {
+public:
+ EmergeManager *m_emerge;
+ BiomeManager *bmgr;
+
+ int ystride;
+ int zstride_1d;
+
+ v3s16 node_min;
+ v3s16 node_max;
+ v3s16 full_node_min;
+ v3s16 full_node_max;
+
+ u32 spflags;
+ s16 ground_level;
+ s16 large_cave_depth;
+ float cave_width;
+ float lake_threshold;
+ float lake_steepness;
+ float hill_threshold;
+ float hill_steepness;
+ Noise *noise_terrain;
+ Noise *noise_filler_depth;
+ Noise *noise_cave1;
+ Noise *noise_cave2;
+
+ Noise *noise_heat;
+ Noise *noise_humidity;
+ Noise *noise_heat_blend;
+ Noise *noise_humidity_blend;
+
+ content_t c_stone;
+ content_t c_water_source;
+ content_t c_lava_source;
+ content_t c_desert_stone;
+ content_t c_ice;
+ content_t c_sandstone;
+
+ content_t c_cobble;
+ content_t c_stair_cobble;
+ content_t c_mossycobble;
+ content_t c_sandstonebrick;
+ content_t c_stair_sandstonebrick;
+
+ MapgenFlat(int mapgenid, MapgenParams *params, EmergeManager *emerge);
+ ~MapgenFlat();
+
+ virtual void makeChunk(BlockMakeData *data);
+ int getSpawnLevelAtPoint(v2s16 p);
+ void calculateNoise();
+ s16 generateTerrain();
+ MgStoneType generateBiomes(float *heat_map, float *humidity_map);
+ void dustTopNodes();
+ void generateCaves(s16 max_stone_y);
+};
+
+struct MapgenFactoryFlat : public MapgenFactory {
+ Mapgen *createMapgen(int mgid, MapgenParams *params, EmergeManager *emerge)
+ {
+ return new MapgenFlat(mgid, params, emerge);
+ };
+
+ MapgenSpecificParams *createMapgenParams()
+ {
+ return new MapgenFlatParams();
+ };
+};
+
+#endif
diff --git a/src/mapgen_fractal.cpp b/src/mapgen_fractal.cpp
new file mode 100644
index 000000000..e2e29f875
--- /dev/null
+++ b/src/mapgen_fractal.cpp
@@ -0,0 +1,746 @@
+/*
+Minetest
+Copyright (C) 2010-2015 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
+Copyright (C) 2010-2015 paramat, Matt Gregory
+
+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 "mapgen.h"
+#include "voxel.h"
+#include "noise.h"
+#include "mapblock.h"
+#include "mapnode.h"
+#include "map.h"
+#include "content_sao.h"
+#include "nodedef.h"
+#include "voxelalgorithms.h"
+//#include "profiler.h" // For TimeTaker
+#include "settings.h" // For g_settings
+#include "emerge.h"
+#include "dungeongen.h"
+#include "cavegen.h"
+#include "treegen.h"
+#include "mg_biome.h"
+#include "mg_ore.h"
+#include "mg_decoration.h"
+#include "mapgen_fractal.h"
+
+
+FlagDesc flagdesc_mapgen_fractal[] = {
+ {NULL, 0}
+};
+
+///////////////////////////////////////////////////////////////////////////////////////
+
+
+MapgenFractal::MapgenFractal(int mapgenid, MapgenParams *params, EmergeManager *emerge)
+ : Mapgen(mapgenid, params, emerge)
+{
+ this->m_emerge = emerge;
+ this->bmgr = emerge->biomemgr;
+
+ //// amount of elements to skip for the next index
+ //// for noise/height/biome maps (not vmanip)
+ this->ystride = csize.X;
+ // 1-down overgeneration
+ this->zstride_1d = csize.X * (csize.Y + 1);
+
+ this->biomemap = new u8[csize.X * csize.Z];
+ this->heightmap = new s16[csize.X * csize.Z];
+ this->heatmap = NULL;
+ this->humidmap = NULL;
+
+ MapgenFractalParams *sp = (MapgenFractalParams *)params->sparams;
+
+ this->spflags = sp->spflags;
+ this->cave_width = sp->cave_width;
+ this->fractal = sp->fractal;
+ this->iterations = sp->iterations;
+ this->scale = sp->scale;
+ this->offset = sp->offset;
+ this->slice_w = sp->slice_w;
+ this->julia_x = sp->julia_x;
+ this->julia_y = sp->julia_y;
+ this->julia_z = sp->julia_z;
+ this->julia_w = sp->julia_w;
+
+ //// 2D terrain noise
+ noise_seabed = new Noise(&sp->np_seabed, seed, csize.X, csize.Z);
+ noise_filler_depth = new Noise(&sp->np_filler_depth, seed, csize.X, csize.Z);
+
+ //// 3D terrain noise
+ // 1-down overgeneraion
+ noise_cave1 = new Noise(&sp->np_cave1, seed, csize.X, csize.Y + 1, csize.Z);
+ noise_cave2 = new Noise(&sp->np_cave2, seed, csize.X, csize.Y + 1, csize.Z);
+
+ //// Biome noise
+ noise_heat = new Noise(&params->np_biome_heat, seed, csize.X, csize.Z);
+ noise_humidity = new Noise(&params->np_biome_humidity, seed, csize.X, csize.Z);
+ noise_heat_blend = new Noise(&params->np_biome_heat_blend, seed, csize.X, csize.Z);
+ noise_humidity_blend = new Noise(&params->np_biome_humidity_blend, seed, csize.X, csize.Z);
+
+ this->formula = fractal / 2 + fractal % 2;
+ this->julia = fractal % 2 == 0;
+
+ //// Resolve nodes to be used
+ INodeDefManager *ndef = emerge->ndef;
+
+ c_stone = ndef->getId("mapgen_stone");
+ c_water_source = ndef->getId("mapgen_water_source");
+ c_lava_source = ndef->getId("mapgen_lava_source");
+ c_desert_stone = ndef->getId("mapgen_desert_stone");
+ c_ice = ndef->getId("mapgen_ice");
+ c_sandstone = ndef->getId("mapgen_sandstone");
+
+ c_cobble = ndef->getId("mapgen_cobble");
+ c_stair_cobble = ndef->getId("mapgen_stair_cobble");
+ c_mossycobble = ndef->getId("mapgen_mossycobble");
+ c_sandstonebrick = ndef->getId("mapgen_sandstonebrick");
+ c_stair_sandstonebrick = ndef->getId("mapgen_stair_sandstonebrick");
+
+ if (c_ice == CONTENT_IGNORE)
+ c_ice = CONTENT_AIR;
+ if (c_mossycobble == CONTENT_IGNORE)
+ c_mossycobble = c_cobble;
+ if (c_stair_cobble == CONTENT_IGNORE)
+ c_stair_cobble = c_cobble;
+ if (c_sandstonebrick == CONTENT_IGNORE)
+ c_sandstonebrick = c_sandstone;
+ if (c_stair_sandstonebrick == CONTENT_IGNORE)
+ c_stair_sandstonebrick = c_sandstone;
+}
+
+
+MapgenFractal::~MapgenFractal()
+{
+ delete noise_seabed;
+ delete noise_filler_depth;
+ delete noise_cave1;
+ delete noise_cave2;
+
+ delete noise_heat;
+ delete noise_humidity;
+ delete noise_heat_blend;
+ delete noise_humidity_blend;
+
+ delete[] heightmap;
+ delete[] biomemap;
+}
+
+
+MapgenFractalParams::MapgenFractalParams()
+{
+ spflags = 0;
+ cave_width = 0.3;
+ fractal = 1;
+ iterations = 11;
+ scale = v3f(4096.0, 1024.0, 4096.0);
+ offset = v3f(1.79, 0.0, 0.0);
+ slice_w = 0.0;
+ julia_x = 0.33;
+ julia_y = 0.33;
+ julia_z = 0.33;
+ julia_w = 0.33;
+
+ np_seabed = NoiseParams(-14, 9, v3f(600, 600, 600), 41900, 5, 0.6, 2.0);
+ np_filler_depth = NoiseParams(0, 1.2, v3f(150, 150, 150), 261, 3, 0.7, 2.0);
+ np_cave1 = NoiseParams(0, 12, v3f(96, 96, 96), 52534, 4, 0.5, 2.0);
+ np_cave2 = NoiseParams(0, 12, v3f(96, 96, 96), 10325, 4, 0.5, 2.0);
+}
+
+
+void MapgenFractalParams::readParams(const Settings *settings)
+{
+ settings->getFlagStrNoEx("mgfractal_spflags", spflags, flagdesc_mapgen_fractal);
+ settings->getFloatNoEx("mgfractal_cave_width", cave_width);
+ settings->getU16NoEx("mgfractal_fractal", fractal);
+ settings->getU16NoEx("mgfractal_iterations", iterations);
+ settings->getV3FNoEx("mgfractal_scale", scale);
+ settings->getV3FNoEx("mgfractal_offset", offset);
+ settings->getFloatNoEx("mgfractal_slice_w", slice_w);
+ settings->getFloatNoEx("mgfractal_julia_x", julia_x);
+ settings->getFloatNoEx("mgfractal_julia_y", julia_y);
+ settings->getFloatNoEx("mgfractal_julia_z", julia_z);
+ settings->getFloatNoEx("mgfractal_julia_w", julia_w);
+
+ settings->getNoiseParams("mgfractal_np_seabed", np_seabed);
+ settings->getNoiseParams("mgfractal_np_filler_depth", np_filler_depth);
+ settings->getNoiseParams("mgfractal_np_cave1", np_cave1);
+ settings->getNoiseParams("mgfractal_np_cave2", np_cave2);
+}
+
+
+void MapgenFractalParams::writeParams(Settings *settings) const
+{
+ settings->setFlagStr("mgfractal_spflags", spflags, flagdesc_mapgen_fractal, U32_MAX);
+ settings->setFloat("mgfractal_cave_width", cave_width);
+ settings->setU16("mgfractal_fractal", fractal);
+ settings->setU16("mgfractal_iterations", iterations);
+ settings->setV3F("mgfractal_scale", scale);
+ settings->setV3F("mgfractal_offset", offset);
+ settings->setFloat("mgfractal_slice_w", slice_w);
+ settings->setFloat("mgfractal_julia_x", julia_x);
+ settings->setFloat("mgfractal_julia_y", julia_y);
+ settings->setFloat("mgfractal_julia_z", julia_z);
+ settings->setFloat("mgfractal_julia_w", julia_w);
+
+ settings->setNoiseParams("mgfractal_np_seabed", np_seabed);
+ settings->setNoiseParams("mgfractal_np_filler_depth", np_filler_depth);
+ settings->setNoiseParams("mgfractal_np_cave1", np_cave1);
+ settings->setNoiseParams("mgfractal_np_cave2", np_cave2);
+}
+
+
+/////////////////////////////////////////////////////////////////
+
+
+int MapgenFractal::getSpawnLevelAtPoint(v2s16 p)
+{
+ bool solid_below = false; // Dry solid node is present below to spawn on
+ u8 air_count = 0; // Consecutive air nodes above the dry solid node
+ s16 seabed_level = NoisePerlin2D(&noise_seabed->np, p.X, p.Y, seed);
+ // Seabed can rise above water_level or might be raised to create dry land
+ s16 search_start = MYMAX(seabed_level, water_level + 1);
+ if (seabed_level > water_level)
+ solid_below = true;
+
+ for (s16 y = search_start; y <= search_start + 128; y++) {
+ if (getFractalAtPoint(p.X, y, p.Y)) { // Fractal node
+ solid_below = true;
+ air_count = 0;
+ } else if (solid_below) { // Air above solid node
+ air_count++;
+ if (air_count == 2)
+ return y - 2;
+ }
+ }
+
+ return MAX_MAP_GENERATION_LIMIT; // Unsuitable spawn point
+}
+
+
+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);
+
+ this->generating = true;
+ this->vm = data->vmanip;
+ this->ndef = data->nodedef;
+ //TimeTaker t("makeChunk");
+
+ v3s16 blockpos_min = data->blockpos_min;
+ v3s16 blockpos_max = data->blockpos_max;
+ node_min = blockpos_min * MAP_BLOCKSIZE;
+ node_max = (blockpos_max + v3s16(1, 1, 1)) * MAP_BLOCKSIZE - v3s16(1, 1, 1);
+ full_node_min = (blockpos_min - 1) * MAP_BLOCKSIZE;
+ full_node_max = (blockpos_max + 2) * MAP_BLOCKSIZE - v3s16(1, 1, 1);
+
+ blockseed = getBlockSeed2(full_node_min, seed);
+
+ // Make some noise
+ calculateNoise();
+
+ // Generate base terrain, mountains, and ridges with initial heightmaps
+ s16 stone_surface_max_y = generateTerrain();
+
+ // Create heightmap
+ updateHeightmap(node_min, node_max);
+
+ // Create biomemap at heightmap surface
+ bmgr->calcBiomes(csize.X, csize.Z, noise_heat->result,
+ noise_humidity->result, heightmap, biomemap);
+
+ // Actually place the biome-specific nodes
+ MgStoneType stone_type = generateBiomes(noise_heat->result, noise_humidity->result);
+
+ if (flags & MG_CAVES)
+ generateCaves(stone_surface_max_y);
+
+ if ((flags & MG_DUNGEONS) && (stone_surface_max_y >= node_min.Y)) {
+ DungeonParams dp;
+
+ dp.np_rarity = nparams_dungeon_rarity;
+ dp.np_density = nparams_dungeon_density;
+ dp.np_wetness = nparams_dungeon_wetness;
+ dp.c_water = c_water_source;
+ if (stone_type == STONE) {
+ dp.c_cobble = c_cobble;
+ dp.c_moss = c_mossycobble;
+ dp.c_stair = c_stair_cobble;
+
+ dp.diagonal_dirs = false;
+ dp.mossratio = 3.0;
+ dp.holesize = v3s16(1, 2, 1);
+ dp.roomsize = v3s16(0, 0, 0);
+ dp.notifytype = GENNOTIFY_DUNGEON;
+ } else if (stone_type == DESERT_STONE) {
+ dp.c_cobble = c_desert_stone;
+ dp.c_moss = c_desert_stone;
+ dp.c_stair = c_desert_stone;
+
+ dp.diagonal_dirs = true;
+ dp.mossratio = 0.0;
+ dp.holesize = v3s16(2, 3, 2);
+ dp.roomsize = v3s16(2, 5, 2);
+ dp.notifytype = GENNOTIFY_TEMPLE;
+ } else if (stone_type == SANDSTONE) {
+ dp.c_cobble = c_sandstonebrick;
+ dp.c_moss = c_sandstonebrick;
+ dp.c_stair = c_sandstonebrick;
+
+ dp.diagonal_dirs = false;
+ dp.mossratio = 0.0;
+ dp.holesize = v3s16(2, 2, 2);
+ dp.roomsize = v3s16(2, 0, 2);
+ dp.notifytype = GENNOTIFY_DUNGEON;
+ }
+
+ DungeonGen dgen(this, &dp);
+ dgen.generate(blockseed, full_node_min, full_node_max);
+ }
+
+ // Generate the registered decorations
+ if (flags & MG_DECORATIONS)
+ m_emerge->decomgr->placeAllDecos(this, blockseed, node_min, node_max);
+
+ // Generate the registered ores
+ m_emerge->oremgr->placeAllOres(this, blockseed, node_min, node_max);
+
+ // Sprinkle some dust on top after everything else was generated
+ dustTopNodes();
+
+ //printf("makeChunk: %dms\n", t.stop());
+
+ updateLiquid(&data->transforming_liquid, full_node_min, full_node_max);
+
+ if (flags & MG_LIGHT)
+ calcLighting(node_min - v3s16(0, 1, 0), node_max + v3s16(0, 1, 0),
+ full_node_min, full_node_max);
+
+ //setLighting(node_min - v3s16(1, 0, 1) * MAP_BLOCKSIZE,
+ // node_max + v3s16(1, 0, 1) * MAP_BLOCKSIZE, 0xFF);
+
+ this->generating = false;
+}
+
+
+void MapgenFractal::calculateNoise()
+{
+ //TimeTaker t("calculateNoise", NULL, PRECISION_MICRO);
+ s16 x = node_min.X;
+ s16 z = node_min.Z;
+
+ noise_seabed->perlinMap2D(x, z);
+
+ // Cave noises are calculated in generateCaves()
+ // only if solid terrain is present in mapchunk
+
+ noise_filler_depth->perlinMap2D(x, z);
+ noise_heat->perlinMap2D(x, z);
+ noise_humidity->perlinMap2D(x, z);
+ noise_heat_blend->perlinMap2D(x, z);
+ noise_humidity_blend->perlinMap2D(x, z);
+
+ for (s32 i = 0; i < csize.X * csize.Z; i++) {
+ noise_heat->result[i] += noise_heat_blend->result[i];
+ noise_humidity->result[i] += noise_humidity_blend->result[i];
+ }
+
+ heatmap = noise_heat->result;
+ humidmap = noise_humidity->result;
+ //printf("calculateNoise: %dus\n", t.stop());
+}
+
+
+bool MapgenFractal::getFractalAtPoint(s16 x, s16 y, s16 z)
+{
+ float cx, cy, cz, cw, ox, oy, oz, ow;
+
+ if (julia) { // Julia set
+ cx = julia_x;
+ cy = julia_y;
+ cz = julia_z;
+ cw = julia_w;
+ ox = (float)x / scale.X - offset.X;
+ oy = (float)y / scale.Y - offset.Y;
+ oz = (float)z / scale.Z - offset.Z;
+ ow = slice_w;
+ } else { // Mandelbrot set
+ cx = (float)x / scale.X - offset.X;
+ cy = (float)y / scale.Y - offset.Y;
+ cz = (float)z / scale.Z - offset.Z;
+ cw = slice_w;
+ ox = 0.0f;
+ oy = 0.0f;
+ oz = 0.0f;
+ ow = 0.0f;
+ }
+
+ float nx = 0.0f;
+ float ny = 0.0f;
+ float nz = 0.0f;
+ float nw = 0.0f;
+
+ for (u16 iter = 0; iter < iterations; iter++) {
+
+ if (formula == 1) { // 4D "Roundy"
+ nx = ox * ox - oy * oy - oz * oz - ow * ow + cx;
+ ny = 2.0f * (ox * oy + oz * ow) + cy;
+ nz = 2.0f * (ox * oz + oy * ow) + cz;
+ nw = 2.0f * (ox * ow + oy * oz) + cw;
+ } else if (formula == 2) { // 4D "Squarry"
+ nx = ox * ox - oy * oy - oz * oz - ow * ow + cx;
+ ny = 2.0f * (ox * oy + oz * ow) + cy;
+ nz = 2.0f * (ox * oz + oy * ow) + cz;
+ nw = 2.0f * (ox * ow - oy * oz) + cw;
+ } else if (formula == 3) { // 4D "Mandy Cousin"
+ nx = ox * ox - oy * oy - oz * oz + ow * ow + cx;
+ ny = 2.0f * (ox * oy + oz * ow) + cy;
+ nz = 2.0f * (ox * oz + oy * ow) + cz;
+ nw = 2.0f * (ox * ow + oy * oz) + cw;
+ } else if (formula == 4) { // 4D "Variation"
+ nx = ox * ox - oy * oy - oz * oz - ow * ow + cx;
+ ny = 2.0f * (ox * oy + oz * ow) + cy;
+ nz = 2.0f * (ox * oz - oy * ow) + cz;
+ nw = 2.0f * (ox * ow + oy * oz) + cw;
+ } else if (formula == 5) { // 3D "Mandelbrot/Mandelbar"
+ nx = ox * ox - oy * oy - oz * oz + cx;
+ ny = 2.0f * ox * oy + cy;
+ nz = -2.0f * ox * oz + cz;
+ } else if (formula == 6) { // 3D "Christmas Tree"
+ // Altering the formula here is necessary to avoid division by zero
+ if (fabs(oz) < 0.000000001f) {
+ nx = ox * ox - oy * oy - oz * oz + cx;
+ ny = 2.0f * oy * ox + cy;
+ nz = 4.0f * oz * ox + cz;
+ } else {
+ float a = (2.0f * ox) / (sqrt(oy * oy + oz * oz));
+ nx = ox * ox - oy * oy - oz * oz + cx;
+ ny = a * (oy * oy - oz * oz) + cy;
+ nz = a * 2.0f * oy * oz + cz;
+ }
+ } else if (formula == 7) { // 3D "Mandelbulb"
+ if (fabs(oy) < 0.000000001f) {
+ nx = ox * ox - oz * oz + cx;
+ ny = cy;
+ nz = -2.0f * oz * sqrt(ox * ox) + cz;
+ } else {
+ float a = 1.0f - (oz * oz) / (ox * ox + oy * oy);
+ nx = (ox * ox - oy * oy) * a + cx;
+ ny = 2.0f * ox * oy * a + cy;
+ nz = -2.0f * oz * sqrt(ox * ox + oy * oy) + cz;
+ }
+ } else if (formula == 8) { // 3D "Cosine Mandelbulb"
+ if (fabs(oy) < 0.000000001f) {
+ nx = 2.0f * ox * oz + cx;
+ ny = 4.0f * oy * oz + cy;
+ nz = oz * oz - ox * ox - oy * oy + cz;
+ } else {
+ float a = (2.0f * oz) / sqrt(ox * ox + oy * oy);
+ nx = (ox * ox - oy * oy) * a + cx;
+ ny = 2.0f * ox * oy * a + cy;
+ nz = oz * oz - ox * ox - oy * oy + cz;
+ }
+ } else if (formula == 9) { // 4D "Mandelbulb"
+ float rxy = sqrt(ox * ox + oy * oy);
+ float rxyz = sqrt(ox * ox + oy * oy + oz * oz);
+ if (fabs(ow) < 0.000000001f && fabs(oz) < 0.000000001f) {
+ nx = (ox * ox - oy * oy) + cx;
+ ny = 2.0f * ox * oy + cy;
+ nz = -2.0f * rxy * oz + cz;
+ nw = 2.0f * rxyz * ow + cw;
+ } else {
+ float a = 1.0f - (ow * ow) / (rxyz * rxyz);
+ float b = a * (1.0f - (oz * oz) / (rxy * rxy));
+ nx = (ox * ox - oy * oy) * b + cx;
+ ny = 2.0f * ox * oy * b + cy;
+ nz = -2.0f * rxy * oz * a + cz;
+ nw = 2.0f * rxyz * ow + cw;
+ }
+ }
+
+ if (nx * nx + ny * ny + nz * nz + nw * nw > 4.0f)
+ return false;
+
+ ox = nx;
+ oy = ny;
+ oz = nz;
+ ow = nw;
+ }
+
+ return true;
+}
+
+
+s16 MapgenFractal::generateTerrain()
+{
+ MapNode n_air(CONTENT_AIR);
+ MapNode n_stone(c_stone);
+ MapNode n_water(c_water_source);
+
+ s16 stone_surface_max_y = -MAX_MAP_GENERATION_LIMIT;
+ u32 index2d = 0;
+
+ 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++, vi++, index2d++) {
+ if (vm->m_data[vi].getContent() == CONTENT_IGNORE) {
+ s16 seabed_height = noise_seabed->result[index2d];
+
+ if (y <= seabed_height || getFractalAtPoint(x, y, z)) {
+ vm->m_data[vi] = n_stone;
+ if (y > stone_surface_max_y)
+ stone_surface_max_y = y;
+ } else if (y <= water_level) {
+ vm->m_data[vi] = n_water;
+ } else {
+ vm->m_data[vi] = n_air;
+ }
+ }
+ }
+ index2d -= ystride;
+ }
+ index2d += ystride;
+ }
+
+ return stone_surface_max_y;
+}
+
+
+MgStoneType MapgenFractal::generateBiomes(float *heat_map, float *humidity_map)
+{
+ v3s16 em = vm->m_area.getExtent();
+ u32 index = 0;
+ MgStoneType stone_type = STONE;
+
+ for (s16 z = node_min.Z; z <= node_max.Z; z++)
+ for (s16 x = node_min.X; x <= node_max.X; x++, index++) {
+ Biome *biome = NULL;
+ u16 depth_top = 0;
+ u16 base_filler = 0;
+ u16 depth_water_top = 0;
+ u32 vi = vm->m_area.index(x, node_max.Y, z);
+
+ // Check node at base of mapchunk above, either a node of a previously
+ // generated mapchunk or if not, a node of overgenerated base terrain.
+ content_t c_above = vm->m_data[vi + em.X].getContent();
+ bool air_above = c_above == CONTENT_AIR;
+ bool water_above = c_above == c_water_source;
+
+ // If there is air or water above enable top/filler placement, otherwise force
+ // nplaced to stone level by setting a number exceeding any possible filler depth.
+ u16 nplaced = (air_above || water_above) ? 0 : U16_MAX;
+
+
+ for (s16 y = node_max.Y; y >= node_min.Y; y--) {
+ content_t c = vm->m_data[vi].getContent();
+
+ // Biome is recalculated each time an upper surface is detected while
+ // working down a column. The selected biome then remains in effect for
+ // all nodes below until the next surface and biome recalculation.
+ // Biome is recalculated:
+ // 1. At the surface of stone below air or water.
+ // 2. At the surface of water below air.
+ // 3. When stone or water is detected but biome has not yet been calculated.
+ if ((c == c_stone && (air_above || water_above || !biome)) ||
+ (c == c_water_source && (air_above || !biome))) {
+ biome = bmgr->getBiome(heat_map[index], humidity_map[index], y);
+ depth_top = biome->depth_top;
+ base_filler = MYMAX(depth_top + biome->depth_filler
+ + noise_filler_depth->result[index], 0);
+ depth_water_top = biome->depth_water_top;
+
+ // Detect stone type for dungeons during every biome calculation.
+ // This is more efficient than detecting per-node and will not
+ // miss any desert stone or sandstone biomes.
+ if (biome->c_stone == c_desert_stone)
+ stone_type = DESERT_STONE;
+ else if (biome->c_stone == c_sandstone)
+ stone_type = SANDSTONE;
+ }
+
+ if (c == c_stone) {
+ content_t c_below = vm->m_data[vi - em.X].getContent();
+
+ // If the node below isn't solid, make this node stone, so that
+ // any top/filler nodes above are structurally supported.
+ // This is done by aborting the cycle of top/filler placement
+ // immediately by forcing nplaced to stone level.
+ if (c_below == CONTENT_AIR || c_below == c_water_source)
+ nplaced = U16_MAX;
+
+ if (nplaced < depth_top) {
+ vm->m_data[vi] = MapNode(biome->c_top);
+ nplaced++;
+ } else if (nplaced < base_filler) {
+ vm->m_data[vi] = MapNode(biome->c_filler);
+ nplaced++;
+ } else {
+ vm->m_data[vi] = MapNode(biome->c_stone);
+ }
+
+ air_above = false;
+ water_above = false;
+ } else if (c == c_water_source) {
+ vm->m_data[vi] = MapNode((y > (s32)(water_level - depth_water_top)) ?
+ biome->c_water_top : biome->c_water);
+ nplaced = 0; // Enable top/filler placement for next surface
+ air_above = false;
+ water_above = true;
+ } else if (c == CONTENT_AIR) {
+ nplaced = 0; // Enable top/filler placement for next surface
+ air_above = true;
+ water_above = false;
+ } else { // Possible various nodes overgenerated from neighbouring mapchunks
+ nplaced = U16_MAX; // Disable top/filler placement
+ air_above = false;
+ water_above = false;
+ }
+
+ vm->m_area.add_y(em, vi, -1);
+ }
+ }
+
+ return stone_type;
+}
+
+
+void MapgenFractal::dustTopNodes()
+{
+ if (node_max.Y < water_level)
+ return;
+
+ v3s16 em = vm->m_area.getExtent();
+ u32 index = 0;
+
+ for (s16 z = node_min.Z; z <= node_max.Z; z++)
+ for (s16 x = node_min.X; x <= node_max.X; x++, index++) {
+ Biome *biome = (Biome *)bmgr->getRaw(biomemap[index]);
+
+ if (biome->c_dust == CONTENT_IGNORE)
+ continue;
+
+ u32 vi = vm->m_area.index(x, full_node_max.Y, z);
+ content_t c_full_max = vm->m_data[vi].getContent();
+ s16 y_start;
+
+ if (c_full_max == CONTENT_AIR) {
+ y_start = full_node_max.Y - 1;
+ } else if (c_full_max == CONTENT_IGNORE) {
+ vi = vm->m_area.index(x, node_max.Y + 1, z);
+ content_t c_max = vm->m_data[vi].getContent();
+
+ if (c_max == CONTENT_AIR)
+ y_start = node_max.Y;
+ else
+ continue;
+ } else {
+ continue;
+ }
+
+ vi = vm->m_area.index(x, y_start, z);
+ for (s16 y = y_start; y >= node_min.Y - 1; y--) {
+ if (vm->m_data[vi].getContent() != CONTENT_AIR)
+ break;
+
+ vm->m_area.add_y(em, vi, -1);
+ }
+
+ content_t c = vm->m_data[vi].getContent();
+ if (!ndef->get(c).buildable_to && c != CONTENT_IGNORE && c != biome->c_dust) {
+ vm->m_area.add_y(em, vi, 1);
+ vm->m_data[vi] = MapNode(biome->c_dust);
+ }
+ }
+}
+
+
+void MapgenFractal::generateCaves(s16 max_stone_y)
+{
+ if (max_stone_y < node_min.Y)
+ return;
+
+ noise_cave1->perlinMap3D(node_min.X, node_min.Y - 1, node_min.Z);
+ noise_cave2->perlinMap3D(node_min.X, node_min.Y - 1, node_min.Z);
+
+ v3s16 em = vm->m_area.getExtent();
+ u32 index2d = 0;
+
+ for (s16 z = node_min.Z; z <= node_max.Z; z++)
+ for (s16 x = node_min.X; x <= node_max.X; x++, index2d++) {
+ bool column_is_open = false; // Is column open to overground
+ bool is_tunnel = false; // Is tunnel or tunnel floor
+ u32 vi = vm->m_area.index(x, node_max.Y, z);
+ u32 index3d = (z - node_min.Z) * zstride_1d + csize.Y * ystride +
+ (x - node_min.X);
+ // Biome of column
+ Biome *biome = (Biome *)bmgr->getRaw(biomemap[index2d]);
+
+ // Don't excavate the overgenerated stone at node_max.Y + 1,
+ // this creates a 'roof' over the tunnel, preventing light in
+ // tunnels at mapchunk borders when generating mapchunks upwards.
+ // This 'roof' is removed when the mapchunk above is generated.
+ for (s16 y = node_max.Y; y >= node_min.Y - 1; y--,
+ index3d -= ystride,
+ vm->m_area.add_y(em, vi, -1)) {
+
+ content_t c = vm->m_data[vi].getContent();
+ if (c == CONTENT_AIR || c == biome->c_water_top ||
+ c == biome->c_water) {
+ column_is_open = true;
+ continue;
+ }
+ // Ground
+ float d1 = contour(noise_cave1->result[index3d]);
+ float d2 = contour(noise_cave2->result[index3d]);
+
+ if (d1 * d2 > cave_width && ndef->get(c).is_ground_content) {
+ // In tunnel and ground content, excavate
+ vm->m_data[vi] = MapNode(CONTENT_AIR);
+ is_tunnel = true;
+ } else {
+ // Not in tunnel or not ground content
+ if (is_tunnel && column_is_open &&
+ (c == biome->c_filler || c == biome->c_stone))
+ // Tunnel entrance floor
+ vm->m_data[vi] = MapNode(biome->c_top);
+
+ column_is_open = false;
+ is_tunnel = false;
+ }
+ }
+ }
+
+ if (node_max.Y > MGFRACTAL_LARGE_CAVE_DEPTH)
+ return;
+
+ PseudoRandom ps(blockseed + 21343);
+ u32 bruises_count = ps.range(0, 2);
+ for (u32 i = 0; i < bruises_count; i++) {
+ CaveV5 cave(this, &ps);
+ cave.makeCave(node_min, node_max, max_stone_y);
+ }
+}
diff --git a/src/mapgen_fractal.h b/src/mapgen_fractal.h
new file mode 100644
index 000000000..dd96045e2
--- /dev/null
+++ b/src/mapgen_fractal.h
@@ -0,0 +1,134 @@
+/*
+Minetest
+Copyright (C) 2010-2015 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
+Copyright (C) 2010-2015 paramat, Matt Gregory
+
+Fractal formulas from http://www.bugman123.com/Hypercomplex/index.html
+by Paul Nylander, and from http://www.fractalforums.com, thank you.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef MAPGEN_FRACTAL_HEADER
+#define MAPGEN_FRACTAL_HEADER
+
+#include "mapgen.h"
+
+#define MGFRACTAL_LARGE_CAVE_DEPTH -33
+
+class BiomeManager;
+
+extern FlagDesc flagdesc_mapgen_fractal[];
+
+
+struct MapgenFractalParams : public MapgenSpecificParams {
+ u32 spflags;
+ float cave_width;
+ u16 fractal;
+ u16 iterations;
+ v3f scale;
+ v3f offset;
+ float slice_w;
+ float julia_x;
+ float julia_y;
+ float julia_z;
+ float julia_w;
+ NoiseParams np_seabed;
+ NoiseParams np_filler_depth;
+ NoiseParams np_cave1;
+ NoiseParams np_cave2;
+
+ MapgenFractalParams();
+ ~MapgenFractalParams() {}
+
+ void readParams(const Settings *settings);
+ void writeParams(Settings *settings) const;
+};
+
+class MapgenFractal : public Mapgen {
+public:
+ EmergeManager *m_emerge;
+ BiomeManager *bmgr;
+
+ int ystride;
+ int zstride_1d;
+ u16 formula;
+ bool julia;
+
+ v3s16 node_min;
+ v3s16 node_max;
+ v3s16 full_node_min;
+ v3s16 full_node_max;
+
+ u32 spflags;
+ float cave_width;
+ u16 fractal;
+ u16 iterations;
+ v3f scale;
+ v3f offset;
+ float slice_w;
+ float julia_x;
+ float julia_y;
+ float julia_z;
+ float julia_w;
+ Noise *noise_seabed;
+ Noise *noise_filler_depth;
+ Noise *noise_cave1;
+ Noise *noise_cave2;
+
+ Noise *noise_heat;
+ Noise *noise_humidity;
+ Noise *noise_heat_blend;
+ Noise *noise_humidity_blend;
+
+ content_t c_stone;
+ content_t c_water_source;
+ content_t c_lava_source;
+ content_t c_desert_stone;
+ content_t c_ice;
+ content_t c_sandstone;
+
+ content_t c_cobble;
+ content_t c_stair_cobble;
+ content_t c_mossycobble;
+ content_t c_sandstonebrick;
+ content_t c_stair_sandstonebrick;
+
+ MapgenFractal(int mapgenid, MapgenParams *params, EmergeManager *emerge);
+ ~MapgenFractal();
+
+ virtual void makeChunk(BlockMakeData *data);
+ int getSpawnLevelAtPoint(v2s16 p);
+ void calculateNoise();
+ bool getFractalAtPoint(s16 x, s16 y, s16 z);
+ s16 generateTerrain();
+ MgStoneType generateBiomes(float *heat_map, float *humidity_map);
+ void dustTopNodes();
+ void generateCaves(s16 max_stone_y);
+};
+
+struct MapgenFactoryFractal : public MapgenFactory {
+ Mapgen *createMapgen(int mgid, MapgenParams *params, EmergeManager *emerge)
+ {
+ return new MapgenFractal(mgid, params, emerge);
+ };
+
+ MapgenSpecificParams *createMapgenParams()
+ {
+ return new MapgenFractalParams();
+ };
+};
+
+#endif
diff --git a/src/mapgen_singlenode.cpp b/src/mapgen_singlenode.cpp
index a8b84e849..ff985dd34 100644
--- a/src/mapgen_singlenode.cpp
+++ b/src/mapgen_singlenode.cpp
@@ -1,6 +1,6 @@
/*
Minetest
-Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
+Copyright (C) 2010-2015 celeron55, Perttu Ahola <celeron55@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
@@ -38,6 +38,9 @@ MapgenSinglenode::MapgenSinglenode(int mapgenid,
c_node = ndef->getId("mapgen_singlenode");
if (c_node == CONTENT_IGNORE)
c_node = CONTENT_AIR;
+
+ MapNode n_node(c_node);
+ set_light = (ndef->get(n_node).sunlight_propagates) ? LIGHT_SUN : 0x00;
}
@@ -45,6 +48,7 @@ MapgenSinglenode::~MapgenSinglenode()
{
}
+
//////////////////////// Map generator
void MapgenSinglenode::makeChunk(BlockMakeData *data)
@@ -53,11 +57,11 @@ void MapgenSinglenode::makeChunk(BlockMakeData *data)
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);
+ 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);
+ data->blockpos_requested.Y <= data->blockpos_max.Y &&
+ data->blockpos_requested.Z <= data->blockpos_max.Z);
this->generating = true;
this->vm = data->vmanip;
@@ -67,8 +71,8 @@ void MapgenSinglenode::makeChunk(BlockMakeData *data)
v3s16 blockpos_max = data->blockpos_max;
// Area of central chunk
- v3s16 node_min = blockpos_min*MAP_BLOCKSIZE;
- v3s16 node_max = (blockpos_max+v3s16(1,1,1))*MAP_BLOCKSIZE-v3s16(1,1,1);
+ v3s16 node_min = blockpos_min * MAP_BLOCKSIZE;
+ v3s16 node_max = (blockpos_max + v3s16(1, 1, 1)) * MAP_BLOCKSIZE - v3s16(1, 1, 1);
blockseed = getBlockSeed2(node_min, data->seed);
@@ -87,15 +91,15 @@ void MapgenSinglenode::makeChunk(BlockMakeData *data)
// Add top and bottom side of water to transforming_liquid queue
updateLiquid(&data->transforming_liquid, node_min, node_max);
- // Calculate lighting
- if (flags & MG_LIGHT)
- calcLighting(node_min, node_max);
+ // Set lighting
+ if ((flags & MG_LIGHT) && set_light == LIGHT_SUN)
+ setLighting(LIGHT_SUN, node_min, node_max);
this->generating = false;
}
-int MapgenSinglenode::getGroundLevelAtPoint(v2s16 p)
+
+int MapgenSinglenode::getSpawnLevelAtPoint(v2s16 p)
{
return 0;
}
-
diff --git a/src/mapgen_singlenode.h b/src/mapgen_singlenode.h
index 35f2ba385..2c6496c00 100644
--- a/src/mapgen_singlenode.h
+++ b/src/mapgen_singlenode.h
@@ -1,6 +1,6 @@
/*
Minetest
-Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
+Copyright (C) 2010-2015 celeron55, Perttu Ahola <celeron55@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
@@ -35,12 +35,13 @@ class MapgenSinglenode : public Mapgen {
public:
u32 flags;
content_t c_node;
+ u8 set_light;
MapgenSinglenode(int mapgenid, MapgenParams *params, EmergeManager *emerge);
~MapgenSinglenode();
void makeChunk(BlockMakeData *data);
- int getGroundLevelAtPoint(v2s16 p);
+ int getSpawnLevelAtPoint(v2s16 p);
};
struct MapgenFactorySinglenode : public MapgenFactory {
diff --git a/src/mapgen_v5.cpp b/src/mapgen_v5.cpp
index 5b842a99e..b98acb928 100644
--- a/src/mapgen_v5.cpp
+++ b/src/mapgen_v5.cpp
@@ -1,6 +1,7 @@
/*
Minetest
-Copyright (C) 2010-2013 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
+Copyright (C) 2010-2015 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
+Copyright (C) 2010-2015 paramat, Matt Gregory
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
@@ -27,6 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "content_sao.h"
#include "nodedef.h"
#include "voxelalgorithms.h"
+//#include "profiler.h" // For TimeTaker
#include "settings.h" // For g_settings
#include "emerge.h"
#include "dungeongen.h"
@@ -36,7 +38,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "mg_ore.h"
#include "mg_decoration.h"
#include "mapgen_v5.h"
-#include "util/directiontables.h"
FlagDesc flagdesc_mapgen_v5[] = {
@@ -53,7 +54,8 @@ MapgenV5::MapgenV5(int mapgenid, MapgenParams *params, EmergeManager *emerge)
// amount of elements to skip for the next index
// for noise/height/biome maps (not vmanip)
this->ystride = csize.X;
- this->zstride = csize.X * (csize.Y + 2);
+ // 1-down overgeneration
+ this->zstride_1d = csize.X * (csize.Y + 1);
this->biomemap = new u8[csize.X * csize.Z];
this->heightmap = new s16[csize.X * csize.Z];
@@ -61,7 +63,9 @@ MapgenV5::MapgenV5(int mapgenid, MapgenParams *params, EmergeManager *emerge)
this->humidmap = NULL;
MapgenV5Params *sp = (MapgenV5Params *)params->sparams;
- this->spflags = sp->spflags;
+
+ this->spflags = sp->spflags;
+ this->cave_width = sp->cave_width;
// Terrain noise
noise_filler_depth = new Noise(&sp->np_filler_depth, seed, csize.X, csize.Z);
@@ -69,9 +73,11 @@ MapgenV5::MapgenV5(int mapgenid, MapgenParams *params, EmergeManager *emerge)
noise_height = new Noise(&sp->np_height, seed, csize.X, csize.Z);
// 3D terrain noise
- noise_cave1 = new Noise(&sp->np_cave1, seed, csize.X, csize.Y + 2, csize.Z);
- noise_cave2 = new Noise(&sp->np_cave2, seed, csize.X, csize.Y + 2, csize.Z);
+ // 1-up 1-down overgeneration
noise_ground = new Noise(&sp->np_ground, seed, csize.X, csize.Y + 2, csize.Z);
+ // 1-down overgeneraion
+ noise_cave1 = new Noise(&sp->np_cave1, seed, csize.X, csize.Y + 1, csize.Z);
+ noise_cave2 = new Noise(&sp->np_cave2, seed, csize.X, csize.Y + 1, csize.Z);
// Biome noise
noise_heat = new Noise(&params->np_biome_heat, seed, csize.X, csize.Z);
@@ -129,7 +135,8 @@ MapgenV5::~MapgenV5()
MapgenV5Params::MapgenV5Params()
{
- spflags = 0;
+ spflags = 0;
+ cave_width = 0.125;
np_filler_depth = NoiseParams(0, 1, v3f(150, 150, 150), 261, 4, 0.7, 2.0);
np_factor = NoiseParams(0, 1, v3f(250, 250, 250), 920381, 3, 0.45, 2.0);
@@ -146,7 +153,8 @@ MapgenV5Params::MapgenV5Params()
void MapgenV5Params::readParams(const Settings *settings)
{
- settings->getFlagStrNoEx("mgv5_spflags", spflags, flagdesc_mapgen_v5);
+ settings->getFlagStrNoEx("mgv5_spflags", spflags, flagdesc_mapgen_v5);
+ settings->getFloatNoEx("mgv5_cave_width", cave_width);
settings->getNoiseParams("mgv5_np_filler_depth", np_filler_depth);
settings->getNoiseParams("mgv5_np_factor", np_factor);
@@ -159,7 +167,8 @@ void MapgenV5Params::readParams(const Settings *settings)
void MapgenV5Params::writeParams(Settings *settings) const
{
- settings->setFlagStr("mgv5_spflags", spflags, flagdesc_mapgen_v5, (u32)-1);
+ settings->setFlagStr("mgv5_spflags", spflags, flagdesc_mapgen_v5, U32_MAX);
+ settings->setFloat("mgv5_cave_width", cave_width);
settings->setNoiseParams("mgv5_np_filler_depth", np_filler_depth);
settings->setNoiseParams("mgv5_np_factor", np_factor);
@@ -170,7 +179,7 @@ void MapgenV5Params::writeParams(Settings *settings) const
}
-int MapgenV5::getGroundLevelAtPoint(v2s16 p)
+int MapgenV5::getSpawnLevelAtPoint(v2s16 p)
{
//TimeTaker t("getGroundLevelAtPoint", NULL, PRECISION_MICRO);
@@ -181,23 +190,25 @@ int MapgenV5::getGroundLevelAtPoint(v2s16 p)
f *= 1.6;
float h = NoisePerlin2D(&noise_height->np, p.X, p.Y, seed);
- s16 search_top = water_level + 15;
- s16 search_base = water_level;
-
- s16 level = -31000;
- for (s16 y = search_top; y >= search_base; y--) {
+ for (s16 y = 128; y >= -128; y--) {
float n_ground = NoisePerlin3D(&noise_ground->np, p.X, y, p.Y, seed);
- if (n_ground * f > y - h) {
- if (y >= search_top - 7)
- break;
- else
- level = y;
- break;
+
+ if (n_ground * f > y - h) { // If solid
+ // If either top 2 nodes of search are solid this is inside a
+ // mountain or floatland with possibly no space for the player to spawn.
+ if (y >= 127) {
+ return MAX_MAP_GENERATION_LIMIT; // Unsuitable spawn point
+ } else { // Ground below at least 2 nodes of empty space
+ if (y <= water_level || y > water_level + 16)
+ return MAX_MAP_GENERATION_LIMIT; // Unsuitable spawn point
+ else
+ return y;
+ }
}
}
//printf("getGroundLevelAtPoint: %dus\n", t.stop());
- return level;
+ return MAX_MAP_GENERATION_LIMIT; // Unsuitable spawn position, no ground found
}
@@ -213,9 +224,9 @@ void MapgenV5::makeChunk(BlockMakeData *data)
data->blockpos_requested.Y <= data->blockpos_max.Y &&
data->blockpos_requested.Z <= data->blockpos_max.Z);
- generating = true;
- vm = data->vmanip;
- ndef = data->nodedef;
+ this->generating = true;
+ this->vm = data->vmanip;
+ this->ndef = data->nodedef;
//TimeTaker t("makeChunk");
v3s16 blockpos_min = data->blockpos_min;
@@ -293,7 +304,8 @@ void MapgenV5::makeChunk(BlockMakeData *data)
}
// Generate the registered decorations
- m_emerge->decomgr->placeAllDecos(this, blockseed, node_min, node_max);
+ if (flags & MG_DECORATIONS)
+ m_emerge->decomgr->placeAllDecos(this, blockseed, node_min, node_max);
// Generate the registered ores
m_emerge->oremgr->placeAllOres(this, blockseed, node_min, node_max);
@@ -319,18 +331,16 @@ void MapgenV5::makeChunk(BlockMakeData *data)
void MapgenV5::calculateNoise()
{
//TimeTaker t("calculateNoise", NULL, PRECISION_MICRO);
- int x = node_min.X;
- int y = node_min.Y - 1;
- int z = node_min.Z;
+ s16 x = node_min.X;
+ s16 y = node_min.Y - 1;
+ s16 z = node_min.Z;
noise_factor->perlinMap2D(x, z);
noise_height->perlinMap2D(x, z);
noise_ground->perlinMap3D(x, y, z);
- if (flags & MG_CAVES) {
- noise_cave1->perlinMap3D(x, y, z);
- noise_cave2->perlinMap3D(x, y, z);
- }
+ // Cave noises are calculated in generateCaves()
+ // only if solid terrain is present in mapchunk
noise_filler_depth->perlinMap2D(x, z);
noise_heat->perlinMap2D(x, z);
@@ -374,9 +384,9 @@ int MapgenV5::generateBaseTerrain()
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 i = vm->m_area.index(node_min.X, y, z);
- for (s16 x=node_min.X; x<=node_max.X; x++, i++, index++, index2d++) {
- if (vm->m_data[i].getContent() != CONTENT_IGNORE)
+ u32 vi = vm->m_area.index(node_min.X, y, z);
+ for (s16 x=node_min.X; x<=node_max.X; x++, vi++, index++, index2d++) {
+ if (vm->m_data[vi].getContent() != CONTENT_IGNORE)
continue;
float f = 0.55 + noise_factor->result[index2d];
@@ -388,11 +398,11 @@ int MapgenV5::generateBaseTerrain()
if (noise_ground->result[index] * f < y - h) {
if (y <= water_level)
- vm->m_data[i] = MapNode(c_water_source);
+ vm->m_data[vi] = MapNode(c_water_source);
else
- vm->m_data[i] = MapNode(CONTENT_AIR);
+ vm->m_data[vi] = MapNode(CONTENT_AIR);
} else {
- vm->m_data[i] = MapNode(c_stone);
+ vm->m_data[vi] = MapNode(c_stone);
if (y > stone_surface_max_y)
stone_surface_max_y = y;
}
@@ -428,7 +438,7 @@ MgStoneType MapgenV5::generateBiomes(float *heat_map, float *humidity_map)
// If there is air or water above enable top/filler placement, otherwise force
// nplaced to stone level by setting a number exceeding any possible filler depth.
- u16 nplaced = (air_above || water_above) ? 0 : (u16)-1;
+ u16 nplaced = (air_above || water_above) ? 0 : U16_MAX;
for (s16 y = node_max.Y; y >= node_min.Y; y--) {
content_t c = vm->m_data[vi].getContent();
@@ -465,7 +475,7 @@ MgStoneType MapgenV5::generateBiomes(float *heat_map, float *humidity_map)
// This is done by aborting the cycle of top/filler placement
// immediately by forcing nplaced to stone level.
if (c_below == CONTENT_AIR || c_below == c_water_source)
- nplaced = (u16)-1;
+ nplaced = U16_MAX;
if (nplaced < depth_top) {
vm->m_data[vi] = MapNode(biome->c_top);
@@ -490,7 +500,7 @@ MgStoneType MapgenV5::generateBiomes(float *heat_map, float *humidity_map)
air_above = true;
water_above = false;
} else { // Possible various nodes overgenerated from neighbouring mapchunks
- nplaced = (u16)-1; // Disable top/filler placement
+ nplaced = U16_MAX; // Disable top/filler placement
air_above = false;
water_above = false;
}
@@ -503,40 +513,6 @@ MgStoneType MapgenV5::generateBiomes(float *heat_map, float *humidity_map)
}
-void MapgenV5::generateCaves(int max_stone_y)
-{
- if (max_stone_y >= node_min.Y) {
- u32 index = 0;
-
- 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 i = vm->m_area.index(node_min.X, y, z);
- for (s16 x = node_min.X; x <= node_max.X; x++, i++, index++) {
- float d1 = contour(noise_cave1->result[index]);
- float d2 = contour(noise_cave2->result[index]);
- if (d1*d2 > 0.125) {
- content_t c = vm->m_data[i].getContent();
- if (!ndef->get(c).is_ground_content || c == CONTENT_AIR)
- continue;
-
- vm->m_data[i] = MapNode(CONTENT_AIR);
- }
- }
- }
- }
-
- if (node_max.Y > LARGE_CAVE_DEPTH)
- return;
-
- PseudoRandom ps(blockseed + 21343);
- u32 bruises_count = (ps.range(1, 4) == 1) ? ps.range(1, 2) : 0;
- for (u32 i = 0; i < bruises_count; i++) {
- CaveV5 cave(this, &ps);
- cave.makeCave(node_min, node_max, max_stone_y);
- }
-}
-
-
void MapgenV5::dustTopNodes()
{
if (node_max.Y < water_level)
@@ -585,3 +561,72 @@ void MapgenV5::dustTopNodes()
}
}
}
+
+
+void MapgenV5::generateCaves(int max_stone_y)
+{
+ if (max_stone_y < node_min.Y)
+ return;
+
+ noise_cave1->perlinMap3D(node_min.X, node_min.Y - 1, node_min.Z);
+ noise_cave2->perlinMap3D(node_min.X, node_min.Y - 1, node_min.Z);
+
+ v3s16 em = vm->m_area.getExtent();
+ u32 index2d = 0;
+
+ for (s16 z = node_min.Z; z <= node_max.Z; z++)
+ for (s16 x = node_min.X; x <= node_max.X; x++, index2d++) {
+ bool column_is_open = false; // Is column open to overground
+ bool is_tunnel = false; // Is tunnel or tunnel floor
+ // Indexes at column top (node_max.Y)
+ u32 vi = vm->m_area.index(x, node_max.Y, z);
+ u32 index3d = (z - node_min.Z) * zstride_1d + csize.Y * ystride +
+ (x - node_min.X);
+ // Biome of column
+ Biome *biome = (Biome *)bmgr->getRaw(biomemap[index2d]);
+
+ // Don't excavate the overgenerated stone at node_max.Y + 1,
+ // this creates a 'roof' over the tunnel, preventing light in
+ // tunnels at mapchunk borders when generating mapchunks upwards.
+ // This 'roof' is removed when the mapchunk above is generated.
+ for (s16 y = node_max.Y; y >= node_min.Y - 1; y--,
+ index3d -= ystride,
+ vm->m_area.add_y(em, vi, -1)) {
+
+ content_t c = vm->m_data[vi].getContent();
+ if (c == CONTENT_AIR || c == biome->c_water_top ||
+ c == biome->c_water) {
+ column_is_open = true;
+ continue;
+ }
+ // Ground
+ float d1 = contour(noise_cave1->result[index3d]);
+ float d2 = contour(noise_cave2->result[index3d]);
+
+ if (d1 * d2 > cave_width && ndef->get(c).is_ground_content) {
+ // In tunnel and ground content, excavate
+ vm->m_data[vi] = MapNode(CONTENT_AIR);
+ is_tunnel = true;
+ } else {
+ // Not in tunnel or not ground content
+ if (is_tunnel && column_is_open &&
+ (c == biome->c_filler || c == biome->c_stone))
+ // Tunnel entrance floor
+ vm->m_data[vi] = MapNode(biome->c_top);
+
+ column_is_open = false;
+ is_tunnel = false;
+ }
+ }
+ }
+
+ if (node_max.Y > MGV5_LARGE_CAVE_DEPTH)
+ return;
+
+ PseudoRandom ps(blockseed + 21343);
+ u32 bruises_count = ps.range(0, 2);
+ for (u32 i = 0; i < bruises_count; i++) {
+ CaveV5 cave(this, &ps);
+ cave.makeCave(node_min, node_max, max_stone_y);
+ }
+}
diff --git a/src/mapgen_v5.h b/src/mapgen_v5.h
index a6fdc2b2b..fd2f7f4d8 100644
--- a/src/mapgen_v5.h
+++ b/src/mapgen_v5.h
@@ -1,6 +1,7 @@
/*
Minetest
-Copyright (C) 2010-2013 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
+Copyright (C) 2010-2015 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
+Copyright (C) 2010-2015 paramat, Matt Gregory
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
@@ -22,7 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "mapgen.h"
-#define LARGE_CAVE_DEPTH -256
+#define MGV5_LARGE_CAVE_DEPTH -256
class BiomeManager;
@@ -31,6 +32,7 @@ extern FlagDesc flagdesc_mapgen_v5[];
struct MapgenV5Params : public MapgenSpecificParams {
u32 spflags;
+ float cave_width;
NoiseParams np_filler_depth;
NoiseParams np_factor;
NoiseParams np_height;
@@ -52,14 +54,15 @@ public:
BiomeManager *bmgr;
int ystride;
- int zstride;
- u32 spflags;
+ int zstride_1d;
v3s16 node_min;
v3s16 node_max;
v3s16 full_node_min;
v3s16 full_node_max;
+ u32 spflags;
+ float cave_width;
Noise *noise_filler_depth;
Noise *noise_factor;
Noise *noise_height;
@@ -89,7 +92,7 @@ public:
~MapgenV5();
virtual void makeChunk(BlockMakeData *data);
- int getGroundLevelAtPoint(v2s16 p);
+ int getSpawnLevelAtPoint(v2s16 p);
void calculateNoise();
int generateBaseTerrain();
MgStoneType generateBiomes(float *heat_map, float *humidity_map);
diff --git a/src/mapgen_v6.cpp b/src/mapgen_v6.cpp
index 9e34aac2d..c389b2ed4 100644
--- a/src/mapgen_v6.cpp
+++ b/src/mapgen_v6.cpp
@@ -1,6 +1,6 @@
/*
Minetest
-Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
+Copyright (C) 2010-2015 celeron55, Perttu Ahola <celeron55@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
@@ -17,6 +17,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
+
#include "mapgen.h"
#include "voxel.h"
#include "noise.h"
@@ -26,8 +27,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
//#include "serverobject.h"
#include "content_sao.h"
#include "nodedef.h"
-#include "content_mapnode.h" // For content_mapnode_get_new_name
#include "voxelalgorithms.h"
+//#include "profiler.h" // For TimeTaker
#include "settings.h" // For g_settings
#include "emerge.h"
#include "dungeongen.h"
@@ -37,15 +38,19 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "mg_decoration.h"
#include "mapgen_v6.h"
+
FlagDesc flagdesc_mapgen_v6[] = {
{"jungles", MGV6_JUNGLES},
{"biomeblend", MGV6_BIOMEBLEND},
{"mudflow", MGV6_MUDFLOW},
{"snowbiomes", MGV6_SNOWBIOMES},
+ {"flat", MGV6_FLAT},
+ {"trees", MGV6_TREES},
{NULL, 0}
};
-///////////////////////////////////////////////////////////////////////////////
+
+/////////////////////////////////////////////////////////////////////////////
MapgenV6::MapgenV6(int mapgenid, MapgenParams *params, EmergeManager *emerge)
@@ -135,7 +140,9 @@ MapgenV6::~MapgenV6()
MapgenV6Params::MapgenV6Params()
{
- spflags = MGV6_BIOMEBLEND | MGV6_MUDFLOW;
+ spflags = MGV6_JUNGLES | MGV6_SNOWBIOMES | MGV6_TREES |
+ MGV6_BIOMEBLEND | MGV6_MUDFLOW;
+
freq_desert = 0.45;
freq_beach = 0.15;
@@ -175,7 +182,7 @@ void MapgenV6Params::readParams(const Settings *settings)
void MapgenV6Params::writeParams(Settings *settings) const
{
- settings->setFlagStr("mgv6_spflags", spflags, flagdesc_mapgen_v6, (u32)-1);
+ settings->setFlagStr("mgv6_spflags", spflags, flagdesc_mapgen_v6, U32_MAX);
settings->setFloat("mgv6_freq_desert", freq_desert);
settings->setFloat("mgv6_freq_beach", freq_beach);
@@ -195,7 +202,6 @@ void MapgenV6Params::writeParams(Settings *settings) const
//////////////////////// Some helper functions for the map generator
-
// Returns Y one under area minimum if not found
s16 MapgenV6::find_stone_level(v2s16 p2d)
{
@@ -263,7 +269,7 @@ float MapgenV6::baseTerrainLevel(float terrain_base, float terrain_higher,
float MapgenV6::baseTerrainLevelFromNoise(v2s16 p)
{
- if (flags & MG_FLAT)
+ if ((spflags & MGV6_FLAT) || (flags & MG_FLAT))
return water_level;
float terrain_base = NoisePerlin2D_PO(&noise_terrain_base->np,
@@ -289,7 +295,7 @@ float MapgenV6::baseTerrainLevelFromMap(v2s16 p)
float MapgenV6::baseTerrainLevelFromMap(int index)
{
- if (flags & MG_FLAT)
+ if ((spflags & MGV6_FLAT) || (flags & MG_FLAT))
return water_level;
float terrain_base = noise_terrain_base->result[index];
@@ -304,13 +310,24 @@ float MapgenV6::baseTerrainLevelFromMap(int index)
s16 MapgenV6::find_ground_level_from_noise(u64 seed, v2s16 p2d, s16 precision)
{
- return baseTerrainLevelFromNoise(p2d) + AVERAGE_MUD_AMOUNT;
+ return baseTerrainLevelFromNoise(p2d) + MGV6_AVERAGE_MUD_AMOUNT;
}
int MapgenV6::getGroundLevelAtPoint(v2s16 p)
{
- return baseTerrainLevelFromNoise(p) + AVERAGE_MUD_AMOUNT;
+ return baseTerrainLevelFromNoise(p) + MGV6_AVERAGE_MUD_AMOUNT;
+}
+
+
+int MapgenV6::getSpawnLevelAtPoint(v2s16 p)
+{
+ s16 level_at_point = baseTerrainLevelFromNoise(p) + MGV6_AVERAGE_MUD_AMOUNT;
+ if (level_at_point <= water_level ||
+ level_at_point > water_level + 16)
+ return MAX_MAP_GENERATION_LIMIT; // Unsuitable spawn point
+ else
+ return level_at_point;
}
@@ -386,8 +403,8 @@ bool MapgenV6::getHaveAppleTree(v2s16 p)
float MapgenV6::getMudAmount(int index)
{
- if (flags & MG_FLAT)
- return AVERAGE_MUD_AMOUNT;
+ if ((spflags & MGV6_FLAT) || (flags & MG_FLAT))
+ return MGV6_AVERAGE_MUD_AMOUNT;
/*return ((float)AVERAGE_MUD_AMOUNT + 2.0 * noise2d_perlin(
0.5+(float)p.X/200, 0.5+(float)p.Y/200,
@@ -422,13 +439,13 @@ BiomeV6Type MapgenV6::getBiome(int index, v2s16 p)
if (spflags & MGV6_SNOWBIOMES) {
float blend = (spflags & MGV6_BIOMEBLEND) ? noise2d(p.X, p.Y, seed) / 40 : 0;
- if (d > FREQ_HOT + blend) {
- if (h > FREQ_JUNGLE + blend)
+ if (d > MGV6_FREQ_HOT + blend) {
+ if (h > MGV6_FREQ_JUNGLE + blend)
return BT_JUNGLE;
else
return BT_DESERT;
- } else if (d < FREQ_SNOW + blend) {
- if (h > FREQ_TAIGA + blend)
+ } else if (d < MGV6_FREQ_SNOW + blend) {
+ if (h > MGV6_FREQ_TAIGA + blend)
return BT_TAIGA;
else
return BT_TUNDRA;
@@ -466,11 +483,11 @@ void MapgenV6::makeChunk(BlockMakeData *data)
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);
+ 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);
+ data->blockpos_requested.Y <= data->blockpos_max.Y &&
+ data->blockpos_requested.Z <= data->blockpos_max.Z);
this->generating = true;
this->vm = data->vmanip;
@@ -579,18 +596,21 @@ void MapgenV6::makeChunk(BlockMakeData *data)
growGrass();
// Generate some trees, and add grass, if a jungle
- if (flags & MG_TREES)
+ if ((spflags & MGV6_TREES) || (flags & MG_TREES))
placeTreesAndJungleGrass();
// Generate the registered decorations
- m_emerge->decomgr->placeAllDecos(this, blockseed, node_min, node_max);
+ if (flags & MG_DECORATIONS)
+ m_emerge->decomgr->placeAllDecos(this, blockseed, node_min, node_max);
// Generate the registered ores
m_emerge->oremgr->placeAllOres(this, blockseed, node_min, node_max);
// Calculate lighting
if (flags & MG_LIGHT)
- calcLighting(node_min, node_max);
+ calcLighting(node_min - v3s16(1, 1, 1) * MAP_BLOCKSIZE,
+ node_max + v3s16(1, 0, 1) * MAP_BLOCKSIZE,
+ full_node_min, full_node_max);
this->generating = false;
}
@@ -603,7 +623,7 @@ void MapgenV6::calculateNoise()
int fx = full_node_min.X;
int fz = full_node_min.Z;
- if (!(flags & MG_FLAT)) {
+ if (!((spflags & MGV6_FLAT) || (flags & MG_FLAT))) {
noise_terrain_base->perlinMap2D_PO(x, 0.5, z, 0.5);
noise_terrain_higher->perlinMap2D_PO(x, 0.5, z, 0.5);
noise_steepness->perlinMap2D_PO(x, 0.5, z, 0.5);
@@ -646,11 +666,11 @@ int MapgenV6::generateGround()
for (s16 y = node_min.Y; y <= node_max.Y; y++) {
if (vm->m_data[i].getContent() == CONTENT_IGNORE) {
if (y <= surface_y) {
- vm->m_data[i] = (y >= DESERT_STONE_BASE
+ vm->m_data[i] = (y >= MGV6_DESERT_STONE_BASE
&& bt == BT_DESERT) ?
n_desert_stone : n_stone;
} else if (y <= water_level) {
- vm->m_data[i] = (y >= ICE_BASE
+ vm->m_data[i] = (y >= MGV6_ICE_BASE
&& bt == BT_TUNDRA) ?
n_ice : n_water_source;
} else {
@@ -803,7 +823,7 @@ void MapgenV6::flowMud(s16 &mudflow_minpos, s16 &mudflow_maxpos)
continue;
// Drop mud on side
- for(u32 di = 0; di < 4; di++) {
+ for (u32 di = 0; di < 4; di++) {
v3s16 dirp = dirs4[di];
u32 i2 = i;
// Move to side
@@ -828,7 +848,7 @@ void MapgenV6::flowMud(s16 &mudflow_minpos, s16 &mudflow_maxpos)
vm->m_area.add_y(em, i2, -1);
n2 = &vm->m_data[i2];
// if out of known area
- if(vm->m_area.contains(i2) == false ||
+ if (vm->m_area.contains(i2) == false ||
n2->getContent() == CONTENT_IGNORE) {
dropped_to_unknown = true;
break;
@@ -843,7 +863,7 @@ void MapgenV6::flowMud(s16 &mudflow_minpos, s16 &mudflow_maxpos)
if (!dropped_to_unknown) {
*n2 = *n;
// Set old place to be air (or water)
- if(old_is_water)
+ if (old_is_water)
*n = MapNode(c_water_source);
else
*n = MapNode(CONTENT_AIR);
diff --git a/src/mapgen_v6.h b/src/mapgen_v6.h
index c71cf3c53..a55fc6d53 100644
--- a/src/mapgen_v6.h
+++ b/src/mapgen_v6.h
@@ -1,6 +1,6 @@
/*
Minetest
-Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
+Copyright (C) 2010-2015 celeron55, Perttu Ahola <celeron55@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
@@ -23,19 +23,21 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "mapgen.h"
#include "noise.h"
-#define AVERAGE_MUD_AMOUNT 4
-#define DESERT_STONE_BASE -32
-#define ICE_BASE 0
-#define FREQ_HOT 0.4
-#define FREQ_SNOW -0.4
-#define FREQ_TAIGA 0.5
-#define FREQ_JUNGLE 0.5
+#define MGV6_AVERAGE_MUD_AMOUNT 4
+#define MGV6_DESERT_STONE_BASE -32
+#define MGV6_ICE_BASE 0
+#define MGV6_FREQ_HOT 0.4
+#define MGV6_FREQ_SNOW -0.4
+#define MGV6_FREQ_TAIGA 0.5
+#define MGV6_FREQ_JUNGLE 0.5
//////////// Mapgen V6 flags
#define MGV6_JUNGLES 0x01
#define MGV6_BIOMEBLEND 0x02
#define MGV6_MUDFLOW 0x04
#define MGV6_SNOWBIOMES 0x08
+#define MGV6_FLAT 0x10
+#define MGV6_TREES 0x20
extern FlagDesc flagdesc_mapgen_v6[];
@@ -127,6 +129,7 @@ public:
void makeChunk(BlockMakeData *data);
int getGroundLevelAtPoint(v2s16 p);
+ int getSpawnLevelAtPoint(v2s16 p);
float baseTerrainLevel(float terrain_base, float terrain_higher,
float steepness, float height_select);
diff --git a/src/mapgen_v7.cpp b/src/mapgen_v7.cpp
index 9f612de81..9fb65f577 100644
--- a/src/mapgen_v7.cpp
+++ b/src/mapgen_v7.cpp
@@ -1,6 +1,7 @@
/*
Minetest
-Copyright (C) 2010-2013 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
+Copyright (C) 2010-2015 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
+Copyright (C) 2010-2015 paramat, Matt Gregory
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
@@ -27,6 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "content_sao.h"
#include "nodedef.h"
#include "voxelalgorithms.h"
+//#include "profiler.h" // For TimeTaker
#include "settings.h" // For g_settings
#include "emerge.h"
#include "dungeongen.h"
@@ -44,6 +46,7 @@ FlagDesc flagdesc_mapgen_v7[] = {
{NULL, 0}
};
+
///////////////////////////////////////////////////////////////////////////////
@@ -56,7 +59,10 @@ MapgenV7::MapgenV7(int mapgenid, MapgenParams *params, EmergeManager *emerge)
//// amount of elements to skip for the next index
//// for noise/height/biome maps (not vmanip)
this->ystride = csize.X;
- this->zstride = csize.X * (csize.Y + 2);
+ // 1-up 1-down overgeneration
+ this->zstride_1u1d = csize.X * (csize.Y + 2);
+ // 1-down overgeneration
+ this->zstride_1d = csize.X * (csize.Y + 1);
this->biomemap = new u8[csize.X * csize.Z];
this->heightmap = new s16[csize.X * csize.Z];
@@ -65,7 +71,9 @@ MapgenV7::MapgenV7(int mapgenid, MapgenParams *params, EmergeManager *emerge)
this->ridge_heightmap = new s16[csize.X * csize.Z];
MapgenV7Params *sp = (MapgenV7Params *)params->sparams;
- this->spflags = sp->spflags;
+
+ this->spflags = sp->spflags;
+ this->cave_width = sp->cave_width;
//// Terrain noise
noise_terrain_base = new Noise(&sp->np_terrain_base, seed, csize.X, csize.Z);
@@ -77,10 +85,12 @@ MapgenV7::MapgenV7(int mapgenid, MapgenParams *params, EmergeManager *emerge)
noise_ridge_uwater = new Noise(&sp->np_ridge_uwater, seed, csize.X, csize.Z);
//// 3d terrain noise
+ // 1-up 1-down overgeneration
noise_mountain = new Noise(&sp->np_mountain, seed, csize.X, csize.Y + 2, csize.Z);
noise_ridge = new Noise(&sp->np_ridge, seed, csize.X, csize.Y + 2, csize.Z);
- noise_cave1 = new Noise(&sp->np_cave1, seed, csize.X, csize.Y + 2, csize.Z);
- noise_cave2 = new Noise(&sp->np_cave2, seed, csize.X, csize.Y + 2, csize.Z);
+ // 1-down overgeneraion
+ noise_cave1 = new Noise(&sp->np_cave1, seed, csize.X, csize.Y + 1, csize.Z);
+ noise_cave2 = new Noise(&sp->np_cave2, seed, csize.X, csize.Y + 1, csize.Z);
//// Biome noise
noise_heat = new Noise(&params->np_biome_heat, seed, csize.X, csize.Z);
@@ -144,25 +154,27 @@ MapgenV7::~MapgenV7()
MapgenV7Params::MapgenV7Params()
{
- spflags = MGV7_MOUNTAINS | MGV7_RIDGES;
+ spflags = MGV7_MOUNTAINS | MGV7_RIDGES;
+ cave_width = 0.3;
np_terrain_base = NoiseParams(4, 70, v3f(600, 600, 600), 82341, 5, 0.6, 2.0);
np_terrain_alt = NoiseParams(4, 25, v3f(600, 600, 600), 5934, 5, 0.6, 2.0);
np_terrain_persist = NoiseParams(0.6, 0.1, v3f(2000, 2000, 2000), 539, 3, 0.6, 2.0);
- np_height_select = NoiseParams(-12, 24, v3f(500, 500, 500), 4213, 6, 0.7, 2.0);
+ np_height_select = NoiseParams(-8, 16, v3f(500, 500, 500), 4213, 6, 0.7, 2.0);
np_filler_depth = NoiseParams(0, 1.2, v3f(150, 150, 150), 261, 3, 0.7, 2.0);
np_mount_height = NoiseParams(256, 112, v3f(1000, 1000, 1000), 72449, 3, 0.6, 2.0);
np_ridge_uwater = NoiseParams(0, 1, v3f(1000, 1000, 1000), 85039, 5, 0.6, 2.0);
np_mountain = NoiseParams(-0.6, 1, v3f(250, 350, 250), 5333, 5, 0.63, 2.0);
np_ridge = NoiseParams(0, 1, v3f(100, 100, 100), 6467, 4, 0.75, 2.0);
- np_cave1 = NoiseParams(0, 12, v3f(100, 100, 100), 52534, 4, 0.5, 2.0);
- np_cave2 = NoiseParams(0, 12, v3f(100, 100, 100), 10325, 4, 0.5, 2.0);
+ np_cave1 = NoiseParams(0, 12, v3f(96, 96, 96), 52534, 4, 0.5, 2.0);
+ np_cave2 = NoiseParams(0, 12, v3f(96, 96, 96), 10325, 4, 0.5, 2.0);
}
void MapgenV7Params::readParams(const Settings *settings)
{
- settings->getFlagStrNoEx("mgv7_spflags", spflags, flagdesc_mapgen_v7);
+ settings->getFlagStrNoEx("mgv7_spflags", spflags, flagdesc_mapgen_v7);
+ settings->getFloatNoEx("mgv7_cave_width", cave_width);
settings->getNoiseParams("mgv7_np_terrain_base", np_terrain_base);
settings->getNoiseParams("mgv7_np_terrain_alt", np_terrain_alt);
@@ -180,7 +192,8 @@ void MapgenV7Params::readParams(const Settings *settings)
void MapgenV7Params::writeParams(Settings *settings) const
{
- settings->setFlagStr("mgv7_spflags", spflags, flagdesc_mapgen_v7, (u32)-1);
+ settings->setFlagStr("mgv7_spflags", spflags, flagdesc_mapgen_v7, U32_MAX);
+ settings->setFloat("mgv7_cave_width", cave_width);
settings->setNoiseParams("mgv7_np_terrain_base", np_terrain_base);
settings->setNoiseParams("mgv7_np_terrain_alt", np_terrain_alt);
@@ -196,10 +209,10 @@ void MapgenV7Params::writeParams(Settings *settings) const
}
-///////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
-int MapgenV7::getGroundLevelAtPoint(v2s16 p)
+int MapgenV7::getSpawnLevelAtPoint(v2s16 p)
{
// Base terrain calculation
s16 y = baseTerrainLevelAtPoint(p.X, p.Y);
@@ -207,22 +220,24 @@ int MapgenV7::getGroundLevelAtPoint(v2s16 p)
// Ridge/river terrain calculation
float width = 0.2;
float uwatern = NoisePerlin2D(&noise_ridge_uwater->np, p.X, p.Y, seed) * 2;
- // actually computing the depth of the ridge is much more expensive;
- // if inside a river, simply guess
+ // if inside a river this is an unsuitable spawn point
if (fabs(uwatern) <= width)
- return water_level - 10;
+ return MAX_MAP_GENERATION_LIMIT;
// Mountain terrain calculation
- int iters = 128; // don't even bother iterating more than 128 times..
+ int iters = 128;
while (iters--) {
- //current point would have been air
- if (!getMountainTerrainAtPoint(p.X, y, p.Y))
- return y;
-
+ if (!getMountainTerrainAtPoint(p.X, y + 1, p.Y)) { // Air, y is ground level
+ if (y <= water_level || y > water_level + 16)
+ return MAX_MAP_GENERATION_LIMIT; // Unsuitable spawn point
+ else
+ return y;
+ }
y++;
}
- return y;
+ // Unsuitable spawn point, no ground surface found
+ return MAX_MAP_GENERATION_LIMIT;
}
@@ -255,10 +270,13 @@ void MapgenV7::makeChunk(BlockMakeData *data)
// Make some noise
calculateNoise();
- // Generate base terrain, mountains, and ridges with initial heightmaps
+ // Generate terrain and ridges with initial heightmaps
s16 stone_surface_max_y = generateTerrain();
- // Create heightmap
+ if (spflags & MGV7_RIDGES)
+ generateRidgeTerrain();
+
+ // Update heightmap to include mountain terrain
updateHeightmap(node_min, node_max);
// Create biomemap at heightmap surface
@@ -315,7 +333,8 @@ void MapgenV7::makeChunk(BlockMakeData *data)
}
// Generate the registered decorations
- m_emerge->decomgr->placeAllDecos(this, blockseed, node_min, node_max);
+ if (flags & MG_DECORATIONS)
+ m_emerge->decomgr->placeAllDecos(this, blockseed, node_min, node_max);
// Generate the registered ores
m_emerge->oremgr->placeAllOres(this, blockseed, node_min, node_max);
@@ -341,9 +360,9 @@ void MapgenV7::makeChunk(BlockMakeData *data)
void MapgenV7::calculateNoise()
{
//TimeTaker t("calculateNoise", NULL, PRECISION_MICRO);
- int x = node_min.X;
- int y = node_min.Y - 1;
- int z = node_min.Z;
+ s16 x = node_min.X;
+ s16 y = node_min.Y - 1;
+ s16 z = node_min.Z;
noise_terrain_persist->perlinMap2D(x, z);
float *persistmap = noise_terrain_persist->result;
@@ -352,9 +371,9 @@ void MapgenV7::calculateNoise()
noise_terrain_alt->perlinMap2D(x, z, persistmap);
noise_height_select->perlinMap2D(x, z);
- if (flags & MG_CAVES) {
- noise_cave1->perlinMap3D(x, y, z);
- noise_cave2->perlinMap3D(x, y, z);
+ if (spflags & MGV7_MOUNTAINS) {
+ noise_mountain->perlinMap3D(x, y, z);
+ noise_mount_height->perlinMap2D(x, z);
}
if ((spflags & MGV7_RIDGES) && node_max.Y >= water_level) {
@@ -362,7 +381,8 @@ void MapgenV7::calculateNoise()
noise_ridge_uwater->perlinMap2D(x, z);
}
- // Mountain noises are calculated in generateMountainTerrain()
+ // Cave noises are calculated in generateCaves()
+ // only if solid terrain is present in mapchunk
noise_filler_depth->perlinMap2D(x, z);
noise_heat->perlinMap2D(x, z);
@@ -392,7 +412,7 @@ Biome *MapgenV7::getBiomeAtPoint(v3s16 p)
return bmgr->getBiome(heat, humidity, groundlevel);
}
-//needs to be updated
+
float MapgenV7::baseTerrainLevelAtPoint(s16 x, s16 z)
{
float hselect = NoisePerlin2D(&noise_height_select->np, x, z, seed);
@@ -446,142 +466,62 @@ bool MapgenV7::getMountainTerrainFromMap(int idx_xyz, int idx_xz, s16 y)
}
-#if 0
-void MapgenV7::carveRivers() {
- MapNode n_air(CONTENT_AIR), n_water_source(c_water_source);
- MapNode n_stone(c_stone);
- u32 index = 0;
-
- int river_depth = 4;
-
- for (s16 z = node_min.Z; z <= node_max.Z; z++)
- for (s16 x = node_min.X; x <= node_max.X; x++, index++) {
- float terrain_mod = noise_terrain_mod->result[index];
- NoiseParams *np = noise_terrain_river->np;
- np.persist = noise_terrain_persist->result[index];
- float terrain_river = NoisePerlin2DNoTxfm(np, x, z, seed);
- float height = terrain_river * (1 - abs(terrain_mod)) *
- noise_terrain_river->np.scale;
- height = log(height * height); //log(h^3) is pretty interesting for terrain
-
- s16 y = heightmap[index];
- if (height < 1.0 && y > river_depth &&
- y - river_depth >= node_min.Y && y <= node_max.Y) {
-
- for (s16 ry = y; ry != y - river_depth; ry--) {
- u32 vi = vm->m_area.index(x, ry, z);
- vm->m_data[vi] = n_air;
- }
-
- u32 vi = vm->m_area.index(x, y - river_depth, z);
- vm->m_data[vi] = n_water_source;
- }
- }
-}
-#endif
-
-
int MapgenV7::generateTerrain()
{
- s16 stone_surface_min_y;
- s16 stone_surface_max_y;
-
- generateBaseTerrain(&stone_surface_min_y, &stone_surface_max_y);
-
- if ((spflags & MGV7_MOUNTAINS) && stone_surface_min_y < node_max.Y)
- stone_surface_max_y = generateMountainTerrain(stone_surface_max_y);
-
- if (spflags & MGV7_RIDGES)
- generateRidgeTerrain();
-
- return stone_surface_max_y;
-}
-
-
-void MapgenV7::generateBaseTerrain(s16 *stone_surface_min_y, s16 *stone_surface_max_y)
-{
MapNode n_air(CONTENT_AIR);
MapNode n_stone(c_stone);
MapNode n_water(c_water_source);
v3s16 em = vm->m_area.getExtent();
- s16 surface_min_y = MAX_MAP_GENERATION_LIMIT;
- s16 surface_max_y = -MAX_MAP_GENERATION_LIMIT;
- u32 index = 0;
+ s16 stone_surface_max_y = -MAX_MAP_GENERATION_LIMIT;
+ u32 index2d = 0;
+ bool mountain_flag = spflags & MGV7_MOUNTAINS;
for (s16 z = node_min.Z; z <= node_max.Z; z++)
- for (s16 x = node_min.X; x <= node_max.X; x++, index++) {
- float surface_height = baseTerrainLevelFromMap(index);
- s16 surface_y = (s16)surface_height;
-
- heightmap[index] = surface_y;
- ridge_heightmap[index] = surface_y;
+ for (s16 x = node_min.X; x <= node_max.X; x++, index2d++) {
+ s16 surface_y = baseTerrainLevelFromMap(index2d);
+ heightmap[index2d] = surface_y; // Create base terrain heightmap
+ ridge_heightmap[index2d] = surface_y;
- if (surface_y < surface_min_y)
- surface_min_y = surface_y;
+ if (surface_y > stone_surface_max_y)
+ stone_surface_max_y = surface_y;
- if (surface_y > surface_max_y)
- surface_max_y = surface_y;
+ u32 vi = vm->m_area.index(x, node_min.Y - 1, z);
+ u32 index3d = (z - node_min.Z) * zstride_1u1d + (x - node_min.X);
- u32 i = vm->m_area.index(x, node_min.Y - 1, z);
for (s16 y = node_min.Y - 1; y <= node_max.Y + 1; y++) {
- if (vm->m_data[i].getContent() == CONTENT_IGNORE) {
- if (y <= surface_y)
- vm->m_data[i] = n_stone;
- else if (y <= water_level)
- vm->m_data[i] = n_water;
- else
- vm->m_data[i] = n_air;
- }
- vm->m_area.add_y(em, i, 1);
- }
- }
-
- *stone_surface_min_y = surface_min_y;
- *stone_surface_max_y = surface_max_y;
-}
-
-
-int MapgenV7::generateMountainTerrain(s16 ymax)
-{
- noise_mountain->perlinMap3D(node_min.X, node_min.Y - 1, node_min.Z);
- noise_mount_height->perlinMap2D(node_min.X, node_min.Z);
-
- MapNode n_stone(c_stone);
- u32 j = 0;
-
- 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++) {
- int index = (z - node_min.Z) * csize.X + (x - node_min.X);
- content_t c = vm->m_data[vi].getContent();
-
- if (getMountainTerrainFromMap(j, index, y)
- && (c == CONTENT_AIR || c == c_water_source)) {
- vm->m_data[vi] = n_stone;
- if (y > ymax)
- ymax = y;
+ if (vm->m_data[vi].getContent() == CONTENT_IGNORE) {
+ if (y <= surface_y) {
+ vm->m_data[vi] = n_stone; // Base terrain
+ } else if (mountain_flag &&
+ getMountainTerrainFromMap(index3d, index2d, y)) {
+ vm->m_data[vi] = n_stone; // Mountain terrain
+ if (y > stone_surface_max_y)
+ stone_surface_max_y = y;
+ } else if (y <= water_level) {
+ vm->m_data[vi] = n_water;
+ } else {
+ vm->m_data[vi] = n_air;
+ }
}
-
- vi++;
- j++;
+ vm->m_area.add_y(em, vi, 1);
+ index3d += ystride;
}
}
- return ymax;
+ return stone_surface_max_y;
}
void MapgenV7::generateRidgeTerrain()
{
- if (node_max.Y < water_level)
+ if (node_max.Y < water_level - 16)
return;
MapNode n_water(c_water_source);
MapNode n_air(CONTENT_AIR);
u32 index = 0;
- float width = 0.2; // TODO: figure out acceptable perlin noise values
+ float width = 0.2;
for (s16 z = node_min.Z; z <= node_max.Z; z++)
for (s16 y = node_min.Y - 1; y <= node_max.Y + 1; y++) {
@@ -589,7 +529,7 @@ void MapgenV7::generateRidgeTerrain()
for (s16 x = node_min.X; x <= node_max.X; x++, index++, vi++) {
int j = (z - node_min.Z) * csize.X + (x - node_min.X);
- if (heightmap[j] < water_level - 16)
+ if (heightmap[j] < water_level - 16) // Use base terrain heightmap
continue;
float uwatern = noise_ridge_uwater->result[j] * 2;
@@ -635,7 +575,7 @@ MgStoneType MapgenV7::generateBiomes(float *heat_map, float *humidity_map)
// If there is air or water above enable top/filler placement, otherwise force
// nplaced to stone level by setting a number exceeding any possible filler depth.
- u16 nplaced = (air_above || water_above) ? 0 : (u16)-1;
+ u16 nplaced = (air_above || water_above) ? 0 : U16_MAX;
for (s16 y = node_max.Y; y >= node_min.Y; y--) {
content_t c = vm->m_data[vi].getContent();
@@ -672,7 +612,7 @@ MgStoneType MapgenV7::generateBiomes(float *heat_map, float *humidity_map)
// This is done by aborting the cycle of top/filler placement
// immediately by forcing nplaced to stone level.
if (c_below == CONTENT_AIR || c_below == c_water_source)
- nplaced = (u16)-1;
+ nplaced = U16_MAX;
if (nplaced < depth_top) {
vm->m_data[vi] = MapNode(biome->c_top);
@@ -697,7 +637,7 @@ MgStoneType MapgenV7::generateBiomes(float *heat_map, float *humidity_map)
air_above = true;
water_above = false;
} else { // Possible various nodes overgenerated from neighbouring mapchunks
- nplaced = (u16)-1; // Disable top/filler placement
+ nplaced = U16_MAX; // Disable top/filler placement
air_above = false;
water_above = false;
}
@@ -760,6 +700,143 @@ void MapgenV7::dustTopNodes()
}
+void MapgenV7::generateCaves(s16 max_stone_y)
+{
+ if (max_stone_y < node_min.Y)
+ return;
+
+ noise_cave1->perlinMap3D(node_min.X, node_min.Y - 1, node_min.Z);
+ noise_cave2->perlinMap3D(node_min.X, node_min.Y - 1, node_min.Z);
+
+ v3s16 em = vm->m_area.getExtent();
+ u32 index2d = 0;
+
+ for (s16 z = node_min.Z; z <= node_max.Z; z++)
+ for (s16 x = node_min.X; x <= node_max.X; x++, index2d++) {
+ bool column_is_open = false; // Is column open to overground
+ bool is_tunnel = false; // Is tunnel or tunnel floor
+ // Indexes at column top (node_max.Y)
+ u32 vi = vm->m_area.index(x, node_max.Y, z);
+ u32 index3d = (z - node_min.Z) * zstride_1d + csize.Y * ystride +
+ (x - node_min.X);
+ // Biome of column
+ Biome *biome = (Biome *)bmgr->getRaw(biomemap[index2d]);
+
+ // Don't excavate the overgenerated stone at node_max.Y + 1,
+ // this creates a 'roof' over the tunnel, preventing light in
+ // tunnels at mapchunk borders when generating mapchunks upwards.
+ // This 'roof' is removed when the mapchunk above is generated.
+ for (s16 y = node_max.Y; y >= node_min.Y - 1; y--,
+ index3d -= ystride,
+ vm->m_area.add_y(em, vi, -1)) {
+
+ content_t c = vm->m_data[vi].getContent();
+ if (c == CONTENT_AIR || c == biome->c_water_top ||
+ c == biome->c_water) {
+ column_is_open = true;
+ continue;
+ }
+ // Ground
+ float d1 = contour(noise_cave1->result[index3d]);
+ float d2 = contour(noise_cave2->result[index3d]);
+
+ if (d1 * d2 > cave_width && ndef->get(c).is_ground_content) {
+ // In tunnel and ground content, excavate
+ vm->m_data[vi] = MapNode(CONTENT_AIR);
+ is_tunnel = true;
+ } else {
+ // Not in tunnel or not ground content
+ if (is_tunnel && column_is_open &&
+ (c == biome->c_filler || c == biome->c_stone))
+ // Tunnel entrance floor
+ vm->m_data[vi] = MapNode(biome->c_top);
+
+ column_is_open = false;
+ is_tunnel = false;
+ }
+ }
+ }
+
+ if (node_min.Y >= water_level)
+ return;
+
+ PseudoRandom ps(blockseed + 21343);
+ u32 bruises_count = ps.range(0, 2);
+ for (u32 i = 0; i < bruises_count; i++) {
+ CaveV7 cave(this, &ps);
+ cave.makeCave(node_min, node_max, max_stone_y);
+ }
+}
+
+
+///////////////////////////////////////////////////////////////
+
+
+#if 0
+int MapgenV7::generateMountainTerrain(s16 ymax)
+{
+ MapNode n_stone(c_stone);
+ u32 j = 0;
+
+ 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++) {
+ int index = (z - node_min.Z) * csize.X + (x - node_min.X);
+ content_t c = vm->m_data[vi].getContent();
+
+ if (getMountainTerrainFromMap(j, index, y)
+ && (c == CONTENT_AIR || c == c_water_source)) {
+ vm->m_data[vi] = n_stone;
+ if (y > ymax)
+ ymax = y;
+ }
+
+ vi++;
+ j++;
+ }
+ }
+
+ return ymax;
+}
+#endif
+
+
+#if 0
+void MapgenV7::carveRivers() {
+ MapNode n_air(CONTENT_AIR), n_water_source(c_water_source);
+ MapNode n_stone(c_stone);
+ u32 index = 0;
+
+ int river_depth = 4;
+
+ for (s16 z = node_min.Z; z <= node_max.Z; z++)
+ for (s16 x = node_min.X; x <= node_max.X; x++, index++) {
+ float terrain_mod = noise_terrain_mod->result[index];
+ NoiseParams *np = noise_terrain_river->np;
+ np.persist = noise_terrain_persist->result[index];
+ float terrain_river = NoisePerlin2DNoTxfm(np, x, z, seed);
+ float height = terrain_river * (1 - abs(terrain_mod)) *
+ noise_terrain_river->np.scale;
+ height = log(height * height); //log(h^3) is pretty interesting for terrain
+
+ s16 y = heightmap[index];
+ if (height < 1.0 && y > river_depth &&
+ y - river_depth >= node_min.Y && y <= node_max.Y) {
+
+ for (s16 ry = y; ry != y - river_depth; ry--) {
+ u32 vi = vm->m_area.index(x, ry, z);
+ vm->m_data[vi] = n_air;
+ }
+
+ u32 vi = vm->m_area.index(x, y - river_depth, z);
+ vm->m_data[vi] = n_water_source;
+ }
+ }
+}
+#endif
+
+
#if 0
void MapgenV7::addTopNodes()
{
@@ -854,34 +931,3 @@ void MapgenV7::addTopNodes()
}
}
#endif
-
-
-void MapgenV7::generateCaves(s16 max_stone_y)
-{
- if (max_stone_y >= node_min.Y) {
- u32 index = 0;
-
- 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 i = vm->m_area.index(node_min.X, y, z);
- for (s16 x = node_min.X; x <= node_max.X; x++, i++, index++) {
- float d1 = contour(noise_cave1->result[index]);
- float d2 = contour(noise_cave2->result[index]);
- if (d1 * d2 > 0.3) {
- content_t c = vm->m_data[i].getContent();
- if (!ndef->get(c).is_ground_content || c == CONTENT_AIR)
- continue;
-
- vm->m_data[i] = MapNode(CONTENT_AIR);
- }
- }
- }
- }
-
- PseudoRandom ps(blockseed + 21343);
- u32 bruises_count = (ps.range(1, 4) == 1) ? ps.range(1, 2) : 0;
- for (u32 i = 0; i < bruises_count; i++) {
- CaveV7 cave(this, &ps);
- cave.makeCave(node_min, node_max, max_stone_y);
- }
-}
diff --git a/src/mapgen_v7.h b/src/mapgen_v7.h
index c0cfa8c77..c25220646 100644
--- a/src/mapgen_v7.h
+++ b/src/mapgen_v7.h
@@ -1,6 +1,7 @@
/*
Minetest
-Copyright (C) 2010-2013 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
+Copyright (C) 2010-2015 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
+Copyright (C) 2010-2015 paramat, Matt Gregory
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
@@ -33,6 +34,7 @@ extern FlagDesc flagdesc_mapgen_v7[];
struct MapgenV7Params : public MapgenSpecificParams {
u32 spflags;
+ float cave_width;
NoiseParams np_terrain_base;
NoiseParams np_terrain_alt;
NoiseParams np_terrain_persist;
@@ -58,8 +60,8 @@ public:
BiomeManager *bmgr;
int ystride;
- int zstride;
- u32 spflags;
+ int zstride_1u1d;
+ int zstride_1d;
v3s16 node_min;
v3s16 node_max;
@@ -68,6 +70,8 @@ public:
s16 *ridge_heightmap;
+ u32 spflags;
+ float cave_width;
Noise *noise_terrain_base;
Noise *noise_terrain_alt;
Noise *noise_terrain_persist;
@@ -102,7 +106,7 @@ public:
~MapgenV7();
virtual void makeChunk(BlockMakeData *data);
- int getGroundLevelAtPoint(v2s16 p);
+ int getSpawnLevelAtPoint(v2s16 p);
Biome *getBiomeAtPoint(v3s16 p);
float baseTerrainLevelAtPoint(s16 x, s16 z);
@@ -112,16 +116,12 @@ public:
void calculateNoise();
- virtual int generateTerrain();
- void generateBaseTerrain(s16 *stone_surface_min_y, s16 *stone_surface_max_y);
- int generateMountainTerrain(s16 ymax);
+ int generateTerrain();
void generateRidgeTerrain();
MgStoneType generateBiomes(float *heat_map, float *humidity_map);
void dustTopNodes();
- //void addTopNodes();
-
void generateCaves(s16 max_stone_y);
};
diff --git a/src/mapgen_valleys.cpp b/src/mapgen_valleys.cpp
new file mode 100644
index 000000000..0ec5409cb
--- /dev/null
+++ b/src/mapgen_valleys.cpp
@@ -0,0 +1,1017 @@
+/*
+Minetest Valleys C
+Copyright (C) 2010-2015 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
+Copyright (C) 2010-2015 paramat, Matt Gregory
+Copyright (C) 2016 Duane Robertson <duane@duanerobertson.com>
+
+Based on Valleys Mapgen by Gael de Sailly
+ (https://forum.minetest.net/viewtopic.php?f=9&t=11430)
+and mapgen_v7, mapgen_flat by kwolekr and paramat.
+
+Licensing changed by permission of Gael de Sailly.
+
+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 "mapgen.h"
+#include "voxel.h"
+#include "noise.h"
+#include "mapblock.h"
+#include "mapnode.h"
+#include "map.h"
+#include "content_sao.h"
+#include "nodedef.h"
+#include "voxelalgorithms.h"
+#include "settings.h" // For g_settings
+#include "emerge.h"
+#include "dungeongen.h"
+#include "treegen.h"
+#include "mg_biome.h"
+#include "mg_ore.h"
+#include "mg_decoration.h"
+#include "mapgen_valleys.h"
+#include "cavegen.h"
+
+
+//#undef NDEBUG
+//#include "assert.h"
+
+//#include "util/timetaker.h"
+//#include "profiler.h"
+
+
+//static Profiler mapgen_prof;
+//Profiler *mapgen_profiler = &mapgen_prof;
+
+static FlagDesc flagdesc_mapgen_valleys[] = {
+ {"altitude_chill", MGVALLEYS_ALT_CHILL},
+ {"humid_rivers", MGVALLEYS_HUMID_RIVERS},
+ {NULL, 0}
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+
+MapgenValleys::MapgenValleys(int mapgenid, MapgenParams *params, EmergeManager *emerge)
+ : Mapgen(mapgenid, params, emerge)
+{
+ this->m_emerge = emerge;
+ this->bmgr = emerge->biomemgr;
+
+ //// amount of elements to skip for the next index
+ //// for noise/height/biome maps (not vmanip)
+ this->ystride = csize.X;
+ this->zstride = csize.X * (csize.Y + 2);
+ // 1-down overgeneration
+ this->zstride_1d = csize.X * (csize.Y + 1);
+
+ this->biomemap = new u8[csize.X * csize.Z];
+ this->heightmap = new s16[csize.X * csize.Z];
+ this->heatmap = NULL;
+ this->humidmap = NULL;
+
+ this->map_gen_limit = MYMIN(MAX_MAP_GENERATION_LIMIT,
+ g_settings->getU16("map_generation_limit"));
+
+ MapgenValleysParams *sp = (MapgenValleysParams *)params->sparams;
+
+ this->spflags = sp->spflags;
+ this->altitude_chill = sp->altitude_chill;
+ this->large_cave_depth = sp->large_cave_depth;
+ this->lava_features_lim = rangelim(sp->lava_features, 0, 10);
+ this->massive_cave_depth = sp->massive_cave_depth;
+ this->river_depth_bed = sp->river_depth + 1.f;
+ this->river_size_factor = sp->river_size / 100.f;
+ this->water_features_lim = rangelim(sp->water_features, 0, 10);
+ this->cave_width = sp->cave_width;
+
+ //// 2D Terrain noise
+ noise_filler_depth = new Noise(&sp->np_filler_depth, seed, csize.X, csize.Z);
+ noise_inter_valley_slope = new Noise(&sp->np_inter_valley_slope, seed, csize.X, csize.Z);
+ noise_rivers = new Noise(&sp->np_rivers, seed, csize.X, csize.Z);
+ noise_terrain_height = new Noise(&sp->np_terrain_height, seed, csize.X, csize.Z);
+ noise_valley_depth = new Noise(&sp->np_valley_depth, seed, csize.X, csize.Z);
+ noise_valley_profile = new Noise(&sp->np_valley_profile, seed, csize.X, csize.Z);
+
+ //// 3D Terrain noise
+ // 1-up 1-down overgeneration
+ noise_inter_valley_fill = new Noise(&sp->np_inter_valley_fill, seed, csize.X, csize.Y + 2, csize.Z);
+ // 1-down overgeneraion
+ noise_cave1 = new Noise(&sp->np_cave1, seed, csize.X, csize.Y + 1, csize.Z);
+ noise_cave2 = new Noise(&sp->np_cave2, seed, csize.X, csize.Y + 1, csize.Z);
+ noise_massive_caves = new Noise(&sp->np_massive_caves, seed, csize.X, csize.Y + 1, csize.Z);
+
+ //// Biome noise
+ noise_heat_blend = new Noise(&params->np_biome_heat_blend, seed, csize.X, csize.Z);
+ noise_heat = new Noise(&params->np_biome_heat, seed, csize.X, csize.Z);
+ noise_humidity_blend = new Noise(&params->np_biome_humidity_blend, seed, csize.X, csize.Z);
+ noise_humidity = new Noise(&params->np_biome_humidity, seed, csize.X, csize.Z);
+
+ this->humid_rivers = (spflags & MGVALLEYS_HUMID_RIVERS);
+ this->use_altitude_chill = (spflags & MGVALLEYS_ALT_CHILL);
+ this->humidity_adjust = params->np_biome_humidity.offset - 50.f;
+
+ // a small chance of overflows if the settings are very high
+ this->cave_water_max_height = water_level + MYMAX(0, water_features_lim - 4) * 50;
+ this->lava_max_height = water_level + MYMAX(0, lava_features_lim - 4) * 50;
+
+ tcave_cache = new float[csize.Y + 2];
+
+ //// Resolve nodes to be used
+ INodeDefManager *ndef = emerge->ndef;
+
+ c_cobble = ndef->getId("mapgen_cobble");
+ c_desert_stone = ndef->getId("mapgen_desert_stone");
+ c_dirt = ndef->getId("mapgen_dirt");
+ c_lava_source = ndef->getId("mapgen_lava_source");
+ c_mossycobble = ndef->getId("mapgen_mossycobble");
+ c_river_water_source = ndef->getId("mapgen_river_water_source");
+ c_sand = ndef->getId("mapgen_sand");
+ c_sandstonebrick = ndef->getId("mapgen_sandstonebrick");
+ c_sandstone = ndef->getId("mapgen_sandstone");
+ c_stair_cobble = ndef->getId("mapgen_stair_cobble");
+ c_stair_sandstonebrick = ndef->getId("mapgen_stair_sandstonebrick");
+ c_stone = ndef->getId("mapgen_stone");
+ c_water_source = ndef->getId("mapgen_water_source");
+
+ if (c_mossycobble == CONTENT_IGNORE)
+ c_mossycobble = c_cobble;
+ if (c_river_water_source == CONTENT_IGNORE)
+ c_river_water_source = c_water_source;
+ if (c_sand == CONTENT_IGNORE)
+ c_sand = c_stone;
+ if (c_sandstonebrick == CONTENT_IGNORE)
+ c_sandstonebrick = c_sandstone;
+ if (c_stair_cobble == CONTENT_IGNORE)
+ c_stair_cobble = c_cobble;
+ if (c_stair_sandstonebrick == CONTENT_IGNORE)
+ c_stair_sandstonebrick = c_sandstone;
+}
+
+
+MapgenValleys::~MapgenValleys()
+{
+ delete noise_cave1;
+ delete noise_cave2;
+ delete noise_filler_depth;
+ delete noise_inter_valley_fill;
+ delete noise_inter_valley_slope;
+ delete noise_rivers;
+ delete noise_massive_caves;
+ delete noise_terrain_height;
+ delete noise_valley_depth;
+ delete noise_valley_profile;
+
+ delete noise_heat;
+ delete noise_heat_blend;
+ delete noise_humidity;
+ delete noise_humidity_blend;
+
+ delete[] biomemap;
+ delete[] heightmap;
+ delete[] tcave_cache;
+}
+
+
+MapgenValleysParams::MapgenValleysParams()
+{
+ spflags = MGVALLEYS_HUMID_RIVERS | MGVALLEYS_ALT_CHILL;
+ altitude_chill = 90; // The altitude at which temperature drops by 20C.
+ large_cave_depth = -33;
+ lava_features = 0; // How often water will occur in caves.
+ massive_cave_depth = -256; // highest altitude of massive caves
+ river_depth = 4; // How deep to carve river channels.
+ river_size = 5; // How wide to make rivers.
+ water_features = 0; // How often water will occur in caves.
+ cave_width = 0.3;
+
+ np_cave1 = NoiseParams(0, 12, v3f(96, 96, 96), 52534, 4, 0.5, 2.0);
+ np_cave2 = NoiseParams(0, 12, v3f(96, 96, 96), 10325, 4, 0.5, 2.0);
+ np_filler_depth = NoiseParams(0.f, 1.2f, v3f(256, 256, 256), 1605, 3, 0.5f, 2.f);
+ np_inter_valley_fill = NoiseParams(0.f, 1.f, v3f(256, 512, 256), 1993, 6, 0.8f, 2.f);
+ np_inter_valley_slope = NoiseParams(0.5f, 0.5f, v3f(128, 128, 128), 746, 1, 1.f, 2.f);
+ np_rivers = NoiseParams(0.f, 1.f, v3f(256, 256, 256), -6050, 5, 0.6f, 2.f);
+ np_massive_caves = NoiseParams(0.f, 1.f, v3f(768, 256, 768), 59033, 6, 0.63f, 2.f);
+ np_terrain_height = NoiseParams(-10.f, 50.f, v3f(1024, 1024, 1024), 5202, 6, 0.4f, 2.f);
+ np_valley_depth = NoiseParams(5.f, 4.f, v3f(512, 512, 512), -1914, 1, 1.f, 2.f);
+ np_valley_profile = NoiseParams(0.6f, 0.5f, v3f(512, 512, 512), 777, 1, 1.f, 2.f);
+}
+
+
+void MapgenValleysParams::readParams(const Settings *settings)
+{
+ settings->getFlagStrNoEx("mgvalleys_spflags", spflags, flagdesc_mapgen_valleys);
+ settings->getU16NoEx("mgvalleys_altitude_chill", altitude_chill);
+ settings->getS16NoEx("mgvalleys_large_cave_depth", large_cave_depth);
+ settings->getU16NoEx("mgvalleys_lava_features", lava_features);
+ settings->getS16NoEx("mgvalleys_massive_cave_depth", massive_cave_depth);
+ settings->getU16NoEx("mgvalleys_river_depth", river_depth);
+ settings->getU16NoEx("mgvalleys_river_size", river_size);
+ settings->getU16NoEx("mgvalleys_water_features", water_features);
+ settings->getFloatNoEx("mgvalleys_cave_width", cave_width);
+
+ settings->getNoiseParams("mgvalleys_np_cave1", np_cave1);
+ settings->getNoiseParams("mgvalleys_np_cave2", np_cave2);
+ settings->getNoiseParams("mgvalleys_np_filler_depth", np_filler_depth);
+ settings->getNoiseParams("mgvalleys_np_inter_valley_fill", np_inter_valley_fill);
+ settings->getNoiseParams("mgvalleys_np_inter_valley_slope", np_inter_valley_slope);
+ settings->getNoiseParams("mgvalleys_np_rivers", np_rivers);
+ settings->getNoiseParams("mgvalleys_np_massive_caves", np_massive_caves);
+ settings->getNoiseParams("mgvalleys_np_terrain_height", np_terrain_height);
+ settings->getNoiseParams("mgvalleys_np_valley_depth", np_valley_depth);
+ settings->getNoiseParams("mgvalleys_np_valley_profile", np_valley_profile);
+}
+
+
+void MapgenValleysParams::writeParams(Settings *settings) const
+{
+ settings->setFlagStr("mgvalleys_spflags", spflags, flagdesc_mapgen_valleys, U32_MAX);
+ settings->setU16("mgvalleys_altitude_chill", altitude_chill);
+ settings->setS16("mgvalleys_large_cave_depth", large_cave_depth);
+ settings->setU16("mgvalleys_lava_features", lava_features);
+ settings->setS16("mgvalleys_massive_cave_depth", massive_cave_depth);
+ settings->setU16("mgvalleys_river_depth", river_depth);
+ settings->setU16("mgvalleys_river_size", river_size);
+ settings->setU16("mgvalleys_water_features", water_features);
+ settings->setFloat("mgvalleys_cave_width", cave_width);
+
+ settings->setNoiseParams("mgvalleys_np_cave1", np_cave1);
+ settings->setNoiseParams("mgvalleys_np_cave2", np_cave2);
+ settings->setNoiseParams("mgvalleys_np_filler_depth", np_filler_depth);
+ settings->setNoiseParams("mgvalleys_np_inter_valley_fill", np_inter_valley_fill);
+ settings->setNoiseParams("mgvalleys_np_inter_valley_slope", np_inter_valley_slope);
+ settings->setNoiseParams("mgvalleys_np_rivers", np_rivers);
+ settings->setNoiseParams("mgvalleys_np_massive_caves", np_massive_caves);
+ settings->setNoiseParams("mgvalleys_np_terrain_height", np_terrain_height);
+ settings->setNoiseParams("mgvalleys_np_valley_depth", np_valley_depth);
+ settings->setNoiseParams("mgvalleys_np_valley_profile", np_valley_profile);
+}
+
+
+///////////////////////////////////////
+
+
+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);
+
+ this->generating = true;
+ this->vm = data->vmanip;
+ this->ndef = data->nodedef;
+
+ //TimeTaker t("makeChunk");
+
+ v3s16 blockpos_min = data->blockpos_min;
+ v3s16 blockpos_max = data->blockpos_max;
+ node_min = blockpos_min * MAP_BLOCKSIZE;
+ node_max = (blockpos_max + v3s16(1, 1, 1)) * MAP_BLOCKSIZE - v3s16(1, 1, 1);
+ full_node_min = (blockpos_min - 1) * MAP_BLOCKSIZE;
+ full_node_max = (blockpos_max + 2) * MAP_BLOCKSIZE - v3s16(1, 1, 1);
+
+ blockseed = getBlockSeed2(full_node_min, seed);
+
+ // Generate noise maps and base terrain height.
+ calculateNoise();
+
+ // Generate base terrain with initial heightmaps
+ s16 stone_surface_max_y = generateTerrain();
+
+ // Create biomemap at heightmap surface
+ bmgr->calcBiomes(csize.X, csize.Z, heatmap, humidmap, heightmap, biomemap);
+
+ // Actually place the biome-specific nodes
+ MgStoneType stone_type = generateBiomes(heatmap, humidmap);
+
+ // Cave creation.
+ if (flags & MG_CAVES)
+ generateCaves(stone_surface_max_y);
+
+ // Dungeon creation
+ if ((flags & MG_DUNGEONS) && node_max.Y < 50 && (stone_surface_max_y >= node_min.Y)) {
+ DungeonParams dp;
+
+ dp.np_rarity = nparams_dungeon_rarity;
+ dp.np_density = nparams_dungeon_density;
+ dp.np_wetness = nparams_dungeon_wetness;
+ dp.c_water = c_water_source;
+ if (stone_type == STONE) {
+ dp.c_cobble = c_cobble;
+ dp.c_moss = c_mossycobble;
+ dp.c_stair = c_stair_cobble;
+
+ dp.diagonal_dirs = false;
+ dp.mossratio = 3.f;
+ dp.holesize = v3s16(1, 2, 1);
+ dp.roomsize = v3s16(0, 0, 0);
+ dp.notifytype = GENNOTIFY_DUNGEON;
+ } else if (stone_type == DESERT_STONE) {
+ dp.c_cobble = c_desert_stone;
+ dp.c_moss = c_desert_stone;
+ dp.c_stair = c_desert_stone;
+
+ dp.diagonal_dirs = true;
+ dp.mossratio = 0.f;
+ dp.holesize = v3s16(2, 3, 2);
+ dp.roomsize = v3s16(2, 5, 2);
+ dp.notifytype = GENNOTIFY_TEMPLE;
+ } else if (stone_type == SANDSTONE) {
+ dp.c_cobble = c_sandstonebrick;
+ dp.c_moss = c_sandstonebrick;
+ dp.c_stair = c_sandstonebrick;
+
+ dp.diagonal_dirs = false;
+ dp.mossratio = 0.f;
+ dp.holesize = v3s16(2, 2, 2);
+ dp.roomsize = v3s16(2, 0, 2);
+ dp.notifytype = GENNOTIFY_DUNGEON;
+ }
+
+ DungeonGen dgen(this, &dp);
+ dgen.generate(blockseed, full_node_min, full_node_max);
+ }
+
+ // Generate the registered decorations
+ if (flags & MG_DECORATIONS)
+ m_emerge->decomgr->placeAllDecos(this, blockseed, node_min, node_max);
+
+ // Generate the registered ores
+ m_emerge->oremgr->placeAllOres(this, blockseed, node_min, node_max);
+
+ // Sprinkle some dust on top after everything else was generated
+ dustTopNodes();
+
+ //TimeTaker tll("liquid_lighting");
+
+ updateLiquid(&data->transforming_liquid, full_node_min, full_node_max);
+
+ if (flags & MG_LIGHT)
+ calcLighting(
+ node_min - v3s16(0, 1, 0),
+ node_max + v3s16(0, 1, 0),
+ full_node_min,
+ full_node_max);
+
+ //mapgen_profiler->avg("liquid_lighting", tll.stop() / 1000.f);
+ //mapgen_profiler->avg("makeChunk", t.stop() / 1000.f);
+
+ this->generating = false;
+}
+
+
+// Populate the noise tables and do most of the
+// calculation necessary to determine terrain height.
+void MapgenValleys::calculateNoise()
+{
+ //TimeTaker t("calculateNoise", NULL, PRECISION_MICRO);
+
+ int x = node_min.X;
+ int y = node_min.Y - 1;
+ int z = node_min.Z;
+
+ //TimeTaker tcn("actualNoise");
+
+ noise_filler_depth->perlinMap2D(x, z);
+ noise_heat_blend->perlinMap2D(x, z);
+ noise_heat->perlinMap2D(x, z);
+ noise_humidity_blend->perlinMap2D(x, z);
+ noise_humidity->perlinMap2D(x, z);
+ noise_inter_valley_slope->perlinMap2D(x, z);
+ noise_rivers->perlinMap2D(x, z);
+ noise_terrain_height->perlinMap2D(x, z);
+ noise_valley_depth->perlinMap2D(x, z);
+ noise_valley_profile->perlinMap2D(x, z);
+
+ noise_inter_valley_fill->perlinMap3D(x, y, z);
+
+ //mapgen_profiler->avg("noisemaps", tcn.stop() / 1000.f);
+
+ float heat_offset = 0.f;
+ float humidity_scale = 1.f;
+
+ // Altitude chill tends to reduce the average heat.
+ if (use_altitude_chill)
+ heat_offset = 5.f;
+
+ // River humidity tends to increase the humidity range.
+ if (humid_rivers) {
+ humidity_scale = 0.8f;
+ }
+
+ for (s32 index = 0; index < csize.X * csize.Z; index++) {
+ noise_heat->result[index] += noise_heat_blend->result[index] + heat_offset;
+ noise_humidity->result[index] *= humidity_scale;
+ noise_humidity->result[index] += noise_humidity_blend->result[index];
+ }
+
+ TerrainNoise tn;
+
+ u32 index = 0;
+ for (tn.z = node_min.Z; tn.z <= node_max.Z; tn.z++)
+ for (tn.x = node_min.X; tn.x <= node_max.X; tn.x++, index++) {
+ // The parameters that we actually need to generate terrain
+ // are passed by address (and the return value).
+ tn.terrain_height = noise_terrain_height->result[index];
+ // River noise is replaced with base terrain, which
+ // is basically the height of the water table.
+ tn.rivers = &noise_rivers->result[index];
+ // Valley depth noise is replaced with the valley
+ // number that represents the height of terrain
+ // over rivers and is used to determine about
+ // how close a river is for humidity calculation.
+ tn.valley = &noise_valley_depth->result[index];
+ tn.valley_profile = noise_valley_profile->result[index];
+ // Slope noise is replaced by the calculated slope
+ // which is used to get terrain height in the slow
+ // method, to create sharper mountains.
+ tn.slope = &noise_inter_valley_slope->result[index];
+ tn.inter_valley_fill = noise_inter_valley_fill->result[index];
+
+ // This is the actual terrain height.
+ float mount = terrainLevelFromNoise(&tn);
+ noise_terrain_height->result[index] = mount;
+ }
+
+ heatmap = noise_heat->result;
+ humidmap = noise_humidity->result;
+}
+
+
+// This keeps us from having to maintain two similar sets of
+// complicated code to determine ground level.
+float MapgenValleys::terrainLevelFromNoise(TerrainNoise *tn)
+{
+ // The square function changes the behaviour of this noise:
+ // very often small, and sometimes very high.
+ float valley_d = MYSQUARE(*tn->valley);
+
+ // valley_d is here because terrain is generally higher where valleys
+ // are deep (mountains). base represents the height of the
+ // rivers, most of the surface is above.
+ float base = tn->terrain_height + valley_d;
+
+ // "river" represents the distance from the river, in arbitrary units.
+ float river = fabs(*tn->rivers) - river_size_factor;
+
+ // Use the curve of the function 1-exp(-(x/a)^2) to model valleys.
+ // Making "a" vary (0 < a <= 1) changes the shape of the valleys.
+ // Try it with a geometry software !
+ // (here x = "river" and a = valley_profile).
+ // "valley" represents the height of the terrain, from the rivers.
+ {
+ float t = river / tn->valley_profile;
+ *tn->valley = valley_d * (1.f - exp(- MYSQUARE(t)));
+ }
+
+ // approximate height of the terrain at this point
+ float mount = base + *tn->valley;
+
+ *tn->slope *= *tn->valley;
+
+ // Rivers are placed where "river" is negative, so where the original
+ // noise value is close to zero.
+ // Base ground is returned as rivers since it's basically the water table.
+ *tn->rivers = base;
+ if (river < 0.f) {
+ // Use the the function -sqrt(1-x^2) which models a circle.
+ float depth;
+ {
+ float t = river / river_size_factor + 1;
+ depth = (river_depth_bed * sqrt(MYMAX(0, 1.f - MYSQUARE(t))));
+ }
+
+ // base - depth : height of the bottom of the river
+ // water_level - 3 : don't make rivers below 3 nodes under the surface
+ // We use three because that's as low as the swamp biomes go.
+ // There is no logical equivalent to this using rangelim.
+ mount = MYMIN(MYMAX(base - depth, (float)(water_level - 3)), mount);
+
+ // Slope has no influence on rivers.
+ *tn->slope = 0.f;
+ }
+
+ return mount;
+}
+
+
+// This avoids duplicating the code in terrainLevelFromNoise, adding
+// only the final step of terrain generation without a noise map.
+float MapgenValleys::adjustedTerrainLevelFromNoise(TerrainNoise *tn)
+{
+ float mount = terrainLevelFromNoise(tn);
+ s16 y_start = myround(mount);
+
+ for (s16 y = y_start; y <= y_start + 1000; y++) {
+ float fill = NoisePerlin3D(&noise_inter_valley_fill->np, tn->x, y, tn->z, seed);
+
+ if (fill * *tn->slope < y - mount) {
+ mount = MYMAX(y - 1, mount);
+ break;
+ }
+ }
+
+ return mount;
+}
+
+
+int MapgenValleys::getSpawnLevelAtPoint(v2s16 p)
+{
+ // Check to make sure this isn't a request for a location in a river.
+ float rivers = NoisePerlin2D(&noise_rivers->np, p.X, p.Y, seed);
+ if (fabs(rivers) < river_size_factor)
+ return MAX_MAP_GENERATION_LIMIT; // Unsuitable spawn point
+
+ s16 level_at_point = terrainLevelAtPoint(p.X, p.Y);
+ if (level_at_point <= water_level ||
+ level_at_point > water_level + 32)
+ return MAX_MAP_GENERATION_LIMIT; // Unsuitable spawn point
+ else
+ return level_at_point;
+}
+
+
+float MapgenValleys::terrainLevelAtPoint(s16 x, s16 z)
+{
+ TerrainNoise tn;
+
+ float rivers = NoisePerlin2D(&noise_rivers->np, x, z, seed);
+ float valley = NoisePerlin2D(&noise_valley_depth->np, x, z, seed);
+ float inter_valley_slope = NoisePerlin2D(&noise_inter_valley_slope->np, x, z, seed);
+
+ tn.x = x;
+ tn.z = z;
+ tn.terrain_height = NoisePerlin2D(&noise_terrain_height->np, x, z, seed);
+ tn.rivers = &rivers;
+ tn.valley = &valley;
+ tn.valley_profile = NoisePerlin2D(&noise_valley_profile->np, x, z, seed);
+ tn.slope = &inter_valley_slope;
+ tn.inter_valley_fill = 0.f;
+
+ return adjustedTerrainLevelFromNoise(&tn);
+}
+
+
+int MapgenValleys::generateTerrain()
+{
+ // Raising this reduces the rate of evaporation.
+ static const float evaporation = 300.f;
+ // from the lua
+ static const float humidity_dropoff = 4.f;
+ // constant to convert altitude chill (compatible with lua) to heat
+ static const float alt_to_heat = 20.f;
+ // humidity reduction by altitude
+ static const float alt_to_humid = 10.f;
+
+ MapNode n_air(CONTENT_AIR);
+ MapNode n_river_water(c_river_water_source);
+ MapNode n_sand(c_sand);
+ MapNode n_stone(c_stone);
+ MapNode n_water(c_water_source);
+
+ v3s16 em = vm->m_area.getExtent();
+ s16 surface_max_y = -MAX_MAP_GENERATION_LIMIT;
+ u32 index_2d = 0;
+
+ for (s16 z = node_min.Z; z <= node_max.Z; z++)
+ for (s16 x = node_min.X; x <= node_max.X; x++, index_2d++) {
+ float river_y = noise_rivers->result[index_2d];
+ float surface_y = noise_terrain_height->result[index_2d];
+ float slope = noise_inter_valley_slope->result[index_2d];
+ float t_heat = noise_heat->result[index_2d];
+
+ heightmap[index_2d] = -MAX_MAP_GENERATION_LIMIT;
+
+ if (surface_y > surface_max_y)
+ surface_max_y = ceil(surface_y);
+
+ if (humid_rivers) {
+ // Derive heat from (base) altitude. This will be most correct
+ // at rivers, since other surface heights may vary below.
+ if (use_altitude_chill && (surface_y > 0.f || river_y > 0.f))
+ t_heat -= alt_to_heat * MYMAX(surface_y, river_y) / altitude_chill;
+
+ // If humidity is low or heat is high, lower the water table.
+ float delta = noise_humidity->result[index_2d] - 50.f;
+ if (delta < 0.f) {
+ float t_evap = (t_heat - 32.f) / evaporation;
+ river_y += delta * MYMAX(t_evap, 0.08f);
+ }
+ }
+
+ u32 index_3d = (z - node_min.Z) * zstride + (x - node_min.X);
+ u32 index_data = vm->m_area.index(x, node_min.Y - 1, z);
+
+ // Mapgens concern themselves with stone and water.
+ for (s16 y = node_min.Y - 1; y <= node_max.Y + 1; y++) {
+ if (vm->m_data[index_data].getContent() == CONTENT_IGNORE) {
+ float fill = noise_inter_valley_fill->result[index_3d];
+ float surface_delta = (float)y - surface_y;
+ bool river = y + 1 < river_y;
+
+ if (fabs(surface_delta) <= 0.5f && y > water_level && river) {
+ // river bottom
+ vm->m_data[index_data] = n_sand;
+ } else if (slope * fill > surface_delta) {
+ // ground
+ vm->m_data[index_data] = n_stone;
+ if (y > heightmap[index_2d])
+ heightmap[index_2d] = y;
+ if (y > surface_max_y)
+ surface_max_y = y;
+ } else if (y <= water_level) {
+ // sea
+ vm->m_data[index_data] = n_water;
+ } else if (river) {
+ // river
+ vm->m_data[index_data] = n_river_water;
+ } else {
+ vm->m_data[index_data] = n_air;
+ }
+ }
+
+ vm->m_area.add_y(em, index_data, 1);
+ index_3d += ystride;
+ }
+
+ // This happens if we're generating a chunk that doesn't
+ // contain the terrain surface, in which case, we need
+ // to set heightmap to a value outside of the chunk,
+ // to avoid confusing lua mods that use heightmap.
+ if (heightmap[index_2d] == -MAX_MAP_GENERATION_LIMIT) {
+ s16 surface_y_int = myround(surface_y);
+ if (surface_y_int > node_max.Y + 1 || surface_y_int < node_min.Y - 1) {
+ // If surface_y is outside the chunk, it's good enough.
+ heightmap[index_2d] = surface_y_int;
+ } else {
+ // If the ground is outside of this chunk, but surface_y
+ // is within the chunk, give a value outside.
+ heightmap[index_2d] = node_min.Y - 2;
+ }
+ }
+
+ if (humid_rivers) {
+ // Use base ground (water table) in a riverbed, to
+ // avoid an unnatural rise in humidity.
+ float t_alt = MYMAX(noise_rivers->result[index_2d], (float)heightmap[index_2d]);
+ float humid = noise_humidity->result[index_2d];
+ float water_depth = (t_alt - river_y) / humidity_dropoff;
+ humid *= 1.f + pow(0.5f, MYMAX(water_depth, 1.f));
+
+ // Reduce humidity with altitude (ignoring riverbeds).
+ // This is similar to the lua version's seawater adjustment,
+ // but doesn't increase the base humidity, which causes
+ // problems with the default biomes.
+ if (t_alt > 0.f)
+ humid -= alt_to_humid * t_alt / altitude_chill;
+
+ noise_humidity->result[index_2d] = humid;
+ }
+
+ // Assign the heat adjusted by any changed altitudes.
+ // The altitude will change about half the time.
+ if (use_altitude_chill) {
+ // ground height ignoring riverbeds
+ float t_alt = MYMAX(noise_rivers->result[index_2d], (float)heightmap[index_2d]);
+ if (humid_rivers && heightmap[index_2d] == (s16)myround(surface_y))
+ // The altitude hasn't changed. Use the first result.
+ noise_heat->result[index_2d] = t_heat;
+ else if (t_alt > 0.f)
+ noise_heat->result[index_2d] -= alt_to_heat * t_alt / altitude_chill;
+ }
+ }
+
+ return surface_max_y;
+}
+
+
+MgStoneType MapgenValleys::generateBiomes(float *heat_map, float *humidity_map)
+{
+ v3s16 em = vm->m_area.getExtent();
+ u32 index = 0;
+ MgStoneType stone_type = STONE;
+
+ for (s16 z = node_min.Z; z <= node_max.Z; z++)
+ for (s16 x = node_min.X; x <= node_max.X; x++, index++) {
+ Biome *biome = NULL;
+ u16 depth_top = 0;
+ u16 base_filler = 0;
+ u16 depth_water_top = 0;
+ u32 vi = vm->m_area.index(x, node_max.Y, z);
+
+ // Check node at base of mapchunk above, either a node of a previously
+ // generated mapchunk or if not, a node of overgenerated base terrain.
+ content_t c_above = vm->m_data[vi + em.X].getContent();
+ bool air_above = c_above == CONTENT_AIR;
+ bool water_above = (c_above == c_water_source || c_above == c_river_water_source);
+
+ // If there is air or water above enable top/filler placement, otherwise force
+ // nplaced to stone level by setting a number exceeding any possible filler depth.
+ u16 nplaced = (air_above || water_above) ? 0 : U16_MAX;
+
+ for (s16 y = node_max.Y; y >= node_min.Y; y--) {
+ content_t c = vm->m_data[vi].getContent();
+
+ // Biome is recalculated each time an upper surface is detected while
+ // working down a column. The selected biome then remains in effect for
+ // all nodes below until the next surface and biome recalculation.
+ // Biome is recalculated:
+ // 1. At the surface of stone below air or water.
+ // 2. At the surface of water below air.
+ // 3. When stone or water is detected but biome has not yet been calculated.
+ if ((c == c_stone && (air_above || water_above || !biome))
+ || ((c == c_water_source || c == c_river_water_source)
+ && (air_above || !biome))) {
+ // Both heat and humidity have already been adjusted for altitude.
+ biome = bmgr->getBiome(heat_map[index], humidity_map[index], y);
+
+ depth_top = biome->depth_top;
+ base_filler = MYMAX(depth_top
+ + biome->depth_filler
+ + noise_filler_depth->result[index], 0.f);
+ depth_water_top = biome->depth_water_top;
+
+ // Detect stone type for dungeons during every biome calculation.
+ // This is more efficient than detecting per-node and will not
+ // miss any desert stone or sandstone biomes.
+ if (biome->c_stone == c_desert_stone)
+ stone_type = DESERT_STONE;
+ else if (biome->c_stone == c_sandstone)
+ stone_type = SANDSTONE;
+ }
+
+ if (c == c_stone) {
+ content_t c_below = vm->m_data[vi - em.X].getContent();
+
+ // If the node below isn't solid, make this node stone, so that
+ // any top/filler nodes above are structurally supported.
+ // This is done by aborting the cycle of top/filler placement
+ // immediately by forcing nplaced to stone level.
+ if (c_below == CONTENT_AIR
+ || c_below == c_water_source
+ || c_below == c_river_water_source)
+ nplaced = U16_MAX;
+
+ if (nplaced < depth_top) {
+ vm->m_data[vi] = MapNode(biome->c_top);
+ nplaced++;
+ } else if (nplaced < base_filler) {
+ vm->m_data[vi] = MapNode(biome->c_filler);
+ nplaced++;
+ } else {
+ vm->m_data[vi] = MapNode(biome->c_stone);
+ }
+
+ air_above = false;
+ water_above = false;
+ } else if (c == c_water_source) {
+ vm->m_data[vi] = MapNode((y > (s32)(water_level - depth_water_top))
+ ? biome->c_water_top : biome->c_water);
+ nplaced = 0; // Enable top/filler placement for next surface
+ air_above = false;
+ water_above = true;
+ } else if (c == c_river_water_source) {
+ vm->m_data[vi] = MapNode(biome->c_river_water);
+ nplaced = depth_top; // Enable filler placement for next surface
+ air_above = false;
+ water_above = true;
+ } else if (c == CONTENT_AIR) {
+ nplaced = 0; // Enable top/filler placement for next surface
+ air_above = true;
+ water_above = false;
+ } else { // Possible various nodes overgenerated from neighbouring mapchunks
+ nplaced = U16_MAX; // Disable top/filler placement
+ air_above = false;
+ water_above = false;
+ }
+
+ vm->m_area.add_y(em, vi, -1);
+ }
+ }
+
+ return stone_type;
+}
+
+
+void MapgenValleys::dustTopNodes()
+{
+ if (node_max.Y < water_level)
+ return;
+
+ v3s16 em = vm->m_area.getExtent();
+ u32 index = 0;
+
+ for (s16 z = node_min.Z; z <= node_max.Z; z++)
+ for (s16 x = node_min.X; x <= node_max.X; x++, index++) {
+ Biome *biome = (Biome *)bmgr->getRaw(biomemap[index]);
+
+ if (biome->c_dust == CONTENT_IGNORE)
+ continue;
+
+ u32 vi = vm->m_area.index(x, full_node_max.Y, z);
+ content_t c_full_max = vm->m_data[vi].getContent();
+ s16 y_start;
+
+ if (c_full_max == CONTENT_AIR) {
+ y_start = full_node_max.Y - 1;
+ } else if (c_full_max == CONTENT_IGNORE) {
+ vi = vm->m_area.index(x, node_max.Y + 1, z);
+ content_t c_max = vm->m_data[vi].getContent();
+
+ if (c_max == CONTENT_AIR)
+ y_start = node_max.Y;
+ else
+ continue;
+ } else {
+ continue;
+ }
+
+ vi = vm->m_area.index(x, y_start, z);
+ for (s16 y = y_start; y >= node_min.Y - 1; y--) {
+ if (vm->m_data[vi].getContent() != CONTENT_AIR)
+ break;
+
+ vm->m_area.add_y(em, vi, -1);
+ }
+
+ content_t c = vm->m_data[vi].getContent();
+ if (!ndef->get(c).buildable_to && c != CONTENT_IGNORE && c != biome->c_dust) {
+ vm->m_area.add_y(em, vi, 1);
+ vm->m_data[vi] = MapNode(biome->c_dust);
+ }
+ }
+}
+
+
+void MapgenValleys::generateCaves(s16 max_stone_y)
+{
+ if (max_stone_y < node_min.Y)
+ return;
+
+ noise_cave1->perlinMap3D(node_min.X, node_min.Y - 1, node_min.Z);
+ noise_cave2->perlinMap3D(node_min.X, node_min.Y - 1, node_min.Z);
+
+ PseudoRandom ps(blockseed + 72202);
+
+ MapNode n_air(CONTENT_AIR);
+ MapNode n_lava(c_lava_source);
+ MapNode n_water(c_river_water_source);
+
+ v3s16 em = vm->m_area.getExtent();
+
+ // Cave blend distance near YMIN, YMAX
+ const float massive_cave_blend = 128.f;
+ // noise threshold for massive caves
+ const float massive_cave_threshold = 0.6f;
+ // mct: 1 = small rare caves, 0.5 1/3rd ground volume, 0 = 1/2 ground volume.
+
+ float yblmin = -map_gen_limit + massive_cave_blend * 1.5f;
+ float yblmax = massive_cave_depth - massive_cave_blend * 1.5f;
+ bool made_a_big_one = false;
+
+ // Cache the tcave values as they only vary by altitude.
+ if (node_max.Y <= massive_cave_depth) {
+ noise_massive_caves->perlinMap3D(node_min.X, node_min.Y - 1, node_min.Z);
+
+ for (s16 y = node_min.Y - 1; y <= node_max.Y; y++) {
+ float tcave = massive_cave_threshold;
+
+ if (y < yblmin) {
+ float t = (yblmin - y) / massive_cave_blend;
+ tcave += MYSQUARE(t);
+ } else if (y > yblmax) {
+ float t = (y - yblmax) / massive_cave_blend;
+ tcave += MYSQUARE(t);
+ }
+
+ tcave_cache[y - node_min.Y + 1] = tcave;
+ }
+ }
+
+ // lava_depth varies between one and ten as you approach
+ // the bottom of the world.
+ s16 lava_depth = ceil((lava_max_height - node_min.Y + 1) * 10.f / map_gen_limit);
+ // This allows random lava spawns to be less common at the surface.
+ s16 lava_chance = MYCUBE(lava_features_lim) * lava_depth;
+ // water_depth varies between ten and one on the way down.
+ s16 water_depth = ceil((map_gen_limit - abs(node_min.Y) + 1) * 10.f / map_gen_limit);
+ // This allows random water spawns to be more common at the surface.
+ s16 water_chance = MYCUBE(water_features_lim) * water_depth;
+
+ // Reduce the odds of overflows even further.
+ if (node_max.Y > water_level) {
+ lava_chance /= 3;
+ water_chance /= 3;
+ }
+
+ u32 index_2d = 0;
+ for (s16 z = node_min.Z; z <= node_max.Z; z++)
+ for (s16 x = node_min.X; x <= node_max.X; x++, index_2d++) {
+ Biome *biome = (Biome *)bmgr->getRaw(biomemap[index_2d]);
+ bool tunnel_air_above = false;
+ bool underground = false;
+ u32 index_data = vm->m_area.index(x, node_max.Y, z);
+ u32 index_3d = (z - node_min.Z) * zstride_1d + csize.Y * ystride + (x - node_min.X);
+
+ // Dig caves on down loop to check for air above.
+ // Don't excavate the overgenerated stone at node_max.Y + 1,
+ // this creates a 'roof' over the tunnel, preventing light in
+ // tunnels at mapchunk borders when generating mapchunks upwards.
+ // This 'roof' is removed when the mapchunk above is generated.
+ for (s16 y = node_max.Y; y >= node_min.Y - 1; y--,
+ index_3d -= ystride,
+ vm->m_area.add_y(em, index_data, -1)) {
+
+ float terrain = noise_terrain_height->result[index_2d];
+
+ // Saves some time.
+ if (y > terrain + 10)
+ continue;
+ else if (y < terrain - 40)
+ underground = true;
+
+ // Dig massive caves.
+ if (node_max.Y <= massive_cave_depth
+ && noise_massive_caves->result[index_3d]
+ > tcave_cache[y - node_min.Y + 1]) {
+ vm->m_data[index_data] = n_air;
+ made_a_big_one = true;
+ continue;
+ }
+
+ content_t c = vm->m_data[index_data].getContent();
+ float d1 = contour(noise_cave1->result[index_3d]);
+ float d2 = contour(noise_cave2->result[index_3d]);
+
+ // River water is not set as ground content
+ // in the default game. This can produce strange results
+ // when a tunnel undercuts a river. However, that's not for
+ // the mapgen to correct. Fix it in lua.
+
+ if (d1 * d2 > cave_width && ndef->get(c).is_ground_content) {
+ // in a tunnel
+ vm->m_data[index_data] = n_air;
+ tunnel_air_above = true;
+ } else if (c == biome->c_filler || c == biome->c_stone) {
+ if (tunnel_air_above) {
+ // at the tunnel floor
+ s16 sr = ps.range(0, 39);
+ u32 j = index_data;
+ vm->m_area.add_y(em, j, 1);
+
+ if (sr > terrain - y) {
+ // Put dirt in tunnels near the surface.
+ if (underground)
+ vm->m_data[index_data] = MapNode(biome->c_filler);
+ else
+ vm->m_data[index_data] = MapNode(biome->c_top);
+ } else if (sr < 3 && underground) {
+ sr = abs(ps.next());
+ if (lava_features_lim > 0 && y <= lava_max_height
+ && c == biome->c_stone && sr < lava_chance)
+ vm->m_data[j] = n_lava;
+
+ sr -= lava_chance;
+
+ // If sr < 0 then we should have already placed lava --
+ // don't immediately dump water on it.
+ if (water_features_lim > 0 && y <= cave_water_max_height
+ && sr >= 0 && sr < water_chance)
+ vm->m_data[j] = n_water;
+ }
+ }
+
+ tunnel_air_above = false;
+ underground = true;
+ } else {
+ tunnel_air_above = false;
+ }
+ }
+ }
+
+ if (node_max.Y <= large_cave_depth && !made_a_big_one) {
+ u32 bruises_count = ps.range(0, 2);
+ for (u32 i = 0; i < bruises_count; i++) {
+ CaveV5 cave(this, &ps);
+ cave.makeCave(node_min, node_max, max_stone_y);
+ }
+ }
+}
diff --git a/src/mapgen_valleys.h b/src/mapgen_valleys.h
new file mode 100644
index 000000000..5224ea54b
--- /dev/null
+++ b/src/mapgen_valleys.h
@@ -0,0 +1,187 @@
+/*
+Minetest Valleys C
+Copyright (C) 2010-2015 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
+Copyright (C) 2010-2015 paramat, Matt Gregory
+Copyright (C) 2016 Duane Robertson <duane@duanerobertson.com>
+
+Based on Valleys Mapgen by Gael de Sailly
+ (https://forum.minetest.net/viewtopic.php?f=9&t=11430)
+and mapgen_v7 by kwolekr and paramat.
+
+Licensing changed by permission of Gael de Sailly.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef MAPGEN_VALLEYS_HEADER
+#define MAPGEN_VALLEYS_HEADER
+
+#include "mapgen.h"
+
+////////////// Mapgen Valleys flags
+#define MGVALLEYS_ALT_CHILL 0x01
+#define MGVALLEYS_HUMID_RIVERS 0x02
+
+// Feed only one variable into these.
+#define MYSQUARE(x) (x) * (x)
+#define MYCUBE(x) (x) * (x) * (x)
+
+class BiomeManager;
+
+// Global profiler
+//class Profiler;
+//extern Profiler *mapgen_profiler;
+
+
+struct MapgenValleysParams : public MapgenSpecificParams {
+ u32 spflags;
+ s16 large_cave_depth;
+ s16 massive_cave_depth;
+ u16 altitude_chill;
+ u16 lava_features;
+ u16 river_depth;
+ u16 river_size;
+ u16 water_features;
+ float cave_width;
+ NoiseParams np_cave1;
+ NoiseParams np_cave2;
+ NoiseParams np_filler_depth;
+ NoiseParams np_inter_valley_fill;
+ NoiseParams np_inter_valley_slope;
+ NoiseParams np_rivers;
+ NoiseParams np_massive_caves;
+ NoiseParams np_terrain_height;
+ NoiseParams np_valley_depth;
+ NoiseParams np_valley_profile;
+
+ MapgenValleysParams();
+ ~MapgenValleysParams() {}
+
+ void readParams(const Settings *settings);
+ void writeParams(Settings *settings) const;
+};
+
+struct TerrainNoise {
+ s16 x;
+ s16 z;
+ float terrain_height;
+ float *rivers;
+ float *valley;
+ float valley_profile;
+ float *slope;
+ float inter_valley_fill;
+};
+
+class MapgenValleys : public Mapgen {
+public:
+
+ MapgenValleys(int mapgenid, MapgenParams *params, EmergeManager *emerge);
+ ~MapgenValleys();
+
+ virtual void makeChunk(BlockMakeData *data);
+ int getSpawnLevelAtPoint(v2s16 p);
+
+ s16 large_cave_depth;
+
+private:
+ EmergeManager *m_emerge;
+ BiomeManager *bmgr;
+
+ int ystride;
+ int zstride;
+ int zstride_1d;
+
+ float map_gen_limit;
+
+ bool humid_rivers;
+ bool use_altitude_chill;
+ float humidity_adjust;
+ s16 cave_water_max_height;
+ s16 lava_max_height;
+
+ v3s16 node_min;
+ v3s16 node_max;
+ v3s16 full_node_min;
+ v3s16 full_node_max;
+
+ u32 spflags;
+ float altitude_chill;
+ s16 lava_features_lim;
+ s16 massive_cave_depth;
+ float river_depth_bed;
+ float river_size_factor;
+ float *tcave_cache;
+ s16 water_features_lim;
+ float cave_width;
+ Noise *noise_filler_depth;
+ Noise *noise_cave1;
+ Noise *noise_cave2;
+ Noise *noise_inter_valley_fill;
+ Noise *noise_inter_valley_slope;
+ Noise *noise_rivers;
+ Noise *noise_massive_caves;
+ Noise *noise_terrain_height;
+ Noise *noise_valley_depth;
+ Noise *noise_valley_profile;
+
+ Noise *noise_heat;
+ Noise *noise_heat_blend;
+ Noise *noise_humidity;
+ Noise *noise_humidity_blend;
+
+ content_t c_cobble;
+ content_t c_desert_stone;
+ content_t c_dirt;
+ content_t c_ice;
+ content_t c_lava_source;
+ content_t c_mossycobble;
+ content_t c_river_water_source;
+ content_t c_sand;
+ content_t c_sandstone;
+ content_t c_sandstonebrick;
+ content_t c_stair_cobble;
+ content_t c_stair_sandstonebrick;
+ content_t c_stone;
+ content_t c_water_source;
+
+ float terrainLevelAtPoint(s16 x, s16 z);
+
+ void calculateNoise();
+
+ virtual int generateTerrain();
+ float terrainLevelFromNoise(TerrainNoise *tn);
+ float adjustedTerrainLevelFromNoise(TerrainNoise *tn);
+
+ float humidityByTerrain(float humidity_base, float mount, float rivers, float valley);
+
+ MgStoneType generateBiomes(float *heat_map, float *humidity_map);
+ void dustTopNodes();
+
+ void generateCaves(s16 max_stone_y);
+};
+
+struct MapgenFactoryValleys : public MapgenFactory {
+ Mapgen *createMapgen(int mgid, MapgenParams *params, EmergeManager *emerge)
+ {
+ return new MapgenValleys(mgid, params, emerge);
+ };
+
+ MapgenSpecificParams *createMapgenParams()
+ {
+ return new MapgenValleysParams();
+ };
+};
+
+#endif
diff --git a/src/mapnode.cpp b/src/mapnode.cpp
index fe9686f0d..eba47446d 100644
--- a/src/mapnode.cpp
+++ b/src/mapnode.cpp
@@ -159,16 +159,50 @@ v3s16 MapNode::getWallMountedDir(INodeDefManager *nodemgr) const
}
}
-void MapNode::rotateAlongYAxis(INodeDefManager *nodemgr, Rotation rot) {
+void MapNode::rotateAlongYAxis(INodeDefManager *nodemgr, Rotation rot)
+{
ContentParamType2 cpt2 = nodemgr->get(*this).param_type_2;
if (cpt2 == CPT2_FACEDIR) {
- if (param2 >= 4)
- return;
-
- u8 newrot = param2 & 3;
- param2 &= ~3;
- param2 |= (newrot + rot) & 3;
+ static const u8 rotate_facedir[24 * 4] = {
+ // Table value = rotated facedir
+ // Columns: 0, 90, 180, 270 degrees rotation around vertical axis
+ // Rotation is anticlockwise as seen from above (+Y)
+
+ 0, 1, 2, 3, // Initial facedir 0 to 3
+ 1, 2, 3, 0,
+ 2, 3, 0, 1,
+ 3, 0, 1, 2,
+
+ 4, 13, 10, 19, // 4 to 7
+ 5, 14, 11, 16,
+ 6, 15, 8, 17,
+ 7, 12, 9, 18,
+
+ 8, 17, 6, 15, // 8 to 11
+ 9, 18, 7, 12,
+ 10, 19, 4, 13,
+ 11, 16, 5, 14,
+
+ 12, 9, 18, 7, // 12 to 15
+ 13, 10, 19, 4,
+ 14, 11, 16, 5,
+ 15, 8, 17, 6,
+
+ 16, 5, 14, 11, // 16 to 19
+ 17, 6, 15, 8,
+ 18, 7, 12, 9,
+ 19, 4, 13, 10,
+
+ 20, 23, 22, 21, // 20 to 23
+ 21, 20, 23, 22,
+ 22, 21, 20, 23,
+ 23, 22, 21, 20
+ };
+ u8 facedir = (param2 & 31) % 24;
+ u8 index = facedir * 4 + rot;
+ param2 &= ~31;
+ param2 |= rotate_facedir[index];
} else if (cpt2 == CPT2_WALLMOUNTED) {
u8 wmountface = (param2 & 7);
if (wmountface <= 1)
@@ -180,19 +214,19 @@ void MapNode::rotateAlongYAxis(INodeDefManager *nodemgr, Rotation rot) {
}
}
-static std::vector<aabb3f> transformNodeBox(const MapNode &n,
- const NodeBox &nodebox, INodeDefManager *nodemgr)
+void transformNodeBox(const MapNode &n, const NodeBox &nodebox,
+ INodeDefManager *nodemgr, std::vector<aabb3f> *p_boxes, u8 neighbors = 0)
{
- std::vector<aabb3f> boxes;
- if(nodebox.type == NODEBOX_FIXED || nodebox.type == NODEBOX_LEVELED)
- {
+ std::vector<aabb3f> &boxes = *p_boxes;
+
+ if (nodebox.type == NODEBOX_FIXED || nodebox.type == NODEBOX_LEVELED) {
const std::vector<aabb3f> &fixed = nodebox.fixed;
int facedir = n.getFaceDir(nodemgr);
u8 axisdir = facedir>>2;
facedir&=0x03;
for(std::vector<aabb3f>::const_iterator
i = fixed.begin();
- i != fixed.end(); i++)
+ i != fixed.end(); ++i)
{
aabb3f box = *i;
@@ -361,32 +395,71 @@ static std::vector<aabb3f> transformNodeBox(const MapNode &n,
boxes.push_back(box);
}
}
+ else if (nodebox.type == NODEBOX_CONNECTED)
+ {
+ size_t boxes_size = boxes.size();
+ boxes_size += nodebox.fixed.size();
+ if (neighbors & 1)
+ boxes_size += nodebox.connect_top.size();
+ if (neighbors & 2)
+ boxes_size += nodebox.connect_bottom.size();
+ if (neighbors & 4)
+ boxes_size += nodebox.connect_front.size();
+ if (neighbors & 8)
+ boxes_size += nodebox.connect_left.size();
+ if (neighbors & 16)
+ boxes_size += nodebox.connect_back.size();
+ if (neighbors & 32)
+ boxes_size += nodebox.connect_right.size();
+ boxes.reserve(boxes_size);
+
+#define BOXESPUSHBACK(c) do { \
+ for (std::vector<aabb3f>::const_iterator \
+ it = (c).begin(); \
+ it != (c).end(); ++it) \
+ (boxes).push_back(*it); \
+ } while (0)
+
+ BOXESPUSHBACK(nodebox.fixed);
+
+ if (neighbors & 1)
+ BOXESPUSHBACK(nodebox.connect_top);
+ if (neighbors & 2)
+ BOXESPUSHBACK(nodebox.connect_bottom);
+ if (neighbors & 4)
+ BOXESPUSHBACK(nodebox.connect_front);
+ if (neighbors & 8)
+ BOXESPUSHBACK(nodebox.connect_left);
+ if (neighbors & 16)
+ BOXESPUSHBACK(nodebox.connect_back);
+ if (neighbors & 32)
+ BOXESPUSHBACK(nodebox.connect_right);
+ }
else // NODEBOX_REGULAR
{
boxes.push_back(aabb3f(-BS/2,-BS/2,-BS/2,BS/2,BS/2,BS/2));
}
- return boxes;
}
-std::vector<aabb3f> MapNode::getNodeBoxes(INodeDefManager *nodemgr) const
+void MapNode::getNodeBoxes(INodeDefManager *nodemgr, std::vector<aabb3f> *boxes, u8 neighbors)
{
const ContentFeatures &f = nodemgr->get(*this);
- return transformNodeBox(*this, f.node_box, nodemgr);
+ transformNodeBox(*this, f.node_box, nodemgr, boxes, neighbors);
}
-std::vector<aabb3f> MapNode::getCollisionBoxes(INodeDefManager *nodemgr) const
+void MapNode::getCollisionBoxes(INodeDefManager *nodemgr, std::vector<aabb3f> *boxes, u8 neighbors)
{
const ContentFeatures &f = nodemgr->get(*this);
if (f.collision_box.fixed.empty())
- return transformNodeBox(*this, f.node_box, nodemgr);
+ transformNodeBox(*this, f.node_box, nodemgr, boxes, neighbors);
else
- return transformNodeBox(*this, f.collision_box, nodemgr);
+ transformNodeBox(*this, f.collision_box, nodemgr, boxes, neighbors);
}
-std::vector<aabb3f> MapNode::getSelectionBoxes(INodeDefManager *nodemgr) const
+void MapNode::getSelectionBoxes(INodeDefManager *nodemgr, std::vector<aabb3f> *boxes, u8 neighbors)
{
const ContentFeatures &f = nodemgr->get(*this);
- return transformNodeBox(*this, f.selection_box, nodemgr);
+ transformNodeBox(*this, f.selection_box, nodemgr, boxes, neighbors);
}
u8 MapNode::getMaxLevel(INodeDefManager *nodemgr) const
diff --git a/src/mapnode.h b/src/mapnode.h
index 7cc25c60c..2f6224f02 100644
--- a/src/mapnode.h
+++ b/src/mapnode.h
@@ -240,17 +240,17 @@ struct MapNode
/*
Gets list of node boxes (used for rendering (NDT_NODEBOX))
*/
- std::vector<aabb3f> getNodeBoxes(INodeDefManager *nodemgr) const;
+ void getNodeBoxes(INodeDefManager *nodemgr, std::vector<aabb3f> *boxes, u8 neighbors = 0);
/*
Gets list of selection boxes
*/
- std::vector<aabb3f> getSelectionBoxes(INodeDefManager *nodemgr) const;
+ void getSelectionBoxes(INodeDefManager *nodemg, std::vector<aabb3f> *boxes, u8 neighbors = 0);
/*
Gets list of collision boxes
*/
- std::vector<aabb3f> getCollisionBoxes(INodeDefManager *nodemgr) const;
+ void getCollisionBoxes(INodeDefManager *nodemgr, std::vector<aabb3f> *boxes, u8 neighbors = 0);
/*
Liquid helpers
diff --git a/src/mapsector.cpp b/src/mapsector.cpp
index 9ce3c8eb3..1588a5962 100644
--- a/src/mapsector.cpp
+++ b/src/mapsector.cpp
@@ -220,7 +220,7 @@ ServerMapSector* ServerMapSector::deSerialize(
if(n != sectors.end())
{
- dstream<<"WARNING: deSerializing existent sectors not supported "
+ warningstream<<"deSerializing existent sectors not supported "
"at the moment, because code hasn't been tested."
<<std::endl;
diff --git a/src/mesh.cpp b/src/mesh.cpp
index dab1575f3..b5bf8660a 100644
--- a/src/mesh.cpp
+++ b/src/mesh.cpp
@@ -104,7 +104,7 @@ void scaleMesh(scene::IMesh *mesh, v3f scale)
if (mesh == NULL)
return;
- core::aabbox3d<f32> bbox;
+ aabb3f bbox;
bbox.reset(0, 0, 0);
u32 mc = mesh->getMeshBufferCount();
@@ -132,7 +132,7 @@ void translateMesh(scene::IMesh *mesh, v3f vec)
if (mesh == NULL)
return;
- core::aabbox3d<f32> bbox;
+ aabb3f bbox;
bbox.reset(0, 0, 0);
u32 mc = mesh->getMeshBufferCount();
@@ -206,146 +206,139 @@ void setMeshColorByNormalXYZ(scene::IMesh *mesh,
const video::SColor &colorY,
const video::SColor &colorZ)
{
- if(mesh == NULL)
+ if (mesh == NULL)
return;
-
+
u16 mc = mesh->getMeshBufferCount();
- for(u16 j=0; j<mc; j++)
- {
+ for (u16 j = 0; j < mc; j++) {
scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
- video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices();
- u16 vc = buf->getVertexCount();
- for(u16 i=0; i<vc; i++)
- {
- f32 x = fabs(vertices[i].Normal.X);
- f32 y = fabs(vertices[i].Normal.Y);
- f32 z = fabs(vertices[i].Normal.Z);
- if(x >= y && x >= z)
- vertices[i].Color = colorX;
- else if(y >= z)
- vertices[i].Color = colorY;
+ const u32 stride = getVertexPitchFromType(buf->getVertexType());
+ u32 vertex_count = buf->getVertexCount();
+ u8 *vertices = (u8 *)buf->getVertices();
+ for (u32 i = 0; i < vertex_count; i++) {
+ video::S3DVertex *vertex = (video::S3DVertex *)(vertices + i * stride);
+ f32 x = fabs(vertex->Normal.X);
+ f32 y = fabs(vertex->Normal.Y);
+ f32 z = fabs(vertex->Normal.Z);
+ if (x >= y && x >= z)
+ vertex->Color = colorX;
+ else if (y >= z)
+ vertex->Color = colorY;
else
- vertices[i].Color = colorZ;
+ vertex->Color = colorZ;
}
}
}
-void rotateMeshXYby (scene::IMesh *mesh, f64 degrees)
-{
+void rotateMeshXYby(scene::IMesh *mesh, f64 degrees)
+{
u16 mc = mesh->getMeshBufferCount();
- for(u16 j = 0; j < mc; j++)
- {
+ for (u16 j = 0; j < mc; j++) {
scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
- video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices();
- u16 vc = buf->getVertexCount();
- for(u16 i = 0; i < vc; i++)
- {
- vertices[i].Pos.rotateXYBy(degrees);
- }
+ const u32 stride = getVertexPitchFromType(buf->getVertexType());
+ u32 vertex_count = buf->getVertexCount();
+ u8 *vertices = (u8 *)buf->getVertices();
+ for (u32 i = 0; i < vertex_count; i++)
+ ((video::S3DVertex *)(vertices + i * stride))->Pos.rotateXYBy(degrees);
}
}
-void rotateMeshXZby (scene::IMesh *mesh, f64 degrees)
-{
+void rotateMeshXZby(scene::IMesh *mesh, f64 degrees)
+{
u16 mc = mesh->getMeshBufferCount();
- for(u16 j = 0; j < mc; j++)
- {
+ for (u16 j = 0; j < mc; j++) {
scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
- video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices();
- u16 vc = buf->getVertexCount();
- for(u16 i = 0; i < vc; i++)
- {
- vertices[i].Pos.rotateXZBy(degrees);
- }
+ const u32 stride = getVertexPitchFromType(buf->getVertexType());
+ u32 vertex_count = buf->getVertexCount();
+ u8 *vertices = (u8 *)buf->getVertices();
+ for (u32 i = 0; i < vertex_count; i++)
+ ((video::S3DVertex *)(vertices + i * stride))->Pos.rotateXZBy(degrees);
}
}
-void rotateMeshYZby (scene::IMesh *mesh, f64 degrees)
-{
+void rotateMeshYZby(scene::IMesh *mesh, f64 degrees)
+{
u16 mc = mesh->getMeshBufferCount();
- for(u16 j = 0; j < mc; j++)
- {
+ for (u16 j = 0; j < mc; j++) {
scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
- video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices();
- u16 vc = buf->getVertexCount();
- for(u16 i = 0; i < vc; i++)
- {
- vertices[i].Pos.rotateYZBy(degrees);
- }
+ const u32 stride = getVertexPitchFromType(buf->getVertexType());
+ u32 vertex_count = buf->getVertexCount();
+ u8 *vertices = (u8 *)buf->getVertices();
+ for (u32 i = 0; i < vertex_count; i++)
+ ((video::S3DVertex *)(vertices + i * stride))->Pos.rotateYZBy(degrees);
}
}
void rotateMeshBy6dFacedir(scene::IMesh *mesh, int facedir)
-{
- int axisdir = facedir>>2;
+{
+ int axisdir = facedir >> 2;
facedir &= 0x03;
u16 mc = mesh->getMeshBufferCount();
- for(u16 j = 0; j < mc; j++)
- {
+ for (u16 j = 0; j < mc; j++) {
scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
- video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices();
- u16 vc = buf->getVertexCount();
- for(u16 i=0; i<vc; i++)
- {
- switch (axisdir)
- {
- case 0:
- if(facedir == 1)
- vertices[i].Pos.rotateXZBy(-90);
- else if(facedir == 2)
- vertices[i].Pos.rotateXZBy(180);
- else if(facedir == 3)
- vertices[i].Pos.rotateXZBy(90);
- break;
- case 1: // z+
- vertices[i].Pos.rotateYZBy(90);
- if(facedir == 1)
- vertices[i].Pos.rotateXYBy(90);
- else if(facedir == 2)
- vertices[i].Pos.rotateXYBy(180);
- else if(facedir == 3)
- vertices[i].Pos.rotateXYBy(-90);
- break;
- case 2: //z-
- vertices[i].Pos.rotateYZBy(-90);
- if(facedir == 1)
- vertices[i].Pos.rotateXYBy(-90);
- else if(facedir == 2)
- vertices[i].Pos.rotateXYBy(180);
- else if(facedir == 3)
- vertices[i].Pos.rotateXYBy(90);
- break;
- case 3: //x+
- vertices[i].Pos.rotateXYBy(-90);
- if(facedir == 1)
- vertices[i].Pos.rotateYZBy(90);
- else if(facedir == 2)
- vertices[i].Pos.rotateYZBy(180);
- else if(facedir == 3)
- vertices[i].Pos.rotateYZBy(-90);
- break;
- case 4: //x-
- vertices[i].Pos.rotateXYBy(90);
- if(facedir == 1)
- vertices[i].Pos.rotateYZBy(-90);
- else if(facedir == 2)
- vertices[i].Pos.rotateYZBy(180);
- else if(facedir == 3)
- vertices[i].Pos.rotateYZBy(90);
- break;
- case 5:
- vertices[i].Pos.rotateXYBy(-180);
- if(facedir == 1)
- vertices[i].Pos.rotateXZBy(90);
- else if(facedir == 2)
- vertices[i].Pos.rotateXZBy(180);
- else if(facedir == 3)
- vertices[i].Pos.rotateXZBy(-90);
- break;
- default:
- break;
+ const u32 stride = getVertexPitchFromType(buf->getVertexType());
+ u32 vertex_count = buf->getVertexCount();
+ u8 *vertices = (u8 *)buf->getVertices();
+ for (u32 i = 0; i < vertex_count; i++) {
+ video::S3DVertex *vertex = (video::S3DVertex *)(vertices + i * stride);
+ switch (axisdir) {
+ case 0:
+ if (facedir == 1)
+ vertex->Pos.rotateXZBy(-90);
+ else if (facedir == 2)
+ vertex->Pos.rotateXZBy(180);
+ else if (facedir == 3)
+ vertex->Pos.rotateXZBy(90);
+ break;
+ case 1: // z+
+ vertex->Pos.rotateYZBy(90);
+ if (facedir == 1)
+ vertex->Pos.rotateXYBy(90);
+ else if (facedir == 2)
+ vertex->Pos.rotateXYBy(180);
+ else if (facedir == 3)
+ vertex->Pos.rotateXYBy(-90);
+ break;
+ case 2: //z-
+ vertex->Pos.rotateYZBy(-90);
+ if (facedir == 1)
+ vertex->Pos.rotateXYBy(-90);
+ else if (facedir == 2)
+ vertex->Pos.rotateXYBy(180);
+ else if (facedir == 3)
+ vertex->Pos.rotateXYBy(90);
+ break;
+ case 3: //x+
+ vertex->Pos.rotateXYBy(-90);
+ if (facedir == 1)
+ vertex->Pos.rotateYZBy(90);
+ else if (facedir == 2)
+ vertex->Pos.rotateYZBy(180);
+ else if (facedir == 3)
+ vertex->Pos.rotateYZBy(-90);
+ break;
+ case 4: //x-
+ vertex->Pos.rotateXYBy(90);
+ if (facedir == 1)
+ vertex->Pos.rotateYZBy(-90);
+ else if (facedir == 2)
+ vertex->Pos.rotateYZBy(180);
+ else if (facedir == 3)
+ vertex->Pos.rotateYZBy(90);
+ break;
+ case 5:
+ vertex->Pos.rotateXYBy(-180);
+ if (facedir == 1)
+ vertex->Pos.rotateXZBy(90);
+ else if (facedir == 2)
+ vertex->Pos.rotateXZBy(180);
+ else if (facedir == 3)
+ vertex->Pos.rotateXZBy(-90);
+ break;
+ default:
+ break;
}
}
}
@@ -353,13 +346,12 @@ void rotateMeshBy6dFacedir(scene::IMesh *mesh, int facedir)
void recalculateBoundingBox(scene::IMesh *src_mesh)
{
- core::aabbox3d<f32> bbox;
+ aabb3f bbox;
bbox.reset(0,0,0);
- for(u16 j = 0; j < src_mesh->getMeshBufferCount(); j++)
- {
+ for (u16 j = 0; j < src_mesh->getMeshBufferCount(); j++) {
scene::IMeshBuffer *buf = src_mesh->getMeshBuffer(j);
buf->recalculateBoundingBox();
- if(j == 0)
+ if (j == 0)
bbox = buf->getBoundingBox();
else
bbox.addInternalBox(buf->getBoundingBox());
@@ -370,23 +362,54 @@ void recalculateBoundingBox(scene::IMesh *src_mesh)
scene::IMesh* cloneMesh(scene::IMesh *src_mesh)
{
scene::SMesh* dst_mesh = new scene::SMesh();
- for(u16 j = 0; j < src_mesh->getMeshBufferCount(); j++)
- {
+ for (u16 j = 0; j < src_mesh->getMeshBufferCount(); j++) {
scene::IMeshBuffer *buf = src_mesh->getMeshBuffer(j);
- video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices();
- u16 *indices = (u16*)buf->getIndices();
- scene::SMeshBuffer *temp_buf = new scene::SMeshBuffer();
- temp_buf->append(vertices, buf->getVertexCount(),
- indices, buf->getIndexCount());
- dst_mesh->addMeshBuffer(temp_buf);
- temp_buf->drop();
+ switch (buf->getVertexType()) {
+ case video::EVT_STANDARD: {
+ video::S3DVertex *v =
+ (video::S3DVertex *) buf->getVertices();
+ u16 *indices = (u16*)buf->getIndices();
+ scene::SMeshBuffer *temp_buf = new scene::SMeshBuffer();
+ temp_buf->append(v, buf->getVertexCount(),
+ indices, buf->getIndexCount());
+ dst_mesh->addMeshBuffer(temp_buf);
+ temp_buf->drop();
+ break;
+ }
+ case video::EVT_2TCOORDS: {
+ video::S3DVertex2TCoords *v =
+ (video::S3DVertex2TCoords *) buf->getVertices();
+ u16 *indices = (u16*)buf->getIndices();
+ scene::SMeshBufferTangents *temp_buf =
+ new scene::SMeshBufferTangents();
+ temp_buf->append(v, buf->getVertexCount(),
+ indices, buf->getIndexCount());
+ dst_mesh->addMeshBuffer(temp_buf);
+ temp_buf->drop();
+ break;
+ }
+ case video::EVT_TANGENTS: {
+ video::S3DVertexTangents *v =
+ (video::S3DVertexTangents *) buf->getVertices();
+ u16 *indices = (u16*)buf->getIndices();
+ scene::SMeshBufferTangents *temp_buf =
+ new scene::SMeshBufferTangents();
+ temp_buf->append(v, buf->getVertexCount(),
+ indices, buf->getIndexCount());
+ dst_mesh->addMeshBuffer(temp_buf);
+ temp_buf->drop();
+ break;
+ }
+ }
}
- return dst_mesh;
+ return dst_mesh;
}
-scene::IMesh* convertNodeboxNodeToMesh(ContentFeatures *f)
+scene::IMesh* convertNodeboxesToMesh(const std::vector<aabb3f> &boxes,
+ const f32 *uv_coords, float expand)
{
scene::SMesh* dst_mesh = new scene::SMesh();
+
for (u16 j = 0; j < 6; j++)
{
scene::IMeshBuffer *buf = new scene::SMeshBuffer();
@@ -395,57 +418,49 @@ scene::IMesh* convertNodeboxNodeToMesh(ContentFeatures *f)
dst_mesh->addMeshBuffer(buf);
buf->drop();
}
-
+
video::SColor c(255,255,255,255);
- std::vector<aabb3f> boxes = f->node_box.fixed;
-
- for(std::vector<aabb3f>::iterator
+ for (std::vector<aabb3f>::const_iterator
i = boxes.begin();
- i != boxes.end(); i++)
+ i != boxes.end(); ++i)
{
aabb3f box = *i;
-
- f32 temp;
- if (box.MinEdge.X > box.MaxEdge.X)
- {
- temp=box.MinEdge.X;
- box.MinEdge.X=box.MaxEdge.X;
- box.MaxEdge.X=temp;
- }
- if (box.MinEdge.Y > box.MaxEdge.Y)
- {
- temp=box.MinEdge.Y;
- box.MinEdge.Y=box.MaxEdge.Y;
- box.MaxEdge.Y=temp;
- }
- if (box.MinEdge.Z > box.MaxEdge.Z)
- {
- temp=box.MinEdge.Z;
- box.MinEdge.Z=box.MaxEdge.Z;
- box.MaxEdge.Z=temp;
- }
- // Compute texture coords
- f32 tx1 = (box.MinEdge.X/BS)+0.5;
- f32 ty1 = (box.MinEdge.Y/BS)+0.5;
- f32 tz1 = (box.MinEdge.Z/BS)+0.5;
- f32 tx2 = (box.MaxEdge.X/BS)+0.5;
- f32 ty2 = (box.MaxEdge.Y/BS)+0.5;
- f32 tz2 = (box.MaxEdge.Z/BS)+0.5;
- f32 txc[24] = {
+ box.repair();
+
+ box.MinEdge.X -= expand;
+ box.MinEdge.Y -= expand;
+ box.MinEdge.Z -= expand;
+ box.MaxEdge.X += expand;
+ box.MaxEdge.Y += expand;
+ box.MaxEdge.Z += expand;
+
+ // Compute texture UV coords
+ f32 tx1 = (box.MinEdge.X / BS) + 0.5;
+ f32 ty1 = (box.MinEdge.Y / BS) + 0.5;
+ f32 tz1 = (box.MinEdge.Z / BS) + 0.5;
+ f32 tx2 = (box.MaxEdge.X / BS) + 0.5;
+ f32 ty2 = (box.MaxEdge.Y / BS) + 0.5;
+ f32 tz2 = (box.MaxEdge.Z / BS) + 0.5;
+
+ f32 txc_default[24] = {
// up
- tx1, 1-tz2, tx2, 1-tz1,
+ tx1, 1 - tz2, tx2, 1 - tz1,
// down
tx1, tz1, tx2, tz2,
// right
- tz1, 1-ty2, tz2, 1-ty1,
+ tz1, 1 - ty2, tz2, 1 - ty1,
// left
- 1-tz2, 1-ty2, 1-tz1, 1-ty1,
+ 1 - tz2, 1 - ty2, 1 - tz1, 1 - ty1,
// back
- 1-tx2, 1-ty2, 1-tx1, 1-ty1,
+ 1 - tx2, 1 - ty2, 1 - tx1, 1 - ty1,
// front
- tx1, 1-ty2, tx2, 1-ty1,
+ tx1, 1 - ty2, tx2, 1 - ty1,
};
+
+ // use default texture UV mapping if not provided
+ const f32 *txc = uv_coords ? uv_coords : txc_default;
+
v3f min = box.MinEdge;
v3f max = box.MaxEdge;
diff --git a/src/mesh.h b/src/mesh.h
index ec109e9e9..8e1893773 100644
--- a/src/mesh.h
+++ b/src/mesh.h
@@ -83,9 +83,13 @@ void rotateMeshYZby (scene::IMesh *mesh, f64 degrees);
scene::IMesh* cloneMesh(scene::IMesh *src_mesh);
/*
- Convert nodebox drawtype node to mesh.
+ Convert nodeboxes to mesh.
+ boxes - set of nodeboxes to be converted into cuboids
+ uv_coords[24] - table of texture uv coords for each cuboid face
+ expand - factor by which cuboids will be resized
*/
-scene::IMesh* convertNodeboxNodeToMesh(ContentFeatures *f);
+scene::IMesh* convertNodeboxesToMesh(const std::vector<aabb3f> &boxes,
+ const f32 *uv_coords = NULL, float expand = 0);
/*
Update bounding box for a mesh.
diff --git a/src/mg_biome.cpp b/src/mg_biome.cpp
index 055ce0198..9ab8d06cc 100644
--- a/src/mg_biome.cpp
+++ b/src/mg_biome.cpp
@@ -56,7 +56,7 @@ BiomeManager::BiomeManager(IGameDef *gamedef) :
b->m_nodenames.push_back("mapgen_water_source");
b->m_nodenames.push_back("mapgen_water_source");
b->m_nodenames.push_back("mapgen_river_water_source");
- b->m_nodenames.push_back("air");
+ b->m_nodenames.push_back("ignore");
m_ndef->pendNodeResolve(b);
add(b);
@@ -138,5 +138,5 @@ void Biome::resolveNodeNames()
getIdFromNrBacklog(&c_water_top, "mapgen_water_source", CONTENT_AIR);
getIdFromNrBacklog(&c_water, "mapgen_water_source", CONTENT_AIR);
getIdFromNrBacklog(&c_river_water, "mapgen_river_water_source", CONTENT_AIR);
- getIdFromNrBacklog(&c_dust, "air", CONTENT_IGNORE);
+ getIdFromNrBacklog(&c_dust, "ignore", CONTENT_IGNORE);
}
diff --git a/src/mg_decoration.cpp b/src/mg_decoration.cpp
index 0d6693929..8b6abb5d5 100644
--- a/src/mg_decoration.cpp
+++ b/src/mg_decoration.cpp
@@ -30,6 +30,7 @@ FlagDesc flagdesc_deco[] = {
{"place_center_y", DECO_PLACE_CENTER_Y},
{"place_center_z", DECO_PLACE_CENTER_Z},
{"force_placement", DECO_FORCE_PLACEMENT},
+ {"liquid_surface", DECO_LIQUID_SURFACE},
{NULL, 0}
};
@@ -86,15 +87,13 @@ void Decoration::resolveNodeNames()
size_t Decoration::placeDeco(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax)
{
- PseudoRandom ps(blockseed + 53);
+ PcgRandom ps(blockseed + 53);
int carea_size = nmax.X - nmin.X + 1;
// Divide area into parts
- if (carea_size % sidelen) {
- errorstream << "Decoration::placeDeco: chunk size is not divisible by "
- "sidelen; setting sidelen to " << carea_size << std::endl;
+ // If chunksize is changed it may no longer be divisable by sidelen
+ if (carea_size % sidelen)
sidelen = carea_size;
- }
s16 divlen = carea_size / sidelen;
int area = sidelen * sidelen;
@@ -118,7 +117,15 @@ size_t Decoration::placeDeco(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax)
float nval = (flags & DECO_USE_NOISE) ?
NoisePerlin2D(&np, p2d_center.X, p2d_center.Y, mapseed) :
fill_ratio;
- u32 deco_count = area * MYMAX(nval, 0.f);
+ u32 deco_count = 0;
+ float deco_count_f = (float)area * nval;
+ if (deco_count_f >= 1.f) {
+ deco_count = deco_count_f;
+ } else if (deco_count_f > 0.f) {
+ // For low density decorations calculate a chance for 1 decoration
+ if (ps.range(1000) <= deco_count_f * 1000.f)
+ deco_count = 1;
+ }
for (u32 i = 0; i < deco_count; i++) {
s16 x = ps.range(p2d_min.X, p2d_max.X);
@@ -126,9 +133,13 @@ size_t Decoration::placeDeco(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax)
int mapindex = carea_size * (z - nmin.Z) + (x - nmin.X);
- s16 y = mg->heightmap ?
- mg->heightmap[mapindex] :
- mg->findGroundLevel(v2s16(x, z), nmin.Y, nmax.Y);
+ s16 y = -MAX_MAP_GENERATION_LIMIT;
+ if (flags & DECO_LIQUID_SURFACE)
+ y = mg->findLiquidSurface(v2s16(x, z), nmin.Y, nmax.Y);
+ else if (mg->heightmap)
+ y = mg->heightmap[mapindex];
+ else
+ y = mg->findGroundLevel(v2s16(x, z), nmin.Y, nmax.Y);
if (y < nmin.Y || y > nmax.Y ||
y < y_min || y > y_max)
@@ -139,7 +150,7 @@ size_t Decoration::placeDeco(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax)
#if 0
printf("Decoration at (%d %d %d) cut off\n", x, y, z);
//add to queue
- JMutexAutoLock cutofflock(cutoff_mutex);
+ MutexAutoLock cutofflock(cutoff_mutex);
cutoffs.push_back(CutoffData(x, y, z, height));
#endif
}
@@ -167,12 +178,12 @@ size_t Decoration::placeDeco(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax)
#if 0
void Decoration::placeCutoffs(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax)
{
- PseudoRandom pr(blockseed + 53);
+ PcgRandom pr(blockseed + 53);
std::vector<CutoffData> handled_cutoffs;
// Copy over the cutoffs we're interested in so we don't needlessly hold a lock
{
- JMutexAutoLock cutofflock(cutoff_mutex);
+ MutexAutoLock cutofflock(cutoff_mutex);
for (std::list<CutoffData>::iterator i = cutoffs.begin();
i != cutoffs.end(); ++i) {
CutoffData cutoff = *i;
@@ -203,7 +214,7 @@ void Decoration::placeCutoffs(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax)
// Remove cutoffs that were handled from the cutoff list
{
- JMutexAutoLock cutofflock(cutoff_mutex);
+ MutexAutoLock cutofflock(cutoff_mutex);
for (std::list<CutoffData>::iterator i = cutoffs.begin();
i != cutoffs.end(); ++i) {
@@ -283,7 +294,7 @@ bool DecoSimple::canPlaceDecoration(MMVManip *vm, v3s16 p)
}
-size_t DecoSimple::generate(MMVManip *vm, PseudoRandom *pr, v3s16 p)
+size_t DecoSimple::generate(MMVManip *vm, PcgRandom *pr, v3s16 p)
{
if (!canPlaceDecoration(vm, p))
return 0;
@@ -293,13 +304,16 @@ size_t DecoSimple::generate(MMVManip *vm, PseudoRandom *pr, v3s16 p)
s16 height = (deco_height_max > 0) ?
pr->range(deco_height, deco_height_max) : deco_height;
+ bool force_placement = (flags & DECO_FORCE_PLACEMENT);
+
v3s16 em = vm->m_area.getExtent();
u32 vi = vm->m_area.index(p);
for (int i = 0; i < height; i++) {
vm->m_area.add_y(em, vi, 1);
content_t c = vm->m_data[vi].getContent();
- if (c != CONTENT_AIR && c != CONTENT_IGNORE)
+ if (c != CONTENT_AIR && c != CONTENT_IGNORE &&
+ !force_placement)
break;
vm->m_data[vi] = MapNode(c_place);
@@ -324,7 +338,7 @@ DecoSchematic::DecoSchematic()
}
-size_t DecoSchematic::generate(MMVManip *vm, PseudoRandom *pr, v3s16 p)
+size_t DecoSchematic::generate(MMVManip *vm, PcgRandom *pr, v3s16 p)
{
// Schematic could have been unloaded but not the decoration
// In this case generate() does nothing (but doesn't *fail*)
@@ -348,7 +362,7 @@ size_t DecoSchematic::generate(MMVManip *vm, PseudoRandom *pr, v3s16 p)
bool force_placement = (flags & DECO_FORCE_PLACEMENT);
- schematic->blitToVManip(p, vm, rot, force_placement);
+ schematic->blitToVManip(vm, p, rot, force_placement);
return 1;
}
diff --git a/src/mg_decoration.h b/src/mg_decoration.h
index 056748918..ba3e9d3b2 100644
--- a/src/mg_decoration.h
+++ b/src/mg_decoration.h
@@ -27,7 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
class Mapgen;
class MMVManip;
-class PseudoRandom;
+class PcgRandom;
class Schematic;
enum DecorationType {
@@ -41,6 +41,7 @@ enum DecorationType {
#define DECO_PLACE_CENTER_Z 0x04
#define DECO_USE_NOISE 0x08
#define DECO_FORCE_PLACEMENT 0x10
+#define DECO_LIQUID_SURFACE 0x20
extern FlagDesc flagdesc_deco[];
@@ -70,7 +71,7 @@ public:
size_t placeDeco(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax);
//size_t placeCutoffs(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax);
- virtual size_t generate(MMVManip *vm, PseudoRandom *pr, v3s16 p) = 0;
+ virtual size_t generate(MMVManip *vm, PcgRandom *pr, v3s16 p) = 0;
virtual int getHeight() = 0;
u32 flags;
@@ -84,12 +85,12 @@ public:
std::set<u8> biomes;
//std::list<CutoffData> cutoffs;
- //JMutex cutoff_mutex;
+ //Mutex cutoff_mutex;
};
class DecoSimple : public Decoration {
public:
- virtual size_t generate(MMVManip *vm, PseudoRandom *pr, v3s16 p);
+ virtual size_t generate(MMVManip *vm, PcgRandom *pr, v3s16 p);
bool canPlaceDecoration(MMVManip *vm, v3s16 p);
virtual int getHeight();
@@ -106,7 +107,7 @@ class DecoSchematic : public Decoration {
public:
DecoSchematic();
- virtual size_t generate(MMVManip *vm, PseudoRandom *pr, v3s16 p);
+ virtual size_t generate(MMVManip *vm, PcgRandom *pr, v3s16 p);
virtual int getHeight();
Rotation rotation;
diff --git a/src/mg_ore.cpp b/src/mg_ore.cpp
index a94d1d6d9..257901614 100644
--- a/src/mg_ore.cpp
+++ b/src/mg_ore.cpp
@@ -25,8 +25,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "log.h"
FlagDesc flagdesc_ore[] = {
- {"absheight", OREFLAG_ABSHEIGHT},
- {NULL, 0}
+ {"absheight", OREFLAG_ABSHEIGHT},
+ {"puff_cliffs", OREFLAG_PUFF_CLIFFS},
+ {"puff_additive_composition", OREFLAG_PUFF_ADDITIVE},
+ {NULL, 0}
};
@@ -124,7 +126,7 @@ size_t Ore::placeOre(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax)
void OreScatter::generate(MMVManip *vm, int mapseed, u32 blockseed,
v3s16 nmin, v3s16 nmax, u8 *biomemap)
{
- PseudoRandom pr(blockseed);
+ PcgRandom pr(blockseed);
MapNode n_ore(c_ore, 0, ore_param2);
u32 sizex = (nmax.X - nmin.X + 1);
@@ -132,7 +134,7 @@ void OreScatter::generate(MMVManip *vm, int mapseed, u32 blockseed,
(nmax.Y - nmin.Y + 1) *
(nmax.Z - nmin.Z + 1);
u32 csize = clust_size;
- u32 orechance = (csize * csize * csize) / clust_num_ores;
+ u32 cvolume = csize * csize * csize;
u32 nclusters = volume / clust_scarcity;
for (u32 i = 0; i != nclusters; i++) {
@@ -154,7 +156,7 @@ void OreScatter::generate(MMVManip *vm, int mapseed, u32 blockseed,
for (u32 z1 = 0; z1 != csize; z1++)
for (u32 y1 = 0; y1 != csize; y1++)
for (u32 x1 = 0; x1 != csize; x1++) {
- if (pr.range(1, orechance) != 1)
+ if (pr.range(1, cvolume) > clust_num_ores)
continue;
u32 i = vm->m_area.index(x0 + x1, y0 + y1, z0 + z1);
@@ -173,11 +175,16 @@ void OreScatter::generate(MMVManip *vm, int mapseed, u32 blockseed,
void OreSheet::generate(MMVManip *vm, int mapseed, u32 blockseed,
v3s16 nmin, v3s16 nmax, u8 *biomemap)
{
- PseudoRandom pr(blockseed + 4234);
+ PcgRandom pr(blockseed + 4234);
MapNode n_ore(c_ore, 0, ore_param2);
- int max_height = clust_size;
- int y_start = pr.range(nmin.Y, nmax.Y - max_height);
+ u16 max_height = column_height_max;
+ int y_start_min = nmin.Y + max_height;
+ int y_start_max = nmax.Y - max_height;
+
+ int y_start = y_start_min < y_start_max ?
+ pr.range(y_start_min, y_start_max) :
+ (y_start_min + y_start_max) / 2;
if (!noise) {
int sx = nmax.X - nmin.X + 1;
@@ -200,10 +207,12 @@ void OreSheet::generate(MMVManip *vm, int mapseed, u32 blockseed,
continue;
}
- int height = max_height * (1. / pr.range(1, 3));
- int y0 = y_start + np.scale * noiseval; //pr.range(1, 3) - 1;
- int y1 = y0 + height;
- for (int y = y0; y != y1; y++) {
+ u16 height = pr.range(column_height_min, column_height_max);
+ int ymidpoint = y_start + noiseval;
+ int y0 = MYMAX(nmin.Y, ymidpoint - height * (1 - column_midpoint_factor));
+ int y1 = MYMIN(nmax.Y, y0 + height - 1);
+
+ for (int y = y0; y <= y1; y++) {
u32 i = vm->m_area.index(x, y, z);
if (!vm->m_area.contains(i))
continue;
@@ -218,10 +227,98 @@ void OreSheet::generate(MMVManip *vm, int mapseed, u32 blockseed,
///////////////////////////////////////////////////////////////////////////////
+OrePuff::OrePuff() :
+ Ore()
+{
+ noise_puff_top = NULL;
+ noise_puff_bottom = NULL;
+}
+
+
+OrePuff::~OrePuff()
+{
+ delete noise_puff_top;
+ delete noise_puff_bottom;
+}
+
+
+void OrePuff::generate(MMVManip *vm, int mapseed, u32 blockseed,
+ v3s16 nmin, v3s16 nmax, u8 *biomemap)
+{
+ PcgRandom pr(blockseed + 4234);
+ MapNode n_ore(c_ore, 0, ore_param2);
+
+ int y_start = pr.range(nmin.Y, nmax.Y);
+
+ if (!noise) {
+ int sx = nmax.X - nmin.X + 1;
+ int sz = nmax.Z - nmin.Z + 1;
+ noise = new Noise(&np, 0, sx, sz);
+ noise_puff_top = new Noise(&np_puff_top, 0, sx, sz);
+ noise_puff_bottom = new Noise(&np_puff_bottom, 0, sx, sz);
+ }
+
+ noise->seed = mapseed + y_start;
+ noise->perlinMap2D(nmin.X, nmin.Z);
+ bool noise_generated = false;
+
+ size_t index = 0;
+ for (int z = nmin.Z; z <= nmax.Z; z++)
+ for (int x = nmin.X; x <= nmax.X; x++, index++) {
+ float noiseval = noise->result[index];
+ if (noiseval < nthresh)
+ continue;
+
+ if (biomemap && !biomes.empty()) {
+ std::set<u8>::iterator it = biomes.find(biomemap[index]);
+ if (it == biomes.end())
+ continue;
+ }
+
+ if (!noise_generated) {
+ noise_generated = true;
+ noise_puff_top->perlinMap2D(nmin.X, nmin.Z);
+ noise_puff_bottom->perlinMap2D(nmin.X, nmin.Z);
+ }
+
+ float ntop = noise_puff_top->result[index];
+ float nbottom = noise_puff_bottom->result[index];
+
+ if (!(flags & OREFLAG_PUFF_CLIFFS)) {
+ float ndiff = noiseval - nthresh;
+ if (ndiff < 1.0f) {
+ ntop *= ndiff;
+ nbottom *= ndiff;
+ }
+ }
+
+ int ymid = y_start;
+ int y0 = ymid - nbottom;
+ int y1 = ymid + ntop;
+
+ if ((flags & OREFLAG_PUFF_ADDITIVE) && (y0 > y1))
+ SWAP(int, y0, y1);
+
+ for (int y = y0; y <= y1; y++) {
+ u32 i = vm->m_area.index(x, y, z);
+ if (!vm->m_area.contains(i))
+ continue;
+ if (!CONTAINS(c_wherein, vm->m_data[i].getContent()))
+ continue;
+
+ vm->m_data[i] = n_ore;
+ }
+ }
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+
void OreBlob::generate(MMVManip *vm, int mapseed, u32 blockseed,
v3s16 nmin, v3s16 nmax, u8 *biomemap)
{
- PseudoRandom pr(blockseed + 2404);
+ PcgRandom pr(blockseed + 2404);
MapNode n_ore(c_ore, 0, ore_param2);
u32 sizex = (nmax.X - nmin.X + 1);
@@ -266,9 +363,9 @@ void OreBlob::generate(MMVManip *vm, int mapseed, u32 blockseed,
float noiseval = noise->result[index];
- float xdist = x1 - csize / 2;
- float ydist = y1 - csize / 2;
- float zdist = z1 - csize / 2;
+ float xdist = (s32)x1 - (s32)csize / 2;
+ float ydist = (s32)y1 - (s32)csize / 2;
+ float zdist = (s32)z1 - (s32)csize / 2;
noiseval -= (sqrt(xdist * xdist + ydist * ydist + zdist * zdist) / csize);
@@ -283,7 +380,8 @@ void OreBlob::generate(MMVManip *vm, int mapseed, u32 blockseed,
///////////////////////////////////////////////////////////////////////////////
-OreVein::OreVein()
+OreVein::OreVein() :
+ Ore()
{
noise2 = NULL;
}
@@ -298,7 +396,7 @@ OreVein::~OreVein()
void OreVein::generate(MMVManip *vm, int mapseed, u32 blockseed,
v3s16 nmin, v3s16 nmax, u8 *biomemap)
{
- PseudoRandom pr(blockseed + 520);
+ PcgRandom pr(blockseed + 520);
MapNode n_ore(c_ore, 0, ore_param2);
u32 sizex = (nmax.X - nmin.X + 1);
diff --git a/src/mg_ore.h b/src/mg_ore.h
index ffe8cfe50..2e065cee3 100644
--- a/src/mg_ore.h
+++ b/src/mg_ore.h
@@ -30,17 +30,18 @@ class MMVManip;
/////////////////// Ore generation flags
-// Use absolute value of height to determine ore placement
-#define OREFLAG_ABSHEIGHT 0x01
-#define OREFLAG_USE_NOISE 0x08
+#define OREFLAG_ABSHEIGHT 0x01
+#define OREFLAG_PUFF_CLIFFS 0x02
+#define OREFLAG_PUFF_ADDITIVE 0x04
+#define OREFLAG_USE_NOISE 0x08
#define ORE_RANGE_ACTUAL 1
#define ORE_RANGE_MIRROR 2
-
enum OreType {
ORE_SCATTER,
ORE_SHEET,
+ ORE_PUFF,
ORE_BLOB,
ORE_VEIN,
};
@@ -60,7 +61,7 @@ public:
s16 y_max;
u8 ore_param2; // to set node-specific attributes
u32 flags; // attributes for this ore
- float nthresh; // threshhold for noise at which an ore is placed
+ float nthresh; // threshold for noise at which an ore is placed
NoiseParams np; // noise for distribution of clusters (NULL for uniform scattering)
Noise *noise;
std::set<u8> biomes;
@@ -87,6 +88,26 @@ class OreSheet : public Ore {
public:
static const bool NEEDS_NOISE = true;
+ u16 column_height_min;
+ u16 column_height_max;
+ float column_midpoint_factor;
+
+ virtual void generate(MMVManip *vm, int mapseed, u32 blockseed,
+ v3s16 nmin, v3s16 nmax, u8 *biomemap);
+};
+
+class OrePuff : public Ore {
+public:
+ static const bool NEEDS_NOISE = true;
+
+ NoiseParams np_puff_top;
+ NoiseParams np_puff_bottom;
+ Noise *noise_puff_top;
+ Noise *noise_puff_bottom;
+
+ OrePuff();
+ virtual ~OrePuff();
+
virtual void generate(MMVManip *vm, int mapseed, u32 blockseed,
v3s16 nmin, v3s16 nmax, u8 *biomemap);
};
@@ -130,6 +151,8 @@ public:
return new OreScatter;
case ORE_SHEET:
return new OreSheet;
+ case ORE_PUFF:
+ return new OrePuff;
case ORE_BLOB:
return new OreBlob;
case ORE_VEIN:
diff --git a/src/mg_schematic.cpp b/src/mg_schematic.cpp
index a5ffb20b8..0b95fa267 100644
--- a/src/mg_schematic.cpp
+++ b/src/mg_schematic.cpp
@@ -94,7 +94,7 @@ void Schematic::resolveNodeNames()
}
-void Schematic::blitToVManip(v3s16 p, MMVManip *vm, Rotation rot, bool force_place)
+void Schematic::blitToVManip(MMVManip *vm, v3s16 p, Rotation rot, bool force_place)
{
sanity_check(m_ndef != NULL);
@@ -175,20 +175,52 @@ void Schematic::blitToVManip(v3s16 p, MMVManip *vm, Rotation rot, bool force_pla
}
-void Schematic::placeStructure(Map *map, v3s16 p, u32 flags,
+bool Schematic::placeOnVManip(MMVManip *vm, v3s16 p, u32 flags,
Rotation rot, bool force_place)
{
- assert(schemdata != NULL); // Pre-condition
+ assert(vm != NULL);
+ assert(schemdata != NULL);
sanity_check(m_ndef != NULL);
- MMVManip *vm = new MMVManip(map);
+ //// Determine effective rotation and effective schematic dimensions
+ if (rot == ROTATE_RAND)
+ rot = (Rotation)myrand_range(ROTATE_0, ROTATE_270);
+
+ v3s16 s = (rot == ROTATE_90 || rot == ROTATE_270) ?
+ v3s16(size.Z, size.Y, size.X) : size;
+
+ //// Adjust placement position if necessary
+ if (flags & DECO_PLACE_CENTER_X)
+ p.X -= (s.X + 1) / 2;
+ if (flags & DECO_PLACE_CENTER_Y)
+ p.Y -= (s.Y + 1) / 2;
+ if (flags & DECO_PLACE_CENTER_Z)
+ p.Z -= (s.Z + 1) / 2;
+
+ blitToVManip(vm, p, rot, force_place);
+
+ return vm->m_area.contains(VoxelArea(p, p + s - v3s16(1,1,1)));
+}
+
+void Schematic::placeOnMap(Map *map, v3s16 p, u32 flags,
+ Rotation rot, bool force_place)
+{
+ std::map<v3s16, MapBlock *> lighting_modified_blocks;
+ std::map<v3s16, MapBlock *> modified_blocks;
+ std::map<v3s16, MapBlock *>::iterator it;
+ assert(map != NULL);
+ assert(schemdata != NULL);
+ sanity_check(m_ndef != NULL);
+
+ //// Determine effective rotation and effective schematic dimensions
if (rot == ROTATE_RAND)
rot = (Rotation)myrand_range(ROTATE_0, ROTATE_270);
v3s16 s = (rot == ROTATE_90 || rot == ROTATE_270) ?
- v3s16(size.Z, size.Y, size.X) : size;
+ v3s16(size.Z, size.Y, size.X) : size;
+ //// Adjust placement position if necessary
if (flags & DECO_PLACE_CENTER_X)
p.X -= (s.X + 1) / 2;
if (flags & DECO_PLACE_CENTER_Y)
@@ -196,25 +228,29 @@ void Schematic::placeStructure(Map *map, v3s16 p, u32 flags,
if (flags & DECO_PLACE_CENTER_Z)
p.Z -= (s.Z + 1) / 2;
+ //// Create VManip for effected area, emerge our area, modify area
+ //// inside VManip, then blit back.
v3s16 bp1 = getNodeBlockPos(p);
v3s16 bp2 = getNodeBlockPos(p + s - v3s16(1,1,1));
- vm->initialEmerge(bp1, bp2);
- blitToVManip(p, vm, rot, force_place);
+ MMVManip vm(map);
+ vm.initialEmerge(bp1, bp2);
- std::map<v3s16, MapBlock *> lighting_modified_blocks;
- std::map<v3s16, MapBlock *> modified_blocks;
- vm->blitBackAll(&modified_blocks);
+ blitToVManip(&vm, p, rot, force_place);
+
+ vm.blitBackAll(&modified_blocks);
+ //// Carry out post-map-modification actions
+
+ //// Update lighting
// TODO: Optimize this by using Mapgen::calcLighting() instead
lighting_modified_blocks.insert(modified_blocks.begin(), modified_blocks.end());
map->updateLighting(lighting_modified_blocks, modified_blocks);
+ //// Create & dispatch map modification events to observers
MapEditEvent event;
event.type = MEET_OTHER;
- for (std::map<v3s16, MapBlock *>::iterator
- it = modified_blocks.begin();
- it != modified_blocks.end(); ++it)
+ for (it = modified_blocks.begin(); it != modified_blocks.end(); ++it)
event.modified_blocks.insert(it->first);
map->dispatchEvent(&event);
@@ -231,7 +267,7 @@ bool Schematic::deserializeFromMts(std::istream *is,
//// Read signature
u32 signature = readU32(ss);
if (signature != MTSCHEM_FILE_SIGNATURE) {
- errorstream << "Schematic::deserializeFromMts: invalid schematic "
+ errorstream << __FUNCTION__ << ": invalid schematic "
"file" << std::endl;
return false;
}
@@ -239,7 +275,7 @@ bool Schematic::deserializeFromMts(std::istream *is,
//// Read version
u16 version = readU16(ss);
if (version > MTSCHEM_FILE_VER_HIGHEST_READ) {
- errorstream << "Schematic::deserializeFromMts: unsupported schematic "
+ errorstream << __FUNCTION__ << ": unsupported schematic "
"file version" << std::endl;
return false;
}
@@ -403,7 +439,7 @@ bool Schematic::loadSchematicFromFile(const std::string &filename,
{
std::ifstream is(filename.c_str(), std::ios_base::binary);
if (!is.good()) {
- errorstream << "Schematic::loadSchematicFile: unable to open file '"
+ errorstream << __FUNCTION__ << ": unable to open file '"
<< filename << "'" << std::endl;
return false;
}
@@ -412,17 +448,19 @@ bool Schematic::loadSchematicFromFile(const std::string &filename,
if (!deserializeFromMts(&is, &m_nodenames))
return false;
+ m_nnlistsizes.push_back(m_nodenames.size() - origsize);
+
+ name = filename;
+
if (replace_names) {
- for (size_t i = origsize; i != m_nodenames.size(); i++) {
- std::string &name = m_nodenames[i];
- StringMap::iterator it = replace_names->find(name);
+ for (size_t i = origsize; i < m_nodenames.size(); i++) {
+ std::string &node_name = m_nodenames[i];
+ StringMap::iterator it = replace_names->find(node_name);
if (it != replace_names->end())
- name = it->second;
+ node_name = it->second;
}
}
- m_nnlistsizes.push_back(m_nodenames.size() - origsize);
-
if (ndef)
ndef->pendNodeResolve(this);
diff --git a/src/mg_schematic.h b/src/mg_schematic.h
index 5c732648e..da8859540 100644
--- a/src/mg_schematic.h
+++ b/src/mg_schematic.h
@@ -106,8 +106,9 @@ public:
bool serializeToLua(std::ostream *os, const std::vector<std::string> &names,
bool use_comments, u32 indent_spaces);
- void blitToVManip(v3s16 p, MMVManip *vm, Rotation rot, bool force_place);
- void placeStructure(Map *map, v3s16 p, u32 flags, Rotation rot, bool force_place);
+ void blitToVManip(MMVManip *vm, v3s16 p, Rotation rot, bool force_place);
+ bool placeOnVManip(MMVManip *vm, v3s16 p, u32 flags, Rotation rot, bool force_place);
+ void placeOnMap(Map *map, v3s16 p, u32 flags, Rotation rot, bool force_place);
void applyProbabilities(v3s16 p0,
std::vector<std::pair<v3s16, u8> > *plist,
diff --git a/src/minimap.cpp b/src/minimap.cpp
index d1fb3867d..8cd0a7beb 100644
--- a/src/minimap.cpp
+++ b/src/minimap.cpp
@@ -18,16 +18,15 @@ with this program; if not, write to the Free Software Foundation, Inc.,
*/
#include "minimap.h"
-#include <math.h>
-#include "logoutputbuffer.h"
-#include "jthread/jmutexautolock.h"
-#include "jthread/jsemaphore.h"
+#include "threading/mutex_auto_lock.h"
+#include "threading/semaphore.h"
#include "clientmap.h"
#include "settings.h"
#include "nodedef.h"
#include "porting.h"
#include "util/numeric.h"
#include "util/string.h"
+#include <math.h>
////
@@ -52,7 +51,7 @@ MinimapUpdateThread::~MinimapUpdateThread()
bool MinimapUpdateThread::pushBlockUpdate(v3s16 pos, MinimapMapblock *data)
{
- JMutexAutoLock lock(m_queue_mutex);
+ MutexAutoLock lock(m_queue_mutex);
// Find if block is already in queue.
// If it is, update the data and quit.
@@ -78,7 +77,7 @@ bool MinimapUpdateThread::pushBlockUpdate(v3s16 pos, MinimapMapblock *data)
bool MinimapUpdateThread::popBlockUpdate(QueuedMinimapUpdate *update)
{
- JMutexAutoLock lock(m_queue_mutex);
+ MutexAutoLock lock(m_queue_mutex);
if (m_update_queue.empty())
return false;
@@ -212,11 +211,14 @@ void MinimapUpdateThread::getMap(v3s16 pos, s16 size, s16 height, bool is_radar)
Mapper::Mapper(IrrlichtDevice *device, Client *client)
{
+ this->client = client;
this->driver = device->getVideoDriver();
this->m_tsrc = client->getTextureSource();
this->m_shdrsrc = client->getShaderSource();
this->m_ndef = client->getNodeDefManager();
+ m_angle = 0.f;
+
// Initialize static settings
m_enable_shaders = g_settings->getBool("enable_shaders");
m_surface_mode_scan_height =
@@ -249,6 +251,8 @@ Mapper::Mapper(IrrlichtDevice *device, Client *client)
// Create player marker texture
data->player_marker = m_tsrc->getTexture("player_marker.png");
+ // Create object marker texture
+ data->object_marker_red = m_tsrc->getTexture("object_marker_red.png");
// Create mesh buffer for minimap
m_meshbuffer = getMinimapMeshBuffer();
@@ -256,13 +260,13 @@ Mapper::Mapper(IrrlichtDevice *device, Client *client)
// Initialize and start thread
m_minimap_update_thread = new MinimapUpdateThread();
m_minimap_update_thread->data = data;
- m_minimap_update_thread->Start();
+ m_minimap_update_thread->start();
}
Mapper::~Mapper()
{
- m_minimap_update_thread->Stop();
- m_minimap_update_thread->Wait();
+ m_minimap_update_thread->stop();
+ m_minimap_update_thread->wait();
m_meshbuffer->drop();
@@ -273,6 +277,7 @@ Mapper::~Mapper()
driver->removeTexture(data->heightmap_texture);
driver->removeTexture(data->minimap_overlay_round);
driver->removeTexture(data->minimap_overlay_square);
+ driver->removeTexture(data->object_marker_red);
delete data;
delete m_minimap_update_thread;
@@ -290,7 +295,7 @@ MinimapMode Mapper::getMinimapMode()
void Mapper::toggleMinimapShape()
{
- JMutexAutoLock lock(m_mutex);
+ MutexAutoLock lock(m_mutex);
data->minimap_shape_round = !data->minimap_shape_round;
g_settings->setBool("minimap_shape_round", data->minimap_shape_round);
@@ -312,7 +317,7 @@ void Mapper::setMinimapMode(MinimapMode mode)
if (mode >= MINIMAP_MODE_COUNT)
return;
- JMutexAutoLock lock(m_mutex);
+ MutexAutoLock lock(m_mutex);
data->is_radar = modedefs[mode].is_radar;
data->scan_height = modedefs[mode].scan_height;
@@ -327,7 +332,7 @@ void Mapper::setPos(v3s16 pos)
bool do_update = false;
{
- JMutexAutoLock lock(m_mutex);
+ MutexAutoLock lock(m_mutex);
if (pos != data->old_pos) {
data->old_pos = data->pos;
@@ -467,6 +472,7 @@ void Mapper::drawMinimap()
if (!minimap_texture)
return;
+ updateActiveMarkers();
v2u32 screensize = porting::getWindowSize();
const u32 size = 0.25 * screensize.Y;
@@ -526,6 +532,70 @@ void Mapper::drawMinimap()
driver->setTransform(video::ETS_VIEW, oldViewMat);
driver->setTransform(video::ETS_PROJECTION, oldProjMat);
driver->setViewPort(oldViewPort);
+
+ // Draw player markers
+ v2s32 s_pos(screensize.X - size - 10, 10);
+ 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 = sin(m_angle * core::DEGTORAD);
+ f32 cos_angle = cos(m_angle * core::DEGTORAD);
+ s32 marker_size2 = 0.025 * (float)size;
+ for (std::list<v2f>::const_iterator
+ i = m_active_markers.begin();
+ i != m_active_markers.end(); ++i) {
+ v2f posf = *i;
+ if (data->minimap_shape_round) {
+ f32 t1 = posf.X * cos_angle - posf.Y * sin_angle;
+ f32 t2 = posf.X * sin_angle + posf.Y * cos_angle;
+ posf.X = t1;
+ posf.Y = t2;
+ }
+ posf.X = (posf.X + 0.5) * (float)size;
+ posf.Y = (posf.Y + 0.5) * (float)size;
+ core::rect<s32> dest_rect(
+ s_pos.X + posf.X - marker_size2,
+ s_pos.Y + posf.Y - marker_size2,
+ s_pos.X + posf.X + marker_size2,
+ s_pos.Y + posf.Y + marker_size2);
+ driver->draw2DImage(data->object_marker_red, dest_rect,
+ img_rect, &dest_rect, &c[0], true);
+ }
+}
+
+void Mapper::updateActiveMarkers ()
+{
+ video::IImage *minimap_mask = data->minimap_shape_round ?
+ data->minimap_mask_round : data->minimap_mask_square;
+
+ std::list<Nametag *> *nametags = client->getCamera()->getNametags();
+
+ m_active_markers.clear();
+
+ for (std::list<Nametag *>::const_iterator
+ i = nametags->begin();
+ i != nametags->end(); ++i) {
+ Nametag *nametag = *i;
+ v3s16 pos = floatToInt(nametag->parent_node->getPosition() +
+ 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) {
+ continue;
+ }
+ pos.X = ((float)pos.X / data->map_size) * MINIMAP_MAX_SX;
+ pos.Z = ((float)pos.Z / data->map_size) * MINIMAP_MAX_SY;
+ video::SColor mask_col = minimap_mask->getPixel(pos.X, pos.Z);
+ if (!mask_col.getAlpha()) {
+ continue;
+ }
+ m_active_markers.push_back(v2f(((float)pos.X / (float)MINIMAP_MAX_SX) - 0.5,
+ (1.0 - (float)pos.Z / (float)MINIMAP_MAX_SY) - 0.5));
+ }
}
////
diff --git a/src/minimap.h b/src/minimap.h
index 628be7489..743b2bff2 100644
--- a/src/minimap.h
+++ b/src/minimap.h
@@ -20,18 +20,20 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#ifndef MINIMAP_HEADER
#define MINIMAP_HEADER
-#include <map>
-#include <string>
-#include <vector>
#include "irrlichttypes_extrabloated.h"
#include "client.h"
#include "voxel.h"
-#include "jthread/jmutex.h"
-#include "jthread/jsemaphore.h"
+#include "threading/mutex.h"
+#include "threading/semaphore.h"
+#include <map>
+#include <string>
+#include <vector>
+#include "camera.h"
#define MINIMAP_MAX_SX 512
#define MINIMAP_MAX_SY 512
+
enum MinimapMode {
MINIMAP_MODE_OFF,
MINIMAP_MODE_SURFACEx1,
@@ -81,6 +83,7 @@ struct MinimapData {
video::ITexture *minimap_overlay_round;
video::ITexture *minimap_overlay_square;
video::ITexture *player_marker;
+ video::ITexture *object_marker_red;
};
struct QueuedMinimapUpdate {
@@ -90,6 +93,7 @@ struct QueuedMinimapUpdate {
class MinimapUpdateThread : public UpdateThread {
public:
+ MinimapUpdateThread() : UpdateThread("Minimap") {}
virtual ~MinimapUpdateThread();
void getMap(v3s16 pos, s16 size, s16 height, bool radar);
@@ -105,11 +109,10 @@ public:
MinimapData *data;
protected:
- const char *getName() { return "MinimapUpdateThread"; }
virtual void doUpdate();
private:
- JMutex m_queue_mutex;
+ Mutex m_queue_mutex;
std::deque<QueuedMinimapUpdate> m_update_queue;
std::map<v3s16, MinimapMapblock *> m_blocks_cache;
};
@@ -137,9 +140,12 @@ public:
video::IImage *heightmap_image);
scene::SMeshBuffer *getMinimapMeshBuffer();
+
+ void updateActiveMarkers();
void drawMinimap();
video::IVideoDriver *driver;
+ Client* client;
MinimapData *data;
private:
@@ -151,7 +157,8 @@ private:
bool m_enable_shaders;
u16 m_surface_mode_scan_height;
f32 m_angle;
- JMutex m_mutex;
+ Mutex m_mutex;
+ std::list<v2f> m_active_markers;
};
#endif
diff --git a/src/modalMenu.h b/src/modalMenu.h
index 72ecedcb9..43bb8e1b8 100644
--- a/src/modalMenu.h
+++ b/src/modalMenu.h
@@ -31,8 +31,8 @@ class IMenuManager
{
public:
// A GUIModalMenu calls these when this class is passed as a parameter
- virtual void createdMenu(GUIModalMenu *menu) = 0;
- virtual void deletingMenu(GUIModalMenu *menu) = 0;
+ virtual void createdMenu(gui::IGUIElement *menu) = 0;
+ virtual void deletingMenu(gui::IGUIElement *menu) = 0;
};
/*
@@ -108,7 +108,7 @@ public:
this->remove();
#ifdef HAVE_TOUCHSCREENGUI
if (g_touchscreengui)
- g_touchscreengui->Show();
+ g_touchscreengui->show();
#endif
}
diff --git a/src/mods.cpp b/src/mods.cpp
index a81dd4604..1b1bdb07b 100644
--- a/src/mods.cpp
+++ b/src/mods.cpp
@@ -21,12 +21,13 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <fstream>
#include "mods.h"
#include "filesys.h"
-#include "strfnd.h"
+#include "util/strfnd.h"
#include "log.h"
#include "subgame.h"
#include "settings.h"
-#include "strfnd.h"
+#include "util/strfnd.h"
#include "convert_json.h"
+#include "exceptions.h"
static bool parseDependsLine(std::istream &is,
std::string &dep, std::set<char> &symbols)
@@ -256,7 +257,7 @@ void ModConfiguration::addMods(std::vector<ModSpec> new_mods)
// BAD CASE: name conflict in different levels.
u32 oldindex = existing_mods[mod.name];
const ModSpec &oldmod = m_unsatisfied_mods[oldindex];
- actionstream<<"WARNING: Mod name conflict detected: \""
+ warningstream<<"Mod name conflict detected: \""
<<mod.name<<"\""<<std::endl
<<"Will not load: "<<oldmod.path<<std::endl
<<"Overridden by: "<<mod.path<<std::endl;
@@ -270,7 +271,7 @@ void ModConfiguration::addMods(std::vector<ModSpec> new_mods)
// VERY BAD CASE: name conflict in the same level.
u32 oldindex = existing_mods[mod.name];
const ModSpec &oldmod = m_unsatisfied_mods[oldindex];
- errorstream<<"WARNING: Mod name conflict detected: \""
+ warningstream<<"Mod name conflict detected: \""
<<mod.name<<"\""<<std::endl
<<"Will not load: "<<oldmod.path<<std::endl
<<"Will not load: "<<mod.path<<std::endl;
diff --git a/src/mods.h b/src/mods.h
index f35bd18db..12576516d 100644
--- a/src/mods.h
+++ b/src/mods.h
@@ -26,29 +26,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <vector>
#include <string>
#include <map>
-#include <exception>
#include "json/json.h"
#include "config.h"
#define MODNAME_ALLOWED_CHARS "abcdefghijklmnopqrstuvwxyz0123456789_"
-class ModError : public std::exception
-{
-public:
- ModError(const std::string &s)
- {
- m_s = "ModError: ";
- m_s += s;
- }
- virtual ~ModError() throw()
- {}
- virtual const char * what() const throw()
- {
- return m_s.c_str();
- }
- std::string m_s;
-};
-
struct ModSpec
{
std::string name;
diff --git a/src/nameidmapping.cpp b/src/nameidmapping.cpp
index ebe65076e..ed59ddd16 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(std::map<u16, std::string>::const_iterator
i = m_id_to_name.begin();
- i != m_id_to_name.end(); i++){
+ i != m_id_to_name.end(); ++i){
writeU16(os, i->first);
os<<serializeString(i->second);
}
diff --git a/src/network/clientpackethandler.cpp b/src/network/clientpackethandler.cpp
index 86091bc88..0498f4048 100644
--- a/src/network/clientpackethandler.cpp
+++ b/src/network/clientpackethandler.cpp
@@ -24,10 +24,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "log.h"
#include "map.h"
#include "mapsector.h"
+#include "minimap.h"
#include "nodedef.h"
#include "serialization.h"
#include "server.h"
-#include "strfnd.h"
+#include "util/strfnd.h"
#include "network/clientopcodes.h"
#include "util/serialize.h"
#include "util/srp.h"
@@ -551,6 +552,7 @@ void Client::handleCommand_MovePlayer(NetworkPacket* pkt)
*pkt >> pos >> pitch >> yaw;
+ player->got_teleported = true;
player->setPosition(pos);
infostream << "Client got TOCLIENT_MOVE_PLAYER"
@@ -578,7 +580,7 @@ void Client::handleCommand_MovePlayer(NetworkPacket* pkt)
void Client::handleCommand_PlayerItem(NetworkPacket* pkt)
{
- infostream << "Client: WARNING: Ignoring TOCLIENT_PLAYERITEM" << std::endl;
+ warningstream << "Client: Ignoring TOCLIENT_PLAYERITEM" << std::endl;
}
void Client::handleCommand_DeathScreen(NetworkPacket* pkt)
@@ -621,7 +623,7 @@ void Client::handleCommand_AnnounceMedia(NetworkPacket* pkt)
// Mesh update thread must be stopped while
// updating content definitions
- sanity_check(!m_mesh_update_thread.IsRunning());
+ sanity_check(!m_mesh_update_thread.isRunning());
for (u16 i = 0; i < num_files; i++) {
std::string name, sha1_base64;
@@ -639,7 +641,7 @@ void Client::handleCommand_AnnounceMedia(NetworkPacket* pkt)
*pkt >> str;
Strfnd sf(str);
- while(!sf.atend()) {
+ while(!sf.at_end()) {
std::string baseurl = trim(sf.next(","));
if (baseurl != "")
m_media_downloader->addRemoteServer(baseurl);
@@ -694,7 +696,7 @@ void Client::handleCommand_Media(NetworkPacket* pkt)
// Mesh update thread must be stopped while
// updating content definitions
- sanity_check(!m_mesh_update_thread.IsRunning());
+ sanity_check(!m_mesh_update_thread.isRunning());
for (u32 i=0; i < num_files; i++) {
std::string name;
@@ -710,7 +712,7 @@ void Client::handleCommand_Media(NetworkPacket* pkt)
void Client::handleCommand_ToolDef(NetworkPacket* pkt)
{
- infostream << "Client: WARNING: Ignoring TOCLIENT_TOOLDEF" << std::endl;
+ warningstream << "Client: Ignoring TOCLIENT_TOOLDEF" << std::endl;
}
void Client::handleCommand_NodeDef(NetworkPacket* pkt)
@@ -720,7 +722,7 @@ void Client::handleCommand_NodeDef(NetworkPacket* pkt)
// Mesh update thread must be stopped while
// updating content definitions
- sanity_check(!m_mesh_update_thread.IsRunning());
+ sanity_check(!m_mesh_update_thread.isRunning());
// Decompress node definitions
std::string datastring(pkt->getString(0), pkt->getSize());
@@ -737,7 +739,7 @@ void Client::handleCommand_NodeDef(NetworkPacket* pkt)
void Client::handleCommand_CraftItemDef(NetworkPacket* pkt)
{
- infostream << "Client: WARNING: Ignoring TOCLIENT_CRAFTITEMDEF" << std::endl;
+ warningstream << "Client: Ignoring TOCLIENT_CRAFTITEMDEF" << std::endl;
}
void Client::handleCommand_ItemDef(NetworkPacket* pkt)
@@ -747,7 +749,7 @@ void Client::handleCommand_ItemDef(NetworkPacket* pkt)
// Mesh update thread must be stopped while
// updating content definitions
- sanity_check(!m_mesh_update_thread.IsRunning());
+ sanity_check(!m_mesh_update_thread.isRunning());
// Decompress item definitions
std::string datastring(pkt->getString(0), pkt->getSize());
@@ -1094,8 +1096,19 @@ void Client::handleCommand_HudSetFlags(NetworkPacket* pkt)
Player *player = m_env.getLocalPlayer();
assert(player != NULL);
+ bool was_minimap_visible = player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE;
+
player->hud_flags &= ~mask;
player->hud_flags |= flags;
+
+ m_minimap_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE);
+
+ // Hide minimap if it has been disabled by the server
+ if (m_minimap_disabled_by_server && was_minimap_visible) {
+ // defers a minimap update, therefore only call it if really
+ // needed, by checking that minimap was visible before
+ m_mapper->setMinimapMode(MINIMAP_MODE_OFF);
+ }
}
void Client::handleCommand_HudSetParam(NetworkPacket* pkt)
diff --git a/src/network/connection.cpp b/src/network/connection.cpp
index 7794ce10f..f7452d8e4 100644
--- a/src/network/connection.cpp
+++ b/src/network/connection.cpp
@@ -42,10 +42,10 @@ namespace con
#undef DEBUG_CONNECTION_KBPS
#else
/* this mutex is used to achieve log message consistency */
-JMutex log_message_mutex;
+Mutex log_message_mutex;
#define LOG(a) \
{ \
- JMutexAutoLock loglock(log_message_mutex); \
+ MutexAutoLock loglock(log_message_mutex); \
a; \
}
#define PROFILE(a) a
@@ -209,7 +209,7 @@ ReliablePacketBuffer::ReliablePacketBuffer(): m_list_size(0) {}
void ReliablePacketBuffer::print()
{
- JMutexAutoLock listlock(m_list_mutex);
+ MutexAutoLock listlock(m_list_mutex);
LOG(dout_con<<"Dump of ReliablePacketBuffer:" << std::endl);
unsigned int index = 0;
for(std::list<BufferedPacket>::iterator i = m_list.begin();
@@ -223,7 +223,7 @@ void ReliablePacketBuffer::print()
}
bool ReliablePacketBuffer::empty()
{
- JMutexAutoLock listlock(m_list_mutex);
+ MutexAutoLock listlock(m_list_mutex);
return m_list.empty();
}
@@ -256,7 +256,7 @@ RPBSearchResult ReliablePacketBuffer::notFound()
}
bool ReliablePacketBuffer::getFirstSeqnum(u16& result)
{
- JMutexAutoLock listlock(m_list_mutex);
+ MutexAutoLock listlock(m_list_mutex);
if (m_list.empty())
return false;
BufferedPacket p = *m_list.begin();
@@ -266,7 +266,7 @@ bool ReliablePacketBuffer::getFirstSeqnum(u16& result)
BufferedPacket ReliablePacketBuffer::popFirst()
{
- JMutexAutoLock listlock(m_list_mutex);
+ MutexAutoLock listlock(m_list_mutex);
if (m_list.empty())
throw NotFoundException("Buffer is empty");
BufferedPacket p = *m_list.begin();
@@ -283,7 +283,7 @@ BufferedPacket ReliablePacketBuffer::popFirst()
}
BufferedPacket ReliablePacketBuffer::popSeqnum(u16 seqnum)
{
- JMutexAutoLock listlock(m_list_mutex);
+ MutexAutoLock listlock(m_list_mutex);
RPBSearchResult r = findPacket(seqnum);
if (r == notFound()) {
LOG(dout_con<<"Sequence number: " << seqnum
@@ -294,7 +294,7 @@ BufferedPacket ReliablePacketBuffer::popSeqnum(u16 seqnum)
RPBSearchResult next = r;
- next++;
+ ++next;
if (next != notFound()) {
u16 s = readU16(&(next->data[BASE_HEADER_SIZE+1]));
m_oldest_non_answered_ack = s;
@@ -311,7 +311,7 @@ BufferedPacket ReliablePacketBuffer::popSeqnum(u16 seqnum)
}
void ReliablePacketBuffer::insert(BufferedPacket &p,u16 next_expected)
{
- JMutexAutoLock listlock(m_list_mutex);
+ MutexAutoLock listlock(m_list_mutex);
if (p.data.getSize() < BASE_HEADER_SIZE + 3) {
errorstream << "ReliablePacketBuffer::insert(): Invalid data size for "
"reliable packet" << std::endl;
@@ -358,7 +358,7 @@ void ReliablePacketBuffer::insert(BufferedPacket &p,u16 next_expected)
/* this is true e.g. on wrap around */
if (seqnum < next_expected) {
while(((s < seqnum) || (s >= next_expected)) && (i != m_list.end())) {
- i++;
+ ++i;
if (i != m_list.end())
s = readU16(&(i->data[BASE_HEADER_SIZE+1]));
}
@@ -367,7 +367,7 @@ void ReliablePacketBuffer::insert(BufferedPacket &p,u16 next_expected)
else
{
while(((s < seqnum) && (s >= next_expected)) && (i != m_list.end())) {
- i++;
+ ++i;
if (i != m_list.end())
s = readU16(&(i->data[BASE_HEADER_SIZE+1]));
}
@@ -411,7 +411,7 @@ void ReliablePacketBuffer::insert(BufferedPacket &p,u16 next_expected)
void ReliablePacketBuffer::incrementTimeouts(float dtime)
{
- JMutexAutoLock listlock(m_list_mutex);
+ MutexAutoLock listlock(m_list_mutex);
for(std::list<BufferedPacket>::iterator i = m_list.begin();
i != m_list.end(); ++i)
{
@@ -423,7 +423,7 @@ void ReliablePacketBuffer::incrementTimeouts(float dtime)
std::list<BufferedPacket> ReliablePacketBuffer::getTimedOuts(float timeout,
unsigned int max_packets)
{
- JMutexAutoLock listlock(m_list_mutex);
+ MutexAutoLock listlock(m_list_mutex);
std::list<BufferedPacket> timed_outs;
for(std::list<BufferedPacket>::iterator i = m_list.begin();
i != m_list.end(); ++i)
@@ -446,7 +446,7 @@ std::list<BufferedPacket> ReliablePacketBuffer::getTimedOuts(float timeout,
IncomingSplitBuffer::~IncomingSplitBuffer()
{
- JMutexAutoLock listlock(m_map_mutex);
+ MutexAutoLock listlock(m_map_mutex);
for(std::map<u16, IncomingSplitPacket*>::iterator i = m_buf.begin();
i != m_buf.end(); ++i)
{
@@ -459,7 +459,7 @@ IncomingSplitBuffer::~IncomingSplitBuffer()
*/
SharedBuffer<u8> IncomingSplitBuffer::insert(BufferedPacket &p, bool reliable)
{
- JMutexAutoLock listlock(m_map_mutex);
+ MutexAutoLock listlock(m_map_mutex);
u32 headersize = BASE_HEADER_SIZE + 7;
if (p.data.getSize() < headersize) {
errorstream << "Invalid data size for split packet" << std::endl;
@@ -546,7 +546,7 @@ void IncomingSplitBuffer::removeUnreliableTimedOuts(float dtime, float timeout)
{
std::list<u16> remove_queue;
{
- JMutexAutoLock listlock(m_map_mutex);
+ MutexAutoLock listlock(m_map_mutex);
for(std::map<u16, IncomingSplitPacket*>::iterator i = m_buf.begin();
i != m_buf.end(); ++i)
{
@@ -562,7 +562,7 @@ void IncomingSplitBuffer::removeUnreliableTimedOuts(float dtime, float timeout)
for(std::list<u16>::iterator j = remove_queue.begin();
j != remove_queue.end(); ++j)
{
- JMutexAutoLock listlock(m_map_mutex);
+ MutexAutoLock listlock(m_map_mutex);
LOG(dout_con<<"NOTE: Removing timed out unreliable split packet"<<std::endl);
delete m_buf[*j];
m_buf.erase(*j);
@@ -605,13 +605,13 @@ Channel::~Channel()
u16 Channel::readNextIncomingSeqNum()
{
- JMutexAutoLock internal(m_internal_mutex);
+ MutexAutoLock internal(m_internal_mutex);
return next_incoming_seqnum;
}
u16 Channel::incNextIncomingSeqNum()
{
- JMutexAutoLock internal(m_internal_mutex);
+ MutexAutoLock internal(m_internal_mutex);
u16 retval = next_incoming_seqnum;
next_incoming_seqnum++;
return retval;
@@ -619,18 +619,18 @@ u16 Channel::incNextIncomingSeqNum()
u16 Channel::readNextSplitSeqNum()
{
- JMutexAutoLock internal(m_internal_mutex);
+ MutexAutoLock internal(m_internal_mutex);
return next_outgoing_split_seqnum;
}
void Channel::setNextSplitSeqNum(u16 seqnum)
{
- JMutexAutoLock internal(m_internal_mutex);
+ MutexAutoLock internal(m_internal_mutex);
next_outgoing_split_seqnum = seqnum;
}
u16 Channel::getOutgoingSequenceNumber(bool& successful)
{
- JMutexAutoLock internal(m_internal_mutex);
+ MutexAutoLock internal(m_internal_mutex);
u16 retval = next_outgoing_seqnum;
u16 lowest_unacked_seqnumber;
@@ -670,7 +670,7 @@ u16 Channel::getOutgoingSequenceNumber(bool& successful)
u16 Channel::readOutgoingSequenceNumber()
{
- JMutexAutoLock internal(m_internal_mutex);
+ MutexAutoLock internal(m_internal_mutex);
return next_outgoing_seqnum;
}
@@ -686,32 +686,32 @@ bool Channel::putBackSequenceNumber(u16 seqnum)
void Channel::UpdateBytesSent(unsigned int bytes, unsigned int packets)
{
- JMutexAutoLock internal(m_internal_mutex);
+ MutexAutoLock internal(m_internal_mutex);
current_bytes_transfered += bytes;
current_packet_successfull += packets;
}
void Channel::UpdateBytesReceived(unsigned int bytes) {
- JMutexAutoLock internal(m_internal_mutex);
+ MutexAutoLock internal(m_internal_mutex);
current_bytes_received += bytes;
}
void Channel::UpdateBytesLost(unsigned int bytes)
{
- JMutexAutoLock internal(m_internal_mutex);
+ MutexAutoLock internal(m_internal_mutex);
current_bytes_lost += bytes;
}
void Channel::UpdatePacketLossCounter(unsigned int count)
{
- JMutexAutoLock internal(m_internal_mutex);
+ MutexAutoLock internal(m_internal_mutex);
current_packet_loss += count;
}
void Channel::UpdatePacketTooLateCounter()
{
- JMutexAutoLock internal(m_internal_mutex);
+ MutexAutoLock internal(m_internal_mutex);
current_packet_too_late++;
}
@@ -731,7 +731,7 @@ void Channel::UpdateTimers(float dtime,bool legacy_peer)
bool reasonable_amount_of_data_transmitted = false;
{
- JMutexAutoLock internal(m_internal_mutex);
+ MutexAutoLock internal(m_internal_mutex);
packet_loss = current_packet_loss;
//packet_too_late = current_packet_too_late;
packets_successfull = current_packet_successfull;
@@ -802,7 +802,7 @@ void Channel::UpdateTimers(float dtime,bool legacy_peer)
if (bpm_counter > 10.0)
{
{
- JMutexAutoLock internal(m_internal_mutex);
+ MutexAutoLock internal(m_internal_mutex);
cur_kbps =
(((float) current_bytes_transfered)/bpm_counter)/1024.0;
current_bytes_transfered = 0;
@@ -903,7 +903,7 @@ bool PeerHelper::operator!=(void* ptr)
bool Peer::IncUseCount()
{
- JMutexAutoLock lock(m_exclusive_access_mutex);
+ MutexAutoLock lock(m_exclusive_access_mutex);
if (!m_pending_deletion)
{
@@ -917,7 +917,7 @@ bool Peer::IncUseCount()
void Peer::DecUseCount()
{
{
- JMutexAutoLock lock(m_exclusive_access_mutex);
+ MutexAutoLock lock(m_exclusive_access_mutex);
sanity_check(m_usage > 0);
m_usage--;
@@ -978,7 +978,7 @@ void Peer::RTTStatistics(float rtt, std::string profiler_id,
bool Peer::isTimedOut(float timeout)
{
- JMutexAutoLock lock(m_exclusive_access_mutex);
+ MutexAutoLock lock(m_exclusive_access_mutex);
u32 current_time = porting::getTimeMs();
float dtime = CALC_DTIME(m_last_timeout_check,current_time);
@@ -992,7 +992,7 @@ bool Peer::isTimedOut(float timeout)
void Peer::Drop()
{
{
- JMutexAutoLock usage_lock(m_exclusive_access_mutex);
+ MutexAutoLock usage_lock(m_exclusive_access_mutex);
m_pending_deletion = true;
if (m_usage != 0)
return;
@@ -1051,7 +1051,7 @@ void UDPPeer::reportRTT(float rtt)
if (timeout > RESEND_TIMEOUT_MAX)
timeout = RESEND_TIMEOUT_MAX;
- JMutexAutoLock usage_lock(m_exclusive_access_mutex);
+ MutexAutoLock usage_lock(m_exclusive_access_mutex);
resend_timeout = timeout;
}
@@ -1255,8 +1255,9 @@ SharedBuffer<u8> UDPPeer::addSpiltPacket(u8 channel,
/* Connection Threads */
/******************************************************************************/
-ConnectionSendThread::ConnectionSendThread( unsigned int max_packet_size,
- float timeout) :
+ConnectionSendThread::ConnectionSendThread(unsigned int max_packet_size,
+ float timeout) :
+ Thread("ConnectionSend"),
m_connection(NULL),
m_max_packet_size(max_packet_size),
m_timeout(timeout),
@@ -1266,11 +1267,9 @@ ConnectionSendThread::ConnectionSendThread( unsigned int max_packet_size,
{
}
-void * ConnectionSendThread::Thread()
+void * ConnectionSendThread::run()
{
- assert(m_connection != NULL);
- ThreadStarted();
- log_register_thread("ConnectionSend");
+ assert(m_connection);
LOG(dout_con<<m_connection->getDesc()
<<"ConnectionSend thread started"<<std::endl);
@@ -1281,21 +1280,19 @@ void * ConnectionSendThread::Thread()
PROFILE(std::stringstream ThreadIdentifier);
PROFILE(ThreadIdentifier << "ConnectionSend: [" << m_connection->getDesc() << "]");
- porting::setThreadName("ConnectionSend");
-
/* if stop is requested don't stop immediately but try to send all */
/* packets first */
- while(!StopRequested() || packetsQueued()) {
+ while(!stopRequested() || packetsQueued()) {
BEGIN_DEBUG_EXCEPTION_HANDLER
PROFILE(ScopeProfiler sp(g_profiler, ThreadIdentifier.str(), SPT_AVG));
m_iteration_packets_avaialble = m_max_data_packets_per_iteration;
/* wait for trigger or timeout */
- m_send_sleep_semaphore.Wait(50);
+ m_send_sleep_semaphore.wait(50);
/* remove all triggers */
- while(m_send_sleep_semaphore.Wait(0)) {}
+ while(m_send_sleep_semaphore.wait(0)) {}
lasttime = curtime;
curtime = porting::getTimeMs();
@@ -1319,7 +1316,7 @@ void * ConnectionSendThread::Thread()
/* send non reliable packets */
sendPackets(dtime);
- END_DEBUG_EXCEPTION_HANDLER(errorstream);
+ END_DEBUG_EXCEPTION_HANDLER
}
PROFILE(g_profiler->remove(ThreadIdentifier.str()));
@@ -1328,7 +1325,7 @@ void * ConnectionSendThread::Thread()
void ConnectionSendThread::Trigger()
{
- m_send_sleep_semaphore.Post();
+ m_send_sleep_semaphore.post();
}
bool ConnectionSendThread::packetsQueued()
@@ -1763,7 +1760,7 @@ void ConnectionSendThread::disconnect()
for (std::list<u16>::iterator i = peerids.begin();
i != peerids.end();
- i++)
+ ++i)
{
sendAsPacket(*i, 0,data,false);
}
@@ -1843,7 +1840,7 @@ void ConnectionSendThread::sendToAll(u8 channelnum, SharedBuffer<u8> data)
for (std::list<u16>::iterator i = peerids.begin();
i != peerids.end();
- i++)
+ ++i)
{
send(*i, channelnum, data);
}
@@ -1855,7 +1852,7 @@ void ConnectionSendThread::sendToAllReliable(ConnectionCommand &c)
for (std::list<u16>::iterator i = peerids.begin();
i != peerids.end();
- i++)
+ ++i)
{
PeerHelper peer = m_connection->getPeerNoEx(*i);
@@ -1984,7 +1981,7 @@ void ConnectionSendThread::sendPackets(float dtime)
}
else if (
( peer->m_increment_packets_remaining > 0) ||
- (StopRequested())) {
+ (stopRequested())) {
rawSendAsPacket(packet.peer_id, packet.channelnum,
packet.data, packet.reliable);
peer->m_increment_packets_remaining--;
@@ -2014,15 +2011,14 @@ void ConnectionSendThread::sendAsPacket(u16 peer_id, u8 channelnum,
}
ConnectionReceiveThread::ConnectionReceiveThread(unsigned int max_packet_size) :
+ Thread("ConnectionReceive"),
m_connection(NULL)
{
}
-void * ConnectionReceiveThread::Thread()
+void * ConnectionReceiveThread::run()
{
- assert(m_connection != NULL);
- ThreadStarted();
- log_register_thread("ConnectionReceive");
+ assert(m_connection);
LOG(dout_con<<m_connection->getDesc()
<<"ConnectionReceive thread started"<<std::endl);
@@ -2030,15 +2026,13 @@ void * ConnectionReceiveThread::Thread()
PROFILE(std::stringstream ThreadIdentifier);
PROFILE(ThreadIdentifier << "ConnectionReceive: [" << m_connection->getDesc() << "]");
- porting::setThreadName("ConnectionReceive");
-
#ifdef DEBUG_CONNECTION_KBPS
u32 curtime = porting::getTimeMs();
u32 lasttime = curtime;
float debug_print_timer = 0.0;
#endif
- while(!StopRequested()) {
+ while(!stopRequested()) {
BEGIN_DEBUG_EXCEPTION_HANDLER
PROFILE(ScopeProfiler sp(g_profiler, ThreadIdentifier.str(), SPT_AVG));
@@ -2103,8 +2097,9 @@ void * ConnectionReceiveThread::Thread()
}
}
#endif
- END_DEBUG_EXCEPTION_HANDLER(errorstream);
+ END_DEBUG_EXCEPTION_HANDLER
}
+
PROFILE(g_profiler->remove(ThreadIdentifier.str()));
return NULL;
}
@@ -2684,8 +2679,8 @@ Connection::Connection(u32 protocol_id, u32 max_packet_size, float timeout,
m_sendThread.setParent(this);
m_receiveThread.setParent(this);
- m_sendThread.Start();
- m_receiveThread.Start();
+ m_sendThread.start();
+ m_receiveThread.start();
}
@@ -2694,8 +2689,8 @@ Connection::~Connection()
{
m_shutting_down = true;
// request threads to stop
- m_sendThread.Stop();
- m_receiveThread.Stop();
+ m_sendThread.stop();
+ m_receiveThread.stop();
//TODO for some unkonwn reason send/receive threads do not exit as they're
// supposed to be but wait on peer timeout. To speed up shutdown we reduce
@@ -2703,8 +2698,8 @@ Connection::~Connection()
m_sendThread.setPeerTimeout(0.5);
// wait for threads to finish
- m_sendThread.Wait();
- m_receiveThread.Wait();
+ m_sendThread.wait();
+ m_receiveThread.wait();
// Delete peers
for(std::map<u16, Peer*>::iterator
@@ -2724,7 +2719,7 @@ void Connection::putEvent(ConnectionEvent &e)
PeerHelper Connection::getPeer(u16 peer_id)
{
- JMutexAutoLock peerlock(m_peers_mutex);
+ MutexAutoLock peerlock(m_peers_mutex);
std::map<u16, Peer*>::iterator node = m_peers.find(peer_id);
if (node == m_peers.end()) {
@@ -2739,7 +2734,7 @@ PeerHelper Connection::getPeer(u16 peer_id)
PeerHelper Connection::getPeerNoEx(u16 peer_id)
{
- JMutexAutoLock peerlock(m_peers_mutex);
+ MutexAutoLock peerlock(m_peers_mutex);
std::map<u16, Peer*>::iterator node = m_peers.find(peer_id);
if (node == m_peers.end()) {
@@ -2755,7 +2750,7 @@ PeerHelper Connection::getPeerNoEx(u16 peer_id)
/* find peer_id for address */
u16 Connection::lookupPeer(Address& sender)
{
- JMutexAutoLock peerlock(m_peers_mutex);
+ MutexAutoLock peerlock(m_peers_mutex);
std::map<u16, Peer*>::iterator j;
j = m_peers.begin();
for(; j != m_peers.end(); ++j)
@@ -2794,7 +2789,7 @@ bool Connection::deletePeer(u16 peer_id, bool timeout)
/* lock list as short as possible */
{
- JMutexAutoLock peerlock(m_peers_mutex);
+ MutexAutoLock peerlock(m_peers_mutex);
if (m_peers.find(peer_id) == m_peers.end())
return false;
peer = m_peers[peer_id];
@@ -2852,7 +2847,7 @@ void Connection::Connect(Address address)
bool Connection::Connected()
{
- JMutexAutoLock peerlock(m_peers_mutex);
+ MutexAutoLock peerlock(m_peers_mutex);
if (m_peers.size() != 1)
return false;
@@ -2987,7 +2982,7 @@ u16 Connection::createPeer(Address& sender, MTProtocols protocol, int fd)
/*
Find an unused peer id
*/
- JMutexAutoLock lock(m_peers_mutex);
+ MutexAutoLock lock(m_peers_mutex);
bool out_of_ids = false;
for(;;) {
// Check if exists
@@ -3038,9 +3033,9 @@ u16 Connection::createPeer(Address& sender, MTProtocols protocol, int fd)
void Connection::PrintInfo(std::ostream &out)
{
- m_info_mutex.Lock();
+ m_info_mutex.lock();
out<<getDesc()<<": ";
- m_info_mutex.Unlock();
+ m_info_mutex.unlock();
}
void Connection::PrintInfo()
@@ -3091,7 +3086,7 @@ UDPPeer* Connection::createServerPeer(Address& address)
UDPPeer *peer = new UDPPeer(PEER_ID_SERVER, address, this);
{
- JMutexAutoLock lock(m_peers_mutex);
+ MutexAutoLock lock(m_peers_mutex);
m_peers[peer->id] = peer;
m_peer_ids.push_back(peer->id);
}
diff --git a/src/network/connection.h b/src/network/connection.h
index 15ea7e20f..fe2c9819d 100644
--- a/src/network/connection.h
+++ b/src/network/connection.h
@@ -349,7 +349,7 @@ private:
u16 m_oldest_non_answered_ack;
- JMutex m_list_mutex;
+ Mutex m_list_mutex;
};
/*
@@ -372,7 +372,7 @@ private:
// Key is seqnum
std::map<u16, IncomingSplitPacket*> m_buf;
- JMutex m_map_mutex;
+ Mutex m_map_mutex;
};
struct OutgoingPacket
@@ -519,32 +519,32 @@ public:
void UpdateTimers(float dtime, bool legacy_peer);
const float getCurrentDownloadRateKB()
- { JMutexAutoLock lock(m_internal_mutex); return cur_kbps; };
+ { MutexAutoLock lock(m_internal_mutex); return cur_kbps; };
const float getMaxDownloadRateKB()
- { JMutexAutoLock lock(m_internal_mutex); return max_kbps; };
+ { MutexAutoLock lock(m_internal_mutex); return max_kbps; };
const float getCurrentLossRateKB()
- { JMutexAutoLock lock(m_internal_mutex); return cur_kbps_lost; };
+ { MutexAutoLock lock(m_internal_mutex); return cur_kbps_lost; };
const float getMaxLossRateKB()
- { JMutexAutoLock lock(m_internal_mutex); return max_kbps_lost; };
+ { MutexAutoLock lock(m_internal_mutex); return max_kbps_lost; };
const float getCurrentIncomingRateKB()
- { JMutexAutoLock lock(m_internal_mutex); return cur_incoming_kbps; };
+ { MutexAutoLock lock(m_internal_mutex); return cur_incoming_kbps; };
const float getMaxIncomingRateKB()
- { JMutexAutoLock lock(m_internal_mutex); return max_incoming_kbps; };
+ { MutexAutoLock lock(m_internal_mutex); return max_incoming_kbps; };
const float getAvgDownloadRateKB()
- { JMutexAutoLock lock(m_internal_mutex); return avg_kbps; };
+ { MutexAutoLock lock(m_internal_mutex); return avg_kbps; };
const float getAvgLossRateKB()
- { JMutexAutoLock lock(m_internal_mutex); return avg_kbps_lost; };
+ { MutexAutoLock lock(m_internal_mutex); return avg_kbps_lost; };
const float getAvgIncomingRateKB()
- { JMutexAutoLock lock(m_internal_mutex); return avg_incoming_kbps; };
+ { MutexAutoLock lock(m_internal_mutex); return avg_incoming_kbps; };
const unsigned int getWindowSize() const { return window_size; };
void setWindowSize(unsigned int size) { window_size = size; };
private:
- JMutex m_internal_mutex;
+ Mutex m_internal_mutex;
int window_size;
u16 next_incoming_seqnum;
@@ -675,7 +675,7 @@ class Peer {
};
virtual ~Peer() {
- JMutexAutoLock usage_lock(m_exclusive_access_mutex);
+ MutexAutoLock usage_lock(m_exclusive_access_mutex);
FATAL_ERROR_IF(m_usage != 0, "Reference counting failure");
};
@@ -692,15 +692,15 @@ class Peer {
virtual bool getAddress(MTProtocols type, Address& toset) = 0;
void ResetTimeout()
- {JMutexAutoLock lock(m_exclusive_access_mutex); m_timeout_counter=0.0; };
+ {MutexAutoLock lock(m_exclusive_access_mutex); m_timeout_counter=0.0; };
bool isTimedOut(float timeout);
void setSentWithID()
- { JMutexAutoLock lock(m_exclusive_access_mutex); m_has_sent_with_id = true; };
+ { MutexAutoLock lock(m_exclusive_access_mutex); m_has_sent_with_id = true; };
bool hasSentWithID()
- { JMutexAutoLock lock(m_exclusive_access_mutex); return m_has_sent_with_id; };
+ { MutexAutoLock lock(m_exclusive_access_mutex); return m_has_sent_with_id; };
unsigned int m_increment_packets_remaining;
unsigned int m_increment_bytes_remaining;
@@ -744,7 +744,7 @@ class Peer {
bool IncUseCount();
void DecUseCount();
- JMutex m_exclusive_access_mutex;
+ Mutex m_exclusive_access_mutex;
bool m_pending_deletion;
@@ -826,10 +826,10 @@ protected:
unsigned int maxtransfer);
float getResendTimeout()
- { JMutexAutoLock lock(m_exclusive_access_mutex); return resend_timeout; }
+ { MutexAutoLock lock(m_exclusive_access_mutex); return resend_timeout; }
void setResendTimeout(float timeout)
- { JMutexAutoLock lock(m_exclusive_access_mutex); resend_timeout = timeout; }
+ { MutexAutoLock lock(m_exclusive_access_mutex); resend_timeout = timeout; }
bool Ping(float dtime,SharedBuffer<u8>& data);
Channel channels[CHANNEL_COUNT];
@@ -910,14 +910,14 @@ struct ConnectionEvent
}
};
-class ConnectionSendThread : public JThread {
+class ConnectionSendThread : public Thread {
public:
friend class UDPPeer;
ConnectionSendThread(unsigned int max_packet_size, float timeout);
- void * Thread ();
+ void *run();
void Trigger();
@@ -961,7 +961,7 @@ private:
unsigned int m_max_packet_size;
float m_timeout;
std::queue<OutgoingPacket> m_outgoing_queue;
- JSemaphore m_send_sleep_semaphore;
+ Semaphore m_send_sleep_semaphore;
unsigned int m_iteration_packets_avaialble;
unsigned int m_max_commands_per_iteration;
@@ -969,24 +969,24 @@ private:
unsigned int m_max_packets_requeued;
};
-class ConnectionReceiveThread : public JThread {
+class ConnectionReceiveThread : public Thread {
public:
ConnectionReceiveThread(unsigned int max_packet_size);
- void * Thread ();
+ void *run();
- void setParent(Connection* parent) {
- assert(parent != NULL); // Pre-condition
+ void setParent(Connection *parent) {
+ assert(parent); // Pre-condition
m_connection = parent;
}
private:
- void receive ();
+ void receive();
// Returns next data from a buffer if possible
// If found, returns true; if not, false.
// If found, sets peer_id and dst
- bool getFromBuffers (u16 &peer_id, SharedBuffer<u8> &dst);
+ bool getFromBuffers(u16 &peer_id, SharedBuffer<u8> &dst);
bool checkIncomingBuffers(Channel *channel, u16 &peer_id,
SharedBuffer<u8> &dst);
@@ -1054,7 +1054,7 @@ protected:
std::list<u16> getPeerIDs()
{
- JMutexAutoLock peerlock(m_peers_mutex);
+ MutexAutoLock peerlock(m_peers_mutex);
return m_peer_ids;
}
@@ -1075,12 +1075,12 @@ private:
std::map<u16, Peer*> m_peers;
std::list<u16> m_peer_ids;
- JMutex m_peers_mutex;
+ Mutex m_peers_mutex;
ConnectionSendThread m_sendThread;
ConnectionReceiveThread m_receiveThread;
- JMutex m_info_mutex;
+ Mutex m_info_mutex;
// Backwards compatibility
PeerHandler *m_bc_peerhandler;
diff --git a/src/network/networkpacket.cpp b/src/network/networkpacket.cpp
index b5e451cdb..388afc18e 100644
--- a/src/network/networkpacket.cpp
+++ b/src/network/networkpacket.cpp
@@ -77,6 +77,9 @@ void NetworkPacket::putRawString(const char* src, u32 len)
m_data.resize(m_datasize);
}
+ if (len == 0)
+ return;
+
memcpy(&m_data[m_read_offset], src, len);
m_read_offset += len;
}
diff --git a/src/network/networkprotocol.h b/src/network/networkprotocol.h
index 82c82f79e..177b97680 100644
--- a/src/network/networkprotocol.h
+++ b/src/network/networkprotocol.h
@@ -132,16 +132,23 @@ with this program; if not, write to the Free Software Foundation, Inc.,
Rename GENERIC_CMD_SET_ATTACHMENT to GENERIC_CMD_ATTACH_TO
PROTOCOL_VERSION 26:
Add TileDef tileable_horizontal, tileable_vertical flags
+ PROTOCOL_VERSION 27:
+ backface_culling: backwards compatibility for playing with
+ newer client on pre-27 servers.
+ Add nodedef v3 - connected nodeboxes
*/
-#define LATEST_PROTOCOL_VERSION 26
+#define LATEST_PROTOCOL_VERSION 27
// Server's supported network protocol range
#define SERVER_PROTOCOL_VERSION_MIN 13
#define SERVER_PROTOCOL_VERSION_MAX LATEST_PROTOCOL_VERSION
// Client's supported network protocol range
-#define CLIENT_PROTOCOL_VERSION_MIN 13
+// The minimal version depends on whether
+// send_pre_v25_init is enabled or not
+#define CLIENT_PROTOCOL_VERSION_MIN 25
+#define CLIENT_PROTOCOL_VERSION_MIN_LEGACY 13
#define CLIENT_PROTOCOL_VERSION_MAX LATEST_PROTOCOL_VERSION
// Constant that differentiates the protocol from random data and other protocols
@@ -208,7 +215,6 @@ enum ToClientCommand
TOCLIENT_BLOCKDATA = 0x20, //TODO: Multiple blocks
TOCLIENT_ADDNODE = 0x21,
/*
- u16 command
v3s16 position
serialized mapnode
u8 keep_metadata // Added in protocol version 22
@@ -255,7 +261,6 @@ enum ToClientCommand
/*
Sent as unreliable.
- u16 command
u16 number of player positions
for each player:
u16 peer_id
@@ -271,7 +276,6 @@ enum ToClientCommand
TOCLIENT_TIME_OF_DAY = 0x29,
/*
- u16 command
u16 time (0-23999)
Added in a later version:
f1000 time_speed
@@ -281,14 +285,12 @@ enum ToClientCommand
TOCLIENT_CHAT_MESSAGE = 0x30,
/*
- u16 command
u16 length
wstring message
*/
TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD = 0x31,
/*
- u16 command
u16 count of removed objects
for all removed objects {
u16 id
@@ -304,7 +306,6 @@ enum ToClientCommand
TOCLIENT_ACTIVE_OBJECT_MESSAGES = 0x32,
/*
- u16 command
for all objects
{
u16 id
@@ -315,13 +316,11 @@ enum ToClientCommand
TOCLIENT_HP = 0x33,
/*
- u16 command
u8 hp
*/
TOCLIENT_MOVE_PLAYER = 0x34,
/*
- u16 command
v3f1000 player position
f1000 player pitch
f1000 player yaw
@@ -329,14 +328,12 @@ enum ToClientCommand
TOCLIENT_ACCESS_DENIED_LEGACY = 0x35,
/*
- u16 command
u16 reason_length
wstring reason
*/
TOCLIENT_PLAYERITEM = 0x36, // Obsolete
/*
- u16 command
u16 count of player items
for all player items {
u16 peer id
@@ -347,14 +344,12 @@ enum ToClientCommand
TOCLIENT_DEATHSCREEN = 0x37,
/*
- u16 command
u8 bool set camera point target
v3f1000 camera point target (to point the death cause or whatever)
*/
TOCLIENT_MEDIA = 0x38,
/*
- u16 command
u16 total number of texture bunches
u16 index of this bunch
u32 number of files in this bunch
@@ -370,21 +365,18 @@ enum ToClientCommand
TOCLIENT_TOOLDEF = 0x39,
/*
- u16 command
u32 length of the next item
serialized ToolDefManager
*/
TOCLIENT_NODEDEF = 0x3a,
/*
- u16 command
u32 length of the next item
serialized NodeDefManager
*/
TOCLIENT_CRAFTITEMDEF = 0x3b,
/*
- u16 command
u32 length of the next item
serialized CraftiItemDefManager
*/
@@ -392,7 +384,6 @@ enum ToClientCommand
TOCLIENT_ANNOUNCE_MEDIA = 0x3c,
/*
- u16 command
u32 number of files
for each texture {
u16 length of name
@@ -404,14 +395,12 @@ enum ToClientCommand
TOCLIENT_ITEMDEF = 0x3d,
/*
- u16 command
u32 length of next item
serialized ItemDefManager
*/
TOCLIENT_PLAY_SOUND = 0x3f,
/*
- u16 command
s32 sound_id
u16 len
u8[len] sound name
@@ -424,13 +413,11 @@ enum ToClientCommand
TOCLIENT_STOP_SOUND = 0x40,
/*
- u16 command
s32 sound_id
*/
TOCLIENT_PRIVILEGES = 0x41,
/*
- u16 command
u16 number of privileges
for each privilege
u16 len
@@ -439,7 +426,6 @@ enum ToClientCommand
TOCLIENT_INVENTORY_FORMSPEC = 0x42,
/*
- u16 command
u32 len
u8[len] formspec
*/
@@ -463,7 +449,6 @@ enum ToClientCommand
TOCLIENT_MOVEMENT = 0x45,
/*
- u16 command
f1000 movement_acceleration_default
f1000 movement_acceleration_air
f1000 movement_acceleration_fast
@@ -480,7 +465,6 @@ enum ToClientCommand
TOCLIENT_SPAWN_PARTICLE = 0x46,
/*
- u16 command
v3f1000 pos
v3f1000 velocity
v3f1000 acceleration
@@ -494,7 +478,6 @@ enum ToClientCommand
TOCLIENT_ADD_PARTICLESPAWNER = 0x47,
/*
- u16 command
u16 amount
f1000 spawntime
v3f1000 minpos
@@ -516,13 +499,11 @@ enum ToClientCommand
TOCLIENT_DELETE_PARTICLESPAWNER_LEGACY = 0x48,
/*
- u16 command
u16 id
*/
TOCLIENT_HUDADD = 0x49,
/*
- u16 command
u32 id
u8 type
v2f1000 pos
@@ -542,13 +523,11 @@ enum ToClientCommand
TOCLIENT_HUDRM = 0x4a,
/*
- u16 command
u32 id
*/
TOCLIENT_HUDCHANGE = 0x4b,
/*
- u16 command
u32 id
u8 stat
[v2f1000 data |
@@ -559,14 +538,12 @@ enum ToClientCommand
TOCLIENT_HUD_SET_FLAGS = 0x4c,
/*
- u16 command
u32 flags
u32 mask
*/
TOCLIENT_HUD_SET_PARAM = 0x4d,
/*
- u16 command
u16 param
u16 len
u8[len] value
@@ -574,13 +551,11 @@ enum ToClientCommand
TOCLIENT_BREATH = 0x4e,
/*
- u16 command
u16 breath
*/
TOCLIENT_SET_SKY = 0x4f,
/*
- u16 command
u8[4] color (ARGB)
u8 len
u8[len] type
@@ -592,14 +567,12 @@ enum ToClientCommand
TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO = 0x50,
/*
- u16 command
u8 do_override (boolean)
u16 day-night ratio 0...65535
*/
TOCLIENT_LOCAL_PLAYER_ANIMATIONS = 0x51,
/*
- u16 command
v2s32 stand/idle
v2s32 walk
v2s32 dig
@@ -609,14 +582,12 @@ enum ToClientCommand
TOCLIENT_EYE_OFFSET = 0x52,
/*
- u16 command
v3f1000 first
v3f1000 third
*/
TOCLIENT_DELETE_PARTICLESPAWNER = 0x53,
/*
- u16 command
u32 id
*/
@@ -624,7 +595,6 @@ enum ToClientCommand
/*
Belonging to AUTH_MECHANISM_LEGACY_PASSWORD and AUTH_MECHANISM_SRP.
- u16 command
std::string bytes_s
std::string bytes_B
*/
@@ -735,7 +705,6 @@ enum ToServerCommand
TOSERVER_SIGNTEXT = 0x30, // Old signs, obsolete
/*
- u16 command
v3s16 blockpos
s16 id
u16 textlen
@@ -749,14 +718,12 @@ enum ToServerCommand
TOSERVER_CHAT_MESSAGE = 0x32,
/*
- u16 command
u16 length
wstring message
*/
TOSERVER_SIGNNODETEXT = 0x33, // obsolete
/*
- u16 command
v3s16 p
u16 textlen
textdata
@@ -773,7 +740,6 @@ enum ToServerCommand
TOSERVER_DAMAGE = 0x35,
/*
- u16 command
u8 amount
*/
@@ -818,14 +784,12 @@ enum ToServerCommand
TOSERVER_REMOVED_SOUNDS = 0x3a,
/*
- u16 command
u16 len
s32[len] sound_id
*/
TOSERVER_NODEMETA_FIELDS = 0x3b,
/*
- u16 command
v3s16 p
u16 len
u8[len] form name (reserved for future use)
@@ -839,7 +803,6 @@ enum ToServerCommand
TOSERVER_INVENTORY_FIELDS = 0x3c,
/*
- u16 command
u16 len
u8[len] form name (reserved for future use)
u16 number of fields
@@ -852,7 +815,6 @@ enum ToServerCommand
TOSERVER_REQUEST_MEDIA = 0x40,
/*
- u16 command
u16 number of files requested
for each file {
u16 length of name
@@ -862,12 +824,11 @@ enum ToServerCommand
TOSERVER_RECEIVED_MEDIA = 0x41,
/*
- u16 command
+ <no payload data>
*/
TOSERVER_BREATH = 0x42,
/*
- u16 command
u16 breath
*/
diff --git a/src/network/serverpackethandler.cpp b/src/network/serverpackethandler.cpp
index f756d80ef..1bcb78a8a 100644
--- a/src/network/serverpackethandler.cpp
+++ b/src/network/serverpackethandler.cpp
@@ -103,7 +103,7 @@ void Server::handleCommand_Init(NetworkPacket* pkt)
// Use the highest version supported by both
u8 depl_serial_v = std::min(client_max, our_max);
// If it's lower than the lowest supported, give up.
- if (depl_serial_v < SER_FMT_VER_LOWEST)
+ if (depl_serial_v < SER_FMT_VER_LOWEST_READ)
depl_serial_v = SER_FMT_VER_INVALID;
if (depl_serial_v == SER_FMT_VER_INVALID) {
@@ -261,9 +261,9 @@ void Server::handleCommand_Init(NetworkPacket* pkt)
auth_mechs |= AUTH_MECHANISM_FIRST_SRP;
} else {
// Take care of default passwords.
- client->enc_pwd = getSRPVerifier(playerName, default_password);
+ client->enc_pwd = get_encoded_srp_verifier(playerName, default_password);
auth_mechs |= AUTH_MECHANISM_SRP;
- // Create auth, but only on successful login
+ // Allocate player in db, but only on successful login.
client->create_player_on_auth_success = true;
}
}
@@ -347,7 +347,7 @@ void Server::handleCommand_Init_Legacy(NetworkPacket* pkt)
// Use the highest version supported by both
int deployed = std::min(client_max, our_max);
// If it's lower than the lowest supported, give up.
- if (deployed < SER_FMT_VER_LOWEST)
+ if (deployed < SER_FMT_VER_LOWEST_READ)
deployed = SER_FMT_VER_INVALID;
if (deployed == SER_FMT_VER_INVALID) {
@@ -549,7 +549,7 @@ void Server::handleCommand_Init_Legacy(NetworkPacket* pkt)
std::string raw_default_password =
g_settings->get("default_password");
std::string initial_password =
- translatePassword(playername, raw_default_password);
+ translate_password(playername, raw_default_password);
// If default_password is empty, allow any initial password
if (raw_default_password.length() == 0)
@@ -568,8 +568,10 @@ void Server::handleCommand_Init_Legacy(NetworkPacket* pkt)
}
if (given_password != checkpwd) {
- actionstream << "Server: " << playername << " supplied wrong password"
- << std::endl;
+ actionstream << "Server: User " << playername
+ << " at " << addr_s
+ << " supplied wrong password (auth mechanism: legacy)."
+ << std::endl;
DenyAccess_Legacy(pkt->getPeerId(), L"Wrong password");
return;
}
@@ -760,14 +762,14 @@ void Server::handleCommand_GotBlocks(NetworkPacket* pkt)
RemoteClient *client = getClient(pkt->getPeerId());
- for (u16 i = 0; i < count; i++) {
- if ((s16)pkt->getSize() < 1 + (i + 1) * 6)
- throw con::InvalidIncomingDataException
+ if ((s16)pkt->getSize() < 1 + (int)count * 6) {
+ throw con::InvalidIncomingDataException
("GOTBLOCKS length is too short");
- v3s16 p;
+ }
+ for (u16 i = 0; i < count; i++) {
+ v3s16 p;
*pkt >> p;
-
client->GotBlock(p);
}
}
@@ -863,13 +865,14 @@ void Server::handleCommand_DeletedBlocks(NetworkPacket* pkt)
RemoteClient *client = getClient(pkt->getPeerId());
- for (u16 i = 0; i < count; i++) {
- if ((s16)pkt->getSize() < 1 + (i + 1) * 6)
- throw con::InvalidIncomingDataException
+ if ((s16)pkt->getSize() < 1 + (int)count * 6) {
+ throw con::InvalidIncomingDataException
("DELETEDBLOCKS length is too short");
+ }
+
+ for (u16 i = 0; i < count; i++) {
v3s16 p;
*pkt >> p;
-
client->SetBlockNotSent(p);
}
}
@@ -1057,69 +1060,15 @@ void Server::handleCommand_ChatMessage(NetworkPacket* pkt)
return;
}
- // If something goes wrong, this player is to blame
- RollbackScopeActor rollback_scope(m_rollback,
- std::string("player:")+player->getName());
-
// Get player name of this client
- std::wstring name = narrow_to_wide(player->getName());
-
- // Run script hook
- bool ate = m_script->on_chat_message(player->getName(),
- wide_to_narrow(message));
- // If script ate the message, don't proceed
- if (ate)
- return;
-
- // Line to send to players
- std::wstring line;
- // Whether to send to the player that sent the line
- bool send_to_sender_only = false;
-
- // Commands are implemented in Lua, so only catch invalid
- // commands that were not "eaten" and send an error back
- if (message[0] == L'/') {
- message = message.substr(1);
- send_to_sender_only = true;
- if (message.length() == 0)
- line += L"-!- Empty command";
- else
- line += L"-!- Invalid command: " + str_split(message, L' ')[0];
- }
- else {
- if (checkPriv(player->getName(), "shout")) {
- line += L"<";
- line += name;
- line += L"> ";
- line += message;
- } else {
- line += L"-!- You don't have permission to shout.";
- send_to_sender_only = true;
- }
- }
+ std::string name = player->getName();
+ std::wstring wname = narrow_to_wide(name);
- if (line != L"")
- {
- /*
- Send the message to sender
- */
- if (send_to_sender_only) {
- SendChatMessage(pkt->getPeerId(), line);
- }
- /*
- Send the message to others
- */
- else {
- actionstream << "CHAT: " << wide_to_narrow(line)<<std::endl;
-
- std::vector<u16> clients = m_clients.getClientIDs();
-
- for (std::vector<u16>::iterator i = clients.begin();
- i != clients.end(); ++i) {
- if (*i != pkt->getPeerId())
- SendChatMessage(*i, line);
- }
- }
+ std::wstring answer_to_sender = handleChat(name, wname, message,
+ true, pkt->getPeerId());
+ if (!answer_to_sender.empty()) {
+ // Send the answer to sender
+ SendChatMessage(pkt->getPeerId(), answer_to_sender);
}
}
@@ -1410,7 +1359,9 @@ void Server::handleCommand_Interact(NetworkPacket* pkt)
Check that target is reasonably close
(only when digging or placing things)
*/
- if (action == 0 || action == 2 || action == 3) {
+ static const bool enable_anticheat = !g_settings->getBool("disable_anticheat");
+ if ((action == 0 || action == 2 || action == 3) &&
+ (enable_anticheat && !isSingleplayer())) {
float d = player_pos.getDistanceFrom(pointed_pos_under);
float max_d = BS * 14; // Just some large enough value
if (d > max_d) {
@@ -1467,21 +1418,22 @@ void Server::handleCommand_Interact(NetworkPacket* pkt)
NOTE: This can be used in the future to check if
somebody is cheating, by checking the timing.
*/
+
MapNode n(CONTENT_IGNORE);
bool pos_ok;
- n = m_env->getMap().getNodeNoEx(p_under, &pos_ok);
- if (pos_ok)
- n = m_env->getMap().getNodeNoEx(p_under, &pos_ok);
+ n = m_env->getMap().getNodeNoEx(p_under, &pos_ok);
if (!pos_ok) {
infostream << "Server: Not punching: Node not found."
<< " Adding block to emerge queue."
<< std::endl;
- m_emerge->enqueueBlockEmerge(pkt->getPeerId(), getNodeBlockPos(p_above), false);
+ m_emerge->enqueueBlockEmerge(pkt->getPeerId(),
+ getNodeBlockPos(p_above), false);
}
if (n.getContent() != CONTENT_IGNORE)
m_script->node_on_punch(p_under, n, playersao, pointed);
+
// Cheat prevention
playersao->noCheatDigStart(p_under);
}
@@ -1538,14 +1490,15 @@ void Server::handleCommand_Interact(NetworkPacket* pkt)
MapNode n = m_env->getMap().getNodeNoEx(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(pkt->getPeerId(), getNodeBlockPos(p_above), false);
+ << " Adding block to emerge queue."
+ << std::endl;
+ m_emerge->enqueueBlockEmerge(pkt->getPeerId(),
+ getNodeBlockPos(p_above), false);
}
/* Cheat prevention */
bool is_valid_dig = true;
- if (!isSingleplayer() && !g_settings->getBool("disable_anticheat")) {
+ if (enable_anticheat && !isSingleplayer()) {
v3s16 nocheat_p = playersao->getNoCheatDigPos();
float nocheat_t = playersao->getNoCheatDigTime();
playersao->noCheatDigEnd();
@@ -1703,13 +1656,30 @@ void Server::handleCommand_Interact(NetworkPacket* pkt)
}
} // action == 4
+
+ /*
+ 5: rightclick air
+ */
+ else if (action == 5) {
+ ItemStack item = playersao->getWieldedItem();
+
+ actionstream << player->getName() << " activates "
+ << item.name << std::endl;
+
+ if (m_script->item_OnSecondaryUse(
+ item, playersao)) {
+ if( playersao->setWieldedItem(item)) {
+ SendInventory(playersao);
+ }
+ }
+ }
/*
Catch invalid actions
*/
else {
- infostream << "WARNING: Server: Invalid action "
+ warningstream << "Server: Invalid action "
<< action << std::endl;
}
}
@@ -1838,7 +1808,7 @@ void Server::handleCommand_FirstSrp(NetworkPacket* pkt)
*pkt >> salt >> verification_key >> is_empty;
verbosestream << "Server: Got TOSERVER_FIRST_SRP from " << addr_s
- << ", with is_empty= " << is_empty << std::endl;
+ << ", with is_empty=" << (is_empty == 1) << std::endl;
// Either this packet is sent because the user is new or to change the password
if (cstate == CS_HelloSent) {
@@ -1861,7 +1831,7 @@ void Server::handleCommand_FirstSrp(NetworkPacket* pkt)
std::string initial_ver_key;
- initial_ver_key = encodeSRPVerifier(verification_key, salt);
+ initial_ver_key = encode_srp_verifier(verification_key, salt);
m_script->createAuth(playername, initial_ver_key);
acceptAuth(pkt->getPeerId(), false);
@@ -1873,7 +1843,7 @@ void Server::handleCommand_FirstSrp(NetworkPacket* pkt)
return;
}
m_clients.event(pkt->getPeerId(), CSE_SudoLeave);
- std::string pw_db_field = encodeSRPVerifier(verification_key, salt);
+ std::string pw_db_field = encode_srp_verifier(verification_key, salt);
bool success = m_script->setPassword(playername, pw_db_field);
if (success) {
actionstream << playername << " changes password" << std::endl;
@@ -1947,22 +1917,14 @@ void Server::handleCommand_SrpBytesA(NetworkPacket* pkt)
client->chosen_mech = chosen;
- std::string bytes_s;
- std::string bytes_v;
+ std::string salt;
+ std::string verifier;
if (based_on == 0) {
- char *p_bytes_s = 0;
- size_t len_s = 0;
- char *p_bytes_v = 0;
- size_t len_v = 0;
- getSRPVerifier(client->getName(), client->enc_pwd,
- &p_bytes_s, &len_s,
- &p_bytes_v, &len_v);
- bytes_s = std::string(p_bytes_s, len_s);
- bytes_v = std::string(p_bytes_v, len_v);
- free(p_bytes_s);
- free(p_bytes_v);
- } else if (!decodeSRPVerifier(client->enc_pwd, &bytes_s, &bytes_v)) {
+
+ generate_srp_verifier_and_salt(client->getName(), client->enc_pwd,
+ &verifier, &salt);
+ } else if (!decode_srp_verifier_and_salt(client->enc_pwd, &verifier, &salt)) {
// Non-base64 errors should have been catched in the init handler
actionstream << "Server: User " << client->getName()
<< " tried to log in, but srp verifier field"
@@ -1976,8 +1938,8 @@ void Server::handleCommand_SrpBytesA(NetworkPacket* pkt)
client->auth_data = srp_verifier_new(SRP_SHA256, SRP_NG_2048,
client->getName().c_str(),
- (const unsigned char *) bytes_s.c_str(), bytes_s.size(),
- (const unsigned char *) bytes_v.c_str(), bytes_v.size(),
+ (const unsigned char *) salt.c_str(), salt.size(),
+ (const unsigned char *) verifier.c_str(), verifier.size(),
(const unsigned char *) bytes_A.c_str(), bytes_A.size(),
NULL, 0,
(unsigned char **) &bytes_B, &len_B, NULL, NULL);
@@ -1996,7 +1958,7 @@ void Server::handleCommand_SrpBytesA(NetworkPacket* pkt)
}
NetworkPacket resp_pkt(TOCLIENT_SRP_BYTES_S_B, 0, pkt->getPeerId());
- resp_pkt << bytes_s << std::string(bytes_B, len_B);
+ resp_pkt << salt << std::string(bytes_B, len_B);
Send(&resp_pkt);
}
@@ -2060,9 +2022,8 @@ void Server::handleCommand_SrpBytesM(NetworkPacket* pkt)
} else {
actionstream << "Server: User " << client->getName()
<< " at " << getPeerAddress(pkt->getPeerId()).serializeString()
- << " supplied wrong (SRP) password from address "
- << getPeerAddress(pkt->getPeerId()).serializeString()
- << "." << std::endl;
+ << " supplied wrong password (auth mechanism: SRP)."
+ << std::endl;
DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_WRONG_PASSWORD);
return;
}
diff --git a/src/nodedef.cpp b/src/nodedef.cpp
index 269c2b9d6..3a2cb00b1 100644
--- a/src/nodedef.cpp
+++ b/src/nodedef.cpp
@@ -33,6 +33,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "exceptions.h"
#include "debug.h"
#include "gamedef.h"
+#include "mapnode.h"
#include <fstream> // Used in applyTextureOverrides()
/*
@@ -48,44 +49,91 @@ void NodeBox::reset()
wall_top = aabb3f(-BS/2, BS/2-BS/16., -BS/2, BS/2, BS/2, BS/2);
wall_bottom = aabb3f(-BS/2, -BS/2, -BS/2, BS/2, -BS/2+BS/16., BS/2);
wall_side = aabb3f(-BS/2, -BS/2, -BS/2, -BS/2+BS/16., BS/2, BS/2);
+ // no default for other parts
+ connect_top.clear();
+ connect_bottom.clear();
+ connect_front.clear();
+ connect_left.clear();
+ connect_back.clear();
+ connect_right.clear();
}
void NodeBox::serialize(std::ostream &os, u16 protocol_version) const
{
- int version = protocol_version >= 21 ? 2 : 1;
+ int version = 1;
+ if (protocol_version >= 27)
+ version = 3;
+ else if (protocol_version >= 21)
+ version = 2;
writeU8(os, version);
- if (version == 1 && type == NODEBOX_LEVELED)
- writeU8(os, NODEBOX_FIXED);
- else
- writeU8(os, type);
+ switch (type) {
+ case NODEBOX_LEVELED:
+ case NODEBOX_FIXED:
+ if (version == 1)
+ writeU8(os, NODEBOX_FIXED);
+ else
+ writeU8(os, type);
- if(type == NODEBOX_FIXED || type == NODEBOX_LEVELED)
- {
writeU16(os, fixed.size());
- for(std::vector<aabb3f>::const_iterator
+ for (std::vector<aabb3f>::const_iterator
i = fixed.begin();
- i != fixed.end(); i++)
+ i != fixed.end(); ++i)
{
writeV3F1000(os, i->MinEdge);
writeV3F1000(os, i->MaxEdge);
}
- }
- else if(type == NODEBOX_WALLMOUNTED)
- {
+ break;
+ case NODEBOX_WALLMOUNTED:
+ writeU8(os, type);
+
writeV3F1000(os, wall_top.MinEdge);
writeV3F1000(os, wall_top.MaxEdge);
writeV3F1000(os, wall_bottom.MinEdge);
writeV3F1000(os, wall_bottom.MaxEdge);
writeV3F1000(os, wall_side.MinEdge);
writeV3F1000(os, wall_side.MaxEdge);
+ break;
+ case NODEBOX_CONNECTED:
+ if (version <= 2) {
+ // send old clients nodes that can't be walked through
+ // to prevent abuse
+ writeU8(os, NODEBOX_FIXED);
+
+ writeU16(os, 1);
+ writeV3F1000(os, v3f(-BS/2, -BS/2, -BS/2));
+ writeV3F1000(os, v3f(BS/2, BS/2, BS/2));
+ } else {
+ writeU8(os, type);
+
+#define WRITEBOX(box) do { \
+ writeU16(os, (box).size()); \
+ for (std::vector<aabb3f>::const_iterator \
+ i = (box).begin(); \
+ i != (box).end(); ++i) { \
+ writeV3F1000(os, i->MinEdge); \
+ writeV3F1000(os, i->MaxEdge); \
+ }; } while (0)
+
+ WRITEBOX(fixed);
+ WRITEBOX(connect_top);
+ WRITEBOX(connect_bottom);
+ WRITEBOX(connect_front);
+ WRITEBOX(connect_left);
+ WRITEBOX(connect_back);
+ WRITEBOX(connect_right);
+ }
+ break;
+ default:
+ writeU8(os, type);
+ break;
}
}
void NodeBox::deSerialize(std::istream &is)
{
int version = readU8(is);
- if(version < 1 || version > 2)
+ if (version < 1 || version > 3)
throw SerializationError("unsupported NodeBox version");
reset();
@@ -112,6 +160,26 @@ void NodeBox::deSerialize(std::istream &is)
wall_side.MinEdge = readV3F1000(is);
wall_side.MaxEdge = readV3F1000(is);
}
+ else if (type == NODEBOX_CONNECTED)
+ {
+#define READBOXES(box) do { \
+ count = readU16(is); \
+ (box).reserve(count); \
+ while (count--) { \
+ v3f min = readV3F1000(is); \
+ v3f max = readV3F1000(is); \
+ (box).push_back(aabb3f(min, max)); }; } while (0)
+
+ u16 count;
+
+ READBOXES(fixed);
+ READBOXES(connect_top);
+ READBOXES(connect_bottom);
+ READBOXES(connect_front);
+ READBOXES(connect_left);
+ READBOXES(connect_back);
+ READBOXES(connect_right);
+ }
}
/*
@@ -139,7 +207,7 @@ void TileDef::serialize(std::ostream &os, u16 protocol_version) const
}
}
-void TileDef::deSerialize(std::istream &is)
+void TileDef::deSerialize(std::istream &is, const u8 contenfeatures_version, const NodeDrawType drawtype)
{
int version = readU8(is);
name = deSerializeString(is);
@@ -153,6 +221,13 @@ void TileDef::deSerialize(std::istream &is)
tileable_horizontal = readU8(is);
tileable_vertical = readU8(is);
}
+
+ if ((contenfeatures_version < 8) &&
+ ((drawtype == NDT_MESH) ||
+ (drawtype == NDT_FIRELIKE) ||
+ (drawtype == NDT_LIQUID) ||
+ (drawtype == NDT_PLANTLIKE)))
+ backface_culling = false;
}
@@ -233,6 +308,7 @@ void ContentFeatures::reset()
diggable = true;
climbable = false;
buildable_to = false;
+ floodable = false;
rightclickable = true;
leveled = 0;
liquid_type = LIQUID_NONE;
@@ -253,6 +329,9 @@ void ContentFeatures::reset()
sound_footstep = SimpleSoundSpec();
sound_dig = SimpleSoundSpec("__group");
sound_dug = SimpleSoundSpec();
+ connects_to.clear();
+ connects_to_ids.clear();
+ connect_sides = 0;
}
void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const
@@ -262,11 +341,12 @@ void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const
return;
}
- writeU8(os, 7); // version
+ writeU8(os, protocol_version < 27 ? 7 : 8);
+
os<<serializeString(name);
writeU16(os, groups.size());
for(ItemGroupList::const_iterator
- i = groups.begin(); i != groups.end(); i++){
+ i = groups.begin(); i != groups.end(); ++i){
os<<serializeString(i->first);
writeS16(os, i->second);
}
@@ -318,14 +398,22 @@ void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const
// the protocol version
os<<serializeString(mesh);
collision_box.serialize(os, protocol_version);
+ writeU8(os, floodable);
+ writeU16(os, connects_to_ids.size());
+ for (std::set<content_t>::const_iterator i = connects_to_ids.begin();
+ i != connects_to_ids.end(); ++i)
+ writeU16(os, *i);
+ writeU8(os, connect_sides);
}
void ContentFeatures::deSerialize(std::istream &is)
{
int version = readU8(is);
- if(version != 7){
+ if (version < 7) {
deSerializeOld(is, version);
return;
+ } else if (version > 8) {
+ throw SerializationError("unsupported ContentFeatures version");
}
name = deSerializeString(is);
@@ -337,15 +425,16 @@ void ContentFeatures::deSerialize(std::istream &is)
groups[name] = value;
}
drawtype = (enum NodeDrawType)readU8(is);
+
visual_scale = readF1000(is);
if(readU8(is) != 6)
throw SerializationError("unsupported tile count");
for(u32 i = 0; i < 6; i++)
- tiledef[i].deSerialize(is);
+ tiledef[i].deSerialize(is, version, drawtype);
if(readU8(is) != CF_SPECIAL_COUNT)
throw SerializationError("unsupported CF_SPECIAL_COUNT");
for(u32 i = 0; i < CF_SPECIAL_COUNT; i++)
- tiledef_special[i].deSerialize(is);
+ tiledef_special[i].deSerialize(is, version, drawtype);
alpha = readU8(is);
post_effect_color.setAlpha(readU8(is));
post_effect_color.setRed(readU8(is));
@@ -388,6 +477,12 @@ void ContentFeatures::deSerialize(std::istream &is)
// otherwise changes the protocol version
mesh = deSerializeString(is);
collision_box.deSerialize(is);
+ floodable = readU8(is);
+ u16 connects_to_size = readU16(is);
+ connects_to_ids.clear();
+ for (u16 i = 0; i < connects_to_size; i++)
+ connects_to_ids.insert(readU16(is));
+ connect_sides = readU8(is);
}catch(SerializationError &e) {};
}
@@ -405,7 +500,7 @@ public:
inline virtual const ContentFeatures& get(const MapNode &n) const;
virtual bool getId(const std::string &name, content_t &result) const;
virtual content_t getId(const std::string &name) const;
- virtual void getIds(const std::string &name, std::set<content_t> &result) const;
+ virtual bool getIds(const std::string &name, std::set<content_t> &result) const;
virtual const ContentFeatures& get(const std::string &name) const;
content_t allocateId();
virtual content_t set(const std::string &name, const ContentFeatures &def);
@@ -425,6 +520,8 @@ public:
virtual bool cancelNodeResolveCallback(NodeResolver *nr);
virtual void runNodeResolveCallbacks();
virtual void resetNodeResolveState();
+ virtual void mapNodeboxConnections();
+ virtual bool nodeboxConnects(MapNode from, MapNode to, u8 connect_face);
private:
void addNameIdMapping(content_t i, std::string name);
@@ -520,6 +617,7 @@ void CNodeDefManager::clear()
f.pointable = false;
f.diggable = false;
f.buildable_to = true;
+ f.floodable = true;
f.is_ground_content = true;
// Insert directly into containers
content_t c = CONTENT_AIR;
@@ -588,22 +686,23 @@ content_t CNodeDefManager::getId(const std::string &name) const
}
-void CNodeDefManager::getIds(const std::string &name,
+bool CNodeDefManager::getIds(const std::string &name,
std::set<content_t> &result) const
{
//TimeTaker t("getIds", NULL, PRECISION_MICRO);
if (name.substr(0,6) != "group:") {
content_t id = CONTENT_IGNORE;
- if(getId(name, id))
+ bool exists = getId(name, id);
+ if (exists)
result.insert(id);
- return;
+ return exists;
}
std::string group = name.substr(6);
std::map<std::string, GroupItems>::const_iterator
i = m_group_to_items.find(group);
if (i == m_group_to_items.end())
- return;
+ return true;
const GroupItems &items = i->second;
for (GroupItems::const_iterator j = items.begin();
@@ -612,6 +711,7 @@ void CNodeDefManager::getIds(const std::string &name,
result.insert((*j).first);
}
//printf("getIds: %dus\n", t.stop());
+ return true;
}
@@ -653,7 +753,7 @@ content_t CNodeDefManager::set(const std::string &name, const ContentFeatures &d
// Don't allow redefining ignore (but allow air and unknown)
if (name == "ignore") {
- infostream << "NodeDefManager: WARNING: Ignoring "
+ warningstream << "NodeDefManager: Ignoring "
"CONTENT_IGNORE redefinition"<<std::endl;
return CONTENT_IGNORE;
}
@@ -663,7 +763,7 @@ content_t CNodeDefManager::set(const std::string &name, const ContentFeatures &d
// Get new id
id = allocateId();
if (id == CONTENT_IGNORE) {
- infostream << "NodeDefManager: WARNING: Absolute "
+ warningstream << "NodeDefManager: Absolute "
"limit reached" << std::endl;
return CONTENT_IGNORE;
}
@@ -709,7 +809,7 @@ void CNodeDefManager::updateAliases(IItemDefManager *idef)
std::set<std::string> all = idef->getAll();
m_name_id_mapping_with_aliases.clear();
for (std::set<std::string>::iterator
- i = all.begin(); i != all.end(); i++) {
+ i = all.begin(); i != all.end(); ++i) {
std::string name = *i;
std::string convert_to = idef->getAlias(name);
content_t id;
@@ -791,7 +891,6 @@ void CNodeDefManager::updateTextures(IGameDef *gamedef,
scene::ISceneManager* smgr = gamedef->getSceneManager();
scene::IMeshManipulator* meshmanip = smgr->getMeshManipulator();
- bool new_style_water = g_settings->getBool("new_style_water");
bool connected_glass = g_settings->getBool("connected_glass");
bool opaque_water = g_settings->getBool("opaque_water");
bool enable_shaders = g_settings->getBool("enable_shaders");
@@ -839,12 +938,7 @@ void CNodeDefManager::updateTextures(IGameDef *gamedef,
assert(f->liquid_type == LIQUID_SOURCE);
if (opaque_water)
f->alpha = 255;
- if (new_style_water){
- f->solidness = 0;
- } else {
- f->solidness = 1;
- f->backface_culling = false;
- }
+ f->solidness = 1;
is_liquid = true;
break;
case NDT_FLOWINGLIQUID:
@@ -895,17 +989,14 @@ void CNodeDefManager::updateTextures(IGameDef *gamedef,
break;
case NDT_PLANTLIKE:
f->solidness = 0;
- f->backface_culling = false;
if (f->waving == 1)
material_type = TILE_MATERIAL_WAVING_PLANTS;
break;
case NDT_FIRELIKE:
- f->backface_culling = false;
f->solidness = 0;
break;
case NDT_MESH:
f->solidness = 0;
- f->backface_culling = false;
break;
case NDT_TORCHLIKE:
case NDT_SIGNLIKE:
@@ -937,7 +1028,7 @@ void CNodeDefManager::updateTextures(IGameDef *gamedef,
// Tiles (fill in f->tiles[])
for (u16 j = 0; j < 6; j++) {
fillTileAttribs(tsrc, &f->tiles[j], &tiledef[j], tile_shader[j],
- use_normal_texture, f->backface_culling, f->alpha, material_type);
+ use_normal_texture, f->tiledef[j].backface_culling, f->alpha, material_type);
}
// Special tiles (fill in f->special_tiles[])
@@ -964,7 +1055,7 @@ void CNodeDefManager::updateTextures(IGameDef *gamedef,
//Convert regular nodebox nodes to meshnodes
//Change the drawtype and apply scale
f->drawtype = NDT_MESH;
- f->mesh_ptr[0] = convertNodeboxNodeToMesh(f);
+ f->mesh_ptr[0] = convertNodeboxesToMesh(f->node_box.fixed);
v3f scale = v3f(1.0, 1.0, 1.0) * f->visual_scale;
scaleMesh(f->mesh_ptr[0], scale);
recalculateBoundingBox(f->mesh_ptr[0]);
@@ -1112,12 +1203,12 @@ void CNodeDefManager::deSerialize(std::istream &is)
// Check error conditions
if (i == CONTENT_IGNORE || i == CONTENT_AIR || i == CONTENT_UNKNOWN) {
- infostream << "NodeDefManager::deSerialize(): WARNING: "
+ warningstream << "NodeDefManager::deSerialize(): "
"not changing builtin node " << i << std::endl;
continue;
}
if (f.name == "") {
- infostream << "NodeDefManager::deSerialize(): WARNING: "
+ warningstream << "NodeDefManager::deSerialize(): "
"received empty name" << std::endl;
continue;
}
@@ -1125,7 +1216,7 @@ void CNodeDefManager::deSerialize(std::istream &is)
// Ignore aliases
u16 existing_id;
if (m_name_id_mapping.getId(f.name, existing_id) && i != existing_id) {
- infostream << "NodeDefManager::deSerialize(): WARNING: "
+ warningstream << "NodeDefManager::deSerialize(): "
"already defined with different ID: " << f.name << std::endl;
continue;
}
@@ -1162,7 +1253,7 @@ void ContentFeatures::serializeOld(std::ostream &os, u16 protocol_version) const
os<<serializeString(name);
writeU16(os, groups.size());
for (ItemGroupList::const_iterator
- i = groups.begin(); i != groups.end(); i++) {
+ i = groups.begin(); i != groups.end(); ++i) {
os<<serializeString(i->first);
writeS16(os, i->second);
}
@@ -1210,7 +1301,7 @@ void ContentFeatures::serializeOld(std::ostream &os, u16 protocol_version) const
os<<serializeString(name);
writeU16(os, groups.size());
for (ItemGroupList::const_iterator
- i = groups.begin(); i != groups.end(); i++) {
+ i = groups.begin(); i != groups.end(); ++i) {
os<<serializeString(i->first);
writeS16(os, i->second);
}
@@ -1262,7 +1353,6 @@ void ContentFeatures::serializeOld(std::ostream &os, u16 protocol_version) const
"Unsupported version requested");
}
-
void ContentFeatures::deSerializeOld(std::istream &is, int version)
{
if (version == 5) // In PROTOCOL_VERSION 13
@@ -1276,15 +1366,16 @@ void ContentFeatures::deSerializeOld(std::istream &is, int version)
groups[name] = value;
}
drawtype = (enum NodeDrawType)readU8(is);
+
visual_scale = readF1000(is);
if (readU8(is) != 6)
throw SerializationError("unsupported tile count");
for (u32 i = 0; i < 6; i++)
- tiledef[i].deSerialize(is);
+ tiledef[i].deSerialize(is, version, drawtype);
if (readU8(is) != CF_SPECIAL_COUNT)
throw SerializationError("unsupported CF_SPECIAL_COUNT");
for (u32 i = 0; i < CF_SPECIAL_COUNT; i++)
- tiledef_special[i].deSerialize(is);
+ tiledef_special[i].deSerialize(is, version, drawtype);
alpha = readU8(is);
post_effect_color.setAlpha(readU8(is));
post_effect_color.setRed(readU8(is));
@@ -1328,12 +1419,12 @@ void ContentFeatures::deSerializeOld(std::istream &is, int version)
if (readU8(is) != 6)
throw SerializationError("unsupported tile count");
for (u32 i = 0; i < 6; i++)
- tiledef[i].deSerialize(is);
+ tiledef[i].deSerialize(is, version, drawtype);
// CF_SPECIAL_COUNT in version 6 = 2
if (readU8(is) != 2)
throw SerializationError("unsupported CF_SPECIAL_COUNT");
for (u32 i = 0; i < 2; i++)
- tiledef_special[i].deSerialize(is);
+ tiledef_special[i].deSerialize(is, version, drawtype);
alpha = readU8(is);
post_effect_color.setAlpha(readU8(is));
post_effect_color.setRed(readU8(is));
@@ -1430,6 +1521,57 @@ void CNodeDefManager::resetNodeResolveState()
m_pending_resolve_callbacks.clear();
}
+void CNodeDefManager::mapNodeboxConnections()
+{
+ for (u32 i = 0; i < m_content_features.size(); i++) {
+ ContentFeatures *f = &m_content_features[i];
+ if ((f->drawtype != NDT_NODEBOX) || (f->node_box.type != NODEBOX_CONNECTED))
+ continue;
+ for (std::vector<std::string>::iterator it = f->connects_to.begin();
+ it != f->connects_to.end(); ++it) {
+ getIds(*it, f->connects_to_ids);
+ }
+ }
+}
+
+bool CNodeDefManager::nodeboxConnects(MapNode from, MapNode to, u8 connect_face)
+{
+ const ContentFeatures &f1 = get(from);
+
+ if ((f1.drawtype != NDT_NODEBOX) || (f1.node_box.type != NODEBOX_CONNECTED))
+ return false;
+
+ // lookup target in connected set
+ if (f1.connects_to_ids.find(to.param0) == f1.connects_to_ids.end())
+ return false;
+
+ const ContentFeatures &f2 = get(to);
+
+ if ((f2.drawtype == NDT_NODEBOX) && (f2.node_box.type == NODEBOX_CONNECTED))
+ // ignores actually looking if back connection exists
+ return (f2.connects_to_ids.find(from.param0) != f2.connects_to_ids.end());
+
+ // does to node declare usable faces?
+ if (f2.connect_sides > 0) {
+ if ((f2.param_type_2 == CPT2_FACEDIR) && (connect_face >= 4)) {
+ static const u8 rot[33 * 4] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 4, 32, 16, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 4 - back
+ 8, 4, 32, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8 - right
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 16, 8, 4, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16 - front
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 32, 16, 8, 4 // 32 - left
+ };
+ return (f2.connect_sides & rot[(connect_face * 4) + to.param2]);
+ }
+ return (f2.connect_sides & connect_face);
+ }
+ // the target is just a regular node, so connect no matter back connection
+ return true;
+}
////
//// NodeResolver
diff --git a/src/nodedef.h b/src/nodedef.h
index a4a7b6e41..58d0faffa 100644
--- a/src/nodedef.h
+++ b/src/nodedef.h
@@ -63,6 +63,8 @@ enum ContentParamType2
CPT2_WALLMOUNTED,
// Block level like FLOWINGLIQUID
CPT2_LEVELED,
+ // 2D rotation for things like plants
+ CPT2_DEGROTATE,
};
enum LiquidType
@@ -78,6 +80,7 @@ enum NodeBoxType
NODEBOX_FIXED, // Static separately defined box(es)
NODEBOX_WALLMOUNTED, // Box for wall mounted nodes; (top, bottom, side)
NODEBOX_LEVELED, // Same as fixed, but with dynamic height from param2. for snow, ...
+ NODEBOX_CONNECTED, // optionally draws nodeboxes if a neighbor node attaches
};
struct NodeBox
@@ -90,6 +93,13 @@ struct NodeBox
aabb3f wall_top;
aabb3f wall_bottom;
aabb3f wall_side; // being at the -X side
+ // NODEBOX_CONNECTED
+ std::vector<aabb3f> connect_top;
+ std::vector<aabb3f> connect_bottom;
+ std::vector<aabb3f> connect_front;
+ std::vector<aabb3f> connect_left;
+ std::vector<aabb3f> connect_back;
+ std::vector<aabb3f> connect_right;
NodeBox()
{ reset(); }
@@ -102,6 +112,30 @@ struct NodeBox
struct MapNode;
class NodeMetadata;
+enum NodeDrawType
+{
+ NDT_NORMAL, // A basic solid block
+ NDT_AIRLIKE, // Nothing is drawn
+ NDT_LIQUID, // Do not draw face towards same kind of flowing/source liquid
+ NDT_FLOWINGLIQUID, // A very special kind of thing
+ NDT_GLASSLIKE, // Glass-like, don't draw faces towards other glass
+ NDT_ALLFACES, // Leaves-like, draw all faces no matter what
+ NDT_ALLFACES_OPTIONAL, // Fancy -> allfaces, fast -> normal
+ NDT_TORCHLIKE,
+ NDT_SIGNLIKE,
+ NDT_PLANTLIKE,
+ NDT_FENCELIKE,
+ NDT_RAILLIKE,
+ NDT_NODEBOX,
+ NDT_GLASSLIKE_FRAMED, // Glass-like, draw connected frames and all all
+ // visible faces
+ // uses 2 textures, one for frames, second for faces
+ NDT_FIRELIKE, // Draw faces slightly rotated and only on connecting nodes,
+ NDT_GLASSLIKE_FRAMED_OPTIONAL, // enabled -> connected, disabled -> Glass-like
+ // uses 2 textures, one for frames, second for faces
+ NDT_MESH, // Uses static meshes
+};
+
/*
Stand-alone definition of a TileSpec (basically a server-side TileSpec)
*/
@@ -135,31 +169,7 @@ struct TileDef
}
void serialize(std::ostream &os, u16 protocol_version) const;
- void deSerialize(std::istream &is);
-};
-
-enum NodeDrawType
-{
- NDT_NORMAL, // A basic solid block
- NDT_AIRLIKE, // Nothing is drawn
- NDT_LIQUID, // Do not draw face towards same kind of flowing/source liquid
- NDT_FLOWINGLIQUID, // A very special kind of thing
- NDT_GLASSLIKE, // Glass-like, don't draw faces towards other glass
- NDT_ALLFACES, // Leaves-like, draw all faces no matter what
- NDT_ALLFACES_OPTIONAL, // Fancy -> allfaces, fast -> normal
- NDT_TORCHLIKE,
- NDT_SIGNLIKE,
- NDT_PLANTLIKE,
- NDT_FENCELIKE,
- NDT_RAILLIKE,
- NDT_NODEBOX,
- NDT_GLASSLIKE_FRAMED, // Glass-like, draw connected frames and all all
- // visible faces
- // uses 2 textures, one for frames, second for faces
- NDT_FIRELIKE, // Draw faces slightly rotated and only on connecting nodes,
- NDT_GLASSLIKE_FRAMED_OPTIONAL, // enabled -> connected, disabled -> Glass-like
- // uses 2 textures, one for frames, second for faces
- NDT_MESH, // Uses static meshes
+ void deSerialize(std::istream &is, const u8 contentfeatures_version, const NodeDrawType drawtype);
};
#define CF_SPECIAL_COUNT 6
@@ -227,6 +237,8 @@ struct ContentFeatures
bool climbable;
// Player can build on these
bool buildable_to;
+ // Liquids flow into and replace node
+ bool floodable;
// Player cannot build to these (placement prediction disabled)
bool rightclickable;
// Flowing liquid or snow, value = default level
@@ -259,12 +271,17 @@ struct ContentFeatures
bool legacy_facedir_simple;
// Set to true if wall_mounted used to be set to true
bool legacy_wallmounted;
+ // for NDT_CONNECTED pairing
+ u8 connect_sides;
// Sound properties
SimpleSoundSpec sound_footstep;
SimpleSoundSpec sound_dig;
SimpleSoundSpec sound_dug;
+ std::vector<std::string> connects_to;
+ std::set<content_t> connects_to_ids;
+
/*
Methods
*/
@@ -299,7 +316,8 @@ public:
virtual bool getId(const std::string &name, content_t &result) const=0;
virtual content_t getId(const std::string &name) const=0;
// Allows "group:name" in addition to regular node names
- virtual void getIds(const std::string &name, std::set<content_t> &result)
+ // returns false if node name not found, true otherwise
+ virtual bool getIds(const std::string &name, std::set<content_t> &result)
const=0;
virtual const ContentFeatures &get(const std::string &name) const=0;
@@ -309,6 +327,7 @@ public:
virtual void pendNodeResolve(NodeResolver *nr)=0;
virtual bool cancelNodeResolveCallback(NodeResolver *nr)=0;
+ virtual bool nodeboxConnects(const MapNode from, const MapNode to, u8 connect_face)=0;
};
class IWritableNodeDefManager : public INodeDefManager {
@@ -323,7 +342,7 @@ public:
// If not found, returns CONTENT_IGNORE
virtual content_t getId(const std::string &name) const=0;
// Allows "group:name" in addition to regular node names
- virtual void getIds(const std::string &name, std::set<content_t> &result)
+ virtual bool getIds(const std::string &name, std::set<content_t> &result)
const=0;
// If not found, returns the features of CONTENT_UNKNOWN
virtual const ContentFeatures &get(const std::string &name) const=0;
@@ -363,6 +382,7 @@ public:
virtual bool cancelNodeResolveCallback(NodeResolver *nr)=0;
virtual void runNodeResolveCallbacks()=0;
virtual void resetNodeResolveState()=0;
+ virtual void mapNodeboxConnections()=0;
};
IWritableNodeDefManager *createNodeDefManager();
diff --git a/src/nodemetadata.cpp b/src/nodemetadata.cpp
index d4da7a5ed..126889ecf 100644
--- a/src/nodemetadata.cpp
+++ b/src/nodemetadata.cpp
@@ -30,9 +30,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
NodeMetadata
*/
-NodeMetadata::NodeMetadata(IGameDef *gamedef):
+NodeMetadata::NodeMetadata(IItemDefManager *item_def_mgr):
m_stringvars(),
- m_inventory(new Inventory(gamedef->idef()))
+ m_inventory(new Inventory(item_def_mgr))
{
}
@@ -96,39 +96,39 @@ void NodeMetadataList::serialize(std::ostream &os) const
for(std::map<v3s16, NodeMetadata*>::const_iterator
i = m_data.begin();
- i != m_data.end(); i++)
+ i != m_data.end(); ++i)
{
v3s16 p = i->first;
NodeMetadata *data = i->second;
- u16 p16 = p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X;
+ u16 p16 = p.Z * MAP_BLOCKSIZE * MAP_BLOCKSIZE + p.Y * MAP_BLOCKSIZE + p.X;
writeU16(os, p16);
data->serialize(os);
}
}
-void NodeMetadataList::deSerialize(std::istream &is, IGameDef *gamedef)
+void NodeMetadataList::deSerialize(std::istream &is, IItemDefManager *item_def_mgr)
{
clear();
u8 version = readU8(is);
- if(version == 0){
+ if (version == 0) {
// Nothing
return;
}
- if(version != 1){
- infostream<<__FUNCTION_NAME<<": version "<<version<<" not supported"
- <<std::endl;
- throw SerializationError("NodeMetadataList::deSerialize");
+ if (version != 1) {
+ std::string err_str = std::string(FUNCTION_NAME)
+ + ": version " + itos(version) + " not supported";
+ infostream << err_str << std::endl;
+ throw SerializationError(err_str);
}
u16 count = readU16(is);
- for(u16 i=0; i<count; i++)
- {
+ for (u16 i=0; i < count; i++) {
u16 p16 = readU16(is);
v3s16 p;
@@ -138,16 +138,15 @@ void NodeMetadataList::deSerialize(std::istream &is, IGameDef *gamedef)
p16 &= MAP_BLOCKSIZE - 1;
p.X = p16;
- if(m_data.find(p) != m_data.end())
- {
- infostream<<"WARNING: NodeMetadataList::deSerialize(): "
+ if (m_data.find(p) != m_data.end()) {
+ warningstream<<"NodeMetadataList::deSerialize(): "
<<"already set data at position"
<<"("<<p.X<<","<<p.Y<<","<<p.Z<<"): Ignoring."
<<std::endl;
continue;
}
- NodeMetadata *data = new NodeMetadata(gamedef);
+ NodeMetadata *data = new NodeMetadata(item_def_mgr);
data->deSerialize(is);
m_data[p] = data;
}
diff --git a/src/nodemetadata.h b/src/nodemetadata.h
index 8e84e5af3..8d1298212 100644
--- a/src/nodemetadata.h
+++ b/src/nodemetadata.h
@@ -35,12 +35,12 @@ with this program; if not, write to the Free Software Foundation, Inc.,
*/
class Inventory;
-class IGameDef;
+class IItemDefManager;
class NodeMetadata
{
public:
- NodeMetadata(IGameDef *gamedef);
+ NodeMetadata(IItemDefManager *item_def_mgr);
~NodeMetadata();
void serialize(std::ostream &os) const;
@@ -80,7 +80,7 @@ public:
~NodeMetadataList();
void serialize(std::ostream &os) const;
- void deSerialize(std::istream &is, IGameDef *gamedef);
+ void deSerialize(std::istream &is, IItemDefManager *item_def_mgr);
// Add all keys in this list to the vector keys
std::vector<v3s16> getAllKeys();
diff --git a/src/nodetimer.cpp b/src/nodetimer.cpp
index 790b46076..350940546 100644
--- a/src/nodetimer.cpp
+++ b/src/nodetimer.cpp
@@ -45,9 +45,9 @@ void NodeTimer::deSerialize(std::istream &is)
void NodeTimerList::serialize(std::ostream &os, u8 map_format_version) const
{
- if(map_format_version == 24){
+ if (map_format_version == 24) {
// Version 0 is a placeholder for "nothing to see here; go away."
- if(m_data.empty()){
+ if (m_data.empty()) {
writeU8(os, 0); // version
return;
}
@@ -55,18 +55,18 @@ void NodeTimerList::serialize(std::ostream &os, u8 map_format_version) const
writeU16(os, m_data.size());
}
- if(map_format_version >= 25){
- writeU8(os, 2+4+4);
+ if (map_format_version >= 25) {
+ writeU8(os, 2 + 4 + 4); // length of the data for a single timer
writeU16(os, m_data.size());
}
- for(std::map<v3s16, NodeTimer>::const_iterator
+ for (std::map<v3s16, NodeTimer>::const_iterator
i = m_data.begin();
- i != m_data.end(); i++){
+ i != m_data.end(); ++i) {
v3s16 p = i->first;
NodeTimer t = i->second;
- u16 p16 = p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X;
+ u16 p16 = p.Z * MAP_BLOCKSIZE * MAP_BLOCKSIZE + p.Y * MAP_BLOCKSIZE + p.X;
writeU16(os, p16);
t.serialize(os);
}
@@ -75,7 +75,7 @@ void NodeTimerList::serialize(std::ostream &os, u8 map_format_version) const
void NodeTimerList::deSerialize(std::istream &is, u8 map_format_version)
{
m_data.clear();
-
+
if(map_format_version == 24){
u8 timer_version = readU8(is);
if(timer_version == 0)
@@ -108,7 +108,7 @@ void NodeTimerList::deSerialize(std::istream &is, u8 map_format_version)
if(t.timeout <= 0)
{
- infostream<<"WARNING: NodeTimerList::deSerialize(): "
+ warningstream<<"NodeTimerList::deSerialize(): "
<<"invalid data at position"
<<"("<<p.X<<","<<p.Y<<","<<p.Z<<"): Ignoring."
<<std::endl;
@@ -117,7 +117,7 @@ void NodeTimerList::deSerialize(std::istream &is, u8 map_format_version)
if(m_data.find(p) != m_data.end())
{
- infostream<<"WARNING: NodeTimerList::deSerialize(): "
+ warningstream<<"NodeTimerList::deSerialize(): "
<<"already set data at position"
<<"("<<p.X<<","<<p.Y<<","<<p.Z<<"): Ignoring."
<<std::endl;
@@ -134,7 +134,7 @@ std::map<v3s16, NodeTimer> NodeTimerList::step(float dtime)
// Increment timers
for(std::map<v3s16, NodeTimer>::iterator
i = m_data.begin();
- i != m_data.end(); i++){
+ i != m_data.end(); ++i){
v3s16 p = i->first;
NodeTimer t = i->second;
t.elapsed += dtime;
@@ -146,7 +146,7 @@ std::map<v3s16, NodeTimer> NodeTimerList::step(float dtime)
// Delete elapsed timers
for(std::map<v3s16, NodeTimer>::const_iterator
i = elapsed_timers.begin();
- i != elapsed_timers.end(); i++){
+ i != elapsed_timers.end(); ++i){
v3s16 p = i->first;
m_data.erase(p);
}
diff --git a/src/noise.cpp b/src/noise.cpp
index 2948fb765..2ddc3926f 100644
--- a/src/noise.cpp
+++ b/src/noise.cpp
@@ -99,17 +99,17 @@ u32 PcgRandom::range(u32 bound)
Using rand() % 3, the number 0 would be twice as likely to appear.
With a very large RNG range, the effect becomes less prevalent but
still present. This can be solved by modifying the range of the RNG
- to become a multiple of bound by dropping values above the a threshhold.
- In our example, threshhold == 4 - 3 = 1 % 3 == 1, so reject 0, thus
+ to become a multiple of bound by dropping values above the a threshold.
+ In our example, threshold == 4 - 3 = 1 % 3 == 1, so reject 0, thus
making the range 3 with no bias.
This loop looks dangerous, but will always terminate due to the
RNG's property of uniformity.
*/
- u32 threshhold = -bound % bound;
+ u32 threshold = -bound % bound;
u32 r;
- while ((r = next()) < threshhold)
+ while ((r = next()) < threshold)
;
return r % bound;
@@ -158,21 +158,21 @@ s32 PcgRandom::randNormalDist(s32 min, s32 max, int num_trials)
float noise2d(int x, int y, int seed)
{
- int n = (NOISE_MAGIC_X * x + NOISE_MAGIC_Y * y
+ unsigned int n = (NOISE_MAGIC_X * x + NOISE_MAGIC_Y * y
+ NOISE_MAGIC_SEED * seed) & 0x7fffffff;
n = (n >> 13) ^ n;
n = (n * (n * n * 60493 + 19990303) + 1376312589) & 0x7fffffff;
- return 1.f - (float)n / 0x40000000;
+ return 1.f - (float)(int)n / 0x40000000;
}
float noise3d(int x, int y, int z, int seed)
{
- int n = (NOISE_MAGIC_X * x + NOISE_MAGIC_Y * y + NOISE_MAGIC_Z * z
+ unsigned int n = (NOISE_MAGIC_X * x + NOISE_MAGIC_Y * y + NOISE_MAGIC_Z * z
+ NOISE_MAGIC_SEED * seed) & 0x7fffffff;
n = (n >> 13) ^ n;
n = (n * (n * n * 60493 + 19990303) + 1376312589) & 0x7fffffff;
- return 1.f - (float)n / 0x40000000;
+ return 1.f - (float)(int)n / 0x40000000;
}
diff --git a/src/objdef.h b/src/objdef.h
index 65e5c0176..77189e454 100644
--- a/src/objdef.h
+++ b/src/objdef.h
@@ -20,6 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#ifndef OBJDEF_HEADER
#define OBJDEF_HEADER
+#include "util/basic_macros.h"
#include "porting.h"
class IGameDef;
@@ -90,6 +91,9 @@ protected:
INodeDefManager *m_ndef;
std::vector<ObjDef *> m_objects;
ObjDefType m_objtype;
+
+private:
+ DISABLE_CLASS_COPY(ObjDefManager);
};
#endif
diff --git a/src/object_properties.cpp b/src/object_properties.cpp
index f560f5934..89ca26274 100644
--- a/src/object_properties.cpp
+++ b/src/object_properties.cpp
@@ -42,7 +42,11 @@ ObjectProperties::ObjectProperties():
automatic_rotate(0),
stepheight(0),
automatic_face_movement_dir(false),
- automatic_face_movement_dir_offset(0.0)
+ automatic_face_movement_dir_offset(0.0),
+ backface_culling(true),
+ nametag(""),
+ nametag_color(255, 255, 255, 255),
+ automatic_face_movement_max_rotation_per_sec(-1)
{
textures.push_back("unknown_object.png");
colors.push_back(video::SColor(255,255,255,255));
@@ -74,6 +78,10 @@ std::string ObjectProperties::dump()
os<<", is_visible="<<is_visible;
os<<", makes_footstep_sound="<<makes_footstep_sound;
os<<", automatic_rotate="<<automatic_rotate;
+ os<<", backface_culling="<<backface_culling;
+ os << ", nametag=" << nametag;
+ os << ", nametag_color=" << "\"" << nametag_color.getAlpha() << "," << nametag_color.getRed()
+ << "," << nametag_color.getGreen() << "," << nametag_color.getBlue() << "\" ";
return os.str();
}
@@ -106,6 +114,12 @@ void ObjectProperties::serialize(std::ostream &os) const
writeF1000(os,stepheight);
writeU8(os, automatic_face_movement_dir);
writeF1000(os, automatic_face_movement_dir_offset);
+ writeU8(os, backface_culling);
+ os << serializeString(nametag);
+ writeARGB8(os, nametag_color);
+ writeF1000(os, automatic_face_movement_max_rotation_per_sec);
+ os << serializeString(infotext);
+
// Add stuff only at the bottom.
// Never remove anything, because we don't want new versions of this
}
@@ -142,6 +156,11 @@ void ObjectProperties::deSerialize(std::istream &is)
stepheight = readF1000(is);
automatic_face_movement_dir = readU8(is);
automatic_face_movement_dir_offset = readF1000(is);
+ backface_culling = readU8(is);
+ nametag = deSerializeString(is);
+ nametag_color = readARGB8(is);
+ automatic_face_movement_max_rotation_per_sec = readF1000(is);
+ infotext = deSerializeString(is);
}catch(SerializationError &e){}
}
else
@@ -149,4 +168,3 @@ void ObjectProperties::deSerialize(std::istream &is)
throw SerializationError("unsupported ObjectProperties version");
}
}
-
diff --git a/src/object_properties.h b/src/object_properties.h
index 4b7f9a5eb..082d9a529 100644
--- a/src/object_properties.h
+++ b/src/object_properties.h
@@ -33,7 +33,7 @@ struct ObjectProperties
bool physical;
bool collideWithObjects;
float weight;
- core::aabbox3d<f32> collisionbox;
+ aabb3f collisionbox;
std::string visual;
std::string mesh;
v2f visual_size;
@@ -47,7 +47,11 @@ struct ObjectProperties
f32 stepheight;
bool automatic_face_movement_dir;
f32 automatic_face_movement_dir_offset;
-
+ bool backface_culling;
+ std::string nametag;
+ video::SColor nametag_color;
+ f32 automatic_face_movement_max_rotation_per_sec;
+ std::string infotext;
ObjectProperties();
std::string dump();
@@ -56,4 +60,3 @@ struct ObjectProperties
};
#endif
-
diff --git a/src/particles.cpp b/src/particles.cpp
index 15e2a6597..525258a25 100644
--- a/src/particles.cpp
+++ b/src/particles.cpp
@@ -88,7 +88,7 @@ Particle::Particle(
m_vertical = vertical;
// Irrlicht stuff
- m_collisionbox = core::aabbox3d<f32>
+ m_collisionbox = aabb3f
(-size/2,-size/2,-size/2,size/2,size/2,size/2);
this->setAutomaticCulling(scene::EAC_OFF);
@@ -128,17 +128,15 @@ void Particle::step(float dtime)
m_time += dtime;
if (m_collisiondetection)
{
- core::aabbox3d<f32> box = m_collisionbox;
+ aabb3f box = m_collisionbox;
v3f p_pos = m_pos*BS;
v3f p_velocity = m_velocity*BS;
- v3f p_acceleration = m_acceleration*BS;
collisionMoveSimple(m_env, m_gamedef,
BS*0.5, box,
0, dtime,
- p_pos, p_velocity, p_acceleration);
+ &p_pos, &p_velocity, m_acceleration * BS);
m_pos = p_pos/BS;
m_velocity = p_velocity/BS;
- m_acceleration = p_acceleration/BS;
}
else
{
@@ -288,7 +286,7 @@ void ParticleSpawner::step(float dtime, ClientEnvironment* env)
}
else
{
- i++;
+ ++i;
}
}
}
@@ -347,8 +345,8 @@ void ParticleManager::step(float dtime)
void ParticleManager::stepSpawners (float dtime)
{
- JMutexAutoLock lock(m_spawner_list_lock);
- for(std::map<u32, ParticleSpawner*>::iterator i =
+ MutexAutoLock lock(m_spawner_list_lock);
+ for (std::map<u32, ParticleSpawner*>::iterator i =
m_particle_spawners.begin();
i != m_particle_spawners.end();)
{
@@ -360,14 +358,14 @@ void ParticleManager::stepSpawners (float dtime)
else
{
i->second->step(dtime, m_env);
- i++;
+ ++i;
}
}
}
void ParticleManager::stepParticles (float dtime)
{
- JMutexAutoLock lock(m_particle_list_lock);
+ MutexAutoLock lock(m_particle_list_lock);
for(std::vector<Particle*>::iterator i = m_particles.begin();
i != m_particles.end();)
{
@@ -380,15 +378,15 @@ void ParticleManager::stepParticles (float dtime)
else
{
(*i)->step(dtime);
- i++;
+ ++i;
}
}
}
void ParticleManager::clearAll ()
{
- JMutexAutoLock lock(m_spawner_list_lock);
- JMutexAutoLock lock2(m_particle_list_lock);
+ MutexAutoLock lock(m_spawner_list_lock);
+ MutexAutoLock lock2(m_particle_list_lock);
for(std::map<u32, ParticleSpawner*>::iterator i =
m_particle_spawners.begin();
i != m_particle_spawners.end();)
@@ -410,94 +408,92 @@ void ParticleManager::clearAll ()
void ParticleManager::handleParticleEvent(ClientEvent *event, IGameDef *gamedef,
scene::ISceneManager* smgr, LocalPlayer *player)
{
- if (event->type == CE_DELETE_PARTICLESPAWNER) {
- JMutexAutoLock lock(m_spawner_list_lock);
- if (m_particle_spawners.find(event->delete_particlespawner.id) !=
- m_particle_spawners.end())
- {
- delete m_particle_spawners.find(event->delete_particlespawner.id)->second;
- m_particle_spawners.erase(event->delete_particlespawner.id);
+ switch (event->type) {
+ case CE_DELETE_PARTICLESPAWNER: {
+ MutexAutoLock lock(m_spawner_list_lock);
+ if (m_particle_spawners.find(event->delete_particlespawner.id) !=
+ m_particle_spawners.end()) {
+ delete m_particle_spawners.find(event->delete_particlespawner.id)->second;
+ m_particle_spawners.erase(event->delete_particlespawner.id);
+ }
+ // no allocated memory in delete event
+ break;
}
- // no allocated memory in delete event
- return;
- }
+ case CE_ADD_PARTICLESPAWNER: {
+ {
+ MutexAutoLock lock(m_spawner_list_lock);
+ if (m_particle_spawners.find(event->add_particlespawner.id) !=
+ m_particle_spawners.end()) {
+ delete m_particle_spawners.find(event->add_particlespawner.id)->second;
+ m_particle_spawners.erase(event->add_particlespawner.id);
+ }
+ }
- if (event->type == CE_ADD_PARTICLESPAWNER) {
+ video::ITexture *texture =
+ gamedef->tsrc()->getTextureForMesh(*(event->add_particlespawner.texture));
+
+ ParticleSpawner* toadd = new ParticleSpawner(gamedef, smgr, player,
+ event->add_particlespawner.amount,
+ event->add_particlespawner.spawntime,
+ *event->add_particlespawner.minpos,
+ *event->add_particlespawner.maxpos,
+ *event->add_particlespawner.minvel,
+ *event->add_particlespawner.maxvel,
+ *event->add_particlespawner.minacc,
+ *event->add_particlespawner.maxacc,
+ event->add_particlespawner.minexptime,
+ event->add_particlespawner.maxexptime,
+ event->add_particlespawner.minsize,
+ event->add_particlespawner.maxsize,
+ event->add_particlespawner.collisiondetection,
+ event->add_particlespawner.vertical,
+ texture,
+ event->add_particlespawner.id,
+ this);
+
+ /* delete allocated content of event */
+ delete event->add_particlespawner.minpos;
+ delete event->add_particlespawner.maxpos;
+ delete event->add_particlespawner.minvel;
+ delete event->add_particlespawner.maxvel;
+ delete event->add_particlespawner.minacc;
+ delete event->add_particlespawner.texture;
+ delete event->add_particlespawner.maxacc;
- {
- JMutexAutoLock lock(m_spawner_list_lock);
- if (m_particle_spawners.find(event->add_particlespawner.id) !=
- m_particle_spawners.end())
{
- delete m_particle_spawners.find(event->add_particlespawner.id)->second;
- m_particle_spawners.erase(event->add_particlespawner.id);
+ MutexAutoLock lock(m_spawner_list_lock);
+ m_particle_spawners.insert(
+ std::pair<u32, ParticleSpawner*>(
+ event->add_particlespawner.id,
+ toadd));
}
+ break;
}
- video::ITexture *texture =
- gamedef->tsrc()->getTextureForMesh(*(event->add_particlespawner.texture));
-
- ParticleSpawner* toadd = new ParticleSpawner(gamedef, smgr, player,
- event->add_particlespawner.amount,
- event->add_particlespawner.spawntime,
- *event->add_particlespawner.minpos,
- *event->add_particlespawner.maxpos,
- *event->add_particlespawner.minvel,
- *event->add_particlespawner.maxvel,
- *event->add_particlespawner.minacc,
- *event->add_particlespawner.maxacc,
- event->add_particlespawner.minexptime,
- event->add_particlespawner.maxexptime,
- event->add_particlespawner.minsize,
- event->add_particlespawner.maxsize,
- event->add_particlespawner.collisiondetection,
- event->add_particlespawner.vertical,
- texture,
- event->add_particlespawner.id,
- this);
-
- /* delete allocated content of event */
- delete event->add_particlespawner.minpos;
- delete event->add_particlespawner.maxpos;
- delete event->add_particlespawner.minvel;
- delete event->add_particlespawner.maxvel;
- delete event->add_particlespawner.minacc;
- delete event->add_particlespawner.texture;
- delete event->add_particlespawner.maxacc;
+ case CE_SPAWN_PARTICLE: {
+ video::ITexture *texture =
+ gamedef->tsrc()->getTextureForMesh(*(event->spawn_particle.texture));
+
+ Particle* toadd = new Particle(gamedef, smgr, player, m_env,
+ *event->spawn_particle.pos,
+ *event->spawn_particle.vel,
+ *event->spawn_particle.acc,
+ event->spawn_particle.expirationtime,
+ event->spawn_particle.size,
+ event->spawn_particle.collisiondetection,
+ event->spawn_particle.vertical,
+ texture,
+ v2f(0.0, 0.0),
+ v2f(1.0, 1.0));
- {
- JMutexAutoLock lock(m_spawner_list_lock);
- m_particle_spawners.insert(
- std::pair<u32, ParticleSpawner*>(
- event->add_particlespawner.id,
- toadd));
- }
+ addParticle(toadd);
- return;
- }
+ delete event->spawn_particle.pos;
+ delete event->spawn_particle.vel;
+ delete event->spawn_particle.acc;
- if (event->type == CE_SPAWN_PARTICLE) {
- video::ITexture *texture =
- gamedef->tsrc()->getTextureForMesh(*(event->spawn_particle.texture));
-
- Particle* toadd = new Particle(gamedef, smgr, player, m_env,
- *event->spawn_particle.pos,
- *event->spawn_particle.vel,
- *event->spawn_particle.acc,
- event->spawn_particle.expirationtime,
- event->spawn_particle.size,
- event->spawn_particle.collisiondetection,
- event->spawn_particle.vertical,
- texture,
- v2f(0.0, 0.0),
- v2f(1.0, 1.0));
-
- addParticle(toadd);
-
- delete event->spawn_particle.pos;
- delete event->spawn_particle.vel;
- delete event->spawn_particle.acc;
-
- return;
+ break;
+ }
+ default: break;
}
}
@@ -568,6 +564,6 @@ void ParticleManager::addNodeParticle(IGameDef* gamedef, scene::ISceneManager* s
void ParticleManager::addParticle(Particle* toadd)
{
- JMutexAutoLock lock(m_particle_list_lock);
+ MutexAutoLock lock(m_particle_list_lock);
m_particles.push_back(toadd);
}
diff --git a/src/particles.h b/src/particles.h
index 2bc2e7bfa..dda84385c 100644
--- a/src/particles.h
+++ b/src/particles.h
@@ -52,7 +52,7 @@ class Particle : public scene::ISceneNode
);
~Particle();
- virtual const core::aabbox3d<f32>& getBoundingBox() const
+ virtual const aabb3f &getBoundingBox() const
{
return m_box;
}
@@ -85,8 +85,8 @@ private:
ClientEnvironment *m_env;
IGameDef *m_gamedef;
- core::aabbox3d<f32> m_box;
- core::aabbox3d<f32> m_collisionbox;
+ aabb3f m_box;
+ aabb3f m_collisionbox;
video::SMaterial m_material;
v2f m_texpos;
v2f m_texsize;
@@ -190,8 +190,8 @@ private:
std::map<u32, ParticleSpawner*> m_particle_spawners;
ClientEnvironment* m_env;
- JMutex m_particle_list_lock;
- JMutex m_spawner_list_lock;
+ Mutex m_particle_list_lock;
+ Mutex m_spawner_list_lock;
};
#endif
diff --git a/src/pathfinder.cpp b/src/pathfinder.cpp
index 8eb52bfd1..57a008ead 100644
--- a/src/pathfinder.cpp
+++ b/src/pathfinder.cpp
@@ -1,6 +1,7 @@
/*
Minetest
Copyright (C) 2013 sapier, sapier at gmx dot net
+Copyright (C) 2016 est31, <MTest31@outlook.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
@@ -23,11 +24,20 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "pathfinder.h"
#include "environment.h"
+#include "gamedef.h"
+#include "nodedef.h"
#include "map.h"
#include "log.h"
+#include "irr_aabb3d.h"
+//#define PATHFINDER_DEBUG
+//#define PATHFINDER_CALC_TIME
+
+#ifdef PATHFINDER_DEBUG
+ #include <string>
+#endif
#ifdef PATHFINDER_DEBUG
-#include <iomanip>
+ #include <iomanip>
#endif
#ifdef PATHFINDER_CALC_TIME
#include <sys/time.h>
@@ -37,8 +47,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
/* Typedefs and macros */
/******************************************************************************/
-//#define PATHFINDER_CALC_TIME
-
/** shortcut to print a 3d pos */
#define PPOS(pos) "(" << pos.X << "," << pos.Y << "," << pos.Z << ")"
@@ -51,32 +59,349 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#define ERROR_TARGET std::cout
#else
#define DEBUG_OUT(a) while(0)
-#define INFO_TARGET infostream << "pathfinder: "
-#define VERBOSE_TARGET verbosestream << "pathfinder: "
-#define ERROR_TARGET errorstream << "pathfinder: "
+#define INFO_TARGET infostream << "Pathfinder: "
+#define VERBOSE_TARGET verbosestream << "Pathfinder: "
+#define ERROR_TARGET errorstream << "Pathfinder: "
+#endif
+
+/******************************************************************************/
+/* Class definitions */
+/******************************************************************************/
+
+
+/** representation of cost in specific direction */
+class PathCost {
+public:
+
+ /** default constructor */
+ PathCost();
+
+ /** copy constructor */
+ PathCost(const PathCost &b);
+
+ /** assignment operator */
+ PathCost &operator= (const PathCost &b);
+
+ bool valid; /**< movement is possible */
+ int value; /**< cost of movement */
+ int direction; /**< y-direction of movement */
+ bool updated; /**< this cost has ben calculated */
+
+};
+
+
+/** representation of a mapnode to be used for pathfinding */
+class PathGridnode {
+
+public:
+ /** default constructor */
+ PathGridnode();
+
+ /** copy constructor */
+ PathGridnode(const PathGridnode &b);
+
+ /**
+ * assignment operator
+ * @param b node to copy
+ */
+ PathGridnode &operator= (const PathGridnode &b);
+
+ /**
+ * read cost in a specific direction
+ * @param dir direction of cost to fetch
+ */
+ PathCost getCost(v3s16 dir);
+
+ /**
+ * set cost value for movement
+ * @param dir direction to set cost for
+ * @cost cost to set
+ */
+ void setCost(v3s16 dir, PathCost cost);
+
+ bool valid; /**< node is on surface */
+ bool target; /**< node is target position */
+ bool source; /**< node is stating position */
+ int totalcost; /**< cost to move here from starting point */
+ v3s16 sourcedir; /**< origin of movement for current cost */
+ v3s16 pos; /**< real position of node */
+ PathCost directions[4]; /**< cost in different directions */
+
+ /* debug values */
+ bool is_element; /**< node is element of path detected */
+ char type; /**< type of node */
+};
+
+class Pathfinder;
+
+/** Abstract class to manage the map data */
+class GridNodeContainer {
+public:
+ virtual PathGridnode &access(v3s16 p)=0;
+ virtual ~GridNodeContainer() {}
+protected:
+ Pathfinder *m_pathf;
+
+ void initNode(v3s16 ipos, PathGridnode *p_node);
+};
+
+class ArrayGridNodeContainer : public GridNodeContainer {
+public:
+ virtual ~ArrayGridNodeContainer() {}
+ ArrayGridNodeContainer(Pathfinder *pathf, v3s16 dimensions);
+ virtual PathGridnode &access(v3s16 p);
+private:
+ v3s16 m_dimensions;
+
+ int m_x_stride;
+ int m_y_stride;
+ std::vector<PathGridnode> m_nodes_array;
+};
+
+class MapGridNodeContainer : public GridNodeContainer {
+public:
+ virtual ~MapGridNodeContainer() {}
+ MapGridNodeContainer(Pathfinder *pathf);
+ virtual PathGridnode &access(v3s16 p);
+private:
+ std::map<v3s16, PathGridnode> m_nodes;
+};
+
+/** class doing pathfinding */
+class Pathfinder {
+
+public:
+ /**
+ * default constructor
+ */
+ Pathfinder();
+
+ ~Pathfinder();
+
+ /**
+ * path evaluation function
+ * @param env environment to look for path
+ * @param source origin of path
+ * @param destination end position of path
+ * @param searchdistance maximum number of nodes to look in each direction
+ * @param max_jump maximum number of blocks a path may jump up
+ * @param max_drop maximum number of blocks a path may drop
+ * @param algo Algorithm to use for finding a path
+ */
+ std::vector<v3s16> getPath(ServerEnvironment *env,
+ v3s16 source,
+ v3s16 destination,
+ unsigned int searchdistance,
+ unsigned int max_jump,
+ unsigned int max_drop,
+ PathAlgorithm algo);
+
+private:
+ /* helper functions */
+
+ /**
+ * transform index pos to mappos
+ * @param ipos a index position
+ * @return map position
+ */
+ v3s16 getRealPos(v3s16 ipos);
+
+ /**
+ * transform mappos to index pos
+ * @param pos a real pos
+ * @return index position
+ */
+ v3s16 getIndexPos(v3s16 pos);
+
+ /**
+ * get gridnode at a specific index position
+ * @param ipos index position
+ * @return gridnode for index
+ */
+ PathGridnode &getIndexElement(v3s16 ipos);
+
+ /**
+ * Get gridnode at a specific index position
+ * @return gridnode for index
+ */
+ PathGridnode &getIdxElem(s16 x, s16 y, s16 z);
+
+ /**
+ * invert a 3d position
+ * @param pos 3d position
+ * @return pos *-1
+ */
+ v3s16 invert(v3s16 pos);
+
+ /**
+ * check if a index is within current search area
+ * @param index position to validate
+ * @return true/false
+ */
+ bool isValidIndex(v3s16 index);
+
+ /**
+ * translate position to float position
+ * @param pos integer position
+ * @return float position
+ */
+ v3f tov3f(v3s16 pos);
+
+
+ /* algorithm functions */
+
+ /**
+ * calculate 2d manahttan distance to target on the xz plane
+ * @param pos position to calc distance
+ * @return integer distance
+ */
+ int getXZManhattanDist(v3s16 pos);
+
+ /**
+ * get best direction based uppon heuristics
+ * @param directions list of unchecked directions
+ * @param g_pos mapnode to start from
+ * @return direction to check
+ */
+ v3s16 getDirHeuristic(std::vector<v3s16> &directions, PathGridnode &g_pos);
+
+ /**
+ * build internal data representation of search area
+ * @return true/false if costmap creation was successfull
+ */
+ bool buildCostmap();
+
+ /**
+ * calculate cost of movement
+ * @param pos real world position to start movement
+ * @param dir direction to move to
+ * @return cost information
+ */
+ PathCost calcCost(v3s16 pos, v3s16 dir);
+
+ /**
+ * recursive update whole search areas total cost information
+ * @param ipos position to check next
+ * @param srcdir positionc checked last time
+ * @param total_cost cost of moving to ipos
+ * @param level current recursion depth
+ * @return true/false path to destination has been found
+ */
+ bool updateAllCosts(v3s16 ipos, v3s16 srcdir, int total_cost, int level);
+
+ /**
+ * recursive try to find a patrh to destionation
+ * @param ipos position to check next
+ * @param srcdir positionc checked last time
+ * @param total_cost cost of moving to ipos
+ * @param level current recursion depth
+ * @return true/false path to destination has been found
+ */
+ bool updateCostHeuristic(v3s16 ipos, v3s16 srcdir, int current_cost, int level);
+
+ /**
+ * recursive build a vector containing all nodes from source to destination
+ * @param path vector to add nodes to
+ * @param pos pos to check next
+ * @param level recursion depth
+ */
+ void buildPath(std::vector<v3s16> &path, v3s16 pos, int level);
+
+ /* variables */
+ int m_max_index_x; /**< max index of search area in x direction */
+ int m_max_index_y; /**< max index of search area in y direction */
+ int m_max_index_z; /**< max index of search area in z direction */
+
+
+ int m_searchdistance; /**< max distance to search in each direction */
+ int m_maxdrop; /**< maximum number of blocks a path may drop */
+ int m_maxjump; /**< maximum number of blocks a path may jump */
+ int m_min_target_distance; /**< current smalest path to target */
+
+ bool m_prefetch; /**< prefetch cost data */
+
+ v3s16 m_start; /**< source position */
+ v3s16 m_destination; /**< destination position */
+
+ core::aabbox3d<s16> m_limits; /**< position limits in real map coordinates */
+
+ /** contains all map data already collected and analyzed.
+ Access it via the getIndexElement/getIdxElem methods. */
+ friend class GridNodeContainer;
+ GridNodeContainer *m_nodes_container;
+
+ ServerEnvironment *m_env; /**< minetest environment pointer */
+
+#ifdef PATHFINDER_DEBUG
+
+ /**
+ * print collected cost information
+ */
+ void printCost();
+
+ /**
+ * print collected cost information in a specific direction
+ * @param dir direction to print
+ */
+ void printCost(PathDirections dir);
+
+ /**
+ * print type of node as evaluated
+ */
+ void printType();
+
+ /**
+ * print pathlenght for all nodes in search area
+ */
+ void printPathLen();
+
+ /**
+ * print a path
+ * @param path path to show
+ */
+ void printPath(std::vector<v3s16> path);
+
+ /**
+ * print y direction for all movements
+ */
+ void printYdir();
+
+ /**
+ * print y direction for moving in a specific direction
+ * @param dir direction to show data
+ */
+ void printYdir(PathDirections dir);
+
+ /**
+ * helper function to translate a direction to speaking text
+ * @param dir direction to translate
+ * @return textual name of direction
+ */
+ std::string dirToName(PathDirections dir);
#endif
+};
/******************************************************************************/
/* implementation */
/******************************************************************************/
-std::vector<v3s16> get_Path(ServerEnvironment* env,
+std::vector<v3s16> get_path(ServerEnvironment* env,
v3s16 source,
v3s16 destination,
unsigned int searchdistance,
unsigned int max_jump,
unsigned int max_drop,
- algorithm algo) {
-
- pathfinder searchclass;
+ PathAlgorithm algo)
+{
+ Pathfinder searchclass;
- return searchclass.get_Path(env,
- source,destination,
- searchdistance,max_jump,max_drop,algo);
+ return searchclass.getPath(env,
+ source, destination,
+ searchdistance, max_jump, max_drop, algo);
}
/******************************************************************************/
-path_cost::path_cost()
+PathCost::PathCost()
: valid(false),
value(0),
direction(0),
@@ -86,7 +411,8 @@ path_cost::path_cost()
}
/******************************************************************************/
-path_cost::path_cost(const path_cost& b) {
+PathCost::PathCost(const PathCost &b)
+{
valid = b.valid;
direction = b.direction;
value = b.value;
@@ -94,7 +420,8 @@ path_cost::path_cost(const path_cost& b) {
}
/******************************************************************************/
-path_cost& path_cost::operator= (const path_cost& b) {
+PathCost &PathCost::operator= (const PathCost &b)
+{
valid = b.valid;
direction = b.direction;
value = b.value;
@@ -104,14 +431,13 @@ path_cost& path_cost::operator= (const path_cost& b) {
}
/******************************************************************************/
-path_gridnode::path_gridnode()
+PathGridnode::PathGridnode()
: valid(false),
target(false),
source(false),
totalcost(-1),
- sourcedir(v3s16(0,0,0)),
- surfaces(0),
- pos(v3s16(0,0,0)),
+ sourcedir(v3s16(0, 0, 0)),
+ pos(v3s16(0, 0, 0)),
is_element(false),
type('u')
{
@@ -119,17 +445,16 @@ path_gridnode::path_gridnode()
}
/******************************************************************************/
-path_gridnode::path_gridnode(const path_gridnode& b)
+PathGridnode::PathGridnode(const PathGridnode &b)
: valid(b.valid),
target(b.target),
source(b.source),
totalcost(b.totalcost),
sourcedir(b.sourcedir),
- surfaces(b.surfaces),
pos(b.pos),
is_element(b.is_element),
type(b.type)
- {
+{
directions[DIR_XP] = b.directions[DIR_XP];
directions[DIR_XM] = b.directions[DIR_XM];
@@ -138,14 +463,14 @@ path_gridnode::path_gridnode(const path_gridnode& b)
}
/******************************************************************************/
-path_gridnode& path_gridnode::operator= (const path_gridnode& b) {
+PathGridnode &PathGridnode::operator= (const PathGridnode &b)
+{
valid = b.valid;
target = b.target;
source = b.source;
is_element = b.is_element;
totalcost = b.totalcost;
sourcedir = b.sourcedir;
- surfaces = b.surfaces;
pos = b.pos;
type = b.type;
@@ -158,7 +483,8 @@ path_gridnode& path_gridnode::operator= (const path_gridnode& b) {
}
/******************************************************************************/
-path_cost path_gridnode::get_cost(v3s16 dir) {
+PathCost PathGridnode::getCost(v3s16 dir)
+{
if (dir.X > 0) {
return directions[DIR_XP];
}
@@ -171,12 +497,13 @@ path_cost path_gridnode::get_cost(v3s16 dir) {
if (dir.Z < 0) {
return directions[DIR_ZM];
}
- path_cost retval;
+ PathCost retval;
return retval;
}
/******************************************************************************/
-void path_gridnode::set_cost(v3s16 dir,path_cost cost) {
+void PathGridnode::setCost(v3s16 dir, PathCost cost)
+{
if (dir.X > 0) {
directions[DIR_XP] = cost;
}
@@ -191,14 +518,105 @@ void path_gridnode::set_cost(v3s16 dir,path_cost cost) {
}
}
+void GridNodeContainer::initNode(v3s16 ipos, PathGridnode *p_node)
+{
+ INodeDefManager *ndef = m_pathf->m_env->getGameDef()->ndef();
+ PathGridnode &elem = *p_node;
+
+ v3s16 realpos = m_pathf->getRealPos(ipos);
+
+ MapNode current = m_pathf->m_env->getMap().getNodeNoEx(realpos);
+ MapNode below = m_pathf->m_env->getMap().getNodeNoEx(realpos + v3s16(0, -1, 0));
+
+
+ if ((current.param0 == CONTENT_IGNORE) ||
+ (below.param0 == CONTENT_IGNORE)) {
+ DEBUG_OUT("Pathfinder: " << PPOS(realpos) <<
+ " current or below is invalid element" << std::endl);
+ if (current.param0 == CONTENT_IGNORE) {
+ elem.type = 'i';
+ DEBUG_OUT(PPOS(ipos) << ": " << 'i' << std::endl);
+ }
+ return;
+ }
+
+ //don't add anything if it isn't an air node
+ if (ndef->get(current).walkable || !ndef->get(below).walkable) {
+ DEBUG_OUT("Pathfinder: " << PPOS(realpos)
+ << " not on surface" << std::endl);
+ if (ndef->get(current).walkable) {
+ elem.type = 's';
+ DEBUG_OUT(PPOS(ipos) << ": " << 's' << std::endl);
+ } else {
+ elem.type = '-';
+ DEBUG_OUT(PPOS(ipos) << ": " << '-' << std::endl);
+ }
+ return;
+ }
+
+ elem.valid = true;
+ elem.pos = realpos;
+ elem.type = 'g';
+ DEBUG_OUT(PPOS(ipos) << ": " << 'a' << std::endl);
+
+ if (m_pathf->m_prefetch) {
+ elem.directions[DIR_XP] = m_pathf->calcCost(realpos, v3s16( 1, 0, 0));
+ elem.directions[DIR_XM] = m_pathf->calcCost(realpos, v3s16(-1, 0, 0));
+ elem.directions[DIR_ZP] = m_pathf->calcCost(realpos, v3s16( 0, 0, 1));
+ elem.directions[DIR_ZM] = m_pathf->calcCost(realpos, v3s16( 0, 0,-1));
+ }
+}
+
+ArrayGridNodeContainer::ArrayGridNodeContainer(Pathfinder *pathf, v3s16 dimensions) :
+ m_x_stride(dimensions.Y * dimensions.Z),
+ m_y_stride(dimensions.Z)
+{
+ m_pathf = pathf;
+
+ m_nodes_array.resize(dimensions.X * dimensions.Y * dimensions.Z);
+ INFO_TARGET << "Pathfinder ArrayGridNodeContainer constructor." << std::endl;
+ for (int x = 0; x < dimensions.X; x++) {
+ for (int y = 0; y < dimensions.Y; y++) {
+ for (int z= 0; z < dimensions.Z; z++) {
+ v3s16 ipos(x, y, z);
+ initNode(ipos, &access(ipos));
+ }
+ }
+ }
+}
+
+PathGridnode &ArrayGridNodeContainer::access(v3s16 p)
+{
+ return m_nodes_array[p.X * m_x_stride + p.Y * m_y_stride + p.Z];
+}
+
+MapGridNodeContainer::MapGridNodeContainer(Pathfinder *pathf)
+{
+ m_pathf = pathf;
+}
+
+PathGridnode &MapGridNodeContainer::access(v3s16 p)
+{
+ std::map<v3s16, PathGridnode>::iterator it = m_nodes.find(p);
+ if (it != m_nodes.end()) {
+ return it->second;
+ }
+ PathGridnode &n = m_nodes[p];
+ initNode(p, &n);
+ return n;
+}
+
+
+
/******************************************************************************/
-std::vector<v3s16> pathfinder::get_Path(ServerEnvironment* env,
+std::vector<v3s16> Pathfinder::getPath(ServerEnvironment *env,
v3s16 source,
v3s16 destination,
unsigned int searchdistance,
unsigned int max_jump,
unsigned int max_drop,
- algorithm algo) {
+ PathAlgorithm algo)
+{
#ifdef PATHFINDER_CALC_TIME
timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
@@ -220,47 +638,51 @@ std::vector<v3s16> pathfinder::get_Path(ServerEnvironment* env,
m_min_target_distance = -1;
m_prefetch = true;
- if (algo == A_PLAIN_NP) {
+ if (algo == PA_PLAIN_NP) {
m_prefetch = false;
}
- int min_x = MYMIN(source.X,destination.X);
- int max_x = MYMAX(source.X,destination.X);
+ int min_x = MYMIN(source.X, destination.X);
+ int max_x = MYMAX(source.X, destination.X);
- int min_y = MYMIN(source.Y,destination.Y);
- int max_y = MYMAX(source.Y,destination.Y);
+ int min_y = MYMIN(source.Y, destination.Y);
+ int max_y = MYMAX(source.Y, destination.Y);
- int min_z = MYMIN(source.Z,destination.Z);
- int max_z = MYMAX(source.Z,destination.Z);
+ int min_z = MYMIN(source.Z, destination.Z);
+ int max_z = MYMAX(source.Z, destination.Z);
- m_limits.X.min = min_x - searchdistance;
- m_limits.X.max = max_x + searchdistance;
- m_limits.Y.min = min_y - searchdistance;
- m_limits.Y.max = max_y + searchdistance;
- m_limits.Z.min = min_z - searchdistance;
- m_limits.Z.max = max_z + searchdistance;
+ m_limits.MinEdge.X = min_x - searchdistance;
+ m_limits.MinEdge.Y = min_y - searchdistance;
+ m_limits.MinEdge.Z = min_z - searchdistance;
- m_max_index_x = m_limits.X.max - m_limits.X.min;
- m_max_index_y = m_limits.Y.max - m_limits.Y.min;
- m_max_index_z = m_limits.Z.max - m_limits.Z.min;
+ m_limits.MaxEdge.X = max_x + searchdistance;
+ m_limits.MaxEdge.Y = max_y + searchdistance;
+ m_limits.MaxEdge.Z = max_z + searchdistance;
- //build data map
- if (!build_costmap()) {
- ERROR_TARGET << "failed to build costmap" << std::endl;
- return retval;
+ v3s16 diff = m_limits.MaxEdge - m_limits.MinEdge;
+
+ m_max_index_x = diff.X;
+ m_max_index_y = diff.Y;
+ m_max_index_z = diff.Z;
+
+ delete m_nodes_container;
+ if (diff.getLength() > 5) {
+ m_nodes_container = new MapGridNodeContainer(this);
+ } else {
+ m_nodes_container = new ArrayGridNodeContainer(this, diff);
}
#ifdef PATHFINDER_DEBUG
- print_type();
- print_cost();
- print_ydir();
+ printType();
+ printCost();
+ printYdir();
#endif
//validate and mark start and end pos
v3s16 StartIndex = getIndexPos(source);
v3s16 EndIndex = getIndexPos(destination);
- path_gridnode& startpos = getIndexElement(StartIndex);
- path_gridnode& endpos = getIndexElement(EndIndex);
+ PathGridnode &startpos = getIndexElement(StartIndex);
+ PathGridnode &endpos = getIndexElement(EndIndex);
if (!startpos.valid) {
VERBOSE_TARGET << "invalid startpos" <<
@@ -282,15 +704,15 @@ std::vector<v3s16> pathfinder::get_Path(ServerEnvironment* env,
bool update_cost_retval = false;
switch (algo) {
- case DIJKSTRA:
- update_cost_retval = update_all_costs(StartIndex,v3s16(0,0,0),0,0);
+ case PA_DIJKSTRA:
+ update_cost_retval = updateAllCosts(StartIndex, v3s16(0, 0, 0), 0, 0);
break;
- case A_PLAIN_NP:
- case A_PLAIN:
- update_cost_retval = update_cost_heuristic(StartIndex,v3s16(0,0,0),0,0);
+ case PA_PLAIN_NP:
+ case PA_PLAIN:
+ update_cost_retval = updateCostHeuristic(StartIndex, v3s16(0, 0, 0), 0, 0);
break;
default:
- ERROR_TARGET << "missing algorithm"<< std::endl;
+ ERROR_TARGET << "missing PathAlgorithm"<< std::endl;
break;
}
@@ -298,28 +720,28 @@ std::vector<v3s16> pathfinder::get_Path(ServerEnvironment* env,
#ifdef PATHFINDER_DEBUG
std::cout << "Path to target found!" << std::endl;
- print_pathlen();
+ printPathLen();
#endif
//find path
std::vector<v3s16> path;
- build_path(path,EndIndex,0);
+ buildPath(path, EndIndex, 0);
#ifdef PATHFINDER_DEBUG
std::cout << "Full index path:" << std::endl;
- print_path(path);
+ printPath(path);
#endif
//finalize path
std::vector<v3s16> full_path;
for (std::vector<v3s16>::iterator i = path.begin();
- i != path.end(); i++) {
+ i != path.end(); ++i) {
full_path.push_back(getIndexElement(*i).pos);
}
#ifdef PATHFINDER_DEBUG
std::cout << "full path:" << std::endl;
- print_path(full_path);
+ printPath(full_path);
#endif
#ifdef PATHFINDER_CALC_TIME
timespec ts2;
@@ -337,7 +759,7 @@ std::vector<v3s16> pathfinder::get_Path(ServerEnvironment* env,
}
else {
#ifdef PATHFINDER_DEBUG
- print_pathlen();
+ printPathLen();
#endif
ERROR_TARGET << "failed to update cost map"<< std::endl;
}
@@ -348,7 +770,7 @@ std::vector<v3s16> pathfinder::get_Path(ServerEnvironment* env,
}
/******************************************************************************/
-pathfinder::pathfinder() :
+Pathfinder::Pathfinder() :
m_max_index_x(0),
m_max_index_y(0),
m_max_index_z(0),
@@ -357,123 +779,36 @@ pathfinder::pathfinder() :
m_maxjump(0),
m_min_target_distance(0),
m_prefetch(true),
- m_start(0,0,0),
- m_destination(0,0,0),
- m_limits(),
- m_data(),
+ m_start(0, 0, 0),
+ m_destination(0, 0, 0),
+ m_nodes_container(NULL),
m_env(0)
{
//intentionaly empty
}
-/******************************************************************************/
-v3s16 pathfinder::getRealPos(v3s16 ipos) {
-
- v3s16 retval = ipos;
-
- retval.X += m_limits.X.min;
- retval.Y += m_limits.Y.min;
- retval.Z += m_limits.Z.min;
-
- return retval;
+Pathfinder::~Pathfinder()
+{
+ delete m_nodes_container;
}
-
/******************************************************************************/
-bool pathfinder::build_costmap()
+v3s16 Pathfinder::getRealPos(v3s16 ipos)
{
- INFO_TARGET << "Pathfinder build costmap: (" << m_limits.X.min << ","
- << m_limits.Z.min << ") ("
- << m_limits.X.max << ","
- << m_limits.Z.max << ")"
- << std::endl;
- m_data.resize(m_max_index_x);
- for (int x = 0; x < m_max_index_x; x++) {
- m_data[x].resize(m_max_index_z);
- for (int z = 0; z < m_max_index_z; z++) {
- m_data[x][z].resize(m_max_index_y);
-
- int surfaces = 0;
- for (int y = 0; y < m_max_index_y; y++) {
- v3s16 ipos(x,y,z);
-
- v3s16 realpos = getRealPos(ipos);
-
- MapNode current = m_env->getMap().getNodeNoEx(realpos);
- MapNode below = m_env->getMap().getNodeNoEx(realpos + v3s16(0,-1,0));
-
-
- if ((current.param0 == CONTENT_IGNORE) ||
- (below.param0 == CONTENT_IGNORE)) {
- DEBUG_OUT("Pathfinder: " << PPOS(realpos) <<
- " current or below is invalid element" << std::endl);
- if (current.param0 == CONTENT_IGNORE) {
- m_data[x][z][y].type = 'i';
- DEBUG_OUT(x << "," << y << "," << z << ": " << 'i' << std::endl);
- }
- continue;
- }
-
- //don't add anything if it isn't an air node
- if ((current.param0 != CONTENT_AIR) ||
- (below.param0 == CONTENT_AIR )) {
- DEBUG_OUT("Pathfinder: " << PPOS(realpos)
- << " not on surface" << std::endl);
- if (current.param0 != CONTENT_AIR) {
- m_data[x][z][y].type = 's';
- DEBUG_OUT(x << "," << y << "," << z << ": " << 's' << std::endl);
- }
- else {
- m_data[x][z][y].type = '-';
- DEBUG_OUT(x << "," << y << "," << z << ": " << '-' << std::endl);
- }
- continue;
- }
-
- surfaces++;
-
- m_data[x][z][y].valid = true;
- m_data[x][z][y].pos = realpos;
- m_data[x][z][y].type = 'g';
- DEBUG_OUT(x << "," << y << "," << z << ": " << 'a' << std::endl);
-
- if (m_prefetch) {
- m_data[x][z][y].directions[DIR_XP] =
- calc_cost(realpos,v3s16( 1,0, 0));
- m_data[x][z][y].directions[DIR_XM] =
- calc_cost(realpos,v3s16(-1,0, 0));
- m_data[x][z][y].directions[DIR_ZP] =
- calc_cost(realpos,v3s16( 0,0, 1));
- m_data[x][z][y].directions[DIR_ZM] =
- calc_cost(realpos,v3s16( 0,0,-1));
- }
-
- }
-
- if (surfaces >= 1 ) {
- for (int y = 0; y < m_max_index_y; y++) {
- if (m_data[x][z][y].valid) {
- m_data[x][z][y].surfaces = surfaces;
- }
- }
- }
- }
- }
- return true;
+ return m_limits.MinEdge + ipos;
}
/******************************************************************************/
-path_cost pathfinder::calc_cost(v3s16 pos,v3s16 dir) {
- path_cost retval;
+PathCost Pathfinder::calcCost(v3s16 pos, v3s16 dir)
+{
+ INodeDefManager *ndef = m_env->getGameDef()->ndef();
+ PathCost retval;
retval.updated = true;
v3s16 pos2 = pos + dir;
//check limits
- if ( (pos2.X < m_limits.X.min) ||
- (pos2.X >= m_limits.X.max) ||
- (pos2.Z < m_limits.Z.min) ||
- (pos2.Z >= m_limits.Z.max)) {
+ if (!m_limits.isPointInside(pos2)) {
DEBUG_OUT("Pathfinder: " << PPOS(pos2) <<
" no cost -> out of limits" << std::endl);
return retval;
@@ -488,18 +823,18 @@ path_cost pathfinder::calc_cost(v3s16 pos,v3s16 dir) {
return retval;
}
- if (node_at_pos2.param0 == CONTENT_AIR) {
+ if (!ndef->get(node_at_pos2).walkable) {
MapNode node_below_pos2 =
- m_env->getMap().getNodeNoEx(pos2 + v3s16(0,-1,0));
+ m_env->getMap().getNodeNoEx(pos2 + v3s16(0, -1, 0));
//did we get information about node?
if (node_below_pos2.param0 == CONTENT_IGNORE ) {
VERBOSE_TARGET << "Pathfinder: (2) area at pos: "
- << PPOS((pos2 + v3s16(0,-1,0))) << " not loaded";
+ << PPOS((pos2 + v3s16(0, -1, 0))) << " not loaded";
return retval;
}
- if (node_below_pos2.param0 != CONTENT_AIR) {
+ if (ndef->get(node_below_pos2).walkable) {
retval.valid = true;
retval.value = 1;
retval.direction = 0;
@@ -507,20 +842,20 @@ path_cost pathfinder::calc_cost(v3s16 pos,v3s16 dir) {
<< " cost same height found" << std::endl);
}
else {
- v3s16 testpos = pos2 - v3s16(0,-1,0);
+ v3s16 testpos = pos2 - v3s16(0, -1, 0);
MapNode node_at_pos = m_env->getMap().getNodeNoEx(testpos);
while ((node_at_pos.param0 != CONTENT_IGNORE) &&
- (node_at_pos.param0 == CONTENT_AIR) &&
- (testpos.Y > m_limits.Y.min)) {
- testpos += v3s16(0,-1,0);
+ (!ndef->get(node_at_pos).walkable) &&
+ (testpos.Y > m_limits.MinEdge.Y)) {
+ testpos += v3s16(0, -1, 0);
node_at_pos = m_env->getMap().getNodeNoEx(testpos);
}
//did we find surface?
- if ((testpos.Y >= m_limits.Y.min) &&
+ if ((testpos.Y >= m_limits.MinEdge.Y) &&
(node_at_pos.param0 != CONTENT_IGNORE) &&
- (node_at_pos.param0 != CONTENT_AIR)) {
+ (ndef->get(node_at_pos).walkable)) {
if ((pos2.Y - testpos.Y - 1) <= m_maxdrop) {
retval.valid = true;
retval.value = 2;
@@ -545,15 +880,15 @@ path_cost pathfinder::calc_cost(v3s16 pos,v3s16 dir) {
MapNode node_at_pos = m_env->getMap().getNodeNoEx(testpos);
while ((node_at_pos.param0 != CONTENT_IGNORE) &&
- (node_at_pos.param0 != CONTENT_AIR) &&
- (testpos.Y < m_limits.Y.max)) {
- testpos += v3s16(0,1,0);
+ (ndef->get(node_at_pos).walkable) &&
+ (testpos.Y < m_limits.MaxEdge.Y)) {
+ testpos += v3s16(0, 1, 0);
node_at_pos = m_env->getMap().getNodeNoEx(testpos);
}
//did we find surface?
- if ((testpos.Y <= m_limits.Y.max) &&
- (node_at_pos.param0 == CONTENT_AIR)) {
+ if ((testpos.Y <= m_limits.MaxEdge.Y) &&
+ (!ndef->get(node_at_pos).walkable)) {
if (testpos.Y - pos2.Y <= m_maxjump) {
retval.valid = true;
@@ -575,23 +910,26 @@ path_cost pathfinder::calc_cost(v3s16 pos,v3s16 dir) {
}
/******************************************************************************/
-v3s16 pathfinder::getIndexPos(v3s16 pos) {
-
- v3s16 retval = pos;
- retval.X -= m_limits.X.min;
- retval.Y -= m_limits.Y.min;
- retval.Z -= m_limits.Z.min;
+v3s16 Pathfinder::getIndexPos(v3s16 pos)
+{
+ return pos - m_limits.MinEdge;
+}
- return retval;
+/******************************************************************************/
+PathGridnode &Pathfinder::getIndexElement(v3s16 ipos)
+{
+ return m_nodes_container->access(ipos);
}
/******************************************************************************/
-path_gridnode& pathfinder::getIndexElement(v3s16 ipos) {
- return m_data[ipos.X][ipos.Z][ipos.Y];
+inline PathGridnode &Pathfinder::getIdxElem(s16 x, s16 y, s16 z)
+{
+ return m_nodes_container->access(v3s16(x,y,z));
}
/******************************************************************************/
-bool pathfinder::valid_index(v3s16 index) {
+bool Pathfinder::isValidIndex(v3s16 index)
+{
if ( (index.X < m_max_index_x) &&
(index.Y < m_max_index_y) &&
(index.Z < m_max_index_z) &&
@@ -604,7 +942,8 @@ bool pathfinder::valid_index(v3s16 index) {
}
/******************************************************************************/
-v3s16 pathfinder::invert(v3s16 pos) {
+v3s16 Pathfinder::invert(v3s16 pos)
+{
v3s16 retval = pos;
retval.X *=-1;
@@ -615,12 +954,12 @@ v3s16 pathfinder::invert(v3s16 pos) {
}
/******************************************************************************/
-bool pathfinder::update_all_costs( v3s16 ipos,
- v3s16 srcdir,
- int current_cost,
- int level) {
-
- path_gridnode& g_pos = getIndexElement(ipos);
+bool Pathfinder::updateAllCosts(v3s16 ipos,
+ v3s16 srcdir,
+ int current_cost,
+ int level)
+{
+ PathGridnode &g_pos = getIndexElement(ipos);
g_pos.totalcost = current_cost;
g_pos.sourcedir = srcdir;
@@ -644,22 +983,20 @@ bool pathfinder::update_all_costs( v3s16 ipos,
for (unsigned int i=0; i < directions.size(); i++) {
if (directions[i] != srcdir) {
- path_cost cost = g_pos.get_cost(directions[i]);
+ PathCost cost = g_pos.getCost(directions[i]);
if (cost.valid) {
directions[i].Y = cost.direction;
v3s16 ipos2 = ipos + directions[i];
- if (!valid_index(ipos2)) {
+ if (!isValidIndex(ipos2)) {
DEBUG_OUT(LVL " Pathfinder: " << PPOS(ipos2) <<
- " out of range (" << m_limits.X.max << "," <<
- m_limits.Y.max << "," << m_limits.Z.max
- <<")" << std::endl);
+ " out of range, max=" << PPOS(m_limits.MaxEdge) << std::endl);
continue;
}
- path_gridnode& g_pos2 = getIndexElement(ipos2);
+ PathGridnode &g_pos2 = getIndexElement(ipos2);
if (!g_pos2.valid) {
VERBOSE_TARGET << LVL "Pathfinder: no data for new position: "
@@ -682,8 +1019,8 @@ bool pathfinder::update_all_costs( v3s16 ipos,
DEBUG_OUT(LVL "Pathfinder: updating path at: "<<
PPOS(ipos2) << " from: " << g_pos2.totalcost << " to "<<
new_cost << std::endl);
- if (update_all_costs(ipos2,invert(directions[i]),
- new_cost,level)) {
+ if (updateAllCosts(ipos2, invert(directions[i]),
+ new_cost, level)) {
retval = true;
}
}
@@ -704,36 +1041,37 @@ bool pathfinder::update_all_costs( v3s16 ipos,
}
/******************************************************************************/
-int pathfinder::get_manhattandistance(v3s16 pos) {
-
- int min_x = MYMIN(pos.X,m_destination.X);
- int max_x = MYMAX(pos.X,m_destination.X);
- int min_z = MYMIN(pos.Z,m_destination.Z);
- int max_z = MYMAX(pos.Z,m_destination.Z);
+int Pathfinder::getXZManhattanDist(v3s16 pos)
+{
+ int min_x = MYMIN(pos.X, m_destination.X);
+ int max_x = MYMAX(pos.X, m_destination.X);
+ int min_z = MYMIN(pos.Z, m_destination.Z);
+ int max_z = MYMAX(pos.Z, m_destination.Z);
return (max_x - min_x) + (max_z - min_z);
}
/******************************************************************************/
-v3s16 pathfinder::get_dir_heuristic(std::vector<v3s16>& directions,path_gridnode& g_pos) {
+v3s16 Pathfinder::getDirHeuristic(std::vector<v3s16> &directions, PathGridnode &g_pos)
+{
int minscore = -1;
- v3s16 retdir = v3s16(0,0,0);
+ v3s16 retdir = v3s16(0, 0, 0);
v3s16 srcpos = g_pos.pos;
DEBUG_OUT("Pathfinder: remaining dirs at beginning:"
<< directions.size() << std::endl);
for (std::vector<v3s16>::iterator iter = directions.begin();
iter != directions.end();
- iter ++) {
+ ++iter) {
- v3s16 pos1 = v3s16(srcpos.X + iter->X,0,srcpos.Z+iter->Z);
+ v3s16 pos1 = v3s16(srcpos.X + iter->X, 0, srcpos.Z+iter->Z);
- int cur_manhattan = get_manhattandistance(pos1);
- path_cost cost = g_pos.get_cost(*iter);
+ int cur_manhattan = getXZManhattanDist(pos1);
+ PathCost cost = g_pos.getCost(*iter);
if (!cost.updated) {
- cost = calc_cost(g_pos.pos,*iter);
- g_pos.set_cost(*iter,cost);
+ cost = calcCost(g_pos.pos, *iter);
+ g_pos.setCost(*iter, cost);
}
if (cost.valid) {
@@ -746,10 +1084,10 @@ v3s16 pathfinder::get_dir_heuristic(std::vector<v3s16>& directions,path_gridnode
}
}
- if (retdir != v3s16(0,0,0)) {
+ if (retdir != v3s16(0, 0, 0)) {
for (std::vector<v3s16>::iterator iter = directions.begin();
iter != directions.end();
- iter ++) {
+ ++iter) {
if(*iter == retdir) {
DEBUG_OUT("Pathfinder: removing return direction" << std::endl);
directions.erase(iter);
@@ -768,12 +1106,13 @@ v3s16 pathfinder::get_dir_heuristic(std::vector<v3s16>& directions,path_gridnode
}
/******************************************************************************/
-bool pathfinder::update_cost_heuristic( v3s16 ipos,
- v3s16 srcdir,
- int current_cost,
- int level) {
+bool Pathfinder::updateCostHeuristic( v3s16 ipos,
+ v3s16 srcdir,
+ int current_cost,
+ int level)
+{
- path_gridnode& g_pos = getIndexElement(ipos);
+ PathGridnode &g_pos = getIndexElement(ipos);
g_pos.totalcost = current_cost;
g_pos.sourcedir = srcdir;
@@ -790,38 +1129,36 @@ bool pathfinder::update_cost_heuristic( v3s16 ipos,
std::vector<v3s16> directions;
- directions.push_back(v3s16( 1,0, 0));
- directions.push_back(v3s16(-1,0, 0));
- directions.push_back(v3s16( 0,0, 1));
- directions.push_back(v3s16( 0,0,-1));
+ directions.push_back(v3s16( 1, 0, 0));
+ directions.push_back(v3s16(-1, 0, 0));
+ directions.push_back(v3s16( 0, 0, 1));
+ directions.push_back(v3s16( 0, 0, -1));
- v3s16 direction = get_dir_heuristic(directions,g_pos);
+ v3s16 direction = getDirHeuristic(directions, g_pos);
- while (direction != v3s16(0,0,0) && (!retval)) {
+ while (direction != v3s16(0, 0, 0) && (!retval)) {
if (direction != srcdir) {
- path_cost cost = g_pos.get_cost(direction);
+ PathCost cost = g_pos.getCost(direction);
if (cost.valid) {
direction.Y = cost.direction;
v3s16 ipos2 = ipos + direction;
- if (!valid_index(ipos2)) {
+ if (!isValidIndex(ipos2)) {
DEBUG_OUT(LVL " Pathfinder: " << PPOS(ipos2) <<
- " out of range (" << m_limits.X.max << "," <<
- m_limits.Y.max << "," << m_limits.Z.max
- <<")" << std::endl);
- direction = get_dir_heuristic(directions,g_pos);
+ " out of range, max=" << PPOS(m_limits.MaxEdge) << std::endl);
+ direction = getDirHeuristic(directions, g_pos);
continue;
}
- path_gridnode& g_pos2 = getIndexElement(ipos2);
+ PathGridnode &g_pos2 = getIndexElement(ipos2);
if (!g_pos2.valid) {
VERBOSE_TARGET << LVL "Pathfinder: no data for new position: "
<< PPOS(ipos2) << std::endl;
- direction = get_dir_heuristic(directions,g_pos);
+ direction = getDirHeuristic(directions, g_pos);
continue;
}
@@ -844,8 +1181,8 @@ bool pathfinder::update_cost_heuristic( v3s16 ipos,
PPOS(ipos2) << " from: " << g_pos2.totalcost << " to "<<
new_cost << " srcdir=" <<
PPOS(invert(direction))<< std::endl);
- if (update_cost_heuristic(ipos2,invert(direction),
- new_cost,level)) {
+ if (updateCostHeuristic(ipos2, invert(direction),
+ new_cost, level)) {
retval = true;
}
}
@@ -866,24 +1203,25 @@ bool pathfinder::update_cost_heuristic( v3s16 ipos,
" skipping srcdir: "
<< PPOS(direction) << std::endl);
}
- direction = get_dir_heuristic(directions,g_pos);
+ direction = getDirHeuristic(directions, g_pos);
}
return retval;
}
/******************************************************************************/
-void pathfinder::build_path(std::vector<v3s16>& path,v3s16 pos, int level) {
+void Pathfinder::buildPath(std::vector<v3s16> &path, v3s16 pos, int level)
+{
level ++;
if (level > 700) {
ERROR_TARGET
- << LVL "Pathfinder: path is too long aborting" << std::endl;
+ << LVL "Pathfinder: path is too long aborting" << std::endl;
return;
}
- path_gridnode& g_pos = getIndexElement(pos);
+ PathGridnode &g_pos = getIndexElement(pos);
if (!g_pos.valid) {
ERROR_TARGET
- << LVL "Pathfinder: invalid next pos detected aborting" << std::endl;
+ << LVL "Pathfinder: invalid next pos detected aborting" << std::endl;
return;
}
@@ -895,37 +1233,40 @@ void pathfinder::build_path(std::vector<v3s16>& path,v3s16 pos, int level) {
return;
}
- build_path(path,pos + g_pos.sourcedir,level);
+ buildPath(path, pos + g_pos.sourcedir, level);
path.push_back(pos);
}
/******************************************************************************/
-v3f pathfinder::tov3f(v3s16 pos) {
- return v3f(BS*pos.X,BS*pos.Y,BS*pos.Z);
+v3f Pathfinder::tov3f(v3s16 pos)
+{
+ return v3f(BS * pos.X, BS * pos.Y, BS * pos.Z);
}
#ifdef PATHFINDER_DEBUG
/******************************************************************************/
-void pathfinder::print_cost() {
- print_cost(DIR_XP);
- print_cost(DIR_XM);
- print_cost(DIR_ZP);
- print_cost(DIR_ZM);
+void Pathfinder::printCost()
+{
+ printCost(DIR_XP);
+ printCost(DIR_XM);
+ printCost(DIR_ZP);
+ printCost(DIR_ZM);
}
/******************************************************************************/
-void pathfinder::print_ydir() {
- print_ydir(DIR_XP);
- print_ydir(DIR_XM);
- print_ydir(DIR_ZP);
- print_ydir(DIR_ZM);
+void Pathfinder::printYdir()
+{
+ printYdir(DIR_XP);
+ printYdir(DIR_XM);
+ printYdir(DIR_ZP);
+ printYdir(DIR_ZM);
}
/******************************************************************************/
-void pathfinder::print_cost(path_directions dir) {
-
- std::cout << "Cost in direction: " << dir_to_name(dir) << std::endl;
+void Pathfinder::printCost(PathDirections dir)
+{
+ std::cout << "Cost in direction: " << dirToName(dir) << std::endl;
std::cout << std::setfill('-') << std::setw(80) << "-" << std::endl;
std::cout << std::setfill(' ');
for (int y = 0; y < m_max_index_y; y++) {
@@ -941,9 +1282,9 @@ void pathfinder::print_cost(path_directions dir) {
for (int z = 0; z < m_max_index_z; z++) {
std::cout << std::setw(4) << z <<": ";
for (int x = 0; x < m_max_index_x; x++) {
- if (m_data[x][z][y].directions[dir].valid)
+ if (getIdxElem(x, y, z).directions[dir].valid)
std::cout << std::setw(4)
- << m_data[x][z][y].directions[dir].value;
+ << getIdxElem(x, y, z).directions[dir].value;
else
std::cout << std::setw(4) << "-";
}
@@ -954,9 +1295,9 @@ void pathfinder::print_cost(path_directions dir) {
}
/******************************************************************************/
-void pathfinder::print_ydir(path_directions dir) {
-
- std::cout << "Height difference in direction: " << dir_to_name(dir) << std::endl;
+void Pathfinder::printYdir(PathDirections dir)
+{
+ std::cout << "Height difference in direction: " << dirToName(dir) << std::endl;
std::cout << std::setfill('-') << std::setw(80) << "-" << std::endl;
std::cout << std::setfill(' ');
for (int y = 0; y < m_max_index_y; y++) {
@@ -972,9 +1313,9 @@ void pathfinder::print_ydir(path_directions dir) {
for (int z = 0; z < m_max_index_z; z++) {
std::cout << std::setw(4) << z <<": ";
for (int x = 0; x < m_max_index_x; x++) {
- if (m_data[x][z][y].directions[dir].valid)
+ if (getIdxElem(x, y, z).directions[dir].valid)
std::cout << std::setw(4)
- << m_data[x][z][y].directions[dir].direction;
+ << getIdxElem(x, y, z).directions[dir].direction;
else
std::cout << std::setw(4) << "-";
}
@@ -985,7 +1326,8 @@ void pathfinder::print_ydir(path_directions dir) {
}
/******************************************************************************/
-void pathfinder::print_type() {
+void Pathfinder::printType()
+{
std::cout << "Type of node:" << std::endl;
std::cout << std::setfill('-') << std::setw(80) << "-" << std::endl;
std::cout << std::setfill(' ');
@@ -1002,7 +1344,7 @@ void pathfinder::print_type() {
for (int z = 0; z < m_max_index_z; z++) {
std::cout << std::setw(3) << z <<": ";
for (int x = 0; x < m_max_index_x; x++) {
- char toshow = m_data[x][z][y].type;
+ char toshow = getIdxElem(x, y, z).type;
std::cout << std::setw(3) << toshow;
}
std::cout << std::endl;
@@ -1013,7 +1355,8 @@ void pathfinder::print_type() {
}
/******************************************************************************/
-void pathfinder::print_pathlen() {
+void Pathfinder::printPathLen()
+{
std::cout << "Pathlen:" << std::endl;
std::cout << std::setfill('-') << std::setw(80) << "-" << std::endl;
std::cout << std::setfill(' ');
@@ -1030,7 +1373,7 @@ void pathfinder::print_pathlen() {
for (int z = 0; z < m_max_index_z; z++) {
std::cout << std::setw(3) << z <<": ";
for (int x = 0; x < m_max_index_x; x++) {
- std::cout << std::setw(3) << m_data[x][z][y].totalcost;
+ std::cout << std::setw(3) << getIdxElem(x, y, z).totalcost;
}
std::cout << std::endl;
}
@@ -1040,7 +1383,8 @@ void pathfinder::print_pathlen() {
}
/******************************************************************************/
-std::string pathfinder::dir_to_name(path_directions dir) {
+std::string Pathfinder::dirToName(PathDirections dir)
+{
switch (dir) {
case DIR_XP:
return "XP";
@@ -1060,11 +1404,11 @@ std::string pathfinder::dir_to_name(path_directions dir) {
}
/******************************************************************************/
-void pathfinder::print_path(std::vector<v3s16> path) {
-
+void Pathfinder::printPath(std::vector<v3s16> path)
+{
unsigned int current = 0;
for (std::vector<v3s16>::iterator i = path.begin();
- i != path.end(); i++) {
+ i != path.end(); ++i) {
std::cout << std::setw(3) << current << ":" << PPOS((*i)) << std::endl;
current++;
}
diff --git a/src/pathfinder.h b/src/pathfinder.h
index dd41227f7..ba95aaf1c 100644
--- a/src/pathfinder.h
+++ b/src/pathfinder.h
@@ -24,10 +24,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
/* Includes */
/******************************************************************************/
#include <vector>
-
#include "irr_v3d.h"
-
/******************************************************************************/
/* Forward declarations */
/******************************************************************************/
@@ -38,313 +36,31 @@ class ServerEnvironment;
/* Typedefs and macros */
/******************************************************************************/
-//#define PATHFINDER_DEBUG
-
typedef enum {
DIR_XP,
DIR_XM,
DIR_ZP,
DIR_ZM
-} path_directions;
+} PathDirections;
/** List of supported algorithms */
typedef enum {
- DIJKSTRA, /**< Dijkstra shortest path algorithm */
- A_PLAIN, /**< A* algorithm using heuristics to find a path */
- A_PLAIN_NP /**< A* algorithm without prefetching of map data */
-} algorithm;
+ PA_DIJKSTRA, /**< Dijkstra shortest path algorithm */
+ PA_PLAIN, /**< A* algorithm using heuristics to find a path */
+ PA_PLAIN_NP /**< A* algorithm without prefetching of map data */
+} PathAlgorithm;
/******************************************************************************/
/* declarations */
/******************************************************************************/
/** c wrapper function to use from scriptapi */
-std::vector<v3s16> get_Path(ServerEnvironment* env,
+std::vector<v3s16> get_path(ServerEnvironment *env,
v3s16 source,
v3s16 destination,
unsigned int searchdistance,
unsigned int max_jump,
unsigned int max_drop,
- algorithm algo);
-
-/** representation of cost in specific direction */
-class path_cost {
-public:
-
- /** default constructor */
- path_cost();
-
- /** copy constructor */
- path_cost(const path_cost& b);
-
- /** assignment operator */
- path_cost& operator= (const path_cost& b);
-
- bool valid; /**< movement is possible */
- int value; /**< cost of movement */
- int direction; /**< y-direction of movement */
- bool updated; /**< this cost has ben calculated */
-
-};
-
-
-/** representation of a mapnode to be used for pathfinding */
-class path_gridnode {
-
-public:
- /** default constructor */
- path_gridnode();
-
- /** copy constructor */
- path_gridnode(const path_gridnode& b);
-
- /**
- * assignment operator
- * @param b node to copy
- */
- path_gridnode& operator= (const path_gridnode& b);
-
- /**
- * read cost in a specific direction
- * @param dir direction of cost to fetch
- */
- path_cost get_cost(v3s16 dir);
-
- /**
- * set cost value for movement
- * @param dir direction to set cost for
- * @cost cost to set
- */
- void set_cost(v3s16 dir,path_cost cost);
-
- bool valid; /**< node is on surface */
- bool target; /**< node is target position */
- bool source; /**< node is stating position */
- int totalcost; /**< cost to move here from starting point */
- v3s16 sourcedir; /**< origin of movement for current cost */
- int surfaces; /**< number of surfaces with same x,z value*/
- v3s16 pos; /**< real position of node */
- path_cost directions[4]; /**< cost in different directions */
-
- /* debug values */
- bool is_element; /**< node is element of path detected */
- char type; /**< type of node */
-};
-
-/** class doing pathfinding */
-class pathfinder {
-
-public:
- /**
- * default constructor
- */
- pathfinder();
-
- /**
- * path evaluation function
- * @param env environment to look for path
- * @param source origin of path
- * @param destination end position of path
- * @param searchdistance maximum number of nodes to look in each direction
- * @param max_jump maximum number of blocks a path may jump up
- * @param max_drop maximum number of blocks a path may drop
- * @param algo algorithm to use for finding a path
- */
- std::vector<v3s16> get_Path(ServerEnvironment* env,
- v3s16 source,
- v3s16 destination,
- unsigned int searchdistance,
- unsigned int max_jump,
- unsigned int max_drop,
- algorithm algo);
-
-private:
- /** data struct for storing internal information */
- struct limits {
- struct limit {
- int min;
- int max;
- };
-
- limit X;
- limit Y;
- limit Z;
- };
-
- /* helper functions */
-
- /**
- * transform index pos to mappos
- * @param ipos a index position
- * @return map position
- */
- v3s16 getRealPos(v3s16 ipos);
-
- /**
- * transform mappos to index pos
- * @param pos a real pos
- * @return index position
- */
- v3s16 getIndexPos(v3s16 pos);
-
- /**
- * get gridnode at a specific index position
- * @param ipos index position
- * @return gridnode for index
- */
- path_gridnode& getIndexElement(v3s16 ipos);
-
- /**
- * invert a 3d position
- * @param pos 3d position
- * @return pos *-1
- */
- v3s16 invert(v3s16 pos);
-
- /**
- * check if a index is within current search area
- * @param index position to validate
- * @return true/false
- */
- bool valid_index(v3s16 index);
-
- /**
- * translate position to float position
- * @param pos integer position
- * @return float position
- */
- v3f tov3f(v3s16 pos);
-
-
- /* algorithm functions */
-
- /**
- * calculate 2d manahttan distance to target
- * @param pos position to calc distance
- * @return integer distance
- */
- int get_manhattandistance(v3s16 pos);
-
- /**
- * get best direction based uppon heuristics
- * @param directions list of unchecked directions
- * @param g_pos mapnode to start from
- * @return direction to check
- */
- v3s16 get_dir_heuristic(std::vector<v3s16>& directions,path_gridnode& g_pos);
-
- /**
- * build internal data representation of search area
- * @return true/false if costmap creation was successfull
- */
- bool build_costmap();
-
- /**
- * calculate cost of movement
- * @param pos real world position to start movement
- * @param dir direction to move to
- * @return cost information
- */
- path_cost calc_cost(v3s16 pos,v3s16 dir);
-
- /**
- * recursive update whole search areas total cost information
- * @param ipos position to check next
- * @param srcdir positionc checked last time
- * @param total_cost cost of moving to ipos
- * @param level current recursion depth
- * @return true/false path to destination has been found
- */
- bool update_all_costs(v3s16 ipos,v3s16 srcdir,int total_cost,int level);
-
- /**
- * recursive try to find a patrh to destionation
- * @param ipos position to check next
- * @param srcdir positionc checked last time
- * @param total_cost cost of moving to ipos
- * @param level current recursion depth
- * @return true/false path to destination has been found
- */
- bool update_cost_heuristic(v3s16 ipos,v3s16 srcdir,int current_cost,int level);
-
- /**
- * recursive build a vector containing all nodes from source to destination
- * @param path vector to add nodes to
- * @param pos pos to check next
- * @param level recursion depth
- */
- void build_path(std::vector<v3s16>& path,v3s16 pos, int level);
-
- /* variables */
- int m_max_index_x; /**< max index of search area in x direction */
- int m_max_index_y; /**< max index of search area in y direction */
- int m_max_index_z; /**< max index of search area in z direction */
-
-
- int m_searchdistance; /**< max distance to search in each direction */
- int m_maxdrop; /**< maximum number of blocks a path may drop */
- int m_maxjump; /**< maximum number of blocks a path may jump */
- int m_min_target_distance; /**< current smalest path to target */
-
- bool m_prefetch; /**< prefetch cost data */
-
- v3s16 m_start; /**< source position */
- v3s16 m_destination; /**< destination position */
-
- limits m_limits; /**< position limits in real map coordinates */
-
- /** 3d grid containing all map data already collected and analyzed */
- std::vector<std::vector<std::vector<path_gridnode> > > m_data;
-
- ServerEnvironment* m_env; /**< minetest environment pointer */
-
-#ifdef PATHFINDER_DEBUG
-
- /**
- * print collected cost information
- */
- void print_cost();
-
- /**
- * print collected cost information in a specific direction
- * @param dir direction to print
- */
- void print_cost(path_directions dir);
-
- /**
- * print type of node as evaluated
- */
- void print_type();
-
- /**
- * print pathlenght for all nodes in search area
- */
- void print_pathlen();
-
- /**
- * print a path
- * @param path path to show
- */
- void print_path(std::vector<v3s16> path);
-
- /**
- * print y direction for all movements
- */
- void print_ydir();
-
- /**
- * print y direction for moving in a specific direction
- * @param dir direction to show data
- */
- void print_ydir(path_directions dir);
-
- /**
- * helper function to translate a direction to speaking text
- * @param dir direction to translate
- * @return textual name of direction
- */
- std::string dir_to_name(path_directions dir);
-#endif
-};
+ PathAlgorithm algo);
#endif /* PATHFINDER_H_ */
diff --git a/src/player.cpp b/src/player.cpp
index cb2286ef6..5949712a5 100644
--- a/src/player.cpp
+++ b/src/player.cpp
@@ -20,7 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "player.h"
#include <fstream>
-#include "jthread/jmutexautolock.h"
+#include "threading/mutex_auto_lock.h"
#include "util/numeric.h"
#include "hud.h"
#include "constants.h"
@@ -33,6 +33,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
Player::Player(IGameDef *gamedef, const char *name):
+ got_teleported(false),
touching_ground(false),
in_liquid(false),
in_liquid_stable(false),
@@ -111,41 +112,6 @@ Player::~Player()
clearHud();
}
-// Horizontal acceleration (X and Z), Y direction is ignored
-void Player::accelerateHorizontal(v3f target_speed, f32 max_increase)
-{
- if(max_increase == 0)
- return;
-
- v3f d_wanted = target_speed - m_speed;
- d_wanted.Y = 0;
- f32 dl = d_wanted.getLength();
- if(dl > max_increase)
- dl = max_increase;
-
- v3f d = d_wanted.normalize() * dl;
-
- m_speed.X += d.X;
- m_speed.Z += d.Z;
-
-}
-
-// Vertical acceleration (Y), X and Z directions are ignored
-void Player::accelerateVertical(v3f target_speed, f32 max_increase)
-{
- if(max_increase == 0)
- return;
-
- f32 d_wanted = target_speed.Y - m_speed.Y;
- if(d_wanted > max_increase)
- d_wanted = max_increase;
- else if(d_wanted < -max_increase)
- d_wanted = -max_increase;
-
- m_speed.Y += d_wanted;
-
-}
-
v3s16 Player::getLightPosition() const
{
return floatToInt(m_position + v3f(0,BS+BS/2,0), BS);
@@ -217,7 +183,7 @@ void Player::deSerialize(std::istream &is, std::string playername)
u32 Player::addHud(HudElement *toadd)
{
- JMutexAutoLock lock(m_mutex);
+ MutexAutoLock lock(m_mutex);
u32 id = getFreeHudID();
@@ -231,7 +197,7 @@ u32 Player::addHud(HudElement *toadd)
HudElement* Player::getHud(u32 id)
{
- JMutexAutoLock lock(m_mutex);
+ MutexAutoLock lock(m_mutex);
if (id < hud.size())
return hud[id];
@@ -241,7 +207,7 @@ HudElement* Player::getHud(u32 id)
HudElement* Player::removeHud(u32 id)
{
- JMutexAutoLock lock(m_mutex);
+ MutexAutoLock lock(m_mutex);
HudElement* retval = NULL;
if (id < hud.size()) {
@@ -253,7 +219,7 @@ HudElement* Player::removeHud(u32 id)
void Player::clearHud()
{
- JMutexAutoLock lock(m_mutex);
+ MutexAutoLock lock(m_mutex);
while(!hud.empty()) {
delete hud.back();
@@ -261,6 +227,23 @@ void Player::clearHud()
}
}
+RemotePlayer::RemotePlayer(IGameDef *gamedef, const char *name):
+ Player(gamedef, name),
+ m_sao(NULL)
+{
+ movement_acceleration_default = g_settings->getFloat("movement_acceleration_default") * BS;
+ movement_acceleration_air = g_settings->getFloat("movement_acceleration_air") * BS;
+ movement_acceleration_fast = g_settings->getFloat("movement_acceleration_fast") * BS;
+ movement_speed_walk = g_settings->getFloat("movement_speed_walk") * BS;
+ movement_speed_crouch = g_settings->getFloat("movement_speed_crouch") * BS;
+ movement_speed_fast = g_settings->getFloat("movement_speed_fast") * BS;
+ movement_speed_climb = g_settings->getFloat("movement_speed_climb") * BS;
+ movement_speed_jump = g_settings->getFloat("movement_speed_jump") * BS;
+ movement_liquid_fluidity = g_settings->getFloat("movement_liquid_fluidity") * BS;
+ movement_liquid_fluidity_smooth = g_settings->getFloat("movement_liquid_fluidity_smooth") * BS;
+ movement_liquid_sink = g_settings->getFloat("movement_liquid_sink") * BS;
+ movement_gravity = g_settings->getFloat("movement_gravity") * BS;
+}
void RemotePlayer::save(std::string savedir)
{
diff --git a/src/player.h b/src/player.h
index 3a336afc4..b317cda4f 100644
--- a/src/player.h
+++ b/src/player.h
@@ -23,12 +23,13 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "irrlichttypes_bloated.h"
#include "inventory.h"
#include "constants.h" // BS
-#include "jthread/jmutex.h"
+#include "threading/mutex.h"
#include <list>
#define PLAYERNAME_SIZE 20
#define PLAYERNAME_ALLOWED_CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"
+#define PLAYERNAME_ALLOWED_CHARS_USER_EXPL "'a' to 'z', 'A' to 'Z', '0' to '9', '-', '_'"
struct PlayerControl
{
@@ -118,9 +119,6 @@ public:
m_speed = speed;
}
- void accelerateHorizontal(v3f target_speed, f32 max_increase);
- void accelerateVertical(v3f target_speed, f32 max_increase);
-
v3f getPosition()
{
return m_position;
@@ -197,7 +195,7 @@ public:
return m_name;
}
- core::aabbox3d<f32> getCollisionbox()
+ aabb3f getCollisionbox()
{
return m_collisionbox;
}
@@ -320,6 +318,7 @@ public:
// Use a function, if isDead can be defined by other conditions
bool isDead() { return hp == 0; }
+ bool got_teleported;
bool touching_ground;
// This oscillates so that the player jumps a bit above the surface
bool in_liquid;
@@ -397,7 +396,7 @@ protected:
f32 m_yaw;
v3f m_speed;
v3f m_position;
- core::aabbox3d<f32> m_collisionbox;
+ aabb3f m_collisionbox;
bool m_dirty;
@@ -413,7 +412,7 @@ private:
// Protect some critical areas
// hud for example can be modified by EmergeThread
// and ServerThread
- JMutex m_mutex;
+ Mutex m_mutex;
};
@@ -423,10 +422,7 @@ private:
class RemotePlayer : public Player
{
public:
- RemotePlayer(IGameDef *gamedef, const char *name):
- Player(gamedef, name),
- m_sao(NULL)
- {}
+ RemotePlayer(IGameDef *gamedef, const char *name);
virtual ~RemotePlayer() {}
void save(std::string savedir);
diff --git a/src/porting.cpp b/src/porting.cpp
index 44f1fcff1..98b85b7d0 100644
--- a/src/porting.cpp
+++ b/src/porting.cpp
@@ -29,6 +29,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <sys/types.h>
#include <sys/sysctl.h>
#elif defined(_WIN32)
+ #include <windows.h>
+ #include <wincrypt.h>
#include <algorithm>
#endif
#if !defined(_WIN32)
@@ -75,13 +77,13 @@ bool * signal_handler_killstatus(void)
void sigint_handler(int sig)
{
- if(!g_killed) {
- dstream<<DTIME<<"INFO: sigint_handler(): "
- <<"Ctrl-C pressed, shutting down."<<std::endl;
+ if (!g_killed) {
+ dstream << "INFO: sigint_handler(): "
+ << "Ctrl-C pressed, shutting down." << std::endl;
// Comment out for less clutter when testing scripts
- /*dstream<<DTIME<<"INFO: sigint_handler(): "
- <<"Printing debug stacks"<<std::endl;
+ /*dstream << "INFO: sigint_handler(): "
+ << "Printing debug stacks" << std::endl;
debug_stacks_print();*/
g_killed = true;
@@ -105,8 +107,8 @@ BOOL WINAPI event_handler(DWORD sig)
case CTRL_CLOSE_EVENT:
case CTRL_LOGOFF_EVENT:
case CTRL_SHUTDOWN_EVENT:
- if (g_killed == false) {
- dstream << DTIME << "INFO: event_handler(): "
+ if (!g_killed) {
+ dstream << "INFO: event_handler(): "
<< "Ctrl+C, Close Event, Logoff Event or Shutdown Event,"
" shutting down." << std::endl;
g_killed = true;
@@ -130,136 +132,15 @@ void signal_handler_init(void)
/*
- Multithreading support
-*/
-int getNumberOfProcessors()
-{
-#if defined(_SC_NPROCESSORS_ONLN)
-
- return sysconf(_SC_NPROCESSORS_ONLN);
-
-#elif defined(__FreeBSD__) || defined(__APPLE__)
-
- unsigned int len, count;
- len = sizeof(count);
- return sysctlbyname("hw.ncpu", &count, &len, NULL, 0);
-
-#elif defined(_GNU_SOURCE)
-
- return get_nprocs();
-
-#elif defined(_WIN32)
-
- SYSTEM_INFO sysinfo;
- GetSystemInfo(&sysinfo);
- return sysinfo.dwNumberOfProcessors;
-
-#elif defined(PTW32_VERSION) || defined(__hpux)
-
- return pthread_num_processors_np();
-
-#else
-
- return 1;
-
-#endif
-}
-
-
-#ifndef __ANDROID__
-bool threadBindToProcessor(threadid_t tid, int pnumber)
-{
-#if defined(_WIN32)
-
- HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, 0, tid);
- if (!hThread)
- return false;
-
- bool success = SetThreadAffinityMask(hThread, 1 << pnumber) != 0;
-
- CloseHandle(hThread);
- return success;
-
-#elif (defined(__FreeBSD__) && (__FreeBSD_version >= 702106)) \
- || defined(__linux) || defined(linux)
-
- cpu_set_t cpuset;
-
- CPU_ZERO(&cpuset);
- CPU_SET(pnumber, &cpuset);
- return pthread_setaffinity_np(tid, sizeof(cpuset), &cpuset) == 0;
-
-#elif defined(__sun) || defined(sun)
-
- return processor_bind(P_LWPID, MAKE_LWPID_PTHREAD(tid),
- pnumber, NULL) == 0;
-
-#elif defined(_AIX)
-
- return bindprocessor(BINDTHREAD, (tid_t)tid, pnumber) == 0;
-
-#elif defined(__hpux) || defined(hpux)
-
- pthread_spu_t answer;
-
- return pthread_processor_bind_np(PTHREAD_BIND_ADVISORY_NP,
- &answer, pnumber, tid) == 0;
-
-#elif defined(__APPLE__)
-
- struct thread_affinity_policy tapol;
-
- thread_port_t threadport = pthread_mach_thread_np(tid);
- tapol.affinity_tag = pnumber + 1;
- return thread_policy_set(threadport, THREAD_AFFINITY_POLICY,
- (thread_policy_t)&tapol, THREAD_AFFINITY_POLICY_COUNT) == KERN_SUCCESS;
-
-#else
-
- return false;
-
-#endif
-}
-#endif
-
-bool threadSetPriority(threadid_t tid, int prio)
-{
-#if defined(_WIN32)
-
- HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, 0, tid);
- if (!hThread)
- return false;
-
- bool success = SetThreadPriority(hThread, prio) != 0;
-
- CloseHandle(hThread);
- return success;
-
-#else
-
- struct sched_param sparam;
- int policy;
-
- if (pthread_getschedparam(tid, &policy, &sparam) != 0)
- return false;
-
- int min = sched_get_priority_min(policy);
- int max = sched_get_priority_max(policy);
-
- sparam.sched_priority = min + prio * (max - min) / THREAD_PRIORITY_HIGHEST;
- return pthread_setschedparam(tid, policy, &sparam) == 0;
-
-#endif
-}
-
-
-/*
Path mangler
*/
// Default to RUN_IN_PLACE style relative paths
std::string path_share = "..";
std::string path_user = "..";
+std::string path_locale = path_share + DIR_DELIM + "locale";
+std::string path_cache = path_user + DIR_DELIM + "cache";
+
std::string getDataPath(const char *subpath)
{
@@ -282,6 +163,8 @@ bool detectMSVCBuildDir(const std::string &path)
{
const char *ends[] = {
"bin\\Release",
+ "bin\\MinSizeRel",
+ "bin\\RelWithDebInfo",
"bin\\Debug",
"bin\\Build",
NULL
@@ -523,14 +406,14 @@ bool setSystemPaths()
const std::string &trypath = *i;
if (!fs::PathExists(trypath) ||
!fs::PathExists(trypath + DIR_DELIM + "builtin")) {
- dstream << "WARNING: system-wide share not found at \""
+ warningstream << "system-wide share not found at \""
<< trypath << "\""<< std::endl;
continue;
}
// Warn if was not the first alternative
if (i != trylist.begin()) {
- dstream << "WARNING: system-wide share found at \""
+ warningstream << "system-wide share found at \""
<< trypath << "\"" << std::endl;
}
@@ -559,7 +442,7 @@ bool setSystemPaths()
TRUE, (UInt8 *)path, PATH_MAX)) {
path_share = std::string(path);
} else {
- dstream << "WARNING: Could not determine bundle resource path" << std::endl;
+ warningstream << "Could not determine bundle resource path" << std::endl;
}
CFRelease(resources_url);
@@ -583,6 +466,25 @@ bool setSystemPaths()
#endif
+void migrateCachePath()
+{
+ const std::string local_cache_path = path_user + DIR_DELIM + "cache";
+
+ // Delete tmp folder if it exists (it only ever contained
+ // a temporary ogg file, which is no longer used).
+ if (fs::PathExists(local_cache_path + DIR_DELIM + "tmp"))
+ fs::RecursiveDelete(local_cache_path + DIR_DELIM + "tmp");
+
+ // Bail if migration impossible
+ if (path_cache == local_cache_path || !fs::PathExists(local_cache_path)
+ || fs::PathExists(path_cache)) {
+ return;
+ }
+ if (!fs::Rename(local_cache_path, path_cache)) {
+ errorstream << "Failed to migrate local cache path "
+ "to system path!" << std::endl;
+ }
+}
void initializePaths()
{
@@ -627,17 +529,61 @@ void initializePaths()
path_share = execpath;
path_user = execpath;
}
-
+ path_cache = path_user + DIR_DELIM + "cache";
#else
infostream << "Using system-wide paths (NOT RUN_IN_PLACE)" << std::endl;
if (!setSystemPaths())
errorstream << "Failed to get one or more system-wide path" << std::endl;
+ // Initialize path_cache
+ // First try $XDG_CACHE_HOME/PROJECT_NAME
+ const char *cache_dir = getenv("XDG_CACHE_HOME");
+ const char *home_dir = getenv("HOME");
+ if (cache_dir) {
+ path_cache = std::string(cache_dir) + DIR_DELIM + PROJECT_NAME;
+ } else if (home_dir) {
+ // Then try $HOME/.cache/PROJECT_NAME
+ path_cache = std::string(home_dir) + DIR_DELIM + ".cache"
+ + DIR_DELIM + PROJECT_NAME;
+ } else {
+ // If neither works, use $PATH_USER/cache
+ path_cache = path_user + DIR_DELIM + "cache";
+ }
+ // Migrate cache folder to new location if possible
+ migrateCachePath();
#endif
infostream << "Detected share path: " << path_share << std::endl;
infostream << "Detected user path: " << path_user << std::endl;
+ infostream << "Detected cache path: " << path_cache << std::endl;
+
+#ifdef USE_GETTEXT
+ bool found_localedir = false;
+# ifdef STATIC_LOCALEDIR
+ if (STATIC_LOCALEDIR[0] && fs::PathExists(STATIC_LOCALEDIR)) {
+ found_localedir = true;
+ path_locale = STATIC_LOCALEDIR;
+ infostream << "Using locale directory " << STATIC_LOCALEDIR << std::endl;
+ } else {
+ path_locale = getDataPath("locale");
+ if (fs::PathExists(path_locale)) {
+ found_localedir = true;
+ infostream << "Using in-place locale directory " << path_locale
+ << " even though a static one was provided "
+ << "(RUN_IN_PLACE or CUSTOM_LOCALEDIR)." << std::endl;
+ }
+ }
+# else
+ path_locale = getDataPath("locale");
+ if (fs::PathExists(path_locale)) {
+ found_localedir = true;
+ }
+# endif
+ if (!found_localedir) {
+ warningstream << "Couldn't find a locale directory!" << std::endl;
+ }
+#endif // USE_GETTEXT
}
@@ -798,5 +744,44 @@ v2u32 getDisplaySize()
# endif // __ANDROID__
#endif // SERVER
-} //namespace porting
+////
+//// OS-specific Secure Random
+////
+
+#ifdef WIN32
+
+bool secure_rand_fill_buf(void *buf, size_t len)
+{
+ HCRYPTPROV wctx;
+
+ if (!CryptAcquireContext(&wctx, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
+ return false;
+
+ CryptGenRandom(wctx, len, (BYTE *)buf);
+ CryptReleaseContext(wctx, 0);
+ return true;
+}
+
+#else
+
+bool secure_rand_fill_buf(void *buf, size_t len)
+{
+ // N.B. This function checks *only* for /dev/urandom, because on most
+ // common OSes it is non-blocking, whereas /dev/random is blocking, and it
+ // is exceptionally uncommon for there to be a situation where /dev/random
+ // exists but /dev/urandom does not. This guesswork is necessary since
+ // random devices are not covered by any POSIX standard...
+ FILE *fp = fopen("/dev/urandom", "rb");
+ if (!fp)
+ return false;
+
+ bool success = fread(buf, len, 1, fp) == 1;
+
+ fclose(fp);
+ return success;
+}
+
+#endif
+
+} //namespace porting
diff --git a/src/porting.h b/src/porting.h
index 2a91fdd06..4d51c5058 100644
--- a/src/porting.h
+++ b/src/porting.h
@@ -64,28 +64,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#define _GNU_SOURCE
#endif
- #include <sched.h>
-
- #ifdef __FreeBSD__
- #include <pthread_np.h>
- typedef cpuset_t cpu_set_t;
- #elif defined(__sun) || defined(sun)
- #include <sys/types.h>
- #include <sys/processor.h>
- #elif defined(_AIX)
- #include <sys/processor.h>
- #elif __APPLE__
- #include <mach/mach_init.h>
- #include <mach/thread_policy.h>
- #endif
-
#define sleep_ms(x) usleep(x*1000)
-
- #define THREAD_PRIORITY_LOWEST 0
- #define THREAD_PRIORITY_BELOW_NORMAL 1
- #define THREAD_PRIORITY_NORMAL 2
- #define THREAD_PRIORITY_ABOVE_NORMAL 3
- #define THREAD_PRIORITY_HIGHEST 4
#endif
#ifdef _MSC_VER
@@ -129,6 +108,15 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <CoreFoundation/CoreFoundation.h>
#endif
+#ifndef _WIN32 // Posix
+ #include <sys/time.h>
+ #include <time.h>
+ #if defined(__MACH__) && defined(__APPLE__)
+ #include <mach/clock.h>
+ #include <mach/mach.h>
+ #endif
+#endif
+
namespace porting
{
@@ -155,30 +143,31 @@ extern std::string path_share;
extern std::string path_user;
/*
- Get full path of stuff in data directory.
- Example: "stone.png" -> "../data/stone.png"
+ Path to gettext locale files
*/
-std::string getDataPath(const char *subpath);
+extern std::string path_locale;
/*
- Initialize path_share and path_user.
+ Path to directory for storing caches.
*/
-void initializePaths();
+extern std::string path_cache;
/*
- Get number of online processors in the system.
+ Get full path of stuff in data directory.
+ Example: "stone.png" -> "../data/stone.png"
*/
-int getNumberOfProcessors();
+std::string getDataPath(const char *subpath);
/*
- Set a thread's affinity to a particular processor.
+ Move cache folder from path_user to the
+ system cache location if possible.
*/
-bool threadBindToProcessor(threadid_t tid, int pnumber);
+void migrateCachePath();
/*
- Set a thread's priority.
+ Initialize path_*.
*/
-bool threadSetPriority(threadid_t tid, int prio);
+void initializePaths();
/*
Return system information
@@ -194,10 +183,6 @@ void initIrrlicht(irr::IrrlichtDevice * );
Overflow can occur at any value higher than 10000000.
*/
#ifdef _WIN32 // Windows
-#ifndef _WIN32_WINNT
- #define _WIN32_WINNT 0x0501
-#endif
- #include <windows.h>
inline u32 getTimeS()
{
@@ -226,49 +211,56 @@ void initIrrlicht(irr::IrrlichtDevice * );
}
#else // Posix
-#include <sys/time.h>
-#include <time.h>
+ inline void _os_get_clock(struct timespec *ts)
+ {
#if defined(__MACH__) && defined(__APPLE__)
-#include <mach/clock.h>
-#include <mach/mach.h>
-#endif
+ // from http://stackoverflow.com/questions/5167269/clock-gettime-alternative-in-mac-os-x
+ // OS X does not have clock_gettime, use clock_get_time
+ clock_serv_t cclock;
+ mach_timespec_t mts;
+ host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock);
+ clock_get_time(cclock, &mts);
+ mach_port_deallocate(mach_task_self(), cclock);
+ ts->tv_sec = mts.tv_sec;
+ ts->tv_nsec = mts.tv_nsec;
+#elif defined(CLOCK_MONOTONIC_RAW)
+ clock_gettime(CLOCK_MONOTONIC_RAW, ts);
+#elif defined(_POSIX_MONOTONIC_CLOCK)
+ clock_gettime(CLOCK_MONOTONIC, ts);
+#else
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ TIMEVAL_TO_TIMESPEC(&tv, ts);
+#endif // defined(__MACH__) && defined(__APPLE__)
+ }
+ // Note: these clock functions do not return wall time, but
+ // generally a clock that starts at 0 when the process starts.
inline u32 getTimeS()
{
- struct timeval tv;
- gettimeofday(&tv, NULL);
- return tv.tv_sec;
+ struct timespec ts;
+ _os_get_clock(&ts);
+ return ts.tv_sec;
}
inline u32 getTimeMs()
{
- struct timeval tv;
- gettimeofday(&tv, NULL);
- return tv.tv_sec * 1000 + tv.tv_usec / 1000;
+ struct timespec ts;
+ _os_get_clock(&ts);
+ return ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
}
inline u32 getTimeUs()
{
- struct timeval tv;
- gettimeofday(&tv, NULL);
- return tv.tv_sec * 1000000 + tv.tv_usec;
+ struct timespec ts;
+ _os_get_clock(&ts);
+ return ts.tv_sec * 1000000 + ts.tv_nsec / 1000;
}
inline u32 getTimeNs()
{
struct timespec ts;
- // from http://stackoverflow.com/questions/5167269/clock-gettime-alternative-in-mac-os-x
-#if defined(__MACH__) && defined(__APPLE__) // OS X does not have clock_gettime, use clock_get_time
- clock_serv_t cclock;
- mach_timespec_t mts;
- host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock);
- clock_get_time(cclock, &mts);
- mach_port_deallocate(mach_task_self(), cclock);
- ts.tv_sec = mts.tv_sec;
- ts.tv_nsec = mts.tv_nsec;
-#else
- clock_gettime(CLOCK_REALTIME, &ts);
-#endif
+ _os_get_clock(&ts);
return ts.tv_sec * 1000000000 + ts.tv_nsec;
}
@@ -311,59 +303,6 @@ inline u32 getDeltaMs(u32 old_time_ms, u32 new_time_ms)
}
}
-#if defined(linux) || defined(__linux)
- #include <sys/prctl.h>
-
- inline void setThreadName(const char *name) {
- /* It would be cleaner to do this with pthread_setname_np,
- * which was added to glibc in version 2.12, but some major
- * distributions are still runing 2.11 and previous versions.
- */
- prctl(PR_SET_NAME, name);
- }
-#elif defined(__FreeBSD__) || defined(__OpenBSD__)
- #include <pthread.h>
- #include <pthread_np.h>
-
- inline void setThreadName(const char *name) {
- pthread_set_name_np(pthread_self(), name);
- }
-#elif defined(__NetBSD__)
- #include <pthread.h>
-
- inline void setThreadName(const char *name) {
- pthread_setname_np(pthread_self(), name);
- }
-#elif defined(_MSC_VER)
- typedef struct tagTHREADNAME_INFO {
- DWORD dwType; // must be 0x1000
- LPCSTR szName; // pointer to name (in user addr space)
- DWORD dwThreadID; // thread ID (-1=caller thread)
- DWORD dwFlags; // reserved for future use, must be zero
- } THREADNAME_INFO;
-
- inline void setThreadName(const char *name) {
- THREADNAME_INFO info;
- info.dwType = 0x1000;
- info.szName = name;
- info.dwThreadID = -1;
- info.dwFlags = 0;
- __try {
- RaiseException(0x406D1388, 0, sizeof(info) / sizeof(DWORD), (ULONG_PTR *) &info);
- } __except (EXCEPTION_CONTINUE_EXECUTION) {}
- }
-#elif defined(__APPLE__)
- #include <pthread.h>
-
- inline void setThreadName(const char *name) {
- pthread_setname_np(name);
- }
-#elif defined(_WIN32) || defined(__GNU__)
- inline void setThreadName(const char* name) {}
-#else
- #warning "Unrecognized platform, thread names will not be available."
- inline void setThreadName(const char* name) {}
-#endif
#ifndef SERVER
float getDisplayDensity();
@@ -428,6 +367,7 @@ void setXorgClassHint(const video::SExposedVideoData &video_data,
// threads in the process inherit this exception handler
void setWin32ExceptionHandler();
+bool secure_rand_fill_buf(void *buf, size_t len);
} // namespace porting
#ifdef __ANDROID__
diff --git a/src/porting_android.cpp b/src/porting_android.cpp
index 6871ce465..72b625d73 100644
--- a/src/porting_android.cpp
+++ b/src/porting_android.cpp
@@ -21,12 +21,17 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#error This file may only be compiled for android!
#endif
+#include "util/numeric.h"
#include "porting.h"
#include "porting_android.h"
+#include "threading/thread.h"
#include "config.h"
#include "filesys.h"
#include "log.h"
+
#include <sstream>
+#include <exception>
+#include <stdlib.h>
#ifdef GPROF
#include "prof.h"
@@ -39,30 +44,23 @@ void android_main(android_app *app)
int retval = 0;
porting::app_global = app;
- porting::setThreadName("MainThread");
+ Thread::setName("Main");
try {
app_dummy();
- char *argv[] = { (char*) "minetest" };
- main(sizeof(argv) / sizeof(argv[0]), argv);
- }
- catch(BaseException e) {
- std::stringstream msg;
- msg << "Exception handled by main: " << e.what();
- const char* message = msg.str().c_str();
- __android_log_print(ANDROID_LOG_ERROR, PROJECT_NAME, "%s", message);
- errorstream << msg << std::endl;
+ char *argv[] = {strdup(PROJECT_NAME), NULL};
+ main(ARRLEN(argv) - 1, argv);
+ free(argv[0]);
+ } catch (std::exception &e) {
+ errorstream << "Uncaught exception in main thread: " << e.what() << std::endl;
retval = -1;
- }
- catch(...) {
- __android_log_print(ANDROID_LOG_ERROR, PROJECT_NAME,
- "Some exception occured");
+ } catch (...) {
errorstream << "Uncaught exception in main thread!" << std::endl;
retval = -1;
}
porting::cleanupAndroid();
- errorstream << "Shutting down minetest." << std::endl;
+ infostream << "Shutting down." << std::endl;
exit(retval);
}
@@ -126,7 +124,7 @@ void initAndroid()
JavaVM *jvm = app_global->activity->vm;
JavaVMAttachArgs lJavaVMAttachArgs;
lJavaVMAttachArgs.version = JNI_VERSION_1_6;
- lJavaVMAttachArgs.name = "MinetestNativeThread";
+ lJavaVMAttachArgs.name = PROJECT_NAME_C "NativeThread";
lJavaVMAttachArgs.group = NULL;
#ifdef NDEBUG
// This is a ugly hack as arm v7a non debuggable builds crash without this
@@ -147,7 +145,7 @@ void initAndroid()
#ifdef GPROF
/* in the start-up code */
- __android_log_print(ANDROID_LOG_ERROR, PROJECT_NAME,
+ __android_log_print(ANDROID_LOG_ERROR, PROJECT_NAME_C,
"Initializing GPROF profiler");
monstartup("libminetest.so");
#endif
@@ -166,29 +164,63 @@ void cleanupAndroid()
jvm->DetachCurrentThread();
}
-void setExternalStorageDir(JNIEnv* lJNIEnv)
+static std::string javaStringToUTF8(jstring js)
+{
+ std::string str;
+ // Get string as a UTF-8 c-string
+ const char *c_str = jnienv->GetStringUTFChars(js, NULL);
+ // Save it
+ str = c_str;
+ // And free the c-string
+ jnienv->ReleaseStringUTFChars(js, c_str);
+ return str;
+}
+
+// Calls static method if obj is NULL
+static std::string getAndroidPath(jclass cls, jobject obj, jclass cls_File,
+ jmethodID mt_getAbsPath, const char *getter)
+{
+ // Get getter method
+ jmethodID mt_getter;
+ if (obj)
+ mt_getter = jnienv->GetMethodID(cls, getter,
+ "()Ljava/io/File;");
+ else
+ mt_getter = jnienv->GetStaticMethodID(cls, getter,
+ "()Ljava/io/File;");
+
+ // Call getter
+ jobject ob_file;
+ if (obj)
+ ob_file = jnienv->CallObjectMethod(obj, mt_getter);
+ else
+ ob_file = jnienv->CallStaticObjectMethod(cls, mt_getter);
+
+ // Call getAbsolutePath
+ jstring js_path = (jstring) jnienv->CallObjectMethod(ob_file,
+ mt_getAbsPath);
+
+ return javaStringToUTF8(js_path);
+}
+
+void initializePathsAndroid()
{
- // Android: Retrieve ablsolute path to external storage device (sdcard)
- jclass ClassEnv = lJNIEnv->FindClass("android/os/Environment");
- jmethodID MethodDir =
- lJNIEnv->GetStaticMethodID(ClassEnv,
- "getExternalStorageDirectory","()Ljava/io/File;");
- jobject ObjectFile = lJNIEnv->CallStaticObjectMethod(ClassEnv, MethodDir);
- jclass ClassFile = lJNIEnv->FindClass("java/io/File");
-
- jmethodID MethodPath =
- lJNIEnv->GetMethodID(ClassFile, "getAbsolutePath",
- "()Ljava/lang/String;");
- jstring StringPath =
- (jstring) lJNIEnv->CallObjectMethod(ObjectFile, MethodPath);
-
- const char *externalPath = lJNIEnv->GetStringUTFChars(StringPath, NULL);
- std::string userPath(externalPath);
- lJNIEnv->ReleaseStringUTFChars(StringPath, externalPath);
-
- path_storage = userPath;
- path_user = userPath + DIR_DELIM + PROJECT_NAME;
- path_share = userPath + DIR_DELIM + PROJECT_NAME;
+ // Get Environment class
+ jclass cls_Env = jnienv->FindClass("android/os/Environment");
+ // Get File class
+ jclass cls_File = jnienv->FindClass("java/io/File");
+ // Get getAbsolutePath method
+ jmethodID mt_getAbsPath = jnienv->GetMethodID(cls_File,
+ "getAbsolutePath", "()Ljava/lang/String;");
+
+ path_cache = getAndroidPath(nativeActivity, app_global->activity->clazz,
+ cls_File, mt_getAbsPath, "getCacheDir");
+ path_storage = getAndroidPath(cls_Env, NULL, cls_File, mt_getAbsPath,
+ "getExternalStorageDirectory");
+ path_user = path_storage + DIR_DELIM + PROJECT_NAME_C;
+ path_share = path_storage + DIR_DELIM + PROJECT_NAME_C;
+
+ migrateCachePath();
}
void showInputDialog(const std::string& acceptButton, const std::string& hint,
@@ -241,7 +273,7 @@ std::string getInputDialogValue()
return text;
}
-#if not defined(SERVER)
+#ifndef SERVER
float getDisplayDensity()
{
static bool firstrun = true;
@@ -291,5 +323,5 @@ v2u32 getDisplaySize()
}
return retval;
}
-#endif //SERVER
+#endif // ndef SERVER
}
diff --git a/src/porting_android.h b/src/porting_android.h
index bfdadfbff..e4be0740d 100644
--- a/src/porting_android.h
+++ b/src/porting_android.h
@@ -43,10 +43,10 @@ void initAndroid();
void cleanupAndroid();
/**
- * set storage dir on external sdcard#
- * @param lJNIEnv environment from android
+ * Initializes path_* variables for Android
+ * @param env Android JNI environment
*/
-void setExternalStorageDir(JNIEnv* lJNIEnv);
+void initializePathsAndroid();
/**
* use java function to copy media from assets to external storage
diff --git a/src/profiler.h b/src/profiler.h
index 78d3b08e0..e8eac86b1 100644
--- a/src/profiler.h
+++ b/src/profiler.h
@@ -24,8 +24,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <string>
#include <map>
-#include "jthread/jmutex.h"
-#include "jthread/jmutexautolock.h"
+#include "threading/mutex.h"
+#include "threading/mutex_auto_lock.h"
#include "util/timetaker.h"
#include "util/numeric.h" // paging()
#include "debug.h" // assert()
@@ -49,7 +49,7 @@ public:
void add(const std::string &name, float value)
{
- JMutexAutoLock lock(m_mutex);
+ MutexAutoLock lock(m_mutex);
{
/* No average shall have been used; mark add used as -2 */
std::map<std::string, int>::iterator n = m_avgcounts.find(name);
@@ -72,7 +72,7 @@ public:
void avg(const std::string &name, float value)
{
- JMutexAutoLock lock(m_mutex);
+ MutexAutoLock lock(m_mutex);
int &count = m_avgcounts[name];
assert(count != -2);
@@ -82,7 +82,7 @@ public:
void clear()
{
- JMutexAutoLock lock(m_mutex);
+ MutexAutoLock lock(m_mutex);
for(std::map<std::string, float>::iterator
i = m_data.begin();
i != m_data.end(); ++i)
@@ -114,7 +114,7 @@ public:
void printPage(std::ostream &o, u32 page, u32 pagecount)
{
- JMutexAutoLock lock(m_mutex);
+ MutexAutoLock lock(m_mutex);
u32 minindex, maxindex;
paging(m_data.size(), page, pagecount, minindex, maxindex);
@@ -159,7 +159,7 @@ public:
void graphAdd(const std::string &id, float value)
{
- JMutexAutoLock lock(m_mutex);
+ MutexAutoLock lock(m_mutex);
std::map<std::string, float>::iterator i =
m_graphvalues.find(id);
if(i == m_graphvalues.end())
@@ -169,20 +169,20 @@ public:
}
void graphGet(GraphValues &result)
{
- JMutexAutoLock lock(m_mutex);
+ MutexAutoLock lock(m_mutex);
result = m_graphvalues;
m_graphvalues.clear();
}
void remove(const std::string& name)
{
- JMutexAutoLock lock(m_mutex);
+ MutexAutoLock lock(m_mutex);
m_avgcounts.erase(name);
m_data.erase(name);
}
private:
- JMutex m_mutex;
+ Mutex m_mutex;
std::map<std::string, float> m_data;
std::map<std::string, int> m_avgcounts;
std::map<std::string, float> m_graphvalues;
diff --git a/src/quicktune.cpp b/src/quicktune.cpp
index 2f9f987bd..b0e2dc6d5 100644
--- a/src/quicktune.cpp
+++ b/src/quicktune.cpp
@@ -18,8 +18,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
*/
#include "quicktune.h"
-#include "jthread/jmutex.h"
-#include "jthread/jmutexautolock.h"
+#include "threading/mutex.h"
+#include "threading/mutex_auto_lock.h"
#include "util/string.h"
std::string QuicktuneValue::getString()
@@ -49,12 +49,12 @@ void QuicktuneValue::relativeAdd(float amount)
static std::map<std::string, QuicktuneValue> g_values;
static std::vector<std::string> g_names;
-JMutex *g_mutex = NULL;
+Mutex *g_mutex = NULL;
static void makeMutex()
{
if(!g_mutex){
- g_mutex = new JMutex();
+ g_mutex = new Mutex();
}
}
@@ -66,7 +66,7 @@ std::vector<std::string> getQuicktuneNames()
QuicktuneValue getQuicktuneValue(const std::string &name)
{
makeMutex();
- JMutexAutoLock lock(*g_mutex);
+ MutexAutoLock lock(*g_mutex);
std::map<std::string, QuicktuneValue>::iterator i = g_values.find(name);
if(i == g_values.end()){
QuicktuneValue val;
@@ -79,7 +79,7 @@ QuicktuneValue getQuicktuneValue(const std::string &name)
void setQuicktuneValue(const std::string &name, const QuicktuneValue &val)
{
makeMutex();
- JMutexAutoLock lock(*g_mutex);
+ MutexAutoLock lock(*g_mutex);
g_values[name] = val;
g_values[name].modified = true;
}
@@ -87,7 +87,7 @@ void setQuicktuneValue(const std::string &name, const QuicktuneValue &val)
void updateQuicktuneValue(const std::string &name, QuicktuneValue &val)
{
makeMutex();
- JMutexAutoLock lock(*g_mutex);
+ MutexAutoLock lock(*g_mutex);
std::map<std::string, QuicktuneValue>::iterator i = g_values.find(name);
if(i == g_values.end()){
g_values[name] = val;
diff --git a/src/rollback.cpp b/src/rollback.cpp
index 7267a50f6..2367c3a21 100644
--- a/src/rollback.cpp
+++ b/src/rollback.cpp
@@ -107,6 +107,8 @@ RollbackManager::RollbackManager(const std::string & world_path,
RollbackManager::~RollbackManager()
{
+ flush();
+
SQLOK(sqlite3_finalize(stmt_insert));
SQLOK(sqlite3_finalize(stmt_replace));
SQLOK(sqlite3_finalize(stmt_select));
@@ -240,8 +242,7 @@ bool RollbackManager::createTables()
" FOREIGN KEY (`oldNode`) REFERENCES `node`(`id`),\n"
" FOREIGN KEY (`newNode`) REFERENCES `node`(`id`)\n"
");\n"
- "CREATE INDEX IF NOT EXISTS `actionActor` ON `action`(`actor`);\n"
- "CREATE INDEX IF NOT EXISTS `actionTimestamp` ON `action`(`timestamp`);\n",
+ "CREATE INDEX IF NOT EXISTS `actionIndex` ON `action`(`x`,`y`,`z`,`timestamp`,`actor`);\n",
NULL, NULL, NULL));
verbosestream << "SQL Rollback: SQLite3 database structure was created" << std::endl;
@@ -858,7 +859,7 @@ std::string RollbackManager::getSuspect(v3s16 p, float nearness_shortcut,
float likely_suspect_nearness = 0;
for (std::list<RollbackAction>::const_reverse_iterator
i = action_latest_buffer.rbegin();
- i != action_latest_buffer.rend(); i++) {
+ i != action_latest_buffer.rend(); ++i) {
if (i->unix_time < first_time) {
break;
}
@@ -897,7 +898,7 @@ void RollbackManager::flush()
for (iter = action_todisk_buffer.begin();
iter != action_todisk_buffer.end();
- iter++) {
+ ++iter) {
if (iter->actor == "") {
continue;
}
@@ -930,6 +931,7 @@ std::list<RollbackAction> RollbackManager::getEntriesSince(time_t first_time)
std::list<RollbackAction> RollbackManager::getNodeActors(v3s16 pos, int range,
time_t seconds, int limit)
{
+ flush();
time_t cur_time = time(0);
time_t first_time = cur_time - seconds;
diff --git a/src/rollback_interface.cpp b/src/rollback_interface.cpp
index b3f457029..bffe0a82c 100644
--- a/src/rollback_interface.cpp
+++ b/src/rollback_interface.cpp
@@ -155,7 +155,7 @@ bool RollbackAction::applyRevert(Map *map, InventoryManager *imgr, IGameDef *gam
} else {
NodeMetadata *meta = map->getNodeMetadata(p);
if (!meta) {
- meta = new NodeMetadata(gamedef);
+ meta = new NodeMetadata(gamedef->idef());
if (!map->setNodeMetadata(p, meta)) {
delete meta;
infostream << "RollbackAction::applyRevert(): "
diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp
index 3754fc2ff..06e20c2a0 100644
--- a/src/script/common/c_content.cpp
+++ b/src/script/common/c_content.cpp
@@ -76,7 +76,7 @@ ItemDefinition read_item_definition(lua_State* L,int index,
getboolfield(L, index, "liquids_pointable", def.liquids_pointable);
warn_if_field_exists(L, index, "tool_digging_properties",
- "deprecated: use tool_capabilities");
+ "Deprecated; use tool_capabilities");
lua_getfield(L, index, "tool_capabilities");
if(lua_istable(L, -1)){
@@ -100,6 +100,9 @@ ItemDefinition read_item_definition(lua_State* L,int index,
lua_getfield(L, -1, "place");
read_soundspec(L, -1, def.sound_place);
lua_pop(L, 1);
+ lua_getfield(L, -1, "place_failed");
+ read_soundspec(L, -1, def.sound_place_failed);
+ lua_pop(L, 1);
}
lua_pop(L, 1);
@@ -197,6 +200,23 @@ void read_object_properties(lua_State *L, int index,
prop->automatic_face_movement_dir_offset = 0.0;
}
lua_pop(L, 1);
+ getboolfield(L, -1, "backface_culling", prop->backface_culling);
+
+ getstringfield(L, -1, "nametag", prop->nametag);
+ lua_getfield(L, -1, "nametag_color");
+ if (!lua_isnil(L, -1)) {
+ video::SColor color = prop->nametag_color;
+ if (read_color(L, -1, &color))
+ prop->nametag_color = color;
+ }
+ lua_pop(L, 1);
+
+ lua_getfield(L, -1, "automatic_face_movement_max_rotation_per_sec");
+ if (lua_isnumber(L, -1)) {
+ prop->automatic_face_movement_max_rotation_per_sec = luaL_checknumber(L, -1);
+ }
+ lua_pop(L, 1);
+ getstringfield(L, -1, "infotext", prop->infotext);
}
/******************************************************************************/
@@ -255,6 +275,16 @@ void push_object_properties(lua_State *L, ObjectProperties *prop)
else
lua_pushboolean(L, false);
lua_setfield(L, -2, "automatic_face_movement_dir");
+ lua_pushboolean(L, prop->backface_culling);
+ lua_setfield(L, -2, "backface_culling");
+ lua_pushlstring(L, prop->nametag.c_str(), prop->nametag.size());
+ lua_setfield(L, -2, "nametag");
+ push_ARGB8(L, prop->nametag_color);
+ lua_setfield(L, -2, "nametag_color");
+ lua_pushnumber(L, prop->automatic_face_movement_max_rotation_per_sec);
+ lua_setfield(L, -2, "automatic_face_movement_max_rotation_per_sec");
+ lua_pushlstring(L, prop->infotext.c_str(), prop->infotext.size());
+ lua_setfield(L, -2, "infotext");
}
/******************************************************************************/
@@ -264,14 +294,31 @@ TileDef read_tiledef(lua_State *L, int index, u8 drawtype)
index = lua_gettop(L) + 1 + index;
TileDef tiledef;
- bool default_tiling = (drawtype == NDT_PLANTLIKE || drawtype == NDT_FIRELIKE)
- ? false : true;
+
+ bool default_tiling = true;
+ bool default_culling = true;
+ switch (drawtype) {
+ case NDT_PLANTLIKE:
+ case NDT_FIRELIKE:
+ default_tiling = false;
+ // "break" is omitted here intentionaly, as PLANTLIKE
+ // FIRELIKE drawtype both should default to having
+ // backface_culling to false.
+ case NDT_MESH:
+ case NDT_LIQUID:
+ default_culling = false;
+ break;
+ default:
+ break;
+ }
+
// key at index -2 and value at index
if(lua_isstring(L, index)){
// "default_lava.png"
tiledef.name = lua_tostring(L, index);
tiledef.tileable_vertical = default_tiling;
tiledef.tileable_horizontal = default_tiling;
+ tiledef.backface_culling = default_culling;
}
else if(lua_istable(L, index))
{
@@ -280,7 +327,7 @@ TileDef read_tiledef(lua_State *L, int index, u8 drawtype)
getstringfield(L, index, "name", tiledef.name);
getstringfield(L, index, "image", tiledef.name); // MaterialSpec compat.
tiledef.backface_culling = getboolfield_default(
- L, index, "backface_culling", true);
+ L, index, "backface_culling", default_culling);
tiledef.tileable_horizontal = getboolfield_default(
L, index, "tileable_horizontal", default_tiling);
tiledef.tileable_vertical = getboolfield_default(
@@ -427,17 +474,17 @@ ContentFeatures read_content_features(lua_State *L, int index)
// Warn about some deprecated fields
warn_if_field_exists(L, index, "wall_mounted",
- "deprecated: use paramtype2 = 'wallmounted'");
+ "Deprecated; use paramtype2 = 'wallmounted'");
warn_if_field_exists(L, index, "light_propagates",
- "deprecated: determined from paramtype");
+ "Deprecated; determined from paramtype");
warn_if_field_exists(L, index, "dug_item",
- "deprecated: use 'drop' field");
+ "Deprecated; use 'drop' field");
warn_if_field_exists(L, index, "extra_dug_item",
- "deprecated: use 'drop' field");
+ "Deprecated; use 'drop' field");
warn_if_field_exists(L, index, "extra_dug_item_rarity",
- "deprecated: use 'drop' field");
+ "Deprecated; use 'drop' field");
warn_if_field_exists(L, index, "metadata_name",
- "deprecated: use on_add and metadata callbacks");
+ "Deprecated; use on_add and metadata callbacks");
// True for all ground-like things like stone and mud, false for eg. trees
getboolfield(L, index, "is_ground_content", f.is_ground_content);
@@ -454,6 +501,8 @@ ContentFeatures read_content_features(lua_State *L, int index)
getboolfield(L, index, "climbable", f.climbable);
// Player can build on these
getboolfield(L, index, "buildable_to", f.buildable_to);
+ // Liquids flow into and replace node
+ getboolfield(L, index, "floodable", f.floodable);
// Whether the node is non-liquid, source liquid or flowing liquid
f.liquid_type = (LiquidType)getenumfield(L, index, "liquidtype",
ScriptApiNode::es_LiquidType, LIQUID_NONE);
@@ -486,6 +535,46 @@ ContentFeatures read_content_features(lua_State *L, int index)
f.node_box = read_nodebox(L, -1);
lua_pop(L, 1);
+ lua_getfield(L, index, "connects_to");
+ if (lua_istable(L, -1)) {
+ int table = lua_gettop(L);
+ lua_pushnil(L);
+ while (lua_next(L, table) != 0) {
+ // Value at -1
+ f.connects_to.push_back(lua_tostring(L, -1));
+ lua_pop(L, 1);
+ }
+ }
+ lua_pop(L, 1);
+
+ lua_getfield(L, index, "connect_sides");
+ if (lua_istable(L, -1)) {
+ int table = lua_gettop(L);
+ lua_pushnil(L);
+ while (lua_next(L, table) != 0) {
+ // Value at -1
+ std::string side(lua_tostring(L, -1));
+ // Note faces are flipped to make checking easier
+ if (side == "top")
+ f.connect_sides |= 2;
+ else if (side == "bottom")
+ f.connect_sides |= 1;
+ else if (side == "front")
+ f.connect_sides |= 16;
+ else if (side == "left")
+ f.connect_sides |= 32;
+ else if (side == "back")
+ f.connect_sides |= 4;
+ else if (side == "right")
+ f.connect_sides |= 8;
+ else
+ warningstream << "Unknown value for \"connect_sides\": "
+ << side << std::endl;
+ lua_pop(L, 1);
+ }
+ }
+ lua_pop(L, 1);
+
lua_getfield(L, index, "selection_box");
if(lua_istable(L, -1))
f.selection_box = read_nodebox(L, -1);
@@ -578,25 +667,31 @@ NodeBox read_nodebox(lua_State *L, int index)
nodebox.type = (NodeBoxType)getenumfield(L, index, "type",
ScriptApiNode::es_NodeBoxType, NODEBOX_REGULAR);
- lua_getfield(L, index, "fixed");
- if(lua_istable(L, -1))
- nodebox.fixed = read_aabb3f_vector(L, -1, BS);
- lua_pop(L, 1);
-
- lua_getfield(L, index, "wall_top");
- if(lua_istable(L, -1))
- nodebox.wall_top = read_aabb3f(L, -1, BS);
- lua_pop(L, 1);
-
- lua_getfield(L, index, "wall_bottom");
- if(lua_istable(L, -1))
- nodebox.wall_bottom = read_aabb3f(L, -1, BS);
- lua_pop(L, 1);
-
- lua_getfield(L, index, "wall_side");
- if(lua_istable(L, -1))
- nodebox.wall_side = read_aabb3f(L, -1, BS);
- lua_pop(L, 1);
+#define NODEBOXREAD(n, s) \
+ do { \
+ lua_getfield(L, index, (s)); \
+ if (lua_istable(L, -1)) \
+ (n) = read_aabb3f(L, -1, BS); \
+ lua_pop(L, 1); \
+ } while (0)
+
+#define NODEBOXREADVEC(n, s) \
+ do { \
+ lua_getfield(L, index, (s)); \
+ if (lua_istable(L, -1)) \
+ (n) = read_aabb3f_vector(L, -1, BS); \
+ lua_pop(L, 1); \
+ } while (0)
+ NODEBOXREADVEC(nodebox.fixed, "fixed");
+ NODEBOXREAD(nodebox.wall_top, "wall_top");
+ NODEBOXREAD(nodebox.wall_bottom, "wall_bottom");
+ NODEBOXREAD(nodebox.wall_side, "wall_side");
+ NODEBOXREADVEC(nodebox.connect_top, "connect_top");
+ NODEBOXREADVEC(nodebox.connect_bottom, "connect_bottom");
+ NODEBOXREADVEC(nodebox.connect_front, "connect_front");
+ NODEBOXREADVEC(nodebox.connect_left, "connect_left");
+ NODEBOXREADVEC(nodebox.connect_back, "connect_back");
+ NODEBOXREADVEC(nodebox.connect_right, "connect_right");
}
return nodebox;
}
@@ -639,14 +734,13 @@ void pushnode(lua_State *L, const MapNode &n, INodeDefManager *ndef)
/******************************************************************************/
void warn_if_field_exists(lua_State *L, int table,
- const char *fieldname, const std::string &message)
+ const char *name, const std::string &message)
{
- lua_getfield(L, table, fieldname);
- if(!lua_isnil(L, -1)){
-//TODO find way to access backtrace fct from here
- // infostream<<script_get_backtrace(L)<<std::endl;
- infostream<<"WARNING: field \""<<fieldname<<"\": "
- <<message<<std::endl;
+ lua_getfield(L, table, name);
+ if (!lua_isnil(L, -1)) {
+ warningstream << "Field \"" << name << "\": "
+ << message << std::endl;
+ infostream << script_get_backtrace(L) << std::endl;
}
lua_pop(L, 1);
}
@@ -705,7 +799,7 @@ ItemStack read_item(lua_State* L, int index,Server* srv)
}
catch(SerializationError &e)
{
- infostream<<"WARNING: unable to create item from itemstring"
+ warningstream<<"unable to create item from itemstring"
<<": "<<itemstring<<std::endl;
return ItemStack();
}
@@ -840,14 +934,14 @@ ToolCapabilities read_tool_capabilities(
getintfield(L, table_groupcap, "uses", groupcap.uses);
// DEPRECATED: maxwear
float maxwear = 0;
- if(getfloatfield(L, table_groupcap, "maxwear", maxwear)){
- if(maxwear != 0)
+ if (getfloatfield(L, table_groupcap, "maxwear", maxwear)){
+ if (maxwear != 0)
groupcap.uses = 1.0/maxwear;
else
groupcap.uses = 0;
- infostream<<script_get_backtrace(L)<<std::endl;
- infostream<<"WARNING: field \"maxwear\" is deprecated; "
- <<"should replace with uses=1/maxwear"<<std::endl;
+ warningstream << "Field \"maxwear\" is deprecated; "
+ << "replace with uses=1/maxwear" << std::endl;
+ infostream << script_get_backtrace(L) << std::endl;
}
// Read "times" table
lua_getfield(L, table_groupcap, "times");
@@ -1232,4 +1326,3 @@ void read_json_value(lua_State *L, Json::Value &root, int index, u8 recursion)
}
lua_pop(L, 1); // Pop value
}
-
diff --git a/src/script/common/c_converter.cpp b/src/script/common/c_converter.cpp
index f1d3cc421..55c4a5f5a 100644
--- a/src/script/common/c_converter.cpp
+++ b/src/script/common/c_converter.cpp
@@ -517,6 +517,15 @@ bool getboolfield_default(lua_State *L, int table,
return result;
}
+void setstringfield(lua_State *L, int table,
+ const char *fieldname, const char *value)
+{
+ lua_pushstring(L, value);
+ if(table < 0)
+ table -= 1;
+ lua_setfield(L, table, fieldname);
+}
+
void setintfield(lua_State *L, int table,
const char *fieldname, int value)
{
diff --git a/src/script/common/c_converter.h b/src/script/common/c_converter.h
index 18a045d2a..eefac0ed7 100644
--- a/src/script/common/c_converter.h
+++ b/src/script/common/c_converter.h
@@ -69,6 +69,8 @@ bool getfloatfield(lua_State *L, int table,
std::string checkstringfield(lua_State *L, int table,
const char *fieldname);
+void setstringfield(lua_State *L, int table,
+ const char *fieldname, const char *value);
void setintfield(lua_State *L, int table,
const char *fieldname, int value);
void setfloatfield(lua_State *L, int table,
diff --git a/src/script/common/c_internal.cpp b/src/script/common/c_internal.cpp
index 2a10ce0f2..b349f9dd1 100644
--- a/src/script/common/c_internal.cpp
+++ b/src/script/common/c_internal.cpp
@@ -136,61 +136,56 @@ void script_run_callbacks_f(lua_State *L, int nargs,
FATAL_ERROR_IF(lua_gettop(L) < nargs + 1, "Not enough arguments");
// Insert error handler
- lua_pushcfunction(L, script_error_handler);
- int errorhandler = lua_gettop(L) - nargs - 1;
- lua_insert(L, errorhandler);
+ PUSH_ERROR_HANDLER(L);
+ int error_handler = lua_gettop(L) - nargs - 1;
+ lua_insert(L, error_handler);
// Insert run_callbacks between error handler and table
lua_getglobal(L, "core");
lua_getfield(L, -1, "run_callbacks");
lua_remove(L, -2);
- lua_insert(L, errorhandler + 1);
+ lua_insert(L, error_handler + 1);
// Insert mode after table
lua_pushnumber(L, (int) mode);
- lua_insert(L, errorhandler + 3);
+ lua_insert(L, error_handler + 3);
// Stack now looks like this:
// ... <error handler> <run_callbacks> <table> <mode> <arg#1> <arg#2> ... <arg#n>
- int result = lua_pcall(L, nargs + 2, 1, errorhandler);
+ int result = lua_pcall(L, nargs + 2, 1, error_handler);
if (result != 0)
script_error(L, result, NULL, fxn);
- lua_remove(L, -2); // Remove error handler
+ lua_remove(L, error_handler);
}
void log_deprecated(lua_State *L, const std::string &message)
{
static bool configured = false;
- static bool dolog = false;
- static bool doerror = false;
+ static bool do_log = false;
+ static bool do_error = false;
- // performance optimization to not have to read and compare setting for every logline
+ // Only read settings on first call
if (!configured) {
std::string value = g_settings->get("deprecated_lua_api_handling");
if (value == "log") {
- dolog = true;
+ do_log = true;
} else if (value == "error") {
- dolog = true;
- doerror = true;
+ do_log = true;
+ do_error = true;
}
}
- if (doerror) {
- if (L != NULL) {
- script_error(L, LUA_ERRRUN, NULL, NULL);
- } else {
- FATAL_ERROR("Can't do a scripterror for this deprecated message, "
- "so exit completely!");
- }
- }
-
- if (dolog) {
- /* abusing actionstream because of lack of file-only-logged loglevel */
- actionstream << message << std::endl;
- if (L != NULL) {
- actionstream << script_get_backtrace(L) << std::endl;
+ if (do_log) {
+ warningstream << message << std::endl;
+ // L can be NULL if we get called by log_deprecated(const std::string &msg)
+ // from scripting_game.cpp.
+ if (L) {
+ if (do_error)
+ script_error(L, LUA_ERRRUN, NULL, NULL);
+ else
+ infostream << script_get_backtrace(L) << std::endl;
}
}
}
diff --git a/src/script/common/c_internal.h b/src/script/common/c_internal.h
index ecb514c8f..fc59b0e2e 100644
--- a/src/script/common/c_internal.h
+++ b/src/script/common/c_internal.h
@@ -34,6 +34,31 @@ extern "C" {
#include "common/c_types.h"
+
+/*
+ Define our custom indices into the Lua registry table.
+
+ Lua 5.2 and above define the LUA_RIDX_LAST macro. Only numbers above that
+ may be used for custom indices, anything else is reserved.
+
+ Lua 5.1 / LuaJIT do not use any numeric indices (only string indices),
+ so we can use numeric indices freely.
+*/
+#ifdef LUA_RIDX_LAST
+#define CUSTOM_RIDX_BASE ((LUA_RIDX_LAST)+1)
+#else
+#define CUSTOM_RIDX_BASE 1
+#endif
+
+#define CUSTOM_RIDX_SCRIPTAPI (CUSTOM_RIDX_BASE)
+#define CUSTOM_RIDX_GLOBALS_BACKUP (CUSTOM_RIDX_BASE + 1)
+#define CUSTOM_RIDX_CURRENT_MOD_NAME (CUSTOM_RIDX_BASE + 2)
+#define CUSTOM_RIDX_ERROR_HANDLER (CUSTOM_RIDX_BASE + 3)
+
+// Pushes the error handler onto the stack and returns its index
+#define PUSH_ERROR_HANDLER(L) \
+ (lua_rawgeti((L), LUA_REGISTRYINDEX, CUSTOM_RIDX_ERROR_HANDLER), lua_gettop((L)))
+
#define PCALL_RESL(L, RES) do { \
int result_ = (RES); \
if (result_ != 0) { \
diff --git a/src/script/common/c_types.h b/src/script/common/c_types.h
index 706470737..056f30251 100644
--- a/src/script/common/c_types.h
+++ b/src/script/common/c_types.h
@@ -52,10 +52,10 @@ public:
}
};
-class LuaError : public ServerError
+class LuaError : public ModError
{
public:
- LuaError(const std::string &s) : ServerError(s) {}
+ LuaError(const std::string &s) : ModError(s) {}
};
diff --git a/src/script/cpp_api/s_async.cpp b/src/script/cpp_api/s_async.cpp
index c00b22f98..9bf3fcf49 100644
--- a/src/script/cpp_api/s_async.cpp
+++ b/src/script/cpp_api/s_async.cpp
@@ -47,32 +47,31 @@ AsyncEngine::~AsyncEngine()
// Request all threads to stop
for (std::vector<AsyncWorkerThread *>::iterator it = workerThreads.begin();
it != workerThreads.end(); it++) {
- (*it)->Stop();
+ (*it)->stop();
}
// Wake up all threads
for (std::vector<AsyncWorkerThread *>::iterator it = workerThreads.begin();
it != workerThreads.end(); it++) {
- jobQueueCounter.Post();
+ jobQueueCounter.post();
}
// Wait for threads to finish
for (std::vector<AsyncWorkerThread *>::iterator it = workerThreads.begin();
it != workerThreads.end(); it++) {
- (*it)->Wait();
+ (*it)->wait();
}
// Force kill all threads
for (std::vector<AsyncWorkerThread *>::iterator it = workerThreads.begin();
it != workerThreads.end(); it++) {
- (*it)->Kill();
delete *it;
}
- jobQueueMutex.Lock();
+ jobQueueMutex.lock();
jobQueue.clear();
- jobQueueMutex.Unlock();
+ jobQueueMutex.unlock();
workerThreads.clear();
}
@@ -92,16 +91,17 @@ void AsyncEngine::initialize(unsigned int numEngines)
initDone = true;
for (unsigned int i = 0; i < numEngines; i++) {
- AsyncWorkerThread *toAdd = new AsyncWorkerThread(this, i);
+ AsyncWorkerThread *toAdd = new AsyncWorkerThread(this,
+ std::string("AsyncWorker-") + itos(i));
workerThreads.push_back(toAdd);
- toAdd->Start();
+ toAdd->start();
}
}
/******************************************************************************/
unsigned int AsyncEngine::queueAsyncJob(std::string func, std::string params)
{
- jobQueueMutex.Lock();
+ jobQueueMutex.lock();
LuaJobInfo toAdd;
toAdd.id = jobIdCounter++;
toAdd.serializedFunction = func;
@@ -109,9 +109,9 @@ unsigned int AsyncEngine::queueAsyncJob(std::string func, std::string params)
jobQueue.push_back(toAdd);
- jobQueueCounter.Post();
+ jobQueueCounter.post();
- jobQueueMutex.Unlock();
+ jobQueueMutex.unlock();
return toAdd.id;
}
@@ -119,8 +119,8 @@ unsigned int AsyncEngine::queueAsyncJob(std::string func, std::string params)
/******************************************************************************/
LuaJobInfo AsyncEngine::getJob()
{
- jobQueueCounter.Wait();
- jobQueueMutex.Lock();
+ jobQueueCounter.wait();
+ jobQueueMutex.lock();
LuaJobInfo retval;
retval.valid = false;
@@ -130,7 +130,7 @@ LuaJobInfo AsyncEngine::getJob()
jobQueue.pop_front();
retval.valid = true;
}
- jobQueueMutex.Unlock();
+ jobQueueMutex.unlock();
return retval;
}
@@ -138,16 +138,17 @@ LuaJobInfo AsyncEngine::getJob()
/******************************************************************************/
void AsyncEngine::putJobResult(LuaJobInfo result)
{
- resultQueueMutex.Lock();
+ resultQueueMutex.lock();
resultQueue.push_back(result);
- resultQueueMutex.Unlock();
+ resultQueueMutex.unlock();
}
/******************************************************************************/
-void AsyncEngine::step(lua_State *L, int errorhandler)
+void AsyncEngine::step(lua_State *L)
{
+ int error_handler = PUSH_ERROR_HANDLER(L);
lua_getglobal(L, "core");
- resultQueueMutex.Lock();
+ resultQueueMutex.lock();
while (!resultQueue.empty()) {
LuaJobInfo jobDone = resultQueue.front();
resultQueue.pop_front();
@@ -164,16 +165,16 @@ void AsyncEngine::step(lua_State *L, int errorhandler)
lua_pushlstring(L, jobDone.serializedResult.data(),
jobDone.serializedResult.size());
- PCALL_RESL(L, lua_pcall(L, 2, 0, errorhandler));
+ PCALL_RESL(L, lua_pcall(L, 2, 0, error_handler));
}
- resultQueueMutex.Unlock();
- lua_pop(L, 1); // Pop core
+ resultQueueMutex.unlock();
+ lua_pop(L, 2); // Pop core and error handler
}
/******************************************************************************/
void AsyncEngine::pushFinishedJobs(lua_State* L) {
// Result Table
- resultQueueMutex.Lock();
+ MutexAutoLock l(resultQueueMutex);
unsigned int index = 1;
lua_createtable(L, resultQueue.size(), 0);
@@ -197,8 +198,6 @@ void AsyncEngine::pushFinishedJobs(lua_State* L) {
lua_rawseti(L, top, index++);
}
-
- resultQueueMutex.Unlock();
}
/******************************************************************************/
@@ -214,10 +213,10 @@ void AsyncEngine::prepareEnvironment(lua_State* L, int top)
/******************************************************************************/
AsyncWorkerThread::AsyncWorkerThread(AsyncEngine* jobDispatcher,
- unsigned int threadNum) :
+ const std::string &name) :
+ Thread(name),
ScriptApiBase(),
- jobDispatcher(jobDispatcher),
- threadnum(threadNum)
+ jobDispatcher(jobDispatcher)
{
lua_State *L = getStack();
@@ -235,50 +234,42 @@ AsyncWorkerThread::AsyncWorkerThread(AsyncEngine* jobDispatcher,
/******************************************************************************/
AsyncWorkerThread::~AsyncWorkerThread()
{
- sanity_check(IsRunning() == false);
+ sanity_check(!isRunning());
}
/******************************************************************************/
-void* AsyncWorkerThread::Thread()
+void* AsyncWorkerThread::run()
{
- ThreadStarted();
-
- // Register thread for error logging
- char number[21];
- snprintf(number, sizeof(number), "%u", threadnum);
- log_register_thread(std::string("AsyncWorkerThread_") + number);
-
- porting::setThreadName((std::string("AsyncWorkTh_") + number).c_str());
-
lua_State *L = getStack();
std::string script = getServer()->getBuiltinLuaPath() + DIR_DELIM + "init.lua";
- if (!loadScript(script)) {
- errorstream
- << "AsyncWorkerThread execution of async base environment failed!"
- << std::endl;
- abort();
+ try {
+ loadScript(script);
+ } catch (const ModError &e) {
+ errorstream << "Execution of async base environment failed: "
+ << e.what() << std::endl;
+ FATAL_ERROR("Execution of async base environment failed");
}
+ int error_handler = PUSH_ERROR_HANDLER(L);
+
lua_getglobal(L, "core");
if (lua_isnil(L, -1)) {
- errorstream << "Unable to find core within async environment!";
- abort();
+ FATAL_ERROR("Unable to find core within async environment!");
}
// Main loop
- while (!StopRequested()) {
+ while (!stopRequested()) {
// Wait for job
LuaJobInfo toProcess = jobDispatcher->getJob();
- if (toProcess.valid == false || StopRequested()) {
+ if (toProcess.valid == false || stopRequested()) {
continue;
}
lua_getfield(L, -1, "job_processor");
if (lua_isnil(L, -1)) {
- errorstream << "Unable to get async job processor!" << std::endl;
- abort();
+ FATAL_ERROR("Unable to get async job processor!");
}
luaL_checktype(L, -1, LUA_TFUNCTION);
@@ -291,7 +282,7 @@ void* AsyncWorkerThread::Thread()
toProcess.serializedParams.data(),
toProcess.serializedParams.size());
- int result = lua_pcall(L, 2, 1, m_errorhandler);
+ int result = lua_pcall(L, 2, 1, error_handler);
if (result) {
PCALL_RES(result);
toProcess.serializedResult = "";
@@ -308,9 +299,7 @@ void* AsyncWorkerThread::Thread()
jobDispatcher->putJobResult(toProcess);
}
- lua_pop(L, 1); // Pop core
-
- log_deregister_thread();
+ lua_pop(L, 2); // Pop core and error handler
return 0;
}
diff --git a/src/script/cpp_api/s_async.h b/src/script/cpp_api/s_async.h
index a6459c18d..8d612d58c 100644
--- a/src/script/cpp_api/s_async.h
+++ b/src/script/cpp_api/s_async.h
@@ -24,9 +24,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <deque>
#include <map>
-#include "jthread/jthread.h"
-#include "jthread/jmutex.h"
-#include "jthread/jsemaphore.h"
+#include "threading/thread.h"
+#include "threading/mutex.h"
+#include "threading/semaphore.h"
#include "debug.h"
#include "lua.h"
#include "cpp_api/s_base.h"
@@ -52,24 +52,15 @@ struct LuaJobInfo {
};
// Asynchronous working environment
-class AsyncWorkerThread : public JThread, public ScriptApiBase {
+class AsyncWorkerThread : public Thread, public ScriptApiBase {
public:
- /**
- * default constructor
- * @param pointer to job dispatcher
- */
- AsyncWorkerThread(AsyncEngine* jobDispatcher, unsigned int threadNum);
-
+ AsyncWorkerThread(AsyncEngine* jobDispatcher, const std::string &name);
virtual ~AsyncWorkerThread();
- void *Thread();
+ void *run();
private:
AsyncEngine *jobDispatcher;
-
- // Thread number. Used for debug output
- unsigned int threadnum;
-
};
// Asynchornous thread and job management
@@ -104,9 +95,8 @@ public:
* Engine step to process finished jobs
* the engine step is one way to pass events back, PushFinishedJobs another
* @param L The Lua stack
- * @param errorhandler Stack index of the Lua error handler
*/
- void step(lua_State *L, int errorhandler);
+ void step(lua_State *L);
/**
* Push a list of finished jobs onto the stack
@@ -148,13 +138,13 @@ private:
unsigned int jobIdCounter;
// Mutex to protect job queue
- JMutex jobQueueMutex;
+ Mutex jobQueueMutex;
// Job queue
std::deque<LuaJobInfo> jobQueue;
// Mutex to protect result queue
- JMutex resultQueueMutex;
+ Mutex resultQueueMutex;
// Result queue
std::deque<LuaJobInfo> resultQueue;
@@ -162,7 +152,7 @@ private:
std::vector<AsyncWorkerThread*> workerThreads;
// Counter semaphore for job dispatching
- JSemaphore jobQueueCounter;
+ Semaphore jobQueueCounter;
};
#endif // CPP_API_ASYNC_EVENTS_HEADER
diff --git a/src/script/cpp_api/s_base.cpp b/src/script/cpp_api/s_base.cpp
index dcfbac4bf..679a517ee 100644
--- a/src/script/cpp_api/s_base.cpp
+++ b/src/script/cpp_api/s_base.cpp
@@ -52,13 +52,13 @@ public:
{
// Store current mod name in registry
lua_pushstring(L, mod_name.c_str());
- lua_setfield(L, LUA_REGISTRYINDEX, SCRIPT_MOD_NAME_FIELD);
+ lua_rawseti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_CURRENT_MOD_NAME);
}
~ModNameStorer()
{
// Clear current mod name from registry
lua_pushnil(L);
- lua_setfield(L, LUA_REGISTRYINDEX, SCRIPT_MOD_NAME_FIELD);
+ lua_rawseti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_CURRENT_MOD_NAME);
}
};
@@ -67,10 +67,11 @@ public:
ScriptApiBase
*/
-ScriptApiBase::ScriptApiBase()
+ScriptApiBase::ScriptApiBase() :
+ m_luastackmutex()
{
#ifdef SCRIPTAPI_LOCK_DEBUG
- m_locked = false;
+ m_lock_recursion_count = 0;
#endif
m_luastack = luaL_newstate();
@@ -78,13 +79,13 @@ ScriptApiBase::ScriptApiBase()
luaL_openlibs(m_luastack);
- // Add and save an error handler
- lua_pushcfunction(m_luastack, script_error_handler);
- m_errorhandler = lua_gettop(m_luastack);
-
// Make the ScriptApiBase* accessible to ModApiBase
lua_pushlightuserdata(m_luastack, this);
- lua_setfield(m_luastack, LUA_REGISTRYINDEX, "scriptapi");
+ lua_rawseti(m_luastack, LUA_REGISTRYINDEX, CUSTOM_RIDX_SCRIPTAPI);
+
+ // Add and save an error handler
+ lua_pushcfunction(m_luastack, script_error_handler);
+ lua_rawseti(m_luastack, LUA_REGISTRYINDEX, CUSTOM_RIDX_ERROR_HANDLER);
// If we are using LuaJIT add a C++ wrapper function to catch
// exceptions thrown in Lua -> C++ calls
@@ -119,40 +120,36 @@ ScriptApiBase::~ScriptApiBase()
lua_close(m_luastack);
}
-bool ScriptApiBase::loadMod(const std::string &script_path,
- const std::string &mod_name, std::string *error)
+void ScriptApiBase::loadMod(const std::string &script_path,
+ const std::string &mod_name)
{
ModNameStorer mod_name_storer(getStack(), mod_name);
- return loadScript(script_path, error);
+ loadScript(script_path);
}
-bool ScriptApiBase::loadScript(const std::string &script_path, std::string *error)
+void ScriptApiBase::loadScript(const std::string &script_path)
{
verbosestream << "Loading and running script from " << script_path << std::endl;
lua_State *L = getStack();
+ int error_handler = PUSH_ERROR_HANDLER(L);
+
bool ok;
if (m_secure) {
ok = ScriptApiSecurity::safeLoadFile(L, script_path.c_str());
} else {
ok = !luaL_loadfile(L, script_path.c_str());
}
- ok = ok && !lua_pcall(L, 0, 0, m_errorhandler);
+ ok = ok && !lua_pcall(L, 0, 0, error_handler);
if (!ok) {
std::string error_msg = lua_tostring(L, -1);
- if (error)
- *error = error_msg;
- errorstream << "========== ERROR FROM LUA ===========" << std::endl
- << "Failed to load and run script from " << std::endl
- << script_path << ":" << std::endl << std::endl
- << error_msg << std::endl << std::endl
- << "======= END OF ERROR FROM LUA ========" << std::endl;
- lua_pop(L, 1); // Pop error message from stack
- return false;
+ lua_pop(L, 2); // Pop error message and error handler
+ throw ModError("Failed to load and run script from " +
+ script_path + ":\n" + error_msg);
}
- return true;
+ lua_pop(L, 1); // Pop error handler
}
// Push the list of callbacks (a lua table).
@@ -161,35 +158,40 @@ bool ScriptApiBase::loadScript(const std::string &script_path, std::string *erro
// - runs the callbacks
// - replaces the table and arguments with the return value,
// computed depending on mode
+// This function must only be called with scriptlock held (i.e. inside of a
+// code block with SCRIPTAPI_PRECHECKHEADER declared)
void ScriptApiBase::runCallbacksRaw(int nargs,
RunCallbacksMode mode, const char *fxn)
{
+#ifdef SCRIPTAPI_LOCK_DEBUG
+ assert(m_lock_recursion_count > 0);
+#endif
lua_State *L = getStack();
FATAL_ERROR_IF(lua_gettop(L) < nargs + 1, "Not enough arguments");
// Insert error handler
- lua_pushcfunction(L, script_error_handler);
- int errorhandler = lua_gettop(L) - nargs - 1;
- lua_insert(L, errorhandler);
+ PUSH_ERROR_HANDLER(L);
+ int error_handler = lua_gettop(L) - nargs - 1;
+ lua_insert(L, error_handler);
// Insert run_callbacks between error handler and table
lua_getglobal(L, "core");
lua_getfield(L, -1, "run_callbacks");
lua_remove(L, -2);
- lua_insert(L, errorhandler + 1);
+ lua_insert(L, error_handler + 1);
// Insert mode after table
lua_pushnumber(L, (int)mode);
- lua_insert(L, errorhandler + 3);
+ lua_insert(L, error_handler + 3);
// Stack now looks like this:
// ... <error handler> <run_callbacks> <table> <mode> <arg#1> <arg#2> ... <arg#n>
- int result = lua_pcall(L, nargs + 2, 1, errorhandler);
+ int result = lua_pcall(L, nargs + 2, 1, error_handler);
if (result != 0)
scriptError(result, fxn);
- lua_remove(L, -2); // Remove error handler
+ lua_remove(L, error_handler);
}
void ScriptApiBase::realityCheck()
diff --git a/src/script/cpp_api/s_base.h b/src/script/cpp_api/s_base.h
index d653b5bac..f52474f00 100644
--- a/src/script/cpp_api/s_base.h
+++ b/src/script/cpp_api/s_base.h
@@ -28,15 +28,15 @@ extern "C" {
}
#include "irrlichttypes.h"
-#include "jthread/jmutex.h"
-#include "jthread/jmutexautolock.h"
+#include "threads.h"
+#include "threading/mutex.h"
+#include "threading/mutex_auto_lock.h"
#include "common/c_types.h"
#include "common/c_internal.h"
#define SCRIPTAPI_LOCK_DEBUG
#define SCRIPTAPI_DEBUG
-#define SCRIPT_MOD_NAME_FIELD "current_mod_name"
// MUST be an invalid mod name so that mods can't
// use that name to bypass security!
#define BUILTIN_MOD_NAME "*builtin*"
@@ -64,9 +64,9 @@ public:
ScriptApiBase();
virtual ~ScriptApiBase();
- bool loadMod(const std::string &script_path, const std::string &mod_name,
- std::string *error=NULL);
- bool loadScript(const std::string &script_path, std::string *error=NULL);
+ // These throw a ModError on failure
+ void loadMod(const std::string &script_path, const std::string &mod_name);
+ void loadScript(const std::string &script_path);
void runCallbacksRaw(int nargs,
RunCallbacksMode mode, const char *fxn);
@@ -83,6 +83,7 @@ public:
protected:
friend class LuaABM;
+ friend class LuaLBM;
friend class InvRef;
friend class ObjectRef;
friend class NodeMetaRef;
@@ -108,13 +109,12 @@ protected:
void objectrefGetOrCreate(lua_State *L, ServerActiveObject *cobj);
void objectrefGet(lua_State *L, u16 id);
- JMutex m_luastackmutex;
+ RecursiveMutex m_luastackmutex;
std::string m_last_run_mod;
- // Stack index of Lua error handler
- int m_errorhandler;
bool m_secure;
#ifdef SCRIPTAPI_LOCK_DEBUG
- bool m_locked;
+ int m_lock_recursion_count;
+ threadid_t m_owning_thread;
#endif
private:
diff --git a/src/script/cpp_api/s_entity.cpp b/src/script/cpp_api/s_entity.cpp
index 0d159846a..378a6bf09 100644
--- a/src/script/cpp_api/s_entity.cpp
+++ b/src/script/cpp_api/s_entity.cpp
@@ -80,6 +80,8 @@ void ScriptApiEntity::luaentity_Activate(u16 id,
verbosestream << "scriptapi_luaentity_activate: id=" << id << std::endl;
+ int error_handler = PUSH_ERROR_HANDLER(L);
+
// Get core.luaentities[id]
luaentity_get(L, id);
int object = lua_gettop(L);
@@ -93,11 +95,11 @@ void ScriptApiEntity::luaentity_Activate(u16 id,
lua_pushinteger(L, dtime_s);
setOriginFromTable(object);
- PCALL_RES(lua_pcall(L, 3, 0, m_errorhandler));
+ PCALL_RES(lua_pcall(L, 3, 0, error_handler));
} else {
lua_pop(L, 1);
}
- lua_pop(L, 1); // Pop object
+ lua_pop(L, 2); // Pop object and error handler
}
void ScriptApiEntity::luaentity_Remove(u16 id)
@@ -126,6 +128,8 @@ std::string ScriptApiEntity::luaentity_GetStaticdata(u16 id)
//infostream<<"scriptapi_luaentity_get_staticdata: id="<<id<<std::endl;
+ int error_handler = PUSH_ERROR_HANDLER(L);
+
// Get core.luaentities[id]
luaentity_get(L, id);
int object = lua_gettop(L);
@@ -140,9 +144,10 @@ std::string ScriptApiEntity::luaentity_GetStaticdata(u16 id)
lua_pushvalue(L, object); // self
setOriginFromTable(object);
- PCALL_RES(lua_pcall(L, 1, 1, m_errorhandler));
+ PCALL_RES(lua_pcall(L, 1, 1, error_handler));
- lua_remove(L, object); // Remove object
+ lua_remove(L, object);
+ lua_remove(L, error_handler);
size_t len = 0;
const char *s = lua_tolstring(L, -1, &len);
@@ -196,6 +201,8 @@ void ScriptApiEntity::luaentity_Step(u16 id, float dtime)
//infostream<<"scriptapi_luaentity_step: id="<<id<<std::endl;
+ int error_handler = PUSH_ERROR_HANDLER(L);
+
// Get core.luaentities[id]
luaentity_get(L, id);
int object = lua_gettop(L);
@@ -211,9 +218,9 @@ void ScriptApiEntity::luaentity_Step(u16 id, float dtime)
lua_pushnumber(L, dtime); // dtime
setOriginFromTable(object);
- PCALL_RES(lua_pcall(L, 2, 0, m_errorhandler));
+ PCALL_RES(lua_pcall(L, 2, 0, error_handler));
- lua_pop(L, 1); // Pop object
+ lua_pop(L, 2); // Pop object and error handler
}
// Calls entity:on_punch(ObjectRef puncher, time_from_last_punch,
@@ -226,6 +233,8 @@ void ScriptApiEntity::luaentity_Punch(u16 id,
//infostream<<"scriptapi_luaentity_step: id="<<id<<std::endl;
+ int error_handler = PUSH_ERROR_HANDLER(L);
+
// Get core.luaentities[id]
luaentity_get(L,id);
int object = lua_gettop(L);
@@ -244,9 +253,9 @@ void ScriptApiEntity::luaentity_Punch(u16 id,
push_v3f(L, dir);
setOriginFromTable(object);
- PCALL_RES(lua_pcall(L, 5, 0, m_errorhandler));
+ PCALL_RES(lua_pcall(L, 5, 0, error_handler));
- lua_pop(L, 1); // Pop object
+ lua_pop(L, 2); // Pop object and error handler
}
// Calls entity:on_rightclick(ObjectRef clicker)
@@ -257,6 +266,8 @@ void ScriptApiEntity::luaentity_Rightclick(u16 id,
//infostream<<"scriptapi_luaentity_step: id="<<id<<std::endl;
+ int error_handler = PUSH_ERROR_HANDLER(L);
+
// Get core.luaentities[id]
luaentity_get(L, id);
int object = lua_gettop(L);
@@ -272,8 +283,8 @@ void ScriptApiEntity::luaentity_Rightclick(u16 id,
objectrefGetOrCreate(L, clicker); // Clicker reference
setOriginFromTable(object);
- PCALL_RES(lua_pcall(L, 2, 0, m_errorhandler));
+ PCALL_RES(lua_pcall(L, 2, 0, error_handler));
- lua_pop(L, 1); // Pop object
+ lua_pop(L, 2); // Pop object and error handler
}
diff --git a/src/script/cpp_api/s_env.cpp b/src/script/cpp_api/s_env.cpp
index 9c733773a..82d0d4f0e 100644
--- a/src/script/cpp_api/s_env.cpp
+++ b/src/script/cpp_api/s_env.cpp
@@ -27,7 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "server.h"
void ScriptApiEnv::environment_OnGenerated(v3s16 minp, v3s16 maxp,
- u32 blockseed)
+ u32 blockseed)
{
SCRIPTAPI_PRECHECKHEADER
@@ -44,7 +44,7 @@ void ScriptApiEnv::environment_OnGenerated(v3s16 minp, v3s16 maxp,
void ScriptApiEnv::environment_Step(float dtime)
{
SCRIPTAPI_PRECHECKHEADER
- //infostream<<"scriptapi_environment_step"<<std::endl;
+ //infostream << "scriptapi_environment_step" << std::endl;
// Get core.registered_globalsteps
lua_getglobal(L, "core");
@@ -58,7 +58,7 @@ void ScriptApiEnv::environment_Step(float dtime)
}
}
-void ScriptApiEnv::player_event(ServerActiveObject* player, std::string type)
+void ScriptApiEnv::player_event(ServerActiveObject *player, const std::string &type)
{
SCRIPTAPI_PRECHECKHEADER
@@ -82,75 +82,166 @@ void ScriptApiEnv::player_event(ServerActiveObject* player, std::string type)
void ScriptApiEnv::initializeEnvironment(ServerEnvironment *env)
{
SCRIPTAPI_PRECHECKHEADER
- verbosestream<<"scriptapi_add_environment"<<std::endl;
+ verbosestream << "scriptapi_add_environment" << std::endl;
setEnv(env);
/*
- Add ActiveBlockModifiers to environment
+ Add {Loading,Active}BlockModifiers to environment
*/
// Get core.registered_abms
lua_getglobal(L, "core");
lua_getfield(L, -1, "registered_abms");
- luaL_checktype(L, -1, LUA_TTABLE);
int registered_abms = lua_gettop(L);
- if(lua_istable(L, registered_abms)){
- int table = lua_gettop(L);
- lua_pushnil(L);
- while(lua_next(L, table) != 0){
- // key at index -2 and value at index -1
- int id = lua_tonumber(L, -2);
- int current_abm = lua_gettop(L);
-
- std::set<std::string> trigger_contents;
- lua_getfield(L, current_abm, "nodenames");
- if(lua_istable(L, -1)){
- int table = lua_gettop(L);
- lua_pushnil(L);
- while(lua_next(L, table) != 0){
- // key at index -2 and value at index -1
- luaL_checktype(L, -1, LUA_TSTRING);
- trigger_contents.insert(lua_tostring(L, -1));
- // removes value, keeps key for next iteration
- lua_pop(L, 1);
- }
- } else if(lua_isstring(L, -1)){
+ if (!lua_istable(L, registered_abms)) {
+ lua_pop(L, 1);
+ throw LuaError("core.registered_abms was not a lua table, as expected.");
+ }
+ lua_pushnil(L);
+ while (lua_next(L, registered_abms)) {
+ // key at index -2 and value at index -1
+ int id = lua_tonumber(L, -2);
+ int current_abm = lua_gettop(L);
+
+ std::set<std::string> trigger_contents;
+ lua_getfield(L, current_abm, "nodenames");
+ if (lua_istable(L, -1)) {
+ int table = lua_gettop(L);
+ lua_pushnil(L);
+ while (lua_next(L, table)) {
+ // key at index -2 and value at index -1
+ luaL_checktype(L, -1, LUA_TSTRING);
trigger_contents.insert(lua_tostring(L, -1));
+ // removes value, keeps key for next iteration
+ lua_pop(L, 1);
}
- lua_pop(L, 1);
-
- std::set<std::string> required_neighbors;
- lua_getfield(L, current_abm, "neighbors");
- if(lua_istable(L, -1)){
- int table = lua_gettop(L);
- lua_pushnil(L);
- while(lua_next(L, table) != 0){
- // key at index -2 and value at index -1
- luaL_checktype(L, -1, LUA_TSTRING);
- required_neighbors.insert(lua_tostring(L, -1));
- // removes value, keeps key for next iteration
- lua_pop(L, 1);
- }
- } else if(lua_isstring(L, -1)){
+ } else if (lua_isstring(L, -1)) {
+ trigger_contents.insert(lua_tostring(L, -1));
+ }
+ lua_pop(L, 1);
+
+ std::set<std::string> required_neighbors;
+ lua_getfield(L, current_abm, "neighbors");
+ if (lua_istable(L, -1)) {
+ int table = lua_gettop(L);
+ lua_pushnil(L);
+ while (lua_next(L, table)) {
+ // key at index -2 and value at index -1
+ luaL_checktype(L, -1, LUA_TSTRING);
required_neighbors.insert(lua_tostring(L, -1));
+ // removes value, keeps key for next iteration
+ lua_pop(L, 1);
}
- lua_pop(L, 1);
+ } else if (lua_isstring(L, -1)) {
+ required_neighbors.insert(lua_tostring(L, -1));
+ }
+ lua_pop(L, 1);
+
+ float trigger_interval = 10.0;
+ getfloatfield(L, current_abm, "interval", trigger_interval);
+
+ int trigger_chance = 50;
+ getintfield(L, current_abm, "chance", trigger_chance);
- float trigger_interval = 10.0;
- getfloatfield(L, current_abm, "interval", trigger_interval);
+ bool simple_catch_up = true;
+ getboolfield(L, current_abm, "catch_up", simple_catch_up);
- int trigger_chance = 50;
- getintfield(L, current_abm, "chance", trigger_chance);
+ LuaABM *abm = new LuaABM(L, id, trigger_contents, required_neighbors,
+ trigger_interval, trigger_chance, simple_catch_up);
+
+ env->addActiveBlockModifier(abm);
+
+ // removes value, keeps key for next iteration
+ lua_pop(L, 1);
+ }
+ lua_pop(L, 1);
- LuaABM *abm = new LuaABM(L, id, trigger_contents,
- required_neighbors, trigger_interval, trigger_chance);
+ // Get core.registered_lbms
+ lua_getglobal(L, "core");
+ lua_getfield(L, -1, "registered_lbms");
+ int registered_lbms = lua_gettop(L);
- env->addActiveBlockModifier(abm);
+ if (!lua_istable(L, registered_lbms)) {
+ lua_pop(L, 1);
+ throw LuaError("core.registered_lbms was not a lua table, as expected.");
+ }
- // removes value, keeps key for next iteration
- lua_pop(L, 1);
+ lua_pushnil(L);
+ while (lua_next(L, registered_lbms)) {
+ // key at index -2 and value at index -1
+ int id = lua_tonumber(L, -2);
+ int current_lbm = lua_gettop(L);
+
+ std::set<std::string> trigger_contents;
+ lua_getfield(L, current_lbm, "nodenames");
+ if (lua_istable(L, -1)) {
+ int table = lua_gettop(L);
+ lua_pushnil(L);
+ while (lua_next(L, table)) {
+ // key at index -2 and value at index -1
+ luaL_checktype(L, -1, LUA_TSTRING);
+ trigger_contents.insert(lua_tostring(L, -1));
+ // removes value, keeps key for next iteration
+ lua_pop(L, 1);
+ }
+ } else if (lua_isstring(L, -1)) {
+ trigger_contents.insert(lua_tostring(L, -1));
}
+ lua_pop(L, 1);
+
+ std::string name;
+ getstringfield(L, current_lbm, "name", name);
+
+ bool run_at_every_load = getboolfield_default(L, current_lbm,
+ "run_at_every_load", false);
+
+ LuaLBM *lbm = new LuaLBM(L, id, trigger_contents, name,
+ run_at_every_load);
+
+ env->addLoadingBlockModifierDef(lbm);
+
+ // removes value, keeps key for next iteration
+ lua_pop(L, 1);
}
lua_pop(L, 1);
}
+
+void ScriptApiEnv::on_emerge_area_completion(
+ v3s16 blockpos, int action, ScriptCallbackState *state)
+{
+ Server *server = getServer();
+
+ // Note that the order of these locks is important! Envlock must *ALWAYS*
+ // be acquired before attempting to acquire scriptlock, or else ServerThread
+ // will try to acquire scriptlock after it already owns envlock, thus
+ // deadlocking EmergeThread and ServerThread
+ MutexAutoLock envlock(server->m_env_mutex);
+
+ SCRIPTAPI_PRECHECKHEADER
+
+ int error_handler = PUSH_ERROR_HANDLER(L);
+
+ lua_rawgeti(L, LUA_REGISTRYINDEX, state->callback_ref);
+ luaL_checktype(L, -1, LUA_TFUNCTION);
+
+ push_v3s16(L, blockpos);
+ lua_pushinteger(L, action);
+ lua_pushinteger(L, state->refcount);
+ lua_rawgeti(L, LUA_REGISTRYINDEX, state->args_ref);
+
+ setOriginDirect(state->origin.c_str());
+
+ try {
+ PCALL_RES(lua_pcall(L, 4, 0, error_handler));
+ } catch (LuaError &e) {
+ server->setAsyncFatalError(e.what());
+ }
+
+ lua_pop(L, 1); // Pop error handler
+
+ if (state->refcount == 0) {
+ luaL_unref(L, LUA_REGISTRYINDEX, state->callback_ref);
+ luaL_unref(L, LUA_REGISTRYINDEX, state->args_ref);
+ }
+}
diff --git a/src/script/cpp_api/s_env.h b/src/script/cpp_api/s_env.h
index 180cfabd0..e07024565 100644
--- a/src/script/cpp_api/s_env.h
+++ b/src/script/cpp_api/s_env.h
@@ -24,19 +24,23 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "irr_v3d.h"
class ServerEnvironment;
-struct MapgenParams;
+struct ScriptCallbackState;
-class ScriptApiEnv
- : virtual public ScriptApiBase
+class ScriptApiEnv : virtual public ScriptApiBase
{
public:
- // On environment step
+ // Called on environment step
void environment_Step(float dtime);
- // After generating a piece of map
- void environment_OnGenerated(v3s16 minp, v3s16 maxp,u32 blockseed);
- //called on player event
- void player_event(ServerActiveObject* player, std::string type);
+ // Called after generating a piece of map
+ void environment_OnGenerated(v3s16 minp, v3s16 maxp, u32 blockseed);
+
+ // Called on player event
+ void player_event(ServerActiveObject *player, const std::string &type);
+
+ // Called after emerge of a block queued from core.emerge_area()
+ void on_emerge_area_completion(v3s16 blockpos, int action,
+ ScriptCallbackState *state);
void initializeEnvironment(ServerEnvironment *env);
};
diff --git a/src/script/cpp_api/s_internal.h b/src/script/cpp_api/s_internal.h
index 9999a584a..37473c497 100644
--- a/src/script/cpp_api/s_internal.h
+++ b/src/script/cpp_api/s_internal.h
@@ -32,28 +32,50 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#ifdef SCRIPTAPI_LOCK_DEBUG
#include "debug.h" // assert()
+
class LockChecker {
public:
- LockChecker(bool* variable) {
- assert(*variable == false);
+ LockChecker(int *recursion_counter, threadid_t *owning_thread)
+ {
+ m_lock_recursion_counter = recursion_counter;
+ m_owning_thread = owning_thread;
+ m_original_level = *recursion_counter;
+
+ if (*m_lock_recursion_counter > 0)
+ assert(thr_is_current_thread(*m_owning_thread));
+ else
+ *m_owning_thread = thr_get_current_thread_id();
- m_variable = variable;
- *m_variable = true;
+ (*m_lock_recursion_counter)++;
}
- ~LockChecker() {
- *m_variable = false;
+
+ ~LockChecker()
+ {
+ assert(thr_is_current_thread(*m_owning_thread));
+ assert(*m_lock_recursion_counter > 0);
+
+ (*m_lock_recursion_counter)--;
+
+ assert(*m_lock_recursion_counter == m_original_level);
}
+
private:
-bool* m_variable;
+ int *m_lock_recursion_counter;
+ int m_original_level;
+ threadid_t *m_owning_thread;
};
-#define SCRIPTAPI_LOCK_CHECK LockChecker(&(this->m_locked))
+#define SCRIPTAPI_LOCK_CHECK \
+ LockChecker scriptlock_checker( \
+ &this->m_lock_recursion_count, \
+ &this->m_owning_thread)
+
#else
-#define SCRIPTAPI_LOCK_CHECK while(0)
+ #define SCRIPTAPI_LOCK_CHECK while(0)
#endif
#define SCRIPTAPI_PRECHECKHEADER \
- JMutexAutoLock(this->m_luastackmutex); \
+ RecursiveMutexAutoLock scriptlock(this->m_luastackmutex); \
SCRIPTAPI_LOCK_CHECK; \
realityCheck(); \
lua_State *L = getStack(); \
diff --git a/src/script/cpp_api/s_inventory.cpp b/src/script/cpp_api/s_inventory.cpp
index 019d1ccc0..c90c7d4e2 100644
--- a/src/script/cpp_api/s_inventory.cpp
+++ b/src/script/cpp_api/s_inventory.cpp
@@ -33,6 +33,8 @@ int ScriptApiDetached::detached_inventory_AllowMove(
{
SCRIPTAPI_PRECHECKHEADER
+ int error_handler = PUSH_ERROR_HANDLER(L);
+
// Push callback function on stack
if (!getDetachedInventoryCallback(name, "allow_move"))
return count;
@@ -48,11 +50,11 @@ int ScriptApiDetached::detached_inventory_AllowMove(
lua_pushinteger(L, to_index + 1); // to_index
lua_pushinteger(L, count); // count
objectrefGetOrCreate(L, player); // player
- PCALL_RES(lua_pcall(L, 7, 1, m_errorhandler));
+ PCALL_RES(lua_pcall(L, 7, 1, error_handler));
if(!lua_isnumber(L, -1))
throw LuaError("allow_move should return a number. name=" + name);
int ret = luaL_checkinteger(L, -1);
- lua_pop(L, 1); // Pop integer
+ lua_pop(L, 2); // Pop integer and error handler
return ret;
}
@@ -64,6 +66,8 @@ int ScriptApiDetached::detached_inventory_AllowPut(
{
SCRIPTAPI_PRECHECKHEADER
+ int error_handler = PUSH_ERROR_HANDLER(L);
+
// Push callback function on stack
if (!getDetachedInventoryCallback(name, "allow_put"))
return stack.count; // All will be accepted
@@ -76,11 +80,11 @@ int ScriptApiDetached::detached_inventory_AllowPut(
lua_pushinteger(L, index + 1); // index
LuaItemStack::create(L, stack); // stack
objectrefGetOrCreate(L, player); // player
- PCALL_RES(lua_pcall(L, 5, 1, m_errorhandler));
+ PCALL_RES(lua_pcall(L, 5, 1, error_handler));
if (!lua_isnumber(L, -1))
throw LuaError("allow_put should return a number. name=" + name);
int ret = luaL_checkinteger(L, -1);
- lua_pop(L, 1); // Pop integer
+ lua_pop(L, 2); // Pop integer and error handler
return ret;
}
@@ -92,6 +96,8 @@ int ScriptApiDetached::detached_inventory_AllowTake(
{
SCRIPTAPI_PRECHECKHEADER
+ int error_handler = PUSH_ERROR_HANDLER(L);
+
// Push callback function on stack
if (!getDetachedInventoryCallback(name, "allow_take"))
return stack.count; // All will be accepted
@@ -104,11 +110,11 @@ int ScriptApiDetached::detached_inventory_AllowTake(
lua_pushinteger(L, index + 1); // index
LuaItemStack::create(L, stack); // stack
objectrefGetOrCreate(L, player); // player
- PCALL_RES(lua_pcall(L, 5, 1, m_errorhandler));
+ PCALL_RES(lua_pcall(L, 5, 1, error_handler));
if (!lua_isnumber(L, -1))
throw LuaError("allow_take should return a number. name=" + name);
int ret = luaL_checkinteger(L, -1);
- lua_pop(L, 1); // Pop integer
+ lua_pop(L, 2); // Pop integer and error handler
return ret;
}
@@ -121,6 +127,8 @@ void ScriptApiDetached::detached_inventory_OnMove(
{
SCRIPTAPI_PRECHECKHEADER
+ int error_handler = PUSH_ERROR_HANDLER(L);
+
// Push callback function on stack
if (!getDetachedInventoryCallback(name, "on_move"))
return;
@@ -136,7 +144,8 @@ void ScriptApiDetached::detached_inventory_OnMove(
lua_pushinteger(L, to_index + 1); // to_index
lua_pushinteger(L, count); // count
objectrefGetOrCreate(L, player); // player
- PCALL_RES(lua_pcall(L, 7, 0, m_errorhandler));
+ PCALL_RES(lua_pcall(L, 7, 0, error_handler));
+ lua_pop(L, 1); // Pop error handler
}
// Report put items
@@ -147,6 +156,8 @@ void ScriptApiDetached::detached_inventory_OnPut(
{
SCRIPTAPI_PRECHECKHEADER
+ int error_handler = PUSH_ERROR_HANDLER(L);
+
// Push callback function on stack
if (!getDetachedInventoryCallback(name, "on_put"))
return;
@@ -160,7 +171,8 @@ void ScriptApiDetached::detached_inventory_OnPut(
lua_pushinteger(L, index + 1); // index
LuaItemStack::create(L, stack); // stack
objectrefGetOrCreate(L, player); // player
- PCALL_RES(lua_pcall(L, 5, 0, m_errorhandler));
+ PCALL_RES(lua_pcall(L, 5, 0, error_handler));
+ lua_pop(L, 1); // Pop error handler
}
// Report taken items
@@ -171,6 +183,8 @@ void ScriptApiDetached::detached_inventory_OnTake(
{
SCRIPTAPI_PRECHECKHEADER
+ int error_handler = PUSH_ERROR_HANDLER(L);
+
// Push callback function on stack
if (!getDetachedInventoryCallback(name, "on_take"))
return;
@@ -184,7 +198,8 @@ void ScriptApiDetached::detached_inventory_OnTake(
lua_pushinteger(L, index + 1); // index
LuaItemStack::create(L, stack); // stack
objectrefGetOrCreate(L, player); // player
- PCALL_RES(lua_pcall(L, 5, 0, m_errorhandler));
+ PCALL_RES(lua_pcall(L, 5, 0, error_handler));
+ lua_pop(L, 1); // Pop error handler
}
// Retrieves core.detached_inventories[name][callbackname]
diff --git a/src/script/cpp_api/s_item.cpp b/src/script/cpp_api/s_item.cpp
index 4d4d416ec..3c84fb8cf 100644
--- a/src/script/cpp_api/s_item.cpp
+++ b/src/script/cpp_api/s_item.cpp
@@ -34,6 +34,8 @@ bool ScriptApiItem::item_OnDrop(ItemStack &item,
{
SCRIPTAPI_PRECHECKHEADER
+ int error_handler = PUSH_ERROR_HANDLER(L);
+
// Push callback function on stack
if (!getItemCallback(item.name.c_str(), "on_drop"))
return false;
@@ -42,7 +44,7 @@ bool ScriptApiItem::item_OnDrop(ItemStack &item,
LuaItemStack::create(L, item);
objectrefGetOrCreate(L, dropper);
pushFloatPos(L, pos);
- PCALL_RES(lua_pcall(L, 3, 1, m_errorhandler));
+ PCALL_RES(lua_pcall(L, 3, 1, error_handler));
if (!lua_isnil(L, -1)) {
try {
item = read_item(L,-1, getServer());
@@ -50,7 +52,7 @@ bool ScriptApiItem::item_OnDrop(ItemStack &item,
throw LuaError(std::string(e.what()) + ". item=" + item.name);
}
}
- lua_pop(L, 1); // Pop item
+ lua_pop(L, 2); // Pop item and error handler
return true;
}
@@ -59,6 +61,8 @@ bool ScriptApiItem::item_OnPlace(ItemStack &item,
{
SCRIPTAPI_PRECHECKHEADER
+ int error_handler = PUSH_ERROR_HANDLER(L);
+
// Push callback function on stack
if (!getItemCallback(item.name.c_str(), "on_place"))
return false;
@@ -67,7 +71,7 @@ bool ScriptApiItem::item_OnPlace(ItemStack &item,
LuaItemStack::create(L, item);
objectrefGetOrCreate(L, placer);
pushPointedThing(pointed);
- PCALL_RES(lua_pcall(L, 3, 1, m_errorhandler));
+ PCALL_RES(lua_pcall(L, 3, 1, error_handler));
if (!lua_isnil(L, -1)) {
try {
item = read_item(L,-1, getServer());
@@ -75,7 +79,7 @@ bool ScriptApiItem::item_OnPlace(ItemStack &item,
throw LuaError(std::string(e.what()) + ". item=" + item.name);
}
}
- lua_pop(L, 1); // Pop item
+ lua_pop(L, 2); // Pop item and error handler
return true;
}
@@ -84,6 +88,8 @@ bool ScriptApiItem::item_OnUse(ItemStack &item,
{
SCRIPTAPI_PRECHECKHEADER
+ int error_handler = PUSH_ERROR_HANDLER(L);
+
// Push callback function on stack
if (!getItemCallback(item.name.c_str(), "on_use"))
return false;
@@ -92,7 +98,7 @@ bool ScriptApiItem::item_OnUse(ItemStack &item,
LuaItemStack::create(L, item);
objectrefGetOrCreate(L, user);
pushPointedThing(pointed);
- PCALL_RES(lua_pcall(L, 3, 1, m_errorhandler));
+ PCALL_RES(lua_pcall(L, 3, 1, error_handler));
if(!lua_isnil(L, -1)) {
try {
item = read_item(L,-1, getServer());
@@ -100,7 +106,33 @@ bool ScriptApiItem::item_OnUse(ItemStack &item,
throw LuaError(std::string(e.what()) + ". item=" + item.name);
}
}
- lua_pop(L, 1); // Pop item
+ lua_pop(L, 2); // Pop item and error handler
+ return true;
+}
+
+bool ScriptApiItem::item_OnSecondaryUse(ItemStack &item, ServerActiveObject *user)
+{
+ SCRIPTAPI_PRECHECKHEADER
+
+ int error_handler = PUSH_ERROR_HANDLER(L);
+
+ if (!getItemCallback(item.name.c_str(), "on_secondary_use"))
+ return false;
+
+ LuaItemStack::create(L, item);
+ objectrefGetOrCreate(L, user);
+ PointedThing pointed;
+ pointed.type = POINTEDTHING_NOTHING;
+ pushPointedThing(pointed);
+ PCALL_RES(lua_pcall(L, 3, 1, error_handler));
+ if (!lua_isnil(L, -1)) {
+ try {
+ item = read_item(L, -1, getServer());
+ } catch (LuaError &e) {
+ throw LuaError(std::string(e.what()) + ". item=" + item.name);
+ }
+ }
+ lua_pop(L, 2); // Pop item and error handler
return true;
}
@@ -109,6 +141,8 @@ bool ScriptApiItem::item_OnCraft(ItemStack &item, ServerActiveObject *user,
{
SCRIPTAPI_PRECHECKHEADER
+ int error_handler = PUSH_ERROR_HANDLER(L);
+
lua_getglobal(L, "core");
lua_getfield(L, -1, "on_craft");
LuaItemStack::create(L, item);
@@ -122,7 +156,7 @@ bool ScriptApiItem::item_OnCraft(ItemStack &item, ServerActiveObject *user,
push_items(L, items);
InvRef::create(L, craft_inv);
- PCALL_RES(lua_pcall(L, 4, 1, m_errorhandler));
+ PCALL_RES(lua_pcall(L, 4, 1, error_handler));
if (!lua_isnil(L, -1)) {
try {
item = read_item(L,-1, getServer());
@@ -130,7 +164,7 @@ bool ScriptApiItem::item_OnCraft(ItemStack &item, ServerActiveObject *user,
throw LuaError(std::string(e.what()) + ". item=" + item.name);
}
}
- lua_pop(L, 1); // Pop item
+ lua_pop(L, 2); // Pop item and error handler
return true;
}
@@ -139,6 +173,8 @@ bool ScriptApiItem::item_CraftPredict(ItemStack &item, ServerActiveObject *user,
{
SCRIPTAPI_PRECHECKHEADER
+ int error_handler = PUSH_ERROR_HANDLER(L);
+
lua_getglobal(L, "core");
lua_getfield(L, -1, "craft_predict");
LuaItemStack::create(L, item);
@@ -152,7 +188,7 @@ bool ScriptApiItem::item_CraftPredict(ItemStack &item, ServerActiveObject *user,
push_items(L, items);
InvRef::create(L, craft_inv);
- PCALL_RES(lua_pcall(L, 4, 1, m_errorhandler));
+ PCALL_RES(lua_pcall(L, 4, 1, error_handler));
if (!lua_isnil(L, -1)) {
try {
item = read_item(L,-1, getServer());
@@ -160,7 +196,7 @@ bool ScriptApiItem::item_CraftPredict(ItemStack &item, ServerActiveObject *user,
throw LuaError(std::string(e.what()) + ". item=" + item.name);
}
}
- lua_pop(L, 1); // Pop item
+ lua_pop(L, 2); // Pop item and error handler
return true;
}
diff --git a/src/script/cpp_api/s_item.h b/src/script/cpp_api/s_item.h
index 88cc1909d..7350a71c5 100644
--- a/src/script/cpp_api/s_item.h
+++ b/src/script/cpp_api/s_item.h
@@ -42,6 +42,8 @@ public:
ServerActiveObject *placer, const PointedThing &pointed);
bool item_OnUse(ItemStack &item,
ServerActiveObject *user, const PointedThing &pointed);
+ bool item_OnSecondaryUse(ItemStack &item,
+ ServerActiveObject *user);
bool item_OnCraft(ItemStack &item, ServerActiveObject *user,
const InventoryList *old_craft_grid, const InventoryLocation &craft_inv);
bool item_CraftPredict(ItemStack &item, ServerActiveObject *user,
diff --git a/src/script/cpp_api/s_mainmenu.cpp b/src/script/cpp_api/s_mainmenu.cpp
index 17ceff082..e9a7a13b9 100644
--- a/src/script/cpp_api/s_mainmenu.cpp
+++ b/src/script/cpp_api/s_mainmenu.cpp
@@ -43,6 +43,8 @@ void ScriptApiMainMenu::handleMainMenuEvent(std::string text)
{
SCRIPTAPI_PRECHECKHEADER
+ int error_handler = PUSH_ERROR_HANDLER(L);
+
// Get handler function
lua_getglobal(L, "core");
lua_getfield(L, -1, "event_handler");
@@ -55,13 +57,16 @@ void ScriptApiMainMenu::handleMainMenuEvent(std::string text)
// Call it
lua_pushstring(L, text.c_str());
- PCALL_RES(lua_pcall(L, 1, 0, m_errorhandler));
+ PCALL_RES(lua_pcall(L, 1, 0, error_handler));
+ lua_pop(L, 1); // Pop error handler
}
void ScriptApiMainMenu::handleMainMenuButtons(const StringMap &fields)
{
SCRIPTAPI_PRECHECKHEADER
+ int error_handler = PUSH_ERROR_HANDLER(L);
+
// Get handler function
lua_getglobal(L, "core");
lua_getfield(L, -1, "button_handler");
@@ -84,6 +89,7 @@ void ScriptApiMainMenu::handleMainMenuButtons(const StringMap &fields)
}
// Call it
- PCALL_RES(lua_pcall(L, 1, 0, m_errorhandler));
+ PCALL_RES(lua_pcall(L, 1, 0, error_handler));
+ lua_pop(L, 1); // Pop error handler
}
diff --git a/src/script/cpp_api/s_node.cpp b/src/script/cpp_api/s_node.cpp
index dac058b13..17f0f0dac 100644
--- a/src/script/cpp_api/s_node.cpp
+++ b/src/script/cpp_api/s_node.cpp
@@ -57,6 +57,7 @@ struct EnumString ScriptApiNode::es_ContentParamType2[] =
{CPT2_FACEDIR, "facedir"},
{CPT2_WALLMOUNTED, "wallmounted"},
{CPT2_LEVELED, "leveled"},
+ {CPT2_DEGROTATE, "degrotate"},
{0, NULL},
};
@@ -81,6 +82,7 @@ struct EnumString ScriptApiNode::es_NodeBoxType[] =
{NODEBOX_FIXED, "fixed"},
{NODEBOX_WALLMOUNTED, "wallmounted"},
{NODEBOX_LEVELED, "leveled"},
+ {NODEBOX_CONNECTED, "connected"},
{0, NULL},
};
@@ -95,6 +97,8 @@ bool ScriptApiNode::node_on_punch(v3s16 p, MapNode node,
{
SCRIPTAPI_PRECHECKHEADER
+ int error_handler = PUSH_ERROR_HANDLER(L);
+
INodeDefManager *ndef = getServer()->ndef();
// Push callback function on stack
@@ -106,7 +110,8 @@ bool ScriptApiNode::node_on_punch(v3s16 p, MapNode node,
pushnode(L, node, ndef);
objectrefGetOrCreate(L, puncher);
pushPointedThing(pointed);
- PCALL_RES(lua_pcall(L, 4, 0, m_errorhandler));
+ PCALL_RES(lua_pcall(L, 4, 0, error_handler));
+ lua_pop(L, 1); // Pop error handler
return true;
}
@@ -115,6 +120,8 @@ bool ScriptApiNode::node_on_dig(v3s16 p, MapNode node,
{
SCRIPTAPI_PRECHECKHEADER
+ int error_handler = PUSH_ERROR_HANDLER(L);
+
INodeDefManager *ndef = getServer()->ndef();
// Push callback function on stack
@@ -125,7 +132,8 @@ bool ScriptApiNode::node_on_dig(v3s16 p, MapNode node,
push_v3s16(L, p);
pushnode(L, node, ndef);
objectrefGetOrCreate(L, digger);
- PCALL_RES(lua_pcall(L, 3, 0, m_errorhandler));
+ PCALL_RES(lua_pcall(L, 3, 0, error_handler));
+ lua_pop(L, 1); // Pop error handler
return true;
}
@@ -133,6 +141,8 @@ void ScriptApiNode::node_on_construct(v3s16 p, MapNode node)
{
SCRIPTAPI_PRECHECKHEADER
+ int error_handler = PUSH_ERROR_HANDLER(L);
+
INodeDefManager *ndef = getServer()->ndef();
// Push callback function on stack
@@ -141,13 +151,16 @@ void ScriptApiNode::node_on_construct(v3s16 p, MapNode node)
// Call function
push_v3s16(L, p);
- PCALL_RES(lua_pcall(L, 1, 0, m_errorhandler));
+ PCALL_RES(lua_pcall(L, 1, 0, error_handler));
+ lua_pop(L, 1); // Pop error handler
}
void ScriptApiNode::node_on_destruct(v3s16 p, MapNode node)
{
SCRIPTAPI_PRECHECKHEADER
+ int error_handler = PUSH_ERROR_HANDLER(L);
+
INodeDefManager *ndef = getServer()->ndef();
// Push callback function on stack
@@ -156,13 +169,16 @@ void ScriptApiNode::node_on_destruct(v3s16 p, MapNode node)
// Call function
push_v3s16(L, p);
- PCALL_RES(lua_pcall(L, 1, 0, m_errorhandler));
+ PCALL_RES(lua_pcall(L, 1, 0, error_handler));
+ lua_pop(L, 1); // Pop error handler
}
void ScriptApiNode::node_after_destruct(v3s16 p, MapNode node)
{
SCRIPTAPI_PRECHECKHEADER
+ int error_handler = PUSH_ERROR_HANDLER(L);
+
INodeDefManager *ndef = getServer()->ndef();
// Push callback function on stack
@@ -172,13 +188,16 @@ void ScriptApiNode::node_after_destruct(v3s16 p, MapNode node)
// Call function
push_v3s16(L, p);
pushnode(L, node, ndef);
- PCALL_RES(lua_pcall(L, 2, 0, m_errorhandler));
+ PCALL_RES(lua_pcall(L, 2, 0, error_handler));
+ lua_pop(L, 1); // Pop error handler
}
bool ScriptApiNode::node_on_timer(v3s16 p, MapNode node, f32 dtime)
{
SCRIPTAPI_PRECHECKHEADER
+ int error_handler = PUSH_ERROR_HANDLER(L);
+
INodeDefManager *ndef = getServer()->ndef();
// Push callback function on stack
@@ -188,7 +207,8 @@ bool ScriptApiNode::node_on_timer(v3s16 p, MapNode node, f32 dtime)
// Call function
push_v3s16(L, p);
lua_pushnumber(L,dtime);
- PCALL_RES(lua_pcall(L, 2, 1, m_errorhandler));
+ PCALL_RES(lua_pcall(L, 2, 1, error_handler));
+ lua_remove(L, error_handler);
return (bool) lua_isboolean(L, -1) && (bool) lua_toboolean(L, -1) == true;
}
@@ -199,6 +219,8 @@ void ScriptApiNode::node_on_receive_fields(v3s16 p,
{
SCRIPTAPI_PRECHECKHEADER
+ int error_handler = PUSH_ERROR_HANDLER(L);
+
INodeDefManager *ndef = getServer()->ndef();
// If node doesn't exist, we don't know what callback to call
@@ -223,23 +245,30 @@ void ScriptApiNode::node_on_receive_fields(v3s16 p,
lua_settable(L, -3);
}
objectrefGetOrCreate(L, sender); // player
- PCALL_RES(lua_pcall(L, 4, 0, m_errorhandler));
+ PCALL_RES(lua_pcall(L, 4, 0, error_handler));
+ lua_pop(L, 1); // Pop error handler
}
void ScriptApiNode::node_falling_update(v3s16 p)
{
SCRIPTAPI_PRECHECKHEADER
+ int error_handler = PUSH_ERROR_HANDLER(L);
+
lua_getglobal(L, "nodeupdate");
push_v3s16(L, p);
- PCALL_RES(lua_pcall(L, 1, 0, m_errorhandler));
+ PCALL_RES(lua_pcall(L, 1, 0, error_handler));
+ lua_pop(L, 1); // Pop error handler
}
void ScriptApiNode::node_falling_update_single(v3s16 p)
{
SCRIPTAPI_PRECHECKHEADER
+ int error_handler = PUSH_ERROR_HANDLER(L);
+
lua_getglobal(L, "nodeupdate_single");
push_v3s16(L, p);
- PCALL_RES(lua_pcall(L, 1, 0, m_errorhandler));
+ PCALL_RES(lua_pcall(L, 1, 0, error_handler));
+ lua_pop(L, 1); // Pop error handler
}
diff --git a/src/script/cpp_api/s_nodemeta.cpp b/src/script/cpp_api/s_nodemeta.cpp
index 638750b0e..d050c0bc9 100644
--- a/src/script/cpp_api/s_nodemeta.cpp
+++ b/src/script/cpp_api/s_nodemeta.cpp
@@ -34,6 +34,8 @@ int ScriptApiNodemeta::nodemeta_inventory_AllowMove(v3s16 p,
{
SCRIPTAPI_PRECHECKHEADER
+ int error_handler = PUSH_ERROR_HANDLER(L);
+
INodeDefManager *ndef = getServer()->ndef();
// If node doesn't exist, we don't know what callback to call
@@ -54,12 +56,12 @@ int ScriptApiNodemeta::nodemeta_inventory_AllowMove(v3s16 p,
lua_pushinteger(L, to_index + 1); // to_index
lua_pushinteger(L, count); // count
objectrefGetOrCreate(L, player); // player
- PCALL_RES(lua_pcall(L, 7, 1, m_errorhandler));
+ PCALL_RES(lua_pcall(L, 7, 1, error_handler));
if (!lua_isnumber(L, -1))
throw LuaError("allow_metadata_inventory_move should"
" return a number, guilty node: " + nodename);
int num = luaL_checkinteger(L, -1);
- lua_pop(L, 1); // Pop integer
+ lua_pop(L, 2); // Pop integer and error handler
return num;
}
@@ -70,6 +72,8 @@ int ScriptApiNodemeta::nodemeta_inventory_AllowPut(v3s16 p,
{
SCRIPTAPI_PRECHECKHEADER
+ int error_handler = PUSH_ERROR_HANDLER(L);
+
INodeDefManager *ndef = getServer()->ndef();
// If node doesn't exist, we don't know what callback to call
@@ -88,12 +92,12 @@ int ScriptApiNodemeta::nodemeta_inventory_AllowPut(v3s16 p,
lua_pushinteger(L, index + 1); // index
LuaItemStack::create(L, stack); // stack
objectrefGetOrCreate(L, player); // player
- PCALL_RES(lua_pcall(L, 5, 1, m_errorhandler));
+ PCALL_RES(lua_pcall(L, 5, 1, error_handler));
if(!lua_isnumber(L, -1))
throw LuaError("allow_metadata_inventory_put should"
" return a number, guilty node: " + nodename);
int num = luaL_checkinteger(L, -1);
- lua_pop(L, 1); // Pop integer
+ lua_pop(L, 2); // Pop integer and error handler
return num;
}
@@ -104,6 +108,8 @@ int ScriptApiNodemeta::nodemeta_inventory_AllowTake(v3s16 p,
{
SCRIPTAPI_PRECHECKHEADER
+ int error_handler = PUSH_ERROR_HANDLER(L);
+
INodeDefManager *ndef = getServer()->ndef();
// If node doesn't exist, we don't know what callback to call
@@ -122,12 +128,12 @@ int ScriptApiNodemeta::nodemeta_inventory_AllowTake(v3s16 p,
lua_pushinteger(L, index + 1); // index
LuaItemStack::create(L, stack); // stack
objectrefGetOrCreate(L, player); // player
- PCALL_RES(lua_pcall(L, 5, 1, m_errorhandler));
+ PCALL_RES(lua_pcall(L, 5, 1, error_handler));
if (!lua_isnumber(L, -1))
throw LuaError("allow_metadata_inventory_take should"
" return a number, guilty node: " + nodename);
int num = luaL_checkinteger(L, -1);
- lua_pop(L, 1); // Pop integer
+ lua_pop(L, 2); // Pop integer and error handler
return num;
}
@@ -139,6 +145,8 @@ void ScriptApiNodemeta::nodemeta_inventory_OnMove(v3s16 p,
{
SCRIPTAPI_PRECHECKHEADER
+ int error_handler = PUSH_ERROR_HANDLER(L);
+
INodeDefManager *ndef = getServer()->ndef();
// If node doesn't exist, we don't know what callback to call
@@ -159,7 +167,8 @@ void ScriptApiNodemeta::nodemeta_inventory_OnMove(v3s16 p,
lua_pushinteger(L, to_index + 1); // to_index
lua_pushinteger(L, count); // count
objectrefGetOrCreate(L, player); // player
- PCALL_RES(lua_pcall(L, 7, 0, m_errorhandler));
+ PCALL_RES(lua_pcall(L, 7, 0, error_handler));
+ lua_pop(L, 1); // Pop error handler
}
// Report put items
@@ -169,6 +178,8 @@ void ScriptApiNodemeta::nodemeta_inventory_OnPut(v3s16 p,
{
SCRIPTAPI_PRECHECKHEADER
+ int error_handler = PUSH_ERROR_HANDLER(L);
+
INodeDefManager *ndef = getServer()->ndef();
// If node doesn't exist, we don't know what callback to call
@@ -187,7 +198,8 @@ void ScriptApiNodemeta::nodemeta_inventory_OnPut(v3s16 p,
lua_pushinteger(L, index + 1); // index
LuaItemStack::create(L, stack); // stack
objectrefGetOrCreate(L, player); // player
- PCALL_RES(lua_pcall(L, 5, 0, m_errorhandler));
+ PCALL_RES(lua_pcall(L, 5, 0, error_handler));
+ lua_pop(L, 1); // Pop error handler
}
// Report taken items
@@ -197,6 +209,8 @@ void ScriptApiNodemeta::nodemeta_inventory_OnTake(v3s16 p,
{
SCRIPTAPI_PRECHECKHEADER
+ int error_handler = PUSH_ERROR_HANDLER(L);
+
INodeDefManager *ndef = getServer()->ndef();
// If node doesn't exist, we don't know what callback to call
@@ -215,7 +229,8 @@ void ScriptApiNodemeta::nodemeta_inventory_OnTake(v3s16 p,
lua_pushinteger(L, index + 1); // index
LuaItemStack::create(L, stack); // stack
objectrefGetOrCreate(L, player); // player
- PCALL_RES(lua_pcall(L, 5, 0, m_errorhandler));
+ PCALL_RES(lua_pcall(L, 5, 0, error_handler));
+ lua_pop(L, 1); // Pop error handler
}
ScriptApiNodemeta::ScriptApiNodemeta()
diff --git a/src/script/cpp_api/s_player.cpp b/src/script/cpp_api/s_player.cpp
index ef3c31cfd..807430678 100644
--- a/src/script/cpp_api/s_player.cpp
+++ b/src/script/cpp_api/s_player.cpp
@@ -74,6 +74,8 @@ s16 ScriptApiPlayer::on_player_hpchange(ServerActiveObject *player,
{
SCRIPTAPI_PRECHECKHEADER
+ int error_handler = PUSH_ERROR_HANDLER(L);
+
// Get core.registered_on_player_hpchange
lua_getglobal(L, "core");
lua_getfield(L, -1, "registered_on_player_hpchange");
@@ -81,9 +83,9 @@ s16 ScriptApiPlayer::on_player_hpchange(ServerActiveObject *player,
objectrefGetOrCreate(L, player);
lua_pushnumber(L, hp_change);
- PCALL_RES(lua_pcall(L, 2, 1, m_errorhandler));
+ PCALL_RES(lua_pcall(L, 2, 1, error_handler));
hp_change = lua_tointeger(L, -1);
- lua_pop(L, -1);
+ lua_pop(L, 2); // Pop result and error handler
return hp_change;
}
diff --git a/src/script/cpp_api/s_security.cpp b/src/script/cpp_api/s_security.cpp
index 6a6d40307..730235c7b 100644
--- a/src/script/cpp_api/s_security.cpp
+++ b/src/script/cpp_api/s_security.cpp
@@ -47,7 +47,7 @@ static inline void copy_safe(lua_State *L, const char *list[], unsigned len, int
// Pushes the original version of a library function on the stack, from the old version
static inline void push_original(lua_State *L, const char *lib, const char *func)
{
- lua_getfield(L, LUA_REGISTRYINDEX, "globals_backup");
+ lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_GLOBALS_BACKUP);
lua_getfield(L, -1, lib);
lua_remove(L, -2); // Remove globals_backup
lua_getfield(L, -1, func);
@@ -116,7 +116,6 @@ void ScriptApiSecurity::initializeSecurity()
"upvaluejoin",
"sethook",
"debug",
- "getupvalue",
"setlocal",
};
static const char *package_whitelist[] = {
@@ -143,7 +142,7 @@ void ScriptApiSecurity::initializeSecurity()
// Backup globals to the registry
lua_getglobal(L, "_G");
- lua_setfield(L, LUA_REGISTRYINDEX, "globals_backup");
+ lua_rawseti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_GLOBALS_BACKUP);
// Replace the global environment with an empty one
#if LUA_VERSION_NUM <= 501
@@ -165,7 +164,7 @@ void ScriptApiSecurity::initializeSecurity()
#endif
// Get old globals
- lua_getfield(L, LUA_REGISTRYINDEX, "globals_backup");
+ lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_GLOBALS_BACKUP);
int old_globals = lua_gettop(L);
@@ -241,7 +240,7 @@ void ScriptApiSecurity::initializeSecurity()
bool ScriptApiSecurity::isSecure(lua_State *L)
{
- lua_getfield(L, LUA_REGISTRYINDEX, "globals_backup");
+ lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_GLOBALS_BACKUP);
bool secure = !lua_isnil(L, -1);
lua_pop(L, 1);
return secure;
@@ -356,7 +355,7 @@ bool ScriptApiSecurity::checkPath(lua_State *L, const char *path)
if (!removed.empty()) abs_path += DIR_DELIM + removed;
// Get server from registry
- lua_getfield(L, LUA_REGISTRYINDEX, "scriptapi");
+ lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_SCRIPTAPI);
ScriptApiBase *script = (ScriptApiBase *) lua_touserdata(L, -1);
lua_pop(L, 1);
const Server *server = script->getServer();
@@ -364,7 +363,7 @@ bool ScriptApiSecurity::checkPath(lua_State *L, const char *path)
if (!server) return false;
// Get mod name
- lua_getfield(L, LUA_REGISTRYINDEX, SCRIPT_MOD_NAME_FIELD);
+ lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_CURRENT_MOD_NAME);
if (lua_isstring(L, -1)) {
std::string mod_name = lua_tostring(L, -1);
diff --git a/src/script/cpp_api/s_security.h b/src/script/cpp_api/s_security.h
index 4a4389cf5..97bc5c067 100644
--- a/src/script/cpp_api/s_security.h
+++ b/src/script/cpp_api/s_security.h
@@ -25,9 +25,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#define CHECK_SECURE_PATH(L, path) \
if (!ScriptApiSecurity::checkPath(L, path)) { \
- lua_pushstring(L, (std::string("Attempt to access external file ") + \
- path + " with mod security on.").c_str()); \
- lua_error(L); \
+ throw LuaError(std::string("Attempt to access external file ") + \
+ path + " with mod security on."); \
}
#define CHECK_SECURE_PATH_OPTIONAL(L, path) \
if (ScriptApiSecurity::isSecure(L)) { \
diff --git a/src/script/cpp_api/s_server.cpp b/src/script/cpp_api/s_server.cpp
index ec2f9c0af..38bd41f87 100644
--- a/src/script/cpp_api/s_server.cpp
+++ b/src/script/cpp_api/s_server.cpp
@@ -27,13 +27,15 @@ bool ScriptApiServer::getAuth(const std::string &playername,
{
SCRIPTAPI_PRECHECKHEADER
+ int error_handler = PUSH_ERROR_HANDLER(L);
getAuthHandler();
lua_getfield(L, -1, "get_auth");
if (lua_type(L, -1) != LUA_TFUNCTION)
throw LuaError("Authentication handler missing get_auth");
lua_pushstring(L, playername.c_str());
- PCALL_RES(lua_pcall(L, 1, 1, m_errorhandler));
+ PCALL_RES(lua_pcall(L, 1, 1, error_handler));
lua_remove(L, -2); // Remove auth handler
+ lua_remove(L, error_handler);
// nil = login not allowed
if (lua_isnil(L, -1))
@@ -99,6 +101,7 @@ void ScriptApiServer::createAuth(const std::string &playername,
{
SCRIPTAPI_PRECHECKHEADER
+ int error_handler = PUSH_ERROR_HANDLER(L);
getAuthHandler();
lua_getfield(L, -1, "create_auth");
lua_remove(L, -2); // Remove auth handler
@@ -106,7 +109,8 @@ void ScriptApiServer::createAuth(const std::string &playername,
throw LuaError("Authentication handler missing create_auth");
lua_pushstring(L, playername.c_str());
lua_pushstring(L, password.c_str());
- PCALL_RES(lua_pcall(L, 2, 0, m_errorhandler));
+ PCALL_RES(lua_pcall(L, 2, 0, error_handler));
+ lua_pop(L, 1); // Pop error handler
}
bool ScriptApiServer::setPassword(const std::string &playername,
@@ -114,6 +118,7 @@ bool ScriptApiServer::setPassword(const std::string &playername,
{
SCRIPTAPI_PRECHECKHEADER
+ int error_handler = PUSH_ERROR_HANDLER(L);
getAuthHandler();
lua_getfield(L, -1, "set_password");
lua_remove(L, -2); // Remove auth handler
@@ -121,7 +126,8 @@ bool ScriptApiServer::setPassword(const std::string &playername,
throw LuaError("Authentication handler missing set_password");
lua_pushstring(L, playername.c_str());
lua_pushstring(L, password.c_str());
- PCALL_RES(lua_pcall(L, 2, 1, m_errorhandler));
+ PCALL_RES(lua_pcall(L, 2, 1, error_handler));
+ lua_remove(L, error_handler);
return lua_toboolean(L, -1);
}
diff --git a/src/script/lua_api/CMakeLists.txt b/src/script/lua_api/CMakeLists.txt
index 2501ce6d6..d507dcf70 100644
--- a/src/script/lua_api/CMakeLists.txt
+++ b/src/script/lua_api/CMakeLists.txt
@@ -16,6 +16,7 @@ set(common_SCRIPT_LUA_API_SRCS
${CMAKE_CURRENT_SOURCE_DIR}/l_util.cpp
${CMAKE_CURRENT_SOURCE_DIR}/l_vmanip.cpp
${CMAKE_CURRENT_SOURCE_DIR}/l_settings.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/l_http.cpp
PARENT_SCOPE)
set(client_SCRIPT_LUA_API_SRCS
diff --git a/src/script/lua_api/l_areastore.cpp b/src/script/lua_api/l_areastore.cpp
index 1e9075119..20e7875c7 100644
--- a/src/script/lua_api/l_areastore.cpp
+++ b/src/script/lua_api/l_areastore.cpp
@@ -22,11 +22,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "lua_api/l_internal.h"
#include "common/c_converter.h"
#include "cpp_api/s_security.h"
-#include "areastore.h"
+#include "irr_v3d.h"
+#include "util/areastore.h"
#include "filesys.h"
-#ifndef ANDROID
- #include "cmake_config.h"
-#endif
#include <fstream>
static inline void get_data_and_border_flags(lua_State *L, u8 start_i,
@@ -45,6 +43,7 @@ static void push_area(lua_State *L, const Area *a,
{
if (!include_borders && !include_data) {
lua_pushboolean(L, true);
+ return;
}
lua_newtable(L);
if (include_borders) {
@@ -71,6 +70,22 @@ static inline void push_areas(lua_State *L, const std::vector<Area *> &areas,
}
}
+// Deserializes value and handles errors
+static int deserialization_helper(lua_State *L, AreaStore *as,
+ std::istream &is)
+{
+ try {
+ as->deserialize(is);
+ } catch (const SerializationError &e) {
+ lua_pushboolean(L, false);
+ lua_pushstring(L, e.what());
+ return 2;
+ }
+
+ lua_pushboolean(L, true);
+ return 1;
+}
+
// garbage collector
int LuaAreaStore::gc_object(lua_State *L)
{
@@ -149,7 +164,7 @@ int LuaAreaStore::l_get_areas_in_area(lua_State *L)
return 1;
}
-// insert_area(edge1, edge2, data)
+// insert_area(edge1, edge2, data, id)
int LuaAreaStore::l_insert_area(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
@@ -157,26 +172,18 @@ int LuaAreaStore::l_insert_area(lua_State *L)
LuaAreaStore *o = checkobject(L, 1);
AreaStore *ast = o->as;
- Area a;
-
- a.minedge = check_v3s16(L, 2);
- a.maxedge = check_v3s16(L, 3);
-
- a.extremifyEdges();
- a.id = ast->getFreeId(a.minedge, a.maxedge);
-
- if (a.id == AREA_ID_INVALID) {
- // couldn't get free id
- lua_pushnil(L);
- return 1;
- }
+ Area a(check_v3s16(L, 2), check_v3s16(L, 3));
size_t d_len;
const char *data = luaL_checklstring(L, 4, &d_len);
a.data = std::string(data, d_len);
- ast->insertArea(a);
+ if (lua_isnumber(L, 5))
+ a.id = lua_tonumber(L, 5);
+
+ if (!ast->insertArea(&a))
+ return 0;
lua_pushnumber(L, a.id);
return 1;
@@ -229,17 +236,15 @@ int LuaAreaStore::l_set_cache_params(lua_State *L)
return 0;
}
-#if 0
// to_string()
int LuaAreaStore::l_to_string(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
LuaAreaStore *o = checkobject(L, 1);
- AreaStore *ast = o->as;
std::ostringstream os(std::ios_base::binary);
- ast->serialize(os);
+ o->as->serialize(os);
std::string str = os.str();
lua_pushlstring(L, str.c_str(), str.length());
@@ -270,16 +275,12 @@ int LuaAreaStore::l_from_string(lua_State *L)
NO_MAP_LOCK_REQUIRED;
LuaAreaStore *o = checkobject(L, 1);
- AreaStore *ast = o->as;
size_t len;
const char *str = luaL_checklstring(L, 2, &len);
std::istringstream is(std::string(str, len), std::ios::binary);
- bool success = ast->deserialize(is);
-
- lua_pushboolean(L, success);
- return 1;
+ return deserialization_helper(L, o->as, is);
}
// from_file(filename)
@@ -288,26 +289,17 @@ int LuaAreaStore::l_from_file(lua_State *L)
NO_MAP_LOCK_REQUIRED;
LuaAreaStore *o = checkobject(L, 1);
- AreaStore *ast = o->as;
const char *filename = luaL_checkstring(L, 2);
CHECK_SECURE_PATH_OPTIONAL(L, filename);
std::ifstream is(filename, std::ios::binary);
- bool success = ast->deserialize(is);
-
- lua_pushboolean(L, success);
- return 1;
+ return deserialization_helper(L, o->as, is);
}
-#endif
LuaAreaStore::LuaAreaStore()
{
-#if USE_SPATIAL
- this->as = new SpatialAreaStore();
-#else
- this->as = new VectorAreaStore();
-#endif
+ this->as = AreaStore::getOptimalImplementation();
}
LuaAreaStore::LuaAreaStore(const std::string &type)
@@ -393,9 +385,9 @@ const luaL_reg LuaAreaStore::methods[] = {
luamethod(LuaAreaStore, reserve),
luamethod(LuaAreaStore, remove_area),
luamethod(LuaAreaStore, set_cache_params),
- /* luamethod(LuaAreaStore, to_string),
+ luamethod(LuaAreaStore, to_string),
luamethod(LuaAreaStore, to_file),
luamethod(LuaAreaStore, from_string),
- luamethod(LuaAreaStore, from_file),*/
+ luamethod(LuaAreaStore, from_file),
{0,0}
};
diff --git a/src/script/lua_api/l_areastore.h b/src/script/lua_api/l_areastore.h
index a25529627..4bd94cebe 100644
--- a/src/script/lua_api/l_areastore.h
+++ b/src/script/lua_api/l_areastore.h
@@ -17,16 +17,14 @@ with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef L_AREASTORE_H_
-#define L_AREASTORE_H_
+#ifndef L_AREA_STORE_H_
+#define L_AREA_STORE_H_
#include "lua_api/l_base.h"
-#include "irr_v3d.h"
-#include "areastore.h"
-/*
- AreaStore
- */
+
+class AreaStore;
+
class LuaAreaStore : public ModApiBase {
private:
@@ -45,11 +43,11 @@ private:
static int l_set_cache_params(lua_State *L);
- /* static int l_to_string(lua_State *L);
+ static int l_to_string(lua_State *L);
static int l_to_file(lua_State *L);
static int l_from_string(lua_State *L);
- static int l_from_file(lua_State *L); */
+ static int l_from_file(lua_State *L);
public:
AreaStore *as;
@@ -67,4 +65,4 @@ public:
static void Register(lua_State *L);
};
-#endif /* L_AREASTORE_H_ */
+#endif // L_AREA_STORE_H_
diff --git a/src/script/lua_api/l_base.cpp b/src/script/lua_api/l_base.cpp
index 6ad3e4ba2..515a7d933 100644
--- a/src/script/lua_api/l_base.cpp
+++ b/src/script/lua_api/l_base.cpp
@@ -26,7 +26,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
ScriptApiBase *ModApiBase::getScriptApiBase(lua_State *L)
{
// Get server from registry
- lua_getfield(L, LUA_REGISTRYINDEX, "scriptapi");
+ lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_SCRIPTAPI);
ScriptApiBase *sapi_ptr = (ScriptApiBase*) lua_touserdata(L, -1);
lua_pop(L, 1);
return sapi_ptr;
@@ -49,7 +49,7 @@ GUIEngine *ModApiBase::getGuiEngine(lua_State *L)
std::string ModApiBase::getCurrentModPath(lua_State *L)
{
- lua_getfield(L, LUA_REGISTRYINDEX, SCRIPT_MOD_NAME_FIELD);
+ lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_CURRENT_MOD_NAME);
const char *current_mod_name = lua_tostring(L, -1);
if (!current_mod_name)
return ".";
diff --git a/src/script/lua_api/l_env.cpp b/src/script/lua_api/l_env.cpp
index 28afdd071..8284c3fcb 100644
--- a/src/script/lua_api/l_env.cpp
+++ b/src/script/lua_api/l_env.cpp
@@ -33,11 +33,15 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "util/pointedthing.h"
#include "content_sao.h"
#include "treegen.h"
+#include "emerge.h"
#include "pathfinder.h"
-#define GET_ENV_PTR ServerEnvironment* env = \
- dynamic_cast<ServerEnvironment*>(getEnv(L)); \
- if (env == NULL) return 0
+struct EnumString ModApiEnvMod::es_ClearObjectsMode[] =
+{
+ {CLEAR_OBJECTS_MODE_FULL, "full"},
+ {CLEAR_OBJECTS_MODE_QUICK, "quick"},
+ {0, NULL},
+};
///////////////////////////////////////////////////////////////////////////////
@@ -52,8 +56,7 @@ void LuaABM::trigger(ServerEnvironment *env, v3s16 p, MapNode n,
sanity_check(lua_checkstack(L, 20));
StackUnroller stack_unroller(L);
- lua_pushcfunction(L, script_error_handler);
- int errorhandler = lua_gettop(L);
+ int error_handler = PUSH_ERROR_HANDLER(L);
// Get registered_abms
lua_getglobal(L, "core");
@@ -80,13 +83,68 @@ void LuaABM::trigger(ServerEnvironment *env, v3s16 p, MapNode n,
lua_pushnumber(L, active_object_count);
lua_pushnumber(L, active_object_count_wider);
- int result = lua_pcall(L, 4, 0, errorhandler);
+ int result = lua_pcall(L, 4, 0, error_handler);
if (result)
scriptIface->scriptError(result, "LuaABM::trigger");
lua_pop(L, 1); // Pop error handler
}
+void LuaLBM::trigger(ServerEnvironment *env, v3s16 p, MapNode n)
+{
+ GameScripting *scriptIface = env->getScriptIface();
+ scriptIface->realityCheck();
+
+ lua_State *L = scriptIface->getStack();
+ sanity_check(lua_checkstack(L, 20));
+ StackUnroller stack_unroller(L);
+
+ int error_handler = PUSH_ERROR_HANDLER(L);
+
+ // Get registered_lbms
+ lua_getglobal(L, "core");
+ lua_getfield(L, -1, "registered_lbms");
+ luaL_checktype(L, -1, LUA_TTABLE);
+ lua_remove(L, -2); // Remove core
+
+ // Get registered_lbms[m_id]
+ lua_pushnumber(L, m_id);
+ lua_gettable(L, -2);
+ FATAL_ERROR_IF(lua_isnil(L, -1), "Entry with given id not found in registered_lbms table");
+ lua_remove(L, -2); // Remove registered_lbms
+
+ scriptIface->setOriginFromTable(-1);
+
+ // Call action
+ luaL_checktype(L, -1, LUA_TTABLE);
+ lua_getfield(L, -1, "action");
+ luaL_checktype(L, -1, LUA_TFUNCTION);
+ lua_remove(L, -2); // Remove registered_lbms[m_id]
+ push_v3s16(L, p);
+ pushnode(L, n, env->getGameDef()->ndef());
+
+ int result = lua_pcall(L, 2, 0, error_handler);
+ if (result)
+ scriptIface->scriptError(result, "LuaLBM::trigger");
+
+ lua_pop(L, 1); // Pop error handler
+}
+
+void LuaEmergeAreaCallback(v3s16 blockpos, EmergeAction action, void *param)
+{
+ ScriptCallbackState *state = (ScriptCallbackState *)param;
+ assert(state != NULL);
+ assert(state->script != NULL);
+ assert(state->refcount > 0);
+
+ state->refcount--;
+
+ state->script->on_emerge_area_completion(blockpos, action, state);
+
+ if (state->refcount == 0)
+ delete state;
+}
+
// Exported functions
// set_node(pos, node)
@@ -412,8 +470,7 @@ int ModApiEnvMod::l_add_item(lua_State *L)
if(item.empty() || !item.isKnown(getServer(L)->idef()))
return 0;
- lua_pushcfunction(L, script_error_handler);
- int errorhandler = lua_gettop(L);
+ int error_handler = PUSH_ERROR_HANDLER(L);
// Use spawn_item to spawn a __builtin:item
lua_getglobal(L, "core");
@@ -424,9 +481,9 @@ int ModApiEnvMod::l_add_item(lua_State *L)
lua_pushvalue(L, 1);
lua_pushstring(L, item.getItemString().c_str());
- PCALL_RESL(L, lua_pcall(L, 2, 1, errorhandler));
+ PCALL_RESL(L, lua_pcall(L, 2, 1, error_handler));
- lua_remove(L, errorhandler); // Remove error handler
+ lua_remove(L, error_handler);
return 1;
}
@@ -504,6 +561,15 @@ int ModApiEnvMod::l_get_timeofday(lua_State *L)
return 1;
}
+// get_day_count() -> int
+int ModApiEnvMod::l_get_day_count(lua_State *L)
+{
+ GET_ENV_PTR;
+
+ lua_pushnumber(L, env->getDayCount());
+ return 1;
+}
+
// get_gametime()
int ModApiEnvMod::l_get_gametime(lua_State *L)
{
@@ -659,7 +725,7 @@ int ModApiEnvMod::l_find_nodes_in_area_under_air(lua_State *L)
// returns world-specific PerlinNoise
int ModApiEnvMod::l_get_perlin(lua_State *L)
{
- GET_ENV_PTR;
+ GET_ENV_PTR_NO_MAP_LOCK;
NoiseParams params;
@@ -685,7 +751,7 @@ int ModApiEnvMod::l_get_perlin(lua_State *L)
// returns world-specific PerlinNoiseMap
int ModApiEnvMod::l_get_perlin_map(lua_State *L)
{
- GET_ENV_PTR;
+ GET_ENV_PTR_NO_MAP_LOCK;
NoiseParams np;
if (!read_noiseparams(L, 1, &np))
@@ -717,13 +783,20 @@ int ModApiEnvMod::l_get_voxel_manip(lua_State *L)
return 1;
}
-// clear_objects()
+// clear_objects([options])
// clear all objects in the environment
+// where options = {mode = "full" or "quick"}
int ModApiEnvMod::l_clear_objects(lua_State *L)
{
GET_ENV_PTR;
- env->clearAllObjects();
+ ClearObjectsMode mode = CLEAR_OBJECTS_MODE_FULL;
+ if (lua_istable(L, 1)) {
+ mode = (ClearObjectsMode)getenumfield(L, 1, "mode",
+ ModApiEnvMod::es_ClearObjectsMode, mode);
+ }
+
+ env->clearObjects(mode);
return 0;
}
@@ -753,6 +826,51 @@ int ModApiEnvMod::l_line_of_sight(lua_State *L)
return 1;
}
+// emerge_area(p1, p2, [callback, context])
+// emerge mapblocks in area p1..p2, calls callback with context upon completion
+int ModApiEnvMod::l_emerge_area(lua_State *L)
+{
+ GET_ENV_PTR;
+
+ EmergeCompletionCallback callback = NULL;
+ ScriptCallbackState *state = NULL;
+
+ EmergeManager *emerge = getServer(L)->getEmergeManager();
+
+ v3s16 bpmin = getNodeBlockPos(read_v3s16(L, 1));
+ v3s16 bpmax = getNodeBlockPos(read_v3s16(L, 2));
+ sortBoxVerticies(bpmin, bpmax);
+
+ size_t num_blocks = VoxelArea(bpmin, bpmax).getVolume();
+ assert(num_blocks != 0);
+
+ if (lua_isfunction(L, 3)) {
+ callback = LuaEmergeAreaCallback;
+
+ lua_pushvalue(L, 3);
+ int callback_ref = luaL_ref(L, LUA_REGISTRYINDEX);
+
+ lua_pushvalue(L, 4);
+ int args_ref = luaL_ref(L, LUA_REGISTRYINDEX);
+
+ state = new ScriptCallbackState;
+ state->script = getServer(L)->getScriptIface();
+ state->callback_ref = callback_ref;
+ state->args_ref = args_ref;
+ state->refcount = num_blocks;
+ state->origin = getScriptApiBase(L)->getOrigin();
+ }
+
+ for (s16 z = bpmin.Z; z <= bpmax.Z; z++)
+ for (s16 y = bpmin.Y; y <= bpmax.Y; y++)
+ for (s16 x = bpmin.X; x <= bpmax.X; x++) {
+ emerge->enqueueBlockEmergeEx(v3s16(x, y, z), PEER_ID_INEXISTENT,
+ BLOCK_EMERGE_ALLOW_GEN | BLOCK_EMERGE_FORCE_QUEUE, callback, state);
+ }
+
+ return 0;
+}
+
// delete_area(p1, p2)
// delete mapblocks in area p1..p2
int ModApiEnvMod::l_delete_area(lua_State *L)
@@ -797,19 +915,19 @@ int ModApiEnvMod::l_find_path(lua_State *L)
unsigned int searchdistance = luaL_checkint(L, 3);
unsigned int max_jump = luaL_checkint(L, 4);
unsigned int max_drop = luaL_checkint(L, 5);
- algorithm algo = A_PLAIN_NP;
+ PathAlgorithm algo = PA_PLAIN_NP;
if (!lua_isnil(L, 6)) {
std::string algorithm = luaL_checkstring(L,6);
if (algorithm == "A*")
- algo = A_PLAIN;
+ algo = PA_PLAIN;
if (algorithm == "Dijkstra")
- algo = DIJKSTRA;
+ algo = PA_DIJKSTRA;
}
- std::vector<v3s16> path =
- get_Path(env,pos1,pos2,searchdistance,max_jump,max_drop,algo);
+ std::vector<v3s16> path = get_path(env, pos1, pos2,
+ searchdistance, max_jump, max_drop, algo);
if (path.size() > 0)
{
@@ -920,13 +1038,6 @@ int ModApiEnvMod::l_forceload_free_block(lua_State *L)
return 0;
}
-// get_us_time()
-int ModApiEnvMod::l_get_us_time(lua_State *L)
-{
- lua_pushnumber(L, porting::getTimeUs());
- return 1;
-}
-
void ModApiEnvMod::Initialize(lua_State *L, int top)
{
API_FCT(set_node);
@@ -953,9 +1064,11 @@ void ModApiEnvMod::Initialize(lua_State *L, int top)
API_FCT(set_timeofday);
API_FCT(get_timeofday);
API_FCT(get_gametime);
+ API_FCT(get_day_count);
API_FCT(find_node_near);
API_FCT(find_nodes_in_area);
API_FCT(find_nodes_in_area_under_air);
+ API_FCT(emerge_area);
API_FCT(delete_area);
API_FCT(get_perlin);
API_FCT(get_perlin_map);
@@ -967,5 +1080,4 @@ void ModApiEnvMod::Initialize(lua_State *L, int top)
API_FCT(transforming_liquid_add);
API_FCT(forceload_block);
API_FCT(forceload_free_block);
- API_FCT(get_us_time);
}
diff --git a/src/script/lua_api/l_env.h b/src/script/lua_api/l_env.h
index 0d4ca788e..89dd7978f 100644
--- a/src/script/lua_api/l_env.h
+++ b/src/script/lua_api/l_env.h
@@ -113,6 +113,9 @@ private:
// get_gametime()
static int l_get_gametime(lua_State *L);
+ // get_day_count() -> int
+ static int l_get_day_count(lua_State *L);
+
// find_node_near(pos, radius, nodenames) -> pos or nil
// nodenames: eg. {"ignore", "group:tree"} or "default:dirt"
static int l_find_node_near(lua_State *L);
@@ -125,6 +128,9 @@ private:
// nodenames: eg. {"ignore", "group:tree"} or "default:dirt"
static int l_find_nodes_in_area_under_air(lua_State *L);
+ // emerge_area(p1, p2)
+ static int l_emerge_area(lua_State *L);
+
// delete_area(p1, p2) -> true/false
static int l_delete_area(lua_State *L);
@@ -165,15 +171,13 @@ private:
// stops forceloading a position
static int l_forceload_free_block(lua_State *L);
- // get us precision time
- static int l_get_us_time(lua_State *L);
-
public:
static void Initialize(lua_State *L, int top);
+
+ static struct EnumString es_ClearObjectsMode[];
};
-class LuaABM : public ActiveBlockModifier
-{
+class LuaABM : public ActiveBlockModifier {
private:
int m_id;
@@ -181,16 +185,18 @@ private:
std::set<std::string> m_required_neighbors;
float m_trigger_interval;
u32 m_trigger_chance;
+ bool m_simple_catch_up;
public:
LuaABM(lua_State *L, int id,
const std::set<std::string> &trigger_contents,
const std::set<std::string> &required_neighbors,
- float trigger_interval, u32 trigger_chance):
+ float trigger_interval, u32 trigger_chance, bool simple_catch_up):
m_id(id),
m_trigger_contents(trigger_contents),
m_required_neighbors(required_neighbors),
m_trigger_interval(trigger_interval),
- m_trigger_chance(trigger_chance)
+ m_trigger_chance(trigger_chance),
+ m_simple_catch_up(simple_catch_up)
{
}
virtual std::set<std::string> getTriggerContents()
@@ -209,8 +215,38 @@ public:
{
return m_trigger_chance;
}
+ virtual bool getSimpleCatchUp()
+ {
+ return m_simple_catch_up;
+ }
virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n,
u32 active_object_count, u32 active_object_count_wider);
};
+class LuaLBM : public LoadingBlockModifierDef
+{
+private:
+ int m_id;
+public:
+ LuaLBM(lua_State *L, int id,
+ const std::set<std::string> &trigger_contents,
+ const std::string &name,
+ bool run_at_every_load):
+ m_id(id)
+ {
+ this->run_at_every_load = run_at_every_load;
+ this->trigger_contents = trigger_contents;
+ this->name = name;
+ }
+ virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n);
+};
+
+struct ScriptCallbackState {
+ GameScripting *script;
+ int callback_ref;
+ int args_ref;
+ unsigned int refcount;
+ std::string origin;
+};
+
#endif /* L_ENV_H_ */
diff --git a/src/script/lua_api/l_http.cpp b/src/script/lua_api/l_http.cpp
new file mode 100644
index 000000000..8bd39b6ed
--- /dev/null
+++ b/src/script/lua_api/l_http.cpp
@@ -0,0 +1,193 @@
+/*
+Minetest
+Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@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 "lua_api/l_internal.h"
+#include "common/c_converter.h"
+#include "common/c_content.h"
+#include "lua_api/l_http.h"
+#include "httpfetch.h"
+#include "settings.h"
+#include "debug.h"
+#include "log.h"
+
+#include <algorithm>
+#include <iomanip>
+#include <cctype>
+
+#define HTTP_API(name) \
+ lua_pushstring(L, #name); \
+ lua_pushcfunction(L, l_http_##name); \
+ lua_settable(L, -3);
+
+#if USE_CURL
+void ModApiHttp::read_http_fetch_request(lua_State *L, HTTPFetchRequest &req)
+{
+ luaL_checktype(L, 1, LUA_TTABLE);
+
+ req.caller = httpfetch_caller_alloc_secure();
+ getstringfield(L, 1, "url", req.url);
+ lua_getfield(L, 1, "user_agent");
+ if (lua_isstring(L, -1))
+ req.useragent = getstringfield_default(L, 1, "user_agent", "");
+ lua_pop(L, 1);
+ 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, "post_data");
+ if (lua_istable(L, 2)) {
+ lua_pushnil(L);
+ while (lua_next(L, 2) != 0)
+ {
+ req.post_fields[luaL_checkstring(L, -2)] = luaL_checkstring(L, -1);
+ lua_pop(L, 1);
+ }
+ } else if (lua_isstring(L, 2)) {
+ req.post_data = lua_tostring(L, 2);
+ }
+ lua_pop(L, 1);
+
+ lua_getfield(L, 1, "extra_headers");
+ if (lua_istable(L, 2)) {
+ lua_pushnil(L);
+ while (lua_next(L, 2) != 0)
+ {
+ const char *header = luaL_checkstring(L, -1);
+ req.extra_headers.push_back(header);
+ lua_pop(L, 1);
+ }
+ }
+ lua_pop(L, 1);
+}
+
+void ModApiHttp::push_http_fetch_result(lua_State *L, HTTPFetchResult &res, bool completed)
+{
+ lua_newtable(L);
+ setboolfield(L, -1, "succeeded", res.succeeded);
+ setboolfield(L, -1, "timeout", res.timeout);
+ setboolfield(L, -1, "completed", completed);
+ setintfield(L, -1, "code", res.response_code);
+ setstringfield(L, -1, "data", res.data.c_str());
+}
+
+// http_api.fetch_async(HTTPRequest definition)
+int ModApiHttp::l_http_fetch_async(lua_State *L)
+{
+ NO_MAP_LOCK_REQUIRED;
+
+ HTTPFetchRequest req;
+ read_http_fetch_request(L, req);
+
+ actionstream << "Mod performs HTTP request with URL " << req.url << std::endl;
+ httpfetch_async(req);
+
+ // Convert handle to hex string since lua can't handle 64-bit integers
+ std::stringstream handle_conversion_stream;
+ handle_conversion_stream << std::hex << req.caller;
+ std::string caller_handle(handle_conversion_stream.str());
+
+ lua_pushstring(L, caller_handle.c_str());
+ return 1;
+}
+
+// http_api.fetch_async_get(handle)
+int ModApiHttp::l_http_fetch_async_get(lua_State *L)
+{
+ NO_MAP_LOCK_REQUIRED;
+
+ std::string handle_str = luaL_checkstring(L, 1);
+
+ // Convert hex string back to 64-bit handle
+ u64 handle;
+ std::stringstream handle_conversion_stream;
+ handle_conversion_stream << std::hex << handle_str;
+ handle_conversion_stream >> handle;
+
+ HTTPFetchResult res;
+ bool completed = httpfetch_async_get(handle, res);
+
+ push_http_fetch_result(L, res, completed);
+
+ return 1;
+}
+
+int ModApiHttp::l_request_http_api(lua_State *L)
+{
+ NO_MAP_LOCK_REQUIRED;
+
+ // We have to make sure that this function is being called directly by
+ // a mod, otherwise a malicious mod could override this function and
+ // steal its return value.
+ lua_Debug info;
+
+ // Make sure there's only one item below this function on the stack...
+ if (lua_getstack(L, 2, &info)) {
+ return 0;
+ }
+ FATAL_ERROR_IF(!lua_getstack(L, 1, &info), "lua_getstack() failed");
+ FATAL_ERROR_IF(!lua_getinfo(L, "S", &info), "lua_getinfo() failed");
+
+ // ...and that that item is the main file scope.
+ if (strcmp(info.what, "main") != 0) {
+ return 0;
+ }
+
+ // Mod must be listed in secure.http_mods or secure.trusted_mods
+ lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_CURRENT_MOD_NAME);
+ if (!lua_isstring(L, -1)) {
+ return 0;
+ }
+
+ const char *mod_name = lua_tostring(L, -1);
+ std::string http_mods = g_settings->get("secure.http_mods");
+ http_mods.erase(std::remove(http_mods.begin(), http_mods.end(), ' '), http_mods.end());
+ std::vector<std::string> mod_list_http = str_split(http_mods, ',');
+
+ std::string trusted_mods = g_settings->get("secure.trusted_mods");
+ trusted_mods.erase(std::remove(trusted_mods.begin(), trusted_mods.end(), ' '), trusted_mods.end());
+ std::vector<std::string> mod_list_trusted = str_split(trusted_mods, ',');
+
+ mod_list_http.insert(mod_list_http.end(), mod_list_trusted.begin(), mod_list_trusted.end());
+ if (std::find(mod_list_http.begin(), mod_list_http.end(), mod_name) == mod_list_http.end()) {
+ lua_pushnil(L);
+ return 1;
+ }
+
+ lua_getglobal(L, "core");
+ lua_getfield(L, -1, "http_add_fetch");
+
+ lua_newtable(L);
+ HTTP_API(fetch_async);
+ HTTP_API(fetch_async_get);
+
+ // Stack now looks like this:
+ // <core.http_add_fetch> <table with fetch_async, fetch_async_get>
+ // Now call core.http_add_fetch to append .fetch(request, callback) to table
+ lua_call(L, 1, 1);
+
+ return 1;
+}
+#endif
+
+void ModApiHttp::Initialize(lua_State *L, int top)
+{
+#if USE_CURL
+ API_FCT(request_http_api);
+#endif
+}
diff --git a/src/logoutputbuffer.h b/src/script/lua_api/l_http.h
index 69f06c444..077ade691 100644
--- a/src/logoutputbuffer.h
+++ b/src/script/lua_api/l_http.h
@@ -17,42 +17,34 @@ with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef LOGOUTPUTBUFFER_HEADER
-#define LOGOUTPUTBUFFER_HEADER
+#ifndef L_HTTP_H_
+#define L_HTTP_H_
-#include "log.h"
-#include <queue>
+#include "lua_api/l_base.h"
+#include "config.h"
-class LogOutputBuffer : public ILogOutput
-{
-public:
- LogOutputBuffer(LogMessageLevel maxlev)
- {
- log_add_output(this, maxlev);
- }
- ~LogOutputBuffer()
- {
- log_remove_output(this);
- }
- virtual void printLog(const std::string &line)
- {
- m_buf.push(line);
- }
- std::string get()
- {
- if(empty())
- return "";
- std::string s = m_buf.front();
- m_buf.pop();
- return s;
- }
- bool empty()
- {
- return m_buf.empty();
- }
+struct HTTPFetchRequest;
+struct HTTPFetchResult;
+
+class ModApiHttp : public ModApiBase {
private:
- std::queue<std::string> m_buf;
-};
+#if USE_CURL
+ // Helpers for HTTP fetch functions
+ 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_async({url=, timeout=, post_data=})
+ static int l_http_fetch_async(lua_State *L);
+ // http_fetch_async_get(handle)
+ static int l_http_fetch_async_get(lua_State *L);
+
+ // request_http_api()
+ static int l_request_http_api(lua_State *L);
#endif
+public:
+ static void Initialize(lua_State *L, int top);
+};
+
+#endif /* L_HTTP_H_ */
diff --git a/src/script/lua_api/l_internal.h b/src/script/lua_api/l_internal.h
index 1e40c5c4a..456c8fcce 100644
--- a/src/script/lua_api/l_internal.h
+++ b/src/script/lua_api/l_internal.h
@@ -33,6 +33,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#define API_FCT(name) registerFunction(L, #name, l_##name,top)
#define ASYNC_API_FCT(name) engine.registerFunction(#name, l_##name)
+#define MAP_LOCK_REQUIRED
#define NO_MAP_LOCK_REQUIRED
/*
@@ -45,4 +46,13 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#endif
*/
+#define GET_ENV_PTR_NO_MAP_LOCK \
+ ServerEnvironment *env = (ServerEnvironment *)getEnv(L); \
+ if (env == NULL) \
+ return 0
+
+#define GET_ENV_PTR \
+ MAP_LOCK_REQUIRED; \
+ GET_ENV_PTR_NO_MAP_LOCK
+
#endif /* L_INTERNAL_H_ */
diff --git a/src/script/lua_api/l_inventory.cpp b/src/script/lua_api/l_inventory.cpp
index f48f6083b..de9f9374a 100644
--- a/src/script/lua_api/l_inventory.cpp
+++ b/src/script/lua_api/l_inventory.cpp
@@ -491,6 +491,7 @@ int ModApiInventory::l_get_inventory(lua_State *L)
std::string type = checkstringfield(L, 1, "type");
if(type == "node"){
+ MAP_LOCK_REQUIRED;
lua_getfield(L, 1, "pos");
v3s16 pos = check_v3s16(L, -1);
loc.setNodeMeta(pos);
@@ -514,7 +515,7 @@ int ModApiInventory::l_get_inventory(lua_State *L)
InvRef::create(L, loc);
else
lua_pushnil(L);
- return 1;
+ return 1;
// END NO_MAP_LOCK_REQUIRED;
}
}
diff --git a/src/script/lua_api/l_item.cpp b/src/script/lua_api/l_item.cpp
index 842b15709..5381cba76 100644
--- a/src/script/lua_api/l_item.cpp
+++ b/src/script/lua_api/l_item.cpp
@@ -94,7 +94,7 @@ int LuaItemStack::l_set_count(lua_State *L)
bool status;
lua_Integer count = luaL_checkinteger(L, 2);
- if (count <= 65535) {
+ if (count > 0 && count <= 65535) {
item.count = count;
status = true;
} else {
diff --git a/src/script/lua_api/l_mainmenu.cpp b/src/script/lua_api/l_mainmenu.cpp
index 92311d6fc..7b29db159 100644
--- a/src/script/lua_api/l_mainmenu.cpp
+++ b/src/script/lua_api/l_mainmenu.cpp
@@ -706,16 +706,13 @@ int ModApiMainMenu::l_set_topleft_text(lua_State *L)
/******************************************************************************/
int ModApiMainMenu::l_get_mapgen_names(lua_State *L)
{
- lua_newtable(L);
-
- std::list<const char *> names;
- EmergeManager::getMapgenNames(names);
+ std::vector<const char *> names;
+ EmergeManager::getMapgenNames(&names, lua_toboolean(L, 1));
- int i = 1;
- for (std::list<const char *>::const_iterator
- it = names.begin(); it != names.end(); ++it) {
- lua_pushstring(L, *it);
- lua_rawseti(L, -2, i++);
+ lua_newtable(L);
+ for (size_t i = 0; i != names.size(); i++) {
+ lua_pushstring(L, names[i]);
+ lua_rawseti(L, -2, i + 1);
}
return 1;
@@ -725,8 +722,8 @@ int ModApiMainMenu::l_get_mapgen_names(lua_State *L)
/******************************************************************************/
int ModApiMainMenu::l_get_modpath(lua_State *L)
{
- std::string modpath
- = fs::RemoveRelativePathComponents(porting::path_user + DIR_DELIM + "mods" + DIR_DELIM);
+ std::string modpath = fs::RemoveRelativePathComponents(
+ porting::path_user + DIR_DELIM + "mods" + DIR_DELIM);
lua_pushstring(L, modpath.c_str());
return 1;
}
@@ -734,8 +731,8 @@ int ModApiMainMenu::l_get_modpath(lua_State *L)
/******************************************************************************/
int ModApiMainMenu::l_get_gamepath(lua_State *L)
{
- std::string gamepath
- = fs::RemoveRelativePathComponents(porting::path_user + DIR_DELIM + "games" + DIR_DELIM);
+ std::string gamepath = fs::RemoveRelativePathComponents(
+ porting::path_user + DIR_DELIM + "games" + DIR_DELIM);
lua_pushstring(L, gamepath.c_str());
return 1;
}
@@ -743,44 +740,46 @@ int ModApiMainMenu::l_get_gamepath(lua_State *L)
/******************************************************************************/
int ModApiMainMenu::l_get_texturepath(lua_State *L)
{
- std::string gamepath
- = fs::RemoveRelativePathComponents(porting::path_user + DIR_DELIM + "textures");
+ std::string gamepath = fs::RemoveRelativePathComponents(
+ porting::path_user + DIR_DELIM + "textures");
lua_pushstring(L, gamepath.c_str());
return 1;
}
int ModApiMainMenu::l_get_texturepath_share(lua_State *L)
{
- std::string gamepath
- = fs::RemoveRelativePathComponents(porting::path_share + DIR_DELIM + "textures");
+ std::string gamepath = fs::RemoveRelativePathComponents(
+ porting::path_share + DIR_DELIM + "textures");
lua_pushstring(L, gamepath.c_str());
return 1;
}
/******************************************************************************/
int ModApiMainMenu::l_create_dir(lua_State *L) {
- const char *path = luaL_checkstring(L, 1);
+ const char *path = luaL_checkstring(L, 1);
if (ModApiMainMenu::isMinetestPath(path)) {
- lua_pushboolean(L,fs::CreateAllDirs(path));
+ lua_pushboolean(L, fs::CreateAllDirs(path));
return 1;
}
- lua_pushboolean(L,false);
+
+ lua_pushboolean(L, false);
return 1;
}
/******************************************************************************/
int ModApiMainMenu::l_delete_dir(lua_State *L)
{
- const char *path = luaL_checkstring(L, 1);
+ const char *path = luaL_checkstring(L, 1);
std::string absolute_path = fs::RemoveRelativePathComponents(path);
if (ModApiMainMenu::isMinetestPath(absolute_path)) {
- lua_pushboolean(L,fs::RecursiveDelete(absolute_path));
+ lua_pushboolean(L, fs::RecursiveDelete(absolute_path));
return 1;
}
- lua_pushboolean(L,false);
+
+ lua_pushboolean(L, false);
return 1;
}
@@ -1060,8 +1059,8 @@ int ModApiMainMenu::l_get_video_modes(lua_State *L)
/******************************************************************************/
int ModApiMainMenu::l_gettext(lua_State *L)
{
- std::wstring wtext = wstrgettext((std::string) luaL_checkstring(L, 1));
- lua_pushstring(L, wide_to_utf8(wtext).c_str());
+ std::string text = strgettext(std::string(luaL_checkstring(L, 1)));
+ lua_pushstring(L, text.c_str());
return 1;
}
@@ -1096,7 +1095,9 @@ int ModApiMainMenu::l_get_screen_info(lua_State *L)
/******************************************************************************/
int ModApiMainMenu::l_get_min_supp_proto(lua_State *L)
{
- lua_pushinteger(L, CLIENT_PROTOCOL_VERSION_MIN);
+ u16 proto_version_min = g_settings->getFlag("send_pre_v25_init") ?
+ CLIENT_PROTOCOL_VERSION_MIN_LEGACY : CLIENT_PROTOCOL_VERSION_MIN;
+ lua_pushinteger(L, proto_version_min);
return 1;
}
diff --git a/src/script/lua_api/l_mainmenu.h b/src/script/lua_api/l_mainmenu.h
index 9c1fed272..405af25e8 100644
--- a/src/script/lua_api/l_mainmenu.h
+++ b/src/script/lua_api/l_mainmenu.h
@@ -119,8 +119,6 @@ private:
static int l_get_texturepath_share(lua_State *L);
- static int l_get_dirlist(lua_State *L);
-
static int l_create_dir(lua_State *L);
static int l_delete_dir(lua_State *L);
diff --git a/src/script/lua_api/l_mapgen.cpp b/src/script/lua_api/l_mapgen.cpp
index d30b68054..fb839176b 100644
--- a/src/script/lua_api/l_mapgen.cpp
+++ b/src/script/lua_api/l_mapgen.cpp
@@ -70,6 +70,7 @@ struct EnumString ModApiMapgen::es_OreType[] =
{
{ORE_SCATTER, "scatter"},
{ORE_SHEET, "sheet"},
+ {ORE_PUFF, "puff"},
{ORE_BLOB, "blob"},
{ORE_VEIN, "vein"},
{0, NULL},
@@ -449,10 +450,38 @@ size_t get_biome_list(lua_State *L, int index,
///////////////////////////////////////////////////////////////////////////////
+// get_biome_id(biomename)
+// returns the biome id used in biomemap
+int ModApiMapgen::l_get_biome_id(lua_State *L)
+{
+ NO_MAP_LOCK_REQUIRED;
+
+ const char *biome_str = lua_tostring(L, 1);
+ if (!biome_str)
+ return 0;
+
+ BiomeManager *bmgr = getServer(L)->getEmergeManager()->biomemgr;
+
+ if (!bmgr)
+ return 0;
+
+ Biome *biome = (Biome *)bmgr->getByName(biome_str);
+
+ if (!biome || biome->index == OBJDEF_INVALID_INDEX)
+ return 0;
+
+ lua_pushinteger(L, biome->index);
+
+ return 1;
+}
+
+
// get_mapgen_object(objectname)
// returns the requested object used during map generation
int ModApiMapgen::l_get_mapgen_object(lua_State *L)
{
+ NO_MAP_LOCK_REQUIRED;
+
const char *mgobjstr = lua_tostring(L, 1);
int mgobjint;
@@ -464,7 +493,7 @@ int ModApiMapgen::l_get_mapgen_object(lua_State *L)
EmergeManager *emerge = getServer(L)->getEmergeManager();
Mapgen *mg = emerge->getCurrentMapgen();
if (!mg)
- return 0;
+ throw LuaError("Must only be called in a mapgen thread!");
size_t maplen = mg->csize.X * mg->csize.Z;
@@ -563,6 +592,8 @@ int ModApiMapgen::l_get_mapgen_object(lua_State *L)
int ModApiMapgen::l_get_mapgen_params(lua_State *L)
{
+ NO_MAP_LOCK_REQUIRED;
+
MapgenParams *params = &getServer(L)->getEmergeManager()->params;
lua_newtable(L);
@@ -579,7 +610,7 @@ int ModApiMapgen::l_get_mapgen_params(lua_State *L)
lua_pushinteger(L, params->chunksize);
lua_setfield(L, -2, "chunksize");
- std::string flagstr = writeFlagString(params->flags, flagdesc_mapgen, (u32)-1);
+ std::string flagstr = writeFlagString(params->flags, flagdesc_mapgen, U32_MAX);
lua_pushstring(L, flagstr.c_str());
lua_setfield(L, -2, "flags");
@@ -591,10 +622,16 @@ int ModApiMapgen::l_get_mapgen_params(lua_State *L)
// set mapgen parameters
int ModApiMapgen::l_set_mapgen_params(lua_State *L)
{
+ NO_MAP_LOCK_REQUIRED;
+
if (!lua_istable(L, 1))
return 0;
- MapgenParams *params = &getServer(L)->getEmergeManager()->params;
+ EmergeManager *emerge = getServer(L)->getEmergeManager();
+ if (emerge->isRunning())
+ throw LuaError("Cannot set parameters while mapgen is running");
+
+ MapgenParams *params = &emerge->params;
u32 flags = 0, flagmask = 0;
lua_getfield(L, 1, "mgname");
@@ -612,6 +649,10 @@ int ModApiMapgen::l_set_mapgen_params(lua_State *L)
if (lua_isnumber(L, -1))
params->water_level = lua_tointeger(L, -1);
+ lua_getfield(L, 1, "chunksize");
+ if (lua_isnumber(L, -1))
+ params->chunksize = lua_tointeger(L, -1);
+
warn_if_field_exists(L, 1, "flagmask",
"Deprecated: flags field now includes unset flags.");
lua_getfield(L, 1, "flagmask");
@@ -631,6 +672,8 @@ int ModApiMapgen::l_set_mapgen_params(lua_State *L)
// set global config values for noise parameters
int ModApiMapgen::l_set_noiseparams(lua_State *L)
{
+ NO_MAP_LOCK_REQUIRED;
+
const char *name = luaL_checkstring(L, 1);
NoiseParams np;
@@ -648,6 +691,8 @@ int ModApiMapgen::l_set_noiseparams(lua_State *L)
// get_noiseparams(name)
int ModApiMapgen::l_get_noiseparams(lua_State *L)
{
+ NO_MAP_LOCK_REQUIRED;
+
std::string name = luaL_checkstring(L, 1);
NoiseParams np;
@@ -662,6 +707,8 @@ int ModApiMapgen::l_get_noiseparams(lua_State *L)
// set_gen_notify(flags, {deco_id_table})
int ModApiMapgen::l_set_gen_notify(lua_State *L)
{
+ NO_MAP_LOCK_REQUIRED;
+
u32 flags = 0, flagmask = 0;
EmergeManager *emerge = getServer(L)->getEmergeManager();
@@ -686,6 +733,8 @@ int ModApiMapgen::l_set_gen_notify(lua_State *L)
// get_gen_notify()
int ModApiMapgen::l_get_gen_notify(lua_State *L)
{
+ NO_MAP_LOCK_REQUIRED;
+
EmergeManager *emerge = getServer(L)->getEmergeManager();
push_flags_string(L, flagdesc_gennotify, emerge->gen_notify_on,
emerge->gen_notify_on);
@@ -705,6 +754,8 @@ int ModApiMapgen::l_get_gen_notify(lua_State *L)
// register_biome({lots of stuff})
int ModApiMapgen::l_register_biome(lua_State *L)
{
+ NO_MAP_LOCK_REQUIRED;
+
int index = 1;
luaL_checktype(L, index, LUA_TTABLE);
@@ -729,6 +780,8 @@ int ModApiMapgen::l_register_biome(lua_State *L)
// register_decoration({lots of stuff})
int ModApiMapgen::l_register_decoration(lua_State *L)
{
+ NO_MAP_LOCK_REQUIRED;
+
int index = 1;
luaL_checktype(L, index, LUA_TTABLE);
@@ -869,6 +922,8 @@ bool read_deco_schematic(lua_State *L, SchematicManager *schemmgr, DecoSchematic
// register_ore({lots of stuff})
int ModApiMapgen::l_register_ore(lua_State *L)
{
+ NO_MAP_LOCK_REQUIRED;
+
int index = 1;
luaL_checktype(L, index, LUA_TTABLE);
@@ -880,7 +935,7 @@ int ModApiMapgen::l_register_ore(lua_State *L)
"ore_type", es_OreType, ORE_SCATTER);
Ore *ore = oremgr->create(oretype);
if (!ore) {
- errorstream << "register_ore: ore_type " << oretype << " not implemented";
+ errorstream << "register_ore: ore_type " << oretype << " not implemented\n";
return 0;
}
@@ -889,10 +944,19 @@ int ModApiMapgen::l_register_ore(lua_State *L)
ore->clust_scarcity = getintfield_default(L, index, "clust_scarcity", 1);
ore->clust_num_ores = getintfield_default(L, index, "clust_num_ores", 1);
ore->clust_size = getintfield_default(L, index, "clust_size", 0);
- ore->nthresh = getfloatfield_default(L, index, "noise_threshhold", 0);
ore->noise = NULL;
ore->flags = 0;
+ //// Get noise_threshold
+ warn_if_field_exists(L, index, "noise_threshhold",
+ "Deprecated: new name is \"noise_threshold\".");
+
+ float nthresh;
+ if (!getfloatfield(L, index, "noise_threshold", nthresh) &&
+ !getfloatfield(L, index, "noise_threshhold", nthresh))
+ nthresh = 0;
+ ore->nthresh = nthresh;
+
//// Get y_min/y_max
warn_if_field_exists(L, index, "height_min",
"Deprecated: new name is \"y_min\".");
@@ -937,10 +1001,43 @@ int ModApiMapgen::l_register_ore(lua_State *L)
}
lua_pop(L, 1);
- if (oretype == ORE_VEIN) {
- OreVein *orevein = (OreVein *)ore;
- orevein->random_factor = getfloatfield_default(L, index,
- "random_factor", 1.f);
+ //// Get type-specific parameters
+ switch (oretype) {
+ case ORE_SHEET: {
+ OreSheet *oresheet = (OreSheet *)ore;
+
+ oresheet->column_height_min = getintfield_default(L, index,
+ "column_height_min", 1);
+ oresheet->column_height_max = getintfield_default(L, index,
+ "column_height_max", ore->clust_size);
+ oresheet->column_midpoint_factor = getfloatfield_default(L, index,
+ "column_midpoint_factor", 0.5f);
+
+ break;
+ }
+ case ORE_PUFF: {
+ OrePuff *orepuff = (OrePuff *)ore;
+
+ lua_getfield(L, index, "np_puff_top");
+ read_noiseparams(L, -1, &orepuff->np_puff_top);
+ lua_pop(L, 1);
+
+ lua_getfield(L, index, "np_puff_bottom");
+ read_noiseparams(L, -1, &orepuff->np_puff_bottom);
+ lua_pop(L, 1);
+
+ break;
+ }
+ case ORE_VEIN: {
+ OreVein *orevein = (OreVein *)ore;
+
+ orevein->random_factor = getfloatfield_default(L, index,
+ "random_factor", 1.f);
+
+ break;
+ }
+ default:
+ break;
}
ObjDefHandle handle = oremgr->add(ore);
@@ -964,6 +1061,8 @@ int ModApiMapgen::l_register_ore(lua_State *L)
// register_schematic({schematic}, replacements={})
int ModApiMapgen::l_register_schematic(lua_State *L)
{
+ NO_MAP_LOCK_REQUIRED;
+
SchematicManager *schemmgr = getServer(L)->getEmergeManager()->schemmgr;
StringMap replace_names;
@@ -989,6 +1088,8 @@ int ModApiMapgen::l_register_schematic(lua_State *L)
// clear_registered_biomes()
int ModApiMapgen::l_clear_registered_biomes(lua_State *L)
{
+ NO_MAP_LOCK_REQUIRED;
+
BiomeManager *bmgr = getServer(L)->getEmergeManager()->biomemgr;
bmgr->clear();
return 0;
@@ -998,6 +1099,8 @@ int ModApiMapgen::l_clear_registered_biomes(lua_State *L)
// clear_registered_decorations()
int ModApiMapgen::l_clear_registered_decorations(lua_State *L)
{
+ NO_MAP_LOCK_REQUIRED;
+
DecorationManager *dmgr = getServer(L)->getEmergeManager()->decomgr;
dmgr->clear();
return 0;
@@ -1007,6 +1110,8 @@ int ModApiMapgen::l_clear_registered_decorations(lua_State *L)
// clear_registered_ores()
int ModApiMapgen::l_clear_registered_ores(lua_State *L)
{
+ NO_MAP_LOCK_REQUIRED;
+
OreManager *omgr = getServer(L)->getEmergeManager()->oremgr;
omgr->clear();
return 0;
@@ -1016,6 +1121,8 @@ int ModApiMapgen::l_clear_registered_ores(lua_State *L)
// clear_registered_schematics()
int ModApiMapgen::l_clear_registered_schematics(lua_State *L)
{
+ NO_MAP_LOCK_REQUIRED;
+
SchematicManager *smgr = getServer(L)->getEmergeManager()->schemmgr;
smgr->clear();
return 0;
@@ -1025,6 +1132,8 @@ int ModApiMapgen::l_clear_registered_schematics(lua_State *L)
// generate_ores(vm, p1, p2, [ore_id])
int ModApiMapgen::l_generate_ores(lua_State *L)
{
+ NO_MAP_LOCK_REQUIRED;
+
EmergeManager *emerge = getServer(L)->getEmergeManager();
Mapgen mg;
@@ -1049,6 +1158,8 @@ int ModApiMapgen::l_generate_ores(lua_State *L)
// generate_decorations(vm, p1, p2, [deco_id])
int ModApiMapgen::l_generate_decorations(lua_State *L)
{
+ NO_MAP_LOCK_REQUIRED;
+
EmergeManager *emerge = getServer(L)->getEmergeManager();
Mapgen mg;
@@ -1073,6 +1184,8 @@ int ModApiMapgen::l_generate_decorations(lua_State *L)
// create_schematic(p1, p2, probability_list, filename, y_slice_prob_list)
int ModApiMapgen::l_create_schematic(lua_State *L)
{
+ MAP_LOCK_REQUIRED;
+
INodeDefManager *ndef = getServer(L)->getNodeDefManager();
const char *filename = luaL_checkstring(L, 4);
@@ -1136,6 +1249,8 @@ int ModApiMapgen::l_create_schematic(lua_State *L)
// place_schematic(p, schematic, rotation, replacement)
int ModApiMapgen::l_place_schematic(lua_State *L)
{
+ MAP_LOCK_REQUIRED;
+
Map *map = &(getEnv(L)->getMap());
SchematicManager *schemmgr = getServer(L)->getEmergeManager()->schemmgr;
@@ -1165,15 +1280,59 @@ int ModApiMapgen::l_place_schematic(lua_State *L)
return 0;
}
- schem->placeStructure(map, p, 0, (Rotation)rot, force_placement);
+ schem->placeOnMap(map, p, 0, (Rotation)rot, force_placement);
lua_pushboolean(L, true);
return 1;
}
+int ModApiMapgen::l_place_schematic_on_vmanip(lua_State *L)
+{
+ NO_MAP_LOCK_REQUIRED;
+
+ SchematicManager *schemmgr = getServer(L)->getEmergeManager()->schemmgr;
+
+ //// Read VoxelManip object
+ MMVManip *vm = LuaVoxelManip::checkobject(L, 1)->vm;
+
+ //// Read position
+ v3s16 p = check_v3s16(L, 2);
+
+ //// Read rotation
+ int rot = ROTATE_0;
+ const char *enumstr = lua_tostring(L, 4);
+ if (enumstr)
+ string_to_enum(es_Rotation, rot, std::string(enumstr));
+
+ //// Read force placement
+ bool force_placement = true;
+ if (lua_isboolean(L, 6))
+ force_placement = lua_toboolean(L, 6);
+
+ //// Read node replacements
+ StringMap replace_names;
+ if (lua_istable(L, 5))
+ read_schematic_replacements(L, 5, &replace_names);
+
+ //// Read schematic
+ Schematic *schem = get_or_load_schematic(L, 3, schemmgr, &replace_names);
+ if (!schem) {
+ errorstream << "place_schematic: failed to get schematic" << std::endl;
+ return 0;
+ }
+
+ bool schematic_did_fit = schem->placeOnVManip(
+ vm, p, 0, (Rotation)rot, force_placement);
+
+ lua_pushboolean(L, schematic_did_fit);
+ return 1;
+}
+
// serialize_schematic(schematic, format, options={...})
int ModApiMapgen::l_serialize_schematic(lua_State *L)
{
+ NO_MAP_LOCK_REQUIRED;
+
SchematicManager *schemmgr = getServer(L)->getEmergeManager()->schemmgr;
//// Read options
@@ -1223,6 +1382,7 @@ int ModApiMapgen::l_serialize_schematic(lua_State *L)
void ModApiMapgen::Initialize(lua_State *L, int top)
{
+ API_FCT(get_biome_id);
API_FCT(get_mapgen_object);
API_FCT(get_mapgen_params);
@@ -1246,5 +1406,6 @@ void ModApiMapgen::Initialize(lua_State *L, int top)
API_FCT(generate_decorations);
API_FCT(create_schematic);
API_FCT(place_schematic);
+ API_FCT(place_schematic_on_vmanip);
API_FCT(serialize_schematic);
}
diff --git a/src/script/lua_api/l_mapgen.h b/src/script/lua_api/l_mapgen.h
index 7440d1285..9751c0db6 100644
--- a/src/script/lua_api/l_mapgen.h
+++ b/src/script/lua_api/l_mapgen.h
@@ -24,6 +24,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
class ModApiMapgen : public ModApiBase {
private:
+ // get_biome_id(biomename)
+ // returns the biome id used in biomemap
+ static int l_get_biome_id(lua_State *L);
+
// get_mapgen_object(objectname)
// returns the requested object used during map generation
static int l_get_mapgen_object(lua_State *L);
@@ -81,9 +85,13 @@ private:
// create_schematic(p1, p2, probability_list, filename)
static int l_create_schematic(lua_State *L);
- // place_schematic(p, schematic, rotation, replacement)
+ // place_schematic(p, schematic, rotation, replacements, force_placement)
static int l_place_schematic(lua_State *L);
+ // place_schematic_on_vmanip(vm, p, schematic,
+ // rotation, replacements, force_placement)
+ static int l_place_schematic_on_vmanip(lua_State *L);
+
// serialize_schematic(schematic, format, options={...})
static int l_serialize_schematic(lua_State *L);
diff --git a/src/script/lua_api/l_nodemeta.cpp b/src/script/lua_api/l_nodemeta.cpp
index 6cdbe5c68..c8bc7d558 100644
--- a/src/script/lua_api/l_nodemeta.cpp
+++ b/src/script/lua_api/l_nodemeta.cpp
@@ -24,6 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "common/c_content.h"
#include "environment.h"
#include "map.h"
+#include "gamedef.h"
#include "nodemetadata.h"
@@ -43,7 +44,7 @@ NodeMetadata* NodeMetaRef::getmeta(NodeMetaRef *ref, bool auto_create)
{
NodeMetadata *meta = ref->m_env->getMap().getNodeMetadata(ref->m_p);
if(meta == NULL && auto_create) {
- meta = new NodeMetadata(ref->m_env->getGameDef());
+ meta = new NodeMetadata(ref->m_env->getGameDef()->idef());
if(!ref->m_env->getMap().setNodeMetadata(ref->m_p, meta)) {
delete meta;
return NULL;
@@ -81,6 +82,8 @@ int NodeMetaRef::gc_object(lua_State *L) {
// get_string(self, name)
int NodeMetaRef::l_get_string(lua_State *L)
{
+ MAP_LOCK_REQUIRED;
+
NodeMetaRef *ref = checkobject(L, 1);
std::string name = luaL_checkstring(L, 2);
@@ -97,6 +100,8 @@ int NodeMetaRef::l_get_string(lua_State *L)
// set_string(self, name, var)
int NodeMetaRef::l_set_string(lua_State *L)
{
+ MAP_LOCK_REQUIRED;
+
NodeMetaRef *ref = checkobject(L, 1);
std::string name = luaL_checkstring(L, 2);
size_t len = 0;
@@ -114,6 +119,8 @@ int NodeMetaRef::l_set_string(lua_State *L)
// get_int(self, name)
int NodeMetaRef::l_get_int(lua_State *L)
{
+ MAP_LOCK_REQUIRED;
+
NodeMetaRef *ref = checkobject(L, 1);
std::string name = lua_tostring(L, 2);
@@ -130,6 +137,8 @@ int NodeMetaRef::l_get_int(lua_State *L)
// set_int(self, name, var)
int NodeMetaRef::l_set_int(lua_State *L)
{
+ MAP_LOCK_REQUIRED;
+
NodeMetaRef *ref = checkobject(L, 1);
std::string name = lua_tostring(L, 2);
int a = lua_tointeger(L, 3);
@@ -146,6 +155,8 @@ int NodeMetaRef::l_set_int(lua_State *L)
// get_float(self, name)
int NodeMetaRef::l_get_float(lua_State *L)
{
+ MAP_LOCK_REQUIRED;
+
NodeMetaRef *ref = checkobject(L, 1);
std::string name = lua_tostring(L, 2);
@@ -162,6 +173,8 @@ int NodeMetaRef::l_get_float(lua_State *L)
// set_float(self, name, var)
int NodeMetaRef::l_set_float(lua_State *L)
{
+ MAP_LOCK_REQUIRED;
+
NodeMetaRef *ref = checkobject(L, 1);
std::string name = lua_tostring(L, 2);
float a = lua_tonumber(L, 3);
@@ -178,6 +191,8 @@ int NodeMetaRef::l_set_float(lua_State *L)
// get_inventory(self)
int NodeMetaRef::l_get_inventory(lua_State *L)
{
+ MAP_LOCK_REQUIRED;
+
NodeMetaRef *ref = checkobject(L, 1);
getmeta(ref, true); // try to ensure the metadata exists
InvRef::createNodeMeta(L, ref->m_p);
@@ -187,6 +202,8 @@ int NodeMetaRef::l_get_inventory(lua_State *L)
// to_table(self)
int NodeMetaRef::l_to_table(lua_State *L)
{
+ MAP_LOCK_REQUIRED;
+
NodeMetaRef *ref = checkobject(L, 1);
NodeMetadata *meta = getmeta(ref, true);
@@ -229,6 +246,8 @@ int NodeMetaRef::l_to_table(lua_State *L)
// from_table(self, table)
int NodeMetaRef::l_from_table(lua_State *L)
{
+ MAP_LOCK_REQUIRED;
+
NodeMetaRef *ref = checkobject(L, 1);
int base = 2;
diff --git a/src/script/lua_api/l_nodetimer.cpp b/src/script/lua_api/l_nodetimer.cpp
index c81a7ebc9..601113516 100644
--- a/src/script/lua_api/l_nodetimer.cpp
+++ b/src/script/lua_api/l_nodetimer.cpp
@@ -39,6 +39,7 @@ NodeTimerRef* NodeTimerRef::checkobject(lua_State *L, int narg)
int NodeTimerRef::l_set(lua_State *L)
{
+ MAP_LOCK_REQUIRED;
NodeTimerRef *o = checkobject(L, 1);
ServerEnvironment *env = o->m_env;
if(env == NULL) return 0;
@@ -50,6 +51,7 @@ int NodeTimerRef::l_set(lua_State *L)
int NodeTimerRef::l_start(lua_State *L)
{
+ MAP_LOCK_REQUIRED;
NodeTimerRef *o = checkobject(L, 1);
ServerEnvironment *env = o->m_env;
if(env == NULL) return 0;
@@ -60,6 +62,7 @@ int NodeTimerRef::l_start(lua_State *L)
int NodeTimerRef::l_stop(lua_State *L)
{
+ MAP_LOCK_REQUIRED;
NodeTimerRef *o = checkobject(L, 1);
ServerEnvironment *env = o->m_env;
if(env == NULL) return 0;
@@ -69,6 +72,7 @@ int NodeTimerRef::l_stop(lua_State *L)
int NodeTimerRef::l_is_started(lua_State *L)
{
+ MAP_LOCK_REQUIRED;
NodeTimerRef *o = checkobject(L, 1);
ServerEnvironment *env = o->m_env;
if(env == NULL) return 0;
@@ -80,6 +84,7 @@ int NodeTimerRef::l_is_started(lua_State *L)
int NodeTimerRef::l_get_timeout(lua_State *L)
{
+ MAP_LOCK_REQUIRED;
NodeTimerRef *o = checkobject(L, 1);
ServerEnvironment *env = o->m_env;
if(env == NULL) return 0;
@@ -91,6 +96,7 @@ int NodeTimerRef::l_get_timeout(lua_State *L)
int NodeTimerRef::l_get_elapsed(lua_State *L)
{
+ MAP_LOCK_REQUIRED;
NodeTimerRef *o = checkobject(L, 1);
ServerEnvironment *env = o->m_env;
if(env == NULL) return 0;
diff --git a/src/script/lua_api/l_noise.cpp b/src/script/lua_api/l_noise.cpp
index c8dc2d2dc..04dc6048f 100644
--- a/src/script/lua_api/l_noise.cpp
+++ b/src/script/lua_api/l_noise.cpp
@@ -22,6 +22,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "common/c_converter.h"
#include "common/c_content.h"
#include "log.h"
+#include "porting.h"
+#include "util/numeric.h"
///////////////////////////////////////
/*
@@ -410,6 +412,7 @@ const luaL_reg LuaPerlinNoiseMap::methods[] = {
int LuaPseudoRandom::l_next(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
+
LuaPseudoRandom *o = checkobject(L, 1);
int min = 0;
int max = 32767;
@@ -437,7 +440,9 @@ int LuaPseudoRandom::l_next(lua_State *L)
int LuaPseudoRandom::create_object(lua_State *L)
{
- int seed = luaL_checknumber(L, 1);
+ NO_MAP_LOCK_REQUIRED;
+
+ u64 seed = luaL_checknumber(L, 1);
LuaPseudoRandom *o = new LuaPseudoRandom(seed);
*(void **)(lua_newuserdata(L, sizeof(void *))) = o;
luaL_getmetatable(L, className);
@@ -532,8 +537,10 @@ int LuaPcgRandom::l_rand_normal_dist(lua_State *L)
int LuaPcgRandom::create_object(lua_State *L)
{
- lua_Integer seed = luaL_checknumber(L, 1);
- LuaPcgRandom *o = lua_isnumber(L, 2) ?
+ NO_MAP_LOCK_REQUIRED;
+
+ u64 seed = luaL_checknumber(L, 1);
+ LuaPcgRandom *o = lua_isnumber(L, 2) ?
new LuaPcgRandom(seed, lua_tointeger(L, 2)) :
new LuaPcgRandom(seed);
*(void **)(lua_newuserdata(L, sizeof(void *))) = o;
@@ -595,3 +602,116 @@ const luaL_reg LuaPcgRandom::methods[] = {
luamethod(LuaPcgRandom, rand_normal_dist),
{0,0}
};
+
+///////////////////////////////////////
+/*
+ LuaSecureRandom
+*/
+
+bool LuaSecureRandom::fillRandBuf()
+{
+ return porting::secure_rand_fill_buf(m_rand_buf, RAND_BUF_SIZE);
+}
+
+int LuaSecureRandom::l_next_bytes(lua_State *L)
+{
+ NO_MAP_LOCK_REQUIRED;
+
+ LuaSecureRandom *o = checkobject(L, 1);
+ u32 count = lua_isnumber(L, 2) ? lua_tointeger(L, 2) : 1;
+
+ // Limit count
+ count = MYMIN(RAND_BUF_SIZE, count);
+
+ // Find out whether we can pass directly from our array, or have to do some gluing
+ size_t count_remaining = RAND_BUF_SIZE - o->m_rand_idx;
+ if (count_remaining >= count) {
+ lua_pushlstring(L, o->m_rand_buf + o->m_rand_idx, count);
+ o->m_rand_idx += count;
+ } else {
+ char output_buf[RAND_BUF_SIZE];
+
+ // Copy over with what we have left from our current buffer
+ memcpy(output_buf, o->m_rand_buf + o->m_rand_idx, count_remaining);
+
+ // Refill buffer and copy over the remainder of what was requested
+ o->fillRandBuf();
+ memcpy(output_buf + count_remaining, o->m_rand_buf, count - count_remaining);
+
+ // Update index
+ o->m_rand_idx = count - count_remaining;
+
+ lua_pushlstring(L, output_buf, count);
+ }
+
+ return 1;
+}
+
+
+int LuaSecureRandom::create_object(lua_State *L)
+{
+ LuaSecureRandom *o = new LuaSecureRandom();
+
+ // Fail and return nil if we can't securely fill the buffer
+ if (!o->fillRandBuf()) {
+ delete o;
+ return 0;
+ }
+
+ *(void **)(lua_newuserdata(L, sizeof(void *))) = o;
+ luaL_getmetatable(L, className);
+ lua_setmetatable(L, -2);
+ return 1;
+}
+
+
+int LuaSecureRandom::gc_object(lua_State *L)
+{
+ LuaSecureRandom *o = *(LuaSecureRandom **)(lua_touserdata(L, 1));
+ delete o;
+ return 0;
+}
+
+
+LuaSecureRandom *LuaSecureRandom::checkobject(lua_State *L, int narg)
+{
+ luaL_checktype(L, narg, LUA_TUSERDATA);
+ void *ud = luaL_checkudata(L, narg, className);
+ if (!ud)
+ luaL_typerror(L, narg, className);
+ return *(LuaSecureRandom **)ud;
+}
+
+
+void LuaSecureRandom::Register(lua_State *L)
+{
+ lua_newtable(L);
+ int methodtable = lua_gettop(L);
+ luaL_newmetatable(L, className);
+ int metatable = lua_gettop(L);
+
+ lua_pushliteral(L, "__metatable");
+ lua_pushvalue(L, methodtable);
+ lua_settable(L, metatable);
+
+ lua_pushliteral(L, "__index");
+ lua_pushvalue(L, methodtable);
+ lua_settable(L, metatable);
+
+ lua_pushliteral(L, "__gc");
+ lua_pushcfunction(L, gc_object);
+ lua_settable(L, metatable);
+
+ lua_pop(L, 1);
+
+ luaL_openlib(L, 0, methods, 0);
+ lua_pop(L, 1);
+
+ lua_register(L, className, create_object);
+}
+
+const char LuaSecureRandom::className[] = "SecureRandom";
+const luaL_reg LuaSecureRandom::methods[] = {
+ luamethod(LuaSecureRandom, next_bytes),
+ {0,0}
+};
diff --git a/src/script/lua_api/l_noise.h b/src/script/lua_api/l_noise.h
index e958c5a23..492eb7550 100644
--- a/src/script/lua_api/l_noise.h
+++ b/src/script/lua_api/l_noise.h
@@ -160,4 +160,37 @@ public:
static void Register(lua_State *L);
};
+
+/*
+ LuaSecureRandom
+*/
+class LuaSecureRandom : public ModApiBase {
+private:
+ static const size_t RAND_BUF_SIZE = 2048;
+ static const char className[];
+ static const luaL_reg methods[];
+
+ u32 m_rand_idx;
+ char m_rand_buf[RAND_BUF_SIZE];
+
+ // Exported functions
+
+ // garbage collector
+ static int gc_object(lua_State *L);
+
+ // next_bytes(self, count) -> get count many bytes
+ static int l_next_bytes(lua_State *L);
+
+public:
+ bool fillRandBuf();
+
+ // LuaSecureRandom()
+ // Creates an LuaSecureRandom and leaves it on top of stack
+ static int create_object(lua_State *L);
+
+ static LuaSecureRandom *checkobject(lua_State *L, int narg);
+
+ static void Register(lua_State *L);
+};
+
#endif /* L_NOISE_H_ */
diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp
index 3ac8eeefb..6d6614e7d 100644
--- a/src/script/lua_api/l_object.cpp
+++ b/src/script/lua_api/l_object.cpp
@@ -31,10 +31,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "hud.h"
#include "scripting_game.h"
-#define GET_ENV_PTR ServerEnvironment* env = \
- dynamic_cast<ServerEnvironment*>(getEnv(L)); \
- if (env == NULL) return 0
-
struct EnumString es_HudElementType[] =
{
{HUD_ELEM_IMAGE, "image"},
@@ -132,7 +128,6 @@ int ObjectRef::gc_object(lua_State *L) {
// remove(self)
int ObjectRef::l_remove(lua_State *L)
{
- NO_MAP_LOCK_REQUIRED;
GET_ENV_PTR;
ObjectRef *ref = checkobject(L, 1);
@@ -409,6 +404,7 @@ int ObjectRef::l_get_armor_groups(lua_State *L)
// physics_override_gravity, sneak, sneak_glitch)
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;
@@ -441,6 +437,7 @@ int ObjectRef::l_set_physics_override(lua_State *L)
// 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)
@@ -509,7 +506,7 @@ int ObjectRef::l_get_animation(lua_State *L)
// set_local_animation(self, {stand/idle}, {walk}, {dig}, {walk+dig}, frame_speed)
int ObjectRef::l_set_local_animation(lua_State *L)
{
- //NO_MAP_LOCK_REQUIRED;
+ NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
Player *player = getplayer(ref);
if (player == NULL)
@@ -534,7 +531,7 @@ int ObjectRef::l_set_local_animation(lua_State *L)
// get_local_animation(self)
int ObjectRef::l_get_local_animation(lua_State *L)
{
- //NO_MAP_LOCK_REQUIRED
+ NO_MAP_LOCK_REQUIRED
ObjectRef *ref = checkobject(L, 1);
Player *player = getplayer(ref);
if (player == NULL)
@@ -555,7 +552,7 @@ int ObjectRef::l_get_local_animation(lua_State *L)
// set_eye_offset(self, v3f first pv, v3f third pv)
int ObjectRef::l_set_eye_offset(lua_State *L)
{
- //NO_MAP_LOCK_REQUIRED;
+ NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
Player *player = getplayer(ref);
if (player == NULL)
@@ -585,7 +582,7 @@ int ObjectRef::l_set_eye_offset(lua_State *L)
// get_eye_offset(self)
int ObjectRef::l_get_eye_offset(lua_State *L)
{
- //NO_MAP_LOCK_REQUIRED;
+ NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
Player *player = getplayer(ref);
if (player == NULL)
@@ -642,7 +639,6 @@ int ObjectRef::l_get_bone_position(lua_State *L)
// set_attach(self, parent, bone, position, rotation)
int ObjectRef::l_set_attach(lua_State *L)
{
- NO_MAP_LOCK_REQUIRED;
GET_ENV_PTR;
ObjectRef *ref = checkobject(L, 1);
@@ -681,7 +677,6 @@ int ObjectRef::l_set_attach(lua_State *L)
// get_attach(self)
int ObjectRef::l_get_attach(lua_State *L)
{
- NO_MAP_LOCK_REQUIRED;
GET_ENV_PTR;
ObjectRef *ref = checkobject(L, 1);
@@ -709,7 +704,6 @@ int ObjectRef::l_get_attach(lua_State *L)
// set_detach(self)
int ObjectRef::l_set_detach(lua_State *L)
{
- NO_MAP_LOCK_REQUIRED;
GET_ENV_PTR;
ObjectRef *ref = checkobject(L, 1);
@@ -773,6 +767,59 @@ int ObjectRef::l_is_player(lua_State *L)
return 1;
}
+// set_nametag_attributes(self, attributes)
+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)
+ return 0;
+ ObjectProperties *prop = co->accessObjectProperties();
+ if (!prop)
+ return 0;
+
+ lua_getfield(L, 2, "color");
+ if (!lua_isnil(L, -1)) {
+ video::SColor color = prop->nametag_color;
+ read_color(L, -1, &color);
+ prop->nametag_color = color;
+ }
+ lua_pop(L, 1);
+
+ std::string nametag = getstringfield_default(L, 2, "text", "");
+ if (nametag != "")
+ prop->nametag = nametag;
+
+ co->notifyObjectPropertiesModified();
+ lua_pushboolean(L, true);
+ return 1;
+}
+
+// get_nametag_attributes(self)
+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)
+ return 0;
+ ObjectProperties *prop = co->accessObjectProperties();
+ if (!prop)
+ return 0;
+
+ video::SColor color = prop->nametag_color;
+
+ lua_newtable(L);
+ push_ARGB8(L, color);
+ lua_setfield(L, -2, "color");
+ lua_pushstring(L, prop->nametag.c_str());
+ lua_setfield(L, -2, "text");
+ return 1;
+}
+
/* LuaEntitySAO-only */
// setvelocity(self, {x=num, y=num, z=num})
@@ -1137,6 +1184,7 @@ int ObjectRef::l_get_player_control_bits(lua_State *L)
// hud_add(self, form)
int ObjectRef::l_hud_add(lua_State *L)
{
+ NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
Player *player = getplayer(ref);
if (player == NULL)
@@ -1187,7 +1235,7 @@ int ObjectRef::l_hud_add(lua_State *L)
}
u32 id = getServer(L)->hudAdd(player, elem);
- if (id == (u32)-1) {
+ if (id == U32_MAX) {
delete elem;
return 0;
}
@@ -1199,6 +1247,7 @@ int ObjectRef::l_hud_add(lua_State *L)
// hud_remove(self, id)
int ObjectRef::l_hud_remove(lua_State *L)
{
+ NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
Player *player = getplayer(ref);
if (player == NULL)
@@ -1218,6 +1267,7 @@ int ObjectRef::l_hud_remove(lua_State *L)
// hud_change(self, id, stat, data)
int ObjectRef::l_hud_change(lua_State *L)
{
+ NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
Player *player = getplayer(ref);
if (player == NULL)
@@ -1294,6 +1344,7 @@ int ObjectRef::l_hud_change(lua_State *L)
// hud_get(self, id)
int ObjectRef::l_hud_get(lua_State *L)
{
+ NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
Player *player = getplayer(ref);
if (player == NULL)
@@ -1344,6 +1395,7 @@ int ObjectRef::l_hud_get(lua_State *L)
// hud_set_flags(self, flags)
int ObjectRef::l_hud_set_flags(lua_State *L)
{
+ NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
Player *player = getplayer(ref);
if (player == NULL)
@@ -1369,6 +1421,7 @@ int ObjectRef::l_hud_set_flags(lua_State *L)
int ObjectRef::l_hud_get_flags(lua_State *L)
{
+ NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
Player *player = getplayer(ref);
if (player == NULL)
@@ -1394,6 +1447,7 @@ int ObjectRef::l_hud_get_flags(lua_State *L)
// hud_set_hotbar_itemcount(self, hotbar_itemcount)
int ObjectRef::l_hud_set_hotbar_itemcount(lua_State *L)
{
+ NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
Player *player = getplayer(ref);
if (player == NULL)
@@ -1411,6 +1465,7 @@ int ObjectRef::l_hud_set_hotbar_itemcount(lua_State *L)
// hud_get_hotbar_itemcount(self)
int ObjectRef::l_hud_get_hotbar_itemcount(lua_State *L)
{
+ NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
Player *player = getplayer(ref);
if (player == NULL)
@@ -1425,6 +1480,7 @@ int ObjectRef::l_hud_get_hotbar_itemcount(lua_State *L)
// hud_set_hotbar_image(self, name)
int ObjectRef::l_hud_set_hotbar_image(lua_State *L)
{
+ NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
Player *player = getplayer(ref);
if (player == NULL)
@@ -1439,6 +1495,7 @@ int ObjectRef::l_hud_set_hotbar_image(lua_State *L)
// hud_get_hotbar_image(self)
int ObjectRef::l_hud_get_hotbar_image(lua_State *L)
{
+ NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
Player *player = getplayer(ref);
if (player == NULL)
@@ -1452,6 +1509,7 @@ int ObjectRef::l_hud_get_hotbar_image(lua_State *L)
// hud_set_hotbar_selected_image(self, name)
int ObjectRef::l_hud_set_hotbar_selected_image(lua_State *L)
{
+ NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
Player *player = getplayer(ref);
if (player == NULL)
@@ -1466,6 +1524,7 @@ int ObjectRef::l_hud_set_hotbar_selected_image(lua_State *L)
// hud_get_hotbar_selected_image(self)
int ObjectRef::l_hud_get_hotbar_selected_image(lua_State *L)
{
+ NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
Player *player = getplayer(ref);
if (player == NULL)
@@ -1479,6 +1538,7 @@ int ObjectRef::l_hud_get_hotbar_selected_image(lua_State *L)
// set_sky(self, bgcolor, type, list)
int ObjectRef::l_set_sky(lua_State *L)
{
+ NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
Player *player = getplayer(ref);
if (player == NULL)
@@ -1517,6 +1577,7 @@ int ObjectRef::l_set_sky(lua_State *L)
// get_sky(self)
int ObjectRef::l_get_sky(lua_State *L)
{
+ NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
Player *player = getplayer(ref);
if (player == NULL)
@@ -1544,6 +1605,7 @@ int ObjectRef::l_get_sky(lua_State *L)
// override_day_night_ratio(self, brightness=0...1)
int ObjectRef::l_override_day_night_ratio(lua_State *L)
{
+ NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
Player *player = getplayer(ref);
if (player == NULL)
@@ -1566,6 +1628,7 @@ int ObjectRef::l_override_day_night_ratio(lua_State *L)
// get_day_night_ratio(self)
int ObjectRef::l_get_day_night_ratio(lua_State *L)
{
+ NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
Player *player = getplayer(ref);
if (player == NULL)
@@ -1583,45 +1646,6 @@ int ObjectRef::l_get_day_night_ratio(lua_State *L)
return 1;
}
-// set_nametag_attributes(self, attributes)
-int ObjectRef::l_set_nametag_attributes(lua_State *L)
-{
- NO_MAP_LOCK_REQUIRED;
- ObjectRef *ref = checkobject(L, 1);
- PlayerSAO *playersao = getplayersao(ref);
- if (playersao == NULL)
- return 0;
-
- lua_getfield(L, 2, "color");
- if (!lua_isnil(L, -1)) {
- video::SColor color = playersao->getNametagColor();
- if (!read_color(L, -1, &color))
- return 0;
- playersao->setNametagColor(color);
- }
-
- lua_pushboolean(L, true);
- return 1;
-}
-
-// get_nametag_attributes(self)
-int ObjectRef::l_get_nametag_attributes(lua_State *L)
-{
- NO_MAP_LOCK_REQUIRED;
- ObjectRef *ref = checkobject(L, 1);
- PlayerSAO *playersao = getplayersao(ref);
- if (playersao == NULL)
- return 0;
-
- video::SColor color = playersao->getNametagColor();
-
- lua_newtable(L);
- push_ARGB8(L, color);
- lua_setfield(L, -2, "color");
-
- return 1;
-}
-
ObjectRef::ObjectRef(ServerActiveObject *object):
m_object(object)
{
@@ -1709,6 +1733,8 @@ const luaL_reg ObjectRef::methods[] = {
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(ObjectRef, setvelocity),
luamethod(ObjectRef, getvelocity),
@@ -1758,7 +1784,5 @@ const luaL_reg ObjectRef::methods[] = {
luamethod(ObjectRef, get_local_animation),
luamethod(ObjectRef, set_eye_offset),
luamethod(ObjectRef, get_eye_offset),
- luamethod(ObjectRef, set_nametag_attributes),
- luamethod(ObjectRef, get_nametag_attributes),
{0,0}
};
diff --git a/src/script/lua_api/l_particles.cpp b/src/script/lua_api/l_particles.cpp
index 2532b2b08..f6c1725de 100644
--- a/src/script/lua_api/l_particles.cpp
+++ b/src/script/lua_api/l_particles.cpp
@@ -32,6 +32,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
// texture = e.g."default_wood.png"
int ModApiParticles::l_add_particle(lua_State *L)
{
+ MAP_LOCK_REQUIRED;
+
// Get parameters
v3f pos, vel, acc;
pos = vel = acc = v3f(0, 0, 0);
@@ -119,6 +121,8 @@ int ModApiParticles::l_add_particle(lua_State *L)
// texture = e.g."default_wood.png"
int ModApiParticles::l_add_particlespawner(lua_State *L)
{
+ MAP_LOCK_REQUIRED;
+
// Get parameters
u16 amount = 1;
v3f minpos, maxpos, minvel, maxvel, minacc, maxacc;
@@ -208,6 +212,8 @@ int ModApiParticles::l_add_particlespawner(lua_State *L)
// player (string) is optional
int ModApiParticles::l_delete_particlespawner(lua_State *L)
{
+ MAP_LOCK_REQUIRED;
+
// Get parameters
u32 id = luaL_checknumber(L, 1);
std::string playername = "";
diff --git a/src/script/lua_api/l_rollback.cpp b/src/script/lua_api/l_rollback.cpp
index 5744e6813..482b0cbf5 100644
--- a/src/script/lua_api/l_rollback.cpp
+++ b/src/script/lua_api/l_rollback.cpp
@@ -38,6 +38,8 @@ void push_RollbackNode(lua_State *L, RollbackNode &node)
// rollback_get_node_actions(pos, range, seconds, limit) -> {{actor, pos, time, oldnode, newnode}, ...}
int ModApiRollback::l_rollback_get_node_actions(lua_State *L)
{
+ NO_MAP_LOCK_REQUIRED;
+
v3s16 pos = read_v3s16(L, 1);
int range = luaL_checknumber(L, 2);
time_t seconds = (time_t) luaL_checknumber(L, 3);
@@ -79,6 +81,8 @@ int ModApiRollback::l_rollback_get_node_actions(lua_State *L)
// rollback_revert_actions_by(actor, seconds) -> bool, log messages
int ModApiRollback::l_rollback_revert_actions_by(lua_State *L)
{
+ MAP_LOCK_REQUIRED;
+
std::string actor = luaL_checkstring(L, 1);
int seconds = luaL_checknumber(L, 2);
Server *server = getServer(L);
diff --git a/src/script/lua_api/l_server.cpp b/src/script/lua_api/l_server.cpp
index 73eca9d60..59d3f5c70 100644
--- a/src/script/lua_api/l_server.cpp
+++ b/src/script/lua_api/l_server.cpp
@@ -30,6 +30,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
// request_shutdown()
int ModApiServer::l_request_shutdown(lua_State *L)
{
+ NO_MAP_LOCK_REQUIRED;
const char *msg = lua_tolstring(L, 1, NULL);
bool reconnect = lua_toboolean(L, 2);
getServer(L)->requestShutdown(msg ? msg : "", reconnect);
@@ -44,6 +45,16 @@ int ModApiServer::l_get_server_status(lua_State *L)
return 1;
}
+// print(text)
+int ModApiServer::l_print(lua_State *L)
+{
+ NO_MAP_LOCK_REQUIRED;
+ std::string text;
+ text = luaL_checkstring(L, 1);
+ getServer(L)->printToConsoleOnly(text);
+ return 0;
+}
+
// chat_send_all(text)
int ModApiServer::l_chat_send_all(lua_State *L)
{
@@ -110,7 +121,7 @@ int ModApiServer::l_get_player_ip(lua_State *L)
}
catch(con::PeerNotFoundException) // unlikely
{
- dstream << __FUNCTION_NAME << ": peer was not found" << std::endl;
+ dstream << FUNCTION_NAME << ": peer was not found" << std::endl;
lua_pushnil(L); // error
return 1;
}
@@ -136,7 +147,7 @@ int ModApiServer::l_get_player_information(lua_State *L)
}
catch(con::PeerNotFoundException) // unlikely
{
- dstream << __FUNCTION_NAME << ": peer was not found" << std::endl;
+ dstream << FUNCTION_NAME << ": peer was not found" << std::endl;
lua_pushnil(L); // error
return 1;
}
@@ -150,7 +161,7 @@ int ModApiServer::l_get_player_information(lua_State *L)
#define ERET(code) \
if (!(code)) { \
- dstream << __FUNCTION_NAME << ": peer was not found" << std::endl; \
+ dstream << FUNCTION_NAME << ": peer was not found" << std::endl; \
lua_pushnil(L); /* error */ \
return 1; \
}
@@ -281,7 +292,7 @@ int ModApiServer::l_ban_player(lua_State *L)
}
catch(con::PeerNotFoundException) // unlikely
{
- dstream << __FUNCTION_NAME << ": peer was not found" << std::endl;
+ dstream << FUNCTION_NAME << ": peer was not found" << std::endl;
lua_pushboolean(L, false); // error
return 1;
}
@@ -345,7 +356,7 @@ int ModApiServer::l_show_formspec(lua_State *L)
int ModApiServer::l_get_current_modname(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
- lua_getfield(L, LUA_REGISTRYINDEX, SCRIPT_MOD_NAME_FIELD);
+ lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_CURRENT_MOD_NAME);
return 1;
}
@@ -442,7 +453,7 @@ int ModApiServer::l_notify_authentication_modified(lua_State *L)
int ModApiServer::l_get_last_run_mod(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
- lua_getfield(L, LUA_REGISTRYINDEX, SCRIPT_MOD_NAME_FIELD);
+ lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_CURRENT_MOD_NAME);
const char *current_mod = lua_tostring(L, -1);
if (current_mod == NULL || current_mod[0] == '\0') {
lua_pop(L, 1);
@@ -504,6 +515,8 @@ void ModApiServer::Initialize(lua_State *L, int top)
API_FCT(get_modpath);
API_FCT(get_modnames);
+ API_FCT(print);
+
API_FCT(chat_send_all);
API_FCT(chat_send_player);
API_FCT(show_formspec);
diff --git a/src/script/lua_api/l_server.h b/src/script/lua_api/l_server.h
index df31f325f..06a5ddc24 100644
--- a/src/script/lua_api/l_server.h
+++ b/src/script/lua_api/l_server.h
@@ -46,6 +46,9 @@ private:
// the returned list is sorted alphabetically for you
static int l_get_modnames(lua_State *L);
+ // print(text)
+ static int l_print(lua_State *L);
+
// chat_send_all(text)
static int l_chat_send_all(lua_State *L);
diff --git a/src/script/lua_api/l_util.cpp b/src/script/lua_api/l_util.cpp
index 12146e80a..c3e6c8964 100644
--- a/src/script/lua_api/l_util.cpp
+++ b/src/script/lua_api/l_util.cpp
@@ -25,9 +25,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "serialization.h"
#include "json/json.h"
#include "cpp_api/s_security.h"
-#include "areastore.h"
-#include "debug.h"
#include "porting.h"
+#include "debug.h"
#include "log.h"
#include "tool.h"
#include "filesys.h"
@@ -35,70 +34,49 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "util/auth.h"
#include <algorithm>
-// debug(...)
-// Writes a line to dstream
-int ModApiUtil::l_debug(lua_State *L)
-{
- NO_MAP_LOCK_REQUIRED;
- // Handle multiple parameters to behave like standard lua print()
- int n = lua_gettop(L);
- lua_getglobal(L, "tostring");
- for (int i = 1; i <= n; i++) {
- /*
- Call tostring(i-th argument).
- This is what print() does, and it behaves a bit
- differently from directly calling lua_tostring.
- */
- lua_pushvalue(L, -1); /* function to be called */
- lua_pushvalue(L, i); /* value to print */
- lua_call(L, 1, 1);
- size_t len;
- const char *s = lua_tolstring(L, -1, &len);
- if (i > 1)
- dstream << "\t";
- if (s)
- dstream << std::string(s, len);
- lua_pop(L, 1);
- }
- dstream << std::endl;
- return 0;
-}
-
// log([level,] text)
// Writes a line to the logger.
// The one-argument version logs to infostream.
-// The two-argument version accept a log level: error, action, info, or verbose.
+// The two-argument version accepts a log level.
+// Either the special case "deprecated" for deprecation notices, or any specified in
+// Logger::stringToLevel(name).
int ModApiUtil::l_log(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
std::string text;
- LogMessageLevel level = LMT_INFO;
+ LogLevel level = LL_NONE;
if (lua_isnone(L, 2)) {
- text = lua_tostring(L, 1);
- }
- else {
- std::string levelname = luaL_checkstring(L, 1);
+ text = luaL_checkstring(L, 1);
+ } else {
+ std::string name = luaL_checkstring(L, 1);
text = luaL_checkstring(L, 2);
- if(levelname == "error")
- level = LMT_ERROR;
- else if(levelname == "action")
- level = LMT_ACTION;
- else if(levelname == "verbose")
- level = LMT_VERBOSE;
- else if (levelname == "deprecated") {
- log_deprecated(L,text);
+ if (name == "deprecated") {
+ log_deprecated(L, text);
return 0;
}
-
+ level = Logger::stringToLevel(name);
+ if (level == LL_MAX) {
+ warningstream << "Tried to log at unknown level '" << name
+ << "'. Defaulting to \"none\"." << std::endl;
+ level = LL_NONE;
+ }
}
- log_printline(level, text);
+ g_logger.log(level, text);
return 0;
}
+// get_us_time()
+int ModApiUtil::l_get_us_time(lua_State *L)
+{
+ NO_MAP_LOCK_REQUIRED;
+ lua_pushnumber(L, porting::getTimeUs());
+ return 1;
+}
+
#define CHECK_SECURE_SETTING(L, name) \
- if (name.compare(0, 7, "secure.") == 0) {\
- lua_pushliteral(L, "Attempt to set secure setting.");\
- lua_error(L);\
+ if (ScriptApiSecurity::isSecure(L) && \
+ name.compare(0, 7, "secure.") == 0) { \
+ throw LuaError("Attempt to set secure setting."); \
}
// setting_set(name, value)
@@ -183,8 +161,14 @@ int ModApiUtil::l_parse_json(lua_State *L)
if (!reader.parse(stream, root)) {
errorstream << "Failed to parse json data "
<< reader.getFormattedErrorMessages();
- errorstream << "data: \"" << jsonstr << "\""
- << std::endl;
+ size_t jlen = strlen(jsonstr);
+ if (jlen > 100) {
+ errorstream << "Data (" << jlen
+ << " bytes) printed to warningstream." << std::endl;
+ warningstream << "data: \"" << jsonstr << "\"" << std::endl;
+ } else {
+ errorstream << "data: \"" << jsonstr << "\"" << std::endl;
+ }
lua_pushnil(L);
return 1;
}
@@ -267,7 +251,7 @@ int ModApiUtil::l_get_password_hash(lua_State *L)
NO_MAP_LOCK_REQUIRED;
std::string name = luaL_checkstring(L, 1);
std::string raw_password = luaL_checkstring(L, 2);
- std::string hash = translatePassword(name, raw_password);
+ std::string hash = translate_password(name, raw_password);
lua_pushstring(L, hash.c_str());
return 1;
}
@@ -290,6 +274,8 @@ int ModApiUtil::l_is_yes(lua_State *L)
int ModApiUtil::l_get_builtin_path(lua_State *L)
{
+ NO_MAP_LOCK_REQUIRED;
+
std::string path = porting::path_share + DIR_DELIM + "builtin";
lua_pushstring(L, path.c_str());
return 1;
@@ -298,6 +284,8 @@ int ModApiUtil::l_get_builtin_path(lua_State *L)
// compress(data, method, level)
int ModApiUtil::l_compress(lua_State *L)
{
+ NO_MAP_LOCK_REQUIRED;
+
size_t size;
const char *data = luaL_checklstring(L, 1, &size);
@@ -317,6 +305,8 @@ int ModApiUtil::l_compress(lua_State *L)
// decompress(data, method)
int ModApiUtil::l_decompress(lua_State *L)
{
+ NO_MAP_LOCK_REQUIRED;
+
size_t size;
const char *data = luaL_checklstring(L, 1, &size);
@@ -367,32 +357,57 @@ int ModApiUtil::l_get_dir_list(lua_State *L)
int ModApiUtil::l_request_insecure_environment(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
+
+ // Just return _G if security is disabled
if (!ScriptApiSecurity::isSecure(L)) {
lua_getglobal(L, "_G");
return 1;
}
- lua_getfield(L, LUA_REGISTRYINDEX, SCRIPT_MOD_NAME_FIELD);
+
+ // We have to make sure that this function is being called directly by
+ // a mod, otherwise a malicious mod could override this function and
+ // steal its return value.
+ lua_Debug info;
+ // Make sure there's only one item below this function on the stack...
+ if (lua_getstack(L, 2, &info)) {
+ return 0;
+ }
+ FATAL_ERROR_IF(!lua_getstack(L, 1, &info), "lua_getstack() failed");
+ FATAL_ERROR_IF(!lua_getinfo(L, "S", &info), "lua_getinfo() failed");
+ // ...and that that item is the main file scope.
+ if (strcmp(info.what, "main") != 0) {
+ return 0;
+ }
+
+ // Get mod name
+ lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_CURRENT_MOD_NAME);
if (!lua_isstring(L, -1)) {
- lua_pushnil(L);
- return 1;
+ return 0;
}
+
+ // Check secure.trusted_mods
const char *mod_name = lua_tostring(L, -1);
std::string trusted_mods = g_settings->get("secure.trusted_mods");
+ trusted_mods.erase(std::remove(trusted_mods.begin(),
+ trusted_mods.end(), ' '), trusted_mods.end());
std::vector<std::string> mod_list = str_split(trusted_mods, ',');
- if (std::find(mod_list.begin(), mod_list.end(), mod_name) == mod_list.end()) {
- lua_pushnil(L);
- return 1;
+ if (std::find(mod_list.begin(), mod_list.end(), mod_name) ==
+ mod_list.end()) {
+ return 0;
}
- lua_getfield(L, LUA_REGISTRYINDEX, "globals_backup");
+
+ // Push insecure environment
+ lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_GLOBALS_BACKUP);
return 1;
}
void ModApiUtil::Initialize(lua_State *L, int top)
{
- API_FCT(debug);
API_FCT(log);
+ API_FCT(get_us_time);
+
API_FCT(setting_set);
API_FCT(setting_get);
API_FCT(setting_setbool);
@@ -422,9 +437,10 @@ void ModApiUtil::Initialize(lua_State *L, int top)
void ModApiUtil::InitializeAsync(AsyncEngine& engine)
{
- ASYNC_API_FCT(debug);
ASYNC_API_FCT(log);
+ ASYNC_API_FCT(get_us_time);
+
//ASYNC_API_FCT(setting_set);
ASYNC_API_FCT(setting_get);
//ASYNC_API_FCT(setting_setbool);
diff --git a/src/script/lua_api/l_util.h b/src/script/lua_api/l_util.h
index e75aa28cb..6fac7e7eb 100644
--- a/src/script/lua_api/l_util.h
+++ b/src/script/lua_api/l_util.h
@@ -35,16 +35,15 @@ private:
GUIEngine instance should be in here.
*/
- // debug(text)
- // Writes a line to dstream
- static int l_debug(lua_State *L);
-
// log([level,] text)
// Writes a line to the logger.
// The one-argument version logs to infostream.
- // The two-argument version accept a log level: error, action, info, or verbose.
+ // The two-argument version accepts a log level.
static int l_log(lua_State *L);
+ // get us precision time
+ static int l_get_us_time(lua_State *L);
+
// setting_set(name, value)
static int l_setting_set(lua_State *L);
diff --git a/src/script/lua_api/l_vmanip.cpp b/src/script/lua_api/l_vmanip.cpp
index ac6c10303..f13866408 100644
--- a/src/script/lua_api/l_vmanip.cpp
+++ b/src/script/lua_api/l_vmanip.cpp
@@ -28,10 +28,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "server.h"
#include "mapgen.h"
-#define GET_ENV_PTR ServerEnvironment* env = \
- dynamic_cast<ServerEnvironment*>(getEnv(L)); \
- if (env == NULL) return 0
-
// garbage collector
int LuaVoxelManip::gc_object(lua_State *L)
{
@@ -43,6 +39,8 @@ int LuaVoxelManip::gc_object(lua_State *L)
int LuaVoxelManip::l_read_from_map(lua_State *L)
{
+ MAP_LOCK_REQUIRED;
+
LuaVoxelManip *o = checkobject(L, 1);
MMVManip *vm = o->vm;
@@ -108,6 +106,8 @@ int LuaVoxelManip::l_set_data(lua_State *L)
int LuaVoxelManip::l_write_to_map(lua_State *L)
{
+ MAP_LOCK_REQUIRED;
+
LuaVoxelManip *o = checkobject(L, 1);
MMVManip *vm = o->vm;
@@ -119,23 +119,25 @@ int LuaVoxelManip::l_write_to_map(lua_State *L)
int LuaVoxelManip::l_get_node_at(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
- GET_ENV_PTR;
+
+ INodeDefManager *ndef = getServer(L)->getNodeDefManager();
LuaVoxelManip *o = checkobject(L, 1);
v3s16 pos = check_v3s16(L, 2);
- pushnode(L, o->vm->getNodeNoExNoEmerge(pos), env->getGameDef()->ndef());
+ pushnode(L, o->vm->getNodeNoExNoEmerge(pos), ndef);
return 1;
}
int LuaVoxelManip::l_set_node_at(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
- GET_ENV_PTR;
+
+ INodeDefManager *ndef = getServer(L)->getNodeDefManager();
LuaVoxelManip *o = checkobject(L, 1);
v3s16 pos = check_v3s16(L, 2);
- MapNode n = readnode(L, 3, env->getGameDef()->ndef());
+ MapNode n = readnode(L, 3, ndef);
o->vm->setNodeNoEmerge(pos, n);
@@ -179,6 +181,7 @@ int LuaVoxelManip::l_calc_lighting(lua_State *L)
v3s16 fpmax = vm->m_area.MaxEdge;
v3s16 pmin = lua_istable(L, 2) ? check_v3s16(L, 2) : fpmin + yblock;
v3s16 pmax = lua_istable(L, 3) ? check_v3s16(L, 3) : fpmax - yblock;
+ bool propagate_shadow = lua_isboolean(L, 4) ? lua_toboolean(L, 4) : true;
sortBoxVerticies(pmin, pmax);
if (!vm->m_area.contains(VoxelArea(pmin, pmax)))
@@ -189,7 +192,7 @@ int LuaVoxelManip::l_calc_lighting(lua_State *L)
mg.ndef = ndef;
mg.water_level = emerge->params.water_level;
- mg.calcLighting(pmin, pmax, fpmin, fpmax);
+ mg.calcLighting(pmin, pmax, fpmin, fpmax, propagate_shadow);
return 0;
}
@@ -313,14 +316,12 @@ int LuaVoxelManip::l_set_param2_data(lua_State *L)
int LuaVoxelManip::l_update_map(lua_State *L)
{
+ GET_ENV_PTR;
+
LuaVoxelManip *o = checkobject(L, 1);
if (o->is_mapgen_vm)
return 0;
- Environment *env = getEnv(L);
- if (!env)
- return 0;
-
Map *map = &(env->getMap());
// TODO: Optimize this by using Mapgen::calcLighting() instead
@@ -359,6 +360,8 @@ int LuaVoxelManip::l_was_modified(lua_State *L)
int LuaVoxelManip::l_get_emerged_area(lua_State *L)
{
+ NO_MAP_LOCK_REQUIRED;
+
LuaVoxelManip *o = checkobject(L, 1);
push_v3s16(L, o->vm->m_area.MinEdge);
@@ -400,11 +403,7 @@ LuaVoxelManip::~LuaVoxelManip()
// Creates an LuaVoxelManip and leaves it on top of stack
int LuaVoxelManip::create_object(lua_State *L)
{
- NO_MAP_LOCK_REQUIRED;
-
- Environment *env = getEnv(L);
- if (!env)
- return 0;
+ GET_ENV_PTR;
Map *map = &(env->getMap());
LuaVoxelManip *o = (lua_istable(L, 1) && lua_istable(L, 2)) ?
diff --git a/src/script/scripting_game.cpp b/src/script/scripting_game.cpp
index 4f0350d41..e313d55f8 100644
--- a/src/script/scripting_game.cpp
+++ b/src/script/scripting_game.cpp
@@ -39,6 +39,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "lua_api/l_util.h"
#include "lua_api/l_vmanip.h"
#include "lua_api/l_settings.h"
+#include "lua_api/l_http.h"
extern "C" {
#include "lualib.h"
@@ -89,6 +90,7 @@ void GameScripting::InitializeModApi(lua_State *L, int top)
ModApiRollback::Initialize(L, top);
ModApiServer::Initialize(L, top);
ModApiUtil::Initialize(L, top);
+ ModApiHttp::Initialize(L, top);
// Register reference classes (userdata)
InvRef::Register(L);
@@ -98,6 +100,7 @@ void GameScripting::InitializeModApi(lua_State *L, int top)
LuaPerlinNoiseMap::Register(L);
LuaPseudoRandom::Register(L);
LuaPcgRandom::Register(L);
+ LuaSecureRandom::Register(L);
LuaVoxelManip::Register(L);
NodeMetaRef::Register(L);
NodeTimerRef::Register(L);
diff --git a/src/script/scripting_mainmenu.cpp b/src/script/scripting_mainmenu.cpp
index c74c18edc..b1e50c94b 100644
--- a/src/script/scripting_mainmenu.cpp
+++ b/src/script/scripting_mainmenu.cpp
@@ -78,7 +78,7 @@ void MainMenuScripting::initializeModApi(lua_State *L, int top)
/******************************************************************************/
void MainMenuScripting::step() {
- asyncEngine.step(getStack(), m_errorhandler);
+ asyncEngine.step(getStack());
}
/******************************************************************************/
diff --git a/src/serialization.cpp b/src/serialization.cpp
index c0fbe10e2..79f66fcae 100644
--- a/src/serialization.cpp
+++ b/src/serialization.cpp
@@ -133,7 +133,8 @@ void decompressZlib(std::istream &is, std::ostream &os)
if(z.avail_in == 0)
{
z.next_in = (Bytef*)input_buffer;
- input_buffer_len = is.readsome(input_buffer, bufsize);
+ is.read(input_buffer, bufsize);
+ input_buffer_len = is.gcount();
z.avail_in = input_buffer_len;
//dstream<<"read fail="<<is.fail()<<" bad="<<is.bad()<<std::endl;
}
@@ -166,6 +167,7 @@ void decompressZlib(std::istream &is, std::ostream &os)
//dstream<<"z.avail_in="<<z.avail_in<<std::endl;
//dstream<<"fail="<<is.fail()<<" bad="<<is.bad()<<std::endl;
// Unget all the data that inflate didn't take
+ is.clear(); // Just in case EOF is set
for(u32 i=0; i < z.avail_in; i++)
{
is.unget();
diff --git a/src/serialization.h b/src/serialization.h
index ab6fe0f79..01d37d363 100644
--- a/src/serialization.h
+++ b/src/serialization.h
@@ -30,11 +30,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
--------------------------------
For map data (blocks, nodes, sectors).
-
+
NOTE: The goal is to increment this so that saved maps will be
loadable by any version. Other compatibility is not
maintained.
-
+
0: original networked test with 1-byte nodes
1: update with 2-byte nodes
2: lighting is transmitted in param
@@ -70,14 +70,14 @@ with this program; if not, write to the Free Software Foundation, Inc.,
// Saved on disk version
#define SER_FMT_VER_HIGHEST_WRITE 25
// Lowest supported serialization version
-#define SER_FMT_VER_LOWEST 0
-// Lowest client supported serialization version
+#define SER_FMT_VER_LOWEST_READ 0
+// Lowest serialization version for writing
// Can't do < 24 anymore; we have 16-bit dynamically allocated node IDs
// in memory; conversion just won't work in this direction.
-#define SER_FMT_CLIENT_VER_LOWEST 24
+#define SER_FMT_VER_LOWEST_WRITE 24
inline bool ser_ver_supported(s32 v) {
- return v >= SER_FMT_VER_LOWEST && v <= SER_FMT_VER_HIGHEST_READ;
+ return v >= SER_FMT_VER_LOWEST_READ && v <= SER_FMT_VER_HIGHEST_READ;
}
/*
diff --git a/src/server.cpp b/src/server.cpp
index dc7b101a6..a3b686c25 100644
--- a/src/server.cpp
+++ b/src/server.cpp
@@ -26,7 +26,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "ban.h"
#include "environment.h"
#include "map.h"
-#include "jthread/jmutexautolock.h"
+#include "threading/mutex_auto_lock.h"
#include "constants.h"
#include "voxel.h"
#include "config.h"
@@ -71,35 +71,29 @@ public:
{}
};
-class ServerThread : public JThread
+class ServerThread : public Thread
{
- Server *m_server;
-
public:
ServerThread(Server *server):
- JThread(),
+ Thread("Server"),
m_server(server)
- {
- }
+ {}
+
+ void *run();
- void * Thread();
+private:
+ Server *m_server;
};
-void *ServerThread::Thread()
+void *ServerThread::run()
{
- log_register_thread("ServerThread");
-
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
BEGIN_DEBUG_EXCEPTION_HANDLER
m_server->AsyncRunStep(true);
- ThreadStarted();
-
- porting::setThreadName("ServerThread");
-
- while (!StopRequested()) {
+ while (!stopRequested()) {
try {
//TimeTaker timer("AsyncRunStep() + Receive()");
@@ -118,7 +112,7 @@ void *ServerThread::Thread()
}
}
- END_DEBUG_EXCEPTION_HANDLER(errorstream)
+ END_DEBUG_EXCEPTION_HANDLER
return NULL;
}
@@ -154,7 +148,8 @@ Server::Server(
const std::string &path_world,
const SubgameSpec &gamespec,
bool simple_singleplayer_mode,
- bool ipv6
+ bool ipv6,
+ ChatInterface *iface
):
m_path_world(path_world),
m_gamespec(gamespec),
@@ -181,6 +176,7 @@ Server::Server(
m_clients(&m_con),
m_shutdown_requested(false),
m_shutdown_ask_reconnect(false),
+ m_admin_chat(iface),
m_ignore_map_edit_events(false),
m_ignore_map_edit_events_peer_id(0),
m_next_sound_id(0)
@@ -267,8 +263,8 @@ Server::Server(
errorstream << std::endl;
}
- // Lock environment
- JMutexAutoLock envlock(m_env_mutex);
+ //lock environment
+ MutexAutoLock envlock(m_env_mutex);
// Load mapgen params from Settings
m_emerge->loadMapgenParams();
@@ -282,41 +278,30 @@ Server::Server(
m_script = new GameScripting(this);
std::string script_path = getBuiltinLuaPath() + DIR_DELIM "init.lua";
- std::string error_msg;
- if (!m_script->loadMod(script_path, BUILTIN_MOD_NAME, &error_msg))
- throw ModError("Failed to load and run " + script_path
- + "\nError from Lua:\n" + error_msg);
+ m_script->loadMod(script_path, BUILTIN_MOD_NAME);
// Print mods
infostream << "Server: Loading mods: ";
for(std::vector<ModSpec>::iterator i = m_mods.begin();
- i != m_mods.end(); i++) {
+ i != m_mods.end(); ++i) {
const ModSpec &mod = *i;
infostream << mod.name << " ";
}
infostream << std::endl;
// Load and run "mod" scripts
- for (std::vector<ModSpec>::iterator i = m_mods.begin();
- i != m_mods.end(); i++) {
- const ModSpec &mod = *i;
+ for (std::vector<ModSpec>::iterator it = m_mods.begin();
+ it != m_mods.end(); ++it) {
+ const ModSpec &mod = *it;
if (!string_allowed(mod.name, MODNAME_ALLOWED_CHARS)) {
- std::ostringstream err;
- err << "Error loading mod \"" << mod.name
- << "\": mod_name does not follow naming conventions: "
- << "Only chararacters [a-z0-9_] are allowed." << std::endl;
- errorstream << err.str().c_str();
- throw ModError(err.str());
+ throw ModError("Error loading mod \"" + mod.name +
+ "\": Mod name does not follow naming conventions: "
+ "Only chararacters [a-z0-9_] are allowed.");
}
- std::string script_path = mod.path + DIR_DELIM "init.lua";
+ std::string script_path = mod.path + DIR_DELIM + "init.lua";
infostream << " [" << padStringRight(mod.name, 12) << "] [\""
<< script_path << "\"]" << std::endl;
- if (!m_script->loadMod(script_path, mod.name, &error_msg)) {
- errorstream << "Server: Failed to load and run "
- << script_path << std::endl;
- throw ModError("Failed to load and run " + script_path
- + "\nError from Lua:\n" + error_msg);
- }
+ m_script->loadMod(script_path, mod.name);
}
// Read Textures and calculate sha1 sums
@@ -335,6 +320,9 @@ Server::Server(
// Perform pending node name resolutions
m_nodedef->runNodeResolveCallbacks();
+ // unmap node names for connected nodeboxes
+ m_nodedef->mapNodeboxConnections();
+
// init the recipe hashes to speed up crafting
m_craftdef->initHashes(this);
@@ -359,10 +347,11 @@ Server::Server(
servermap->addEventReceiver(this);
// If file exists, load environment metadata
- if(fs::PathExists(m_path_world + DIR_DELIM "env_meta.txt"))
- {
- infostream<<"Server: Loading environment metadata"<<std::endl;
+ if (fs::PathExists(m_path_world + DIR_DELIM "env_meta.txt")) {
+ infostream << "Server: Loading environment metadata" << std::endl;
m_env->loadMeta();
+ } else {
+ m_env->loadDefaultMeta();
}
// Add some test ActiveBlockModifiers to environment
@@ -379,7 +368,7 @@ Server::~Server()
SendChatMessage(PEER_ID_INEXISTENT, L"*** Server shutting down");
{
- JMutexAutoLock envlock(m_env_mutex);
+ MutexAutoLock envlock(m_env_mutex);
// Execute script shutdown hooks
m_script->on_shutdown();
@@ -432,14 +421,14 @@ Server::~Server()
// Delete detached inventories
for (std::map<std::string, Inventory*>::iterator
i = m_detached_inventories.begin();
- i != m_detached_inventories.end(); i++) {
+ i != m_detached_inventories.end(); ++i) {
delete i->second;
}
}
void Server::start(Address bind_addr)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
m_bind_addr = bind_addr;
@@ -447,14 +436,14 @@ void Server::start(Address bind_addr)
<< bind_addr.serializeString() <<"..."<<std::endl;
// Stop thread if already running
- m_thread->Stop();
+ m_thread->stop();
// Initialize connection
m_con.SetTimeoutMs(30);
m_con.Serve(bind_addr);
// Start thread
- m_thread->Start();
+ m_thread->start();
// ASCII art for the win!
actionstream
@@ -472,14 +461,14 @@ void Server::start(Address bind_addr)
void Server::stop()
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
infostream<<"Server: Stopping and waiting threads"<<std::endl;
// Stop threads (set run=false first so both start stopping)
- m_thread->Stop();
+ m_thread->stop();
//m_emergethread.setRun(false);
- m_thread->Wait();
+ m_thread->wait();
//m_emergethread.stop();
infostream<<"Server: Threads stopped"<<std::endl;
@@ -487,41 +476,35 @@ void Server::stop()
void Server::step(float dtime)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
// Limit a bit
- if(dtime > 2.0)
+ if (dtime > 2.0)
dtime = 2.0;
{
- JMutexAutoLock lock(m_step_dtime_mutex);
+ MutexAutoLock lock(m_step_dtime_mutex);
m_step_dtime += dtime;
}
// Throw if fatal error occurred in thread
std::string async_err = m_async_fatal_error.get();
- if(async_err != "") {
- if (m_simple_singleplayer_mode) {
- throw ServerError(async_err);
- }
- else {
+ if (!async_err.empty()) {
+ if (!m_simple_singleplayer_mode) {
m_env->kickAllPlayers(SERVER_ACCESSDENIED_CRASH,
g_settings->get("kick_msg_crash"),
g_settings->getBool("ask_reconnect_on_crash"));
- errorstream << "UNRECOVERABLE error occurred. Stopping server. "
- << "Please fix the following error:" << std::endl
- << async_err << std::endl;
- FATAL_ERROR(async_err.c_str());
}
+ throw ServerError(async_err);
}
}
void Server::AsyncRunStep(bool initial_step)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
g_profiler->add("Server::AsyncRunStep (num)", 1);
float dtime;
{
- JMutexAutoLock lock1(m_step_dtime_mutex);
+ MutexAutoLock lock1(m_step_dtime_mutex);
dtime = m_step_dtime;
}
@@ -539,7 +522,7 @@ void Server::AsyncRunStep(bool initial_step)
//infostream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
{
- JMutexAutoLock lock1(m_step_dtime_mutex);
+ MutexAutoLock lock1(m_step_dtime_mutex);
m_step_dtime -= dtime;
}
@@ -570,7 +553,7 @@ void Server::AsyncRunStep(bool initial_step)
}
{
- JMutexAutoLock lock(m_env_mutex);
+ MutexAutoLock lock(m_env_mutex);
// Figure out and report maximum lag to environment
float max_lag = m_env->getMaxLagEstimate();
max_lag *= 0.9998; // Decrease slowly (about half per 5 minutes)
@@ -590,12 +573,28 @@ void Server::AsyncRunStep(bool initial_step)
static const float map_timer_and_unload_dtime = 2.92;
if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
{
- JMutexAutoLock lock(m_env_mutex);
+ MutexAutoLock lock(m_env_mutex);
// Run Map's timers and unload unused data
ScopeProfiler sp(g_profiler, "Server: map timer and unload");
m_env->getMap().timerUpdate(map_timer_and_unload_dtime,
g_settings->getFloat("server_unload_unused_data_timeout"),
- (u32)-1);
+ U32_MAX);
+ }
+
+ /*
+ Listen to the admin chat, if available
+ */
+ if (m_admin_chat) {
+ if (!m_admin_chat->command_queue.empty()) {
+ MutexAutoLock lock(m_env_mutex);
+ while (!m_admin_chat->command_queue.empty()) {
+ ChatEvent *evt = m_admin_chat->command_queue.pop_frontNoEx();
+ handleChatInterfaceEvent(evt);
+ delete evt;
+ }
+ }
+ m_admin_chat->outgoing_queue.push_back(
+ new ChatEventTimeInfo(m_env->getGameTime(), m_env->getTimeOfDay()));
}
/*
@@ -608,7 +607,7 @@ void Server::AsyncRunStep(bool initial_step)
{
m_liquid_transform_timer -= m_liquid_transform_every;
- JMutexAutoLock lock(m_env_mutex);
+ MutexAutoLock lock(m_env_mutex);
ScopeProfiler sp(g_profiler, "Server: liquid transform");
@@ -669,27 +668,28 @@ void Server::AsyncRunStep(bool initial_step)
*/
{
//infostream<<"Server: Checking added and deleted active objects"<<std::endl;
- JMutexAutoLock envlock(m_env_mutex);
+ MutexAutoLock envlock(m_env_mutex);
- m_clients.Lock();
+ m_clients.lock();
std::map<u16, RemoteClient*> clients = m_clients.getClientList();
ScopeProfiler sp(g_profiler, "Server: checking added and deleted objs");
// Radius inside which objects are active
- s16 radius = g_settings->getS16("active_object_send_range_blocks");
- s16 player_radius = g_settings->getS16("player_transfer_distance");
-
- if (player_radius == 0 && g_settings->exists("unlimited_player_transfer_distance") &&
- !g_settings->getBool("unlimited_player_transfer_distance"))
+ static const s16 radius =
+ g_settings->getS16("active_object_send_range_blocks") * MAP_BLOCKSIZE;
+
+ // Radius inside which players are active
+ static const bool is_transfer_limited =
+ g_settings->exists("unlimited_player_transfer_distance") &&
+ !g_settings->getBool("unlimited_player_transfer_distance");
+ static const s16 player_transfer_dist = g_settings->getS16("player_transfer_distance") * MAP_BLOCKSIZE;
+ s16 player_radius = player_transfer_dist;
+ if (player_radius == 0 && is_transfer_limited)
player_radius = radius;
- radius *= MAP_BLOCKSIZE;
- player_radius *= MAP_BLOCKSIZE;
-
- for(std::map<u16, RemoteClient*>::iterator
+ for (std::map<u16, RemoteClient*>::iterator
i = clients.begin();
- i != clients.end(); ++i)
- {
+ i != clients.end(); ++i) {
RemoteClient *client = i->second;
// If definitions and textures have not been sent, don't
@@ -698,27 +698,23 @@ void Server::AsyncRunStep(bool initial_step)
continue;
Player *player = m_env->getPlayer(client->peer_id);
- if(player==NULL)
- {
+ if(player == NULL) {
// This can happen if the client timeouts somehow
- /*infostream<<"WARNING: "<<__FUNCTION_NAME<<": Client "
+ /*warningstream<<FUNCTION_NAME<<": Client "
<<client->peer_id
<<" has no associated player"<<std::endl;*/
continue;
}
- v3s16 pos = floatToInt(player->getPosition(), BS);
- std::set<u16> removed_objects;
- std::set<u16> added_objects;
- m_env->getRemovedActiveObjects(pos, radius, player_radius,
+ std::queue<u16> removed_objects;
+ std::queue<u16> added_objects;
+ m_env->getRemovedActiveObjects(player, radius, player_radius,
client->m_known_objects, removed_objects);
- m_env->getAddedActiveObjects(pos, radius, player_radius,
+ m_env->getAddedActiveObjects(player, radius, player_radius,
client->m_known_objects, added_objects);
// Ignore if nothing happened
- if(removed_objects.empty() && added_objects.empty())
- {
- //infostream<<"active objects: none changed"<<std::endl;
+ if (removed_objects.empty() && added_objects.empty()) {
continue;
}
@@ -729,12 +725,9 @@ void Server::AsyncRunStep(bool initial_step)
// Handle removed objects
writeU16((u8*)buf, removed_objects.size());
data_buffer.append(buf, 2);
- for(std::set<u16>::iterator
- i = removed_objects.begin();
- i != removed_objects.end(); ++i)
- {
+ while (!removed_objects.empty()) {
// Get object
- u16 id = *i;
+ u16 id = removed_objects.front();
ServerActiveObject* obj = m_env->getActiveObject(id);
// Add to data buffer for sending
@@ -746,23 +739,21 @@ void Server::AsyncRunStep(bool initial_step)
if(obj && obj->m_known_by_count > 0)
obj->m_known_by_count--;
+ removed_objects.pop();
}
// Handle added objects
writeU16((u8*)buf, added_objects.size());
data_buffer.append(buf, 2);
- for(std::set<u16>::iterator
- i = added_objects.begin();
- i != added_objects.end(); ++i)
- {
+ while (!added_objects.empty()) {
// Get object
- u16 id = *i;
+ u16 id = added_objects.front();
ServerActiveObject* obj = m_env->getActiveObject(id);
// Get object type
u8 type = ACTIVEOBJECT_TYPE_INVALID;
if(obj == NULL)
- infostream<<"WARNING: "<<__FUNCTION_NAME
+ warningstream<<FUNCTION_NAME
<<": NULL object"<<std::endl;
else
type = obj->getSendType();
@@ -784,6 +775,8 @@ void Server::AsyncRunStep(bool initial_step)
if(obj)
obj->m_known_by_count++;
+
+ added_objects.pop();
}
u32 pktSize = SendActiveObjectRemoveAdd(client->peer_id, data_buffer);
@@ -792,14 +785,14 @@ void Server::AsyncRunStep(bool initial_step)
<< added_objects.size() << " added, "
<< "packet size is " << pktSize << std::endl;
}
- m_clients.Unlock();
+ m_clients.unlock();
}
/*
Send object messages
*/
{
- JMutexAutoLock envlock(m_env_mutex);
+ MutexAutoLock envlock(m_env_mutex);
ScopeProfiler sp(g_profiler, "Server: sending object messages");
// Key = object id
@@ -825,7 +818,7 @@ void Server::AsyncRunStep(bool initial_step)
message_list->push_back(aom);
}
- m_clients.Lock();
+ m_clients.lock();
std::map<u16, RemoteClient*> clients = m_clients.getClientList();
// Route data to every client
for (std::map<u16, RemoteClient*>::iterator
@@ -876,7 +869,7 @@ void Server::AsyncRunStep(bool initial_step)
SendActiveObjectMessages(client->peer_id, unreliable_data, false);
}
}
- m_clients.Unlock();
+ m_clients.unlock();
// Clear buffered_messages
for(std::map<u16, std::vector<ActiveObjectMessage>* >::iterator
@@ -891,7 +884,7 @@ void Server::AsyncRunStep(bool initial_step)
*/
{
// We will be accessing the environment
- JMutexAutoLock lock(m_env_mutex);
+ MutexAutoLock lock(m_env_mutex);
// Don't send too many at a time
//u32 count = 0;
@@ -945,7 +938,7 @@ void Server::AsyncRunStep(bool initial_step)
break;
default:
prof.add("unknown", 1);
- infostream << "WARNING: Server: Unknown MapEditEvent "
+ warningstream << "Server: Unknown MapEditEvent "
<< ((u32)event->type) << std::endl;
break;
}
@@ -997,8 +990,7 @@ void Server::AsyncRunStep(bool initial_step)
{
float &counter = m_emergethread_trigger_timer;
counter += dtime;
- if(counter >= 2.0)
- {
+ if (counter >= 2.0) {
counter = 0.0;
m_emerge->startThreads();
@@ -1009,10 +1001,11 @@ void Server::AsyncRunStep(bool initial_step)
{
float &counter = m_savemap_timer;
counter += dtime;
- if(counter >= g_settings->getFloat("server_map_save_interval"))
- {
+ static const float save_interval =
+ g_settings->getFloat("server_map_save_interval");
+ if (counter >= save_interval) {
counter = 0.0;
- JMutexAutoLock lock(m_env_mutex);
+ MutexAutoLock lock(m_env_mutex);
ScopeProfiler sp(g_profiler, "Server: saving stuff");
@@ -1035,7 +1028,7 @@ void Server::AsyncRunStep(bool initial_step)
void Server::Receive()
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
SharedBuffer<u8> data;
u16 peer_id;
try {
@@ -1068,7 +1061,7 @@ PlayerSAO* Server::StageTwoClientInit(u16 peer_id)
{
std::string playername = "";
PlayerSAO *playersao = NULL;
- m_clients.Lock();
+ m_clients.lock();
try {
RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_InitDone);
if (client != NULL) {
@@ -1076,10 +1069,10 @@ PlayerSAO* Server::StageTwoClientInit(u16 peer_id)
playersao = emergePlayer(playername.c_str(), peer_id, client->net_proto_version);
}
} catch (std::exception &e) {
- m_clients.Unlock();
+ m_clients.unlock();
throw;
}
- m_clients.Unlock();
+ m_clients.unlock();
RemotePlayer *player =
static_cast<RemotePlayer*>(m_env->getPlayer(playername.c_str()));
@@ -1131,16 +1124,19 @@ PlayerSAO* Server::StageTwoClientInit(u16 peer_id)
// Send information about joining in chat
{
- std::wstring name = L"unknown";
+ std::string name = "unknown";
Player *player = m_env->getPlayer(peer_id);
if(player != NULL)
- name = narrow_to_wide(player->getName());
+ name = player->getName();
std::wstring message;
message += L"*** ";
- message += name;
+ message += narrow_to_wide(name);
message += L" joined the game.";
SendChatMessage(PEER_ID_INEXISTENT,message);
+ if (m_admin_chat)
+ m_admin_chat->outgoing_queue.push_back(
+ new ChatEventNick(CET_NICK_ADD, name));
}
}
Address addr = getPeerAddress(player->peer_id);
@@ -1155,7 +1151,7 @@ PlayerSAO* Server::StageTwoClientInit(u16 peer_id)
actionstream<<player->getName() <<" joins game. List of players: ";
for (std::vector<std::string>::iterator i = names.begin();
- i != names.end(); i++) {
+ i != names.end(); ++i) {
actionstream << *i << " ";
}
@@ -1172,9 +1168,9 @@ inline void Server::handleCommand(NetworkPacket* pkt)
void Server::ProcessData(NetworkPacket *pkt)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
// Environment is locked first.
- JMutexAutoLock envlock(m_env_mutex);
+ MutexAutoLock envlock(m_env_mutex);
ScopeProfiler sp(g_profiler, "Server::ProcessData");
u32 peer_id = pkt->getPeerId();
@@ -1356,19 +1352,19 @@ void Server::setInventoryModified(const InventoryLocation &loc, bool playerSend)
void Server::SetBlocksNotSent(std::map<v3s16, MapBlock *>& block)
{
std::vector<u16> clients = m_clients.getClientIDs();
- m_clients.Lock();
+ m_clients.lock();
// Set the modified blocks unsent for all the clients
for (std::vector<u16>::iterator i = clients.begin();
i != clients.end(); ++i) {
if (RemoteClient *client = m_clients.lockedGetClientNoEx(*i))
client->SetBlocksNotSent(block);
}
- m_clients.Unlock();
+ m_clients.unlock();
}
void Server::peerAdded(con::Peer *peer)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
verbosestream<<"Server::peerAdded(): peer->id="
<<peer->id<<std::endl;
@@ -1381,7 +1377,7 @@ void Server::peerAdded(con::Peer *peer)
void Server::deletingPeer(con::Peer *peer, bool timeout)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
verbosestream<<"Server::deletingPeer(): peer->id="
<<peer->id<<", timeout="<<timeout<<std::endl;
@@ -1413,11 +1409,11 @@ bool Server::getClientInfo(
)
{
*state = m_clients.getClientState(peer_id);
- m_clients.Lock();
+ m_clients.lock();
RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_Invalid);
if (client == NULL) {
- m_clients.Unlock();
+ m_clients.unlock();
return false;
}
@@ -1430,7 +1426,7 @@ bool Server::getClientInfo(
*patch = client->getPatch();
*vers_string = client->getPatch();
- m_clients.Unlock();
+ m_clients.unlock();
return true;
}
@@ -1463,6 +1459,16 @@ void Server::handlePeerChanges()
}
}
+void Server::printToConsoleOnly(const std::string &text)
+{
+ if (m_admin_chat) {
+ m_admin_chat->outgoing_queue.push_back(
+ new ChatEventChat("", utf8_to_wide(text)));
+ } else {
+ std::cout << text << std::endl;
+ }
+}
+
void Server::Send(NetworkPacket* pkt)
{
m_clients.send(pkt->getPeerId(),
@@ -1473,7 +1479,7 @@ void Server::Send(NetworkPacket* pkt)
void Server::SendMovement(u16 peer_id)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
std::ostringstream os(std::ios_base::binary);
NetworkPacket pkt(TOCLIENT_MOVEMENT, 12 * sizeof(float), peer_id);
@@ -1510,7 +1516,7 @@ void Server::SendPlayerHPOrDie(PlayerSAO *playersao)
void Server::SendHP(u16 peer_id, u8 hp)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
NetworkPacket pkt(TOCLIENT_HP, 1, peer_id);
pkt << hp;
@@ -1519,7 +1525,7 @@ void Server::SendHP(u16 peer_id, u8 hp)
void Server::SendBreath(u16 peer_id, u16 breath)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
NetworkPacket pkt(TOCLIENT_BREATH, 2, peer_id);
pkt << (u16) breath;
@@ -1543,7 +1549,7 @@ void Server::SendAccessDenied(u16 peer_id, AccessDeniedCode reason,
void Server::SendAccessDenied_Legacy(u16 peer_id,const std::wstring &reason)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
NetworkPacket pkt(TOCLIENT_ACCESS_DENIED_LEGACY, 0, peer_id);
pkt << reason;
@@ -1553,7 +1559,7 @@ void Server::SendAccessDenied_Legacy(u16 peer_id,const std::wstring &reason)
void Server::SendDeathscreen(u16 peer_id,bool set_camera_point_target,
v3f camera_point_target)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
NetworkPacket pkt(TOCLIENT_DEATHSCREEN, 1 + sizeof(v3f), peer_id);
pkt << set_camera_point_target << camera_point_target;
@@ -1563,7 +1569,7 @@ void Server::SendDeathscreen(u16 peer_id,bool set_camera_point_target,
void Server::SendItemDef(u16 peer_id,
IItemDefManager *itemdef, u16 protocol_version)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
NetworkPacket pkt(TOCLIENT_ITEMDEF, 0, peer_id);
@@ -1588,7 +1594,7 @@ void Server::SendItemDef(u16 peer_id,
void Server::SendNodeDef(u16 peer_id,
INodeDefManager *nodedef, u16 protocol_version)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
NetworkPacket pkt(TOCLIENT_NODEDEF, 0, peer_id);
@@ -1617,7 +1623,7 @@ void Server::SendNodeDef(u16 peer_id,
void Server::SendInventory(PlayerSAO* playerSAO)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
UpdateCrafting(playerSAO->getPlayer());
@@ -1638,7 +1644,7 @@ void Server::SendInventory(PlayerSAO* playerSAO)
void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
NetworkPacket pkt(TOCLIENT_CHAT_MESSAGE, 0, peer_id);
pkt << message;
@@ -1654,7 +1660,7 @@ void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
void Server::SendShowFormspecMessage(u16 peer_id, const std::string &formspec,
const std::string &formname)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
NetworkPacket pkt(TOCLIENT_SHOW_FORMSPEC, 0 , peer_id);
@@ -1669,7 +1675,7 @@ void Server::SendSpawnParticle(u16 peer_id, v3f pos, v3f velocity, v3f accelerat
float expirationtime, float size, bool collisiondetection,
bool vertical, std::string texture)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
NetworkPacket pkt(TOCLIENT_SPAWN_PARTICLE, 0, peer_id);
@@ -1691,7 +1697,7 @@ void Server::SendAddParticleSpawner(u16 peer_id, u16 amount, float spawntime, v3
v3f minvel, v3f maxvel, v3f minacc, v3f maxacc, float minexptime, float maxexptime,
float minsize, float maxsize, bool collisiondetection, bool vertical, std::string texture, u32 id)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
NetworkPacket pkt(TOCLIENT_ADD_PARTICLESPAWNER, 0, peer_id);
@@ -1713,7 +1719,7 @@ void Server::SendAddParticleSpawner(u16 peer_id, u16 amount, float spawntime, v3
void Server::SendDeleteParticleSpawner(u16 peer_id, u32 id)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
NetworkPacket pkt(TOCLIENT_DELETE_PARTICLESPAWNER_LEGACY, 2, peer_id);
@@ -1823,7 +1829,7 @@ void Server::SendOverrideDayNightRatio(u16 peer_id, bool do_override,
void Server::SendTimeOfDay(u16 peer_id, u16 time, f32 time_speed)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
NetworkPacket pkt(TOCLIENT_TIME_OF_DAY, 0, peer_id);
pkt << time << time_speed;
@@ -1838,9 +1844,9 @@ void Server::SendTimeOfDay(u16 peer_id, u16 time, f32 time_speed)
void Server::SendPlayerHP(u16 peer_id)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
PlayerSAO *playersao = getPlayerSAO(peer_id);
- // In some rare case, if the player is disconnected
+ // In some rare case if the player is disconnected
// while Lua call l_punch, for example, this can be NULL
if (!playersao)
return;
@@ -1856,7 +1862,7 @@ void Server::SendPlayerHP(u16 peer_id)
void Server::SendPlayerBreath(u16 peer_id)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
PlayerSAO *playersao = getPlayerSAO(peer_id);
assert(playersao);
@@ -1866,7 +1872,7 @@ void Server::SendPlayerBreath(u16 peer_id)
void Server::SendMovePlayer(u16 peer_id)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
Player *player = m_env->getPlayer(peer_id);
assert(player);
@@ -1918,7 +1924,7 @@ void Server::SendPlayerPrivileges(u16 peer_id)
pkt << (u16) privs.size();
for(std::set<std::string>::const_iterator i = privs.begin();
- i != privs.end(); i++) {
+ i != privs.end(); ++i) {
pkt << (*i);
}
@@ -2018,7 +2024,7 @@ s32 Server::playSound(const SimpleSoundSpec &spec,
<< (u8) params.type << pos << params.object << params.loop;
for(std::vector<u16>::iterator i = dst_clients.begin();
- i != dst_clients.end(); i++) {
+ i != dst_clients.end(); ++i) {
psound.clients.insert(*i);
m_clients.send(*i, 0, &pkt, true);
}
@@ -2037,7 +2043,7 @@ void Server::stopSound(s32 handle)
pkt << handle;
for(std::set<u16>::iterator i = psound.clients.begin();
- i != psound.clients.end(); i++) {
+ i != psound.clients.end(); ++i) {
// Send as reliable
m_clients.send(*i, 0, &pkt, true);
}
@@ -2098,7 +2104,7 @@ void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
}
NetworkPacket pkt(TOCLIENT_ADDNODE, 6 + 2 + 1 + 1 + 1);
- m_clients.Lock();
+ m_clients.lock();
RemoteClient* client = m_clients.lockedGetClientNoEx(*i);
if (client != 0) {
pkt << p << n.param0 << n.param1 << n.param2
@@ -2108,11 +2114,11 @@ void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
if (client->net_proto_version <= 21) {
// Old clients always clear metadata; fix it
// by sending the full block again.
- client->SetBlockNotSent(p);
+ client->SetBlockNotSent(getNodeBlockPos(p));
}
}
}
- m_clients.Unlock();
+ m_clients.unlock();
// Send as reliable
if (pkt.getSize() > 0)
@@ -2123,18 +2129,18 @@ void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
void Server::setBlockNotSent(v3s16 p)
{
std::vector<u16> clients = m_clients.getClientIDs();
- m_clients.Lock();
+ m_clients.lock();
for(std::vector<u16>::iterator i = clients.begin();
i != clients.end(); ++i) {
RemoteClient *client = m_clients.lockedGetClientNoEx(*i);
client->SetBlockNotSent(p);
}
- m_clients.Unlock();
+ m_clients.unlock();
}
void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver, u16 net_proto_version)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
v3s16 p = block->getPos();
@@ -2156,9 +2162,9 @@ void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver, u16 net_proto
void Server::SendBlocks(float dtime)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
- JMutexAutoLock envlock(m_env_mutex);
+ MutexAutoLock envlock(m_env_mutex);
//TODO check if one big lock could be faster then multiple small ones
ScopeProfiler sp(g_profiler, "Server: sel and send blocks to clients");
@@ -2172,7 +2178,7 @@ void Server::SendBlocks(float dtime)
std::vector<u16> clients = m_clients.getClientIDs();
- m_clients.Lock();
+ m_clients.lock();
for(std::vector<u16>::iterator i = clients.begin();
i != clients.end(); ++i) {
RemoteClient *client = m_clients.lockedGetClientNoEx(*i, CS_Active);
@@ -2183,7 +2189,7 @@ void Server::SendBlocks(float dtime)
total_sending += client->SendingCount();
client->GetNextBlocks(m_env,m_emerge, dtime, queue);
}
- m_clients.Unlock();
+ m_clients.unlock();
}
// Sort.
@@ -2191,7 +2197,7 @@ void Server::SendBlocks(float dtime)
// Lowest is most important.
std::sort(queue.begin(), queue.end());
- m_clients.Lock();
+ m_clients.lock();
for(u32 i=0; i<queue.size(); i++)
{
//TODO: Calculate limit dynamically
@@ -2221,19 +2227,19 @@ void Server::SendBlocks(float dtime)
client->SentBlock(q.pos);
total_sending++;
}
- m_clients.Unlock();
+ m_clients.unlock();
}
void Server::fillMediaCache()
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
infostream<<"Server: Calculating media file checksums"<<std::endl;
// Collect all media file paths
std::vector<std::string> paths;
for(std::vector<ModSpec>::iterator i = m_mods.begin();
- i != m_mods.end(); i++) {
+ i != m_mods.end(); ++i) {
const ModSpec &mod = *i;
paths.push_back(mod.path + DIR_DELIM + "textures");
paths.push_back(mod.path + DIR_DELIM + "sounds");
@@ -2244,7 +2250,7 @@ void Server::fillMediaCache()
// Collect media file information from paths into cache
for(std::vector<std::string>::iterator i = paths.begin();
- i != paths.end(); i++) {
+ i != paths.end(); ++i) {
std::string mediapath = *i;
std::vector<fs::DirListNode> dirlist = fs::GetDirListing(mediapath);
for (u32 j = 0; j < dirlist.size(); j++) {
@@ -2322,7 +2328,7 @@ void Server::fillMediaCache()
void Server::sendMediaAnnouncement(u16 peer_id)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
verbosestream << "Server: Announcing files to id(" << peer_id << ")"
<< std::endl;
@@ -2359,7 +2365,7 @@ struct SendableMedia
void Server::sendRequestedMedia(u16 peer_id,
const std::vector<std::string> &tosend)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
verbosestream<<"Server::sendRequestedMedia(): "
<<"Sending files to client"<<std::endl;
@@ -2466,7 +2472,7 @@ void Server::sendRequestedMedia(u16 peer_id,
void Server::sendDetachedInventory(const std::string &name, u16 peer_id)
{
if(m_detached_inventories.count(name) == 0) {
- errorstream<<__FUNCTION_NAME<<": \""<<name<<"\" not found"<<std::endl;
+ errorstream<<FUNCTION_NAME<<": \""<<name<<"\" not found"<<std::endl;
return;
}
Inventory *inv = m_detached_inventories[name];
@@ -2491,11 +2497,11 @@ void Server::sendDetachedInventory(const std::string &name, u16 peer_id)
void Server::sendDetachedInventories(u16 peer_id)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
for(std::map<std::string, Inventory*>::iterator
i = m_detached_inventories.begin();
- i != m_detached_inventories.end(); i++) {
+ i != m_detached_inventories.end(); ++i) {
const std::string &name = i->first;
//Inventory *inv = i->second;
sendDetachedInventory(name, peer_id);
@@ -2508,10 +2514,12 @@ void Server::sendDetachedInventories(u16 peer_id)
void Server::DiePlayer(u16 peer_id)
{
- DSTACK(__FUNCTION_NAME);
-
+ DSTACK(FUNCTION_NAME);
PlayerSAO *playersao = getPlayerSAO(peer_id);
- assert(playersao);
+ // In some rare cases this can be NULL -- if the player is disconnected
+ // when a Lua function modifies l_punch, for example
+ if (!playersao)
+ return;
infostream << "Server::DiePlayer(): Player "
<< playersao->getPlayer()->getName()
@@ -2528,7 +2536,7 @@ void Server::DiePlayer(u16 peer_id)
void Server::RespawnPlayer(u16 peer_id)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
PlayerSAO *playersao = getPlayerSAO(peer_id);
assert(playersao);
@@ -2554,7 +2562,7 @@ void Server::RespawnPlayer(u16 peer_id)
void Server::DenySudoAccess(u16 peer_id)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
NetworkPacket pkt(TOCLIENT_DENY_SUDO_MODE, 0, peer_id);
Send(&pkt);
@@ -2565,7 +2573,7 @@ void Server::DenyAccessVerCompliant(u16 peer_id, u16 proto_ver, AccessDeniedCode
const std::string &str_reason, bool reconnect)
{
if (proto_ver >= 25) {
- SendAccessDenied(peer_id, reason, str_reason);
+ SendAccessDenied(peer_id, reason, str_reason, reconnect);
} else {
std::wstring wreason = utf8_to_wide(
reason == SERVER_ACCESSDENIED_CUSTOM_STRING ? str_reason :
@@ -2580,7 +2588,7 @@ void Server::DenyAccessVerCompliant(u16 peer_id, u16 proto_ver, AccessDeniedCode
void Server::DenyAccess(u16 peer_id, AccessDeniedCode reason, const std::string &custom_reason)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
SendAccessDenied(peer_id, reason, custom_reason);
m_clients.event(peer_id, CSE_SetDenied);
@@ -2591,7 +2599,7 @@ void Server::DenyAccess(u16 peer_id, AccessDeniedCode reason, const std::string
// the minimum version for MT users, maybe in 1 year
void Server::DenyAccess_Legacy(u16 peer_id, const std::wstring &reason)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
SendAccessDenied_Legacy(peer_id, reason);
m_clients.event(peer_id, CSE_SetDenied);
@@ -2600,7 +2608,7 @@ void Server::DenyAccess_Legacy(u16 peer_id, const std::wstring &reason)
void Server::acceptAuth(u16 peer_id, bool forSudoMode)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
if (!forSudoMode) {
RemoteClient* client = getClient(peer_id, CS_Invalid);
@@ -2631,7 +2639,7 @@ void Server::acceptAuth(u16 peer_id, bool forSudoMode)
void Server::DeleteClient(u16 peer_id, ClientDeletionReason reason)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
std::wstring message;
{
/*
@@ -2646,7 +2654,7 @@ void Server::DeleteClient(u16 peer_id, ClientDeletionReason reason)
if(psound.clients.empty())
m_playing_sounds.erase(i++);
else
- i++;
+ ++i;
}
Player *player = m_env->getPlayer(peer_id);
@@ -2696,13 +2704,17 @@ void Server::DeleteClient(u16 peer_id, ClientDeletionReason reason)
os << player->getName() << " ";
}
- actionstream << player->getName() << " "
+ std::string name = player->getName();
+ actionstream << name << " "
<< (reason == CDR_TIMEOUT ? "times out." : "leaves game.")
<< " List of players: " << os.str() << std::endl;
+ if (m_admin_chat)
+ m_admin_chat->outgoing_queue.push_back(
+ new ChatEventNick(CET_NICK_REMOVE, name));
}
}
{
- JMutexAutoLock env_lock(m_env_mutex);
+ MutexAutoLock env_lock(m_env_mutex);
m_clients.DeleteClient(peer_id);
}
}
@@ -2714,7 +2726,7 @@ void Server::DeleteClient(u16 peer_id, ClientDeletionReason reason)
void Server::UpdateCrafting(Player* player)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
// Get a preview for crafting
ItemStack preview;
@@ -2731,6 +2743,102 @@ void Server::UpdateCrafting(Player* player)
plist->changeItem(0, preview);
}
+void Server::handleChatInterfaceEvent(ChatEvent *evt)
+{
+ if (evt->type == CET_NICK_ADD) {
+ // The terminal informed us of its nick choice
+ m_admin_nick = ((ChatEventNick *)evt)->nick;
+ if (!m_script->getAuth(m_admin_nick, NULL, NULL)) {
+ errorstream << "You haven't set up an account." << std::endl
+ << "Please log in using the client as '"
+ << m_admin_nick << "' with a secure password." << std::endl
+ << "Until then, you can't execute admin tasks via the console," << std::endl
+ << "and everybody can claim the user account instead of you," << std::endl
+ << "giving them full control over this server." << std::endl;
+ }
+ } else {
+ assert(evt->type == CET_CHAT);
+ handleAdminChat((ChatEventChat *)evt);
+ }
+}
+
+std::wstring Server::handleChat(const std::string &name, const std::wstring &wname,
+ const std::wstring &wmessage, bool check_shout_priv,
+ u16 peer_id_to_avoid_sending)
+{
+ // If something goes wrong, this player is to blame
+ RollbackScopeActor rollback_scope(m_rollback,
+ std::string("player:") + name);
+
+ // Line to send
+ std::wstring line;
+ // Whether to send line to the player that sent the message, or to all players
+ bool broadcast_line = true;
+
+ // Run script hook
+ bool ate = m_script->on_chat_message(name,
+ wide_to_utf8(wmessage));
+ // If script ate the message, don't proceed
+ if (ate)
+ return L"";
+
+ // Commands are implemented in Lua, so only catch invalid
+ // commands that were not "eaten" and send an error back
+ if (wmessage[0] == L'/') {
+ std::wstring wcmd = wmessage.substr(1);
+ broadcast_line = false;
+ if (wcmd.length() == 0)
+ line += L"-!- Empty command";
+ else
+ line += L"-!- Invalid command: " + str_split(wcmd, L' ')[0];
+ } else {
+ if (check_shout_priv && !checkPriv(name, "shout")) {
+ line += L"-!- You don't have permission to shout.";
+ broadcast_line = false;
+ } else {
+ line += L"<";
+ line += wname;
+ line += L"> ";
+ line += wmessage;
+ }
+ }
+
+ /*
+ Tell calling method to send the message to sender
+ */
+ if (!broadcast_line) {
+ return line;
+ } else {
+ /*
+ Send the message to others
+ */
+ actionstream << "CHAT: " << wide_to_narrow(line) << std::endl;
+
+ std::vector<u16> clients = m_clients.getClientIDs();
+
+ for (u16 i = 0; i < clients.size(); i++) {
+ u16 cid = clients[i];
+ if (cid != peer_id_to_avoid_sending)
+ SendChatMessage(cid, line);
+ }
+ }
+ return L"";
+}
+
+void Server::handleAdminChat(const ChatEventChat *evt)
+{
+ std::string name = evt->nick;
+ std::wstring wname = utf8_to_wide(name);
+ std::wstring wmessage = evt->evt_msg;
+
+ std::wstring answer = handleChat(name, wname, wmessage);
+
+ // If asked to send answer to sender
+ if (!answer.empty()) {
+ m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", answer));
+ }
+}
+
RemoteClient* Server::getClient(u16 peer_id, ClientState state_min)
{
RemoteClient *client = getClientNoEx(peer_id,state_min);
@@ -2862,9 +2970,14 @@ void Server::notifyPlayer(const char *name, const std::wstring &msg)
if (!m_env)
return;
+ if (m_admin_nick == name && !m_admin_nick.empty()) {
+ m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", msg));
+ }
+
Player *player = m_env->getPlayer(name);
- if (!player)
+ if (!player) {
return;
+ }
if (player->peer_id == PEER_ID_INEXISTENT)
return;
@@ -2929,7 +3042,8 @@ bool Server::hudSetFlags(Player *player, u32 flags, u32 mask)
return false;
SendHUDSetFlags(player->peer_id, flags, mask);
- player->hud_flags = flags;
+ player->hud_flags &= ~mask;
+ player->hud_flags |= flags;
PlayerSAO* playersao = player->getPlayerSAO();
@@ -3082,19 +3196,7 @@ u32 Server::addParticleSpawner(u16 amount, float spawntime,
peer_id = player->peer_id;
}
- u32 id = 0;
- for(;;) // look for unused particlespawner id
- {
- id++;
- if (std::find(m_particlespawner_ids.begin(),
- m_particlespawner_ids.end(), id)
- == m_particlespawner_ids.end())
- {
- m_particlespawner_ids.push_back(id);
- break;
- }
- }
-
+ u32 id = m_env->addParticleSpawner(spawntime);
SendAddParticleSpawner(peer_id, amount, spawntime,
minpos, maxpos, minvel, maxvel, minacc, maxacc,
minexptime, maxexptime, minsize, maxsize,
@@ -3117,13 +3219,16 @@ void Server::deleteParticleSpawner(const std::string &playername, u32 id)
peer_id = player->peer_id;
}
- m_particlespawner_ids.erase(
- std::remove(m_particlespawner_ids.begin(),
- m_particlespawner_ids.end(), id),
- m_particlespawner_ids.end());
+ m_env->deleteParticleSpawner(id);
SendDeleteParticleSpawner(peer_id, id);
}
+void Server::deleteParticleSpawnerAll(u32 id)
+{
+ m_env->deleteParticleSpawner(id);
+ SendDeleteParticleSpawner(PEER_ID_INEXISTENT, id);
+}
+
Inventory* Server::createDetachedInventory(const std::string &name)
{
if(m_detached_inventories.count(name) > 0){
@@ -3159,7 +3264,7 @@ bool Server::rollbackRevertActions(const std::list<RollbackAction> &actions,
for(std::list<RollbackAction>::const_iterator
i = actions.begin();
- i != actions.end(); i++)
+ i != actions.end(); ++i)
{
const RollbackAction &action = *i;
num_tried++;
@@ -3277,30 +3382,24 @@ v3f Server::findSpawnPos()
return nodeposf * BS;
}
- // Default position is static_spawnpoint
- // We will return it if we don't found a good place
- v3s16 nodepos(nodeposf.X, nodeposf.Y, nodeposf.Z);
-
- s16 water_level = map.getWaterLevel();
-
bool is_good = false;
// Try to find a good place a few times
- for(s32 i = 0; i < 1000 && !is_good; i++) {
+ for(s32 i = 0; i < 4000 && !is_good; i++) {
s32 range = 1 + i;
// We're going to try to throw the player to this position
v2s16 nodepos2d = v2s16(
-range + (myrand() % (range * 2)),
-range + (myrand() % (range * 2)));
- // Get ground height at point
- s16 groundheight = map.findGroundLevel(nodepos2d);
- if (groundheight <= water_level) // Don't go underwater
- continue;
- if (groundheight > water_level + 6) // Don't go to high places
+ // Get spawn level at point
+ s16 spawn_level = m_emerge->getSpawnLevelAtPoint(nodepos2d);
+ // Continue if MAX_MAP_GENERATION_LIMIT was returned by
+ // the mapgen to signify an unsuitable spawn position
+ if (spawn_level == MAX_MAP_GENERATION_LIMIT)
continue;
- nodepos = v3s16(nodepos2d.X, groundheight, nodepos2d.Y);
+ v3s16 nodepos(nodepos2d.X, spawn_level, nodepos2d.Y);
s32 air_count = 0;
for (s32 i = 0; i < 10; i++) {
@@ -3309,7 +3408,11 @@ v3f Server::findSpawnPos()
content_t c = map.getNodeNoEx(nodepos).getContent();
if (c == CONTENT_AIR || c == CONTENT_IGNORE) {
air_count++;
- if (air_count >= 2){
+ if (air_count >= 2) {
+ nodeposf = intToFloat(nodepos, BS);
+ // Don't spawn the player outside map boundaries
+ if (objectpos_over_limit(nodeposf))
+ continue;
is_good = true;
break;
}
@@ -3318,7 +3421,7 @@ v3f Server::findSpawnPos()
}
}
- return intToFloat(nodepos, BS);
+ return nodeposf;
}
PlayerSAO* Server::emergePlayer(const char *name, u16 peer_id, u16 proto_version)
@@ -3367,6 +3470,16 @@ PlayerSAO* Server::emergePlayer(const char *name, u16 peer_id, u16 proto_version
// Add player to environment
m_env->addPlayer(player);
+ } else {
+ // If the player exists, ensure that they respawn inside legal bounds
+ // This fixes an assert crash when the player can't be added
+ // to the environment
+ if (objectpos_over_limit(player->getPosition())) {
+ actionstream << "Respawn position for player \""
+ << name << "\" outside limits, resetting" << std::endl;
+ v3f pos = findSpawnPos();
+ player->setPosition(pos);
+ }
}
// Create a new player active object
@@ -3392,15 +3505,17 @@ PlayerSAO* Server::emergePlayer(const char *name, u16 peer_id, u16 proto_version
void dedicated_server_loop(Server &server, bool &kill)
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
verbosestream<<"dedicated_server_loop()"<<std::endl;
IntervalLimiter m_profiler_interval;
- for(;;)
- {
- float steplen = g_settings->getFloat("dedicated_server_step");
+ static const float steplen = g_settings->getFloat("dedicated_server_step");
+ static const float profiler_print_interval =
+ g_settings->getFloat("profiler_print_interval");
+
+ for(;;) {
// This is kind of a hack but can be done like this
// because server.step() is very light
{
@@ -3422,10 +3537,7 @@ void dedicated_server_loop(Server &server, bool &kill)
/*
Profiler
*/
- float profiler_print_interval =
- g_settings->getFloat("profiler_print_interval");
- if(profiler_print_interval != 0)
- {
+ if (profiler_print_interval != 0) {
if(m_profiler_interval.step(steplen, profiler_print_interval))
{
infostream<<"Profiler:"<<std::endl;
diff --git a/src/server.h b/src/server.h
index d16230967..daf51dee1 100644
--- a/src/server.h
+++ b/src/server.h
@@ -32,6 +32,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "util/numeric.h"
#include "util/thread.h"
#include "environment.h"
+#include "chat_interface.h"
#include "clientiface.h"
#include "network/networkpacket.h"
#include <string>
@@ -171,7 +172,8 @@ public:
const std::string &path_world,
const SubgameSpec &gamespec,
bool simple_singleplayer_mode,
- bool ipv6
+ bool ipv6,
+ ChatInterface *iface = NULL
);
~Server();
void start(Address bind_addr);
@@ -220,7 +222,8 @@ public:
void Send(NetworkPacket* pkt);
- // Environment must be locked when called
+ // Both setter and getter need no envlock,
+ // can be called freely from threads
void setTimeOfDay(u32 time);
/*
@@ -284,6 +287,7 @@ public:
const std::string &playername);
void deleteParticleSpawner(const std::string &playername, u32 id);
+ void deleteParticleSpawnerAll(u32 id);
// Creates or resets inventory
Inventory* createDetachedInventory(const std::string &name);
@@ -368,6 +372,8 @@ public:
u8* ser_vers, u16* prot_vers, u8* major, u8* minor, u8* patch,
std::string* vers_string);
+ void printToConsoleOnly(const std::string &text);
+
void SendPlayerHPOrDie(PlayerSAO *player);
void SendPlayerBreath(u16 peer_id);
void SendInventory(PlayerSAO* playerSAO);
@@ -376,6 +382,9 @@ public:
// Bind address
Address m_bind_addr;
+ // Environment mutex (envlock)
+ Mutex m_env_mutex;
+
private:
friend class EmergeThread;
@@ -468,6 +477,15 @@ private:
void DeleteClient(u16 peer_id, ClientDeletionReason reason);
void UpdateCrafting(Player *player);
+ void handleChatInterfaceEvent(ChatEvent *evt);
+
+ // This returns the answer to the sender of wmessage, or "" if there is none
+ std::wstring handleChat(const std::string &name, const std::wstring &wname,
+ const std::wstring &wmessage,
+ bool check_shout_priv = false,
+ u16 peer_id_to_avoid_sending = PEER_ID_INEXISTENT);
+ void handleAdminChat(const ChatEventChat *evt);
+
v3f findSpawnPos();
// When called, connection mutex should be locked
@@ -516,7 +534,6 @@ private:
// Environment
ServerEnvironment *m_env;
- JMutex m_env_mutex;
// server connection
con::Connection m_con;
@@ -557,7 +574,7 @@ private:
// A buffer for time steps
// step() increments and AsyncRunStep() run by m_thread reads it.
float m_step_dtime;
- JMutex m_step_dtime_mutex;
+ Mutex m_step_dtime_mutex;
// current server step lag counter
float m_lag;
@@ -594,6 +611,9 @@ private:
std::string m_shutdown_msg;
bool m_shutdown_ask_reconnect;
+ ChatInterface *m_admin_chat;
+ std::string m_admin_nick;
+
/*
Map edit event queue. Automatically receives all map edits.
The constructor of this class registers us to receive them through
@@ -643,10 +663,7 @@ private:
// key = name
std::map<std::string, Inventory*> m_detached_inventories;
- /*
- Particles
- */
- std::vector<u32> m_particlespawner_ids;
+ DISABLE_CLASS_COPY(Server);
};
/*
diff --git a/src/serverlist.cpp b/src/serverlist.cpp
index a33d1d6bf..de7962a68 100644
--- a/src/serverlist.cpp
+++ b/src/serverlist.cpp
@@ -69,8 +69,12 @@ std::vector<ServerListSpec> getLocal()
std::vector<ServerListSpec> getOnline()
{
std::ostringstream geturl;
+
+ u16 proto_version_min = g_settings->getFlag("send_pre_v25_init") ?
+ CLIENT_PROTOCOL_VERSION_MIN_LEGACY : CLIENT_PROTOCOL_VERSION_MIN;
+
geturl << g_settings->get("serverlist_url") <<
- "/list?proto_version_min=" << CLIENT_PROTOCOL_VERSION_MIN <<
+ "/list?proto_version_min=" << proto_version_min <<
"&proto_version_max=" << CLIENT_PROTOCOL_VERSION_MAX;
Json::Value root = fetchJsonValue(geturl.str(), NULL);
@@ -165,7 +169,7 @@ const std::string serialize(const std::vector<ServerListSpec> &serverlist)
std::string liststring;
for (std::vector<ServerListSpec>::const_iterator it = serverlist.begin();
it != serverlist.end();
- it++) {
+ ++it) {
liststring += "[server]\n";
liststring += (*it)["name"].asString() + '\n';
liststring += (*it)["address"].asString() + '\n';
@@ -182,7 +186,7 @@ const std::string serializeJson(const std::vector<ServerListSpec> &serverlist)
Json::Value list(Json::arrayValue);
for (std::vector<ServerListSpec>::const_iterator it = serverlist.begin();
it != serverlist.end();
- it++) {
+ ++it) {
list.append(*it);
}
root["list"] = list;
diff --git a/src/serverobject.cpp b/src/serverobject.cpp
index 699040bf2..236d7e8dc 100644
--- a/src/serverobject.cpp
+++ b/src/serverobject.cpp
@@ -52,7 +52,7 @@ ServerActiveObject* ServerActiveObject::create(ActiveObjectType type,
}
// If factory is not found, just return.
- dstream<<"WARNING: ServerActiveObject: No factory for type="
+ warningstream<<"ServerActiveObject: No factory for type="
<<type<<std::endl;
return NULL;
}
diff --git a/src/settings.cpp b/src/settings.cpp
index e95bd436d..56afa6133 100644
--- a/src/settings.cpp
+++ b/src/settings.cpp
@@ -20,8 +20,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "settings.h"
#include "irrlichttypes_bloated.h"
#include "exceptions.h"
-#include "jthread/jmutexautolock.h"
-#include "strfnd.h"
+#include "threading/mutex_auto_lock.h"
+#include "util/strfnd.h"
#include <iostream>
#include <fstream>
#include <sstream>
@@ -56,8 +56,8 @@ Settings & Settings::operator = (const Settings &other)
if (&other == this)
return *this;
- JMutexAutoLock lock(m_mutex);
- JMutexAutoLock lock2(other.m_mutex);
+ MutexAutoLock lock(m_mutex);
+ MutexAutoLock lock2(other.m_mutex);
clearNoLock();
updateNoLock(other);
@@ -155,7 +155,7 @@ bool Settings::readConfigFile(const char *filename)
bool Settings::parseConfigLines(std::istream &is, const std::string &end)
{
- JMutexAutoLock lock(m_mutex);
+ MutexAutoLock lock(m_mutex);
std::string line, name, value;
@@ -194,7 +194,7 @@ bool Settings::parseConfigLines(std::istream &is, const std::string &end)
void Settings::writeLines(std::ostream &os, u32 tab_depth) const
{
- JMutexAutoLock lock(m_mutex);
+ MutexAutoLock lock(m_mutex);
for (std::map<std::string, SettingsEntry>::const_iterator
it = m_settings.begin();
@@ -298,7 +298,7 @@ bool Settings::updateConfigObject(std::istream &is, std::ostream &os,
bool Settings::updateConfigFile(const char *filename)
{
- JMutexAutoLock lock(m_mutex);
+ MutexAutoLock lock(m_mutex);
std::ifstream is(filename);
std::ostringstream os(std::ios_base::binary);
@@ -379,7 +379,7 @@ bool Settings::parseCommandLine(int argc, char *argv[],
const SettingsEntry &Settings::getEntry(const std::string &name) const
{
- JMutexAutoLock lock(m_mutex);
+ MutexAutoLock lock(m_mutex);
std::map<std::string, SettingsEntry>::const_iterator n;
if ((n = m_settings.find(name)) == m_settings.end()) {
@@ -562,7 +562,7 @@ bool Settings::getNoiseParamsFromGroup(const std::string &name,
bool Settings::exists(const std::string &name) const
{
- JMutexAutoLock lock(m_mutex);
+ MutexAutoLock lock(m_mutex);
return (m_settings.find(name) != m_settings.end() ||
m_defaults.find(name) != m_defaults.end());
@@ -742,7 +742,7 @@ bool Settings::setEntry(const std::string &name, const void *data,
return false;
{
- JMutexAutoLock lock(m_mutex);
+ MutexAutoLock lock(m_mutex);
SettingsEntry &entry = set_default ? m_defaults[name] : m_settings[name];
old_group = entry.group;
@@ -878,22 +878,28 @@ bool Settings::setNoiseParams(const std::string &name,
bool Settings::remove(const std::string &name)
{
- JMutexAutoLock lock(m_mutex);
+ MutexAutoLock lock(m_mutex);
- delete m_settings[name].group;
- return m_settings.erase(name);
+ std::map<std::string, SettingsEntry>::iterator it = m_settings.find(name);
+ if (it != m_settings.end()) {
+ delete it->second.group;
+ m_settings.erase(it);
+ return true;
+ } else {
+ return false;
+ }
}
void Settings::clear()
{
- JMutexAutoLock lock(m_mutex);
+ MutexAutoLock lock(m_mutex);
clearNoLock();
}
void Settings::clearDefaults()
{
- JMutexAutoLock lock(m_mutex);
+ MutexAutoLock lock(m_mutex);
clearDefaultsNoLock();
}
@@ -902,7 +908,7 @@ void Settings::updateValue(const Settings &other, const std::string &name)
if (&other == this)
return;
- JMutexAutoLock lock(m_mutex);
+ MutexAutoLock lock(m_mutex);
try {
std::string val = other.get(name);
@@ -918,8 +924,8 @@ void Settings::update(const Settings &other)
if (&other == this)
return;
- JMutexAutoLock lock(m_mutex);
- JMutexAutoLock lock2(other.m_mutex);
+ MutexAutoLock lock(m_mutex);
+ MutexAutoLock lock2(other.m_mutex);
updateNoLock(other);
}
@@ -982,13 +988,13 @@ void Settings::clearDefaultsNoLock()
void Settings::registerChangedCallback(std::string name,
setting_changed_callback cbf, void *userdata)
{
- JMutexAutoLock lock(m_callbackMutex);
+ MutexAutoLock lock(m_callbackMutex);
m_callbacks[name].push_back(std::make_pair(cbf, userdata));
}
void Settings::deregisterChangedCallback(std::string name, setting_changed_callback cbf, void *userdata)
{
- JMutexAutoLock lock(m_callbackMutex);
+ MutexAutoLock lock(m_callbackMutex);
std::map<std::string, std::vector<std::pair<setting_changed_callback, void*> > >::iterator iterToVector = m_callbacks.find(name);
if (iterToVector != m_callbacks.end())
{
@@ -1004,12 +1010,12 @@ void Settings::deregisterChangedCallback(std::string name, setting_changed_callb
void Settings::doCallbacks(const std::string name)
{
- JMutexAutoLock lock(m_callbackMutex);
+ MutexAutoLock lock(m_callbackMutex);
std::map<std::string, std::vector<std::pair<setting_changed_callback, void*> > >::iterator iterToVector = m_callbacks.find(name);
if (iterToVector != m_callbacks.end())
{
std::vector<std::pair<setting_changed_callback, void*> >::iterator iter;
- for (iter = iterToVector->second.begin(); iter != iterToVector->second.end(); iter++)
+ for (iter = iterToVector->second.begin(); iter != iterToVector->second.end(); ++iter)
{
(iter->first)(name, iter->second);
}
diff --git a/src/settings.h b/src/settings.h
index d41f134cd..80d41fd79 100644
--- a/src/settings.h
+++ b/src/settings.h
@@ -22,7 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "irrlichttypes_bloated.h"
#include "util/string.h"
-#include "jthread/jmutex.h"
+#include "threading/mutex.h"
#include <string>
#include <map>
#include <list>
@@ -225,8 +225,8 @@ private:
std::map<std::string, std::vector<std::pair<setting_changed_callback,void*> > > m_callbacks;
- mutable JMutex m_callbackMutex;
- mutable JMutex m_mutex; // All methods that access m_settings/m_defaults directly should lock this.
+ mutable Mutex m_callbackMutex;
+ mutable Mutex m_mutex; // All methods that access m_settings/m_defaults directly should lock this.
};
diff --git a/src/settings_translation_file.cpp b/src/settings_translation_file.cpp
new file mode 100644
index 000000000..f7e14dd65
--- /dev/null
+++ b/src/settings_translation_file.cpp
@@ -0,0 +1,620 @@
+// This file is automatically generated
+// It conatins a bunch of fake gettext calls, to tell xgettext about the strings in config files
+// To update it, refer to the bottom of builtin/mainmenu/tab_settings.lua
+
+fake_function() {
+ gettext("Client");
+ gettext("Controls");
+ gettext("Build inside player");
+ gettext("If enabled, you can place blocks at the position (feet + eye level) where you stand.\nThis is helpful when working with nodeboxes in small areas.");
+ gettext("Flying");
+ gettext("Player is able to fly without being affected by gravity.\nThis requires the \"fly\" privilege on the server.");
+ gettext("Fast movement");
+ gettext("Fast movement (via use key).\nThis requires the \"fast\" privilege on the server.");
+ gettext("Noclip");
+ gettext("If enabled together with fly mode, player is able to fly through solid nodes.\nThis requires the \"noclip\" privilege on the server.");
+ gettext("Cinematic mode");
+ gettext("Smooths camera when moving and looking around.\nUseful for recording videos.");
+ gettext("Camera smoothing");
+ gettext("Smooths rotation of camera. 0 to disable.");
+ gettext("Camera smoothing in cinematic mode");
+ gettext("Smooths rotation of camera in cinematic mode. 0 to disable.");
+ gettext("Invert mouse");
+ gettext("Invert vertical mouse movement.");
+ gettext("Mouse sensitivity");
+ gettext("Mouse sensitivity multiplier.");
+ gettext("Key use for climbing/descending");
+ gettext("If enabled, \"use\" key instead of \"sneak\" key is used for climbing down and descending.");
+ gettext("Double tap jump for fly");
+ gettext("Double-tapping the jump key toggles fly mode.");
+ gettext("Always fly and fast");
+ gettext("If disabled \"use\" key is used to fly fast if both fly and fast mode are enabled.");
+ gettext("Rightclick repetition interval");
+ gettext("The time in seconds it takes between repeated right clicks when holding the right mouse button.");
+ gettext("Random input");
+ gettext("Enable random user input (only used for testing).");
+ gettext("Continuous forward");
+ gettext("Continuous forward movement (only used for testing).");
+ gettext("Forward key");
+ gettext("Key for moving the player forward.\nSee http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3");
+ gettext("Backward key");
+ gettext("Key for moving the player backward.\nSee http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3");
+ gettext("Left key");
+ gettext("Key for moving the player left.\nSee http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3");
+ gettext("Right key");
+ gettext("Key for moving the player right.\nSee http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3");
+ gettext("Jump key");
+ gettext("Key for jumping.\nSee http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3");
+ gettext("Sneak key");
+ gettext("Key for sneaking.\nAlso used for climbing down and descending in water if aux1_descends is disabled.\nSee http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3");
+ gettext("Inventory key");
+ gettext("Key for opening the inventory.\nSee http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3");
+ gettext("Use key");
+ gettext("Key for moving fast in fast mode.\nSee http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3");
+ gettext("Chat key");
+ gettext("Key for opening the chat window.\nSee http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3");
+ gettext("Command key");
+ gettext("Key for opening the chat window to type commands.\nSee http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3");
+ gettext("Console key");
+ gettext("Key for opening the chat console.\nSee http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3");
+ gettext("Range select key");
+ gettext("Key for toggling unlimited view range.\nSee http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3");
+ gettext("Fly key");
+ gettext("Key for toggling flying.\nSee http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3");
+ gettext("Fast key");
+ gettext("Key for toggling fast mode.\nSee http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3");
+ gettext("Noclip key");
+ gettext("Key for toggling noclip mode.\nSee http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3");
+ gettext("Cinematic mode key");
+ gettext("Key for toggling cinematic mode.\nSee http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3");
+ gettext("Minimap key");
+ gettext("Key for toggling display of minimap.\nSee http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3");
+ gettext("Screenshot");
+ gettext("Key for taking screenshots.\nSee http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3");
+ gettext("Drop item key");
+ gettext("Key for dropping the currently selected item.\nSee http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3");
+ gettext("HUD toggle key");
+ gettext("Key for toggling the display of the HUD.\nSee http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3");
+ gettext("Chat toggle key");
+ gettext("Key for toggling the display of the chat.\nSee http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3");
+ gettext("Fog toggle key");
+ gettext("Key for toggling the display of the fog.\nSee http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3");
+ gettext("Camera update toggle key");
+ gettext("Key for toggling the camrea update. Only used for development\nSee http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3");
+ gettext("Debug info toggle key");
+ gettext("Key for toggling the display of debug info.\nSee http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3");
+ gettext("Profiler toggle key");
+ gettext("Key for toggling the display of the profiler. Used for development.\nSee http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3");
+ gettext("Toggle camera mode key");
+ gettext("Key for switching between first- and third-person camera.\nSee http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3");
+ gettext("View range increase key");
+ gettext("Key for increasing the viewing range.\nSee http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3");
+ gettext("View range decrease key");
+ gettext("Key for decreasing the viewing range.\nSee http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3");
+ gettext("Print stacks");
+ gettext("Key for printing debug stacks. Used for development.\nSee http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3");
+ gettext("Network");
+ gettext("Server address");
+ gettext("Address to connect to.\nLeave this blank to start a local server.\nNote that the address field in the main menu overrides this setting.");
+ gettext("Remote port");
+ gettext("Port to connect to (UDP).\nNote that the port field in the main menu overrides this setting.");
+ gettext("Support older servers");
+ gettext("Whether to support older servers before protocol version 25.\nEnable if you want to connect to 0.4.12 servers and before.\nServers starting with 0.4.13 will work, 0.4.12-dev servers may work.\nDisabling this option will protect your password better.");
+ gettext("Saving map received from server");
+ gettext("Save the map received by the client on disk.");
+ gettext("Show entity selection boxes");
+ gettext("Show entity selection boxes");
+ gettext("Connect to external media server");
+ gettext("Enable usage of remote media server (if provided by server).\nRemote servers offer a significantly faster way to download media (e.g. textures)\nwhen connecting to the server.");
+ gettext("Serverlist URL");
+ gettext("URL to the server list displayed in the Multiplayer Tab.");
+ gettext("Serverlist file");
+ gettext("File in client/serverlist/ that contains your favorite servers displayed in the Multiplayer Tab.");
+ gettext("Graphics");
+ gettext("In-Game");
+ gettext("Basic");
+ gettext("VBO");
+ gettext("Enable VBO");
+ gettext("Fog");
+ gettext("Whether to fog out the end of the visible area.");
+ gettext("Leaves style");
+ gettext("Leaves style:\n- Fancy: all faces visible\n- Simple: only outer faces, if defined special_tiles are used\n- Opaque: disable transparency");
+ gettext("Connect glass");
+ gettext("Connects glass if supported by node.");
+ gettext("Smooth lighting");
+ gettext("Enable smooth lighting with simple ambient occlusion.\nDisable for speed or for different looks.");
+ gettext("Clouds");
+ gettext("Clouds are a client side effect.");
+ gettext("3D clouds");
+ gettext("Use 3D cloud look instead of flat.");
+ gettext("Node highlighting");
+ gettext("Method used to highlight selected object.");
+ gettext("Filtering");
+ gettext("Mipmapping");
+ gettext("Use mip mapping to scale textures. May slightly increase performance.");
+ gettext("Anisotropic filtering");
+ gettext("Use anisotropic filtering when viewing at textures from an angle.");
+ gettext("Bilinear filtering");
+ gettext("Use bilinear filtering when scaling textures.");
+ gettext("Trilinear filtering");
+ gettext("Use trilinear filtering when scaling textures.");
+ gettext("Clean transparent textures");
+ gettext("Filtered textures can blend RGB values with fully-transparent neighbors,\nwhich PNG optimizers usually discard, sometimes resulting in a dark or\nlight edge to transparent textures. Apply this filter to clean that up\nat texture load time.");
+ gettext("Minimum texture size for filters");
+ gettext("When using bilinear/trilinear/anisotropic filters, low-resolution textures\ncan be blurred, so automatically upscale them with nearest-neighbor\ninterpolation to preserve crisp pixels. This sets the minimum texture size\nfor the upscaled textures; higher values look sharper, but require more\nmemory. Powers of 2 are recommended. Setting this higher than 1 may not\nhave a visible effect unless bilinear/trilinear/anisotropic filtering is\nenabled.");
+ gettext("FSAA");
+ gettext("Experimental option, might cause visible spaces between blocks\nwhen set to higher number than 0.");
+ gettext("Shaders");
+ gettext("Shaders");
+ gettext("Shaders allow advanced visul effects and may increase performance on some video cards.\nThy only work with the OpenGL video backend.");
+ gettext("Tone Mapping");
+ gettext("Filmic tone mapping");
+ gettext("Enables filmic tone mapping");
+ 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("Parallax Occlusion");
+ gettext("Parallax occlusion");
+ gettext("Enables parallax occlusion mapping.\nRequires shaders to be enabled.");
+ gettext("Parallax occlusion mode");
+ gettext("0 = parallax occlusion with slope information (faster).\n1 = relief mapping (slower, more accurate).");
+ gettext("Parallax occlusion strength");
+ gettext("Strength of parallax.");
+ gettext("Parallax occlusion iterations");
+ gettext("Number of parallax occlusion iterations.");
+ gettext("Parallax occlusion Scale");
+ gettext("Overall scale of parallax occlusion effect.");
+ gettext("Parallax occlusion bias");
+ gettext("Overall bias of parallax occlusion effect, usually scale/2.");
+ gettext("Waving Nodes");
+ gettext("Waving water");
+ gettext("Set to true enables waving water.\nRequires shaders to be enabled.");
+ gettext("Waving water height");
+ gettext("Waving water length");
+ gettext("Waving water speed");
+ gettext("Waving leaves");
+ gettext("Set to true enables waving leaves.\nRequires shaders to be enabled.");
+ gettext("Waving plants");
+ gettext("Set to true enables waving plants.\nRequires shaders to be enabled.");
+ gettext("Advanced");
+ gettext("Maximum FPS");
+ gettext("If FPS would go higher than this, limit it by sleeping\nto not waste CPU power for no benefit.");
+ gettext("FPS in pause menu");
+ gettext("Maximum FPS when game is paused.");
+ gettext("Viewing range");
+ gettext("View distance in nodes.\nMin = 20");
+ gettext("Screen width");
+ gettext("Width component of the initial window size.");
+ gettext("Screen height");
+ gettext("Height component of the initial window size.");
+ gettext("Full screen");
+ gettext("Fullscreen mode.");
+ gettext("Full screen BPP");
+ gettext("Bits per pixel (aka color depth) in fullscreen mode.");
+ gettext("V-Sync");
+ gettext("Vertical screen synchronization.");
+ gettext("Field of view");
+ gettext("Field of view in degrees.");
+ gettext("Gamma");
+ gettext("Adjust the gamma encoding for the light tables. Lower numbers are brighter.\nThis setting is for the client only and is ignored by the server.");
+ gettext("Texture path");
+ gettext("Path to texture directory. All textures are first searched from here.");
+ gettext("Video driver");
+ gettext("The rendering back-end for Irrlicht.");
+ gettext("Cloud height");
+ gettext("Height on which clouds are appearing.");
+ gettext("Cloud radius");
+ gettext("Radius of cloud area stated in number of 64 node cloud squares.\nValues larger than 26 will start to produce sharp cutoffs at cloud area corners.");
+ gettext("View bobbing");
+ gettext("Multiplier for view bobbing.\nFor example: 0 for no view bobbing; 1.0 for normal; 2.0 for double.");
+ gettext("Fall bobbing");
+ gettext("Multiplier for fall bobbing.\nFor example: 0 for no view bobbing; 1.0 for normal; 2.0 for double.");
+ gettext("3D mode");
+ gettext("3D support.\nCurrently supported:\n- none: no 3d output.\n- anaglyph: cyan/magenta color 3d.\n- interlaced: odd/even line based polarisation screen support.\n- topbottom: split screen top/bottom.\n- sidebyside: split screen side by side.\n- pageflip: quadbuffer based 3d.");
+ gettext("Console color");
+ gettext("In-game chat console background color (R,G,B).");
+ gettext("Console alpha");
+ gettext("In-game chat console background alpha (opaqueness, between 0 and 255).");
+ gettext("Selection box color");
+ gettext("Selection box border color (R,G,B).");
+ gettext("Selection box width");
+ gettext("Width of the selectionbox's lines around nodes.");
+ gettext("Crosshair color");
+ gettext("Crosshair color (R,G,B).");
+ gettext("Crosshair alpha");
+ gettext("Crosshair alpha (opaqueness, between 0 and 255).");
+ gettext("Desynchronize block animation");
+ gettext("Whether node texture animations should be desynchronized per mapblock.");
+ gettext("Maximum hotbar width");
+ gettext("Maximum proportion of current window to be used for hotbar.\nUseful if there's something to be displayed right or left of hotbar.");
+ gettext("Mesh cache");
+ gettext("Enables caching of facedir rotated meshes.");
+ gettext("Minimap");
+ gettext("Enables minimap.");
+ gettext("Round minimap");
+ gettext("Shape of the minimap. Enabled = round, disabled = square.");
+ gettext("Minimap scan height");
+ gettext("True = 256\nFalse = 128\nUseable to make minimap smoother on slower machines.");
+ gettext("Colored fog");
+ gettext("Make fog and sky colors depend on daytime (dawn/sunset) and view direction.");
+ gettext("Ambient occlusion gamma");
+ gettext("The strength (darkness) of node ambient-occlusion shading.\nLower is darker, Higher is lighter. The valid range of values for this\nsetting is 0.25 to 4.0 inclusive. If the value is out of range it will be\nset to the nearest valid value.");
+ gettext("Inventory items animations");
+ gettext("Enables animation of inventory items.");
+ gettext("Menus");
+ gettext("Clouds in menu");
+ gettext("Use a cloud animation for the main menu background.");
+ gettext("GUI scaling");
+ gettext("Scale gui by a user specified value.\nUse a nearest-neighbor-anti-alias filter to scale the GUI.\nThis will smooth over some of the rough edges, and blend\npixels when scaling down, at the cost of blurring some\nedge pixels when images are scaled by non-integer sizes.");
+ gettext("GUI scaling filter");
+ gettext("When gui_scaling_filter is true, all GUI images need to be\nfiltered in software, but some images are generated directly\nto hardware (e.g. render-to-texture for nodes in inventory).");
+ gettext("GUI scaling filter txr2img");
+ gettext("When gui_scaling_filter_txr2img is true, copy those images\nfrom hardware to software for scaling. When false, fall back\nto the old scaling method, for video drivers that don't\npropery support downloading textures back from hardware.");
+ gettext("Tooltip delay");
+ gettext("Delay showing tooltips, stated in milliseconds.");
+ gettext("Freetype fonts");
+ gettext("Whether freetype fonts are used, requires freetype support to be compiled in.");
+ gettext("Font path");
+ gettext("Path to TrueTypeFont or bitmap.");
+ gettext("Font size");
+ gettext("Font shadow");
+ gettext("Font shadow offset, if 0 then shadow will not be drawn.");
+ gettext("Font shadow alpha");
+ gettext("Font shadow alpha (opaqueness, between 0 and 255).");
+ gettext("Monospace font path");
+ gettext("Monospace font size");
+ gettext("Fallback font");
+ gettext("This font will be used for certain languages.");
+ gettext("Fallback font size");
+ gettext("Fallback font shadow");
+ gettext("Fallback font shadow alpha");
+ gettext("Screenshot folder");
+ gettext("Path to save screenshots at.");
+ gettext("Screenshot format");
+ gettext("Format of screenshots.");
+ gettext("Screenshot quality");
+ gettext("Screenshot quality. Only used for JPEG format.\n1 means worst quality; 100 means best quality.\nUse 0 for default quality.");
+ gettext("Advanced");
+ gettext("DPI");
+ gettext("Adjust dpi configuration to your screen (non X11/Android only) e.g. for 4k screens.");
+ gettext("Sound");
+ gettext("Sound");
+ gettext("Volume");
+ gettext("Advanced");
+ gettext("Mapblock unload timeout");
+ gettext("Timeout for client to remove unused map data from memory.");
+ gettext("Mapblock limit");
+ gettext("Maximum number of mapblocks for client to be kept in memory.\nSet to -1 for unlimited amount.");
+ gettext("Show debug info");
+ gettext("Whether to show the client debug info (has the same effect as hitting F5).");
+ gettext("Server / Singleplayer");
+ gettext("Server name");
+ gettext("Name of the server, to be displayed when players join and in the serverlist.");
+ gettext("Server description");
+ gettext("Description of server, to be displayed when players join and in the serverlist.");
+ gettext("Server address");
+ gettext("Domain name of server, to be displayed in the serverlist.");
+ gettext("Server URL");
+ gettext("Homepage of server, to be displayed in the serverlist.");
+ gettext("Announce server");
+ gettext("Automaticaly report to the serverlist.");
+ gettext("Serverlist URL");
+ gettext("Announce to this serverlist.\nIf you want to announce your ipv6 address, use serverlist_url = v6.servers.minetest.net.");
+ gettext("Network");
+ gettext("Server port");
+ gettext("Network port to listen (UDP).\nThis value will be overridden when starting from the main menu.");
+ gettext("Bind address");
+ gettext("The network interface that the server listens on.");
+ gettext("Strict protocol checking");
+ gettext("Enable to disallow old clients from connecting.\nOlder clients are compatible in the sense that they will not crash when connecting\nto new servers, but they may not support all new features that you are expecting.");
+ gettext("Remote media");
+ gettext("Specifies URL from which client fetches media instead of using UDP.\n$filename should be accessible from $remote_media$filename via cURL\n(obviously, remote_media should end with a slash).\nFiles that are not present will be fetched the usual way.");
+ gettext("IPv6 server");
+ gettext("Enable/disable running an IPv6 server. An IPv6 server may be restricted\nto IPv6 clients, depending on system configuration.\nIgnored if bind_address is set.");
+ gettext("Advanced");
+ gettext("Maximum simultaneously blocks send per client");
+ gettext("How many blocks are flying in the wire simultaneously per client.");
+ gettext("Maximum simultaneously bocks send total");
+ gettext("How many blocks are flying in the wire simultaneously for the whole server.");
+ gettext("To reduce lag, block transfers are slowed down when a player is building something.\nThis determines how long they are slowed down after placing or removing a node.");
+ gettext("Max. packets per iteration");
+ gettext("Maximum number of packets sent per send step, if you have a slow connection\ntry reducing it, but don't reduce it to a number below double of targeted\nclient number.");
+ gettext("Game");
+ gettext("Default game");
+ gettext("Default game when creating a new world.\nThis will be overridden when creating a world from the main menu.");
+ gettext("Message of the day");
+ gettext("Message of the day displayed to players connecting.");
+ gettext("Maximum users");
+ gettext("Maximum number of players that can connect simultaneously.");
+ gettext("Map directory");
+ gettext("World directory (everything in the world is stored here).\nNot needed if starting from the main menu.");
+ gettext("Item entity TTL");
+ gettext("Time in seconds for item entity (dropped items) to live.\nSetting it to -1 disables the feature.");
+ gettext("Damage");
+ gettext("Enable players getting damage and dying.");
+ gettext("Fixed map seed");
+ gettext("A chosen map seed for a new map, leave empty for random.\nWill be overridden when creating a new world in the main menu.");
+ gettext("Default password");
+ gettext("New users need to input this password.");
+ gettext("Default privileges");
+ gettext("The privileges that new users automatically get.\nSee /privs in game for a full list on your server and mod configuration.");
+ gettext("Basic Privileges");
+ gettext("Privileges that players with basic_privs can grant");
+ gettext("Unlimited player transfer distance");
+ gettext("Whether players are shown to clients without any range limit.\nDeprecated, use the setting player_transfer_distance instead.");
+ gettext("Player transfer distance");
+ gettext("Defines the maximal player transfer distance in blocks (0 = unlimited).");
+ gettext("Player versus Player");
+ gettext("Whether to allow players to damage and kill each other.");
+ gettext("Static spawnpoint");
+ gettext("If this is set, players will always (re)spawn at the given position.");
+ gettext("Disallow empty passwords");
+ gettext("If enabled, new players cannot join with an empty password.");
+ gettext("Disable anticheat");
+ gettext("If enabled, disable cheat prevention in multiplayer.");
+ gettext("Rollback recording");
+ gettext("If enabled, actions are recorded for rollback.\nThis option is only read when server starts.");
+ gettext("Shutdown message");
+ gettext("A message to be displayed to all clients when the server shuts down.");
+ gettext("Crash message");
+ gettext("A message to be displayed to all clients when the server crashes.");
+ gettext("Ask to reconnect after crash");
+ gettext("Whether to ask clients to reconnect after a (Lua) crash.\nSet this to true if your server is set up to restart automatically.");
+ gettext("Active object send range");
+ gettext("From how far clients know about objects, stated in mapblocks (16 nodes).");
+ gettext("Active block range");
+ gettext("How large area of blocks are subject to the active block stuff, stated in mapblocks (16 nodes).\nIn active blocks objects are loaded and ABMs run.");
+ gettext("Max block send distance");
+ gettext("From how far blocks are sent to clients, stated in mapblocks (16 nodes).");
+ gettext("Maximum forceloaded blocks");
+ gettext("Maximum number of forceloaded mapblocks.");
+ gettext("Time send interval");
+ gettext("Interval of sending time of day to clients.");
+ gettext("Time speed");
+ gettext("Controls length of day/night cycle.\nExamples: 72 = 20min, 360 = 4min, 1 = 24hour, 0 = day/night/whatever stays unchanged.");
+ gettext("Map save interval");
+ gettext("Interval of saving important changes in the world, stated in seconds.");
+ gettext("Physics");
+ gettext("Default acceleration");
+ gettext("Acceleration in air");
+ gettext("Fast mode acceleration");
+ gettext("Walking speed");
+ gettext("Crouch speed");
+ gettext("Fast mode speed");
+ gettext("Climbing speed");
+ gettext("Jumping speed");
+ gettext("Descending speed");
+ gettext("Liquid fluidity");
+ gettext("Liquid fluidity smoothing");
+ gettext("Liquid sink");
+ gettext("Gravity");
+ gettext("Advanced");
+ gettext("Deprecated Lua API handling");
+ gettext("Handling for deprecated lua api calls:\n- legacy: (try to) mimic old behaviour (default for release).\n- log: mimic and log backtrace of deprecated call (default for debug).\n- error: abort on usage of deprecated call (suggested for mod developers).");
+ gettext("Mod profiling");
+ gettext("Useful for mod developers.");
+ gettext("Detailed mod profiling");
+ gettext("Detailed mod profile data. Useful for mod developers.");
+ gettext("Profiling print interval");
+ gettext("Profiler data print interval. 0 = disable. Useful for developers.");
+ gettext("Max. clearobjects extra blocks");
+ gettext("Number of extra blocks that can be loaded by /clearobjects at once.\nThis is a trade-off between sqlite transaction overhead and\nmemory consumption (4096=100MB, as a rule of thumb).");
+ gettext("Unload unused server data");
+ gettext("How much the server will wait before unloading unused mapblocks.\nHigher value is smoother, but will use more RAM.");
+ gettext("Maxmimum objects per block");
+ gettext("Maximum number of statically stored objects in a block.");
+ gettext("Synchronous SQLite");
+ gettext("See http://www.sqlite.org/pragma.html#pragma_synchronous");
+ gettext("Dedicated server step");
+ gettext("Length of a server tick and the interval at which objects are generally updated over network.");
+ gettext("Active Block Management interval");
+ gettext("Time in between active block management cycles");
+ gettext("Active Block Modifier interval");
+ gettext("Length of time between ABM execution cycles");
+ gettext("NodeTimer interval");
+ gettext("Length of time between NodeTimer execution cycles");
+ gettext("Ignore world errors");
+ gettext("If enabled, invalid world data won't cause the server to shut down.\nOnly enable this if you know what you are doing.");
+ gettext("Liquid loop max");
+ gettext("Max liquids processed per step.");
+ gettext("Liquid queue purge time");
+ gettext("The time (in seconds) that the liquids queue may grow beyond processing\ncapacity until an attempt is made to decrease its size by dumping old queue\nitems. A value of 0 disables the functionality.");
+ gettext("Liquid update tick");
+ gettext("Liquid update interval in seconds.");
+ gettext("Mapgen");
+ gettext("Mapgen name");
+ gettext("Name of map generator to be used when creating a new world.\nCreating a world in the main menu will override this.");
+ gettext("Water level");
+ gettext("Water surface level of the world.");
+ gettext("Max block generate distance");
+ gettext("From how far blocks are generated for clients, stated in mapblocks (16 nodes).");
+ gettext("Map generation limit");
+ gettext("Where the map generator stops.\nPlease note:\n- Limited to 31000 (setting above has no effect)\n- The map generator works in groups of 80x80x80 nodes (5x5x5 MapBlocks).\n- Those groups have an offset of -32, -32 nodes from the origin.\n- Only groups which are within the map_generation_limit are generated");
+ gettext("Mapgen flags");
+ gettext("Global map generation attributes.\nIn Mapgen v6 the 'decorations' flag controls all decorations except trees\nand junglegrass, in all other mapgens this flag controls all decorations.\nThe default flags set in the engine are: caves, light, decorations\nThe flags string modifies the engine defaults.\nFlags that are not specified in the flag string are not modified from the default.\nFlags starting with 'no' are used to explicitly disable them.");
+ gettext("Advanced");
+ gettext("Chunk size");
+ gettext("Size of chunks to be generated at once by mapgen, stated in mapblocks (16 nodes).");
+ gettext("Mapgen debug");
+ gettext("Dump the mapgen debug infos.");
+ gettext("Absolute limit of emerge queues");
+ gettext("Maximum number of blocks that can be queued for loading.");
+ gettext("Limit of emerge queues on disk");
+ gettext("Maximum number of blocks to be queued that are to be loaded from file.\nSet to blank for an appropriate amount to be chosen automatically.");
+ gettext("Limit of emerge queues to generate");
+ gettext("Maximum number of blocks to be queued that are to be generated.\nSet to blank for an appropriate amount to be chosen automatically.");
+ gettext("Number of emerge threads");
+ gettext("Number of emerge threads to use. Make this field blank, or increase this number\nto use multiple threads. On multiprocessor systems, this will improve mapgen speed greatly\nat the cost of slightly buggy caves.");
+ gettext("Mapgen biome heat noise parameters");
+ gettext("Noise parameters for biome API temperature, humidity and biome blend.");
+ gettext("Mapgen heat blend noise parameters");
+ gettext("Mapgen biome humidity noise parameters");
+ gettext("Mapgen biome humidity blend noise parameters");
+ gettext("Mapgen v5");
+ gettext("Mapgen v5 cave width");
+ gettext("Controls width of tunnels, a smaller value creates wider tunnels.");
+ gettext("Mapgen v5 filler depth noise parameters");
+ gettext("Mapgen v5 factor noise parameters");
+ gettext("Mapgen v5 height noise parameters");
+ gettext("Mapgen v5 cave1 noise parameters");
+ gettext("Mapgen v5 cave2 noise parameters");
+ gettext("Mapgen v6");
+ gettext("Mapgen v6 flags");
+ gettext("Map generation attributes specific to Mapgen v6.\nWhen snowbiomes are enabled jungles are automatically enabled, the 'jungles' flag is ignored.\nThe default flags set in the engine are: biomeblend, mudflow\nThe flags string modifies the engine defaults.\nFlags that are not specified in the flag string are not modified from the default.\nFlags starting with 'no' are used to explicitly disable them.");
+ gettext("Mapgen v6 desert frequency");
+ gettext("Controls size of deserts and beaches in Mapgen v6.\nWhen snowbiomes are enabled 'mgv6_freq_desert' is ignored.");
+ gettext("Mapgen v6 beach frequency");
+ gettext("Mapgen v6 terrain base noise parameters");
+ gettext("Mapgen v6 terrain altitude noise parameters");
+ gettext("Mapgen v6 steepness noise parameters");
+ gettext("Mapgen v6 height select noise parameters");
+ gettext("Mapgen v6 mud noise parameters");
+ gettext("Mapgen v6 beach noise parameters");
+ gettext("Mapgen v6 biome noise parameters");
+ gettext("Mapgen v6 cave noise parameters");
+ gettext("Mapgen v6 humidity noise parameters");
+ gettext("Mapgen v6 trees noise parameters");
+ gettext("Mapgen v6 apple trees noise parameters");
+ gettext("Mapgen v7");
+ gettext("Mapgen v7 flags");
+ gettext("Map generation attributes specific to Mapgen v7.\nThe 'ridges' flag controls the rivers.\nThe default flags set in the engine are: mountains, ridges\nThe flags string modifies the engine defaults.\nFlags that are not specified in the flag string are not modified from the default.\nFlags starting with 'no' are used to explicitly disable them.");
+ gettext("Mapgen v7 cave width");
+ gettext("Controls width of tunnels, a smaller value creates wider tunnels.");
+ gettext("Mapgen v7 terrain base noise parameters");
+ gettext("Mapgen v7 terrain altitude noise parameters");
+ gettext("Mapgen v7 terrain persistation noise parameters");
+ gettext("Mapgen v7 height select noise parameters");
+ gettext("Mapgen v7 filler depth noise parameters");
+ gettext("Mapgen v7 mount height noise parameters");
+ gettext("Mapgen v7 ridge water noise parameters");
+ gettext("Mapgen v7 mountain noise parameters");
+ gettext("Mapgen v7 ridge noise parameters");
+ gettext("Mapgen v7 cave1 noise parameters");
+ gettext("Mapgen v7 cave2 noise parameters");
+ gettext("Mapgen flat");
+ gettext("Mapgen flat flags");
+ gettext("Map generation attributes specific to Mapgen flat.\nOccasional lakes and hills can be added to the flat world.\nThe default flags set in the engine are: none\nThe flags string modifies the engine defaults.\nFlags that are not specified in the flag string are not modified from the default.\nFlags starting with 'no' are used to explicitly disable them.");
+ gettext("Mapgen flat ground level");
+ gettext("Y of flat ground.");
+ gettext("Mapgen flat large cave depth");
+ gettext("Y of upper limit of large pseudorandom caves.");
+ gettext("Mapgen flat cave width");
+ gettext("Controls width of tunnels, a smaller value creates wider tunnels.");
+ gettext("Mapgen flat lake threshold");
+ gettext("Terrain noise threshold for lakes.\nControls proportion of world area covered by lakes.\nAdjust towards 0.0 for a larger proportion.");
+ gettext("Mapgen flat lake steepness");
+ gettext("Controls steepness/depth of lake depressions.");
+ gettext("Mapgen flat hill threshold");
+ gettext("Terrain noise threshold for hills.\nControls proportion of world area covered by hills.\nAdjust towards 0.0 for a larger proportion.");
+ gettext("Mapgen flat hill steepness");
+ gettext("Controls steepness/height of hills.");
+ gettext("Mapgen flat terrain noise parameters");
+ gettext("Determines terrain shape.\nThe 3 numbers in brackets control the scale of the\nterrain, the 3 numbers should be identical.");
+ gettext("Mapgen flat filler depth noise parameters");
+ gettext("Mapgen flat cave1 noise parameters");
+ gettext("Mapgen flat cave2 noise parameters");
+ gettext("Mapgen fractal");
+ gettext("Mapgen fractal cave width");
+ gettext("Controls width of tunnels, a smaller value creates wider tunnels.");
+ gettext("Mapgen fractal fractal");
+ gettext("Choice of 18 fractals from 9 formulas.\n1 = 4D \"Roundy\" mandelbrot set.\n2 = 4D \"Roundy\" julia set.\n3 = 4D \"Squarry\" mandelbrot set.\n4 = 4D \"Squarry\" julia set.\n5 = 4D \"Mandy Cousin\" mandelbrot set.\n6 = 4D \"Mandy Cousin\" julia set.\n7 = 4D \"Variation\" mandelbrot set.\n8 = 4D \"Variation\" julia set.\n9 = 3D \"Mandelbrot/Mandelbar\" mandelbrot set.\n10 = 3D \"Mandelbrot/Mandelbar\" julia set.\n11 = 3D \"Christmas Tree\" mandelbrot set.\n12 = 3D \"Christmas Tree\" julia set.\n13 = 3D \"Mandelbulb\" mandelbrot set.\n14 = 3D \"Mandelbulb\" julia set.\n15 = 3D \"Cosine Mandelbulb\" mandelbrot set.\n16 = 3D \"Cosine Mandelbulb\" julia set.\n17 = 4D \"Mandelbulb\" mandelbrot set.\n18 = 4D \"Mandelbulb\" julia set.");
+ gettext("Mapgen fractal iterations");
+ gettext("Iterations of the recursive function.\nControls the amount of fine detail.");
+ gettext("Mapgen fractal scale");
+ gettext("Approximate (X,Y,Z) scale of fractal in nodes.");
+ gettext("Mapgen fractal offset");
+ gettext("(X,Y,Z) offset of fractal from world centre in units of 'scale'.\nUsed to move a suitable spawn area of low land close to (0, 0).\nThe default is suitable for mandelbrot sets, it needs to be edited for julia sets.\nRange roughly -2 to 2. Multiply by 'scale' for offset in nodes.");
+ gettext("Mapgen fractal slice w");
+ gettext("W co-ordinate of the generated 3D slice of a 4D fractal.\nDetermines which 3D slice of the 4D shape is generated.\nHas no effect on 3D fractals.\nRange roughly -2 to 2.");
+ gettext("Mapgen fractal julia x");
+ gettext("Julia set only: X component of hypercomplex constant determining julia shape.\nRange roughly -2 to 2.");
+ gettext("Mapgen fractal julia y");
+ gettext("Julia set only: Y component of hypercomplex constant determining julia shape.\nRange roughly -2 to 2.");
+ gettext("Mapgen fractal julia z");
+ gettext("Julia set only: Z component of hypercomplex constant determining julia shape.\nRange roughly -2 to 2.");
+ gettext("Mapgen fractal julia w");
+ gettext("Julia set only: W component of hypercomplex constant determining julia shape.\nHas no effect on 3D fractals.\nRange roughly -2 to 2.");
+ gettext("Mapgen fractal seabed noise parameters");
+ gettext("Mapgen fractal filler depth noise parameters");
+ gettext("Mapgen fractal cave1 noise parameters");
+ gettext("Mapgen fractal cave2 noise parameters");
+ gettext("Mapgen Valleys");
+ gettext("General");
+ gettext("Valleys C Flags");
+ gettext("Map generation attributes specific to Mapgen Valleys.\n'altitude_chill' makes higher elevations colder, which may cause biome issues.\n'humid_rivers' modifies the humidity around rivers and in areas where water would tend to pool,\nit may interfere with delicately adjusted biomes.\nThe default flags set in the engine are: altitude_chill, humid_rivers\nThe flags string modifies the engine defaults.\nFlags that are not specified in the flag string are not modified from the default.\nFlags starting with 'no' are used to explicitly disable them.");
+ gettext("Altitude Chill");
+ gettext("The altitude at which temperature drops by 20C");
+ gettext("Large cave depth");
+ gettext("Depth below which you'll find large caves.");
+ gettext("Lava Features");
+ gettext("Creates unpredictable lava features in caves.\nThese can make mining difficult. Zero disables them. (0-10)");
+ gettext("Massive cave depth");
+ gettext("Depth below which you'll find massive caves.");
+ gettext("River Depth");
+ gettext("How deep to make rivers");
+ gettext("River Size");
+ gettext("How wide to make rivers");
+ gettext("Water Features");
+ gettext("Creates unpredictable water features in caves.\nThese can make mining difficult. Zero disables them. (0-10)");
+ gettext("Cave width");
+ gettext("Controls width of tunnels, a smaller value creates wider tunnels.");
+ gettext("Noises");
+ gettext("Cave noise #1");
+ gettext("Caves and tunnels form at the intersection of the two noises");
+ gettext("Cave noise #2");
+ gettext("Caves and tunnels form at the intersection of the two noises");
+ gettext("Filler Depth");
+ gettext("The depth of dirt or other filler");
+ gettext("Massive cave noise");
+ gettext("Massive caves form here.");
+ gettext("River Noise");
+ gettext("River noise -- rivers occur close to zero");
+ gettext("Terrain Height");
+ gettext("Base terrain height");
+ gettext("Valley Depth");
+ gettext("Raises terrain to make valleys around the rivers");
+ gettext("Valley Fill");
+ gettext("Slope and fill work together to modify the heights");
+ gettext("Valley Profile");
+ gettext("Amplifies the valleys");
+ gettext("Valley Slope");
+ gettext("Slope and fill work together to modify the heights");
+ gettext("Security");
+ gettext("Enable mod security");
+ gettext("Prevent mods from doing insecure things like running shell commands.");
+ gettext("Trusted mods");
+ gettext("Comma-separated list of trusted mods that are allowed to access insecure\nfunctions even when mod security is on (via request_insecure_environment()).");
+ gettext("HTTP Mods");
+ gettext("Comma-seperated list of mods that are allowed to access HTTP APIs, which\nallow them to upload and download data to/from the internet.");
+ gettext("Client and Server");
+ gettext("Player name");
+ gettext("Name of the player.\nWhen running a server, clients connecting with this name are admins.\nWhen starting from the main menu, this is overridden.");
+ gettext("Language");
+ gettext("Set the language. Leave empty to use the system language.\nA restart is required after changing this.");
+ gettext("Debug log level");
+ gettext("Level of logging to be written to debug.txt:\n- <nothing> (no logging)\n- none (messages with no level)\n- error\n- warning\n- action\n- info\n- verbose");
+ gettext("IPv6");
+ gettext("IPv6 support.");
+ gettext("Advanced");
+ gettext("cURL timeout");
+ gettext("Default timeout for cURL, stated in milliseconds.\nOnly has an effect if compiled with cURL.");
+ gettext("cURL parallel limit");
+ gettext("Limits number of parallel HTTP requests. Affects:\n- Media fetch if server uses remote_media setting.\n- Serverlist download and server announcement.\n- Downloads performed by main menu (e.g. mod manager).\nOnly has an effect if compiled with cURL.");
+ gettext("cURL file download timeout");
+ gettext("Maximum time in ms a file download (e.g. a mod download) may take.");
+ gettext("High-precision FPU");
+ gettext("Makes DirectX work with LuaJIT. Disable if it causes troubles.");
+ gettext("Main menu script");
+ gettext("Replaces the default main menu with a custom one.");
+ gettext("Main menu game manager");
+ gettext("Main menu mod manager");
+ gettext("Modstore download URL");
+ gettext("Modstore mods list URL");
+ gettext("Modstore details URL");
+}
diff --git a/src/shader.cpp b/src/shader.cpp
index 7e4f40810..e13ab8df3 100644
--- a/src/shader.cpp
+++ b/src/shader.cpp
@@ -35,7 +35,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "EShaderTypes.h"
#include "log.h"
#include "gamedef.h"
-#include "strfnd.h" // trim()
#include "client/tile.h"
/*
@@ -328,7 +327,7 @@ private:
// The first position contains a dummy shader.
std::vector<ShaderInfo> m_shaderinfo_cache;
// The former container is behind this mutex
- JMutex m_shaderinfo_cache_mutex;
+ Mutex m_shaderinfo_cache_mutex;
// Queued shader fetches (to be processed by the main thread)
RequestQueue<std::string, u32, u8, u8> m_get_shader_queue;
@@ -367,7 +366,7 @@ ShaderSource::ShaderSource(IrrlichtDevice *device):
m_shader_callback = new ShaderCallback(this, "default");
- m_main_thread = get_current_thread_id();
+ m_main_thread = thr_get_current_thread_id();
// Add a dummy ShaderInfo as the first index, named ""
m_shaderinfo_cache.push_back(ShaderInfo());
@@ -379,7 +378,7 @@ ShaderSource::ShaderSource(IrrlichtDevice *device):
ShaderSource::~ShaderSource()
{
for (std::vector<IShaderConstantSetter*>::iterator iter = m_global_setters.begin();
- iter != m_global_setters.end(); iter++) {
+ iter != m_global_setters.end(); ++iter) {
delete *iter;
}
m_global_setters.clear();
@@ -397,7 +396,7 @@ u32 ShaderSource::getShader(const std::string &name,
Get shader
*/
- if(get_current_thread_id() == m_main_thread){
+ if (thr_is_current_thread(m_main_thread)) {
return getShaderIdDirect(name, material_type, drawtype);
} else {
/*errorstream<<"getShader(): Queued: name=\""<<name<<"\""<<std::endl;*/
@@ -456,7 +455,7 @@ u32 ShaderSource::getShaderIdDirect(const std::string &name,
/*
Calling only allowed from main thread
*/
- if(get_current_thread_id() != m_main_thread){
+ if (!thr_is_current_thread(m_main_thread)) {
errorstream<<"ShaderSource::getShaderIdDirect() "
"called not from main thread"<<std::endl;
return 0;
@@ -469,7 +468,7 @@ u32 ShaderSource::getShaderIdDirect(const std::string &name,
Add shader to caches (add dummy shaders too)
*/
- JMutexAutoLock lock(m_shaderinfo_cache_mutex);
+ MutexAutoLock lock(m_shaderinfo_cache_mutex);
u32 id = m_shaderinfo_cache.size();
m_shaderinfo_cache.push_back(info);
@@ -483,7 +482,7 @@ u32 ShaderSource::getShaderIdDirect(const std::string &name,
ShaderInfo ShaderSource::getShaderInfo(u32 id)
{
- JMutexAutoLock lock(m_shaderinfo_cache_mutex);
+ MutexAutoLock lock(m_shaderinfo_cache_mutex);
if(id >= m_shaderinfo_cache.size())
return ShaderInfo();
@@ -504,14 +503,14 @@ void ShaderSource::insertSourceShader(const std::string &name_of_shader,
"name_of_shader=\""<<name_of_shader<<"\", "
"filename=\""<<filename<<"\""<<std::endl;*/
- sanity_check(get_current_thread_id() == m_main_thread);
+ sanity_check(thr_is_current_thread(m_main_thread));
m_sourcecache.insert(name_of_shader, filename, program, true);
}
void ShaderSource::rebuildShaders()
{
- JMutexAutoLock lock(m_shaderinfo_cache_mutex);
+ MutexAutoLock lock(m_shaderinfo_cache_mutex);
/*// Oh well... just clear everything, they'll load sometime.
m_shaderinfo_cache.clear();
@@ -764,22 +763,25 @@ ShaderInfo generate_shader(std::string name, u8 material_type, u8 drawtype,
else
shaders_header += "0\n";
- if(pixel_program != "")
- pixel_program = shaders_header + pixel_program;
- if(vertex_program != "")
- vertex_program = shaders_header + vertex_program;
- if(geometry_program != "")
- geometry_program = shaders_header + geometry_program;
+ if (g_settings->getBool("tone_mapping"))
+ shaders_header += "#define ENABLE_TONE_MAPPING\n";
+
// Call addHighLevelShaderMaterial() or addShaderMaterial()
const c8* vertex_program_ptr = 0;
const c8* pixel_program_ptr = 0;
const c8* geometry_program_ptr = 0;
- if(vertex_program != "")
+ if (!vertex_program.empty()) {
+ vertex_program = shaders_header + vertex_program;
vertex_program_ptr = vertex_program.c_str();
- if(pixel_program != "")
+ }
+ if (!pixel_program.empty()) {
+ pixel_program = shaders_header + pixel_program;
pixel_program_ptr = pixel_program.c_str();
- if(geometry_program != "")
+ }
+ if (!geometry_program.empty()) {
+ geometry_program = shaders_header + geometry_program;
geometry_program_ptr = geometry_program.c_str();
+ }
s32 shadermat = -1;
if(is_highlevel){
infostream<<"Compiling high level shaders for "<<name<<std::endl;
@@ -789,7 +791,7 @@ ShaderInfo generate_shader(std::string name, u8 material_type, u8 drawtype,
video::EVST_VS_1_1, // Vertex shader version
pixel_program_ptr, // Pixel shader program
"pixelMain", // Pixel shader entry point
- video::EPST_PS_1_1, // Pixel shader version
+ video::EPST_PS_1_2, // Pixel shader version
geometry_program_ptr, // Geometry shader program
"geometryMain", // Geometry shader entry point
video::EGST_GS_4_0, // Geometry shader version
@@ -805,6 +807,9 @@ ShaderInfo generate_shader(std::string name, u8 material_type, u8 drawtype,
"failed to generate \""<<name<<"\", "
"addHighLevelShaderMaterial failed."
<<std::endl;
+ dumpShaderProgram(warningstream, "Vertex", vertex_program);
+ dumpShaderProgram(warningstream, "Pixel", pixel_program);
+ dumpShaderProgram(warningstream, "Geometry", geometry_program);
return shaderinfo;
}
}
@@ -823,6 +828,8 @@ ShaderInfo generate_shader(std::string name, u8 material_type, u8 drawtype,
"failed to generate \""<<name<<"\", "
"addShaderMaterial failed."
<<std::endl;
+ dumpShaderProgram(warningstream, "Vertex", vertex_program);
+ dumpShaderProgram(warningstream,"Pixel", pixel_program);
return shaderinfo;
}
}
@@ -868,3 +875,21 @@ void load_shaders(std::string name, SourceShaderCache *sourcecache,
}
}
+
+void dumpShaderProgram(std::ostream &output_stream,
+ const std::string &program_type, const std::string &program)
+{
+ output_stream << program_type << " shader program:" << std::endl <<
+ "----------------------------------" << std::endl;
+ size_t pos = 0;
+ size_t prev = 0;
+ s16 line = 1;
+ while ((pos = program.find("\n", prev)) != std::string::npos) {
+ output_stream << line++ << ": "<< program.substr(prev, pos - prev) <<
+ std::endl;
+ prev = pos + 1;
+ }
+ output_stream << line << ": " << program.substr(prev) << std::endl <<
+ "End of " << program_type << " shader program." << std::endl <<
+ " " << std::endl;
+}
diff --git a/src/shader.h b/src/shader.h
index 811f33080..b8aa88bce 100644
--- a/src/shader.h
+++ b/src/shader.h
@@ -110,4 +110,7 @@ public:
IWritableShaderSource* createShaderSource(IrrlichtDevice *device);
+void dumpShaderProgram(std::ostream &output_stream,
+ const std::string &program_type, const std::string &program);
+
#endif
diff --git a/src/sky.cpp b/src/sky.cpp
index 01fb8ef86..682ff05e3 100644
--- a/src/sky.cpp
+++ b/src/sky.cpp
@@ -25,8 +25,8 @@ Sky::Sky(scene::ISceneNode* parent, scene::ISceneManager* mgr, s32 id,
m_cloudcolor_bright_f(1,1,1,1)
{
setAutomaticCulling(scene::EAC_OFF);
- Box.MaxEdge.set(0,0,0);
- Box.MinEdge.set(0,0,0);
+ m_box.MaxEdge.set(0,0,0);
+ m_box.MinEdge.set(0,0,0);
// create material
@@ -94,11 +94,6 @@ void Sky::OnRegisterSceneNode()
scene::ISceneNode::OnRegisterSceneNode();
}
-const core::aabbox3d<f32>& Sky::getBoundingBox() const
-{
- return Box;
-}
-
//! renders the node.
void Sky::render()
{
diff --git a/src/sky.h b/src/sky.h
index 5023cc682..f19891773 100644
--- a/src/sky.h
+++ b/src/sky.h
@@ -42,7 +42,8 @@ public:
//! renders the node.
virtual void render();
- virtual const core::aabbox3d<f32>& getBoundingBox() const;
+ virtual const aabb3f &getBoundingBox() const
+ { return m_box; }
// Used by Irrlicht for optimizing rendering
virtual video::SMaterial& getMaterial(u32 i)
@@ -74,7 +75,7 @@ public:
}
private:
- core::aabbox3d<f32> Box;
+ aabb3f m_box;
video::SMaterial m_materials[SKY_MATERIAL_COUNT];
// How much sun & moon transition should affect horizon color
diff --git a/src/socket.cpp b/src/socket.cpp
index e82052f77..17fa1924d 100644
--- a/src/socket.cpp
+++ b/src/socket.cpp
@@ -44,8 +44,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
-typedef SOCKET socket_t;
-typedef int socklen_t;
+ #define LAST_SOCKET_ERR() WSAGetLastError()
+ typedef SOCKET socket_t;
+ typedef int socklen_t;
#else
#include <sys/types.h>
#include <sys/socket.h>
@@ -54,7 +55,8 @@ typedef int socklen_t;
#include <netdb.h>
#include <unistd.h>
#include <arpa/inet.h>
-typedef int socket_t;
+ #define LAST_SOCKET_ERR() (errno)
+ typedef int socket_t;
#endif
// Set to true to enable verbose debug output
@@ -339,7 +341,8 @@ bool UDPSocket::init(bool ipv6, bool noExceptions)
if (noExceptions) {
return false;
} else {
- throw SocketException("Failed to create socket");
+ throw SocketException(std::string("Failed to create socket: error ")
+ + itos(LAST_SOCKET_ERR()));
}
}
@@ -571,9 +574,8 @@ bool UDPSocket::WaitData(int timeout_ms)
int e = WSAGetLastError();
dstream << (int) m_handle << ": WSAGetLastError()="
<< e << std::endl;
- if(e == 10004 /* = WSAEINTR */ || e == 10009 /*WSAEBADF*/)
- {
- dstream << "WARNING: Ignoring WSAEINTR/WSAEBADF." << std::endl;
+ if (e == 10004 /* WSAEINTR */ || e == 10009 /* WSAEBADF */) {
+ infostream << "Ignoring WSAEINTR/WSAEBADF." << std::endl;
return false;
}
#endif
diff --git a/src/socket.h b/src/socket.h
index c7dd78f66..8d1ad70ff 100644
--- a/src/socket.h
+++ b/src/socket.h
@@ -45,7 +45,7 @@ extern bool socket_enable_debug_output;
class SocketException : public BaseException
{
public:
- SocketException(const char *s):
+ SocketException(const std::string &s):
BaseException(s)
{
}
@@ -54,7 +54,7 @@ public:
class ResolveError : public BaseException
{
public:
- ResolveError(const char *s):
+ ResolveError(const std::string &s):
BaseException(s)
{
}
@@ -63,7 +63,7 @@ public:
class SendFailedException : public BaseException
{
public:
- SendFailedException(const char *s):
+ SendFailedException(const std::string &s):
BaseException(s)
{
}
diff --git a/src/sound_openal.cpp b/src/sound_openal.cpp
index cb4c7b581..e2b6d937a 100644
--- a/src/sound_openal.cpp
+++ b/src/sound_openal.cpp
@@ -39,7 +39,6 @@ with this program; ifnot, write to the Free Software Foundation, Inc.,
#include <vorbis/vorbisfile.h>
#include <assert.h>
#include "log.h"
-#include "filesys.h"
#include "util/numeric.h" // myrand()
#include "porting.h"
#include <map>
@@ -92,7 +91,7 @@ static ALenum warn_if_error(ALenum err, const char *desc)
{
if(err == AL_NO_ERROR)
return err;
- errorstream<<"WARNING: "<<desc<<": "<<alErrorString(err)<<std::endl;
+ warningstream<<desc<<": "<<alErrorString(err)<<std::endl;
return err;
}
@@ -111,31 +110,19 @@ struct SoundBuffer
std::vector<char> buffer;
};
-SoundBuffer* loadOggFile(const std::string &filepath)
+SoundBuffer *load_opened_ogg_file(OggVorbis_File *oggFile,
+ const std::string &filename_for_logging)
{
int endian = 0; // 0 for Little-Endian, 1 for Big-Endian
int bitStream;
long bytes;
char array[BUFFER_SIZE]; // Local fixed size array
vorbis_info *pInfo;
- OggVorbis_File oggFile;
-
- // Do a dumb-ass static string copy for old versions of ov_fopen
- // because they expect a non-const char*
- char nonconst[10000];
- snprintf(nonconst, 10000, "%s", filepath.c_str());
- // Try opening the given file
- //if(ov_fopen(filepath.c_str(), &oggFile) != 0)
- if(ov_fopen(nonconst, &oggFile) != 0)
- {
- infostream<<"Audio: Error opening "<<filepath<<" for decoding"<<std::endl;
- return NULL;
- }
SoundBuffer *snd = new SoundBuffer;
// Get some information about the OGG file
- pInfo = ov_info(&oggFile, -1);
+ pInfo = ov_info(oggFile, -1);
// Check the number of channels... always use 16-bit samples
if(pInfo->channels == 1)
@@ -150,12 +137,13 @@ SoundBuffer* loadOggFile(const std::string &filepath)
do
{
// Read up to a buffer's worth of decoded sound data
- bytes = ov_read(&oggFile, array, BUFFER_SIZE, endian, 2, 1, &bitStream);
+ bytes = ov_read(oggFile, array, BUFFER_SIZE, endian, 2, 1, &bitStream);
if(bytes < 0)
{
- ov_clear(&oggFile);
- infostream<<"Audio: Error decoding "<<filepath<<std::endl;
+ ov_clear(oggFile);
+ infostream << "Audio: Error decoding "
+ << filename_for_logging << std::endl;
return NULL;
}
@@ -175,14 +163,100 @@ SoundBuffer* loadOggFile(const std::string &filepath)
<<"preparing sound buffer"<<std::endl;
}
- infostream<<"Audio file "<<filepath<<" loaded"<<std::endl;
+ infostream << "Audio file "
+ << filename_for_logging << " loaded" << std::endl;
// Clean up!
- ov_clear(&oggFile);
+ ov_clear(oggFile);
return snd;
}
+SoundBuffer *load_ogg_from_file(const std::string &path)
+{
+ OggVorbis_File oggFile;
+
+ // Try opening the given file.
+ // This requires libvorbis >= 1.3.2, as
+ // previous versions expect a non-const char *
+ if (ov_fopen(path.c_str(), &oggFile) != 0) {
+ infostream << "Audio: Error opening " << path
+ << " for decoding" << std::endl;
+ return NULL;
+ }
+
+ return load_opened_ogg_file(&oggFile, path);
+}
+
+struct BufferSource {
+ const char *buf;
+ size_t cur_offset;
+ size_t len;
+};
+
+size_t buffer_sound_read_func(void *ptr, size_t size, size_t nmemb, void *datasource)
+{
+ BufferSource *s = (BufferSource *)datasource;
+ size_t copied_size = MYMIN(s->len - s->cur_offset, size);
+ memcpy(ptr, s->buf + s->cur_offset, copied_size);
+ s->cur_offset += copied_size;
+ return copied_size;
+}
+
+int buffer_sound_seek_func(void *datasource, ogg_int64_t offset, int whence)
+{
+ BufferSource *s = (BufferSource *)datasource;
+ if (whence == SEEK_SET) {
+ if (offset < 0 || (size_t)MYMAX(offset, 0) >= s->len) {
+ // offset out of bounds
+ return -1;
+ }
+ s->cur_offset = offset;
+ return 0;
+ } else if (whence == SEEK_CUR) {
+ if ((size_t)MYMIN(-offset, 0) > s->cur_offset
+ || s->cur_offset + offset > s->len) {
+ // offset out of bounds
+ return -1;
+ }
+ s->cur_offset += offset;
+ return 0;
+ }
+ // invalid whence param (SEEK_END doesn't have to be supported)
+ return -1;
+}
+
+long BufferSourceell_func(void *datasource)
+{
+ BufferSource *s = (BufferSource *)datasource;
+ return s->cur_offset;
+}
+
+static ov_callbacks g_buffer_ov_callbacks = {
+ &buffer_sound_read_func,
+ &buffer_sound_seek_func,
+ NULL,
+ &BufferSourceell_func
+};
+
+SoundBuffer *load_ogg_from_buffer(const std::string &buf, const std::string &id_for_log)
+{
+ OggVorbis_File oggFile;
+
+ BufferSource s;
+ s.buf = buf.c_str();
+ s.cur_offset = 0;
+ s.len = buf.size();
+
+ if (ov_open_callbacks(&s, &oggFile, NULL, 0, g_buffer_ov_callbacks) != 0) {
+ infostream << "Audio: Error opening " << id_for_log
+ << " for decoding" << std::endl;
+ return NULL;
+ }
+
+ return load_opened_ogg_file(&oggFile, id_for_log);
+}
+
struct PlayingSound
{
ALuint source_id;
@@ -195,7 +269,6 @@ private:
OnDemandSoundFetcher *m_fetcher;
ALCdevice *m_device;
ALCcontext *m_context;
- bool m_can_vorbis;
int m_next_id;
std::map<std::string, std::vector<SoundBuffer*> > m_buffers;
std::map<int, PlayingSound*> m_sounds_playing;
@@ -206,12 +279,11 @@ public:
m_fetcher(fetcher),
m_device(NULL),
m_context(NULL),
- m_can_vorbis(false),
m_next_id(1),
m_is_initialized(false)
{
ALCenum error = ALC_NO_ERROR;
-
+
infostream<<"Audio: Initializing..."<<std::endl;
m_device = alcOpenDevice(NULL);
@@ -221,14 +293,6 @@ public:
return;
}
- if(alcIsExtensionPresent(m_device, "EXT_vorbis")){
- infostream<<"Audio: Vorbis extension present"<<std::endl;
- m_can_vorbis = true;
- } else{
- infostream<<"Audio: Vorbis extension NOT present"<<std::endl;
- m_can_vorbis = false;
- }
-
m_context = alcCreateContext(m_device, NULL);
if(!m_context){
error = alcGetError(m_device);
@@ -273,9 +337,9 @@ public:
m_device = NULL;
for (std::map<std::string, std::vector<SoundBuffer*> >::iterator i = m_buffers.begin();
- i != m_buffers.end(); i++) {
+ i != m_buffers.end(); ++i) {
for (std::vector<SoundBuffer*>::iterator iter = (*i).second.begin();
- iter != (*i).second.end(); iter++) {
+ iter != (*i).second.end(); ++iter) {
delete *iter;
}
(*i).second.clear();
@@ -283,7 +347,7 @@ public:
m_buffers.clear();
infostream<<"Audio: Deinitialized."<<std::endl;
}
-
+
void addBuffer(const std::string &name, SoundBuffer *buf)
{
std::map<std::string, std::vector<SoundBuffer*> >::iterator i =
@@ -375,7 +439,7 @@ public:
m_sounds_playing[id] = sound;
return id;
}
-
+
void deleteSound(int id)
{
std::map<int, PlayingSound*>::iterator i =
@@ -383,7 +447,7 @@ public:
if(i == m_sounds_playing.end())
return;
PlayingSound *sound = i->second;
-
+
alDeleteSources(1, &sound->source_id);
delete sound;
@@ -402,16 +466,16 @@ public:
std::set<std::string> datas;
m_fetcher->fetchSounds(name, paths, datas);
for(std::set<std::string>::iterator i = paths.begin();
- i != paths.end(); i++){
+ i != paths.end(); ++i){
loadSoundFile(name, *i);
}
for(std::set<std::string>::iterator i = datas.begin();
- i != datas.end(); i++){
+ i != datas.end(); ++i){
loadSoundData(name, *i);
}
return getBuffer(name);
}
-
+
// Remove stopped sounds
void maintain()
{
@@ -421,7 +485,7 @@ public:
std::set<int> del_list;
for(std::map<int, PlayingSound*>::iterator
i = m_sounds_playing.begin();
- i != m_sounds_playing.end(); i++)
+ i != m_sounds_playing.end(); ++i)
{
int id = i->first;
PlayingSound *sound = i->second;
@@ -438,7 +502,7 @@ public:
verbosestream<<"OpenALSoundManager::maintain(): deleting "
<<del_list.size()<<" playing sounds"<<std::endl;
for(std::set<int>::iterator i = del_list.begin();
- i != del_list.end(); i++)
+ i != del_list.end(); ++i)
{
deleteSound(*i);
}
@@ -449,26 +513,18 @@ public:
bool loadSoundFile(const std::string &name,
const std::string &filepath)
{
- SoundBuffer *buf = loadOggFile(filepath);
- if(buf)
+ SoundBuffer *buf = load_ogg_from_file(filepath);
+ if (buf)
addBuffer(name, buf);
return false;
}
bool loadSoundData(const std::string &name,
const std::string &filedata)
{
- // The vorbis API sucks; just write it to a file and use vorbisfile
- // TODO: Actually load it directly from memory
- std::string basepath = porting::path_user + DIR_DELIM + "cache" +
- DIR_DELIM + "tmp";
- std::string path = basepath + DIR_DELIM + "tmp.ogg";
- verbosestream<<"OpenALSoundManager::loadSoundData(): Writing "
- <<"temporary file to ["<<path<<"]"<<std::endl;
- fs::CreateAllDirs(basepath);
- std::ofstream of(path.c_str(), std::ios::binary);
- of.write(filedata.c_str(), filedata.size());
- of.close();
- return loadSoundFile(name, path);
+ SoundBuffer *buf = load_ogg_from_buffer(filedata, name);
+ if (buf)
+ addBuffer(name, buf);
+ return false;
}
void updateListener(v3f pos, v3f vel, v3f at, v3f up)
@@ -482,7 +538,7 @@ public:
alListenerfv(AL_ORIENTATION, f);
warn_if_error(alGetError(), "updateListener");
}
-
+
void setListenerGain(float gain)
{
alListenerf(AL_GAIN, gain);
diff --git a/src/staticobject.cpp b/src/staticobject.cpp
index 2e7d45a47..d656b6221 100644
--- a/src/staticobject.cpp
+++ b/src/staticobject.cpp
@@ -19,6 +19,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "staticobject.h"
#include "util/serialize.h"
+#include "log.h"
void StaticObject::serialize(std::ostream &os)
{
@@ -44,9 +45,20 @@ void StaticObjectList::serialize(std::ostream &os)
// version
u8 version = 0;
writeU8(os, version);
+
// count
- u16 count = m_stored.size() + m_active.size();
+ size_t count = m_stored.size() + m_active.size();
+ // Make sure it fits into u16, else it would get truncated and cause e.g.
+ // issue #2610 (Invalid block data in database: unsupported NameIdMapping version).
+ if (count > U16_MAX) {
+ errorstream << "StaticObjectList::serialize(): "
+ << "too many objects (" << count << ") in list, "
+ << "not writing them to disk." << std::endl;
+ writeU16(os, 0); // count = 0
+ return;
+ }
writeU16(os, count);
+
for(std::vector<StaticObject>::iterator
i = m_stored.begin();
i != m_stored.end(); ++i) {
diff --git a/src/staticobject.h b/src/staticobject.h
index 95a1b945e..208fb2cc8 100644
--- a/src/staticobject.h
+++ b/src/staticobject.h
@@ -79,7 +79,7 @@ public:
assert(id != 0); // Pre-condition
if(m_active.find(id) == m_active.end())
{
- dstream<<"WARNING: StaticObjectList::remove(): id="<<id
+ warningstream<<"StaticObjectList::remove(): id="<<id
<<" not found"<<std::endl;
return;
}
diff --git a/src/strfnd.h b/src/strfnd.h
deleted file mode 100644
index 3142cc10d..000000000
--- a/src/strfnd.h
+++ /dev/null
@@ -1,176 +0,0 @@
-/*
-Minetest
-Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@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.
-*/
-
-#ifndef STRFND_HEADER
-#define STRFND_HEADER
-
-#include <string>
-
-class Strfnd{
- std::string tek;
- unsigned int p;
-public:
- void start(std::string niinq){
- tek = niinq;
- p=0;
- }
- unsigned int where(){
- return p;
- }
- void to(unsigned int i){
- p = i;
- }
- std::string what(){
- return tek;
- }
- std::string next(std::string plop){
- //std::cout<<"tek=\""<<tek<<"\" plop=\""<<plop<<"\""<<std::endl;
- size_t n;
- std::string palautus;
- if (p < tek.size())
- {
- //std::cout<<"\tp<tek.size()"<<std::endl;
- if ((n = tek.find(plop, p)) == std::string::npos || plop == "")
- {
- //std::cout<<"\t\tn == string::npos || plop == \"\""<<std::endl;
- n = tek.size();
- }
- else
- {
- //std::cout<<"\t\tn != string::npos"<<std::endl;
- }
- palautus = tek.substr(p, n-p);
- p = n + plop.length();
- }
- //else
- //std::cout<<"\tp>=tek.size()"<<std::endl;
- //std::cout<<"palautus=\""<<palautus<<"\""<<std::endl;
- return palautus;
- }
-
- // Returns substr of tek up to the next occurence of plop that isn't escaped with '\'
- std::string next_esc(std::string plop) {
- size_t n, realp;
-
- if (p >= tek.size())
- return "";
-
- realp = p;
- do {
- n = tek.find(plop, p);
- if (n == std::string::npos || plop == "")
- n = tek.length();
- p = n + plop.length();
- } while (n > 0 && tek[n - 1] == '\\');
-
- return tek.substr(realp, n - realp);
- }
-
- void skip_over(std::string chars){
- while(p < tek.size()){
- bool is = false;
- for(unsigned int i=0; i<chars.size(); i++){
- if(chars[i] == tek[p]){
- is = true;
- break;
- }
- }
- if(!is) break;
- p++;
- }
- }
- bool atend(){
- if(p>=tek.size()) return true;
- return false;
- }
- Strfnd(std::string s){
- start(s);
- }
-};
-
-class WStrfnd{
- std::wstring tek;
- unsigned int p;
-public:
- void start(std::wstring niinq){
- tek = niinq;
- p=0;
- }
- unsigned int where(){
- return p;
- }
- void to(unsigned int i){
- p = i;
- }
- std::wstring what(){
- return tek;
- }
- std::wstring next(std::wstring plop){
- //std::cout<<"tek=\""<<tek<<"\" plop=\""<<plop<<"\""<<std::endl;
- size_t n;
- std::wstring palautus;
- if (p < tek.size())
- {
- //std::cout<<"\tp<tek.size()"<<std::endl;
- if ((n = tek.find(plop, p)) == std::wstring::npos || plop == L"")
- {
- //std::cout<<"\t\tn == string::npos || plop == \"\""<<std::endl;
- n = tek.size();
- }
- else
- {
- //std::cout<<"\t\tn != string::npos"<<std::endl;
- }
- palautus = tek.substr(p, n-p);
- p = n + plop.length();
- }
- //else
- //std::cout<<"\tp>=tek.size()"<<std::endl;
- //std::cout<<"palautus=\""<<palautus<<"\""<<std::endl;
- return palautus;
- }
-
- std::wstring next_esc(std::wstring plop) {
- size_t n, realp;
-
- if (p >= tek.size())
- return L"";
-
- realp = p;
- do {
- n = tek.find(plop, p);
- if (n == std::wstring::npos || plop == L"")
- n = tek.length();
- p = n + plop.length();
- } while (n > 0 && tek[n - 1] == '\\');
-
- return tek.substr(realp, n - realp);
- }
-
- bool atend(){
- if(p>=tek.size()) return true;
- return false;
- }
- WStrfnd(std::wstring s){
- start(s);
- }
-};
-
-#endif
-
diff --git a/src/subgame.cpp b/src/subgame.cpp
index f736a78c6..7e9a0b368 100644
--- a/src/subgame.cpp
+++ b/src/subgame.cpp
@@ -22,7 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "filesys.h"
#include "settings.h"
#include "log.h"
-#include "strfnd.h"
+#include "util/strfnd.h"
#include "defaultsettings.h" // for override_default_settings
#include "mapgen.h" // for MapgenParams
#include "util/string.h"
@@ -63,15 +63,10 @@ struct GameFindPath
{}
};
-Strfnd getSubgamePathEnv() {
- std::string sp;
+std::string getSubgamePathEnv()
+{
char *subgame_path = getenv("MINETEST_SUBGAME_PATH");
-
- if(subgame_path) {
- sp = std::string(subgame_path);
- }
-
- return Strfnd(sp);
+ return subgame_path ? std::string(subgame_path) : "";
}
SubgameSpec findSubgame(const std::string &id)
@@ -82,10 +77,10 @@ SubgameSpec findSubgame(const std::string &id)
std::string user = porting::path_user;
std::vector<GameFindPath> find_paths;
- Strfnd search_paths = getSubgamePathEnv();
+ Strfnd search_paths(getSubgamePathEnv());
- while(!search_paths.atend()) {
- std::string path = search_paths.next(":");
+ while (!search_paths.at_end()) {
+ std::string path = search_paths.next(PATH_DELIM);
find_paths.push_back(GameFindPath(
path + DIR_DELIM + id, false));
find_paths.push_back(GameFindPath(
@@ -156,14 +151,13 @@ std::set<std::string> getAvailableGameIds()
gamespaths.insert(porting::path_share + DIR_DELIM + "games");
gamespaths.insert(porting::path_user + DIR_DELIM + "games");
- Strfnd search_paths = getSubgamePathEnv();
+ Strfnd search_paths(getSubgamePathEnv());
- while(!search_paths.atend()) {
- gamespaths.insert(search_paths.next(":"));
- }
+ while (!search_paths.at_end())
+ gamespaths.insert(search_paths.next(PATH_DELIM));
- for(std::set<std::string>::const_iterator i = gamespaths.begin();
- i != gamespaths.end(); i++){
+ for (std::set<std::string>::const_iterator i = gamespaths.begin();
+ i != gamespaths.end(); ++i){
std::vector<fs::DirListNode> dirlist = fs::GetDirListing(*i);
for(u32 j=0; j<dirlist.size(); j++){
if(!dirlist[j].dir)
@@ -189,7 +183,7 @@ std::vector<SubgameSpec> getAvailableGames()
std::vector<SubgameSpec> specs;
std::set<std::string> gameids = getAvailableGameIds();
for(std::set<std::string>::const_iterator i = gameids.begin();
- i != gameids.end(); i++)
+ i != gameids.end(); ++i)
specs.push_back(findSubgame(*i));
return specs;
}
@@ -223,15 +217,27 @@ std::string getWorldGameId(const std::string &world_path, bool can_be_legacy)
return conf.get("gameid");
}
+std::string getWorldPathEnv()
+{
+ char *world_path = getenv("MINETEST_WORLD_PATH");
+ return world_path ? std::string(world_path) : "";
+}
+
std::vector<WorldSpec> getAvailableWorlds()
{
std::vector<WorldSpec> worlds;
std::set<std::string> worldspaths;
+
+ Strfnd search_paths(getWorldPathEnv());
+
+ while (!search_paths.at_end())
+ worldspaths.insert(search_paths.next(PATH_DELIM));
+
worldspaths.insert(porting::path_user + DIR_DELIM + "worlds");
- infostream<<"Searching worlds..."<<std::endl;
- for(std::set<std::string>::const_iterator i = worldspaths.begin();
- i != worldspaths.end(); i++){
- infostream<<" In "<<(*i)<<": "<<std::endl;
+ infostream << "Searching worlds..." << std::endl;
+ for (std::set<std::string>::const_iterator i = worldspaths.begin();
+ i != worldspaths.end(); ++i) {
+ infostream << " In " << (*i) << ": " <<std::endl;
std::vector<fs::DirListNode> dirvector = fs::GetDirListing(*i);
for(u32 j=0; j<dirvector.size(); j++){
if(!dirvector[j].dir)
diff --git a/src/terminal_chat_console.cpp b/src/terminal_chat_console.cpp
new file mode 100644
index 000000000..c86a960fa
--- /dev/null
+++ b/src/terminal_chat_console.cpp
@@ -0,0 +1,453 @@
+/*
+Minetest
+Copyright (C) 2015 est31 <MTest31@outlook.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 "config.h"
+#if USE_CURSES
+#include "version.h"
+#include "terminal_chat_console.h"
+#include "porting.h"
+#include "settings.h"
+#include "util/numeric.h"
+#include "util/string.h"
+
+TerminalChatConsole g_term_console;
+
+// include this last to avoid any conflicts
+// (likes to set macros to common names, conflicting various stuff)
+#if CURSES_HAVE_NCURSESW_NCURSES_H
+#include <ncursesw/ncurses.h>
+#elif CURSES_HAVE_NCURSESW_CURSES_H
+#include <ncursesw/curses.h>
+#elif CURSES_HAVE_CURSES_H
+#include <curses.h>
+#elif CURSES_HAVE_NCURSES_H
+#include <ncurses.h>
+#elif CURSES_HAVE_NCURSES_NCURSES_H
+#include <ncurses/ncurses.h>
+#elif CURSES_HAVE_NCURSES_CURSES_H
+#include <ncurses/curses.h>
+#endif
+
+// Some functions to make drawing etc position independent
+static bool reformat_backend(ChatBackend *backend, int rows, int cols)
+{
+ if (rows < 2)
+ return false;
+ backend->reformat(cols, rows - 2);
+ return true;
+}
+
+static void move_for_backend(int row, int col)
+{
+ move(row + 1, col);
+}
+
+void TerminalChatConsole::initOfCurses()
+{
+ initscr();
+ cbreak(); //raw();
+ noecho();
+ keypad(stdscr, TRUE);
+ nodelay(stdscr, TRUE);
+ timeout(100);
+
+ // To make esc not delay up to one second. According to the internet,
+ // this is the value vim uses, too.
+ set_escdelay(25);
+
+ getmaxyx(stdscr, m_rows, m_cols);
+ m_can_draw_text = reformat_backend(&m_chat_backend, m_rows, m_cols);
+}
+
+void TerminalChatConsole::deInitOfCurses()
+{
+ endwin();
+}
+
+void *TerminalChatConsole::run()
+{
+ BEGIN_DEBUG_EXCEPTION_HANDLER
+
+ std::cout << "========================" << std::endl;
+ std::cout << "Begin log output over terminal"
+ << " (no stdout/stderr backlog during that)" << std::endl;
+ // Make the loggers to stdout/stderr shut up.
+ // Go over our own loggers instead.
+ LogLevelMask err_mask = g_logger.removeOutput(&stderr_output);
+ LogLevelMask out_mask = g_logger.removeOutput(&stdout_output);
+
+ g_logger.addOutput(&m_log_output);
+
+ // Inform the server of our nick
+ m_chat_interface->command_queue.push_back(
+ new ChatEventNick(CET_NICK_ADD, m_nick));
+
+ {
+ // Ensures that curses is deinitialized even on an exception being thrown
+ CursesInitHelper helper(this);
+
+ while (!stopRequested()) {
+
+ int ch = getch();
+ if (stopRequested())
+ break;
+
+ step(ch);
+ }
+ }
+
+ if (m_kill_requested)
+ *m_kill_requested = true;
+
+ g_logger.removeOutput(&m_log_output);
+ g_logger.addOutputMasked(&stderr_output, err_mask);
+ g_logger.addOutputMasked(&stdout_output, out_mask);
+
+ std::cout << "End log output over terminal"
+ << " (no stdout/stderr backlog during that)" << std::endl;
+ std::cout << "========================" << std::endl;
+
+ END_DEBUG_EXCEPTION_HANDLER
+
+ return NULL;
+}
+
+void TerminalChatConsole::typeChatMessage(const std::wstring &msg)
+{
+ // Discard empty line
+ if (msg.empty())
+ return;
+
+ // Send to server
+ m_chat_interface->command_queue.push_back(
+ new ChatEventChat(m_nick, msg));
+
+ // Print if its a command (gets eaten by server otherwise)
+ if (msg[0] == L'/') {
+ m_chat_backend.addMessage(L"", (std::wstring)L"Issued command: " + msg);
+ }
+}
+
+void TerminalChatConsole::handleInput(int ch, bool &complete_redraw_needed)
+{
+ ChatPrompt &prompt = m_chat_backend.getPrompt();
+ // Helpful if you want to collect key codes that aren't documented
+ /*if (ch != ERR) {
+ m_chat_backend.addMessage(L"",
+ (std::wstring)L"Pressed key " + utf8_to_wide(
+ std::string(keyname(ch)) + " (code " + itos(ch) + ")"));
+ complete_redraw_needed = true;
+ }//*/
+
+ // All the key codes below are compatible to xterm
+ // Only add new ones if you have tried them there,
+ // to ensure compatibility with not just xterm but the wide
+ // range of terminals that are compatible to xterm.
+
+ switch (ch) {
+ case ERR: // no input
+ break;
+ case 27: // ESC
+ // Toggle ESC mode
+ m_esc_mode = !m_esc_mode;
+ break;
+ case KEY_PPAGE:
+ m_chat_backend.scrollPageUp();
+ complete_redraw_needed = true;
+ break;
+ case KEY_NPAGE:
+ m_chat_backend.scrollPageDown();
+ complete_redraw_needed = true;
+ break;
+ case KEY_ENTER:
+ case '\r':
+ case '\n': {
+ prompt.addToHistory(prompt.getLine());
+ typeChatMessage(prompt.replace(L""));
+ break;
+ }
+ case KEY_UP:
+ prompt.historyPrev();
+ break;
+ case KEY_DOWN:
+ prompt.historyNext();
+ break;
+ case KEY_LEFT:
+ // Left pressed
+ // move character to the left
+ prompt.cursorOperation(
+ ChatPrompt::CURSOROP_MOVE,
+ ChatPrompt::CURSOROP_DIR_LEFT,
+ ChatPrompt::CURSOROP_SCOPE_CHARACTER);
+ break;
+ case 545:
+ // Ctrl-Left pressed
+ // move word to the left
+ prompt.cursorOperation(
+ ChatPrompt::CURSOROP_MOVE,
+ ChatPrompt::CURSOROP_DIR_LEFT,
+ ChatPrompt::CURSOROP_SCOPE_WORD);
+ break;
+ case KEY_RIGHT:
+ // Right pressed
+ // move character to the right
+ prompt.cursorOperation(
+ ChatPrompt::CURSOROP_MOVE,
+ ChatPrompt::CURSOROP_DIR_RIGHT,
+ ChatPrompt::CURSOROP_SCOPE_CHARACTER);
+ break;
+ case 560:
+ // Ctrl-Right pressed
+ // move word to the right
+ prompt.cursorOperation(
+ ChatPrompt::CURSOROP_MOVE,
+ ChatPrompt::CURSOROP_DIR_RIGHT,
+ ChatPrompt::CURSOROP_SCOPE_WORD);
+ break;
+ case KEY_HOME:
+ // Home pressed
+ // move to beginning of line
+ prompt.cursorOperation(
+ ChatPrompt::CURSOROP_MOVE,
+ ChatPrompt::CURSOROP_DIR_LEFT,
+ ChatPrompt::CURSOROP_SCOPE_LINE);
+ break;
+ case KEY_END:
+ // End pressed
+ // move to end of line
+ prompt.cursorOperation(
+ ChatPrompt::CURSOROP_MOVE,
+ ChatPrompt::CURSOROP_DIR_RIGHT,
+ ChatPrompt::CURSOROP_SCOPE_LINE);
+ break;
+ case KEY_BACKSPACE:
+ case '\b':
+ case 127:
+ // Backspace pressed
+ // delete character to the left
+ prompt.cursorOperation(
+ ChatPrompt::CURSOROP_DELETE,
+ ChatPrompt::CURSOROP_DIR_LEFT,
+ ChatPrompt::CURSOROP_SCOPE_CHARACTER);
+ break;
+ case KEY_DC:
+ // Delete pressed
+ // delete character to the right
+ prompt.cursorOperation(
+ ChatPrompt::CURSOROP_DELETE,
+ ChatPrompt::CURSOROP_DIR_RIGHT,
+ ChatPrompt::CURSOROP_SCOPE_CHARACTER);
+ break;
+ case 519:
+ // Ctrl-Delete pressed
+ // delete word to the right
+ prompt.cursorOperation(
+ ChatPrompt::CURSOROP_DELETE,
+ ChatPrompt::CURSOROP_DIR_RIGHT,
+ ChatPrompt::CURSOROP_SCOPE_WORD);
+ break;
+ case 21:
+ // Ctrl-U pressed
+ // kill line to left end
+ prompt.cursorOperation(
+ ChatPrompt::CURSOROP_DELETE,
+ ChatPrompt::CURSOROP_DIR_LEFT,
+ ChatPrompt::CURSOROP_SCOPE_LINE);
+ break;
+ case 11:
+ // Ctrl-K pressed
+ // kill line to right end
+ prompt.cursorOperation(
+ ChatPrompt::CURSOROP_DELETE,
+ ChatPrompt::CURSOROP_DIR_RIGHT,
+ ChatPrompt::CURSOROP_SCOPE_LINE);
+ break;
+ case KEY_TAB:
+ // Tab pressed
+ // Nick completion
+ prompt.nickCompletion(m_nicks, false);
+ break;
+ default:
+ // Add character to the prompt,
+ // assuming UTF-8.
+ if (IS_UTF8_MULTB_START(ch)) {
+ m_pending_utf8_bytes.append(1, (char)ch);
+ m_utf8_bytes_to_wait += UTF8_MULTB_START_LEN(ch) - 1;
+ } else if (m_utf8_bytes_to_wait != 0) {
+ m_pending_utf8_bytes.append(1, (char)ch);
+ m_utf8_bytes_to_wait--;
+ if (m_utf8_bytes_to_wait == 0) {
+ std::wstring w = utf8_to_wide(m_pending_utf8_bytes);
+ m_pending_utf8_bytes = "";
+ // hopefully only one char in the wstring...
+ for (size_t i = 0; i < w.size(); i++) {
+ prompt.input(w.c_str()[i]);
+ }
+ }
+ } else if (IS_ASCII_PRINTABLE_CHAR(ch)) {
+ prompt.input(ch);
+ } else {
+ // Silently ignore characters we don't handle
+
+ //warningstream << "Pressed invalid character '"
+ // << keyname(ch) << "' (code " << itos(ch) << ")" << std::endl;
+ }
+ break;
+ }
+}
+
+void TerminalChatConsole::step(int ch)
+{
+ bool complete_redraw_needed = false;
+
+ // empty queues
+ while (!m_chat_interface->outgoing_queue.empty()) {
+ ChatEvent *evt = m_chat_interface->outgoing_queue.pop_frontNoEx();
+ switch (evt->type) {
+ case CET_NICK_REMOVE:
+ m_nicks.remove(((ChatEventNick *)evt)->nick);
+ break;
+ case CET_NICK_ADD:
+ m_nicks.push_back(((ChatEventNick *)evt)->nick);
+ break;
+ case CET_CHAT:
+ complete_redraw_needed = true;
+ // This is only used for direct replies from commands
+ // or for lua's print() functionality
+ m_chat_backend.addMessage(L"", ((ChatEventChat *)evt)->evt_msg);
+ break;
+ case CET_TIME_INFO:
+ ChatEventTimeInfo *tevt = (ChatEventTimeInfo *)evt;
+ m_game_time = tevt->game_time;
+ m_time_of_day = tevt->time;
+ };
+ delete evt;
+ }
+ while (!m_log_output.queue.empty()) {
+ complete_redraw_needed = true;
+ std::pair<LogLevel, std::string> p = m_log_output.queue.pop_frontNoEx();
+ if (p.first > m_log_level)
+ continue;
+
+ m_chat_backend.addMessage(
+ utf8_to_wide(Logger::getLevelLabel(p.first)),
+ utf8_to_wide(p.second));
+ }
+
+ // handle input
+ if (!m_esc_mode) {
+ handleInput(ch, complete_redraw_needed);
+ } else {
+ switch (ch) {
+ case ERR: // no input
+ break;
+ case 27: // ESC
+ // Toggle ESC mode
+ m_esc_mode = !m_esc_mode;
+ break;
+ case 'L':
+ m_log_level--;
+ m_log_level = MYMAX(m_log_level, LL_NONE + 1); // LL_NONE isn't accessible
+ break;
+ case 'l':
+ m_log_level++;
+ m_log_level = MYMIN(m_log_level, LL_MAX - 1);
+ break;
+ }
+ }
+
+ // was there a resize?
+ int xn, yn;
+ getmaxyx(stdscr, yn, xn);
+ if (xn != m_cols || yn != m_rows) {
+ m_cols = xn;
+ m_rows = yn;
+ m_can_draw_text = reformat_backend(&m_chat_backend, m_rows, m_cols);
+ complete_redraw_needed = true;
+ }
+
+ // draw title
+ move(0, 0);
+ clrtoeol();
+ addstr(PROJECT_NAME_C);
+ addstr(" ");
+ addstr(g_version_hash);
+
+ u32 minutes = m_time_of_day % 1000;
+ u32 hours = m_time_of_day / 1000;
+ minutes = (float)minutes / 1000 * 60;
+
+ if (m_game_time)
+ printw(" | Game %d Time of day %02d:%02d ",
+ m_game_time, hours, minutes);
+
+ // draw text
+ if (complete_redraw_needed && m_can_draw_text)
+ draw_text();
+
+ // draw prompt
+ if (!m_esc_mode) {
+ // normal prompt
+ ChatPrompt& prompt = m_chat_backend.getPrompt();
+ std::string prompt_text = wide_to_utf8(prompt.getVisiblePortion());
+ move(m_rows - 1, 0);
+ clrtoeol();
+ addstr(prompt_text.c_str());
+ // Draw cursor
+ s32 cursor_pos = prompt.getVisibleCursorPosition();
+ if (cursor_pos >= 0) {
+ move(m_rows - 1, cursor_pos);
+ }
+ } else {
+ // esc prompt
+ move(m_rows - 1, 0);
+ clrtoeol();
+ printw("[ESC] Toggle ESC mode |"
+ " [CTRL+C] Shut down |"
+ " (L) in-, (l) decrease loglevel %s",
+ Logger::getLevelLabel((LogLevel) m_log_level).c_str());
+ }
+
+ refresh();
+}
+
+void TerminalChatConsole::draw_text()
+{
+ ChatBuffer& buf = m_chat_backend.getConsoleBuffer();
+ for (u32 row = 0; row < buf.getRows(); row++) {
+ move_for_backend(row, 0);
+ clrtoeol();
+ const ChatFormattedLine& line = buf.getFormattedLine(row);
+ if (line.fragments.empty())
+ continue;
+ for (u32 i = 0; i < line.fragments.size(); ++i) {
+ const ChatFormattedFragment& fragment = line.fragments[i];
+ addstr(wide_to_utf8(fragment.text).c_str());
+ }
+ }
+}
+
+void TerminalChatConsole::stopAndWaitforThread()
+{
+ clearKillStatus();
+ stop();
+ wait();
+}
+
+#endif
diff --git a/src/terminal_chat_console.h b/src/terminal_chat_console.h
new file mode 100644
index 000000000..2111b7ecb
--- /dev/null
+++ b/src/terminal_chat_console.h
@@ -0,0 +1,131 @@
+/*
+Minetest
+Copyright (C) 2015 est31 <MTest31@outlook.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.
+*/
+
+#ifndef TERMINAL_CHAT_CONSOLE_H
+#define TERMINAL_CHAT_CONSOLE_H
+
+#include "chat.h"
+#include "threading/thread.h"
+#include "chat_interface.h"
+#include "log.h"
+
+#include <sstream>
+
+class TermLogOutput : public ILogOutput {
+public:
+
+ void logRaw(LogLevel lev, const std::string &line)
+ {
+ queue.push_back(std::make_pair(lev, line));
+ }
+
+ virtual void log(LogLevel lev, const std::string &combined,
+ const std::string &time, const std::string &thread_name,
+ const std::string &payload_text)
+ {
+ std::ostringstream os(std::ios_base::binary);
+ os << time << ": [" << thread_name << "] " << payload_text;
+
+ queue.push_back(std::make_pair(lev, os.str()));
+ }
+
+ MutexedQueue<std::pair<LogLevel, std::string> > queue;
+};
+
+class TerminalChatConsole : public Thread {
+public:
+
+ TerminalChatConsole() :
+ Thread("TerminalThread"),
+ m_log_level(LL_ACTION),
+ m_utf8_bytes_to_wait(0),
+ m_kill_requested(NULL),
+ m_esc_mode(false),
+ m_game_time(0),
+ m_time_of_day(0)
+ {}
+
+ void setup(
+ ChatInterface *iface,
+ bool *kill_requested,
+ const std::string &nick)
+ {
+ m_nick = nick;
+ m_kill_requested = kill_requested;
+ m_chat_interface = iface;
+ }
+
+ virtual void *run();
+
+ // Highly required!
+ void clearKillStatus() { m_kill_requested = NULL; }
+
+ void stopAndWaitforThread();
+
+private:
+ // these have stupid names so that nobody missclassifies them
+ // as curses functions. Oh, curses has stupid names too?
+ // Well, at least it was worth a try...
+ void initOfCurses();
+ void deInitOfCurses();
+
+ void draw_text();
+
+ void typeChatMessage(const std::wstring &m);
+
+ void handleInput(int ch, bool &complete_redraw_needed);
+
+ void step(int ch);
+
+ // Used to ensure the deinitialisation is always called.
+ struct CursesInitHelper {
+ TerminalChatConsole *cons;
+ CursesInitHelper(TerminalChatConsole * a_console)
+ : cons(a_console)
+ { cons->initOfCurses(); }
+ ~CursesInitHelper() { cons->deInitOfCurses(); }
+ };
+
+ int m_log_level;
+ std::string m_nick;
+
+ u8 m_utf8_bytes_to_wait;
+ std::string m_pending_utf8_bytes;
+
+ std::list<std::string> m_nicks;
+
+ int m_cols;
+ int m_rows;
+ bool m_can_draw_text;
+
+ bool *m_kill_requested;
+ ChatBackend m_chat_backend;
+ ChatInterface *m_chat_interface;
+
+ TermLogOutput m_log_output;
+
+ bool m_esc_mode;
+
+ u64 m_game_time;
+ u32 m_time_of_day;
+};
+
+extern TerminalChatConsole g_term_console;
+
+#endif
diff --git a/src/threading/CMakeLists.txt b/src/threading/CMakeLists.txt
new file mode 100644
index 000000000..5dd60ef1a
--- /dev/null
+++ b/src/threading/CMakeLists.txt
@@ -0,0 +1,7 @@
+set(JTHREAD_SRCS
+ ${CMAKE_CURRENT_SOURCE_DIR}/event.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/mutex.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/thread.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/semaphore.cpp
+ PARENT_SCOPE)
+
diff --git a/src/threading/atomic.h b/src/threading/atomic.h
new file mode 100644
index 000000000..c9a454f20
--- /dev/null
+++ b/src/threading/atomic.h
@@ -0,0 +1,139 @@
+/*
+Minetest
+Copyright (C) 2015 ShadowNinja <shadowninja@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
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef THREADING_ATOMIC_H
+#define THREADING_ATOMIC_H
+
+
+#if __cplusplus >= 201103L
+ #include <atomic>
+ template<typename T> using Atomic = std::atomic<T>;
+ template<typename T> using GenericAtomic = std::atomic<T>;
+#else
+
+#define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
+#define CLANG_VERSION (__clang_major__ * 100 + __clang_minor__)
+#if GCC_VERSION >= 407 || CLANG_VERSION >= 302
+ #define ATOMIC_LOAD_GENERIC(T, v) do { \
+ T _val; \
+ __atomic_load(&(v), &(_val), __ATOMIC_SEQ_CST); \
+ return _val; \
+ } while(0)
+ #define ATOMIC_LOAD(T, v) return __atomic_load_n (&(v), __ATOMIC_SEQ_CST)
+ #define ATOMIC_STORE(T, v, x) __atomic_store (&(v), &(x), __ATOMIC_SEQ_CST); return x
+ #define ATOMIC_EXCHANGE(T, v, x) return __atomic_exchange (&(v), &(x), __ATOMIC_SEQ_CST)
+ #define ATOMIC_ADD_EQ(T, v, x) return __atomic_add_fetch (&(v), (x), __ATOMIC_SEQ_CST)
+ #define ATOMIC_SUB_EQ(T, v, x) return __atomic_sub_fetch (&(v), (x), __ATOMIC_SEQ_CST)
+ #define ATOMIC_POST_INC(T, v) return __atomic_fetch_add (&(v), 1, __ATOMIC_SEQ_CST)
+ #define ATOMIC_POST_DEC(T, v) return __atomic_fetch_sub (&(v), 1, __ATOMIC_SEQ_CST)
+ #define ATOMIC_CAS(T, v, e, d) return __atomic_compare_exchange(&(v), &(e), &(d), \
+ false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)
+#else
+ #define ATOMIC_USE_LOCK
+ #include "threading/mutex.h"
+
+ #define ATOMIC_LOCK_OP(T, op) do { \
+ m_mutex.lock(); \
+ T _val = (op); \
+ m_mutex.unlock(); \
+ return _val; \
+ } while (0)
+ #define ATOMIC_LOCK_CAS(T, v, e, d) do { \
+ m_mutex.lock(); \
+ bool _eq = (v == e); \
+ if (_eq) \
+ v = d; \
+ m_mutex.unlock(); \
+ return _eq; \
+ } while (0)
+ #define ATOMIC_LOAD(T, v) ATOMIC_LOCK_OP(T, v)
+ #define ATOMIC_LOAD_GENERIC(T, v) ATOMIC_LOAD(T, v)
+ #define ATOMIC_STORE(T, v, x) ATOMIC_LOCK_OP(T, v = x)
+ #define ATOMIC_EXCHANGE(T, v, x) do { \
+ m_mutex.lock(); \
+ T _val = v; \
+ v = x; \
+ m_mutex.unlock(); \
+ return _val; \
+ } while (0)
+ #if GCC_VERSION >= 401
+ #define ATOMIC_ADD_EQ(T, v, x) return __sync_add_and_fetch(&(v), (x))
+ #define ATOMIC_SUB_EQ(T, v, x) return __sync_sub_and_fetch(&(v), (x))
+ #define ATOMIC_POST_INC(T, v) return __sync_fetch_and_add(&(v), 1)
+ #define ATOMIC_POST_DEC(T, v) return __sync_fetch_and_sub(&(v), 1)
+ #define ATOMIC_CAS(T, v, e, d) return __sync_bool_compare_and_swap(&(v), &(e), (d))
+ #else
+ #define ATOMIC_ADD_EQ(T, v, x) ATOMIC_LOCK_OP(T, v += x)
+ #define ATOMIC_SUB_EQ(T, v, x) ATOMIC_LOCK_OP(T, v -= x)
+ #define ATOMIC_POST_INC(T, v) ATOMIC_LOCK_OP(T, v++)
+ #define ATOMIC_POST_DEC(T, v) ATOMIC_LOCK_OP(T, v--)
+ #define ATOMIC_CAS(T, v, e, d) ATOMIC_LOCK_CAS(T, v, e, d)
+ #endif
+#endif
+
+// For usage with integral types.
+template<typename T>
+class Atomic {
+public:
+ Atomic(const T &v = 0) : m_val(v) {}
+
+ operator T () { ATOMIC_LOAD(T, m_val); }
+
+ T exchange(T x) { ATOMIC_EXCHANGE(T, m_val, x); }
+ bool compare_exchange_strong(T &expected, T desired) { ATOMIC_CAS(T, m_val, expected, desired); }
+
+ T operator = (T x) { ATOMIC_STORE(T, m_val, x); }
+ T operator += (T x) { ATOMIC_ADD_EQ(T, m_val, x); }
+ T operator -= (T x) { ATOMIC_SUB_EQ(T, m_val, x); }
+ T operator ++ () { return *this += 1; }
+ T operator -- () { return *this -= 1; }
+ T operator ++ (int) { ATOMIC_POST_INC(T, m_val); }
+ T operator -- (int) { ATOMIC_POST_DEC(T, m_val); }
+private:
+ T m_val;
+#ifdef ATOMIC_USE_LOCK
+ Mutex m_mutex;
+#endif
+};
+
+// For usage with non-integral types like float for example.
+// Needed because the other operations aren't provided by gcc
+// for non-integral types:
+// https://gcc.gnu.org/onlinedocs/gcc-4.7.0/gcc/_005f_005fatomic-Builtins.html
+template<typename T>
+class GenericAtomic {
+public:
+ GenericAtomic(const T &v = 0) : m_val(v) {}
+
+ operator T () { ATOMIC_LOAD_GENERIC(T, m_val); }
+
+ T exchange(T x) { ATOMIC_EXCHANGE(T, m_val, x); }
+ bool compare_exchange_strong(T &expected, T desired) { ATOMIC_CAS(T, m_val, expected, desired); }
+
+ T operator = (T x) { ATOMIC_STORE(T, m_val, x); }
+private:
+ T m_val;
+#ifdef ATOMIC_USE_LOCK
+ Mutex m_mutex;
+#endif
+};
+
+#endif // C++11
+
+#endif
diff --git a/src/threading/event.cpp b/src/threading/event.cpp
new file mode 100644
index 000000000..165f9d83f
--- /dev/null
+++ b/src/threading/event.cpp
@@ -0,0 +1,89 @@
+/*
+This file is a part of the JThread package, which contains some object-
+oriented thread wrappers for different thread implementations.
+
+Copyright (c) 2000-2006 Jori Liesenborgs (jori.liesenborgs@gmail.com)
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+#include "threading/event.h"
+
+Event::Event()
+{
+#if __cplusplus < 201103L
+# ifdef _WIN32
+ event = CreateEvent(NULL, false, false, NULL);
+# else
+ pthread_cond_init(&cv, NULL);
+ pthread_mutex_init(&mutex, NULL);
+ notified = false;
+# endif
+#endif
+}
+
+#if __cplusplus < 201103L
+Event::~Event()
+{
+#ifdef _WIN32
+ CloseHandle(event);
+#else
+ pthread_cond_destroy(&cv);
+ pthread_mutex_destroy(&mutex);
+#endif
+}
+#endif
+
+
+void Event::wait()
+{
+#if __cplusplus >= 201103L
+ MutexAutoLock lock(mutex);
+ while (!notified) {
+ cv.wait(lock);
+ }
+ notified = false;
+#elif defined(_WIN32)
+ WaitForSingleObject(event, INFINITE);
+#else
+ pthread_mutex_lock(&mutex);
+ while (!notified) {
+ pthread_cond_wait(&cv, &mutex);
+ }
+ notified = false;
+ pthread_mutex_unlock(&mutex);
+#endif
+}
+
+
+void Event::signal()
+{
+#if __cplusplus >= 201103L
+ MutexAutoLock lock(mutex);
+ notified = true;
+ cv.notify_one();
+#elif defined(_WIN32)
+ SetEvent(event);
+#else
+ pthread_mutex_lock(&mutex);
+ notified = true;
+ pthread_cond_signal(&cv);
+ pthread_mutex_unlock(&mutex);
+#endif
+}
diff --git a/src/threading/event.h b/src/threading/event.h
new file mode 100644
index 000000000..dd5164576
--- /dev/null
+++ b/src/threading/event.h
@@ -0,0 +1,72 @@
+/*
+This file is a part of the JThread package, which contains some object-
+oriented thread wrappers for different thread implementations.
+
+Copyright (c) 2000-2006 Jori Liesenborgs (jori.liesenborgs@gmail.com)
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef THREADING_EVENT_H
+#define THREADING_EVENT_H
+
+#if __cplusplus >= 201103L
+ #include <condition_variable>
+ #include "threading/mutex.h"
+ #include "threading/mutex_auto_lock.h"
+#elif defined(_WIN32)
+ #ifndef WIN32_LEAN_AND_MEAN
+ #define WIN32_LEAN_AND_MEAN
+ #endif
+ #include <windows.h>
+#else
+ #include <pthread.h>
+#endif
+
+
+/** A syncronization primitive that will wake up one waiting thread when signaled.
+ * Calling @c signal() multiple times before a waiting thread has had a chance
+ * to notice the signal will wake only one thread. Additionally, if no threads
+ * are waiting on the event when it is signaled, the next call to @c wait()
+ * will return (almost) immediately.
+ */
+class Event {
+public:
+ Event();
+#if __cplusplus < 201103L
+ ~Event();
+#endif
+ void wait();
+ void signal();
+
+private:
+#if __cplusplus >= 201103L
+ std::condition_variable cv;
+ Mutex mutex;
+ bool notified;
+#elif defined(_WIN32)
+ HANDLE event;
+#else
+ pthread_cond_t cv;
+ pthread_mutex_t mutex;
+ bool notified;
+#endif
+};
+
+#endif
diff --git a/src/threading/mutex.cpp b/src/threading/mutex.cpp
new file mode 100644
index 000000000..f2b07bec3
--- /dev/null
+++ b/src/threading/mutex.cpp
@@ -0,0 +1,108 @@
+/*
+This file is a part of the JThread package, which contains some object-
+oriented thread wrappers for different thread implementations.
+
+Copyright (c) 2000-2006 Jori Liesenborgs (jori.liesenborgs@gmail.com)
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+// Windows std::mutex is much slower than the critical section API
+#if __cplusplus < 201103L || defined(_WIN32)
+
+#include "threading/mutex.h"
+
+#ifndef _WIN32
+ #include <cassert>
+#endif
+
+#define UNUSED(expr) do { (void)(expr); } while (0)
+
+Mutex::Mutex()
+{
+ init_mutex(false);
+}
+
+
+Mutex::Mutex(bool recursive)
+{
+ init_mutex(recursive);
+}
+
+void Mutex::init_mutex(bool recursive)
+{
+#ifdef _WIN32
+ // Windows critical sections are recursive by default
+ UNUSED(recursive);
+
+ InitializeCriticalSection(&mutex);
+#else
+ pthread_mutexattr_t attr;
+ pthread_mutexattr_init(&attr);
+
+ if (recursive)
+ pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
+
+ int ret = pthread_mutex_init(&mutex, &attr);
+ assert(!ret);
+ UNUSED(ret);
+
+ pthread_mutexattr_destroy(&attr);
+#endif
+}
+
+Mutex::~Mutex()
+{
+#ifdef _WIN32
+ DeleteCriticalSection(&mutex);
+#else
+ int ret = pthread_mutex_destroy(&mutex);
+ assert(!ret);
+ UNUSED(ret);
+#endif
+}
+
+void Mutex::lock()
+{
+#ifdef _WIN32
+ EnterCriticalSection(&mutex);
+#else
+ int ret = pthread_mutex_lock(&mutex);
+ assert(!ret);
+ UNUSED(ret);
+#endif
+}
+
+void Mutex::unlock()
+{
+#ifdef _WIN32
+ LeaveCriticalSection(&mutex);
+#else
+ int ret = pthread_mutex_unlock(&mutex);
+ assert(!ret);
+ UNUSED(ret);
+#endif
+}
+
+RecursiveMutex::RecursiveMutex()
+ : Mutex(true)
+{}
+
+#endif
+
diff --git a/src/threading/mutex.h b/src/threading/mutex.h
new file mode 100644
index 000000000..dadbd050c
--- /dev/null
+++ b/src/threading/mutex.h
@@ -0,0 +1,81 @@
+/*
+This file is a part of the JThread package, which contains some object-
+oriented thread wrappers for different thread implementations.
+
+Copyright (c) 2000-2006 Jori Liesenborgs (jori.liesenborgs@gmail.com)
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef THREADING_MUTEX_H
+#define THREADING_MUTEX_H
+
+// Windows std::mutex is much slower than the critical section API
+#if __cplusplus >= 201103L && !defined(_WIN32)
+ #include <mutex>
+ using Mutex = std::mutex;
+ using RecursiveMutex = std::recursive_mutex;
+#else
+
+#ifdef _WIN32
+ #ifndef _WIN32_WINNT
+ #define _WIN32_WINNT 0x0501
+ #endif
+ #ifndef WIN32_LEAN_AND_MEAN
+ #define WIN32_LEAN_AND_MEAN
+ #endif
+ #include <windows.h>
+#else // pthread
+ #include <pthread.h>
+#endif
+
+#include "util/basic_macros.h"
+
+class Mutex
+{
+public:
+ Mutex();
+ ~Mutex();
+ void lock();
+ void unlock();
+
+protected:
+ Mutex(bool recursive);
+ void init_mutex(bool recursive);
+private:
+#ifdef _WIN32
+ CRITICAL_SECTION mutex;
+#else // pthread
+ pthread_mutex_t mutex;
+#endif
+
+ DISABLE_CLASS_COPY(Mutex);
+};
+
+class RecursiveMutex : public Mutex
+{
+public:
+ RecursiveMutex();
+
+ DISABLE_CLASS_COPY(RecursiveMutex);
+};
+
+#endif // C++11
+
+#endif
diff --git a/src/threading/mutex_auto_lock.h b/src/threading/mutex_auto_lock.h
new file mode 100644
index 000000000..25caf7e14
--- /dev/null
+++ b/src/threading/mutex_auto_lock.h
@@ -0,0 +1,60 @@
+/*
+This file is a part of the JThread package, which contains some object-
+oriented thread wrappers for different thread implementations.
+
+Copyright (c) 2000-2006 Jori Liesenborgs (jori.liesenborgs@gmail.com)
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef THREADING_MUTEX_AUTO_LOCK_H
+#define THREADING_MUTEX_AUTO_LOCK_H
+
+#if __cplusplus >= 201103L
+ #include <mutex>
+ using MutexAutoLock = std::unique_lock<std::mutex>;
+ using RecursiveMutexAutoLock = std::unique_lock<std::recursive_mutex>;
+#else
+
+#include "threading/mutex.h"
+
+
+class MutexAutoLock
+{
+public:
+ MutexAutoLock(Mutex &m) : mutex(m) { mutex.lock(); }
+ ~MutexAutoLock() { mutex.unlock(); }
+
+private:
+ Mutex &mutex;
+};
+
+class RecursiveMutexAutoLock
+{
+public:
+ RecursiveMutexAutoLock(RecursiveMutex &m) : mutex(m) { mutex.lock(); }
+ ~RecursiveMutexAutoLock() { mutex.unlock(); }
+
+private:
+ RecursiveMutex &mutex;
+};
+#endif
+
+#endif
+
diff --git a/src/threading/semaphore.cpp b/src/threading/semaphore.cpp
new file mode 100644
index 000000000..77ceff509
--- /dev/null
+++ b/src/threading/semaphore.cpp
@@ -0,0 +1,162 @@
+/*
+Minetest
+Copyright (C) 2013 sapier <sapier AT gmx DOT 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
+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 "threading/semaphore.h"
+
+#include <iostream>
+#include <cstdlib>
+#include <cassert>
+
+#define UNUSED(expr) do { (void)(expr); } while (0)
+
+#ifdef _WIN32
+ #include <climits>
+ #define MAX_SEMAPHORE_COUNT LONG_MAX - 1
+#else
+ #include <cerrno>
+ #include <sys/time.h>
+ #include <pthread.h>
+ #if defined(__MACH__) && defined(__APPLE__)
+ #include <mach/mach.h>
+ #include <mach/task.h>
+ #include <mach/semaphore.h>
+ #include <sys/semaphore.h>
+ #include <unistd.h>
+
+ #undef sem_t
+ #undef sem_init
+ #undef sem_wait
+ #undef sem_post
+ #undef sem_destroy
+ #define sem_t semaphore_t
+ #define sem_init(s, p, c) semaphore_create(mach_task_self(), (s), 0, (c))
+ #define sem_wait(s) semaphore_wait(*(s))
+ #define sem_post(s) semaphore_signal(*(s))
+ #define sem_destroy(s) semaphore_destroy(mach_task_self(), *(s))
+ #endif
+#endif
+
+
+Semaphore::Semaphore(int val)
+{
+#ifdef _WIN32
+ semaphore = CreateSemaphore(NULL, val, MAX_SEMAPHORE_COUNT, NULL);
+#else
+ int ret = sem_init(&semaphore, 0, val);
+ assert(!ret);
+ UNUSED(ret);
+#endif
+}
+
+
+Semaphore::~Semaphore()
+{
+#ifdef _WIN32
+ CloseHandle(semaphore);
+#else
+ int ret = sem_destroy(&semaphore);
+#ifdef __ANDROID__
+ // Workaround for broken bionic semaphore implementation!
+ assert(!ret || errno == EBUSY);
+#else
+ assert(!ret);
+#endif
+ UNUSED(ret);
+#endif
+}
+
+
+void Semaphore::post(unsigned int num)
+{
+ assert(num > 0);
+#ifdef _WIN32
+ ReleaseSemaphore(semaphore, num, NULL);
+#else
+ for (unsigned i = 0; i < num; i++) {
+ int ret = sem_post(&semaphore);
+ assert(!ret);
+ UNUSED(ret);
+ }
+#endif
+}
+
+
+void Semaphore::wait()
+{
+#ifdef _WIN32
+ WaitForSingleObject(semaphore, INFINITE);
+#else
+ int ret = sem_wait(&semaphore);
+ assert(!ret);
+ UNUSED(ret);
+#endif
+}
+
+
+bool Semaphore::wait(unsigned int time_ms)
+{
+#ifdef _WIN32
+ unsigned int ret = WaitForSingleObject(semaphore, time_ms);
+
+ if (ret == WAIT_OBJECT_0) {
+ return true;
+ } else {
+ assert(ret == WAIT_TIMEOUT);
+ return false;
+ }
+#else
+# if defined(__MACH__) && defined(__APPLE__)
+ mach_timespec_t wait_time;
+ wait_time.tv_sec = time_ms / 1000;
+ wait_time.tv_nsec = 1000000 * (time_ms % 1000);
+
+ errno = 0;
+ int ret = semaphore_timedwait(semaphore, wait_time);
+ switch (ret) {
+ case KERN_OPERATION_TIMED_OUT:
+ errno = ETIMEDOUT;
+ break;
+ case KERN_ABORTED:
+ errno = EINTR;
+ break;
+ default:
+ if (ret)
+ errno = EINVAL;
+ }
+# else
+ struct timespec wait_time;
+ struct timeval now;
+
+ if (gettimeofday(&now, NULL) == -1) {
+ std::cerr << "Semaphore::wait(ms): Unable to get time with gettimeofday!" << std::endl;
+ abort();
+ }
+
+ wait_time.tv_nsec = ((time_ms % 1000) * 1000 * 1000) + (now.tv_usec * 1000);
+ wait_time.tv_sec = (time_ms / 1000) + (wait_time.tv_nsec / (1000 * 1000 * 1000)) + now.tv_sec;
+ wait_time.tv_nsec %= 1000 * 1000 * 1000;
+
+ int ret = sem_timedwait(&semaphore, &wait_time);
+# endif
+
+ assert(!ret || (errno == ETIMEDOUT || errno == EINTR));
+ return !ret;
+#endif
+}
+
diff --git a/src/jthread/jsemaphore.h b/src/threading/semaphore.h
index 32e9bc2f2..822856396 100644
--- a/src/jthread/jsemaphore.h
+++ b/src/threading/semaphore.h
@@ -1,6 +1,6 @@
/*
Minetest
-Copyright (C) 2013 sapier, < sapier AT gmx DOT net >
+Copyright (C) 2013 sapier <sapier AT gmx DOT 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
@@ -17,48 +17,39 @@ with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef JSEMAPHORE_H_
-#define JSEMAPHORE_H_
+#ifndef THREADING_SEMAPHORE_H
+#define THREADING_SEMAPHORE_H
-#if defined(WIN32)
-#include <windows.h>
-#include <assert.h>
-#define MAX_SEMAPHORE_COUNT 1024
+#if defined(_WIN32)
+ #include <windows.h>
#elif defined(__MACH__) && defined(__APPLE__)
-#include <pthread.h>
-#include <mach/mach.h>
-#include <mach/task.h>
-#include <mach/semaphore.h>
-#include <sys/semaphore.h>
-#include <errno.h>
-#include <time.h>
+ #include <mach/semaphore.h>
#else
-#include <pthread.h>
-#include <semaphore.h>
+ #include <semaphore.h>
#endif
-class JSemaphore {
-public:
- JSemaphore();
- ~JSemaphore();
- JSemaphore(int initval);
+#include "util/basic_macros.h"
- void Post();
- void Wait();
- bool Wait(unsigned int time_ms);
+class Semaphore {
+public:
+ Semaphore(int val=0);
+ ~Semaphore();
- int GetValue();
+ void post(unsigned int num=1);
+ void wait();
+ bool wait(unsigned int time_ms);
private:
#if defined(WIN32)
- HANDLE m_hSemaphore;
+ HANDLE semaphore;
#elif defined(__MACH__) && defined(__APPLE__)
- semaphore_t m_semaphore;
- int semcount;
+ semaphore_t semaphore;
#else
- sem_t m_semaphore;
+ sem_t semaphore;
#endif
+
+ DISABLE_CLASS_COPY(Semaphore);
};
+#endif
-#endif /* JSEMAPHORE_H_ */
diff --git a/src/threading/thread.cpp b/src/threading/thread.cpp
new file mode 100644
index 000000000..5161a6c01
--- /dev/null
+++ b/src/threading/thread.cpp
@@ -0,0 +1,430 @@
+/*
+This file is a part of the JThread package, which contains some object-
+oriented thread wrappers for different thread implementations.
+
+Copyright (c) 2000-2006 Jori Liesenborgs (jori.liesenborgs@gmail.com)
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+#include "threading/thread.h"
+#include "threading/mutex_auto_lock.h"
+#include "log.h"
+#include "porting.h"
+
+#define UNUSED(expr) do { (void)(expr); } while (0)
+
+#if USE_CPP11_THREADS
+ #include <chrono>
+ #include <system_error>
+#elif USE_WIN_THREADS
+ #ifndef _WIN32_WCE
+ #include <process.h>
+ #endif
+#elif USE_POSIX_THREADS
+ #include <time.h>
+ #include <assert.h>
+ #include <stdlib.h>
+ #include <unistd.h>
+ #include <sys/time.h>
+
+ #if defined(__FreeBSD__) || defined(__APPLE__)
+ #include <sys/types.h>
+ #include <sys/sysctl.h>
+ #elif defined(_GNU_SOURCE)
+ #include <sys/sysinfo.h>
+ #endif
+#endif
+
+
+// for setName
+#if defined(linux) || defined(__linux)
+ #include <sys/prctl.h>
+#elif defined(__FreeBSD__) || defined(__OpenBSD__)
+ #include <pthread_np.h>
+#elif defined(_MSC_VER)
+ struct THREADNAME_INFO {
+ DWORD dwType; // Must be 0x1000
+ LPCSTR szName; // Pointer to name (in user addr space)
+ DWORD dwThreadID; // Thread ID (-1=caller thread)
+ DWORD dwFlags; // Reserved for future use, must be zero
+ };
+#endif
+
+// for bindToProcessor
+#if __FreeBSD_version >= 702106
+ typedef cpuset_t cpu_set_t;
+#elif defined(__linux) || defined(linux)
+ #include <sched.h>
+#elif defined(__sun) || defined(sun)
+ #include <sys/types.h>
+ #include <sys/processor.h>
+ #include <sys/procset.h>
+#elif defined(_AIX)
+ #include <sys/processor.h>
+ #include <sys/thread.h>
+#elif defined(__APPLE__)
+ #include <mach/mach_init.h>
+ #include <mach/thread_act.h>
+#endif
+
+
+Thread::Thread(const std::string &name) :
+ m_name(name),
+ m_retval(NULL),
+ m_joinable(false),
+ m_request_stop(false),
+ m_running(false)
+{
+#ifdef _AIX
+ m_kernel_thread_id = -1;
+#endif
+}
+
+
+Thread::~Thread()
+{
+ kill();
+}
+
+
+bool Thread::start()
+{
+ MutexAutoLock lock(m_mutex);
+
+ if (m_running)
+ return false;
+
+ m_request_stop = false;
+
+#if USE_CPP11_THREADS
+
+ try {
+ m_thread_obj = new std::thread(threadProc, this);
+ } catch (const std::system_error &e) {
+ return false;
+ }
+
+#elif USE_WIN_THREADS
+
+ m_thread_handle = CreateThread(NULL, 0, threadProc, this, 0, &m_thread_id);
+ if (!m_thread_handle)
+ return false;
+
+#elif USE_POSIX_THREADS
+
+ int status = pthread_create(&m_thread_handle, NULL, threadProc, this);
+ if (status)
+ return false;
+
+#endif
+
+ while (!m_running)
+ sleep_ms(1);
+
+ m_joinable = true;
+
+ return true;
+}
+
+
+bool Thread::stop()
+{
+ m_request_stop = true;
+ return true;
+}
+
+
+bool Thread::wait()
+{
+ MutexAutoLock lock(m_mutex);
+
+ if (!m_joinable)
+ return false;
+
+#if USE_CPP11_THREADS
+
+ m_thread_obj->join();
+
+ delete m_thread_obj;
+ m_thread_obj = NULL;
+
+#elif USE_WIN_THREADS
+
+ int ret = WaitForSingleObject(m_thread_handle, INFINITE);
+ assert(ret == WAIT_OBJECT_0);
+ UNUSED(ret);
+
+ CloseHandle(m_thread_handle);
+ m_thread_handle = NULL;
+ m_thread_id = -1;
+
+#elif USE_POSIX_THREADS
+
+ int ret = pthread_join(m_thread_handle, NULL);
+ assert(ret == 0);
+ UNUSED(ret);
+
+#endif
+
+ assert(m_running == false);
+ m_joinable = false;
+ return true;
+}
+
+
+bool Thread::kill()
+{
+ if (!m_running) {
+ wait();
+ return false;
+ }
+
+ m_running = false;
+
+#ifdef _WIN32
+ TerminateThread(m_thread_handle, 0);
+ CloseHandle(m_thread_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 = NULL;
+ m_joinable = false;
+ m_request_stop = false;
+
+ return true;
+}
+
+
+bool Thread::getReturnValue(void **ret)
+{
+ if (m_running)
+ return false;
+
+ *ret = m_retval;
+ return true;
+}
+
+
+#if USE_CPP11_THREADS || USE_POSIX_THREADS
+void *Thread::threadProc(void *param)
+#elif defined(_WIN32_WCE)
+DWORD Thread::threadProc(LPVOID param)
+#elif defined(_WIN32)
+DWORD WINAPI Thread::threadProc(LPVOID param)
+#endif
+{
+ Thread *thr = (Thread *)param;
+
+#ifdef _AIX
+ m_kernel_thread_id = thread_self();
+#endif
+
+ thr->setName(thr->m_name);
+
+ g_logger.registerThread(thr->m_name);
+ thr->m_running = true;
+
+ thr->m_retval = thr->run();
+
+ thr->m_running = false;
+ g_logger.deregisterThread();
+
+ // 0 is returned here to avoid an unnecessary ifdef clause
+ return 0;
+}
+
+
+void Thread::setName(const std::string &name)
+{
+#if defined(linux) || defined(__linux)
+
+ // It would be cleaner to do this with pthread_setname_np,
+ // which was added to glibc in version 2.12, but some major
+ // distributions are still runing 2.11 and previous versions.
+ prctl(PR_SET_NAME, name.c_str());
+
+#elif defined(__FreeBSD__) || defined(__OpenBSD__)
+
+ pthread_set_name_np(pthread_self(), name.c_str());
+
+#elif defined(__NetBSD__)
+
+ pthread_setname_np(pthread_self(), name.c_str());
+
+#elif defined(__APPLE__)
+
+ pthread_setname_np(name.c_str());
+
+#elif defined(_MSC_VER)
+
+ // Windows itself doesn't support thread names,
+ // but the MSVC debugger does...
+ THREADNAME_INFO info;
+
+ info.dwType = 0x1000;
+ info.szName = name.c_str();
+ info.dwThreadID = -1;
+ info.dwFlags = 0;
+
+ __try {
+ RaiseException(0x406D1388, 0,
+ sizeof(info) / sizeof(DWORD), (ULONG_PTR *)&info);
+ } __except (EXCEPTION_CONTINUE_EXECUTION) {
+ }
+
+#elif defined(_WIN32) || defined(__GNU__)
+
+ // These platforms are known to not support thread names.
+ // Silently ignore the request.
+
+#else
+ #warning "Unrecognized platform, thread names will not be available."
+#endif
+}
+
+
+unsigned int Thread::getNumberOfProcessors()
+{
+#if __cplusplus >= 201103L
+
+ return std::thread::hardware_concurrency();
+
+#elif defined(_SC_NPROCESSORS_ONLN)
+
+ return sysconf(_SC_NPROCESSORS_ONLN);
+
+#elif defined(__FreeBSD__) || defined(__NetBSD__) || \
+ defined(__DragonFly__) || defined(__APPLE__)
+
+ unsigned int num_cpus = 1;
+ size_t len = sizeof(num_cpus);
+
+ int mib[2];
+ mib[0] = CTL_HW;
+ mib[1] = HW_NCPU;
+
+ sysctl(mib, 2, &num_cpus, &len, NULL, 0);
+ return num_cpus;
+
+#elif defined(_GNU_SOURCE)
+
+ return get_nprocs();
+
+#elif defined(_WIN32)
+
+ SYSTEM_INFO sysinfo;
+ GetSystemInfo(&sysinfo);
+ return sysinfo.dwNumberOfProcessors;
+
+#elif defined(PTW32_VERSION) || defined(__hpux)
+
+ return pthread_num_processors_np();
+
+#else
+
+ return 1;
+
+#endif
+}
+
+
+bool Thread::bindToProcessor(unsigned int proc_number)
+{
+#if defined(__ANDROID__)
+
+ return false;
+
+#elif defined(_WIN32)
+
+ return SetThreadAffinityMask(getThreadHandle(), 1 << proc_number);
+
+#elif __FreeBSD_version >= 702106 || defined(__linux) || defined(linux)
+
+ cpu_set_t cpuset;
+
+ CPU_ZERO(&cpuset);
+ CPU_SET(proc_number, &cpuset);
+
+ return pthread_setaffinity_np(getThreadHandle(), sizeof(cpuset), &cpuset) == 0;
+
+#elif defined(__sun) || defined(sun)
+
+ return processor_bind(P_LWPID, P_MYID, proc_number, NULL) == 0
+
+#elif defined(_AIX)
+
+ return bindprocessor(BINDTHREAD, m_kernel_thread_id, proc_number) == 0;
+
+#elif defined(__hpux) || defined(hpux)
+
+ pthread_spu_t answer;
+
+ return pthread_processor_bind_np(PTHREAD_BIND_ADVISORY_NP,
+ &answer, proc_number, getThreadHandle()) == 0;
+
+#elif defined(__APPLE__)
+
+ struct thread_affinity_policy tapol;
+
+ thread_port_t threadport = pthread_mach_thread_np(getThreadHandle());
+ tapol.affinity_tag = proc_number + 1;
+ return thread_policy_set(threadport, THREAD_AFFINITY_POLICY,
+ (thread_policy_t)&tapol,
+ THREAD_AFFINITY_POLICY_COUNT) == KERN_SUCCESS;
+
+#else
+
+ return false;
+
+#endif
+}
+
+
+bool Thread::setPriority(int prio)
+{
+#if defined(_WIN32)
+
+ return SetThreadPriority(getThreadHandle(), prio);
+
+#else
+
+ struct sched_param sparam;
+ int policy;
+
+ if (pthread_getschedparam(getThreadHandle(), &policy, &sparam) != 0)
+ return false;
+
+ int min = sched_get_priority_min(policy);
+ int max = sched_get_priority_max(policy);
+
+ sparam.sched_priority = min + prio * (max - min) / THREAD_PRIORITY_HIGHEST;
+ return pthread_setschedparam(getThreadHandle(), policy, &sparam) == 0;
+
+#endif
+}
+
diff --git a/src/threading/thread.h b/src/threading/thread.h
new file mode 100644
index 000000000..de800ecb7
--- /dev/null
+++ b/src/threading/thread.h
@@ -0,0 +1,183 @@
+/*
+This file is a part of the JThread package, which contains some object-
+oriented thread wrappers for different thread implementations.
+
+Copyright (c) 2000-2006 Jori Liesenborgs (jori.liesenborgs@gmail.com)
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef THREADING_THREAD_H
+#define THREADING_THREAD_H
+
+#include "util/basic_macros.h"
+#include "threading/atomic.h"
+#include "threading/mutex.h"
+#include "threads.h"
+
+#include <string>
+#if USE_CPP11_THREADS
+ #include <thread> // for std::thread
+#endif
+#ifdef _AIX
+ #include <sys/thread.h> // for tid_t
+#endif
+
+/*
+ * On platforms using pthreads, these five priority classes correlate to
+ * even divisions between the minimum and maximum reported thread priority.
+ */
+#if !defined(_WIN32)
+ #define THREAD_PRIORITY_LOWEST 0
+ #define THREAD_PRIORITY_BELOW_NORMAL 1
+ #define THREAD_PRIORITY_NORMAL 2
+ #define THREAD_PRIORITY_ABOVE_NORMAL 3
+ #define THREAD_PRIORITY_HIGHEST 4
+#endif
+
+
+class Thread {
+public:
+ Thread(const std::string &name="");
+ virtual ~Thread();
+
+ /*
+ * Begins execution of a new thread at the pure virtual method Thread::run().
+ * Execution of the thread is guaranteed to have started after this function
+ * returns.
+ */
+ bool start();
+
+ /*
+ * Requests that the thread exit gracefully.
+ * Returns immediately; thread execution is guaranteed to be complete after
+ * a subsequent call to Thread::wait.
+ */
+ 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
+ * on before.
+ */
+ bool wait();
+
+ /*
+ * Returns true if the calling thread is this Thread object.
+ */
+ bool isCurrentThread() { return thr_is_current_thread(getThreadId()); }
+
+ inline bool isRunning() { return m_running; }
+ inline bool stopRequested() { return m_request_stop; }
+
+#if USE_CPP11_THREADS
+ inline threadid_t getThreadId() { return m_thread_obj->get_id(); }
+ inline threadhandle_t getThreadHandle() { return m_thread_obj->native_handle(); }
+#else
+# if USE_WIN_THREADS
+ inline threadid_t getThreadId() { return m_thread_id; }
+# else
+ inline threadid_t getThreadId() { return m_thread_handle; }
+# endif
+ inline threadhandle_t getThreadHandle() { return m_thread_handle; }
+#endif
+
+ /*
+ * Gets the thread return value.
+ * Returns true if the thread has exited and the return value was available,
+ * or false if the thread has yet to finish.
+ */
+ bool getReturnValue(void **ret);
+
+ /*
+ * Binds (if possible, otherwise sets the affinity of) the thread to the
+ * specific processor specified by proc_number.
+ */
+ bool bindToProcessor(unsigned int proc_number);
+
+ /*
+ * Sets the thread priority to the specified priority.
+ *
+ * prio can be one of: THREAD_PRIORITY_LOWEST, THREAD_PRIORITY_BELOW_NORMAL,
+ * THREAD_PRIORITY_NORMAL, THREAD_PRIORITY_ABOVE_NORMAL, THREAD_PRIORITY_HIGHEST.
+ * On Windows, any of the other priorites as defined by SetThreadPriority
+ * are supported as well.
+ *
+ * Note that it may be necessary to first set the threading policy or
+ * scheduling algorithm to one that supports thread priorities if not
+ * supported by default, otherwise this call will have no effect.
+ */
+ bool setPriority(int prio);
+
+ /*
+ * Sets the currently executing thread's name to where supported; useful
+ * for debugging.
+ */
+ static void setName(const std::string &name);
+
+ /*
+ * Returns the number of processors/cores configured and active on this machine.
+ */
+ static unsigned int getNumberOfProcessors();
+
+protected:
+ std::string m_name;
+
+ virtual void *run() = 0;
+
+private:
+ void *m_retval;
+ bool m_joinable;
+ Atomic<bool> m_request_stop;
+ Atomic<bool> m_running;
+ Mutex m_mutex;
+
+#ifndef USE_CPP11_THREADS
+ threadhandle_t m_thread_handle;
+# if _WIN32
+ threadid_t m_thread_id;
+# endif
+#endif
+
+ static ThreadStartFunc threadProc;
+
+#ifdef _AIX
+ // For AIX, there does not exist any mapping from pthread_t to tid_t
+ // available to us, so we maintain one ourselves. This is set on thread start.
+ tid_t m_kernel_thread_id;
+#endif
+
+#if USE_CPP11_THREADS
+ std::thread *m_thread_obj;
+#endif
+
+ DISABLE_CLASS_COPY(Thread);
+};
+
+#endif
+
diff --git a/src/threads.h b/src/threads.h
index 503fa6abf..d4306f631 100644
--- a/src/threads.h
+++ b/src/threads.h
@@ -20,22 +20,75 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#ifndef THREADS_HEADER
#define THREADS_HEADER
-#include "jthread/jmutex.h"
-
-#if (defined(WIN32) || defined(_WIN32_WCE))
-typedef DWORD threadid_t;
+//
+// Determine which threading API we will use
+//
+#if __cplusplus >= 201103L
+ #define USE_CPP11_THREADS 1
+#elif defined(_WIN32)
+ #define USE_WIN_THREADS 1
#else
-typedef pthread_t threadid_t;
+ #define USE_POSIX_THREADS 1
+#endif
+
+///////////////
+
+
+#if USE_CPP11_THREADS
+ #include <thread>
+#endif
+
+#include "threading/mutex.h"
+
+//
+// threadid_t, threadhandle_t
+//
+#if USE_CPP11_THREADS
+ typedef std::thread::id threadid_t;
+ typedef std::thread::native_handle_type threadhandle_t;
+#elif USE_WIN_THREADS
+ typedef DWORD threadid_t;
+ typedef HANDLE threadhandle_t;
+#elif USE_POSIX_THREADS
+ typedef pthread_t threadid_t;
+ typedef pthread_t threadhandle_t;
+#endif
+
+//
+// ThreadStartFunc
+//
+#if USE_CPP11_THREADS || USE_POSIX_THREADS
+ typedef void *ThreadStartFunc(void *param);
+#elif defined(_WIN32_WCE)
+ typedef DWORD ThreadStartFunc(LPVOID param);
+#elif defined(_WIN32)
+ typedef DWORD WINAPI ThreadStartFunc(LPVOID param);
#endif
-inline threadid_t get_current_thread_id()
+
+inline threadid_t thr_get_current_thread_id()
{
-#if (defined(WIN32) || defined(_WIN32_WCE))
+#if USE_CPP11_THREADS
+ return std::this_thread::get_id();
+#elif USE_WIN_THREADS
return GetCurrentThreadId();
-#else
+#elif USE_POSIX_THREADS
return pthread_self();
#endif
}
+inline bool thr_compare_thread_id(threadid_t thr1, threadid_t thr2)
+{
+#if USE_POSIX_THREADS
+ return pthread_equal(thr1, thr2);
+#else
+ return thr1 == thr2;
#endif
+}
+inline bool thr_is_current_thread(threadid_t thr)
+{
+ return thr_compare_thread_id(thr_get_current_thread_id(), thr);
+}
+
+#endif
diff --git a/src/tool.cpp b/src/tool.cpp
index e013d5ea8..54b9f15f4 100644
--- a/src/tool.cpp
+++ b/src/tool.cpp
@@ -35,7 +35,7 @@ void ToolCapabilities::serialize(std::ostream &os, u16 protocol_version) const
writeS16(os, max_drop_level);
writeU32(os, groupcaps.size());
for(std::map<std::string, ToolGroupCap>::const_iterator
- i = groupcaps.begin(); i != groupcaps.end(); i++){
+ i = groupcaps.begin(); i != groupcaps.end(); ++i){
const std::string *name = &i->first;
const ToolGroupCap *cap = &i->second;
os<<serializeString(*name);
@@ -43,7 +43,7 @@ void ToolCapabilities::serialize(std::ostream &os, u16 protocol_version) const
writeS16(os, cap->maxlevel);
writeU32(os, cap->times.size());
for(std::map<int, float>::const_iterator
- i = cap->times.begin(); i != cap->times.end(); i++){
+ i = cap->times.begin(); i != cap->times.end(); ++i){
writeS16(os, i->first);
writeF1000(os, i->second);
}
@@ -51,7 +51,7 @@ void ToolCapabilities::serialize(std::ostream &os, u16 protocol_version) const
if(protocol_version > 17){
writeU32(os, damageGroups.size());
for(std::map<std::string, s16>::const_iterator
- i = damageGroups.begin(); i != damageGroups.end(); i++){
+ i = damageGroups.begin(); i != damageGroups.end(); ++i){
os<<serializeString(i->first);
writeS16(os, i->second);
}
@@ -116,7 +116,7 @@ DigParams getDigParams(const ItemGroupList &groups,
int level = itemgroup_get(groups, "level");
//infostream<<"level="<<level<<std::endl;
for(std::map<std::string, ToolGroupCap>::const_iterator
- i = tp->groupcaps.begin(); i != tp->groupcaps.end(); i++){
+ i = tp->groupcaps.begin(); i != tp->groupcaps.end(); ++i){
const std::string &name = i->first;
//infostream<<"group="<<name<<std::endl;
const ToolGroupCap &cap = i->second;
@@ -164,7 +164,7 @@ HitParams getHitParams(const ItemGroupList &armor_groups,
float full_punch_interval = tp->full_punch_interval;
for(std::map<std::string, s16>::const_iterator
- i = tp->damageGroups.begin(); i != tp->damageGroups.end(); i++){
+ i = tp->damageGroups.begin(); i != tp->damageGroups.end(); ++i){
s16 armor = itemgroup_get(armor_groups, i->first);
damage += i->second * rangelim(time_from_last_punch / full_punch_interval, 0.0, 1.0)
* armor / 100.0;
diff --git a/src/touchscreengui.cpp b/src/touchscreengui.cpp
index f5868133f..f51b2d5fa 100644
--- a/src/touchscreengui.cpp
+++ b/src/touchscreengui.cpp
@@ -44,17 +44,8 @@ const char** touchgui_button_imagenames = (const char*[]) {
"down_arrow.png",
"left_arrow.png",
"right_arrow.png",
- "inventory_btn.png",
- "drop_btn.png",
"jump_btn.png",
- "down.png",
- "fly_btn.png",
- "noclip_btn.png",
- "fast_btn.png",
- "debug_btn.png",
- "chat_btn.png",
- "camera_btn.png",
- "rangeview_btn.png"
+ "down.png"
};
static irr::EKEY_CODE id2keycode(touch_gui_button_id id)
@@ -113,29 +104,13 @@ static irr::EKEY_CODE id2keycode(touch_gui_button_id id)
TouchScreenGUI *g_touchscreengui;
-TouchScreenGUI::TouchScreenGUI(IrrlichtDevice *device, IEventReceiver* receiver):
- m_device(device),
- m_guienv(device->getGUIEnvironment()),
- m_camera_yaw(0.0),
- m_camera_pitch(0.0),
- m_visible(false),
- m_move_id(-1),
- m_receiver(receiver)
-{
- for (unsigned int i=0; i < after_last_element_id; i++) {
- m_buttons[i].guibutton = 0;
- m_buttons[i].repeatcounter = -1;
- m_buttons[i].repeatdelay = BUTTON_REPEAT_DELAY;
- }
-
- m_screensize = m_device->getVideoDriver()->getScreenSize();
-}
-
-void TouchScreenGUI::loadButtonTexture(button_info* btn, const char* path, rect<s32> button_rect)
+static void load_button_texture(button_info* btn, const char* path,
+ rect<s32> button_rect, ISimpleTextureSource* tsrc, video::IVideoDriver *driver)
{
unsigned int tid;
- video::ITexture *texture = guiScalingImageButton(m_device->getVideoDriver(),
- m_texturesource->getTexture(path, &tid), button_rect.getWidth(), button_rect.getHeight());
+ video::ITexture *texture = guiScalingImageButton(driver,
+ tsrc->getTexture(path, &tid), button_rect.getWidth(),
+ button_rect.getHeight());
if (texture) {
btn->guibutton->setUseAlphaChannel(true);
if (g_settings->getBool("gui_scaling_filter")) {
@@ -153,6 +128,314 @@ void TouchScreenGUI::loadButtonTexture(button_info* btn, const char* path, rect<
}
}
+AutoHideButtonBar::AutoHideButtonBar(IrrlichtDevice *device,
+ IEventReceiver* receiver) :
+ m_texturesource(NULL),
+ m_driver(device->getVideoDriver()),
+ m_guienv(device->getGUIEnvironment()),
+ m_receiver(receiver),
+ m_active(false),
+ m_visible(true),
+ m_timeout(0),
+ m_timeout_value(3),
+ m_initialized(false),
+ m_dir(AHBB_Dir_Right_Left)
+{
+ m_screensize = device->getVideoDriver()->getScreenSize();
+
+}
+
+void AutoHideButtonBar::init(ISimpleTextureSource* tsrc,
+ const char* starter_img, int button_id, v2s32 UpperLeft,
+ v2s32 LowerRight, autohide_button_bar_dir dir, float timeout)
+{
+ m_texturesource = tsrc;
+
+ m_upper_left = UpperLeft;
+ m_lower_right = LowerRight;
+
+ /* init settings bar */
+
+ irr::core::rect<int> current_button = rect<s32>(UpperLeft.X, UpperLeft.Y,
+ LowerRight.X, LowerRight.Y);
+
+ m_starter.guibutton = m_guienv->addButton(current_button, 0, button_id, L"", 0);
+ m_starter.guibutton->grab();
+ m_starter.repeatcounter = -1;
+ m_starter.keycode = KEY_OEM_8; // use invalid keycode as it's not relevant
+ m_starter.immediate_release = true;
+ m_starter.ids.clear();
+
+ load_button_texture(&m_starter, starter_img, current_button,
+ m_texturesource, m_driver);
+
+ m_dir = dir;
+ m_timeout_value = timeout;
+
+ m_initialized = true;
+}
+
+AutoHideButtonBar::~AutoHideButtonBar()
+{
+ m_starter.guibutton->setVisible(false);
+ m_starter.guibutton->drop();
+}
+
+void AutoHideButtonBar::addButton(touch_gui_button_id button_id,
+ const wchar_t* caption, const char* btn_image)
+{
+
+ if (!m_initialized) {
+ errorstream << "AutoHideButtonBar::addButton not yet initialized!"
+ << std::endl;
+ return;
+ }
+ int button_size = 0;
+
+ if ((m_dir == AHBB_Dir_Top_Bottom) || (m_dir == AHBB_Dir_Bottom_Top)) {
+ button_size = m_lower_right.X - m_upper_left.X;
+ } else {
+ button_size = m_lower_right.Y - m_upper_left.Y;
+ }
+
+ irr::core::rect<int> current_button;
+
+ if ((m_dir == AHBB_Dir_Right_Left) || (m_dir == AHBB_Dir_Left_Right)) {
+
+ int x_start = 0;
+ int x_end = 0;
+
+ if (m_dir == AHBB_Dir_Left_Right) {
+ x_start = m_lower_right.X + (button_size * 1.25 * m_buttons.size())
+ + (button_size * 0.25);
+ x_end = x_start + button_size;
+ } else {
+ x_end = m_upper_left.X - (button_size * 1.25 * m_buttons.size())
+ - (button_size * 0.25);
+ x_start = x_end - button_size;
+ }
+
+ current_button = rect<s32>(x_start, m_upper_left.Y, x_end,
+ m_lower_right.Y);
+ } else {
+ int y_start = 0;
+ int y_end = 0;
+
+ if (m_dir == AHBB_Dir_Top_Bottom) {
+ y_start = m_lower_right.X + (button_size * 1.25 * m_buttons.size())
+ + (button_size * 0.25);
+ y_end = y_start + button_size;
+ } else {
+ y_end = m_upper_left.X - (button_size * 1.25 * m_buttons.size())
+ - (button_size * 0.25);
+ y_start = y_end - button_size;
+ }
+
+ current_button = rect<s32>(m_upper_left.X, y_start, m_lower_right.Y,
+ y_end);
+ }
+
+ button_info* btn = new button_info();
+ btn->guibutton = m_guienv->addButton(current_button, 0, button_id, caption, 0);
+ btn->guibutton->grab();
+ btn->guibutton->setVisible(false);
+ btn->guibutton->setEnabled(false);
+ btn->repeatcounter = -1;
+ btn->keycode = id2keycode(button_id);
+ btn->immediate_release = true;
+ btn->ids.clear();
+
+ load_button_texture(btn, btn_image, current_button, m_texturesource,
+ m_driver);
+
+ m_buttons.push_back(btn);
+}
+
+bool AutoHideButtonBar::isButton(const SEvent &event)
+{
+ IGUIElement* rootguielement = m_guienv->getRootGUIElement();
+
+ if (rootguielement == NULL) {
+ return false;
+ }
+
+ gui::IGUIElement *element = rootguielement->getElementFromPoint(
+ core::position2d<s32>(event.TouchInput.X, event.TouchInput.Y));
+
+ if (element == NULL) {
+ return false;
+ }
+
+ if (m_active) {
+ /* check for all buttons in vector */
+
+ std::vector<button_info*>::iterator iter = m_buttons.begin();
+
+ while (iter != m_buttons.end()) {
+ if ((*iter)->guibutton == element) {
+
+ SEvent* translated = new SEvent();
+ memset(translated, 0, sizeof(SEvent));
+ translated->EventType = irr::EET_KEY_INPUT_EVENT;
+ translated->KeyInput.Key = (*iter)->keycode;
+ translated->KeyInput.Control = false;
+ translated->KeyInput.Shift = false;
+ translated->KeyInput.Char = 0;
+
+ /* add this event */
+ translated->KeyInput.PressedDown = true;
+ m_receiver->OnEvent(*translated);
+
+ /* remove this event */
+ translated->KeyInput.PressedDown = false;
+ m_receiver->OnEvent(*translated);
+
+ delete translated;
+
+ (*iter)->ids.push_back(event.TouchInput.ID);
+
+ m_timeout = 0;
+
+ return true;
+ }
+ ++iter;
+ }
+ } else {
+ /* check for starter button only */
+ if (element == m_starter.guibutton) {
+ m_starter.ids.push_back(event.TouchInput.ID);
+ m_starter.guibutton->setVisible(false);
+ m_starter.guibutton->setEnabled(false);
+ m_active = true;
+ m_timeout = 0;
+
+ std::vector<button_info*>::iterator iter = m_buttons.begin();
+
+ while (iter != m_buttons.end()) {
+ (*iter)->guibutton->setVisible(true);
+ (*iter)->guibutton->setEnabled(true);
+ ++iter;
+ }
+
+ return true;
+ }
+ }
+ return false;
+}
+
+bool AutoHideButtonBar::isReleaseButton(int eventID)
+{
+ std::vector<int>::iterator id = std::find(m_starter.ids.begin(),
+ m_starter.ids.end(), eventID);
+
+ if (id != m_starter.ids.end()) {
+ m_starter.ids.erase(id);
+ return true;
+ }
+
+ std::vector<button_info*>::iterator iter = m_buttons.begin();
+
+ while (iter != m_buttons.end()) {
+ std::vector<int>::iterator id = std::find((*iter)->ids.begin(),
+ (*iter)->ids.end(), eventID);
+
+ if (id != (*iter)->ids.end()) {
+ (*iter)->ids.erase(id);
+ // TODO handle settings button release
+ return true;
+ }
+ ++iter;
+ }
+
+ return false;
+}
+
+void AutoHideButtonBar::step(float dtime)
+{
+ if (m_active) {
+ m_timeout += dtime;
+
+ if (m_timeout > m_timeout_value) {
+ deactivate();
+ }
+ }
+}
+
+void AutoHideButtonBar::deactivate()
+{
+ if (m_visible == true) {
+ m_starter.guibutton->setVisible(true);
+ m_starter.guibutton->setEnabled(true);
+ }
+ m_active = false;
+
+ std::vector<button_info*>::iterator iter = m_buttons.begin();
+
+ while (iter != m_buttons.end()) {
+ (*iter)->guibutton->setVisible(false);
+ (*iter)->guibutton->setEnabled(false);
+ ++iter;
+ }
+}
+
+void AutoHideButtonBar::hide()
+{
+ m_visible = false;
+ m_starter.guibutton->setVisible(false);
+ m_starter.guibutton->setEnabled(false);
+
+ std::vector<button_info*>::iterator iter = m_buttons.begin();
+
+ while (iter != m_buttons.end()) {
+ (*iter)->guibutton->setVisible(false);
+ (*iter)->guibutton->setEnabled(false);
+ ++iter;
+ }
+}
+
+void AutoHideButtonBar::show()
+{
+ m_visible = true;
+
+ if (m_active) {
+ std::vector<button_info*>::iterator iter = m_buttons.begin();
+
+ while (iter != m_buttons.end()) {
+ (*iter)->guibutton->setVisible(true);
+ (*iter)->guibutton->setEnabled(true);
+ ++iter;
+ }
+ } else {
+ m_starter.guibutton->setVisible(true);
+ m_starter.guibutton->setEnabled(true);
+ }
+}
+
+TouchScreenGUI::TouchScreenGUI(IrrlichtDevice *device, IEventReceiver* receiver):
+ m_device(device),
+ m_guienv(device->getGUIEnvironment()),
+ m_camera_yaw(0.0),
+ m_camera_pitch(0.0),
+ m_visible(false),
+ m_move_id(-1),
+ m_receiver(receiver),
+ m_move_has_really_moved(false),
+ m_move_downtime(0),
+ m_move_sent_as_mouse_event(false),
+ // use some downlocation way off screen as init value to avoid invalid behaviour
+ m_move_downlocation(v2s32(-10000, -10000)),
+ m_settingsbar(device, receiver),
+ m_rarecontrolsbar(device, receiver)
+{
+ for (unsigned int i=0; i < after_last_element_id; i++) {
+ m_buttons[i].guibutton = 0;
+ m_buttons[i].repeatcounter = -1;
+ m_buttons[i].repeatdelay = BUTTON_REPEAT_DELAY;
+ }
+
+ m_screensize = m_device->getVideoDriver()->getScreenSize();
+}
+
void TouchScreenGUI::initButton(touch_gui_button_id id, rect<s32> button_rect,
std::wstring caption, bool immediate_release, float repeat_delay)
{
@@ -166,21 +449,27 @@ void TouchScreenGUI::initButton(touch_gui_button_id id, rect<s32> button_rect,
btn->immediate_release = immediate_release;
btn->ids.clear();
- loadButtonTexture(btn,touchgui_button_imagenames[id], button_rect);
+ load_button_texture(btn,touchgui_button_imagenames[id],button_rect,
+ m_texturesource, m_device->getVideoDriver());
}
static int getMaxControlPadSize(float density) {
return 200 * density * g_settings->getFloat("hud_scaling");
}
-void TouchScreenGUI::init(ISimpleTextureSource* tsrc, float density)
+int TouchScreenGUI::getGuiButtonSize()
{
- assert(tsrc != 0);
+ u32 control_pad_size = MYMIN((2 * m_screensize.Y) / 3,
+ getMaxControlPadSize(porting::getDisplayDensity()));
- u32 control_pad_size =
- MYMIN((2 * m_screensize.Y) / 3,getMaxControlPadSize(density));
+ return control_pad_size / 3;
+}
- u32 button_size = control_pad_size / 3;
+void TouchScreenGUI::init(ISimpleTextureSource* tsrc)
+{
+ assert(tsrc != 0);
+
+ u32 button_size = getGuiButtonSize();
m_visible = true;
m_texturesource = tsrc;
m_control_pad_rect = rect<s32>(0, m_screensize.Y - 3 * button_size,
@@ -223,16 +512,6 @@ void TouchScreenGUI::init(ISimpleTextureSource* tsrc, float density)
}
}
- /* init inventory button */
- initButton(inventory_id,
- rect<s32>(0, m_screensize.Y - (button_size/2),
- (button_size/2), m_screensize.Y), L"inv", true);
-
- /* init drop button */
- initButton(drop_id,
- rect<s32>(2.5*button_size, m_screensize.Y - (button_size/2),
- 3*button_size, m_screensize.Y), L"drop", true);
-
/* init jump button */
initButton(jump_id,
rect<s32>(m_screensize.X-(1.75*button_size),
@@ -249,48 +528,37 @@ void TouchScreenGUI::init(ISimpleTextureSource* tsrc, float density)
m_screensize.Y),
L"H",false);
- /* init fly button */
- initButton(fly_id,
- rect<s32>(m_screensize.X - (0.75*button_size),
- m_screensize.Y - (2.25*button_size),
- m_screensize.X, m_screensize.Y - (button_size*1.5)),
- L"fly", false, SLOW_BUTTON_REPEAT);
-
- /* init noclip button */
- initButton(noclip_id,
- rect<s32>(m_screensize.X - (0.75*button_size), 2.25*button_size,
- m_screensize.X, 3*button_size),
- L"clip", false, SLOW_BUTTON_REPEAT);
-
- /* init fast button */
- initButton(fast_id,
- rect<s32>(m_screensize.X - (0.75*button_size), 1.5*button_size,
- m_screensize.X, 2.25*button_size),
- L"fast", false, SLOW_BUTTON_REPEAT);
-
- /* init debug button */
- initButton(debug_id,
- rect<s32>(m_screensize.X - (0.75*button_size), 0.75*button_size,
- m_screensize.X, 1.5*button_size),
- L"dbg", false, SLOW_BUTTON_REPEAT);
-
- /* init chat button */
- initButton(chat_id,
- rect<s32>(m_screensize.X - (0.75*button_size), 0,
- m_screensize.X, 0.75*button_size),
- L"Chat", true);
-
- /* init camera button */
- initButton(camera_id,
- rect<s32>(m_screensize.X - (1.5*button_size), 0,
- m_screensize.X - (0.75*button_size), 0.75*button_size),
- L"cam", false, SLOW_BUTTON_REPEAT);
-
- /* init rangeselect button */
- initButton(range_id,
- rect<s32>(m_screensize.X - (2.25*button_size), 0,
- m_screensize.X - (1.5*button_size), 0.75*button_size),
- L"far", false, SLOW_BUTTON_REPEAT);
+ m_settingsbar.init(m_texturesource, "gear_icon.png", settings_starter_id,
+ v2s32(m_screensize.X - (button_size / 2),
+ m_screensize.Y - ((SETTINGS_BAR_Y_OFFSET + 1) * button_size)
+ + (button_size * 0.5)),
+ v2s32(m_screensize.X,
+ m_screensize.Y - (SETTINGS_BAR_Y_OFFSET * button_size)
+ + (button_size * 0.5)), AHBB_Dir_Right_Left,
+ 3.0);
+
+ m_settingsbar.addButton(fly_id, L"fly", "fly_btn.png");
+ m_settingsbar.addButton(noclip_id, L"noclip", "noclip_btn.png");
+ m_settingsbar.addButton(fast_id, L"fast", "fast_btn.png");
+ m_settingsbar.addButton(debug_id, L"debug", "debug_btn.png");
+ m_settingsbar.addButton(camera_id, L"camera", "camera_btn.png");
+ m_settingsbar.addButton(range_id, L"rangeview", "rangeview_btn.png");
+
+ m_rarecontrolsbar.init(m_texturesource, "rare_controls.png",
+ rare_controls_starter_id,
+ v2s32(0,
+ m_screensize.Y
+ - ((RARE_CONTROLS_BAR_Y_OFFSET + 1) * button_size)
+ + (button_size * 0.5)),
+ v2s32(button_size / 2,
+ m_screensize.Y - (RARE_CONTROLS_BAR_Y_OFFSET * button_size)
+ + (button_size * 0.5)), AHBB_Dir_Left_Right,
+ 2);
+
+ m_rarecontrolsbar.addButton(chat_id, L"Chat", "chat_btn.png");
+ m_rarecontrolsbar.addButton(inventory_id, L"inv", "inventory_btn.png");
+ m_rarecontrolsbar.addButton(drop_id, L"drop", "drop_btn.png");
+
}
touch_gui_button_id TouchScreenGUI::getButtonID(s32 x, s32 y)
@@ -331,7 +599,7 @@ bool TouchScreenGUI::isHUDButton(const SEvent &event)
{
// check if hud item is pressed
for (std::map<int,rect<s32> >::iterator iter = m_hud_rects.begin();
- iter != m_hud_rects.end(); iter++) {
+ iter != m_hud_rects.end(); ++iter) {
if (iter->second.isPointInside(
v2s32(event.TouchInput.X,
event.TouchInput.Y)
@@ -374,7 +642,7 @@ bool TouchScreenGUI::isReleaseHUDButton(int eventID)
return false;
}
-void TouchScreenGUI::ButtonEvent(touch_gui_button_id button,
+void TouchScreenGUI::handleButtonEvent(touch_gui_button_id button,
int eventID, bool action)
{
button_info* btn = &m_buttons[button];
@@ -417,6 +685,61 @@ void TouchScreenGUI::ButtonEvent(touch_gui_button_id button,
delete translated;
}
+
+void TouchScreenGUI::handleReleaseEvent(int evt_id)
+{
+ touch_gui_button_id button = getButtonID(evt_id);
+
+ /* handle button events */
+ if (button != after_last_element_id) {
+ handleButtonEvent(button, evt_id, false);
+ }
+ /* handle hud button events */
+ else if (isReleaseHUDButton(evt_id)) {
+ /* nothing to do here */
+ } else if (m_settingsbar.isReleaseButton(evt_id)) {
+ /* nothing to do here */
+ } else if (m_rarecontrolsbar.isReleaseButton(evt_id)) {
+ /* nothing to do here */
+ }
+ /* handle the point used for moving view */
+ else if (evt_id == m_move_id) {
+ m_move_id = -1;
+
+ /* if this pointer issued a mouse event issue symmetric release here */
+ if (m_move_sent_as_mouse_event) {
+ SEvent* translated = new SEvent;
+ memset(translated,0,sizeof(SEvent));
+ translated->EventType = EET_MOUSE_INPUT_EVENT;
+ translated->MouseInput.X = m_move_downlocation.X;
+ translated->MouseInput.Y = m_move_downlocation.Y;
+ translated->MouseInput.Shift = false;
+ translated->MouseInput.Control = false;
+ translated->MouseInput.ButtonStates = 0;
+ translated->MouseInput.Event = EMIE_LMOUSE_LEFT_UP;
+ m_receiver->OnEvent(*translated);
+ delete translated;
+ }
+ else {
+ /* do double tap detection */
+ doubleTapDetection();
+ }
+ }
+ else {
+ infostream
+ << "TouchScreenGUI::translateEvent released unknown button: "
+ << evt_id << std::endl;
+ }
+
+ for (std::vector<id_status>::iterator iter = m_known_ids.begin();
+ iter != m_known_ids.end(); ++iter) {
+ if (iter->id == evt_id) {
+ m_known_ids.erase(iter);
+ break;
+ }
+ }
+}
+
void TouchScreenGUI::translateEvent(const SEvent &event)
{
if (!m_visible) {
@@ -447,14 +770,24 @@ void TouchScreenGUI::translateEvent(const SEvent &event)
/* handle button events */
if (button != after_last_element_id) {
- ButtonEvent(button,eventID,true);
- }
- else if (isHUDButton(event))
- {
+ handleButtonEvent(button, eventID, true);
+ m_settingsbar.deactivate();
+ m_rarecontrolsbar.deactivate();
+ } else if (isHUDButton(event)) {
+ m_settingsbar.deactivate();
+ m_rarecontrolsbar.deactivate();
/* already handled in isHUDButton() */
+ } else if (m_settingsbar.isButton(event)) {
+ m_rarecontrolsbar.deactivate();
+ /* already handled in isSettingsBarButton() */
+ } else if (m_rarecontrolsbar.isButton(event)) {
+ m_settingsbar.deactivate();
+ /* already handled in isSettingsBarButton() */
}
/* handle non button events */
else {
+ m_settingsbar.deactivate();
+ m_rarecontrolsbar.deactivate();
/* if we don't already have a moving point make this the moving one */
if (m_move_id == -1) {
m_move_id = event.TouchInput.ID;
@@ -469,53 +802,7 @@ void TouchScreenGUI::translateEvent(const SEvent &event)
}
else if (event.TouchInput.Event == ETIE_LEFT_UP) {
verbosestream << "Up event for pointerid: " << event.TouchInput.ID << std::endl;
-
- touch_gui_button_id button = getButtonID(event.TouchInput.ID);
-
- /* handle button events */
- if (button != after_last_element_id) {
- ButtonEvent(button,event.TouchInput.ID,false);
- }
- /* handle hud button events */
- else if (isReleaseHUDButton(event.TouchInput.ID)) {
- /* nothing to do here */
- }
- /* handle the point used for moving view */
- else if (event.TouchInput.ID == m_move_id) {
- m_move_id = -1;
-
- /* if this pointer issued a mouse event issue symmetric release here */
- if (m_move_sent_as_mouse_event) {
- SEvent* translated = new SEvent;
- memset(translated,0,sizeof(SEvent));
- translated->EventType = EET_MOUSE_INPUT_EVENT;
- translated->MouseInput.X = m_move_downlocation.X;
- translated->MouseInput.Y = m_move_downlocation.Y;
- translated->MouseInput.Shift = false;
- translated->MouseInput.Control = false;
- translated->MouseInput.ButtonStates = 0;
- translated->MouseInput.Event = EMIE_LMOUSE_LEFT_UP;
- m_receiver->OnEvent(*translated);
- delete translated;
- }
- else {
- /* do double tap detection */
- doubleTapDetection();
- }
- }
- else {
- infostream
- << "TouchScreenGUI::translateEvent released unknown button: "
- << event.TouchInput.ID << std::endl;
- }
-
- for (std::vector<id_status>::iterator iter = m_known_ids.begin();
- iter != m_known_ids.end(); iter++) {
- if (iter->id == event.TouchInput.ID) {
- m_known_ids.erase(iter);
- break;
- }
- }
+ handleReleaseEvent(event.TouchInput.ID);
}
else {
assert(event.TouchInput.Event == ETIE_MOVED);
@@ -576,8 +863,7 @@ void TouchScreenGUI::translateEvent(const SEvent &event)
->getRayFromScreenCoordinates(
v2s32(event.TouchInput.X,event.TouchInput.Y));
}
- }
- else {
+ } else {
handleChangedButton(event);
}
}
@@ -590,8 +876,8 @@ void TouchScreenGUI::handleChangedButton(const SEvent &event)
if (m_buttons[i].ids.empty()) {
continue;
}
- for(std::vector<int>::iterator iter = m_buttons[i].ids.begin();
- iter != m_buttons[i].ids.end(); iter++) {
+ for (std::vector<int>::iterator iter = m_buttons[i].ids.begin();
+ iter != m_buttons[i].ids.end(); ++iter) {
if (event.TouchInput.ID == *iter) {
@@ -603,12 +889,12 @@ void TouchScreenGUI::handleChangedButton(const SEvent &event)
}
/* remove old button */
- ButtonEvent((touch_gui_button_id) i,*iter,false);
+ handleButtonEvent((touch_gui_button_id) i,*iter,false);
if (current_button_id == after_last_element_id) {
return;
}
- ButtonEvent((touch_gui_button_id) current_button_id,*iter,true);
+ handleButtonEvent((touch_gui_button_id) current_button_id,*iter,true);
return;
}
@@ -622,8 +908,11 @@ void TouchScreenGUI::handleChangedButton(const SEvent &event)
}
button_info* btn = &m_buttons[current_button_id];
- if (std::find(btn->ids.begin(),btn->ids.end(), event.TouchInput.ID) == btn->ids.end()) {
- ButtonEvent((touch_gui_button_id) current_button_id,event.TouchInput.ID,true);
+ if (std::find(btn->ids.begin(),btn->ids.end(), event.TouchInput.ID)
+ == btn->ids.end())
+ {
+ handleButtonEvent((touch_gui_button_id) current_button_id,
+ event.TouchInput.ID, true);
}
}
@@ -637,7 +926,7 @@ bool TouchScreenGUI::doubleTapDetection()
m_key_events[1].x = m_move_downlocation.X;
m_key_events[1].y = m_move_downlocation.Y;
- u32 delta = porting::getDeltaMs(m_key_events[0].down_time,getTimeMs());
+ u32 delta = porting::getDeltaMs(m_key_events[0].down_time, getTimeMs());
if (delta > 400)
return false;
@@ -646,11 +935,11 @@ bool TouchScreenGUI::doubleTapDetection()
(m_key_events[0].y - m_key_events[1].y) * (m_key_events[0].y - m_key_events[1].y));
- if (distance >(20 + g_settings->getU16("touchscreen_threshold")))
+ if (distance > (20 + g_settings->getU16("touchscreen_threshold")))
return false;
SEvent* translated = new SEvent();
- memset(translated,0,sizeof(SEvent));
+ memset(translated, 0, sizeof(SEvent));
translated->EventType = EET_MOUSE_INPUT_EVENT;
translated->MouseInput.X = m_key_events[0].x;
translated->MouseInput.Y = m_key_events[0].y;
@@ -679,7 +968,7 @@ bool TouchScreenGUI::doubleTapDetection()
TouchScreenGUI::~TouchScreenGUI()
{
- for (unsigned int i=0; i < after_last_element_id; i++) {
+ for (unsigned int i = 0; i < after_last_element_id; i++) {
button_info* btn = &m_buttons[i];
if (btn->guibutton != 0) {
btn->guibutton->drop();
@@ -691,7 +980,7 @@ TouchScreenGUI::~TouchScreenGUI()
void TouchScreenGUI::step(float dtime)
{
/* simulate keyboard repeats */
- for (unsigned int i=0; i < after_last_element_id; i++) {
+ for (unsigned int i = 0; i < after_last_element_id; i++) {
button_info* btn = &m_buttons[i];
if (btn->ids.size() > 0) {
@@ -705,7 +994,7 @@ void TouchScreenGUI::step(float dtime)
btn->repeatcounter = 0;
SEvent translated;
- memset(&translated,0,sizeof(SEvent));
+ memset(&translated, 0, sizeof(SEvent));
translated.EventType = irr::EET_KEY_INPUT_EVENT;
translated.KeyInput.Key = btn->keycode;
translated.KeyInput.PressedDown = false;
@@ -731,7 +1020,7 @@ void TouchScreenGUI::step(float dtime)
v2s32(m_move_downlocation.X,m_move_downlocation.Y));
SEvent translated;
- memset(&translated,0,sizeof(SEvent));
+ memset(&translated, 0, sizeof(SEvent));
translated.EventType = EET_MOUSE_INPUT_EVENT;
translated.MouseInput.X = m_move_downlocation.X;
translated.MouseInput.Y = m_move_downlocation.Y;
@@ -744,6 +1033,9 @@ void TouchScreenGUI::step(float dtime)
m_move_sent_as_mouse_event = true;
}
}
+
+ m_settingsbar.step(dtime);
+ m_rarecontrolsbar.step(dtime);
}
void TouchScreenGUI::resetHud()
@@ -759,20 +1051,39 @@ void TouchScreenGUI::registerHudItem(int index, const rect<s32> &rect)
void TouchScreenGUI::Toggle(bool visible)
{
m_visible = visible;
- for (unsigned int i=0; i < after_last_element_id; i++) {
+ for (unsigned int i = 0; i < after_last_element_id; i++) {
button_info* btn = &m_buttons[i];
if (btn->guibutton != 0) {
btn->guibutton->setVisible(visible);
}
}
+
+ /* clear all active buttons */
+ if (!visible) {
+ while (m_known_ids.size() > 0) {
+ handleReleaseEvent(m_known_ids.begin()->id);
+ }
+
+ m_settingsbar.hide();
+ m_rarecontrolsbar.hide();
+ } else {
+ m_settingsbar.show();
+ m_rarecontrolsbar.show();
+ }
}
-void TouchScreenGUI::Hide()
+void TouchScreenGUI::hide()
{
+ if (!m_visible)
+ return;
+
Toggle(false);
}
-void TouchScreenGUI::Show()
+void TouchScreenGUI::show()
{
+ if (m_visible)
+ return;
+
Toggle(true);
}
diff --git a/src/touchscreengui.h b/src/touchscreengui.h
index bb3231793..d8106a260 100644
--- a/src/touchscreengui.h
+++ b/src/touchscreengui.h
@@ -38,26 +38,105 @@ typedef enum {
backward_id,
left_id,
right_id,
- inventory_id,
- drop_id,
jump_id,
crunch_id,
+ after_last_element_id,
+ settings_starter_id,
+ rare_controls_starter_id,
fly_id,
noclip_id,
fast_id,
debug_id,
- chat_id,
camera_id,
range_id,
- after_last_element_id
+ chat_id,
+ inventory_id,
+ drop_id
} touch_gui_button_id;
+typedef enum {
+ AHBB_Dir_Top_Bottom,
+ AHBB_Dir_Bottom_Top,
+ AHBB_Dir_Left_Right,
+ AHBB_Dir_Right_Left
+} autohide_button_bar_dir;
+
#define MIN_DIG_TIME_MS 500
#define MAX_TOUCH_COUNT 64
#define BUTTON_REPEAT_DELAY 0.2f
+#define SETTINGS_BAR_Y_OFFSET 6.5
+#define RARE_CONTROLS_BAR_Y_OFFSET 4
+
extern const char** touchgui_button_imagenames;
+struct button_info {
+ float repeatcounter;
+ float repeatdelay;
+ irr::EKEY_CODE keycode;
+ std::vector<int> ids;
+ IGUIButton* guibutton;
+ bool immediate_release;
+};
+
+class AutoHideButtonBar
+{
+public:
+
+ AutoHideButtonBar( IrrlichtDevice *device, IEventReceiver* receiver );
+
+ void init(ISimpleTextureSource* tsrc, const char* starter_img,
+ int button_id, v2s32 UpperLeft, v2s32 LowerRight,
+ autohide_button_bar_dir dir, float timeout);
+
+ ~AutoHideButtonBar();
+
+ /* add button to be shown */
+ void addButton(touch_gui_button_id id, const wchar_t* caption,
+ const char* btn_image);
+
+ /* detect settings bar button events */
+ bool isButton(const SEvent &event);
+
+ /* handle released hud buttons */
+ bool isReleaseButton(int eventID);
+
+ /* step handler */
+ void step(float dtime);
+
+ /* deactivate button bar */
+ void deactivate();
+
+ /* hide the whole buttonbar */
+ void hide();
+
+ /* unhide the buttonbar */
+ void show();
+
+private:
+ ISimpleTextureSource* m_texturesource;
+ irr::video::IVideoDriver* m_driver;
+ IGUIEnvironment* m_guienv;
+ IEventReceiver* m_receiver;
+ v2u32 m_screensize;
+ button_info m_starter;
+ std::vector<button_info*> m_buttons;
+
+ v2s32 m_upper_left;
+ v2s32 m_lower_right;
+
+ /* show settings bar */
+ bool m_active;
+
+ bool m_visible;
+
+ /* settings bar timeout */
+ float m_timeout;
+ float m_timeout_value;
+ bool m_initialized;
+ autohide_button_bar_dir m_dir;
+};
+
class TouchScreenGUI
{
public:
@@ -66,7 +145,7 @@ public:
void translateEvent(const SEvent &event);
- void init(ISimpleTextureSource* tsrc,float density);
+ void init(ISimpleTextureSource* tsrc);
double getYaw() { return m_camera_yaw; }
double getPitch() { return m_camera_pitch; }
@@ -77,8 +156,8 @@ public:
void registerHudItem(int index, const rect<s32> &rect);
void Toggle(bool visible);
- void Hide();
- void Show();
+ void hide();
+ void show();
private:
IrrlichtDevice* m_device;
@@ -104,15 +183,6 @@ private:
bool m_move_sent_as_mouse_event;
v2s32 m_move_downlocation;
- struct button_info {
- float repeatcounter;
- float repeatdelay;
- irr::EKEY_CODE keycode;
- std::vector<int> ids;
- IGUIButton* guibutton;
- bool immediate_release;
- };
-
button_info m_buttons[after_last_element_id];
/* gui button detection */
@@ -142,7 +212,7 @@ private:
std::vector<id_status> m_known_ids;
/* handle a button event */
- void ButtonEvent(touch_gui_button_id bID, int eventID, bool action);
+ void handleButtonEvent(touch_gui_button_id bID, int eventID, bool action);
/* handle pressed hud buttons */
bool isHUDButton(const SEvent &event);
@@ -153,6 +223,12 @@ private:
/* handle double taps */
bool doubleTapDetection();
+ /* handle release event */
+ void handleReleaseEvent(int evt_id);
+
+ /* get size of regular gui control button */
+ int getGuiButtonSize();
+
/* doubleclick detection variables */
struct key_event {
unsigned int down_time;
@@ -165,6 +241,12 @@ private:
/* array for doubletap detection */
key_event m_key_events[2];
+
+ /* settings bar */
+ AutoHideButtonBar m_settingsbar;
+
+ /* rare controls bar */
+ AutoHideButtonBar m_rarecontrolsbar;
};
extern TouchScreenGUI *g_touchscreengui;
#endif
diff --git a/src/unittest/CMakeLists.txt b/src/unittest/CMakeLists.txt
index bdff14f05..a07ed8ba5 100644
--- a/src/unittest/CMakeLists.txt
+++ b/src/unittest/CMakeLists.txt
@@ -17,6 +17,7 @@ set (UNITTEST_SRCS
${CMAKE_CURRENT_SOURCE_DIR}/test_serialization.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test_settings.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test_socket.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/test_threading.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test_utilities.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test_voxelalgorithms.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test_voxelmanipulator.cpp
diff --git a/src/unittest/test.cpp b/src/unittest/test.cpp
index d0ffb423f..41ccf0d2d 100644
--- a/src/unittest/test.cpp
+++ b/src/unittest/test.cpp
@@ -19,7 +19,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "test.h"
-#include "debug.h"
#include "log.h"
#include "nodedef.h"
#include "itemdef.h"
@@ -216,14 +215,14 @@ void TestGameDef::defineSomeNodes()
//// run_tests
////
-void run_tests()
+bool run_tests()
{
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
u32 t1 = porting::getTime(PRECISION_MILLI);
TestGameDef gamedef;
- log_set_lev_silence(LMT_ERROR, true);
+ g_logger.setLevelSilenced(LL_ERROR, true);
u32 num_modules_failed = 0;
u32 num_total_tests_failed = 0;
@@ -239,11 +238,11 @@ void run_tests()
u32 tdiff = porting::getTime(PRECISION_MILLI) - t1;
- log_set_lev_silence(LMT_ERROR, false);
+ g_logger.setLevelSilenced(LL_ERROR, false);
const char *overall_status = (num_modules_failed == 0) ? "PASSED" : "FAILED";
- dstream
+ rawstream
<< "++++++++++++++++++++++++++++++++++++++++"
<< "++++++++++++++++++++++++++++++++++++++++" << std::endl
<< "Unit Test Results: " << overall_status << std::endl
@@ -254,8 +253,7 @@ void run_tests()
<< "++++++++++++++++++++++++++++++++++++++++"
<< "++++++++++++++++++++++++++++++++++++++++" << std::endl;
- if (num_modules_failed)
- abort();
+ return num_modules_failed;
}
////
@@ -264,14 +262,14 @@ void run_tests()
bool TestBase::testModule(IGameDef *gamedef)
{
- dstream << "======== Testing module " << getName() << std::endl;
+ rawstream << "======== Testing module " << getName() << std::endl;
u32 t1 = porting::getTime(PRECISION_MILLI);
runTests(gamedef);
u32 tdiff = porting::getTime(PRECISION_MILLI) - t1;
- dstream << "======== Module " << getName() << " "
+ rawstream << "======== Module " << getName() << " "
<< (num_tests_failed ? "failed" : "passed") << " (" << num_tests_failed
<< " failures / " << num_tests_run << " tests) - " << tdiff
<< "ms" << std::endl;
diff --git a/src/unittest/test.h b/src/unittest/test.h
index 47a441e02..e60e657cc 100644
--- a/src/unittest/test.h
+++ b/src/unittest/test.h
@@ -32,60 +32,61 @@ class TestFailedException : public std::exception {
};
// Runs a unit test and reports results
-#define TEST(fxn, ...) do { \
- u32 t1 = porting::getTime(PRECISION_MILLI); \
- try { \
- fxn(__VA_ARGS__); \
- dstream << "[PASS] "; \
- } catch (TestFailedException &e) { \
- dstream << "[FAIL] "; \
- num_tests_failed++; \
- } catch (std::exception &e) { \
- dstream << "Caught unhandled exception: " << e.what() << std::endl; \
- dstream << "[FAIL] "; \
- num_tests_failed++; \
- } \
- num_tests_run++; \
- u32 tdiff = porting::getTime(PRECISION_MILLI) - t1; \
- dstream << #fxn << " - " << tdiff << "ms" << std::endl; \
+#define TEST(fxn, ...) do { \
+ u32 t1 = porting::getTime(PRECISION_MILLI); \
+ try { \
+ fxn(__VA_ARGS__); \
+ rawstream << "[PASS] "; \
+ } catch (TestFailedException &e) { \
+ rawstream << "[FAIL] "; \
+ num_tests_failed++; \
+ } catch (std::exception &e) { \
+ rawstream << "Caught unhandled exception: " << e.what() << std::endl; \
+ rawstream << "[FAIL] "; \
+ num_tests_failed++; \
+ } \
+ num_tests_run++; \
+ u32 tdiff = porting::getTime(PRECISION_MILLI) - t1; \
+ rawstream << #fxn << " - " << tdiff << "ms" << std::endl; \
} while (0)
// Asserts the specified condition is true, or fails the current unit test
-#define UASSERT(x) do { \
- if (!(x)) { \
- dstream << "Test assertion failed: " #x << std::endl \
- << " at " << fs::GetFilenameFromPath(__FILE__) \
- << ":" << __LINE__ << std::endl; \
- throw TestFailedException(); \
- } \
+#define UASSERT(x) do { \
+ if (!(x)) { \
+ rawstream << "Test assertion failed: " #x << std::endl \
+ << " at " << fs::GetFilenameFromPath(__FILE__) \
+ << ":" << __LINE__ << std::endl; \
+ throw TestFailedException(); \
+ } \
} while (0)
// Asserts the specified condition is true, or fails the current unit test
// and prints the format specifier fmt
-#define UTEST(x, fmt, ...) do { \
- if (!(x)) { \
- char utest_buf[1024]; \
- snprintf(utest_buf, sizeof(utest_buf), fmt, __VA_ARGS__); \
- dstream << "Test assertion failed: " << utest_buf << std::endl \
- << " at " << fs::GetFilenameFromPath(__FILE__) \
- << ":" << __LINE__ << std::endl; \
- throw TestFailedException(); \
- } \
+#define UTEST(x, fmt, ...) do { \
+ if (!(x)) { \
+ char utest_buf[1024]; \
+ snprintf(utest_buf, sizeof(utest_buf), fmt, __VA_ARGS__); \
+ rawstream << "Test assertion failed: " << utest_buf << std::endl \
+ << " at " << fs::GetFilenameFromPath(__FILE__) \
+ << ":" << __LINE__ << std::endl; \
+ throw TestFailedException(); \
+ } \
} while (0)
// Asserts the comparison specified by CMP is true, or fails the current unit test
-#define UASSERTCMP(T, CMP, actual, expected) do { \
- T a = (actual); \
- T e = (expected); \
- if (!(a CMP e)) { \
- dstream << "Test assertion failed: " << #actual << " " << #CMP << " " \
- << #expected << std::endl \
- << " at " << fs::GetFilenameFromPath(__FILE__) << ":" \
- << __LINE__ << std::endl \
- << " actual: " << a << std::endl << " expected: " \
- << e << std::endl; \
- throw TestFailedException(); \
- } \
+#define UASSERTCMP(T, CMP, actual, expected) do { \
+ T a = (actual); \
+ T e = (expected); \
+ if (!(a CMP e)) { \
+ rawstream \
+ << "Test assertion failed: " << #actual << " " << #CMP << " " \
+ << #expected << std::endl \
+ << " at " << fs::GetFilenameFromPath(__FILE__) << ":" \
+ << __LINE__ << std::endl \
+ << " actual: " << a << std::endl << " expected: " \
+ << e << std::endl; \
+ throw TestFailedException(); \
+ } \
} while (0)
#define UASSERTEQ(T, actual, expected) UASSERTCMP(T, ==, actual, expected)
@@ -141,6 +142,6 @@ extern content_t t_CONTENT_WATER;
extern content_t t_CONTENT_LAVA;
extern content_t t_CONTENT_BRICK;
-void run_tests();
+bool run_tests();
#endif
diff --git a/src/unittest/test_areastore.cpp b/src/unittest/test_areastore.cpp
index a0dcada94..62d446f5c 100644
--- a/src/unittest/test_areastore.cpp
+++ b/src/unittest/test_areastore.cpp
@@ -19,7 +19,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "test.h"
-#include "areastore.h"
+#include "util/areastore.h"
class TestAreaStore : public TestBase {
public:
@@ -31,6 +31,7 @@ public:
void genericStoreTest(AreaStore *store);
void testVectorStore();
void testSpatialStore();
+ void testSerialization();
};
static TestAreaStore g_test_instance;
@@ -41,6 +42,7 @@ void TestAreaStore::runTests(IGameDef *gamedef)
#if USE_SPATIAL
TEST(testSpatialStore);
#endif
+ TEST(testSerialization);
}
////////////////////////////////////////////////////////////////////////////////
@@ -62,18 +64,15 @@ void TestAreaStore::testSpatialStore()
void TestAreaStore::genericStoreTest(AreaStore *store)
{
Area a(v3s16(-10, -3, 5), v3s16(0, 29, 7));
- a.id = 1;
Area b(v3s16(-5, -2, 5), v3s16(0, 28, 6));
- b.id = 2;
Area c(v3s16(-7, -3, 6), v3s16(-1, 27, 7));
- c.id = 3;
std::vector<Area *> res;
UASSERTEQ(size_t, store->size(), 0);
store->reserve(2); // sic
- store->insertArea(a);
- store->insertArea(b);
- store->insertArea(c);
+ store->insertArea(&a);
+ store->insertArea(&b);
+ store->insertArea(&c);
UASSERTEQ(size_t, store->size(), 3);
store->getAreasForPos(&res, v3s16(-1, 0, 6));
@@ -81,20 +80,18 @@ void TestAreaStore::genericStoreTest(AreaStore *store)
res.clear();
store->getAreasForPos(&res, v3s16(0, 0, 7));
UASSERTEQ(size_t, res.size(), 1);
- UASSERTEQ(u32, res[0]->id, 1);
res.clear();
- store->removeArea(1);
+ store->removeArea(a.id);
store->getAreasForPos(&res, v3s16(0, 0, 7));
UASSERTEQ(size_t, res.size(), 0);
res.clear();
- store->insertArea(a);
+ store->insertArea(&a);
store->getAreasForPos(&res, v3s16(0, 0, 7));
UASSERTEQ(size_t, res.size(), 1);
- UASSERTEQ(u32, res[0]->id, 1);
res.clear();
store->getAreasInArea(&res, v3s16(-10, -3, 5), v3s16(0, 29, 7), false);
@@ -109,21 +106,57 @@ void TestAreaStore::genericStoreTest(AreaStore *store)
UASSERTEQ(size_t, res.size(), 3);
res.clear();
- store->removeArea(1);
- store->removeArea(2);
- store->removeArea(3);
+ store->removeArea(a.id);
+ store->removeArea(b.id);
+ store->removeArea(c.id);
Area d(v3s16(-100, -300, -200), v3s16(-50, -200, -100));
- d.id = 4;
d.data = "Hi!";
- store->insertArea(d);
+ store->insertArea(&d);
store->getAreasForPos(&res, v3s16(-75, -250, -150));
UASSERTEQ(size_t, res.size(), 1);
- UASSERTEQ(u32, res[0]->id, 4);
UASSERTEQ(u16, res[0]->data.size(), 3);
UASSERT(strncmp(res[0]->data.c_str(), "Hi!", 3) == 0);
res.clear();
- store->removeArea(4);
+ store->removeArea(d.id);
}
+
+void TestAreaStore::testSerialization()
+{
+ VectorAreaStore store;
+
+ Area a(v3s16(-1, 0, 1), v3s16(0, 1, 2));
+ a.data = "Area A";
+ store.insertArea(&a);
+
+ Area b(v3s16(123, 456, 789), v3s16(32000, 100, 10));
+ b.data = "Area B";
+ store.insertArea(&b);
+
+ std::ostringstream os;
+ store.serialize(os);
+ std::string str = os.str();
+
+ std::string str_wanted("\x00" // Version
+ "\x00\x02" // Count
+ "\xFF\xFF\x00\x00\x00\x01" // Area A min edge
+ "\x00\x00\x00\x01\x00\x02" // Area A max edge
+ "\x00\x06" // Area A data length
+ "Area A" // Area A data
+ "\x00\x7B\x00\x64\x00\x0A" // Area B min edge (last two swapped with max edge for sorting)
+ "\x7D\x00\x01\xC8\x03\x15" // Area B max edge (^)
+ "\x00\x06" // Area B data length
+ "Area B", // Area B data
+ 1 + 2 +
+ 6 + 6 + 2 + 6 +
+ 6 + 6 + 2 + 6);
+ UASSERTEQ(std::string, str, str_wanted);
+
+ std::istringstream is(str);
+ store.deserialize(is);
+
+ UASSERTEQ(size_t, store.size(), 4); // deserialize() doesn't clear the store
+}
+
diff --git a/src/unittest/test_collision.cpp b/src/unittest/test_collision.cpp
index e505de450..332d3fa13 100644
--- a/src/unittest/test_collision.cpp
+++ b/src/unittest/test_collision.cpp
@@ -51,7 +51,7 @@ void TestCollision::testAxisAlignedCollision()
aabb3f m(bx-2, by, bz, bx-1, by+1, bz+1);
v3f v(1, 0, 0);
f32 dtime = 0;
- UASSERT(axisAlignedCollision(s, m, v, 0, dtime) == 0);
+ UASSERT(axisAlignedCollision(s, m, v, 0, &dtime) == 0);
UASSERT(fabs(dtime - 1.000) < 0.001);
}
{
@@ -59,21 +59,21 @@ void TestCollision::testAxisAlignedCollision()
aabb3f m(bx-2, by, bz, bx-1, by+1, bz+1);
v3f v(-1, 0, 0);
f32 dtime = 0;
- UASSERT(axisAlignedCollision(s, m, v, 0, dtime) == -1);
+ UASSERT(axisAlignedCollision(s, m, v, 0, &dtime) == -1);
}
{
aabb3f s(bx, by, bz, bx+1, by+1, bz+1);
aabb3f m(bx-2, by+1.5, bz, bx-1, by+2.5, bz-1);
v3f v(1, 0, 0);
f32 dtime;
- UASSERT(axisAlignedCollision(s, m, v, 0, dtime) == -1);
+ UASSERT(axisAlignedCollision(s, m, v, 0, &dtime) == -1);
}
{
aabb3f s(bx, by, bz, bx+1, by+1, bz+1);
aabb3f m(bx-2, by-1.5, bz, bx-1.5, by+0.5, bz+1);
v3f v(0.5, 0.1, 0);
f32 dtime;
- UASSERT(axisAlignedCollision(s, m, v, 0, dtime) == 0);
+ UASSERT(axisAlignedCollision(s, m, v, 0, &dtime) == 0);
UASSERT(fabs(dtime - 3.000) < 0.001);
}
{
@@ -81,7 +81,7 @@ void TestCollision::testAxisAlignedCollision()
aabb3f m(bx-2, by-1.5, bz, bx-1.5, by+0.5, bz+1);
v3f v(0.5, 0.1, 0);
f32 dtime;
- UASSERT(axisAlignedCollision(s, m, v, 0, dtime) == 0);
+ UASSERT(axisAlignedCollision(s, m, v, 0, &dtime) == 0);
UASSERT(fabs(dtime - 3.000) < 0.001);
}
@@ -91,7 +91,7 @@ void TestCollision::testAxisAlignedCollision()
aabb3f m(bx+2, by, bz, bx+3, by+1, bz+1);
v3f v(-1, 0, 0);
f32 dtime;
- UASSERT(axisAlignedCollision(s, m, v, 0, dtime) == 0);
+ UASSERT(axisAlignedCollision(s, m, v, 0, &dtime) == 0);
UASSERT(fabs(dtime - 1.000) < 0.001);
}
{
@@ -99,21 +99,21 @@ void TestCollision::testAxisAlignedCollision()
aabb3f m(bx+2, by, bz, bx+3, by+1, bz+1);
v3f v(1, 0, 0);
f32 dtime;
- UASSERT(axisAlignedCollision(s, m, v, 0, dtime) == -1);
+ UASSERT(axisAlignedCollision(s, m, v, 0, &dtime) == -1);
}
{
aabb3f s(bx, by, bz, bx+1, by+1, bz+1);
aabb3f m(bx+2, by, bz+1.5, bx+3, by+1, bz+3.5);
v3f v(-1, 0, 0);
f32 dtime;
- UASSERT(axisAlignedCollision(s, m, v, 0, dtime) == -1);
+ UASSERT(axisAlignedCollision(s, m, v, 0, &dtime) == -1);
}
{
aabb3f s(bx, by, bz, bx+1, by+1, bz+1);
aabb3f m(bx+2, by-1.5, bz, bx+2.5, by-0.5, bz+1);
v3f v(-0.5, 0.2, 0);
f32 dtime;
- UASSERT(axisAlignedCollision(s, m, v, 0, dtime) == 1); // Y, not X!
+ UASSERT(axisAlignedCollision(s, m, v, 0, &dtime) == 1); // Y, not X!
UASSERT(fabs(dtime - 2.500) < 0.001);
}
{
@@ -121,7 +121,7 @@ void TestCollision::testAxisAlignedCollision()
aabb3f m(bx+2, by-1.5, bz, bx+2.5, by-0.5, bz+1);
v3f v(-0.5, 0.3, 0);
f32 dtime;
- UASSERT(axisAlignedCollision(s, m, v, 0, dtime) == 0);
+ UASSERT(axisAlignedCollision(s, m, v, 0, &dtime) == 0);
UASSERT(fabs(dtime - 2.000) < 0.001);
}
@@ -133,7 +133,7 @@ void TestCollision::testAxisAlignedCollision()
aabb3f m(bx+2.3, by+2.29, bz+2.29, bx+4.2, by+4.2, bz+4.2);
v3f v(-1./3, -1./3, -1./3);
f32 dtime;
- UASSERT(axisAlignedCollision(s, m, v, 0, dtime) == 0);
+ UASSERT(axisAlignedCollision(s, m, v, 0, &dtime) == 0);
UASSERT(fabs(dtime - 0.9) < 0.001);
}
{
@@ -141,7 +141,7 @@ void TestCollision::testAxisAlignedCollision()
aabb3f m(bx+2.29, by+2.3, bz+2.29, bx+4.2, by+4.2, bz+4.2);
v3f v(-1./3, -1./3, -1./3);
f32 dtime;
- UASSERT(axisAlignedCollision(s, m, v, 0, dtime) == 1);
+ UASSERT(axisAlignedCollision(s, m, v, 0, &dtime) == 1);
UASSERT(fabs(dtime - 0.9) < 0.001);
}
{
@@ -149,7 +149,7 @@ void TestCollision::testAxisAlignedCollision()
aabb3f m(bx+2.29, by+2.29, bz+2.3, bx+4.2, by+4.2, bz+4.2);
v3f v(-1./3, -1./3, -1./3);
f32 dtime;
- UASSERT(axisAlignedCollision(s, m, v, 0, dtime) == 2);
+ UASSERT(axisAlignedCollision(s, m, v, 0, &dtime) == 2);
UASSERT(fabs(dtime - 0.9) < 0.001);
}
{
@@ -157,7 +157,7 @@ void TestCollision::testAxisAlignedCollision()
aabb3f m(bx-4.2, by-4.2, bz-4.2, bx-2.3, by-2.29, bz-2.29);
v3f v(1./7, 1./7, 1./7);
f32 dtime;
- UASSERT(axisAlignedCollision(s, m, v, 0, dtime) == 0);
+ UASSERT(axisAlignedCollision(s, m, v, 0, &dtime) == 0);
UASSERT(fabs(dtime - 16.1) < 0.001);
}
{
@@ -165,7 +165,7 @@ void TestCollision::testAxisAlignedCollision()
aabb3f m(bx-4.2, by-4.2, bz-4.2, bx-2.29, by-2.3, bz-2.29);
v3f v(1./7, 1./7, 1./7);
f32 dtime;
- UASSERT(axisAlignedCollision(s, m, v, 0, dtime) == 1);
+ UASSERT(axisAlignedCollision(s, m, v, 0, &dtime) == 1);
UASSERT(fabs(dtime - 16.1) < 0.001);
}
{
@@ -173,7 +173,7 @@ void TestCollision::testAxisAlignedCollision()
aabb3f m(bx-4.2, by-4.2, bz-4.2, bx-2.29, by-2.29, bz-2.3);
v3f v(1./7, 1./7, 1./7);
f32 dtime;
- UASSERT(axisAlignedCollision(s, m, v, 0, dtime) == 2);
+ UASSERT(axisAlignedCollision(s, m, v, 0, &dtime) == 2);
UASSERT(fabs(dtime - 16.1) < 0.001);
}
}
diff --git a/src/unittest/test_serialization.cpp b/src/unittest/test_serialization.cpp
index 49f348e9c..4cbc999ea 100644
--- a/src/unittest/test_serialization.cpp
+++ b/src/unittest/test_serialization.cpp
@@ -40,6 +40,9 @@ public:
void testDeSerializeLongString();
void testStreamRead();
void testStreamWrite();
+ void testVecPut();
+ void testStringLengthLimits();
+ void testBufReader();
std::string teststring2;
std::wstring teststring2_w;
@@ -64,6 +67,9 @@ void TestSerialization::runTests(IGameDef *gamedef)
TEST(testSerializeHex);
TEST(testStreamRead);
TEST(testStreamWrite);
+ TEST(testVecPut);
+ TEST(testStringLengthLimits);
+ TEST(testBufReader);
}
////////////////////////////////////////////////////////////////////////////////
@@ -369,6 +375,263 @@ void TestSerialization::testStreamWrite()
}
+void TestSerialization::testVecPut()
+{
+ std::vector<u8> buf;
+
+ putU8(&buf, 0x11);
+ putU16(&buf, 0x2233);
+ putU32(&buf, 0x44556677);
+ putU64(&buf, 0x8899AABBCCDDEEFF);
+
+ 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));
+ putV2F1000(&buf, v2f(500.65661f, 350.34567f));
+
+ 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;
+ v2f v2f_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() == 0x8899AABBCCDDEEFF);
+ 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.getV2F1000() == v2f(500.656f, 350.345f));
+ 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.getV2F1000());
+ 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.getV2F1000NoEx(&v2f_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 == 0x8899AABBCCDDEEFF);
+ 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(v2f_data == v2f(500.656f, 350.345f));
+ 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.getV2F1000NoEx(&v2f_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)));
+}
+
+
const u8 TestSerialization::test_serialized_data[12 * 13] = {
0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc,
0xdd, 0xee, 0xff, 0x80, 0x75, 0x30, 0xff, 0xff, 0xff, 0xfa, 0xff, 0xff,
diff --git a/src/unittest/test_threading.cpp b/src/unittest/test_threading.cpp
new file mode 100644
index 000000000..f0df85b2d
--- /dev/null
+++ b/src/unittest/test_threading.cpp
@@ -0,0 +1,182 @@
+/*
+Minetest
+Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@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 "test.h"
+
+#include "threading/atomic.h"
+#include "threading/semaphore.h"
+#include "threading/thread.h"
+
+
+class TestThreading : public TestBase {
+public:
+ TestThreading() { TestManager::registerTestModule(this); }
+ const char *getName() { return "TestThreading"; }
+ void runTests(IGameDef *gamedef);
+
+ void testStartStopWait();
+ void testThreadKill();
+ void testAtomicSemaphoreThread();
+};
+
+static TestThreading g_test_instance;
+
+void TestThreading::runTests(IGameDef *gamedef)
+{
+ TEST(testStartStopWait);
+ TEST(testThreadKill);
+ TEST(testAtomicSemaphoreThread);
+}
+
+class SimpleTestThread : public Thread {
+public:
+ SimpleTestThread(unsigned int interval) :
+ Thread("SimpleTest"),
+ m_interval(interval)
+ {
+ }
+
+private:
+ void *run()
+ {
+ void *retval = this;
+
+ if (isCurrentThread() == false)
+ retval = (void *)0xBAD;
+
+ while (!stopRequested())
+ sleep_ms(m_interval);
+
+ return retval;
+ }
+
+ unsigned int m_interval;
+};
+
+void TestThreading::testStartStopWait()
+{
+ void *thread_retval;
+ SimpleTestThread *thread = new SimpleTestThread(25);
+
+ // Try this a couple times, since a Thread should be reusable after waiting
+ for (size_t i = 0; i != 5; i++) {
+ // Can't wait() on a joined, stopped thread
+ UASSERT(thread->wait() == false);
+
+ // start() should work the first time, but not the second.
+ UASSERT(thread->start() == true);
+ UASSERT(thread->start() == false);
+
+ UASSERT(thread->isRunning() == true);
+ UASSERT(thread->isCurrentThread() == false);
+
+ // Let it loop a few times...
+ sleep_ms(70);
+
+ // It's still running, so the return value shouldn't be available to us.
+ UASSERT(thread->getReturnValue(&thread_retval) == false);
+
+ // stop() should always succeed
+ UASSERT(thread->stop() == true);
+
+ // wait() only needs to wait the first time - the other two are no-ops.
+ UASSERT(thread->wait() == true);
+ UASSERT(thread->wait() == false);
+ UASSERT(thread->wait() == false);
+
+ // Now that the thread is stopped, we should be able to get the
+ // return value, and it should be the object itself.
+ thread_retval = NULL;
+ UASSERT(thread->getReturnValue(&thread_retval) == true);
+ UASSERT(thread_retval == thread);
+ }
+
+ delete thread;
+}
+
+
+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:
+ AtomicTestThread(Atomic<u32> &v, Semaphore &trigger) :
+ Thread("AtomicTest"),
+ val(v),
+ trigger(trigger)
+ {
+ }
+
+private:
+ void *run()
+ {
+ trigger.wait();
+ for (u32 i = 0; i < 0x10000; ++i)
+ ++val;
+ return NULL;
+ }
+
+ Atomic<u32> &val;
+ Semaphore &trigger;
+};
+
+
+void TestThreading::testAtomicSemaphoreThread()
+{
+ Atomic<u32> val;
+ Semaphore trigger;
+ static const u8 num_threads = 4;
+
+ AtomicTestThread *threads[num_threads];
+ for (u8 i = 0; i < num_threads; ++i) {
+ threads[i] = new AtomicTestThread(val, trigger);
+ UASSERT(threads[i]->start());
+ }
+
+ trigger.post(num_threads);
+
+ for (u8 i = 0; i < num_threads; ++i) {
+ threads[i]->wait();
+ delete threads[i];
+ }
+
+ UASSERT(val == num_threads * 0x10000);
+}
+
diff --git a/src/unittest/test_utilities.cpp b/src/unittest/test_utilities.cpp
index df90d37bd..d73975b9f 100644
--- a/src/unittest/test_utilities.cpp
+++ b/src/unittest/test_utilities.cpp
@@ -43,7 +43,9 @@ public:
void testStrToIntConversion();
void testStringReplace();
void testStringAllowed();
+ void testAsciiPrintableHelper();
void testUTF8();
+ void testRemoveEscapes();
void testWrapRows();
void testIsNumber();
void testIsPowerOfTwo();
@@ -68,7 +70,9 @@ void TestUtilities::runTests(IGameDef *gamedef)
TEST(testStrToIntConversion);
TEST(testStringReplace);
TEST(testStringAllowed);
+ TEST(testAsciiPrintableHelper);
TEST(testUTF8);
+ TEST(testRemoveEscapes);
TEST(testWrapRows);
TEST(testIsNumber);
TEST(testIsPowerOfTwo);
@@ -232,6 +236,18 @@ void TestUtilities::testStringAllowed()
UASSERT(string_allowed_blacklist("hello123", "123") == false);
}
+void TestUtilities::testAsciiPrintableHelper()
+{
+ UASSERT(IS_ASCII_PRINTABLE_CHAR('e') == true);
+ UASSERT(IS_ASCII_PRINTABLE_CHAR('\0') == false);
+
+ // Ensures that there is no cutting off going on...
+ // If there were, 331 would be cut to 75 in this example
+ // and 73 is a valid ASCII char.
+ int ch = 331;
+ UASSERT(IS_ASCII_PRINTABLE_CHAR(ch) == false);
+}
+
void TestUtilities::testUTF8()
{
UASSERT(wide_to_utf8(utf8_to_wide("")) == "");
@@ -239,6 +255,23 @@ void TestUtilities::testUTF8()
== "the shovel dug a crumbly node!");
}
+void TestUtilities::testRemoveEscapes()
+{
+ UASSERT(unescape_enriched<wchar_t>(
+ L"abc\x1bXdef") == L"abcdef");
+ UASSERT(unescape_enriched<wchar_t>(
+ L"abc\x1b(escaped)def") == L"abcdef");
+ UASSERT(unescape_enriched<wchar_t>(
+ L"abc\x1b((escaped with parenthesis\\))def") == L"abcdef");
+ UASSERT(unescape_enriched<wchar_t>(
+ L"abc\x1b(incomplete") == L"abc");
+ UASSERT(unescape_enriched<wchar_t>(
+ L"escape at the end\x1b") == L"escape at the end");
+ // Nested escapes not supported
+ UASSERT(unescape_enriched<wchar_t>(
+ L"abc\x1b(outer \x1b(inner escape)escape)def") == L"abcescape)def");
+}
+
void TestUtilities::testWrapRows()
{
UASSERT(wrap_rows("12345678",4) == "1234\n5678");
@@ -282,7 +315,7 @@ void TestUtilities::testIsPowerOfTwo()
UASSERT(is_power_of_two((1 << exponent)) == true);
UASSERT(is_power_of_two((1 << exponent) + 1) == false);
}
- UASSERT(is_power_of_two((u32)-1) == false);
+ UASSERT(is_power_of_two(U32_MAX) == false);
}
void TestUtilities::testMyround()
diff --git a/src/util/CMakeLists.txt b/src/util/CMakeLists.txt
index 33900a43a..0e7cbad07 100644
--- a/src/util/CMakeLists.txt
+++ b/src/util/CMakeLists.txt
@@ -1,4 +1,5 @@
set(UTIL_SRCS
+ ${CMAKE_CURRENT_SOURCE_DIR}/areastore.cpp
${CMAKE_CURRENT_SOURCE_DIR}/auth.cpp
${CMAKE_CURRENT_SOURCE_DIR}/base64.cpp
${CMAKE_CURRENT_SOURCE_DIR}/directiontables.cpp
diff --git a/src/areastore.cpp b/src/util/areastore.cpp
index f9362c4a6..58f08a8c2 100644
--- a/src/areastore.cpp
+++ b/src/util/areastore.cpp
@@ -17,7 +17,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#include "areastore.h"
+#include "util/areastore.h"
#include "util/serialize.h"
#include "util/container.h"
@@ -44,97 +44,70 @@ with this program; if not, write to the Free Software Foundation, Inc.,
AST_OVERLAPS_IN_DIMENSION((amine), (amaxe), (b), Y) && \
AST_OVERLAPS_IN_DIMENSION((amine), (amaxe), (b), Z))
-u16 AreaStore::size() const
+
+AreaStore *AreaStore::getOptimalImplementation()
{
- return areas_map.size();
+#if USE_SPATIAL
+ return new SpatialAreaStore();
+#else
+ return new VectorAreaStore();
+#endif
}
-u32 AreaStore::getFreeId(v3s16 minedge, v3s16 maxedge)
+const Area *AreaStore::getArea(u32 id) const
{
- int keep_on = 100;
- while (keep_on--) {
- m_highest_id++;
- // Handle overflows, we dont want to return 0
- if (m_highest_id == AREA_ID_INVALID)
- m_highest_id++;
- if (areas_map.find(m_highest_id) == areas_map.end())
- return m_highest_id;
- }
- // search failed
- return AREA_ID_INVALID;
+ AreaMap::const_iterator it = areas_map.find(id);
+ if (it == areas_map.end())
+ return NULL;
+ return &it->second;
}
-const Area *AreaStore::getArea(u32 id) const
+void AreaStore::serialize(std::ostream &os) const
{
- const Area *res = NULL;
- std::map<u32, Area>::const_iterator itr = areas_map.find(id);
- if (itr != areas_map.end()) {
- res = &itr->second;
+ writeU8(os, 0); // Serialisation version
+
+ // TODO: Compression?
+ writeU16(os, areas_map.size());
+ for (AreaMap::const_iterator it = areas_map.begin();
+ it != areas_map.end(); ++it) {
+ const Area &a = it->second;
+ writeV3S16(os, a.minedge);
+ writeV3S16(os, a.maxedge);
+ writeU16(os, a.data.size());
+ os.write(a.data.data(), a.data.size());
}
- return res;
}
-#if 0
-Currently, serialisation is commented out. This is because of multiple reasons:
-1. Why do we store the areastore into a file, why not into the database?
-2. We don't use libspatial's serialisation, but we should, or perhaps not, because
- it would remove the ability to switch. Perhaps write migration routines?
-3. Various things need fixing, e.g. the size is serialized as
- c++ implementation defined size_t
-bool AreaStore::deserialize(std::istream &is)
+void AreaStore::deserialize(std::istream &is)
{
u8 ver = readU8(is);
- if (ver != 1)
- return false;
- u16 count_areas = readU16(is);
- for (u16 i = 0; i < count_areas; i++) {
- // deserialize an area
+ if (ver != 0)
+ throw SerializationError("Unknown AreaStore "
+ "serialization version!");
+
+ u16 num_areas = readU16(is);
+ for (u32 i = 0; i < num_areas; ++i) {
Area a;
- a.id = readU32(is);
a.minedge = readV3S16(is);
a.maxedge = readV3S16(is);
- a.datalen = readU16(is);
- a.data = new char[a.datalen];
- is.read((char *) a.data, a.datalen);
- insertArea(a);
+ u16 data_len = readU16(is);
+ char *data = new char[data_len];
+ is.read(data, data_len);
+ a.data = std::string(data, data_len);
+ insertArea(&a);
}
- return true;
}
-
-static bool serialize_area(void *ostr, Area *a)
-{
- std::ostream &os = *((std::ostream *) ostr);
- writeU32(os, a->id);
- writeV3S16(os, a->minedge);
- writeV3S16(os, a->maxedge);
- writeU16(os, a->datalen);
- os.write(a->data, a->datalen);
-
- return false;
-}
-
-
-void AreaStore::serialize(std::ostream &os) const
-{
- // write initial data
- writeU8(os, 1); // serialisation version
- writeU16(os, areas_map.size()); //DANGER: not platform independent
- forEach(&serialize_area, &os);
-}
-
-#endif
-
void AreaStore::invalidateCache()
{
- if (cache_enabled) {
+ if (m_cache_enabled) {
m_res_cache.invalidate();
}
}
void AreaStore::setCacheParams(bool enabled, u8 block_radius, size_t limit)
{
- cache_enabled = enabled;
+ m_cache_enabled = enabled;
m_cacheblock_radius = MYMAX(block_radius, 16);
m_res_cache.setLimit(MYMAX(limit, 20));
invalidateCache();
@@ -163,7 +136,7 @@ void AreaStore::cacheMiss(void *data, const v3s16 &mpos, std::vector<Area *> *de
void AreaStore::getAreasForPos(std::vector<Area *> *result, v3s16 pos)
{
- if (cache_enabled) {
+ if (m_cache_enabled) {
v3s16 mblock = getContainerPos(pos, m_cacheblock_radius);
const std::vector<Area *> *pre_list = m_res_cache.lookupCache(mblock);
@@ -185,42 +158,41 @@ void AreaStore::getAreasForPos(std::vector<Area *> *result, v3s16 pos)
////
-void VectorAreaStore::insertArea(const Area &a)
+bool VectorAreaStore::insertArea(Area *a)
{
- areas_map[a.id] = a;
- m_areas.push_back(&(areas_map[a.id]));
+ if (a->id == U32_MAX)
+ a->id = getNextId();
+ std::pair<AreaMap::iterator, bool> res =
+ areas_map.insert(std::make_pair(a->id, *a));
+ if (!res.second)
+ // ID is not unique
+ return false;
+ m_areas.push_back(&res.first->second);
invalidateCache();
-}
-
-void VectorAreaStore::reserve(size_t count)
-{
- m_areas.reserve(count);
+ return true;
}
bool VectorAreaStore::removeArea(u32 id)
{
- std::map<u32, Area>::iterator itr = areas_map.find(id);
- if (itr != areas_map.end()) {
- size_t msiz = m_areas.size();
- for (size_t i = 0; i < msiz; i++) {
- Area * b = m_areas[i];
- if (b->id == id) {
- areas_map.erase(itr);
- m_areas.erase(m_areas.begin() + i);
- invalidateCache();
- return true;
- }
+ AreaMap::iterator it = areas_map.find(id);
+ if (it == areas_map.end())
+ return false;
+ Area *a = &it->second;
+ for (std::vector<Area *>::iterator v_it = m_areas.begin();
+ v_it != m_areas.end(); ++v_it) {
+ if (*v_it == a) {
+ m_areas.erase(v_it);
+ break;
}
- // we should never get here, it means we did find it in map,
- // but not in the vector
}
- return false;
+ areas_map.erase(it);
+ invalidateCache();
+ return true;
}
void VectorAreaStore::getAreasForPosImpl(std::vector<Area *> *result, v3s16 pos)
{
- size_t msiz = m_areas.size();
- for (size_t i = 0; i < msiz; i++) {
+ for (size_t i = 0; i < m_areas.size(); ++i) {
Area *b = m_areas[i];
if (AST_CONTAINS_PT(b, pos)) {
result->push_back(b);
@@ -231,9 +203,8 @@ void VectorAreaStore::getAreasForPosImpl(std::vector<Area *> *result, v3s16 pos)
void VectorAreaStore::getAreasInArea(std::vector<Area *> *result,
v3s16 minedge, v3s16 maxedge, bool accept_overlap)
{
- size_t msiz = m_areas.size();
- for (size_t i = 0; i < msiz; i++) {
- Area * b = m_areas[i];
+ for (size_t i = 0; i < m_areas.size(); ++i) {
+ Area *b = m_areas[i];
if (accept_overlap ? AST_AREAS_OVERLAP(minedge, maxedge, b) :
AST_CONTAINS_AREA(minedge, maxedge, b)) {
result->push_back(b);
@@ -241,19 +212,6 @@ void VectorAreaStore::getAreasInArea(std::vector<Area *> *result,
}
}
-#if 0
-bool VectorAreaStore::forEach(bool (*callback)(void *args, Area *a), void *args) const
-{
- size_t msiz = m_areas.size();
- for (size_t i = 0; i < msiz; i++) {
- if (callback(args, m_areas[i])) {
- return true;
- }
- }
- return false;
-}
-#endif
-
#if USE_SPATIAL
static inline SpatialIndex::Region get_spatial_region(const v3s16 minedge,
@@ -273,11 +231,16 @@ static inline SpatialIndex::Point get_spatial_point(const v3s16 pos)
}
-void SpatialAreaStore::insertArea(const Area &a)
+bool SpatialAreaStore::insertArea(Area *a)
{
- areas_map[a.id] = a;
- m_tree->insertData(0, NULL, get_spatial_region(a.minedge, a.maxedge), a.id);
+ if (a->id == U32_MAX)
+ a->id = getNextId();
+ if (!areas_map.insert(std::make_pair(a->id, *a)).second)
+ // ID is not unique
+ return false;
+ m_tree->insertData(0, NULL, get_spatial_region(a->minedge, a->maxedge), a->id);
invalidateCache();
+ return true;
}
bool SpatialAreaStore::removeArea(u32 id)
@@ -287,6 +250,7 @@ bool SpatialAreaStore::removeArea(u32 id)
Area *a = &itr->second;
bool result = m_tree->deleteData(get_spatial_region(a->minedge,
a->maxedge), id);
+ areas_map.erase(itr);
invalidateCache();
return result;
} else {
@@ -312,14 +276,6 @@ void SpatialAreaStore::getAreasInArea(std::vector<Area *> *result,
}
}
-#if 0
-bool SpatialAreaStore::forEach(bool (*callback)(void *args, Area *a), void *args) const
-{
- // TODO ?? (this is only needed for serialisation, but libspatial has its own serialisation)
- return false;
-}
-#endif
-
SpatialAreaStore::~SpatialAreaStore()
{
delete m_tree;
diff --git a/src/areastore.h b/src/util/areastore.h
index 57d96450b..bebecfd78 100644
--- a/src/areastore.h
+++ b/src/util/areastore.h
@@ -17,8 +17,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef AREASTORE_H_
-#define AREASTORE_H_
+#ifndef AREA_STORE_H_
+#define AREA_STORE_H_
#include "irr_v3d.h"
#include "noise.h" // for PcgRandom
@@ -36,141 +36,147 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "util/serialize.h"
#endif
-#define AST_EXTREMIFY(min, max, pa, pb) \
- (min).X = MYMIN((pa).X, (pb).X); \
- (min).Y = MYMIN((pa).Y, (pb).Y); \
- (min).Z = MYMIN((pa).Z, (pb).Z); \
- (max).X = MYMAX((pa).X, (pb).X); \
- (max).Y = MYMAX((pa).Y, (pb).Y); \
- (max).Z = MYMAX((pa).Z, (pb).Z);
-
-#define AREA_ID_INVALID 0
struct Area {
- Area(const v3s16 &minedge, const v3s16 &maxedge)
+ Area() : id(U32_MAX) {}
+ Area(const v3s16 &mine, const v3s16 &maxe) :
+ id(U32_MAX), minedge(mine), maxedge(maxe)
{
- this->minedge = minedge;
- this->maxedge = maxedge;
- }
-
- Area() {}
-
- void extremifyEdges()
- {
- v3s16 nminedge;
- v3s16 nmaxedge;
-
- AST_EXTREMIFY(nminedge, nmaxedge, minedge, maxedge)
-
- maxedge = nmaxedge;
- minedge = nminedge;
+ sortBoxVerticies(minedge, maxedge);
}
u32 id;
- v3s16 minedge;
- v3s16 maxedge;
+ v3s16 minedge, maxedge;
std::string data;
};
-std::vector<std::string> get_areastore_typenames();
class AreaStore {
-protected:
- // TODO change to unordered_map when we can
- std::map<u32, Area> areas_map;
- void invalidateCache();
- virtual void getAreasForPosImpl(std::vector<Area *> *result, v3s16 pos) = 0;
- bool cache_enabled; // don't write to this from subclasses, only read.
public:
- virtual void insertArea(const Area &a) = 0;
+ AreaStore() :
+ m_cache_enabled(true),
+ m_cacheblock_radius(64),
+ m_res_cache(1000, &cacheMiss, this),
+ m_next_id(0)
+ {}
+
+ virtual ~AreaStore() {}
+
+ static AreaStore *getOptimalImplementation();
+
virtual void reserve(size_t count) {};
+ size_t size() const { return areas_map.size(); }
+
+ /// Add an area to the store.
+ /// Updates the area's ID if it hasn't already been set.
+ /// @return Whether the area insertion was successful.
+ virtual bool insertArea(Area *a) = 0;
+
+ /// Removes an area from the store by ID.
+ /// @return Whether the area was in the store and removed.
virtual bool removeArea(u32 id) = 0;
+
+ /// Finds areas that the passed position is contained in.
+ /// Stores output in passed vector.
void getAreasForPos(std::vector<Area *> *result, v3s16 pos);
+
+ /// Finds areas that are completely contained inside the area defined
+ /// by the passed edges. If @p accept_overlap is true this finds any
+ /// areas that intersect with the passed area at any point.
virtual void getAreasInArea(std::vector<Area *> *result,
v3s16 minedge, v3s16 maxedge, bool accept_overlap) = 0;
-#if 0
- // calls a passed function for every stored area, until the
- // callback returns true. If that happens, it returns true,
- // if the search is exhausted, it returns false
- virtual bool forEach(bool (*callback)(void *args, Area *a), void *args) const = 0;
-#endif
-
- virtual ~AreaStore()
- {}
-
- AreaStore() :
- cache_enabled(true),
- m_cacheblock_radius(64),
- m_res_cache(1000, &cacheMiss, this),
- m_highest_id(0)
- {
- }
-
+ /// Sets cache parameters.
void setCacheParams(bool enabled, u8 block_radius, size_t limit);
- u32 getFreeId(v3s16 minedge, v3s16 maxedge);
+ /// Returns a pointer to the area coresponding to the passed ID,
+ /// or NULL if it doesn't exist.
const Area *getArea(u32 id) const;
- u16 size() const;
-#if 0
- bool deserialize(std::istream &is);
+
+ /// Serializes the store's areas to a binary ostream.
void serialize(std::ostream &is) const;
-#endif
+
+ /// Deserializes the Areas from a binary istream.
+ /// This does not currently clear the AreaStore before adding the
+ /// areas, making it possible to deserialize multiple serialized
+ /// AreaStores.
+ void deserialize(std::istream &is);
+
+protected:
+ /// Invalidates the getAreasForPos cache.
+ /// Call after adding or removing an area.
+ void invalidateCache();
+
+ /// Implementation of getAreasForPos.
+ /// getAreasForPos calls this if the cache is disabled.
+ virtual void getAreasForPosImpl(std::vector<Area *> *result, v3s16 pos) = 0;
+
+ /// Returns the next area ID and increments it.
+ u32 getNextId() { return m_next_id++; }
+
+ // Note: This can't be an unordered_map, since all
+ // references would be invalidated on rehash.
+ typedef std::map<u32, Area> AreaMap;
+ AreaMap areas_map;
+
private:
+ /// Called by the cache when a value isn't found in the cache.
static void cacheMiss(void *data, const v3s16 &mpos, std::vector<Area *> *dest);
- u8 m_cacheblock_radius; // if you modify this, call invalidateCache()
+
+ bool m_cache_enabled;
+ /// Range, in nodes, of the getAreasForPos cache.
+ /// If you modify this, call invalidateCache()
+ u8 m_cacheblock_radius;
LRUCache<v3s16, std::vector<Area *> > m_res_cache;
- u32 m_highest_id;
+ u32 m_next_id;
};
class VectorAreaStore : public AreaStore {
-protected:
- virtual void getAreasForPosImpl(std::vector<Area *> *result, v3s16 pos);
public:
- virtual void insertArea(const Area &a);
- virtual void reserve(size_t count);
+ virtual void reserve(size_t count) { m_areas.reserve(count); }
+ virtual bool insertArea(Area *a);
virtual bool removeArea(u32 id);
virtual void getAreasInArea(std::vector<Area *> *result,
v3s16 minedge, v3s16 maxedge, bool accept_overlap);
- // virtual bool forEach(bool (*callback)(void *args, Area *a), void *args) const;
+
+protected:
+ virtual void getAreasForPosImpl(std::vector<Area *> *result, v3s16 pos);
+
private:
std::vector<Area *> m_areas;
};
+
#if USE_SPATIAL
class SpatialAreaStore : public AreaStore {
-protected:
- virtual void getAreasForPosImpl(std::vector<Area *> *result, v3s16 pos);
public:
SpatialAreaStore();
- virtual void insertArea(const Area &a);
+ virtual ~SpatialAreaStore();
+
+ virtual bool insertArea(Area *a);
virtual bool removeArea(u32 id);
virtual void getAreasInArea(std::vector<Area *> *result,
v3s16 minedge, v3s16 maxedge, bool accept_overlap);
- // virtual bool forEach(bool (*callback)(void *args, Area *a), void *args) const;
- virtual ~SpatialAreaStore();
+protected:
+ virtual void getAreasForPosImpl(std::vector<Area *> *result, v3s16 pos);
+
private:
SpatialIndex::ISpatialIndex *m_tree;
SpatialIndex::IStorageManager *m_storagemanager;
class VectorResultVisitor : public SpatialIndex::IVisitor {
- private:
- SpatialAreaStore *m_store;
- std::vector<Area *> *m_result;
public:
- VectorResultVisitor(std::vector<Area *> *result, SpatialAreaStore *store)
- {
- m_store = store;
- m_result = result;
- }
+ VectorResultVisitor(std::vector<Area *> *result, SpatialAreaStore *store) :
+ m_store(store),
+ m_result(result)
+ {}
+ ~VectorResultVisitor() {}
- virtual void visitNode(const SpatialIndex::INode &in)
- {
- }
+ virtual void visitNode(const SpatialIndex::INode &in) {}
virtual void visitData(const SpatialIndex::IData &in)
{
@@ -187,10 +193,12 @@ private:
visitData(*(v[i]));
}
- ~VectorResultVisitor() {}
+ private:
+ SpatialAreaStore *m_store;
+ std::vector<Area *> *m_result;
};
};
-#endif
+#endif // USE_SPATIAL
-#endif /* AREASTORE_H_ */
+#endif // AREA_STORE_H_
diff --git a/src/util/auth.cpp b/src/util/auth.cpp
index df8940e87..912987259 100644
--- a/src/util/auth.cpp
+++ b/src/util/auth.cpp
@@ -1,6 +1,6 @@
/*
Minetest
-Copyright (C) 2015 est31 <MTest31@outlook.com>
+Copyright (C) 2015, 2016 est31 <MTest31@outlook.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
@@ -24,13 +24,14 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "sha1.h"
#include "srp.h"
#include "string.h"
+#include "debug.h"
// Get an sha-1 hash of the player's name combined with
// the password entered. That's what the server uses as
// their password. (Exception : if the password field is
// blank, we send a blank password - this is for backwards
// compatibility with password-less players).
-std::string translatePassword(const std::string &name,
+std::string translate_password(const std::string &name,
const std::string &password)
{
if (password.length() == 0)
@@ -45,82 +46,92 @@ std::string translatePassword(const std::string &name,
return pwd;
}
-void getSRPVerifier(const std::string &name,
+// Call lower level SRP code to generate a verifier with the
+// given pointers. Contains the preparations, call parameters
+// and error checking common to all srp verifier generation code.
+// See docs of srp_create_salted_verification_key for more info.
+static inline void gen_srp_v(const std::string &name,
const std::string &password, char **salt, size_t *salt_len,
char **bytes_v, size_t *len_v)
{
std::string n_name = lowercase(name);
- srp_create_salted_verification_key(SRP_SHA256, SRP_NG_2048,
+ SRP_Result res = srp_create_salted_verification_key(SRP_SHA256, SRP_NG_2048,
n_name.c_str(), (const unsigned char *)password.c_str(),
password.size(), (unsigned char **)salt, salt_len,
(unsigned char **)bytes_v, len_v, NULL, NULL);
+ FATAL_ERROR_IF(res != SRP_OK, "Couldn't create salted SRP verifier");
}
-// Get a db-ready SRP verifier
-// If the salt param is NULL, one is automatically generated.
-// Please free() it afterwards. You shouldn't use it for other purposes,
-// as you will need the contents of salt_len too.
-inline static std::string getSRPVerifier(const std::string &name,
- const std::string &password, char ** salt, size_t salt_len)
+/// Creates a verification key with given salt and password.
+std::string generate_srp_verifier(const std::string &name,
+ const std::string &password, const std::string &salt)
{
- char * bytes_v = NULL;
- size_t len_v;
- getSRPVerifier(name, password, salt, &salt_len,
- &bytes_v, &len_v);
- std::string ret_val = encodeSRPVerifier(std::string(bytes_v, len_v),
- std::string(*salt, salt_len));
+ size_t salt_len = salt.size();
+ // The API promises us that the salt doesn't
+ // get modified if &salt_ptr isn't NULL.
+ char *salt_ptr = (char *)salt.c_str();
+
+ char *bytes_v = NULL;
+ size_t verifier_len = 0;
+ gen_srp_v(name, password, &salt_ptr, &salt_len, &bytes_v, &verifier_len);
+ std::string verifier = std::string(bytes_v, verifier_len);
free(bytes_v);
- return ret_val;
+ return verifier;
}
-// Get a db-ready SRP verifier
-std::string getSRPVerifier(const std::string &name,
- const std::string &password)
+/// Creates a verification key and salt with given password.
+void generate_srp_verifier_and_salt(const std::string &name,
+ const std::string &password, std::string *verifier,
+ std::string *salt)
{
- char * salt = NULL;
- std::string ret_val = getSRPVerifier(name,
- password, &salt, 0);
- free(salt);
- return ret_val;
+ char *bytes_v = NULL;
+ size_t verifier_len;
+ char *salt_ptr = NULL;
+ size_t salt_len;
+ gen_srp_v(name, password, &salt_ptr, &salt_len, &bytes_v, &verifier_len);
+ *verifier = std::string(bytes_v, verifier_len);
+ *salt = std::string(salt_ptr, salt_len);
+ free(bytes_v);
+ free(salt_ptr);
}
-// Get a db-ready SRP verifier
-std::string getSRPVerifier(const std::string &name,
- const std::string &password, const std::string &salt)
+/// Gets an SRP verifier, generating a salt,
+/// and encodes it as DB-ready string.
+std::string get_encoded_srp_verifier(const std::string &name,
+ const std::string &password)
{
- // The implementation won't change the salt if its set,
- // therefore we can cast.
- char *salt_cstr = (char *)salt.c_str();
- return getSRPVerifier(name, password,
- &salt_cstr, salt.size());
+ std::string verifier;
+ std::string salt;
+ generate_srp_verifier_and_salt(name, password, &verifier, &salt);
+ return encode_srp_verifier(verifier, salt);
}
-// Make a SRP verifier db-ready
-std::string encodeSRPVerifier(const std::string &verifier,
+/// Converts the passed SRP verifier into a DB-ready format.
+std::string encode_srp_verifier(const std::string &verifier,
const std::string &salt)
{
std::ostringstream ret_str;
ret_str << "#1#"
- << base64_encode((unsigned char*) salt.c_str(), salt.size()) << "#"
- << base64_encode((unsigned char*) verifier.c_str(), verifier.size());
+ << base64_encode((unsigned char *)salt.c_str(), salt.size()) << "#"
+ << base64_encode((unsigned char *)verifier.c_str(), verifier.size());
return ret_str.str();
}
-bool decodeSRPVerifier(const std::string &enc_pwd,
- std::string *salt, std::string *bytes_v)
+/// Reads the DB-formatted SRP verifier and gets the verifier
+/// and salt components.
+bool decode_srp_verifier_and_salt(const std::string &encoded,
+ std::string *verifier, std::string *salt)
{
- std::vector<std::string> pwd_components = str_split(enc_pwd, '#');
+ std::vector<std::string> components = str_split(encoded, '#');
- if ((pwd_components.size() != 4)
- || (pwd_components[1] != "1") // 1 means srp
- || !base64_is_valid(pwd_components[2])
- || !base64_is_valid(pwd_components[3]))
+ if ((components.size() != 4)
+ || (components[1] != "1") // 1 means srp
+ || !base64_is_valid(components[2])
+ || !base64_is_valid(components[3]))
return false;
- std::string salt_str = base64_decode(pwd_components[2]);
- std::string bytes_v_str = base64_decode(pwd_components[3]);
- *salt = salt_str;
- *bytes_v = bytes_v_str;
+ *salt = base64_decode(components[2]);
+ *verifier = base64_decode(components[3]);
return true;
}
diff --git a/src/util/auth.h b/src/util/auth.h
index 36d8c20a4..1fd6ab453 100644
--- a/src/util/auth.h
+++ b/src/util/auth.h
@@ -1,6 +1,6 @@
/*
Minetest
-Copyright (C) 2015 est31 <MTest31@outlook.com>
+Copyright (C) 2015, 2016 est31 <MTest31@outlook.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
@@ -20,18 +20,31 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#ifndef AUTH_H
#define AUTH_H
-std::string translatePassword(const std::string &name,
+/// Gets the base64 encoded legacy password db entry.
+std::string translate_password(const std::string &name,
const std::string &password);
-void getSRPVerifier(const std::string &name,
- const std::string &password, char **salt, size_t *salt_len,
- char **bytes_v, size_t *len_v);
-std::string getSRPVerifier(const std::string &name,
- const std::string &password);
-std::string getSRPVerifier(const std::string &name,
+
+/// Creates a verification key with given salt and password.
+std::string generate_srp_verifier(const std::string &name,
const std::string &password, const std::string &salt);
-std::string encodeSRPVerifier(const std::string &verifier,
+
+/// Creates a verification key and salt with given password.
+void generate_srp_verifier_and_salt(const std::string &name,
+ const std::string &password, std::string *verifier,
+ std::string *salt);
+
+/// Gets an SRP verifier, generating a salt,
+/// and encodes it as DB-ready string.
+std::string get_encoded_srp_verifier(const std::string &name,
+ const std::string &password);
+
+/// Converts the passed SRP verifier into a DB-ready format.
+std::string encode_srp_verifier(const std::string &verifier,
const std::string &salt);
-bool decodeSRPVerifier(const std::string &enc_pwd,
+
+/// Reads the DB-formatted SRP verifier and gets the verifier
+/// and salt components.
+bool decode_srp_verifier_and_salt(const std::string &encoded,
std::string *salt, std::string *bytes_v);
-#endif \ No newline at end of file
+#endif
diff --git a/src/util/basic_macros.h b/src/util/basic_macros.h
new file mode 100644
index 000000000..c100b4f25
--- /dev/null
+++ b/src/util/basic_macros.h
@@ -0,0 +1,53 @@
+/*
+Minetest
+Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@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.
+*/
+
+#ifndef BASICMACROS_HEADER
+#define BASICMACROS_HEADER
+
+#include <algorithm>
+
+#define ARRLEN(x) (sizeof(x) / sizeof((x)[0]))
+
+#define MYMIN(a, b) ((a) < (b) ? (a) : (b))
+
+#define MYMAX(a, b) ((a) > (b) ? (a) : (b))
+
+#define CONTAINS(c, v) (std::find((c).begin(), (c).end(), (v)) != (c).end())
+
+// To disable copy constructors and assignment operations for some class
+// 'Foobar', add the macro DISABLE_CLASS_COPY(Foobar) as a private member.
+// Note this also disables copying for any classes derived from 'Foobar' as well
+// as classes having a 'Foobar' member.
+#define DISABLE_CLASS_COPY(C) \
+ C(const C &); \
+ C &operator=(const C &)
+
+#ifndef _MSC_VER
+ #define UNUSED_ATTRIBUTE __attribute__ ((unused))
+#else
+ #define UNUSED_ATTRIBUTE
+#endif
+
+// Fail compilation if condition expr is not met.
+// Note that 'msg' must follow the format of a valid identifier, e.g.
+// STATIC_ASSERT(sizeof(foobar_t) == 40), foobar_t_is_wrong_size);
+#define STATIC_ASSERT(expr, msg) \
+ UNUSED_ATTRIBUTE typedef char msg[!!(expr) * 2 - 1]
+
+#endif
diff --git a/src/util/container.h b/src/util/container.h
index 267d54c16..7f66b89ac 100644
--- a/src/util/container.h
+++ b/src/util/container.h
@@ -22,9 +22,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "../irrlichttypes.h"
#include "../exceptions.h"
-#include "../jthread/jmutex.h"
-#include "../jthread/jmutexautolock.h"
-#include "../jthread/jsemaphore.h"
+#include "../threading/mutex.h"
+#include "../threading/mutex_auto_lock.h"
+#include "../threading/semaphore.h"
#include <list>
#include <vector>
#include <map>
@@ -81,111 +81,47 @@ template<typename Key, typename Value>
class MutexedMap
{
public:
- MutexedMap()
- {
- }
+ MutexedMap() {}
void set(const Key &name, const Value &value)
{
- JMutexAutoLock lock(m_mutex);
-
+ MutexAutoLock lock(m_mutex);
m_values[name] = value;
}
- bool get(const Key &name, Value *result)
+ bool get(const Key &name, Value *result) const
{
- JMutexAutoLock lock(m_mutex);
-
- typename std::map<Key, Value>::iterator n;
- n = m_values.find(name);
-
- if(n == m_values.end())
+ MutexAutoLock lock(m_mutex);
+ typename std::map<Key, Value>::const_iterator n =
+ m_values.find(name);
+ if (n == m_values.end())
return false;
-
- if(result != NULL)
+ if (result)
*result = n->second;
-
return true;
}
- std::vector<Value> getValues()
+ std::vector<Value> getValues() const
{
+ MutexAutoLock lock(m_mutex);
std::vector<Value> result;
- for(typename std::map<Key, Value>::iterator
- i = m_values.begin();
- i != m_values.end(); ++i){
- result.push_back(i->second);
+ for (typename std::map<Key, Value>::const_iterator
+ it = m_values.begin();
+ it != m_values.end(); ++it){
+ result.push_back(it->second);
}
return result;
}
- void clear ()
- {
- m_values.clear();
- }
+ void clear() { m_values.clear(); }
private:
std::map<Key, Value> m_values;
- JMutex m_mutex;
+ mutable Mutex m_mutex;
};
-/*
-Generates ids for comparable values.
-Id=0 is reserved for "no value".
-Is fast at:
-- Returning value by id (very fast)
-- Returning id by value
-- Generating a new id for a value
-
-Is not able to:
-- Remove an id/value pair (is possible to implement but slow)
-*/
-template<typename T>
-class MutexedIdGenerator
-{
-public:
- MutexedIdGenerator()
- {
- }
-
- // Returns true if found
- bool getValue(u32 id, T &value)
- {
- if(id == 0)
- return false;
- JMutexAutoLock lock(m_mutex);
- if(m_id_to_value.size() < id)
- return false;
- value = m_id_to_value[id-1];
- return true;
- }
-
- // If id exists for value, returns the id.
- // Otherwise generates an id for the value.
- u32 getId(const T &value)
- {
- JMutexAutoLock lock(m_mutex);
- typename std::map<T, u32>::iterator n;
- n = m_value_to_id.find(value);
- if(n != m_value_to_id.end())
- return n->second;
- m_id_to_value.push_back(value);
- u32 new_id = m_id_to_value.size();
- m_value_to_id.insert(value, new_id);
- return new_id;
- }
-
-private:
- JMutex m_mutex;
- // Values are stored here at id-1 position (id 1 = [0])
- std::vector<T> m_id_to_value;
- std::map<T, u32> m_value_to_id;
-};
-
-/*
-Thread-safe FIFO queue (well, actually a FILO also)
-*/
+// Thread-safe Double-ended queue
template<typename T>
class MutexedQueue
@@ -194,19 +130,18 @@ public:
template<typename Key, typename U, typename Caller, typename CallerData>
friend class RequestQueue;
- MutexedQueue()
- {
- }
- bool empty()
+ MutexedQueue() {}
+ bool empty() const
{
- JMutexAutoLock lock(m_mutex);
- return (m_queue.size() == 0);
+ MutexAutoLock lock(m_mutex);
+ return m_queue.empty();
}
+
void push_back(T t)
{
- JMutexAutoLock lock(m_mutex);
+ MutexAutoLock lock(m_mutex);
m_queue.push_back(t);
- m_size.Post();
+ m_signal.post();
}
/* this version of pop_front returns a empty element of T on timeout.
@@ -214,37 +149,35 @@ public:
*/
T pop_frontNoEx(u32 wait_time_max_ms)
{
- if (m_size.Wait(wait_time_max_ms)) {
- JMutexAutoLock lock(m_mutex);
+ if (m_signal.wait(wait_time_max_ms)) {
+ MutexAutoLock lock(m_mutex);
T t = m_queue.front();
m_queue.pop_front();
return t;
- }
- else {
+ } else {
return T();
}
}
T pop_front(u32 wait_time_max_ms)
{
- if (m_size.Wait(wait_time_max_ms)) {
- JMutexAutoLock lock(m_mutex);
+ if (m_signal.wait(wait_time_max_ms)) {
+ MutexAutoLock lock(m_mutex);
T t = m_queue.front();
m_queue.pop_front();
return t;
- }
- else {
+ } else {
throw ItemNotFoundException("MutexedQueue: queue is empty");
}
}
T pop_frontNoEx()
{
- m_size.Wait();
+ m_signal.wait();
- JMutexAutoLock lock(m_mutex);
+ MutexAutoLock lock(m_mutex);
T t = m_queue.front();
m_queue.pop_front();
@@ -253,14 +186,13 @@ public:
T pop_back(u32 wait_time_max_ms=0)
{
- if (m_size.Wait(wait_time_max_ms)) {
- JMutexAutoLock lock(m_mutex);
+ if (m_signal.wait(wait_time_max_ms)) {
+ MutexAutoLock lock(m_mutex);
T t = m_queue.back();
m_queue.pop_back();
return t;
- }
- else {
+ } else {
throw ItemNotFoundException("MutexedQueue: queue is empty");
}
}
@@ -268,25 +200,24 @@ public:
/* this version of pop_back returns a empty element of T on timeout.
* Make sure default constructor of T creates a recognizable "empty" element
*/
- T pop_backNoEx(u32 wait_time_max_ms=0)
+ T pop_backNoEx(u32 wait_time_max_ms)
{
- if (m_size.Wait(wait_time_max_ms)) {
- JMutexAutoLock lock(m_mutex);
+ if (m_signal.wait(wait_time_max_ms)) {
+ MutexAutoLock lock(m_mutex);
T t = m_queue.back();
m_queue.pop_back();
return t;
- }
- else {
+ } else {
return T();
}
}
T pop_backNoEx()
{
- m_size.Wait();
+ m_signal.wait();
- JMutexAutoLock lock(m_mutex);
+ MutexAutoLock lock(m_mutex);
T t = m_queue.back();
m_queue.pop_back();
@@ -294,19 +225,13 @@ public:
}
protected:
- JMutex & getMutex()
- {
- return m_mutex;
- }
+ Mutex &getMutex() { return m_mutex; }
- std::deque<T> & getQueue()
- {
- return m_queue;
- }
+ std::deque<T> &getQueue() { return m_queue; }
std::deque<T> m_queue;
- JMutex m_mutex;
- JSemaphore m_size;
+ mutable Mutex m_mutex;
+ Semaphore m_signal;
};
template<typename K, typename V>
diff --git a/src/util/numeric.cpp b/src/util/numeric.cpp
index 3fd1c9cf9..42ebd9022 100644
--- a/src/util/numeric.cpp
+++ b/src/util/numeric.cpp
@@ -23,17 +23,17 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "log.h"
#include "../constants.h" // BS, MAP_BLOCKSIZE
#include "../noise.h" // PseudoRandom, PcgRandom
-#include "../jthread/jmutexautolock.h"
+#include "../threading/mutex_auto_lock.h"
#include <string.h>
#include <iostream>
std::map<u16, std::vector<v3s16> > FacePositionCache::m_cache;
-JMutex FacePositionCache::m_cache_mutex;
+Mutex FacePositionCache::m_cache_mutex;
// Calculate the borders of a "d-radius" cube
// TODO: Make it work without mutex and data races, probably thread-local
std::vector<v3s16> FacePositionCache::getFacePositions(u16 d)
{
- JMutexAutoLock cachelock(m_cache_mutex);
+ MutexAutoLock cachelock(m_cache_mutex);
if (m_cache.find(d) != m_cache.end())
return m_cache[d];
@@ -244,7 +244,10 @@ bool isBlockInSight(v3s16 blockpos_b, v3f camera_pos, v3f camera_dir,
f32 cosangle = dforward / blockpos_adj.getLength();
// If block is not in the field of view, skip it
- if(cosangle < cos(camera_fov / 2))
+ // HOTFIX: use sligthly increased angle (+10%) to fix too agressive
+ // culling. Somebody have to find out whats wrong with the math here.
+ // Previous value: camera_fov / 2
+ if(cosangle < cos(camera_fov * 0.55))
return false;
return true;
diff --git a/src/util/numeric.h b/src/util/numeric.h
index 9fe08434f..615327864 100644
--- a/src/util/numeric.h
+++ b/src/util/numeric.h
@@ -20,15 +20,15 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#ifndef UTIL_NUMERIC_HEADER
#define UTIL_NUMERIC_HEADER
+#include "basic_macros.h"
#include "../irrlichttypes.h"
#include "../irr_v2d.h"
#include "../irr_v3d.h"
#include "../irr_aabb3d.h"
-#include "../jthread/jmutex.h"
+#include "../threading/mutex.h"
#include <list>
#include <map>
#include <vector>
-#include <algorithm>
/*
@@ -42,7 +42,7 @@ public:
private:
static void generateFacePosition(u16 d);
static std::map<u16, std::vector<v3s16> > m_cache;
- static JMutex m_cache_mutex;
+ static Mutex m_cache_mutex;
};
class IndentationRaiser
@@ -166,9 +166,6 @@ inline v3s16 arealim(v3s16 p, s16 d)
return p;
}
-#define ARRLEN(x) (sizeof(x) / sizeof((x)[0]))
-#define CONTAINS(c, v) (std::find((c).begin(), (c).end(), (v)) != (c).end())
-
// The naive swap performs better than the xor version
#define SWAP(t, x, y) do { \
t temp = x; \
@@ -279,12 +276,6 @@ bool isBlockInSight(v3s16 blockpos_b, v3f camera_pos, v3f camera_dir,
f32 camera_fov, f32 range, f32 *distance_ptr=NULL);
/*
- Some helper stuff
-*/
-#define MYMIN(a,b) ((a)<(b)?(a):(b))
-#define MYMAX(a,b) ((a)>(b)?(a):(b))
-
-/*
Returns nearest 32-bit integer for given floating point number.
<cmath> and <math.h> in VC++ don't provide round().
*/
@@ -319,9 +310,9 @@ inline v3f intToFloat(v3s16 p, f32 d)
}
// Random helper. Usually d=BS
-inline core::aabbox3d<f32> getNodeBox(v3s16 p, float d)
+inline aabb3f getNodeBox(v3s16 p, float d)
{
- return core::aabbox3d<f32>(
+ return aabb3f(
(float)p.X * d - 0.5*d,
(float)p.Y * d - 0.5*d,
(float)p.Z * d - 0.5*d,
diff --git a/src/util/serialize.cpp b/src/util/serialize.cpp
index c0168776e..99cb990f1 100644
--- a/src/util/serialize.cpp
+++ b/src/util/serialize.cpp
@@ -28,6 +28,77 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <iomanip>
#include <vector>
+SerializationError eof_ser_err("Attempted read past end of data");
+
+////
+//// BufReader
+////
+
+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
////
@@ -158,7 +229,7 @@ std::string deSerializeLongString(std::istream &is)
Buffer<char> buf2(s_size);
is.read(&buf2[0], s_size);
- if (is.gcount() != s_size)
+ if ((u32)is.gcount() != s_size)
throw SerializationError("deSerializeLongString: couldn't read all chars");
s.reserve(s_size);
diff --git a/src/util/serialize.h b/src/util/serialize.h
index bf0d9c863..36324a675 100644
--- a/src/util/serialize.h
+++ b/src/util/serialize.h
@@ -21,14 +21,27 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#define UTIL_SERIALIZE_HEADER
#include "../irrlichttypes_bloated.h"
+#include "../exceptions.h" // for SerializationError
#include "../debug.h" // for assert
+
#include "config.h"
#if HAVE_ENDIAN_H
-#include <endian.h>
-#include <string.h> // for memcpy
+ #ifdef _WIN32
+ #define __BYTE_ORDER 0
+ #define __LITTLE_ENDIAN 0
+ #define __BIG_ENDIAN 1
+ #elif defined(__MACH__) && defined(__APPLE__)
+ #include <machine/endian.h>
+ #elif defined(__FreeBSD__)
+ #include <sys/endian.h>
+ #else
+ #include <endian.h>
+ #endif
#endif
+#include <string.h> // for memcpy
#include <iostream>
#include <string>
+#include <vector>
#define FIXEDPOINT_FACTOR 1000.0f
@@ -405,4 +418,238 @@ bool serializeStructToString(std::string *out,
bool deSerializeStringToStruct(std::string valstr,
std::string format, void *out, size_t olen);
+////
+//// BufReader
+////
+
+extern SerializationError eof_ser_err;
+
+#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 eof_ser_err; \
+ return val; \
+ }
+
+class BufReader {
+public:
+ BufReader(const u8 *data_, size_t size_) :
+ data(data_),
+ size(size_),
+ pos(0)
+ {
+ }
+
+ 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(v2f, V2F1000, 8);
+ 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(v2f, V2F1000);
+ 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 eof_ser_err;
+ }
+
+ inline size_t remaining()
+ {
+ assert(pos <= size);
+ return size - pos;
+ }
+
+ const u8 *data;
+ size_t size;
+ size_t pos;
+};
+
+#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 putV2F1000(std::vector<u8> *dest, v2f val)
+{
+ putF1000(dest, val.X);
+ putF1000(dest, val.Y);
+}
+
+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);
+}
+
#endif
diff --git a/src/util/srp.cpp b/src/util/srp.cpp
index 94426db92..0d3c938a3 100644
--- a/src/util/srp.cpp
+++ b/src/util/srp.cpp
@@ -26,12 +26,14 @@
*
*/
+// clang-format off
#ifdef WIN32
#include <windows.h>
#include <wincrypt.h>
#else
#include <time.h>
#endif
+// clang-format on
#include <stdlib.h>
#include <string.h>
@@ -69,124 +71,137 @@ static int g_initialized = 0;
static unsigned int g_rand_idx;
static unsigned char g_rand_buff[RAND_BUFF_MAX];
-typedef struct
+void *(*srp_alloc)(size_t) = &malloc;
+void *(*srp_realloc)(void *, size_t) = &realloc;
+void (*srp_free)(void *) = &free;
+
+// clang-format off
+void srp_set_memory_functions(
+ void *(*new_srp_alloc)(size_t),
+ void *(*new_srp_realloc)(void *, size_t),
+ void (*new_srp_free)(void *))
{
+ srp_alloc = new_srp_alloc;
+ srp_realloc = new_srp_realloc;
+ srp_free = new_srp_free;
+}
+// clang-format on
+
+typedef struct {
mpz_t N;
mpz_t g;
} NGConstant;
-struct NGHex
-{
- const char* n_hex;
- const char* g_hex;
+struct NGHex {
+ const char *n_hex;
+ const char *g_hex;
};
/* All constants here were pulled from Appendix A of RFC 5054 */
static struct NGHex global_Ng_constants[] = {
- { /* 1024 */
- "EEAF0AB9ADB38DD69C33F80AFA8FC5E86072618775FF3C0B9EA2314C9C256576D674DF7496"
- "EA81D3383B4813D692C6E0E0D5D8E250B98BE48E495C1D6089DAD15DC7D7B46154D6B6CE8E"
- "F4AD69B15D4982559B297BCF1885C529F566660E57EC68EDBC3C05726CC02FD4CBF4976EAA"
- "9AFD5138FE8376435B9FC61D2FC0EB06E3",
- "2"
- },
- { /* 2048 */
- "AC6BDB41324A9A9BF166DE5E1389582FAF72B6651987EE07FC3192943DB56050A37329CBB4"
- "A099ED8193E0757767A13DD52312AB4B03310DCD7F48A9DA04FD50E8083969EDB767B0CF60"
- "95179A163AB3661A05FBD5FAAAE82918A9962F0B93B855F97993EC975EEAA80D740ADBF4FF"
- "747359D041D5C33EA71D281E446B14773BCA97B43A23FB801676BD207A436C6481F1D2B907"
- "8717461A5B9D32E688F87748544523B524B0D57D5EA77A2775D2ECFA032CFBDBF52FB37861"
- "60279004E57AE6AF874E7303CE53299CCC041C7BC308D82A5698F3A8D0C38271AE35F8E9DB"
- "FBB694B5C803D89F7AE435DE236D525F54759B65E372FCD68EF20FA7111F9E4AFF73",
- "2"
- },
- { /* 4096 */
- "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08"
- "8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B"
- "302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9"
- "A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6"
- "49286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8"
- "FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D"
- "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C"
- "180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718"
- "3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D"
- "04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7D"
- "B3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D226"
- "1AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200C"
- "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFC"
- "E0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B26"
- "99C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB"
- "04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2"
- "233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127"
- "D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199"
- "FFFFFFFFFFFFFFFF",
- "5"
- },
- { /* 8192 */
- "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08"
- "8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B"
- "302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9"
- "A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6"
- "49286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8"
- "FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D"
- "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C"
- "180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718"
- "3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D"
- "04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7D"
- "B3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D226"
- "1AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200C"
- "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFC"
- "E0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B26"
- "99C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB"
- "04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2"
- "233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127"
- "D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934028492"
- "36C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406"
- "AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918"
- "DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B33205151"
- "2BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03"
- "F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97F"
- "BEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA"
- "CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58B"
- "B7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632"
- "387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E"
- "6DBE115974A3926F12FEE5E438777CB6A932DF8CD8BEC4D073B931BA"
- "3BC832B68D9DD300741FA7BF8AFC47ED2576F6936BA424663AAB639C"
- "5AE4F5683423B4742BF1C978238F16CBE39D652DE3FDB8BEFC848AD9"
- "22222E04A4037C0713EB57A81A23F0C73473FC646CEA306B4BCBC886"
- "2F8385DDFA9D4B7FA2C087E879683303ED5BDD3A062B3CF5B3A278A6"
- "6D2A13F83F44F82DDF310EE074AB6A364597E899A0255DC164F31CC5"
- "0846851DF9AB48195DED7EA1B1D510BD7EE74D73FAF36BC31ECFA268"
- "359046F4EB879F924009438B481C6CD7889A002ED5EE382BC9190DA6"
- "FC026E479558E4475677E9AA9E3050E2765694DFC81F56E880B96E71"
- "60C980DD98EDD3DFFFFFFFFFFFFFFFFF",
- "13"
- },
- {0,0} /* null sentinel */
+ {/* 1024 */
+ "EEAF0AB9ADB38DD69C33F80AFA8FC5E86072618775FF3C0B9EA2314C"
+ "9C256576D674DF7496EA81D3383B4813D692C6E0E0D5D8E250B98BE4"
+ "8E495C1D6089DAD15DC7D7B46154D6B6CE8EF4AD69B15D4982559B29"
+ "7BCF1885C529F566660E57EC68EDBC3C05726CC02FD4CBF4976EAA9A"
+ "FD5138FE8376435B9FC61D2FC0EB06E3",
+ "2"},
+ {/* 2048 */
+ "AC6BDB41324A9A9BF166DE5E1389582FAF72B6651987EE07FC319294"
+ "3DB56050A37329CBB4A099ED8193E0757767A13DD52312AB4B03310D"
+ "CD7F48A9DA04FD50E8083969EDB767B0CF6095179A163AB3661A05FB"
+ "D5FAAAE82918A9962F0B93B855F97993EC975EEAA80D740ADBF4FF74"
+ "7359D041D5C33EA71D281E446B14773BCA97B43A23FB801676BD207A"
+ "436C6481F1D2B9078717461A5B9D32E688F87748544523B524B0D57D"
+ "5EA77A2775D2ECFA032CFBDBF52FB3786160279004E57AE6AF874E73"
+ "03CE53299CCC041C7BC308D82A5698F3A8D0C38271AE35F8E9DBFBB6"
+ "94B5C803D89F7AE435DE236D525F54759B65E372FCD68EF20FA7111F"
+ "9E4AFF73",
+ "2"},
+ {/* 4096 */
+ "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08"
+ "8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B"
+ "302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9"
+ "A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6"
+ "49286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8"
+ "FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D"
+ "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C"
+ "180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718"
+ "3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D"
+ "04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7D"
+ "B3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D226"
+ "1AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200C"
+ "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFC"
+ "E0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B26"
+ "99C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB"
+ "04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2"
+ "233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127"
+ "D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199"
+ "FFFFFFFFFFFFFFFF",
+ "5"},
+ {/* 8192 */
+ "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08"
+ "8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B"
+ "302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9"
+ "A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6"
+ "49286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8"
+ "FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D"
+ "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C"
+ "180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718"
+ "3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D"
+ "04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7D"
+ "B3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D226"
+ "1AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200C"
+ "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFC"
+ "E0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B26"
+ "99C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB"
+ "04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2"
+ "233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127"
+ "D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934028492"
+ "36C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406"
+ "AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918"
+ "DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B33205151"
+ "2BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03"
+ "F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97F"
+ "BEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA"
+ "CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58B"
+ "B7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632"
+ "387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E"
+ "6DBE115974A3926F12FEE5E438777CB6A932DF8CD8BEC4D073B931BA"
+ "3BC832B68D9DD300741FA7BF8AFC47ED2576F6936BA424663AAB639C"
+ "5AE4F5683423B4742BF1C978238F16CBE39D652DE3FDB8BEFC848AD9"
+ "22222E04A4037C0713EB57A81A23F0C73473FC646CEA306B4BCBC886"
+ "2F8385DDFA9D4B7FA2C087E879683303ED5BDD3A062B3CF5B3A278A6"
+ "6D2A13F83F44F82DDF310EE074AB6A364597E899A0255DC164F31CC5"
+ "0846851DF9AB48195DED7EA1B1D510BD7EE74D73FAF36BC31ECFA268"
+ "359046F4EB879F924009438B481C6CD7889A002ED5EE382BC9190DA6"
+ "FC026E479558E4475677E9AA9E3050E2765694DFC81F56E880B96E71"
+ "60C980DD98EDD3DFFFFFFFFFFFFFFFFF",
+ "13"},
+ {0, 0} /* null sentinel */
};
-
static void delete_ng(NGConstant *ng)
{
if (ng) {
mpz_clear(ng->N);
mpz_clear(ng->g);
- free(ng);
+ srp_free(ng);
}
}
-static NGConstant *new_ng( SRP_NGType ng_type, const char *n_hex, const char *g_hex )
+static NGConstant *new_ng(SRP_NGType ng_type, const char *n_hex, const char *g_hex)
{
- NGConstant *ng = (NGConstant *) malloc(sizeof(NGConstant));
+ NGConstant *ng = (NGConstant *)srp_alloc(sizeof(NGConstant));
+
+ if (!ng) return 0;
+
mpz_init(ng->N);
mpz_init(ng->g);
- if (!ng)
- return 0;
-
if (ng_type != SRP_NG_CUSTOM) {
- n_hex = global_Ng_constants[ ng_type ].n_hex;
- g_hex = global_Ng_constants[ ng_type ].g_hex;
+ n_hex = global_Ng_constants[ng_type].n_hex;
+ g_hex = global_Ng_constants[ng_type].g_hex;
}
int rv = 0;
@@ -201,17 +216,13 @@ static NGConstant *new_ng( SRP_NGType ng_type, const char *n_hex, const char *g_
return ng;
}
-
-typedef union
-{
- SHA_CTX sha;
+typedef union {
+ SHA_CTX sha;
SHA256_CTX sha256;
- //SHA512_CTX sha512;
+ // SHA512_CTX sha512;
} HashCTX;
-
-struct SRPVerifier
-{
+struct SRPVerifier {
SRP_HashAlgorithm hash_alg;
NGConstant *ng;
@@ -224,9 +235,7 @@ struct SRPVerifier
unsigned char session_key[SHA512_DIGEST_LENGTH];
};
-
-struct SRPUser
-{
+struct SRPUser {
SRP_HashAlgorithm hash_alg;
NGConstant *ng;
@@ -247,19 +256,23 @@ struct SRPUser
unsigned char session_key[SHA512_DIGEST_LENGTH];
};
-
+// clang-format off
static int hash_init(SRP_HashAlgorithm alg, HashCTX *c)
{
switch (alg) {
#ifdef CSRP_USE_SHA1
case SRP_SHA1: return SHA1_Init(&c->sha);
#endif
- /*case SRP_SHA224: return SHA224_Init(&c->sha256);*/
+ /*
+ case SRP_SHA224: return SHA224_Init(&c->sha256);
+ */
#ifdef CSRP_USE_SHA256
case SRP_SHA256: return SHA256_Init(&c->sha256);
#endif
- /*case SRP_SHA384: return SHA384_Init(&c->sha512);
- case SRP_SHA512: return SHA512_Init(&c->sha512);*/
+ /*
+ case SRP_SHA384: return SHA384_Init(&c->sha512);
+ case SRP_SHA512: return SHA512_Init(&c->sha512);
+ */
default: return -1;
};
}
@@ -269,12 +282,16 @@ static int hash_update( SRP_HashAlgorithm alg, HashCTX *c, const void *data, siz
#ifdef CSRP_USE_SHA1
case SRP_SHA1: return SHA1_Update(&c->sha, data, len);
#endif
- /*case SRP_SHA224: return SHA224_Update(&c->sha256, data, len);*/
+ /*
+ case SRP_SHA224: return SHA224_Update(&c->sha256, data, len);
+ */
#ifdef CSRP_USE_SHA256
case SRP_SHA256: return SHA256_Update(&c->sha256, data, len);
#endif
- /*case SRP_SHA384: return SHA384_Update( &c->sha512, data, len );
- case SRP_SHA512: return SHA512_Update( &c->sha512, data, len );*/
+ /*
+ case SRP_SHA384: return SHA384_Update(&c->sha512, data, len);
+ case SRP_SHA512: return SHA512_Update(&c->sha512, data, len);
+ */
default: return -1;
};
}
@@ -284,12 +301,16 @@ static int hash_final( SRP_HashAlgorithm alg, HashCTX *c, unsigned char *md )
#ifdef CSRP_USE_SHA1
case SRP_SHA1: return SHA1_Final(md, &c->sha);
#endif
- /*case SRP_SHA224: return SHA224_Final(md, &c->sha256);*/
+ /*
+ case SRP_SHA224: return SHA224_Final(md, &c->sha256);
+ */
#ifdef CSRP_USE_SHA256
case SRP_SHA256: return SHA256_Final(md, &c->sha256);
#endif
- /*case SRP_SHA384: return SHA384_Final(md, &c->sha512);
- case SRP_SHA512: return SHA512_Final(md, &c->sha512);*/
+ /*
+ case SRP_SHA384: return SHA384_Final(md, &c->sha512);
+ case SRP_SHA512: return SHA512_Final(md, &c->sha512);
+ */
default: return -1;
};
}
@@ -299,12 +320,16 @@ static unsigned char *hash(SRP_HashAlgorithm alg, const unsigned char *d, size_t
#ifdef CSRP_USE_SHA1
case SRP_SHA1: return SHA1(d, n, md);
#endif
- /*case SRP_SHA224: return SHA224( d, n, md );*/
+ /*
+ case SRP_SHA224: return SHA224( d, n, md );
+ */
#ifdef CSRP_USE_SHA256
case SRP_SHA256: return SHA256(d, n, md);
#endif
- /*case SRP_SHA384: return SHA384( d, n, md );
- case SRP_SHA512: return SHA512( d, n, md );*/
+ /*
+ case SRP_SHA384: return SHA384( d, n, md );
+ case SRP_SHA512: return SHA512( d, n, md );
+ */
default: return 0;
};
}
@@ -314,19 +339,24 @@ static size_t hash_length(SRP_HashAlgorithm alg)
#ifdef CSRP_USE_SHA1
case SRP_SHA1: return SHA_DIGEST_LENGTH;
#endif
- /*case SRP_SHA224: return SHA224_DIGEST_LENGTH;*/
+ /*
+ case SRP_SHA224: return SHA224_DIGEST_LENGTH;
+ */
#ifdef CSRP_USE_SHA256
case SRP_SHA256: return SHA256_DIGEST_LENGTH;
#endif
- /*case SRP_SHA384: return SHA384_DIGEST_LENGTH;
- case SRP_SHA512: return SHA512_DIGEST_LENGTH;*/
+ /*
+ case SRP_SHA384: return SHA384_DIGEST_LENGTH;
+ case SRP_SHA512: return SHA512_DIGEST_LENGTH;
+ */
default: return -1;
};
}
+// clang-format on
inline static int mpz_num_bytes(const mpz_t op)
{
- return (mpz_sizeinbase (op, 2) + 7) / 8;
+ return (mpz_sizeinbase(op, 2) + 7) / 8;
}
inline static void mpz_to_bin(const mpz_t op, unsigned char *to)
@@ -340,72 +370,77 @@ inline static void mpz_from_bin(const unsigned char *s, size_t len, mpz_t ret)
}
// set op to (op1 * op2) mod d, using tmp for the calculation
-inline static void mpz_mulm(mpz_t op, const mpz_t op1, const mpz_t op2, const mpz_t d, mpz_t tmp)
+inline static void mpz_mulm(
+ mpz_t op, const mpz_t op1, const mpz_t op2, const mpz_t d, mpz_t tmp)
{
mpz_mul(tmp, op1, op2);
mpz_mod(op, tmp, d);
}
// set op to (op1 + op2) mod d, using tmp for the calculation
-inline static void mpz_addm( mpz_t op, const mpz_t op1, const mpz_t op2, const mpz_t d, mpz_t tmp )
+inline static void mpz_addm(
+ mpz_t op, const mpz_t op1, const mpz_t op2, const mpz_t d, mpz_t tmp)
{
mpz_add(tmp, op1, op2);
mpz_mod(op, tmp, d);
}
// set op to (op1 - op2) mod d, using tmp for the calculation
-inline static void mpz_subm(mpz_t op, const mpz_t op1, const mpz_t op2, const mpz_t d, mpz_t tmp)
+inline static void mpz_subm(
+ mpz_t op, const mpz_t op1, const mpz_t op2, const mpz_t d, mpz_t tmp)
{
mpz_sub(tmp, op1, op2);
mpz_mod(op, tmp, d);
}
-static int H_nn(mpz_t result, SRP_HashAlgorithm alg, const mpz_t N, const mpz_t n1, const mpz_t n2)
+static SRP_Result H_nn(
+ mpz_t result, SRP_HashAlgorithm alg, const mpz_t N, const mpz_t n1, const mpz_t n2)
{
unsigned char buff[SHA512_DIGEST_LENGTH];
size_t len_N = mpz_num_bytes(N);
size_t len_n1 = mpz_num_bytes(n1);
size_t len_n2 = mpz_num_bytes(n2);
size_t nbytes = len_N + len_N;
- unsigned char *bin = (unsigned char *) malloc(nbytes);
- if (!bin)
- return 0;
+ unsigned char *bin = (unsigned char *)srp_alloc(nbytes);
+ if (!bin) return SRP_ERR;
if (len_n1 > len_N || len_n2 > len_N) {
- free(bin);
- return 0;
+ srp_free(bin);
+ return SRP_ERR;
}
memset(bin, 0, nbytes);
mpz_to_bin(n1, bin + (len_N - len_n1));
mpz_to_bin(n2, bin + (len_N + len_N - len_n2));
- hash( alg, bin, nbytes, buff );
- free(bin);
+ hash(alg, bin, nbytes, buff);
+ srp_free(bin);
mpz_from_bin(buff, hash_length(alg), result);
- return 1;
+ return SRP_OK;
}
-static int H_ns(mpz_t result, SRP_HashAlgorithm alg, const unsigned char *n, size_t len_n, const unsigned char *bytes, size_t len_bytes)
+static SRP_Result H_ns(mpz_t result, SRP_HashAlgorithm alg, const unsigned char *n,
+ size_t len_n, const unsigned char *bytes, size_t len_bytes)
{
unsigned char buff[SHA512_DIGEST_LENGTH];
size_t nbytes = len_n + len_bytes;
- unsigned char *bin = (unsigned char *) malloc(nbytes);
- if (!bin)
- return 0;
+ unsigned char *bin = (unsigned char *)srp_alloc(nbytes);
+ if (!bin) return SRP_ERR;
memcpy(bin, n, len_n);
memcpy(bin + len_n, bytes, len_bytes);
hash(alg, bin, nbytes, buff);
- free(bin);
+ srp_free(bin);
mpz_from_bin(buff, hash_length(alg), result);
- return 1;
+ return SRP_OK;
}
-static int calculate_x(mpz_t result, SRP_HashAlgorithm alg, const unsigned char *salt, size_t salt_len, const char *username, const unsigned char *password, size_t password_len)
+static int calculate_x(mpz_t result, SRP_HashAlgorithm alg, const unsigned char *salt,
+ size_t salt_len, const char *username, const unsigned char *password,
+ size_t password_len)
{
unsigned char ucp_hash[SHA512_DIGEST_LENGTH];
HashCTX ctx;
hash_init(alg, &ctx);
- srp_dbg_data((char*) username, strlen(username), "Username for x: ");
- srp_dbg_data((char*) password, password_len, "Password for x: ");
+ srp_dbg_data((char *)username, strlen(username), "Username for x: ");
+ srp_dbg_data((char *)password, password_len, "Password for x: ");
hash_update(alg, &ctx, username, strlen(username));
hash_update(alg, &ctx, ":", 1);
hash_update(alg, &ctx, password, password_len);
@@ -415,31 +450,31 @@ static int calculate_x(mpz_t result, SRP_HashAlgorithm alg, const unsigned char
return H_ns(result, alg, salt, salt_len, ucp_hash, hash_length(alg));
}
-static void update_hash_n(SRP_HashAlgorithm alg, HashCTX *ctx, const mpz_t n)
+static SRP_Result update_hash_n(SRP_HashAlgorithm alg, HashCTX *ctx, const mpz_t n)
{
size_t len = mpz_num_bytes(n);
- unsigned char* n_bytes = (unsigned char *) malloc(len);
- if (!n_bytes)
- return;
+ unsigned char *n_bytes = (unsigned char *)srp_alloc(len);
+ if (!n_bytes) return SRP_ERR;
mpz_to_bin(n, n_bytes);
hash_update(alg, ctx, n_bytes, len);
- free(n_bytes);
+ srp_free(n_bytes);
+ return SRP_OK;
}
-static void hash_num( SRP_HashAlgorithm alg, const mpz_t n, unsigned char *dest )
+static SRP_Result hash_num(SRP_HashAlgorithm alg, const mpz_t n, unsigned char *dest)
{
int nbytes = mpz_num_bytes(n);
- unsigned char *bin = (unsigned char *) malloc(nbytes);
- if(!bin)
- return;
+ unsigned char *bin = (unsigned char *)srp_alloc(nbytes);
+ if (!bin) return SRP_ERR;
mpz_to_bin(n, bin);
hash(alg, bin, nbytes, dest);
- free(bin);
+ srp_free(bin);
+ return SRP_OK;
}
-static void calculate_M(SRP_HashAlgorithm alg, NGConstant *ng, unsigned char *dest,
- const char *I, const unsigned char *s_bytes, size_t s_len,
- const mpz_t A, const mpz_t B, const unsigned char *K)
+static SRP_Result calculate_M(SRP_HashAlgorithm alg, NGConstant *ng, unsigned char *dest,
+ const char *I, const unsigned char *s_bytes, size_t s_len, const mpz_t A,
+ const mpz_t B, const unsigned char *K)
{
unsigned char H_N[SHA512_DIGEST_LENGTH];
unsigned char H_g[SHA512_DIGEST_LENGTH];
@@ -449,13 +484,12 @@ static void calculate_M(SRP_HashAlgorithm alg, NGConstant *ng, unsigned char *de
size_t i = 0;
size_t hash_len = hash_length(alg);
- hash_num(alg, ng->N, H_N);
- hash_num(alg, ng->g, H_g);
+ if (!hash_num(alg, ng->N, H_N)) return SRP_ERR;
+ if (!hash_num(alg, ng->g, H_g)) return SRP_ERR;
hash(alg, (const unsigned char *)I, strlen(I), H_I);
-
- for (i = 0; i < hash_len; i++ )
+ for (i = 0; i < hash_len; i++)
H_xor[i] = H_N[i] ^ H_g[i];
hash_init(alg, &ctx);
@@ -463,54 +497,30 @@ static void calculate_M(SRP_HashAlgorithm alg, NGConstant *ng, unsigned char *de
hash_update(alg, &ctx, H_xor, hash_len);
hash_update(alg, &ctx, H_I, hash_len);
hash_update(alg, &ctx, s_bytes, s_len);
- update_hash_n(alg, &ctx, A);
- update_hash_n(alg, &ctx, B);
+ if (!update_hash_n(alg, &ctx, A)) return SRP_ERR;
+ if (!update_hash_n(alg, &ctx, B)) return SRP_ERR;
hash_update(alg, &ctx, K, hash_len);
hash_final(alg, &ctx, dest);
+ return SRP_OK;
}
-static void calculate_H_AMK(SRP_HashAlgorithm alg, unsigned char *dest, const mpz_t A, const unsigned char *M, const unsigned char *K)
+static SRP_Result calculate_H_AMK(SRP_HashAlgorithm alg, unsigned char *dest,
+ const mpz_t A, const unsigned char *M, const unsigned char *K)
{
HashCTX ctx;
hash_init(alg, &ctx);
- update_hash_n(alg, &ctx, A);
+ if (!update_hash_n(alg, &ctx, A)) return SRP_ERR;
hash_update(alg, &ctx, M, hash_length(alg));
hash_update(alg, &ctx, K, hash_length(alg));
hash_final(alg, &ctx, dest);
+ return SRP_OK;
}
-
-struct srp_pcgrandom {
- unsigned long long int m_state;
- unsigned long long int m_inc;
-}; typedef struct srp_pcgrandom srp_pcgrandom;
-
-static unsigned long int srp_pcgrandom_next(srp_pcgrandom *r)
-{
- unsigned long long int oldstate = r->m_state;
- r->m_state = oldstate * 6364136223846793005ULL + r->m_inc;
-
- unsigned long int xorshifted = ((oldstate >> 18u) ^ oldstate) >> 27u;
- unsigned long int rot = oldstate >> 59u;
- return (xorshifted >> rot) | (xorshifted << ((-rot) & 31));
-}
-
-static void srp_pcgrandom_seed(srp_pcgrandom *r, unsigned long long int state,
- unsigned long long int seq)
-{
- r->m_state = 0U;
- r->m_inc = (seq << 1u) | 1u;
- srp_pcgrandom_next(r);
- r->m_state += state;
- srp_pcgrandom_next(r);
-}
-
-
-static int fill_buff()
+static SRP_Result fill_buff()
{
g_rand_idx = 0;
@@ -522,54 +532,48 @@ static int fill_buff()
#ifdef WIN32
- CryptAcquireContext(&wctx, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT);
- CryptGenRandom(wctx, sizeof(g_rand_buff), (BYTE*) g_rand_buff);
- CryptReleaseContext(wctx, 0);
-
- return 1;
+ if (!CryptAcquireContext(&wctx, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
+ return SRP_ERR;
+ if (!CryptGenRandom(wctx, sizeof(g_rand_buff), (BYTE *)g_rand_buff)) return SRP_ERR;
+ if (!CryptReleaseContext(wctx, 0)) return SRP_ERR;
#else
fp = fopen("/dev/urandom", "r");
- if (fp) {
- fread(g_rand_buff, sizeof(g_rand_buff), 1, fp);
- fclose(fp);
- } else {
- srp_pcgrandom *r = (srp_pcgrandom *) malloc(sizeof(srp_pcgrandom));
- srp_pcgrandom_seed(r, time(NULL) ^ clock(), 0xda3e39cb94b95bdbULL);
- size_t i = 0;
- for (i = 0; i < RAND_BUFF_MAX; i++) {
- g_rand_buff[i] = srp_pcgrandom_next(r);
- }
- }
+ if (!fp) return SRP_ERR;
+
+ if (fread(g_rand_buff, sizeof(g_rand_buff), 1, fp) != 1) return SRP_ERR;
+ if (fclose(fp)) return SRP_ERR;
#endif
- return 1;
+ return SRP_OK;
}
-static void mpz_fill_random(mpz_t num)
+static SRP_Result mpz_fill_random(mpz_t num)
{
// was call: BN_rand(num, 256, -1, 0);
if (RAND_BUFF_MAX - g_rand_idx < 32)
- fill_buff();
- mpz_from_bin((const unsigned char *) (&g_rand_buff[g_rand_idx]), 32, num);
+ if (fill_buff() != SRP_OK) return SRP_ERR;
+ mpz_from_bin((const unsigned char *)(&g_rand_buff[g_rand_idx]), 32, num);
g_rand_idx += 32;
+ return SRP_OK;
}
-static void init_random()
+static SRP_Result init_random()
{
- if (g_initialized)
- return;
- g_initialized = fill_buff();
+ if (g_initialized) return SRP_OK;
+ SRP_Result ret = fill_buff();
+ g_initialized = (ret == SRP_OK);
+ return ret;
}
#define srp_dbg_num(num, text) ;
/*void srp_dbg_num(mpz_t num, char * prevtext)
{
int len_num = mpz_num_bytes(num);
- char *bytes_num = (char*) malloc(len_num);
+ char *bytes_num = (char*) srp_alloc(len_num);
mpz_to_bin(num, (unsigned char *) bytes_num);
srp_dbg_data(bytes_num, len_num, prevtext);
- free(bytes_num);
+ srp_free(bytes_num);
}*/
@@ -579,35 +583,41 @@ static void init_random()
*
***********************************************************************************************************/
-void srp_create_salted_verification_key( SRP_HashAlgorithm alg,
+// clang-format off
+SRP_Result srp_create_salted_verification_key( SRP_HashAlgorithm alg,
SRP_NGType ng_type, const char *username_for_verifier,
const unsigned char *password, size_t len_password,
unsigned char **bytes_s, size_t *len_s,
unsigned char **bytes_v, size_t *len_v,
const char *n_hex, const char *g_hex )
{
+ SRP_Result ret = SRP_OK;
+
mpz_t v; mpz_init(v);
mpz_t x; mpz_init(x);
+ // clang-format on
+
NGConstant *ng = new_ng(ng_type, n_hex, g_hex);
- if(!ng)
- goto cleanup_and_exit;
+ if (!ng) goto error_and_exit;
- init_random(); /* Only happens once */
+ if (init_random() != SRP_OK) /* Only happens once */
+ goto error_and_exit;
if (*bytes_s == NULL) {
- *len_s = 16;
- if (RAND_BUFF_MAX - g_rand_idx < 16)
- fill_buff();
- *bytes_s = (unsigned char*)malloc(sizeof(char) * 16);
- memcpy(*bytes_s, &g_rand_buff + g_rand_idx, sizeof(char) * 16);
- g_rand_idx += 16;
+ size_t size_to_fill = 16;
+ *len_s = size_to_fill;
+ if (RAND_BUFF_MAX - g_rand_idx < size_to_fill)
+ if (fill_buff() != SRP_OK) goto error_and_exit;
+ *bytes_s = (unsigned char *)srp_alloc(size_to_fill);
+ if (!*bytes_s) goto error_and_exit;
+ memcpy(*bytes_s, &g_rand_buff + g_rand_idx, size_to_fill);
+ g_rand_idx += size_to_fill;
}
-
- if (!calculate_x(x, alg, *bytes_s, *len_s, username_for_verifier,
- password, len_password))
- goto cleanup_and_exit;
+ if (!calculate_x(
+ x, alg, *bytes_s, *len_s, username_for_verifier, password, len_password))
+ goto error_and_exit;
srp_dbg_num(x, "Server calculated x: ");
@@ -615,20 +625,23 @@ void srp_create_salted_verification_key( SRP_HashAlgorithm alg,
*len_v = mpz_num_bytes(v);
- *bytes_v = (unsigned char*)malloc(*len_v);
+ *bytes_v = (unsigned char *)srp_alloc(*len_v);
- if (!bytes_v)
- goto cleanup_and_exit;
+ if (!*bytes_v) goto error_and_exit;
mpz_to_bin(v, *bytes_v);
cleanup_and_exit:
- delete_ng( ng );
+ delete_ng(ng);
mpz_clear(v);
mpz_clear(x);
+ return ret;
+error_and_exit:
+ ret = SRP_ERR;
+ goto cleanup_and_exit;
}
-
+// clang-format off
/* Out: bytes_B, len_B.
*
@@ -653,6 +666,7 @@ struct SRPVerifier *srp_verifier_new(SRP_HashAlgorithm alg,
mpz_t tmp1; mpz_init(tmp1);
mpz_t tmp2; mpz_init(tmp2);
mpz_t tmp3; mpz_init(tmp3);
+ // clang-format on
size_t ulen = strlen(username) + 1;
NGConstant *ng = new_ng(ng_type, n_hex, g_hex);
struct SRPVerifier *ver = 0;
@@ -660,27 +674,29 @@ struct SRPVerifier *srp_verifier_new(SRP_HashAlgorithm alg,
*len_B = 0;
*bytes_B = 0;
- if (!ng)
- goto cleanup_and_exit;
+ if (!ng) goto cleanup_and_exit;
- ver = (struct SRPVerifier *) malloc( sizeof(struct SRPVerifier) );
+ ver = (struct SRPVerifier *)srp_alloc(sizeof(struct SRPVerifier));
- if (!ver)
- goto cleanup_and_exit;
+ if (!ver) goto cleanup_and_exit;
- init_random(); /* Only happens once */
+ if (init_random() != SRP_OK) { /* Only happens once */
+ srp_free(ver);
+ ver = 0;
+ goto cleanup_and_exit;
+ }
- ver->username = (char *) malloc(ulen);
+ ver->username = (char *)srp_alloc(ulen);
ver->hash_alg = alg;
ver->ng = ng;
if (!ver->username) {
- free(ver);
+ srp_free(ver);
ver = 0;
goto cleanup_and_exit;
}
- memcpy((char*)ver->username, username, ulen);
+ memcpy((char *)ver->username, username, ulen);
ver->authenticated = 0;
@@ -690,25 +706,17 @@ struct SRPVerifier *srp_verifier_new(SRP_HashAlgorithm alg,
if (bytes_b) {
mpz_from_bin(bytes_b, len_b, b);
} else {
- mpz_fill_random(b);
+ if (!mpz_fill_random(b)) goto ver_cleanup_and_exit;
}
- if (!H_nn(k, alg, ng->N, ng->N, ng->g)) {
- free(ver);
- ver = 0;
- goto cleanup_and_exit;
- }
+ if (!H_nn(k, alg, ng->N, ng->N, ng->g)) goto ver_cleanup_and_exit;
/* B = kv + g^b */
mpz_mulm(tmp1, k, v, ng->N, tmp3);
mpz_powm(tmp2, ng->g, b, ng->N);
mpz_addm(B, tmp1, tmp2, ng->N, tmp3);
- if (!H_nn(u, alg, ng->N, A, B)) {
- free(ver);
- ver = 0;
- goto cleanup_and_exit;
- }
+ if (!H_nn(u, alg, ng->N, A, B)) goto ver_cleanup_and_exit;
srp_dbg_num(u, "Server calculated u: ");
@@ -717,27 +725,29 @@ struct SRPVerifier *srp_verifier_new(SRP_HashAlgorithm alg,
mpz_mulm(tmp2, A, tmp1, ng->N, tmp3);
mpz_powm(S, tmp2, b, ng->N);
- hash_num(alg, S, ver->session_key);
+ if (!hash_num(alg, S, ver->session_key)) goto ver_cleanup_and_exit;
- calculate_M(alg, ng, ver->M, username, bytes_s, len_s, A, B, ver->session_key);
- calculate_H_AMK(alg, ver->H_AMK, A, ver->M, ver->session_key);
+ if (!calculate_M(
+ alg, ng, ver->M, username, bytes_s, len_s, A, B, ver->session_key)) {
+ goto ver_cleanup_and_exit;
+ }
+ if (!calculate_H_AMK(alg, ver->H_AMK, A, ver->M, ver->session_key)) {
+ goto ver_cleanup_and_exit;
+ }
*len_B = mpz_num_bytes(B);
- *bytes_B = (unsigned char*)malloc(*len_B);
+ *bytes_B = (unsigned char *)srp_alloc(*len_B);
if (!*bytes_B) {
- free(ver->username);
- free(ver);
- ver = 0;
*len_B = 0;
- goto cleanup_and_exit;
+ goto ver_cleanup_and_exit;
}
mpz_to_bin(B, *bytes_B);
ver->bytes_B = *bytes_B;
} else {
- free(ver);
+ srp_free(ver);
ver = 0;
}
@@ -753,52 +763,49 @@ cleanup_and_exit:
mpz_clear(tmp2);
mpz_clear(tmp3);
return ver;
+ver_cleanup_and_exit:
+ srp_free(ver->username);
+ srp_free(ver);
+ ver = 0;
+ goto cleanup_and_exit;
}
-
-
-
void srp_verifier_delete(struct SRPVerifier *ver)
{
if (ver) {
delete_ng(ver->ng);
- free(ver->username);
- free(ver->bytes_B);
+ srp_free(ver->username);
+ srp_free(ver->bytes_B);
memset(ver, 0, sizeof(*ver));
- free(ver);
+ srp_free(ver);
}
}
-
-
int srp_verifier_is_authenticated(struct SRPVerifier *ver)
{
return ver->authenticated;
}
-
const char *srp_verifier_get_username(struct SRPVerifier *ver)
{
return ver->username;
}
-
-const unsigned char *srp_verifier_get_session_key(struct SRPVerifier *ver, size_t *key_length)
+const unsigned char *srp_verifier_get_session_key(
+ struct SRPVerifier *ver, size_t *key_length)
{
- if (key_length)
- *key_length = hash_length(ver->hash_alg);
+ if (key_length) *key_length = hash_length(ver->hash_alg);
return ver->session_key;
}
-
size_t srp_verifier_get_session_key_length(struct SRPVerifier *ver)
{
return hash_length(ver->hash_alg);
}
-
/* user_M must be exactly SHA512_DIGEST_LENGTH bytes in size */
-void srp_verifier_verify_session(struct SRPVerifier *ver, const unsigned char *user_M, unsigned char **bytes_HAMK)
+void srp_verifier_verify_session(
+ struct SRPVerifier *ver, const unsigned char *user_M, unsigned char **bytes_HAMK)
{
if (memcmp(ver->M, user_M, hash_length(ver->hash_alg)) == 0) {
ver->authenticated = 1;
@@ -811,17 +818,17 @@ void srp_verifier_verify_session(struct SRPVerifier *ver, const unsigned char *u
struct SRPUser *srp_user_new(SRP_HashAlgorithm alg, SRP_NGType ng_type,
const char *username, const char *username_for_verifier,
- const unsigned char *bytes_password, size_t len_password,
- const char *n_hex, const char *g_hex)
+ const unsigned char *bytes_password, size_t len_password, const char *n_hex,
+ const char *g_hex)
{
- struct SRPUser *usr = (struct SRPUser *) malloc(sizeof(struct SRPUser));
- size_t ulen = strlen(username) + 1;
+ struct SRPUser *usr = (struct SRPUser *)srp_alloc(sizeof(struct SRPUser));
+ size_t ulen = strlen(username) + 1;
size_t uvlen = strlen(username_for_verifier) + 1;
- if (!usr)
- goto err_exit;
+ if (!usr) goto err_exit;
- init_random(); /* Only happens once */
+ if (init_random() != SRP_OK) /* Only happens once */
+ goto err_exit;
usr->hash_alg = alg;
usr->ng = new_ng(ng_type, n_hex, g_hex);
@@ -830,16 +837,14 @@ struct SRPUser *srp_user_new(SRP_HashAlgorithm alg, SRP_NGType ng_type,
mpz_init(usr->A);
mpz_init(usr->S);
- if (!usr->ng)
- goto err_exit;
+ if (!usr->ng) goto err_exit;
- usr->username = (char*)malloc(ulen);
- usr->username_verifier = (char*)malloc(uvlen);
- usr->password = (unsigned char*)malloc(len_password);
+ usr->username = (char *)srp_alloc(ulen);
+ usr->username_verifier = (char *)srp_alloc(uvlen);
+ usr->password = (unsigned char *)srp_alloc(len_password);
usr->password_len = len_password;
- if (!usr->username || !usr->password)
- goto err_exit;
+ if (!usr->username || !usr->password || !usr->username_verifier) goto err_exit;
memcpy(usr->username, username, ulen);
memcpy(usr->username_verifier, username_for_verifier, uvlen);
@@ -856,27 +861,22 @@ err_exit:
mpz_clear(usr->a);
mpz_clear(usr->A);
mpz_clear(usr->S);
- if (usr->ng)
- delete_ng(usr->ng);
- if (usr->username)
- free(usr->username);
- if (usr->username_verifier)
- free(usr->username_verifier);
+ if (usr->ng) delete_ng(usr->ng);
+ srp_free(usr->username);
+ srp_free(usr->username_verifier);
if (usr->password) {
memset(usr->password, 0, usr->password_len);
- free(usr->password);
+ srp_free(usr->password);
}
- free(usr);
+ srp_free(usr);
}
return 0;
}
-
-
void srp_user_delete(struct SRPUser *usr)
{
- if(usr) {
+ if (usr) {
mpz_clear(usr->a);
mpz_clear(usr->A);
mpz_clear(usr->S);
@@ -885,77 +885,73 @@ void srp_user_delete(struct SRPUser *usr)
memset(usr->password, 0, usr->password_len);
- free(usr->username);
- free(usr->username_verifier);
- free(usr->password);
+ srp_free(usr->username);
+ srp_free(usr->username_verifier);
+ srp_free(usr->password);
- if (usr->bytes_A)
- free(usr->bytes_A);
+ if (usr->bytes_A) srp_free(usr->bytes_A);
memset(usr, 0, sizeof(*usr));
- free(usr);
+ srp_free(usr);
}
}
-
-
int srp_user_is_authenticated(struct SRPUser *usr)
{
return usr->authenticated;
}
-
const char *srp_user_get_username(struct SRPUser *usr)
{
return usr->username;
}
-
-const unsigned char* srp_user_get_session_key(struct SRPUser* usr, size_t* key_length)
+const unsigned char *srp_user_get_session_key(struct SRPUser *usr, size_t *key_length)
{
- if (key_length)
- *key_length = hash_length(usr->hash_alg);
+ if (key_length) *key_length = hash_length(usr->hash_alg);
return usr->session_key;
}
-
size_t srp_user_get_session_key_length(struct SRPUser *usr)
{
return hash_length(usr->hash_alg);
}
-
+// clang-format off
/* Output: username, bytes_A, len_A */
-void srp_user_start_authentication(struct SRPUser *usr, char **username,
+SRP_Result srp_user_start_authentication(struct SRPUser *usr, char **username,
const unsigned char *bytes_a, size_t len_a,
unsigned char **bytes_A, size_t *len_A)
{
+ // clang-format on
if (bytes_a) {
mpz_from_bin(bytes_a, len_a, usr->a);
} else {
- mpz_fill_random(usr->a);
+ if (!mpz_fill_random(usr->a)) goto error_and_exit;
}
mpz_powm(usr->A, usr->ng->g, usr->a, usr->ng->N);
*len_A = mpz_num_bytes(usr->A);
- *bytes_A = (unsigned char*)malloc(*len_A);
+ *bytes_A = (unsigned char *)srp_alloc(*len_A);
- if (!*bytes_A) {
- *len_A = 0;
- *bytes_A = 0;
- *username = 0;
- return;
- }
+ if (!*bytes_A) goto error_and_exit;
mpz_to_bin(usr->A, *bytes_A);
usr->bytes_A = *bytes_A;
- if (username)
- *username = usr->username;
-}
+ if (username) *username = usr->username;
+ return SRP_OK;
+
+error_and_exit:
+ *len_A = 0;
+ *bytes_A = 0;
+ *username = 0;
+ return SRP_ERR;
+}
+// clang-format off
/* Output: bytes_M. Buffer length is SHA512_DIGEST_LENGTH */
void srp_user_process_challenge(struct SRPUser *usr,
const unsigned char *bytes_s, size_t len_s,
@@ -971,17 +967,17 @@ void srp_user_process_challenge(struct SRPUser *usr,
mpz_t tmp2; mpz_init(tmp2);
mpz_t tmp3; mpz_init(tmp3);
mpz_t tmp4; mpz_init(tmp4);
+ // clang-format on
*len_M = 0;
*bytes_M = 0;
- if (!H_nn(u, usr->hash_alg, usr->ng->N, usr->A, B))
- goto cleanup_and_exit;
+ if (!H_nn(u, usr->hash_alg, usr->ng->N, usr->A, B)) goto cleanup_and_exit;
srp_dbg_num(u, "Client calculated u: ");
- if (!calculate_x(x, usr->hash_alg, bytes_s, len_s,
- usr->username_verifier, usr->password, usr->password_len))
+ if (!calculate_x(x, usr->hash_alg, bytes_s, len_s, usr->username_verifier,
+ usr->password, usr->password_len))
goto cleanup_and_exit;
srp_dbg_num(x, "Client calculated x: ");
@@ -990,11 +986,12 @@ void srp_user_process_challenge(struct SRPUser *usr,
goto cleanup_and_exit;
/* SRP-6a safety check */
- if ( mpz_sgn(B) != 0 && mpz_sgn(u) != 0 ) {
+ if (mpz_sgn(B) != 0 && mpz_sgn(u) != 0) {
mpz_powm(v, usr->ng->g, x, usr->ng->N);
srp_dbg_num(v, "Client calculated v: ");
+ // clang-format off
/* S = (B - k*(g^x)) ^ (a + ux) */
mpz_mul(tmp1, u, x);
mpz_add(tmp2, usr->a, tmp1); /* tmp2 = (a + ux) */
@@ -1002,23 +999,24 @@ void srp_user_process_challenge(struct SRPUser *usr,
mpz_mulm(tmp3, k, tmp1, usr->ng->N, tmp4); /* tmp3 = k*(g^x) */
mpz_subm(tmp1, B, tmp3, usr->ng->N, tmp4); /* tmp1 = (B - K*(g^x)) */
mpz_powm(usr->S, tmp1, tmp2, usr->ng->N);
+ // clang-format on
- hash_num(usr->hash_alg, usr->S, usr->session_key);
+ if (!hash_num(usr->hash_alg, usr->S, usr->session_key)) goto cleanup_and_exit;
- calculate_M( usr->hash_alg, usr->ng, usr->M, usr->username, bytes_s, len_s, usr->A,B, usr->session_key );
- calculate_H_AMK( usr->hash_alg, usr->H_AMK, usr->A, usr->M, usr->session_key );
+ if (!calculate_M(usr->hash_alg, usr->ng, usr->M, usr->username, bytes_s, len_s,
+ usr->A, B, usr->session_key))
+ goto cleanup_and_exit;
+ if (!calculate_H_AMK(usr->hash_alg, usr->H_AMK, usr->A, usr->M, usr->session_key))
+ goto cleanup_and_exit;
*bytes_M = usr->M;
- if (len_M)
- *len_M = hash_length( usr->hash_alg );
+ if (len_M) *len_M = hash_length(usr->hash_alg);
} else {
*bytes_M = NULL;
- if (len_M)
- *len_M = 0;
+ if (len_M) *len_M = 0;
}
cleanup_and_exit:
-
mpz_clear(B);
mpz_clear(u);
mpz_clear(x);
@@ -1030,7 +1028,6 @@ cleanup_and_exit:
mpz_clear(tmp4);
}
-
void srp_user_verify_session(struct SRPUser *usr, const unsigned char *bytes_HAMK)
{
if (memcmp(usr->H_AMK, bytes_HAMK, hash_length(usr->hash_alg)) == 0)
diff --git a/src/util/srp.h b/src/util/srp.h
index 15a2b8a68..2d49b076e 100644
--- a/src/util/srp.h
+++ b/src/util/srp.h
@@ -56,12 +56,10 @@
#ifndef SRP_H
#define SRP_H
-
struct SRPVerifier;
struct SRPUser;
-typedef enum
-{
+typedef enum {
SRP_NG_1024,
SRP_NG_2048,
SRP_NG_4096,
@@ -69,8 +67,7 @@ typedef enum
SRP_NG_CUSTOM
} SRP_NGType;
-typedef enum
-{
+typedef enum {
/*SRP_SHA1,*/
/*SRP_SHA224,*/
SRP_SHA256,
@@ -78,6 +75,23 @@ typedef enum
SRP_SHA512*/
} SRP_HashAlgorithm;
+typedef enum {
+ SRP_ERR,
+ SRP_OK,
+} SRP_Result;
+
+// clang-format off
+
+/* Sets the memory functions used by srp.
+ * Note: this doesn't set the memory functions used by gmp,
+ * but it is supported to have different functions for srp and gmp.
+ * Don't call this after you have already allocated srp structures.
+ */
+void srp_set_memory_functions(
+ void *(*new_srp_alloc) (size_t),
+ void *(*new_srp_realloc) (void *, size_t),
+ void (*new_srp_free) (void *));
+
/* Out: bytes_v, len_v
*
* The caller is responsible for freeing the memory allocated for bytes_v
@@ -85,14 +99,18 @@ typedef enum
* The n_hex and g_hex parameters should be 0 unless SRP_NG_CUSTOM is used for ng_type.
* If provided, they must contain ASCII text of the hexidecimal notation.
*
- * If bytes_s == NULL, it is filled with random data. The caller is responsible for freeing.
+ * If bytes_s == NULL, it is filled with random data.
+ * The caller is responsible for freeing.
+ *
+ * Returns SRP_OK on success, and SRP_ERR on error.
+ * bytes_s might be in this case invalid, don't free it.
*/
-void srp_create_salted_verification_key( SRP_HashAlgorithm alg,
+SRP_Result srp_create_salted_verification_key(SRP_HashAlgorithm alg,
SRP_NGType ng_type, const char *username_for_verifier,
const unsigned char *password, size_t len_password,
unsigned char **bytes_s, size_t *len_s,
unsigned char **bytes_v, size_t *len_v,
- const char * n_hex, const char *g_hex );
+ const char *n_hex, const char *g_hex);
/* Out: bytes_B, len_B.
*
@@ -101,6 +119,8 @@ void srp_create_salted_verification_key( SRP_HashAlgorithm alg,
* The n_hex and g_hex parameters should be 0 unless SRP_NG_CUSTOM is used for ng_type
*
* If bytes_b == NULL, random data is used for b.
+ *
+ * Returns pointer to SRPVerifier on success, and NULL on error.
*/
struct SRPVerifier* srp_verifier_new(SRP_HashAlgorithm alg, SRP_NGType ng_type,
const char *username,
@@ -111,52 +131,54 @@ struct SRPVerifier* srp_verifier_new(SRP_HashAlgorithm alg, SRP_NGType ng_type,
unsigned char** bytes_B, size_t *len_B,
const char* n_hex, const char* g_hex);
+// clang-format on
-void srp_verifier_delete( struct SRPVerifier* ver );
+void srp_verifier_delete(struct SRPVerifier *ver);
+// srp_verifier_verify_session must have been called before
+int srp_verifier_is_authenticated(struct SRPVerifier *ver);
-int srp_verifier_is_authenticated( struct SRPVerifier* ver );
-
-
-const char * srp_verifier_get_username( struct SRPVerifier* ver );
+const char *srp_verifier_get_username(struct SRPVerifier *ver);
/* key_length may be null */
-const unsigned char* srp_verifier_get_session_key( struct SRPVerifier* ver,
- size_t *key_length );
-
-
-size_t srp_verifier_get_session_key_length(struct SRPVerifier* ver);
+const unsigned char *srp_verifier_get_session_key(
+ struct SRPVerifier *ver, size_t *key_length);
+size_t srp_verifier_get_session_key_length(struct SRPVerifier *ver);
-/* user_M must be exactly srp_verifier_get_session_key_length() bytes in size */
-void srp_verifier_verify_session( struct SRPVerifier* ver,
- const unsigned char* user_M, unsigned char** bytes_HAMK );
+/* Verifies session, on success, it writes bytes_HAMK.
+ * user_M must be exactly srp_verifier_get_session_key_length() bytes in size
+ */
+void srp_verifier_verify_session(
+ struct SRPVerifier *ver, const unsigned char *user_M, unsigned char **bytes_HAMK);
/*******************************************************************************/
/* The n_hex and g_hex parameters should be 0 unless SRP_NG_CUSTOM is used for ng_type */
struct SRPUser *srp_user_new(SRP_HashAlgorithm alg, SRP_NGType ng_type,
const char *username, const char *username_for_verifier,
- const unsigned char *bytes_password, size_t len_password,
- const char *n_hex, const char *g_hex);
+ const unsigned char *bytes_password, size_t len_password, const char *n_hex,
+ const char *g_hex);
-void srp_user_delete(struct SRPUser * usr);
+void srp_user_delete(struct SRPUser *usr);
-int srp_user_is_authenticated(struct SRPUser * usr);
+int srp_user_is_authenticated(struct SRPUser *usr);
-
-const char* srp_user_get_username(struct SRPUser * usr);
+const char *srp_user_get_username(struct SRPUser *usr);
/* key_length may be null */
-const unsigned char* srp_user_get_session_key(struct SRPUser* usr, size_t* key_length);
+const unsigned char *srp_user_get_session_key(struct SRPUser *usr, size_t *key_length);
+
+size_t srp_user_get_session_key_length(struct SRPUser *usr);
-size_t srp_user_get_session_key_length(struct SRPUser* usr);
+// clang-format off
-/* Output: username, bytes_A, len_A. If you don't want it get written, set username to NULL.
+/* Output: username, bytes_A, len_A.
+ * If you don't want it get written, set username to NULL.
* If bytes_a == NULL, random data is used for a. */
-void srp_user_start_authentication(struct SRPUser* usr, char** username,
- const unsigned char* bytes_a, size_t len_a,
- unsigned char** bytes_A, size_t* len_A);
+SRP_Result srp_user_start_authentication(struct SRPUser* usr, char **username,
+ const unsigned char *bytes_a, size_t len_a,
+ unsigned char **bytes_A, size_t* len_A);
/* Output: bytes_M, len_M (len_M may be null and will always be
* srp_user_get_session_key_length() bytes in size) */
@@ -164,8 +186,9 @@ void srp_user_process_challenge(struct SRPUser *usr,
const unsigned char *bytes_s, size_t len_s,
const unsigned char *bytes_B, size_t len_B,
unsigned char **bytes_M, size_t *len_M);
+// clang-format on
/* bytes_HAMK must be exactly srp_user_get_session_key_length() bytes in size */
-void srp_user_verify_session(struct SRPUser* usr, const unsigned char* bytes_HAMK);
+void srp_user_verify_session(struct SRPUser *usr, const unsigned char *bytes_HAMK);
#endif /* Include Guard */
diff --git a/src/util/strfnd.h b/src/util/strfnd.h
new file mode 100644
index 000000000..a7cd2badb
--- /dev/null
+++ b/src/util/strfnd.h
@@ -0,0 +1,82 @@
+/*
+Minetest
+Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@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.
+*/
+
+#ifndef STRFND_HEADER
+#define STRFND_HEADER
+
+#include <string>
+
+template <typename T>
+class BasicStrfnd {
+ typedef std::basic_string<T> String;
+ String str;
+ size_t pos;
+public:
+ BasicStrfnd(const String &s) : str(s), pos(0) {}
+ void start(const String &s) { str = s; pos = 0; }
+ size_t where() { return pos; }
+ void to(size_t i) { pos = i; }
+ bool at_end() { return pos >= str.size(); }
+ String what() { return str; }
+
+ String next(const String &sep)
+ {
+ if (pos >= str.size())
+ return String();
+
+ size_t n;
+ if (sep.empty() || (n = str.find(sep, pos)) == String::npos) {
+ n = str.size();
+ }
+ String ret = str.substr(pos, n - pos);
+ pos = n + sep.size();
+ return ret;
+ }
+
+ // Returns substr up to the next occurence of sep that isn't escaped with esc ('\\')
+ String next_esc(const String &sep, T esc=static_cast<T>('\\'))
+ {
+ if (pos >= str.size())
+ return String();
+
+ size_t n, old_p = pos;
+ do {
+ if (sep.empty() || (n = str.find(sep, pos)) == String::npos) {
+ pos = n = str.size();
+ break;
+ }
+ pos = n + sep.length();
+ } while (n > 0 && str[n - 1] == esc);
+
+ return str.substr(old_p, n - old_p);
+ }
+
+ void skip_over(const String &chars)
+ {
+ size_t p = str.find_first_not_of(chars, pos);
+ if (p != String::npos)
+ pos = p;
+ }
+};
+
+typedef BasicStrfnd<char> Strfnd;
+typedef BasicStrfnd<wchar_t> WStrfnd;
+
+#endif
+
diff --git a/src/util/string.h b/src/util/string.h
index 793baad0e..40ef3e4d3 100644
--- a/src/util/string.h
+++ b/src/util/string.h
@@ -32,8 +32,26 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#define STRINGIFY(x) #x
#define TOSTRING(x) STRINGIFY(x)
+// Checks whether a value is an ASCII printable character
+#define IS_ASCII_PRINTABLE_CHAR(x) \
+ (((unsigned int)(x) >= 0x20) && \
+ ( (unsigned int)(x) <= 0x7e))
+
// Checks whether a byte is an inner byte for an utf-8 multibyte sequence
-#define IS_UTF8_MULTB_INNER(x) (((unsigned char)x >= 0x80) && ((unsigned char)x < 0xc0))
+#define IS_UTF8_MULTB_INNER(x) \
+ (((unsigned char)(x) >= 0x80) && \
+ ( (unsigned char)(x) <= 0xbf))
+
+// Checks whether a byte is a start byte for an utf-8 multibyte sequence
+#define IS_UTF8_MULTB_START(x) \
+ (((unsigned char)(x) >= 0xc2) && \
+ ( (unsigned char)(x) <= 0xf4))
+
+// Given a start byte x for an utf-8 multibyte sequence
+// it gives the length of the whole sequence in bytes.
+#define UTF8_MULTB_START_LEN(x) \
+ (((unsigned char)(x) < 0xe0) ? 2 : \
+ (((unsigned char)(x) < 0xf0) ? 3 : 4))
typedef std::map<std::string, std::string> StringMap;
@@ -281,15 +299,6 @@ inline s32 mystoi(const std::string &str, s32 min, s32 max)
}
-/// Returns a 64-bit value represented by the string \p str (decimal).
-inline s64 stoi64(const std::string &str)
-{
- std::stringstream tmp(str);
- s64 t;
- tmp >> t;
- return t;
-}
-
// MSVC2010 includes it's own versions of these
//#if !defined(_MSC_VER) || _MSC_VER < 1600
@@ -328,9 +337,22 @@ inline float mystof(const std::string &str)
#define stoi mystoi
#define stof mystof
+/// Returns a value represented by the string \p val.
+template <typename T>
+inline T from_string(const std::string &str)
+{
+ std::stringstream tmp(str);
+ T t;
+ tmp >> t;
+ return t;
+}
+
+/// Returns a 64-bit signed value represented by the string \p str (decimal).
+inline s64 stoi64(const std::string &str) { return from_string<s64>(str); }
+
// TODO: Replace with C++11 std::to_string.
-/// Returns A string representing the value \p val.
+/// Returns a string representing the value \p val.
template <typename T>
inline std::string to_string(T val)
{
@@ -364,7 +386,6 @@ inline void str_replace(std::string &str, const std::string &pattern,
}
}
-
/**
* Replace all occurrences of the character \p from in \p str with \p to.
*
@@ -447,7 +468,7 @@ inline std::string wrap_rows(const std::string &from,
* Removes backslashes from an escaped string (FormSpec strings)
*/
template <typename T>
-inline std::basic_string<T> unescape_string(std::basic_string<T> &s)
+inline std::basic_string<T> unescape_string(const std::basic_string<T> &s)
{
std::basic_string<T> res;
@@ -463,6 +484,40 @@ inline std::basic_string<T> unescape_string(std::basic_string<T> &s)
return res;
}
+/**
+ * Remove all escape sequences in \p s.
+ *
+ * @param s The string in which to remove escape sequences.
+ * @return \p s, with escape sequences removed.
+ */
+template <typename T>
+std::basic_string<T> unescape_enriched(const std::basic_string<T> &s)
+{
+ std::basic_string<T> output;
+ size_t i = 0;
+ while (i < s.length()) {
+ if (s[i] == '\x1b') {
+ ++i;
+ if (i == s.length()) continue;
+ if (s[i] == '(') {
+ ++i;
+ while (i < s.length() && s[i] != ')') {
+ if (s[i] == '\\') {
+ ++i;
+ }
+ ++i;
+ }
+ ++i;
+ } else {
+ ++i;
+ }
+ continue;
+ }
+ output += s[i];
+ ++i;
+ }
+ return output;
+}
/**
* Checks that all characters in \p to_check are a decimal digits.
diff --git a/src/util/thread.h b/src/util/thread.h
index b3a5e68a2..5ed63544c 100644
--- a/src/util/thread.h
+++ b/src/util/thread.h
@@ -21,9 +21,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#define UTIL_THREAD_HEADER
#include "../irrlichttypes.h"
-#include "../jthread/jthread.h"
-#include "../jthread/jmutex.h"
-#include "../jthread/jmutexautolock.h"
+#include "../threading/thread.h"
+#include "../threading/mutex.h"
+#include "../threading/mutex_auto_lock.h"
#include "porting.h"
#include "log.h"
@@ -36,27 +36,27 @@ public:
T get()
{
- JMutexAutoLock lock(m_mutex);
+ MutexAutoLock lock(m_mutex);
return m_value;
}
void set(T value)
{
- JMutexAutoLock lock(m_mutex);
+ MutexAutoLock lock(m_mutex);
m_value = value;
}
// You'll want to grab this in a SharedPtr
- JMutexAutoLock *getLock()
+ MutexAutoLock *getLock()
{
- return new JMutexAutoLock(m_mutex);
+ return new MutexAutoLock(m_mutex);
}
// You pretty surely want to grab the lock when accessing this
T m_value;
private:
- JMutex m_mutex;
+ Mutex m_mutex;
};
/*
@@ -118,7 +118,7 @@ public:
typename std::list<CallerInfo<Caller, CallerData, Key, T> >::iterator j;
{
- JMutexAutoLock lock(m_queue.getMutex());
+ MutexAutoLock lock(m_queue.getMutex());
/*
If the caller is already on the list, only update CallerData
@@ -192,59 +192,47 @@ private:
MutexedQueue<GetRequest<Key, T, Caller, CallerData> > m_queue;
};
-class UpdateThread : public JThread {
+class UpdateThread : public Thread
+{
public:
- UpdateThread() {}
- virtual ~UpdateThread() {}
+ UpdateThread(const std::string &name) : Thread(name + "Update") {}
+ ~UpdateThread() {}
- void deferUpdate()
- {
- m_update_sem.Post();
- }
+ void deferUpdate() { m_update_sem.post(); }
- void Stop()
+ void stop()
{
- JThread::Stop();
+ Thread::stop();
// give us a nudge
- m_update_sem.Post();
+ m_update_sem.post();
}
- void *Thread()
+ void *run()
{
- ThreadStarted();
-
- const char *thread_name = getName();
- log_register_thread(thread_name);
- porting::setThreadName(thread_name);
-
- DSTACK(__FUNCTION_NAME);
+ DSTACK(FUNCTION_NAME);
BEGIN_DEBUG_EXCEPTION_HANDLER
- while (!StopRequested()) {
- m_update_sem.Wait();
-
- // Empty the queue, just in case doUpdate() is expensive
- while (m_update_sem.GetValue())
- m_update_sem.Wait();
+ while (!stopRequested()) {
+ m_update_sem.wait();
+ // Set semaphore to 0
+ while (m_update_sem.wait(0));
- if (StopRequested())
- break;
+ if (stopRequested()) break;
doUpdate();
}
- END_DEBUG_EXCEPTION_HANDLER(errorstream)
+ END_DEBUG_EXCEPTION_HANDLER
return NULL;
}
protected:
virtual void doUpdate() = 0;
- virtual const char *getName() = 0;
private:
- JSemaphore m_update_sem;
+ Semaphore m_update_sem;
};
#endif
diff --git a/src/wieldmesh.cpp b/src/wieldmesh.cpp
index bc2977a0e..9c4d5b642 100644
--- a/src/wieldmesh.cpp
+++ b/src/wieldmesh.cpp
@@ -114,9 +114,7 @@ static scene::IMesh *createExtrusionMesh(int resolution_x, int resolution_y)
mesh->addMeshBuffer(buf);
buf->drop();
scaleMesh(mesh, scale); // also recalculates bounding box
- scene::IMesh *newmesh = createForsythOptimizedMesh(mesh);
- mesh->drop();
- return newmesh;
+ return mesh;
}
/*
@@ -283,7 +281,7 @@ void WieldMeshSceneNode::setExtruded(const std::string &imagename,
// Customize material
video::SMaterial &material = m_meshnode->getMaterial(0);
- material.setTexture(0, tsrc->getTexture(imagename));
+ material.setTexture(0, tsrc->getTextureForMesh(imagename));
material.TextureLayer[0].TextureWrapU = video::ETC_CLAMP_TO_EDGE;
material.TextureLayer[0].TextureWrapV = video::ETC_CLAMP_TO_EDGE;
material.MaterialType = m_material_type;
@@ -436,3 +434,113 @@ void WieldMeshSceneNode::changeToMesh(scene::IMesh *mesh)
m_meshnode->setMaterialFlag(video::EMF_NORMALIZE_NORMALS, m_lighting);
m_meshnode->setVisible(true);
}
+
+scene::IMesh *getItemMesh(IGameDef *gamedef, const ItemStack &item)
+{
+ ITextureSource *tsrc = gamedef->getTextureSource();
+ IItemDefManager *idef = gamedef->getItemDefManager();
+ INodeDefManager *ndef = gamedef->getNodeDefManager();
+ const ItemDefinition &def = item.getDefinition(idef);
+ const ContentFeatures &f = ndef->get(def.name);
+ content_t id = ndef->getId(def.name);
+
+ if (!g_extrusion_mesh_cache) {
+ g_extrusion_mesh_cache = new ExtrusionMeshCache();
+ } else {
+ g_extrusion_mesh_cache->grab();
+ }
+
+ scene::IMesh *mesh;
+
+ // If inventory_image is defined, it overrides everything else
+ if (def.inventory_image != "") {
+ mesh = getExtrudedMesh(tsrc, def.inventory_image);
+ return mesh;
+ } 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));
+ setMeshColor(mesh, video::SColor (255, 255, 255, 255));
+ } else if (f.drawtype == NDT_PLANTLIKE) {
+ mesh = getExtrudedMesh(tsrc,
+ tsrc->getTextureName(f.tiles[0].texture_id));
+ return mesh;
+ } else if (f.drawtype == NDT_NORMAL || f.drawtype == NDT_ALLFACES
+ || f.drawtype == NDT_LIQUID || f.drawtype == NDT_FLOWINGLIQUID) {
+ mesh = cloneMesh(g_extrusion_mesh_cache->createCube());
+ scaleMesh(mesh, v3f(1.2, 1.2, 1.2));
+ } else {
+ MeshMakeData mesh_make_data(gamedef, false);
+ MapNode mesh_make_node(id, 255, 0);
+ mesh_make_data.fillSingleNode(&mesh_make_node);
+ MapBlockMesh mapblock_mesh(&mesh_make_data, v3s16(0, 0, 0));
+ mesh = cloneMesh(mapblock_mesh.getMesh());
+ translateMesh(mesh, v3f(-BS, -BS, -BS));
+ scaleMesh(mesh, v3f(0.12, 0.12, 0.12));
+ rotateMeshXZby(mesh, -45);
+ rotateMeshYZby(mesh, -30);
+
+ u32 mc = mesh->getMeshBufferCount();
+ for (u32 i = 0; i < mc; ++i) {
+ video::SMaterial &material1 =
+ mesh->getMeshBuffer(i)->getMaterial();
+ video::SMaterial &material2 =
+ mapblock_mesh.getMesh()->getMeshBuffer(i)->getMaterial();
+ material1.setTexture(0, material2.getTexture(0));
+ material1.setTexture(1, material2.getTexture(1));
+ material1.setTexture(2, material2.getTexture(2));
+ material1.setTexture(3, material2.getTexture(3));
+ material1.MaterialType = material2.MaterialType;
+ }
+ return mesh;
+ }
+
+ shadeMeshFaces(mesh);
+ rotateMeshXZby(mesh, -45);
+ rotateMeshYZby(mesh, -30);
+
+ u32 mc = mesh->getMeshBufferCount();
+ for (u32 i = 0; i < mc; ++i) {
+ video::SMaterial &material = mesh->getMeshBuffer(i)->getMaterial();
+ material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
+ material.setFlag(video::EMF_BILINEAR_FILTER, false);
+ material.setFlag(video::EMF_TRILINEAR_FILTER, false);
+ material.setFlag(video::EMF_BACK_FACE_CULLING, true);
+ material.setFlag(video::EMF_LIGHTING, false);
+ if (f.tiles[i].animation_frame_count > 1) {
+ FrameSpec animation_frame = f.tiles[i].frames[0];
+ material.setTexture(0, animation_frame.texture);
+ } else {
+ material.setTexture(0, f.tiles[i].texture);
+ }
+ }
+ return mesh;
+ }
+ return NULL;
+}
+
+scene::IMesh * getExtrudedMesh(ITextureSource *tsrc,
+ const std::string &imagename)
+{
+ video::ITexture *texture = tsrc->getTextureForMesh(imagename);
+ if (!texture) {
+ return NULL;
+ }
+
+ core::dimension2d<u32> dim = texture->getSize();
+ scene::IMesh *mesh = cloneMesh(g_extrusion_mesh_cache->create(dim));
+
+ // Customize material
+ video::SMaterial &material = mesh->getMeshBuffer(0)->getMaterial();
+ material.setTexture(0, tsrc->getTexture(imagename));
+ material.TextureLayer[0].TextureWrapU = video::ETC_CLAMP_TO_EDGE;
+ material.TextureLayer[0].TextureWrapV = video::ETC_CLAMP_TO_EDGE;
+ material.setFlag(video::EMF_BILINEAR_FILTER, false);
+ material.setFlag(video::EMF_TRILINEAR_FILTER, false);
+ material.setFlag(video::EMF_BACK_FACE_CULLING, true);
+ material.setFlag(video::EMF_LIGHTING, false);
+ material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
+ scaleMesh(mesh, v3f(2.0, 2.0, 2.0));
+
+ return mesh;
+}
diff --git a/src/wieldmesh.h b/src/wieldmesh.h
index 3f4f4fc04..0b3136bc1 100644
--- a/src/wieldmesh.h
+++ b/src/wieldmesh.h
@@ -53,7 +53,7 @@ public:
virtual void render();
- virtual const core::aabbox3d<f32>& getBoundingBox() const
+ virtual const aabb3f &getBoundingBox() const
{ return m_bounding_box; }
private:
@@ -74,7 +74,11 @@ private:
// Bounding box culling is disabled for this type of scene node,
// so this variable is just required so we can implement
// getBoundingBox() and is set to an empty box.
- core::aabbox3d<f32> m_bounding_box;
+ aabb3f m_bounding_box;
};
+scene::IMesh *getItemMesh(IGameDef *gamedef, const ItemStack &item);
+
+scene::IMesh *getExtrudedMesh(ITextureSource *tsrc,
+ const std::string &imagename);
#endif