summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt77
-rw-r--r--src/biome.cpp233
-rw-r--r--src/biome.h98
-rw-r--r--src/cguittfont/CGUITTFont.cpp1146
-rw-r--r--src/cguittfont/CGUITTFont.h377
-rw-r--r--src/cguittfont/CMakeLists.txt17
-rw-r--r--src/cguittfont/irrUString.h3877
-rw-r--r--src/cguittfont/xCGUITTFont.cpp5
-rw-r--r--src/cguittfont/xCGUITTFont.h7
-rw-r--r--src/client.cpp20
-rw-r--r--src/clientmap.cpp2
-rw-r--r--src/clientserver.h17
-rw-r--r--src/clouds.cpp3
-rw-r--r--src/cmake_config.h.in1
-rw-r--r--src/config.h3
-rw-r--r--src/content_abm.cpp3
-rw-r--r--src/content_mapblock.cpp12
-rw-r--r--src/defaultsettings.cpp52
-rw-r--r--src/environment.cpp53
-rw-r--r--src/environment.h3
-rw-r--r--src/farmesh.cpp6
-rw-r--r--src/game.cpp12
-rw-r--r--src/gettext.h41
-rw-r--r--src/guiChatConsole.cpp10
-rw-r--r--src/guiConfigureWorld.cpp705
-rw-r--r--src/guiConfigureWorld.h110
-rw-r--r--src/guiKeyChangeMenu.cpp34
-rw-r--r--src/guiMainMenu.cpp241
-rw-r--r--src/guiMainMenu.h18
-rw-r--r--src/guiPauseMenu.cpp506
-rw-r--r--src/guiPauseMenu.h120
-rw-r--r--src/guiTextInputMenu.cpp12
-rw-r--r--src/intlGUIEditBox.cpp1508
-rw-r--r--src/intlGUIEditBox.h178
-rw-r--r--src/irrlichttypes.h3
-rw-r--r--src/itemdef.cpp1
-rw-r--r--src/itemdef.h2
-rw-r--r--src/json/CMakeLists.txt14
-rw-r--r--src/json/UPDATING16
-rw-r--r--src/json/json.h1914
-rw-r--r--src/json/jsoncpp.cpp4367
-rw-r--r--src/localplayer.cpp186
-rw-r--r--src/main.cpp81
-rw-r--r--src/map.cpp335
-rw-r--r--src/map.h98
-rw-r--r--src/mapgen.cpp379
-rw-r--r--src/mapgen.h134
-rw-r--r--src/mapgen_v6.cpp1438
-rw-r--r--src/mapgen_v6.h141
-rw-r--r--src/mods.cpp277
-rw-r--r--src/mods.h90
-rw-r--r--src/nodedef.cpp18
-rw-r--r--src/nodedef.h2
-rw-r--r--src/noise.cpp731
-rw-r--r--src/noise.h171
-rw-r--r--src/player.cpp58
-rw-r--r--src/player.h25
-rw-r--r--src/porting.cpp90
-rw-r--r--src/porting.h24
-rw-r--r--src/scriptapi.cpp513
-rw-r--r--src/server.cpp566
-rw-r--r--src/server.h111
-rw-r--r--src/serverlist.cpp276
-rw-r--r--src/serverlist.h46
-rw-r--r--src/settings.h396
-rw-r--r--src/subgame.cpp6
-rw-r--r--src/subgame.h13
-rw-r--r--src/treegen.cpp126
-rw-r--r--src/treegen.h27
-rw-r--r--src/util/numeric.h1
-rw-r--r--src/util/string.cpp65
-rw-r--r--src/util/string.h8
-rw-r--r--src/voxel.h34
-rw-r--r--src/voxelalgorithms.cpp8
74 files changed, 20451 insertions, 1847 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 24f682f3f..17102e848 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -5,6 +5,7 @@ cmake_minimum_required( VERSION 2.6 )
mark_as_advanced(EXECUTABLE_OUTPUT_PATH LIBRARY_OUTPUT_PATH)
mark_as_advanced(JTHREAD_INCLUDE_DIR JTHREAD_LIBRARY)
mark_as_advanced(SQLITE3_INCLUDE_DIR SQLITE3_LIBRARY)
+mark_as_advanced(JSON_INCLUDE_DIR JSON_LIBRARY)
option(ENABLE_CURL "Enable cURL support for fetching media" 1)
@@ -93,6 +94,12 @@ if(USE_SOUND)
)
endif()
+option(ENABLE_FREETYPE "Enable freetype2 (truetype fonts and basic unicode support)" OFF)
+set(USE_FREETYPE 0)
+if(ENABLE_FREETYPE)
+ set(USE_FREETYPE 1)
+endif(ENABLE_FREETYPE)
+
if(NOT MSVC)
set(USE_GPROF 0 CACHE BOOL "Use -pg flag for g++")
endif()
@@ -117,6 +124,14 @@ if(WIN32)
CACHE FILEPATH "Path to zlibwapi.dll (for installation)")
set(IRRLICHT_SOURCE_DIR "${PROJECT_SOURCE_DIR}/../../irrlicht-1.7.2"
CACHE PATH "irrlicht dir")
+ if(USE_FREETYPE)
+ set(FREETYPE_INCLUDE_DIR_ft2build "${PROJECT_SOURCE_DIR}/../../freetype2/include/"
+ CACHE PATH "freetype include dir")
+ set(FREETYPE_INCLUDE_DIR_freetype2 "${PROJECT_SOURCE_DIR}/../../freetype2/include/freetype"
+ CACHE PATH "freetype include dir")
+ set(FREETYPE_LIBRARY "${PROJECT_SOURCE_DIR}/../../freetype2/objs/win32/vc2005/freetype247.lib"
+ CACHE FILEPATH "Path to freetype247.lib")
+ endif(USE_FREETYPE)
set(MINGWM10_DLL ""
CACHE FILEPATH "Path to mingwm10.dll (for installation)")
if(ENABLE_SOUND)
@@ -156,6 +171,13 @@ endif()
find_package(Jthread REQUIRED)
find_package(Sqlite3 REQUIRED)
+find_package(Json REQUIRED)
+
+if(USE_FREETYPE)
+ find_package(Freetype REQUIRED)
+ set(CGUITTFONT_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/cguittfont")
+ set(CGUITTFONT_LIBRARY cguittfont)
+endif(USE_FREETYPE)
# Do not use system-wide installation of Lua, because it'll likely be a
# different version and/or has different build options.
@@ -188,6 +210,7 @@ set(common_SRCS
log.cpp
content_sao.cpp
mapgen.cpp
+ mapgen_v6.cpp
treegen.cpp
content_nodemeta.cpp
content_mapnode.cpp
@@ -218,8 +241,10 @@ set(common_SRCS
sha1.cpp
base64.cpp
ban.cpp
+ biome.cpp
clientserver.cpp
staticobject.cpp
+ serverlist.cpp
util/serialize.cpp
util/directiontables.cpp
util/numeric.cpp
@@ -275,6 +300,7 @@ set(minetest_SRCS
guiDeathScreen.cpp
guiChatConsole.cpp
guiCreateWorld.cpp
+ guiConfigureWorld.cpp
guiConfirmMenu.cpp
client.cpp
filecache.cpp
@@ -284,6 +310,13 @@ set(minetest_SRCS
main.cpp
)
+if(USE_FREETYPE)
+ set(minetest_SRCS
+ ${minetest_SRCS}
+ intlGUIEditBox.cpp
+ )
+endif(USE_FREETYPE)
+
# Server sources
set(minetestserver_SRCS
${common_SRCS}
@@ -301,8 +334,22 @@ include_directories(
${JTHREAD_INCLUDE_DIR}
${SQLITE3_INCLUDE_DIR}
${LUA_INCLUDE_DIR}
+ ${JSON_INCLUDE_DIR}
)
+if(USE_FREETYPE)
+ include_directories(
+ ${FREETYPE_INCLUDE_DIRS}
+ ${CGUITTFONT_INCLUDE_DIR}
+ )
+endif(USE_FREETYPE)
+
+if(USE_CURL)
+ include_directories(
+ ${CURL_INCLUDE_DIR}
+ )
+endif(USE_CURL)
+
set(EXECUTABLE_OUTPUT_PATH "${CMAKE_SOURCE_DIR}/bin")
if(BUILD_CLIENT)
@@ -321,19 +368,23 @@ if(BUILD_CLIENT)
${JTHREAD_LIBRARY}
${SQLITE3_LIBRARY}
${LUA_LIBRARY}
+ ${JSON_LIBRARY}
${PLATFORM_LIBS}
${CLIENT_PLATFORM_LIBS}
)
-
if(USE_CURL)
target_link_libraries(
${PROJECT_NAME}
${CURL_LIBRARY}
)
- include_directories(
- ${CURL_INCLUDE_DIR}
- )
endif(USE_CURL)
+ if(USE_FREETYPE)
+ target_link_libraries(
+ ${PROJECT_NAME}
+ ${FREETYPE_LIBRARY}
+ ${CGUITTFONT_LIBRARY}
+ )
+ endif(USE_FREETYPE)
endif(BUILD_CLIENT)
if(BUILD_SERVER)
@@ -343,11 +394,20 @@ if(BUILD_SERVER)
${ZLIB_LIBRARIES}
${JTHREAD_LIBRARY}
${SQLITE3_LIBRARY}
+ ${JSON_LIBRARY}
+ ${GETTEXT_LIBRARY}
${LUA_LIBRARY}
${PLATFORM_LIBS}
)
+ if(USE_CURL)
+ target_link_libraries(
+ ${PROJECT_NAME}server
+ ${CURL_LIBRARY}
+ )
+ endif(USE_CURL)
endif(BUILD_SERVER)
+
#
# Set some optimizations and tweaks
#
@@ -514,9 +574,18 @@ else (SQLITE3_FOUND)
add_subdirectory(sqlite)
endif (SQLITE3_FOUND)
+if (BUILD_CLIENT AND USE_FREETYPE)
+ add_subdirectory(cguittfont)
+endif (BUILD_CLIENT AND USE_FREETYPE)
+
if (LUA_FOUND)
else (LUA_FOUND)
add_subdirectory(lua)
endif (LUA_FOUND)
+if (JSON_FOUND)
+else (JSON_FOUND)
+ add_subdirectory(json)
+endif (JSON_FOUND)
+
#end
diff --git a/src/biome.cpp b/src/biome.cpp
new file mode 100644
index 000000000..34d51839f
--- /dev/null
+++ b/src/biome.cpp
@@ -0,0 +1,233 @@
+/*
+Minetest-c55
+Copyright (C) 2010-2011 kwolekr, Ryan Kwolek <kwolekr2@cs.scranton.edu>
+
+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 "biome.h"
+#include "nodedef.h"
+#include "map.h" //for ManualMapVoxelManipulator
+#include "log.h"
+#include "main.h"
+
+#define BT_NONE 0
+#define BT_OCEAN 1
+#define BT_LAKE 2
+#define BT_SBEACH 3
+#define BT_GBEACH 4
+#define BT_PLAINS 5
+#define BT_HILLS 6
+#define BT_EXTREMEHILLS 7
+#define BT_MOUNTAINS 8
+#define BT_DESERT 9
+#define BT_DESERTHILLS 10
+#define BT_HELL 11
+#define BT_AETHER 12
+
+#define BT_BTMASK 0x3F
+
+#define BTF_SNOW 0x40
+#define BTF_FOREST 0x80
+
+#define BGFREQ_1 ( 0.40)
+#define BGFREQ_2 (BGFREQ_1 + 0.05)
+#define BGFREQ_3 (BGFREQ_2 + 0.08)
+#define BGFREQ_4 (BGFREQ_3 + 0.35)
+#define BGFREQ_5 (BGFREQ_4 + 0.18)
+//BGFREQ_5 is not checked as an upper bound; it ought to sum up to 1.00, but it's okay if it doesn't.
+
+
+/*float bg1_temps[] = {0.0};
+int bg1_biomes[] = {BT_OCEAN};
+
+float bg2_temps[] = {10.0};
+int bg2_biomes[] = {BT_GBEACH, BT_SBEACH};
+
+float bg3_temps[] = {30.0, 40.0};
+int bg3_biomes[] = {BT_HILLS, BT_EXTREMEHILLS, BT_MOUNTAINS};
+
+float bg4_temps[] = {25.0, 30.0, 35.0, 40.0};
+int bg4_biomes[] = {BT_HILLS, BT_EXTREMEHILLS, BT_MOUNTAINS, BT_DESERT, BT_DESERTHILLS};
+
+float bg5_temps[] = {5.0, 40.0};
+int bg5_biomes[] = {BT_LAKE, BT_PLAINS, BT_DESERT};*/
+
+NoiseParams np_default = {20.0, 15.0, v3f(250., 250., 250.), 82341, 5, 0.6};
+
+
+BiomeDefManager::BiomeDefManager(IGameDef *gamedef) {
+ this->m_gamedef = gamedef;
+ this->ndef = gamedef->ndef();
+
+ //the initial biome group
+ bgroups.push_back(new std::vector<Biome *>);
+}
+
+
+BiomeDefManager::~BiomeDefManager() {
+ for (unsigned int i = 0; i != bgroups.size(); i++)
+ delete bgroups[i];
+}
+
+
+Biome *BiomeDefManager::createBiome(BiomeTerrainType btt) {
+ switch (btt) {
+ case BIOME_TERRAIN_NORMAL:
+ return new Biome;
+ case BIOME_TERRAIN_LIQUID:
+ return new BiomeLiquid;
+ case BIOME_TERRAIN_NETHER:
+ return new BiomeHell;
+ case BIOME_TERRAIN_AETHER:
+ return new BiomeAether;
+ case BIOME_TERRAIN_FLAT:
+ return new BiomeSuperflat;
+ }
+ return NULL;
+}
+
+
+void BiomeDefManager::addBiomeGroup(float freq) {
+ int size = bgroup_freqs.size();
+ float newfreq = freq;
+
+ if (size)
+ newfreq += bgroup_freqs[size - 1];
+ bgroup_freqs.push_back(newfreq);
+ bgroups.push_back(new std::vector<Biome *>);
+
+ verbosestream << "BiomeDefManager: added biome group with frequency " <<
+ newfreq << std::endl;
+}
+
+
+void BiomeDefManager::addBiome(Biome *b) {
+ std::vector<Biome *> *bgroup;
+
+ if ((unsigned int)b->groupid >= bgroups.size()) {
+ errorstream << "BiomeDefManager: attempted to add biome '" << b->name
+ << "' to nonexistent biome group " << b->groupid << std::endl;
+ return;
+ }
+
+ bgroup = bgroups[b->groupid];
+ bgroup->push_back(b);
+
+ verbosestream << "BiomeDefManager: added biome '" << b->name <<
+ "' to biome group " << (int)b->groupid << std::endl;
+}
+
+
+void BiomeDefManager::addDefaultBiomes() {
+ Biome *b;
+
+ b = new Biome;
+ b->name = "Default";
+ b->n_top = MapNode(ndef->getId("mapgen_stone"));
+ b->n_filler = b->n_top;
+ b->ntopnodes = 0;
+ b->height_min = -MAP_GENERATION_LIMIT;
+ b->height_max = MAP_GENERATION_LIMIT;
+ b->heat_min = FLT_MIN;
+ b->heat_max = FLT_MAX;
+ b->humidity_min = FLT_MIN;
+ b->humidity_max = FLT_MAX;
+ b->np = &np_default;
+ biome_default = b;
+}
+
+
+Biome *BiomeDefManager::getBiome(float bgfreq, float heat, float humidity) {
+ std::vector<Biome *> *bgroup;
+ Biome *b;
+ int i;
+
+ int ngroups = bgroup_freqs.size();
+ if (!ngroups)
+ return biome_default;
+ for (i = 0; (i != ngroups) && (bgfreq > bgroup_freqs[i]); i++);
+ bgroup = bgroups[i];
+
+ int nbiomes = bgroup->size();
+ for (i = 0; i != nbiomes; i++) {
+ b = bgroup->operator[](i);
+ if (heat >= b->heat_min && heat <= b->heat_max &&
+ humidity >= b->humidity_min && humidity <= b->humidity_max)
+ return b;
+ }
+
+ return biome_default;
+}
+
+
+//////////////////////////// [ Generic biome ] ////////////////////////////////
+
+
+int Biome::getSurfaceHeight(float noise_terrain) {
+ return np->offset + np->scale * noise_terrain;
+}
+
+
+void Biome::genColumn(Mapgen *mapgen, int x, int z, int y1, int y2) {
+
+}
+
+
+///////////////////////////// [ Ocean biome ] /////////////////////////////////
+
+
+void BiomeLiquid::genColumn(Mapgen *mapgen, int x, int z, int y1, int y2) {
+
+}
+
+
+///////////////////////////// [ Nether biome ] /////////////////////////////////
+
+
+int BiomeHell::getSurfaceHeight(float noise_terrain) {
+ return np->offset + np->scale * noise_terrain;
+}
+
+
+void BiomeHell::genColumn(Mapgen *mapgen, int x, int z, int y1, int y2) {
+
+}
+
+
+///////////////////////////// [ Aether biome ] ////////////////////////////////
+
+
+int BiomeAether::getSurfaceHeight(float noise_terrain) {
+ return np->offset + np->scale * noise_terrain;
+}
+
+
+void BiomeAether::genColumn(Mapgen *mapgen, int x, int z, int y1, int y2) {
+
+}
+
+
+/////////////////////////// [ Superflat biome ] ///////////////////////////////
+
+
+int BiomeSuperflat::getSurfaceHeight(float noise_terrain) {
+ return ntopnodes;
+}
+
+
+void BiomeSuperflat::genColumn(Mapgen *mapgen, int x, int z, int y1, int y2) {
+
+}
diff --git a/src/biome.h b/src/biome.h
new file mode 100644
index 000000000..265f1df44
--- /dev/null
+++ b/src/biome.h
@@ -0,0 +1,98 @@
+/*
+Minetest-c55
+Copyright (C) 2010-2011 kwolekr, Ryan Kwolek <kwolekr2@cs.scranton.edu>
+
+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 BIOME_HEADER
+#define BIOME_HEADER
+
+#include <string>
+#include "nodedef.h"
+#include "gamedef.h"
+#include "mapnode.h"
+#include "noise.h"
+#include "mapgen.h"
+
+
+enum BiomeTerrainType
+{
+ BIOME_TERRAIN_NORMAL,
+ BIOME_TERRAIN_LIQUID,
+ BIOME_TERRAIN_NETHER,
+ BIOME_TERRAIN_AETHER,
+ BIOME_TERRAIN_FLAT
+};
+
+class Biome {
+public:
+ MapNode n_top;
+ MapNode n_filler;
+ s16 ntopnodes;
+ s8 groupid;
+ s8 flags;
+ s16 height_min;
+ s16 height_max;
+ float heat_min;
+ float heat_max;
+ float humidity_min;
+ float humidity_max;
+ std::string name;
+ NoiseParams *np;
+
+ virtual void genColumn(Mapgen *mg, int x, int z, int y1, int y2);
+ virtual int getSurfaceHeight(float noise_terrain);
+};
+
+class BiomeLiquid : public Biome {
+ virtual void genColumn(Mapgen *mg, int x, int z, int y1, int y2);
+};
+
+class BiomeHell : public Biome {
+ virtual void genColumn(Mapgen *mg, int x, int z, int y1, int y2);
+ virtual int getSurfaceHeight(float noise_terrain);
+};
+
+class BiomeAether : public Biome {
+ virtual void genColumn(Mapgen *mg, int x, int z, int y1, int y2);
+ virtual int getSurfaceHeight(float noise_terrain);
+};
+
+class BiomeSuperflat : public Biome {
+ virtual void genColumn(Mapgen *mg, int x, int z, int y1, int y2);
+ virtual int getSurfaceHeight(float noise_terrain);
+};
+
+class BiomeDefManager {
+public:
+ std::vector<float> bgroup_freqs;
+ std::vector<std::vector<Biome *> *> bgroups;
+ Biome *biome_default;
+ IGameDef *m_gamedef;
+ INodeDefManager *ndef;
+
+ BiomeDefManager(IGameDef *gamedef);
+ ~BiomeDefManager();
+
+ Biome *createBiome(BiomeTerrainType btt);
+ Biome *getBiome(float bgfreq, float heat, float humidity);
+
+ void addBiomeGroup(float freq);
+ void addBiome(Biome *b);
+ void addDefaultBiomes();
+};
+
+#endif
diff --git a/src/cguittfont/CGUITTFont.cpp b/src/cguittfont/CGUITTFont.cpp
new file mode 100644
index 000000000..fb8199e21
--- /dev/null
+++ b/src/cguittfont/CGUITTFont.cpp
@@ -0,0 +1,1146 @@
+/*
+ CGUITTFont FreeType class for Irrlicht
+ Copyright (c) 2009-2010 John Norman
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any
+ damages arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any
+ purpose, including commercial applications, and to alter it and
+ redistribute it freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you
+ must not claim that you wrote the original software. If you use
+ this software in a product, an acknowledgment in the product
+ documentation would be appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and
+ must not be misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+
+ The original version of this class can be located at:
+ http://irrlicht.suckerfreegames.com/
+
+ John Norman
+ john@suckerfreegames.com
+*/
+
+#include <irrlicht.h>
+#include "CGUITTFont.h"
+
+namespace irr
+{
+namespace gui
+{
+
+// Manages the FT_Face cache.
+struct SGUITTFace : public virtual irr::IReferenceCounted
+{
+ SGUITTFace() : face_buffer(0), face_buffer_size(0)
+ {
+ memset((void*)&face, 0, sizeof(FT_Face));
+ }
+
+ ~SGUITTFace()
+ {
+ FT_Done_Face(face);
+ delete[] face_buffer;
+ }
+
+ FT_Face face;
+ FT_Byte* face_buffer;
+ FT_Long face_buffer_size;
+};
+
+// Static variables.
+FT_Library CGUITTFont::c_library;
+core::map<io::path, SGUITTFace*> CGUITTFont::c_faces;
+bool CGUITTFont::c_libraryLoaded = false;
+scene::IMesh* CGUITTFont::shared_plane_ptr_ = 0;
+scene::SMesh CGUITTFont::shared_plane_;
+
+//
+
+video::IImage* SGUITTGlyph::createGlyphImage(const FT_Bitmap& bits, video::IVideoDriver* driver) const
+{
+ // Determine what our texture size should be.
+ // Add 1 because textures are inclusive-exclusive.
+ core::dimension2du d(bits.width + 1, bits.rows + 1);
+ core::dimension2du texture_size;
+ //core::dimension2du texture_size(bits.width + 1, bits.rows + 1);
+
+ // Create and load our image now.
+ video::IImage* image = 0;
+ switch (bits.pixel_mode)
+ {
+ case FT_PIXEL_MODE_MONO:
+ {
+ // Create a blank image and fill it with transparent pixels.
+ texture_size = d.getOptimalSize(true, true);
+ image = driver->createImage(video::ECF_A1R5G5B5, texture_size);
+ image->fill(video::SColor(0, 255, 255, 255));
+
+ // Load the monochrome data in.
+ const u32 image_pitch = image->getPitch() / sizeof(u16);
+ u16* image_data = (u16*)image->lock();
+ u8* glyph_data = bits.buffer;
+ for (s32 y = 0; y < bits.rows; ++y)
+ {
+ u16* row = image_data;
+ for (s32 x = 0; x < bits.width; ++x)
+ {
+ // Monochrome bitmaps store 8 pixels per byte. The left-most pixel is the bit 0x80.
+ // So, we go through the data each bit at a time.
+ if ((glyph_data[y * bits.pitch + (x / 8)] & (0x80 >> (x % 8))) != 0)
+ *row = 0xFFFF;
+ ++row;
+ }
+ image_data += image_pitch;
+ }
+ image->unlock();
+ break;
+ }
+
+ case FT_PIXEL_MODE_GRAY:
+ {
+ // Create our blank image.
+ texture_size = d.getOptimalSize(!driver->queryFeature(video::EVDF_TEXTURE_NPOT), !driver->queryFeature(video::EVDF_TEXTURE_NSQUARE), true, 0);
+ image = driver->createImage(video::ECF_A8R8G8B8, texture_size);
+ image->fill(video::SColor(0, 255, 255, 255));
+
+ // Load the grayscale data in.
+ const float gray_count = static_cast<float>(bits.num_grays);
+ const u32 image_pitch = image->getPitch() / sizeof(u32);
+ u32* image_data = (u32*)image->lock();
+ u8* glyph_data = bits.buffer;
+ for (s32 y = 0; y < bits.rows; ++y)
+ {
+ u8* row = glyph_data;
+ for (s32 x = 0; x < bits.width; ++x)
+ {
+ image_data[y * image_pitch + x] |= static_cast<u32>(255.0f * (static_cast<float>(*row++) / gray_count)) << 24;
+ //data[y * image_pitch + x] |= ((u32)(*bitsdata++) << 24);
+ }
+ glyph_data += bits.pitch;
+ }
+ image->unlock();
+ break;
+ }
+ default:
+ // TODO: error message?
+ return 0;
+ }
+ return image;
+}
+
+void SGUITTGlyph::preload(u32 char_index, FT_Face face, video::IVideoDriver* driver, u32 font_size, const FT_Int32 loadFlags)
+{
+ if (isLoaded) return;
+
+ // Set the size of the glyph.
+ FT_Set_Pixel_Sizes(face, 0, font_size);
+
+ // Attempt to load the glyph.
+ if (FT_Load_Glyph(face, char_index, loadFlags) != FT_Err_Ok)
+ // TODO: error message?
+ return;
+
+ FT_GlyphSlot glyph = face->glyph;
+ FT_Bitmap bits = glyph->bitmap;
+
+ // Setup the glyph information here:
+ advance = glyph->advance;
+ offset = core::vector2di(glyph->bitmap_left, glyph->bitmap_top);
+
+ // Try to get the last page with available slots.
+ CGUITTGlyphPage* page = parent->getLastGlyphPage();
+
+ // If we need to make a new page, do that now.
+ if (!page)
+ {
+ page = parent->createGlyphPage(bits.pixel_mode);
+ if (!page)
+ // TODO: add error message?
+ return;
+ }
+
+ glyph_page = parent->getLastGlyphPageIndex();
+ u32 texture_side_length = page->texture->getOriginalSize().Width;
+ core::vector2di page_position(
+ (page->used_slots % (texture_side_length / font_size)) * font_size,
+ (page->used_slots / (texture_side_length / font_size)) * font_size
+ );
+ source_rect.UpperLeftCorner = page_position;
+ source_rect.LowerRightCorner = core::vector2di(page_position.X + bits.width, page_position.Y + bits.rows);
+
+ page->dirty = true;
+ ++page->used_slots;
+ --page->available_slots;
+
+ // We grab the glyph bitmap here so the data won't be removed when the next glyph is loaded.
+ surface = createGlyphImage(bits, driver);
+
+ // Set our glyph as loaded.
+ isLoaded = true;
+}
+
+void SGUITTGlyph::unload()
+{
+ if (surface)
+ {
+ surface->drop();
+ surface = 0;
+ }
+ isLoaded = false;
+}
+
+//////////////////////
+
+CGUITTFont* CGUITTFont::createTTFont(IGUIEnvironment *env, const io::path& filename, const u32 size, const bool antialias, const bool transparency)
+{
+ if (!c_libraryLoaded)
+ {
+ if (FT_Init_FreeType(&c_library))
+ return 0;
+ c_libraryLoaded = true;
+ }
+
+ CGUITTFont* font = new CGUITTFont(env);
+ bool ret = font->load(filename, size, antialias, transparency);
+ if (!ret)
+ {
+ font->drop();
+ return 0;
+ }
+
+ return font;
+}
+
+CGUITTFont* CGUITTFont::createTTFont(IrrlichtDevice *device, const io::path& filename, const u32 size, const bool antialias, const bool transparency)
+{
+ if (!c_libraryLoaded)
+ {
+ if (FT_Init_FreeType(&c_library))
+ return 0;
+ c_libraryLoaded = true;
+ }
+
+ CGUITTFont* font = new CGUITTFont(device->getGUIEnvironment());
+ font->Device = device;
+ bool ret = font->load(filename, size, antialias, transparency);
+ if (!ret)
+ {
+ font->drop();
+ return 0;
+ }
+
+ return font;
+}
+
+CGUITTFont* CGUITTFont::create(IGUIEnvironment *env, const io::path& filename, const u32 size, const bool antialias, const bool transparency)
+{
+ return CGUITTFont::createTTFont(env, filename, size, antialias, transparency);
+}
+
+CGUITTFont* CGUITTFont::create(IrrlichtDevice *device, const io::path& filename, const u32 size, const bool antialias, const bool transparency)
+{
+ return CGUITTFont::createTTFont(device, filename, size, antialias, transparency);
+}
+
+//////////////////////
+
+//! Constructor.
+CGUITTFont::CGUITTFont(IGUIEnvironment *env)
+: use_monochrome(false), use_transparency(true), use_hinting(true), use_auto_hinting(true),
+batch_load_size(1), Device(0), Environment(env), Driver(0), GlobalKerningWidth(0), GlobalKerningHeight(0)
+{
+ #ifdef _DEBUG
+ setDebugName("CGUITTFont");
+ #endif
+
+ if (Environment)
+ {
+ // don't grab environment, to avoid circular references
+ Driver = Environment->getVideoDriver();
+ }
+
+ if (Driver)
+ Driver->grab();
+
+ setInvisibleCharacters(L" ");
+
+ // Glyphs aren't reference counted, so don't try to delete them when we free the array.
+ Glyphs.set_free_when_destroyed(false);
+}
+
+bool CGUITTFont::load(const io::path& filename, const u32 size, const bool antialias, const bool transparency)
+{
+ // Some sanity checks.
+ if (Environment == 0 || Driver == 0) return false;
+ if (size == 0) return false;
+ if (filename.size() == 0) return false;
+
+ io::IFileSystem* filesystem = Environment->getFileSystem();
+ irr::ILogger* logger = (Device != 0 ? Device->getLogger() : 0);
+ this->size = size;
+ this->filename = filename;
+
+ // Update the font loading flags when the font is first loaded.
+ this->use_monochrome = !antialias;
+ this->use_transparency = transparency;
+ update_load_flags();
+
+ // Log.
+ if (logger)
+ logger->log(L"CGUITTFont", core::stringw(core::stringw(L"Creating new font: ") + core::ustring(filename).toWCHAR_s() + L" " + core::stringc(size) + L"pt " + (antialias ? L"+antialias " : L"-antialias ") + (transparency ? L"+transparency" : L"-transparency")).c_str(), irr::ELL_INFORMATION);
+
+ // Grab the face.
+ SGUITTFace* face = 0;
+ core::map<io::path, SGUITTFace*>::Node* node = c_faces.find(filename);
+ if (node == 0)
+ {
+ face = new SGUITTFace();
+ c_faces.set(filename, face);
+
+ if (filesystem)
+ {
+ // Read in the file data.
+ io::IReadFile* file = filesystem->createAndOpenFile(filename);
+ if (file == 0)
+ {
+ if (logger) logger->log(L"CGUITTFont", L"Failed to open the file.", irr::ELL_INFORMATION);
+
+ c_faces.remove(filename);
+ delete face;
+ face = 0;
+ return false;
+ }
+ face->face_buffer = new FT_Byte[file->getSize()];
+ file->read(face->face_buffer, file->getSize());
+ face->face_buffer_size = file->getSize();
+ file->drop();
+
+ // Create the face.
+ if (FT_New_Memory_Face(c_library, face->face_buffer, face->face_buffer_size, 0, &face->face))
+ {
+ if (logger) logger->log(L"CGUITTFont", L"FT_New_Memory_Face failed.", irr::ELL_INFORMATION);
+
+ c_faces.remove(filename);
+ delete face;
+ face = 0;
+ return false;
+ }
+ }
+ else
+ {
+ core::ustring converter(filename);
+ if (FT_New_Face(c_library, reinterpret_cast<const char*>(converter.toUTF8_s().c_str()), 0, &face->face))
+ {
+ if (logger) logger->log(L"CGUITTFont", L"FT_New_Face failed.", irr::ELL_INFORMATION);
+
+ c_faces.remove(filename);
+ delete face;
+ face = 0;
+ return false;
+ }
+ }
+ }
+ else
+ {
+ // Using another instance of this face.
+ face = node->getValue();
+ face->grab();
+ }
+
+ // Store our face.
+ tt_face = face->face;
+
+ // Store font metrics.
+ FT_Set_Pixel_Sizes(tt_face, size, 0);
+ font_metrics = tt_face->size->metrics;
+
+ // Allocate our glyphs.
+ Glyphs.clear();
+ Glyphs.reallocate(tt_face->num_glyphs);
+ Glyphs.set_used(tt_face->num_glyphs);
+ for (FT_Long i = 0; i < tt_face->num_glyphs; ++i)
+ {
+ Glyphs[i].isLoaded = false;
+ Glyphs[i].glyph_page = 0;
+ Glyphs[i].source_rect = core::recti();
+ Glyphs[i].offset = core::vector2di();
+ Glyphs[i].advance = FT_Vector();
+ Glyphs[i].surface = 0;
+ Glyphs[i].parent = this;
+ }
+
+ // Cache the first 127 ascii characters.
+ u32 old_size = batch_load_size;
+ batch_load_size = 127;
+ getGlyphIndexByChar((uchar32_t)0);
+ batch_load_size = old_size;
+
+ return true;
+}
+
+CGUITTFont::~CGUITTFont()
+{
+ // Delete the glyphs and glyph pages.
+ reset_images();
+ CGUITTAssistDelete::Delete(Glyphs);
+ //Glyphs.clear();
+
+ // We aren't using this face anymore.
+ core::map<io::path, SGUITTFace*>::Node* n = c_faces.find(filename);
+ if (n)
+ {
+ SGUITTFace* f = n->getValue();
+
+ // Drop our face. If this was the last face, the destructor will clean up.
+ if (f->drop())
+ c_faces.remove(filename);
+
+ // If there are no more faces referenced by FreeType, clean up.
+ if (c_faces.size() == 0)
+ {
+ FT_Done_FreeType(c_library);
+ c_libraryLoaded = false;
+ }
+ }
+
+ // Drop our driver now.
+ if (Driver)
+ Driver->drop();
+}
+
+void CGUITTFont::reset_images()
+{
+ // Delete the glyphs.
+ for (u32 i = 0; i != Glyphs.size(); ++i)
+ Glyphs[i].unload();
+
+ // Unload the glyph pages from video memory.
+ for (u32 i = 0; i != Glyph_Pages.size(); ++i)
+ delete Glyph_Pages[i];
+ Glyph_Pages.clear();
+
+ // Always update the internal FreeType loading flags after resetting.
+ update_load_flags();
+}
+
+void CGUITTFont::update_glyph_pages() const
+{
+ for (u32 i = 0; i != Glyph_Pages.size(); ++i)
+ {
+ if (Glyph_Pages[i]->dirty)
+ Glyph_Pages[i]->updateTexture();
+ }
+}
+
+CGUITTGlyphPage* CGUITTFont::getLastGlyphPage() const
+{
+ CGUITTGlyphPage* page = 0;
+ if (Glyph_Pages.empty())
+ return 0;
+ else
+ {
+ page = Glyph_Pages[getLastGlyphPageIndex()];
+ if (page->available_slots == 0)
+ page = 0;
+ }
+ return page;
+}
+
+CGUITTGlyphPage* CGUITTFont::createGlyphPage(const u8& pixel_mode)
+{
+ CGUITTGlyphPage* page = 0;
+
+ // Name of our page.
+ io::path name("TTFontGlyphPage_");
+ name += tt_face->family_name;
+ name += ".";
+ name += tt_face->style_name;
+ name += ".";
+ name += size;
+ name += "_";
+ name += Glyph_Pages.size(); // The newly created page will be at the end of the collection.
+
+ // Create the new page.
+ page = new CGUITTGlyphPage(Driver, name);
+
+ // Determine our maximum texture size.
+ // If we keep getting 0, set it to 1024x1024, as that number is pretty safe.
+ core::dimension2du max_texture_size = max_page_texture_size;
+ if (max_texture_size.Width == 0 || max_texture_size.Height == 0)
+ max_texture_size = Driver->getMaxTextureSize();
+ if (max_texture_size.Width == 0 || max_texture_size.Height == 0)
+ max_texture_size = core::dimension2du(1024, 1024);
+
+ // We want to try to put at least 144 glyphs on a single texture.
+ core::dimension2du page_texture_size;
+ if (size <= 21) page_texture_size = core::dimension2du(256, 256);
+ else if (size <= 42) page_texture_size = core::dimension2du(512, 512);
+ else if (size <= 84) page_texture_size = core::dimension2du(1024, 1024);
+ else if (size <= 168) page_texture_size = core::dimension2du(2048, 2048);
+ else page_texture_size = core::dimension2du(4096, 4096);
+
+ if (page_texture_size.Width > max_texture_size.Width || page_texture_size.Height > max_texture_size.Height)
+ page_texture_size = max_texture_size;
+
+ if (!page->createPageTexture(pixel_mode, page_texture_size))
+ // TODO: add error message?
+ return 0;
+
+ if (page)
+ {
+ // Determine the number of glyph slots on the page and add it to the list of pages.
+ page->available_slots = (page_texture_size.Width / size) * (page_texture_size.Height / size);
+ Glyph_Pages.push_back(page);
+ }
+ return page;
+}
+
+void CGUITTFont::setTransparency(const bool flag)
+{
+ use_transparency = flag;
+ reset_images();
+}
+
+void CGUITTFont::setMonochrome(const bool flag)
+{
+ use_monochrome = flag;
+ reset_images();
+}
+
+void CGUITTFont::setFontHinting(const bool enable, const bool enable_auto_hinting)
+{
+ use_hinting = enable;
+ use_auto_hinting = enable_auto_hinting;
+ reset_images();
+}
+
+void CGUITTFont::draw(const core::stringw& text, const core::rect<s32>& position, video::SColor color, bool hcenter, bool vcenter, const core::rect<s32>* clip)
+{
+ if (!Driver)
+ return;
+
+ // Clear the glyph pages of their render information.
+ for (u32 i = 0; i < Glyph_Pages.size(); ++i)
+ {
+ Glyph_Pages[i]->render_positions.clear();
+ Glyph_Pages[i]->render_source_rects.clear();
+ }
+
+ // Set up some variables.
+ core::dimension2d<s32> textDimension;
+ core::position2d<s32> offset = position.UpperLeftCorner;
+
+ // Determine offset positions.
+ if (hcenter || vcenter)
+ {
+ textDimension = getDimension(text.c_str());
+
+ if (hcenter)
+ offset.X = ((position.getWidth() - textDimension.Width) >> 1) + offset.X;
+
+ if (vcenter)
+ offset.Y = ((position.getHeight() - textDimension.Height) >> 1) + offset.Y;
+ }
+
+ // Convert to a unicode string.
+ core::ustring utext(text);
+
+ // Set up our render map.
+ core::map<u32, CGUITTGlyphPage*> Render_Map;
+
+ // Start parsing characters.
+ u32 n;
+ uchar32_t previousChar = 0;
+ core::ustring::const_iterator iter(utext);
+ while (!iter.atEnd())
+ {
+ uchar32_t currentChar = *iter;
+ n = getGlyphIndexByChar(currentChar);
+ bool visible = (Invisible.findFirst(currentChar) == -1);
+ if (n > 0 && visible)
+ {
+ bool lineBreak=false;
+ if (currentChar == L'\r') // Mac or Windows breaks
+ {
+ lineBreak = true;
+ if (*(iter + 1) == (uchar32_t)'\n') // Windows line breaks.
+ currentChar = *(++iter);
+ }
+ else if (currentChar == (uchar32_t)'\n') // Unix breaks
+ {
+ lineBreak = true;
+ }
+
+ if (lineBreak)
+ {
+ previousChar = 0;
+ offset.Y += font_metrics.ascender / 64;
+ offset.X = position.UpperLeftCorner.X;
+
+ if (hcenter)
+ offset.X += (position.getWidth() - textDimension.Width) >> 1;
+ ++iter;
+ continue;
+ }
+
+ // Calculate the glyph offset.
+ s32 offx = Glyphs[n-1].offset.X;
+ s32 offy = (font_metrics.ascender / 64) - Glyphs[n-1].offset.Y;
+
+ // Apply kerning.
+ core::vector2di k = getKerning(currentChar, previousChar);
+ offset.X += k.X;
+ offset.Y += k.Y;
+
+ // Determine rendering information.
+ SGUITTGlyph& glyph = Glyphs[n-1];
+ CGUITTGlyphPage* const page = Glyph_Pages[glyph.glyph_page];
+ page->render_positions.push_back(core::position2di(offset.X + offx, offset.Y + offy));
+ page->render_source_rects.push_back(glyph.source_rect);
+ Render_Map.set(glyph.glyph_page, page);
+ }
+ offset.X += getWidthFromCharacter(currentChar);
+
+ previousChar = currentChar;
+ ++iter;
+ }
+
+ // Draw now.
+ update_glyph_pages();
+ core::map<u32, CGUITTGlyphPage*>::Iterator j = Render_Map.getIterator();
+ while (!j.atEnd())
+ {
+ core::map<u32, CGUITTGlyphPage*>::Node* n = j.getNode();
+ j++;
+ if (n == 0) continue;
+
+ CGUITTGlyphPage* page = n->getValue();
+
+ if (!use_transparency) color.color |= 0xff000000;
+ Driver->draw2DImageBatch(page->texture, page->render_positions, page->render_source_rects, clip, color, true);
+ }
+}
+
+core::dimension2d<u32> CGUITTFont::getCharDimension(const wchar_t ch) const
+{
+ return core::dimension2d<u32>(getWidthFromCharacter(ch), getHeightFromCharacter(ch));
+}
+
+core::dimension2d<u32> CGUITTFont::getDimension(const wchar_t* text) const
+{
+ return getDimension(core::ustring(text));
+}
+
+core::dimension2d<u32> CGUITTFont::getDimension(const core::ustring& text) const
+{
+ // Get the maximum font height. Unfortunately, we have to do this hack as
+ // Irrlicht will draw things wrong. In FreeType, the font size is the
+ // maximum size for a single glyph, but that glyph may hang "under" the
+ // draw line, increasing the total font height to beyond the set size.
+ // Irrlicht does not understand this concept when drawing fonts. Also, I
+ // add +1 to give it a 1 pixel blank border. This makes things like
+ // tooltips look nicer.
+ s32 test1 = getHeightFromCharacter((uchar32_t)'g') + 1;
+ s32 test2 = getHeightFromCharacter((uchar32_t)'j') + 1;
+ s32 test3 = getHeightFromCharacter((uchar32_t)'_') + 1;
+ s32 max_font_height = core::max_(test1, core::max_(test2, test3));
+
+ core::dimension2d<u32> text_dimension(0, max_font_height);
+ core::dimension2d<u32> line(0, max_font_height);
+
+ uchar32_t previousChar = 0;
+ core::ustring::const_iterator iter = text.begin();
+ for (; !iter.atEnd(); ++iter)
+ {
+ uchar32_t p = *iter;
+ bool lineBreak = false;
+ if (p == '\r') // Mac or Windows line breaks.
+ {
+ lineBreak = true;
+ if (*(iter + 1) == '\n')
+ {
+ ++iter;
+ p = *iter;
+ }
+ }
+ else if (p == '\n') // Unix line breaks.
+ {
+ lineBreak = true;
+ }
+
+ // Kerning.
+ core::vector2di k = getKerning(p, previousChar);
+ line.Width += k.X;
+ previousChar = p;
+
+ // Check for linebreak.
+ if (lineBreak)
+ {
+ previousChar = 0;
+ text_dimension.Height += line.Height;
+ if (text_dimension.Width < line.Width)
+ text_dimension.Width = line.Width;
+ line.Width = 0;
+ line.Height = max_font_height;
+ continue;
+ }
+ line.Width += getWidthFromCharacter(p);
+ }
+ if (text_dimension.Width < line.Width)
+ text_dimension.Width = line.Width;
+
+ return text_dimension;
+}
+
+inline u32 CGUITTFont::getWidthFromCharacter(wchar_t c) const
+{
+ return getWidthFromCharacter((uchar32_t)c);
+}
+
+inline u32 CGUITTFont::getWidthFromCharacter(uchar32_t c) const
+{
+ // Set the size of the face.
+ // This is because we cache faces and the face may have been set to a different size.
+ //FT_Set_Pixel_Sizes(tt_face, 0, size);
+
+ u32 n = getGlyphIndexByChar(c);
+ if (n > 0)
+ {
+ int w = Glyphs[n-1].advance.x / 64;
+ return w;
+ }
+ if (c >= 0x2000)
+ return (font_metrics.ascender / 64);
+ else return (font_metrics.ascender / 64) / 2;
+}
+
+inline u32 CGUITTFont::getHeightFromCharacter(wchar_t c) const
+{
+ return getHeightFromCharacter((uchar32_t)c);
+}
+
+inline u32 CGUITTFont::getHeightFromCharacter(uchar32_t c) const
+{
+ // Set the size of the face.
+ // This is because we cache faces and the face may have been set to a different size.
+ //FT_Set_Pixel_Sizes(tt_face, 0, size);
+
+ u32 n = getGlyphIndexByChar(c);
+ if (n > 0)
+ {
+ // Grab the true height of the character, taking into account underhanging glyphs.
+ s32 height = (font_metrics.ascender / 64) - Glyphs[n-1].offset.Y + Glyphs[n-1].source_rect.getHeight();
+ return height;
+ }
+ if (c >= 0x2000)
+ return (font_metrics.ascender / 64);
+ else return (font_metrics.ascender / 64) / 2;
+}
+
+u32 CGUITTFont::getGlyphIndexByChar(wchar_t c) const
+{
+ return getGlyphIndexByChar((uchar32_t)c);
+}
+
+u32 CGUITTFont::getGlyphIndexByChar(uchar32_t c) const
+{
+ // Get the glyph.
+ u32 glyph = FT_Get_Char_Index(tt_face, c);
+
+ // Check for a valid glyph. If it is invalid, attempt to use the replacement character.
+ if (glyph == 0)
+ glyph = FT_Get_Char_Index(tt_face, core::unicode::UTF_REPLACEMENT_CHARACTER);
+
+ // If our glyph is already loaded, don't bother doing any batch loading code.
+ if (glyph != 0 && Glyphs[glyph - 1].isLoaded)
+ return glyph;
+
+ // Determine our batch loading positions.
+ u32 half_size = (batch_load_size / 2);
+ u32 start_pos = 0;
+ if (c > half_size) start_pos = c - half_size;
+ u32 end_pos = start_pos + batch_load_size;
+
+ // Load all our characters.
+ do
+ {
+ // Get the character we are going to load.
+ u32 char_index = FT_Get_Char_Index(tt_face, start_pos);
+
+ // If the glyph hasn't been loaded yet, do it now.
+ if (char_index)
+ {
+ SGUITTGlyph& glyph = Glyphs[char_index - 1];
+ if (!glyph.isLoaded)
+ {
+ glyph.preload(char_index, tt_face, Driver, size, load_flags);
+ Glyph_Pages[glyph.glyph_page]->pushGlyphToBePaged(&glyph);
+ }
+ }
+ }
+ while (++start_pos < end_pos);
+
+ // Return our original character.
+ return glyph;
+}
+
+s32 CGUITTFont::getCharacterFromPos(const wchar_t* text, s32 pixel_x) const
+{
+ return getCharacterFromPos(core::ustring(text), pixel_x);
+}
+
+s32 CGUITTFont::getCharacterFromPos(const core::ustring& text, s32 pixel_x) const
+{
+ s32 x = 0;
+ //s32 idx = 0;
+
+ u32 character = 0;
+ uchar32_t previousChar = 0;
+ core::ustring::const_iterator iter = text.begin();
+ while (!iter.atEnd())
+ {
+ uchar32_t c = *iter;
+ x += getWidthFromCharacter(c);
+
+ // Kerning.
+ core::vector2di k = getKerning(c, previousChar);
+ x += k.X;
+
+ if (x >= pixel_x)
+ return character;
+
+ previousChar = c;
+ ++iter;
+ ++character;
+ }
+
+ return -1;
+}
+
+void CGUITTFont::setKerningWidth(s32 kerning)
+{
+ GlobalKerningWidth = kerning;
+}
+
+void CGUITTFont::setKerningHeight(s32 kerning)
+{
+ GlobalKerningHeight = kerning;
+}
+
+s32 CGUITTFont::getKerningWidth(const wchar_t* thisLetter, const wchar_t* previousLetter) const
+{
+ if (tt_face == 0)
+ return GlobalKerningWidth;
+ if (thisLetter == 0 || previousLetter == 0)
+ return 0;
+
+ return getKerningWidth((uchar32_t)*thisLetter, (uchar32_t)*previousLetter);
+}
+
+s32 CGUITTFont::getKerningWidth(const uchar32_t thisLetter, const uchar32_t previousLetter) const
+{
+ // Return only the kerning width.
+ return getKerning(thisLetter, previousLetter).X;
+}
+
+s32 CGUITTFont::getKerningHeight() const
+{
+ // FreeType 2 currently doesn't return any height kerning information.
+ return GlobalKerningHeight;
+}
+
+core::vector2di CGUITTFont::getKerning(const wchar_t thisLetter, const wchar_t previousLetter) const
+{
+ return getKerning((uchar32_t)thisLetter, (uchar32_t)previousLetter);
+}
+
+core::vector2di CGUITTFont::getKerning(const uchar32_t thisLetter, const uchar32_t previousLetter) const
+{
+ if (tt_face == 0 || thisLetter == 0 || previousLetter == 0)
+ return core::vector2di();
+
+ // Set the size of the face.
+ // This is because we cache faces and the face may have been set to a different size.
+ FT_Set_Pixel_Sizes(tt_face, 0, size);
+
+ core::vector2di ret(GlobalKerningWidth, GlobalKerningHeight);
+
+ // If we don't have kerning, no point in continuing.
+ if (!FT_HAS_KERNING(tt_face))
+ return ret;
+
+ // Get the kerning information.
+ FT_Vector v;
+ FT_Get_Kerning(tt_face, getGlyphIndexByChar(previousLetter), getGlyphIndexByChar(thisLetter), FT_KERNING_DEFAULT, &v);
+
+ // If we have a scalable font, the return value will be in font points.
+ if (FT_IS_SCALABLE(tt_face))
+ {
+ // Font points, so divide by 64.
+ ret.X += (v.x / 64);
+ ret.Y += (v.y / 64);
+ }
+ else
+ {
+ // Pixel units.
+ ret.X += v.x;
+ ret.Y += v.y;
+ }
+ return ret;
+}
+
+void CGUITTFont::setInvisibleCharacters(const wchar_t *s)
+{
+ core::ustring us(s);
+ Invisible = us;
+}
+
+void CGUITTFont::setInvisibleCharacters(const core::ustring& s)
+{
+ Invisible = s;
+}
+
+video::IImage* CGUITTFont::createTextureFromChar(const uchar32_t& ch)
+{
+ u32 n = getGlyphIndexByChar(ch);
+ const SGUITTGlyph& glyph = Glyphs[n-1];
+ CGUITTGlyphPage* page = Glyph_Pages[glyph.glyph_page];
+
+ if (page->dirty)
+ page->updateTexture();
+
+ video::ITexture* tex = page->texture;
+
+ // Acquire a read-only lock of the corresponding page texture.
+ #if IRRLICHT_VERSION_MAJOR==1 && IRRLICHT_VERSION_MINOR>=8
+ void* ptr = tex->lock(video::ETLM_READ_ONLY);
+ #else
+ void* ptr = tex->lock(true);
+ #endif
+
+ video::ECOLOR_FORMAT format = tex->getColorFormat();
+ core::dimension2du tex_size = tex->getOriginalSize();
+ video::IImage* pageholder = Driver->createImageFromData(format, tex_size, ptr, true, false);
+
+ // Copy the image data out of the page texture.
+ core::dimension2du glyph_size(glyph.source_rect.getSize());
+ video::IImage* image = Driver->createImage(format, glyph_size);
+ pageholder->copyTo(image, core::position2di(0, 0), glyph.source_rect);
+
+ tex->unlock();
+ return image;
+}
+
+video::ITexture* CGUITTFont::getPageTextureByIndex(const u32& page_index) const
+{
+ if (page_index < Glyph_Pages.size())
+ return Glyph_Pages[page_index]->texture;
+ else
+ return 0;
+}
+
+void CGUITTFont::createSharedPlane()
+{
+ /*
+ 2___3
+ | /|
+ | / | <-- plane mesh is like this, point 2 is (0,0), point 0 is (0, -1)
+ |/ | <-- the texture coords of point 2 is (0,0, point 0 is (0, 1)
+ 0---1
+ */
+
+ using namespace core;
+ using namespace video;
+ using namespace scene;
+ S3DVertex vertices[4];
+ u16 indices[6] = {0,2,3,3,1,0};
+ vertices[0] = S3DVertex(vector3df(0,-1,0), vector3df(0,0,-1), SColor(255,255,255,255), vector2df(0,1));
+ vertices[1] = S3DVertex(vector3df(1,-1,0), vector3df(0,0,-1), SColor(255,255,255,255), vector2df(1,1));
+ vertices[2] = S3DVertex(vector3df(0, 0,0), vector3df(0,0,-1), SColor(255,255,255,255), vector2df(0,0));
+ vertices[3] = S3DVertex(vector3df(1, 0,0), vector3df(0,0,-1), SColor(255,255,255,255), vector2df(1,0));
+
+ SMeshBuffer* buf = new SMeshBuffer();
+ buf->append(vertices, 4, indices, 6);
+
+ shared_plane_.addMeshBuffer( buf );
+
+ shared_plane_ptr_ = &shared_plane_;
+ buf->drop(); //the addMeshBuffer method will grab it, so we can drop this ptr.
+}
+
+core::dimension2d<u32> CGUITTFont::getDimensionUntilEndOfLine(const wchar_t* p) const
+{
+ core::stringw s;
+ for (const wchar_t* temp = p; temp && *temp != '\0' && *temp != L'\r' && *temp != L'\n'; ++temp )
+ s.append(*temp);
+
+ return getDimension(s.c_str());
+}
+
+core::array<scene::ISceneNode*> CGUITTFont::addTextSceneNode(const wchar_t* text, scene::ISceneManager* smgr, scene::ISceneNode* parent, const video::SColor& color, bool center)
+{
+ using namespace core;
+ using namespace video;
+ using namespace scene;
+
+ array<scene::ISceneNode*> container;
+
+ if (!Driver || !smgr) return container;
+ if (!parent)
+ parent = smgr->addEmptySceneNode(smgr->getRootSceneNode(), -1);
+ // if you don't specify parent, then we add a empty node attached to the root node
+ // this is generally undesirable.
+
+ if (!shared_plane_ptr_) //this points to a static mesh that contains the plane
+ createSharedPlane(); //if it's not initialized, we create one.
+
+ dimension2d<s32> text_size(getDimension(text)); //convert from unsigned to signed.
+ vector3df start_point(0, 0, 0), offset;
+
+ /** NOTICE:
+ Because we are considering adding texts into 3D world, all Y axis vectors are inverted.
+ **/
+
+ // There's currently no "vertical center" concept when you apply text scene node to the 3D world.
+ if (center)
+ {
+ offset.X = start_point.X = -text_size.Width / 2.f;
+ offset.Y = start_point.Y = +text_size.Height/ 2.f;
+ offset.X += (text_size.Width - getDimensionUntilEndOfLine(text).Width) >> 1;
+ }
+
+ // the default font material
+ SMaterial mat;
+ mat.setFlag(video::EMF_LIGHTING, true);
+ mat.setFlag(video::EMF_ZWRITE_ENABLE, false);
+ mat.setFlag(video::EMF_NORMALIZE_NORMALS, true);
+ mat.ColorMaterial = video::ECM_NONE;
+ mat.MaterialType = use_transparency ? video::EMT_TRANSPARENT_ALPHA_CHANNEL : video::EMT_SOLID;
+ mat.MaterialTypeParam = 0.01f;
+ mat.DiffuseColor = color;
+
+ wchar_t current_char = 0, previous_char = 0;
+ u32 n = 0;
+
+ array<u32> glyph_indices;
+
+ while (*text)
+ {
+ current_char = *text;
+ bool line_break=false;
+ if (current_char == L'\r') // Mac or Windows breaks
+ {
+ line_break = true;
+ if (*(text + 1) == L'\n') // Windows line breaks.
+ current_char = *(++text);
+ }
+ else if (current_char == L'\n') // Unix breaks
+ {
+ line_break = true;
+ }
+
+ if (line_break)
+ {
+ previous_char = 0;
+ offset.Y -= tt_face->size->metrics.ascender / 64;
+ offset.X = start_point.X;
+ if (center)
+ offset.X += (text_size.Width - getDimensionUntilEndOfLine(text+1).Width) >> 1;
+ ++text;
+ }
+ else
+ {
+ n = getGlyphIndexByChar(current_char);
+ if (n > 0)
+ {
+ glyph_indices.push_back( n );
+
+ // Store glyph size and offset informations.
+ SGUITTGlyph const& glyph = Glyphs[n-1];
+ u32 texw = glyph.source_rect.getWidth();
+ u32 texh = glyph.source_rect.getHeight();
+ s32 offx = glyph.offset.X;
+ s32 offy = (font_metrics.ascender / 64) - glyph.offset.Y;
+
+ // Apply kerning.
+ vector2di k = getKerning(current_char, previous_char);
+ offset.X += k.X;
+ offset.Y += k.Y;
+
+ vector3df current_pos(offset.X + offx, offset.Y - offy, 0);
+ dimension2d<u32> letter_size = dimension2d<u32>(texw, texh);
+
+ // Now we copy planes corresponding to the letter size.
+ IMeshManipulator* mani = smgr->getMeshManipulator();
+ IMesh* meshcopy = mani->createMeshCopy(shared_plane_ptr_);
+ #if IRRLICHT_VERSION_MAJOR==1 && IRRLICHT_VERSION_MINOR>=8
+ mani->scale(meshcopy, vector3df((f32)letter_size.Width, (f32)letter_size.Height, 1));
+ #else
+ mani->scaleMesh(meshcopy, vector3df((f32)letter_size.Width, (f32)letter_size.Height, 1));
+ #endif
+
+ ISceneNode* current_node = smgr->addMeshSceneNode(meshcopy, parent, -1, current_pos);
+ meshcopy->drop();
+
+ current_node->getMaterial(0) = mat;
+ current_node->setAutomaticCulling(EAC_OFF);
+ current_node->setIsDebugObject(true); //so the picking won't have any effect on individual letter
+ //current_node->setDebugDataVisible(EDS_BBOX); //de-comment this when debugging
+
+ container.push_back(current_node);
+ }
+ offset.X += getWidthFromCharacter(current_char);
+ previous_char = current_char;
+ ++text;
+ }
+ }
+
+ update_glyph_pages();
+ //only after we update the textures can we use the glyph page textures.
+
+ for (u32 i = 0; i < glyph_indices.size(); ++i)
+ {
+ u32 n = glyph_indices[i];
+ SGUITTGlyph const& glyph = Glyphs[n-1];
+ ITexture* current_tex = Glyph_Pages[glyph.glyph_page]->texture;
+ f32 page_texture_size = (f32)current_tex->getSize().Width;
+ //Now we calculate the UV position according to the texture size and the source rect.
+ //
+ // 2___3
+ // | /|
+ // | / | <-- plane mesh is like this, point 2 is (0,0), point 0 is (0, -1)
+ // |/ | <-- the texture coords of point 2 is (0,0, point 0 is (0, 1)
+ // 0---1
+ //
+ f32 u1 = glyph.source_rect.UpperLeftCorner.X / page_texture_size;
+ f32 u2 = u1 + (glyph.source_rect.getWidth() / page_texture_size);
+ f32 v1 = glyph.source_rect.UpperLeftCorner.Y / page_texture_size;
+ f32 v2 = v1 + (glyph.source_rect.getHeight() / page_texture_size);
+
+ //we can be quite sure that this is IMeshSceneNode, because we just added them in the above loop.
+ IMeshSceneNode* node = static_cast<IMeshSceneNode*>(container[i]);
+
+ S3DVertex* pv = static_cast<S3DVertex*>(node->getMesh()->getMeshBuffer(0)->getVertices());
+ //pv[0].TCoords.Y = pv[1].TCoords.Y = (letter_size.Height - 1) / static_cast<f32>(letter_size.Height);
+ //pv[1].TCoords.X = pv[3].TCoords.X = (letter_size.Width - 1) / static_cast<f32>(letter_size.Width);
+ pv[0].TCoords = vector2df(u1, v2);
+ pv[1].TCoords = vector2df(u2, v2);
+ pv[2].TCoords = vector2df(u1, v1);
+ pv[3].TCoords = vector2df(u2, v1);
+
+ container[i]->getMaterial(0).setTexture(0, current_tex);
+ }
+
+ return container;
+}
+
+} // end namespace gui
+} // end namespace irr
diff --git a/src/cguittfont/CGUITTFont.h b/src/cguittfont/CGUITTFont.h
new file mode 100644
index 000000000..12e25e0f3
--- /dev/null
+++ b/src/cguittfont/CGUITTFont.h
@@ -0,0 +1,377 @@
+/*
+ CGUITTFont FreeType class for Irrlicht
+ Copyright (c) 2009-2010 John Norman
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any
+ damages arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any
+ purpose, including commercial applications, and to alter it and
+ redistribute it freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you
+ must not claim that you wrote the original software. If you use
+ this software in a product, an acknowledgment in the product
+ documentation would be appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and
+ must not be misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+
+ The original version of this class can be located at:
+ http://irrlicht.suckerfreegames.com/
+
+ John Norman
+ john@suckerfreegames.com
+*/
+
+#ifndef __C_GUI_TTFONT_H_INCLUDED__
+#define __C_GUI_TTFONT_H_INCLUDED__
+
+#include <irrlicht.h>
+#include <ft2build.h>
+#include FT_FREETYPE_H
+
+namespace irr
+{
+namespace gui
+{
+ struct SGUITTFace;
+ class CGUITTFont;
+
+ //! Class to assist in deleting glyphs.
+ class CGUITTAssistDelete
+ {
+ public:
+ template <class T, typename TAlloc>
+ static void Delete(core::array<T, TAlloc>& a)
+ {
+ TAlloc allocator;
+ allocator.deallocate(a.pointer());
+ }
+ };
+
+ //! Structure representing a single TrueType glyph.
+ struct SGUITTGlyph
+ {
+ //! Constructor.
+ SGUITTGlyph() : isLoaded(false), glyph_page(0), surface(0), parent(0) {}
+
+ //! Destructor.
+ ~SGUITTGlyph() { unload(); }
+
+ //! Preload the glyph.
+ //! The preload process occurs when the program tries to cache the glyph from FT_Library.
+ //! However, it simply defines the SGUITTGlyph's properties and will only create the page
+ //! textures if necessary. The actual creation of the textures should only occur right
+ //! before the batch draw call.
+ void preload(u32 char_index, FT_Face face, video::IVideoDriver* driver, u32 font_size, const FT_Int32 loadFlags);
+
+ //! Unloads the glyph.
+ void unload();
+
+ //! Creates the IImage object from the FT_Bitmap.
+ video::IImage* createGlyphImage(const FT_Bitmap& bits, video::IVideoDriver* driver) const;
+
+ //! If true, the glyph has been loaded.
+ bool isLoaded;
+
+ //! The page the glyph is on.
+ u32 glyph_page;
+
+ //! The source rectangle for the glyph.
+ core::recti source_rect;
+
+ //! The offset of glyph when drawn.
+ core::vector2di offset;
+
+ //! Glyph advance information.
+ FT_Vector advance;
+
+ //! This is just the temporary image holder. After this glyph is paged,
+ //! it will be dropped.
+ mutable video::IImage* surface;
+
+ //! The pointer pointing to the parent (CGUITTFont)
+ CGUITTFont* parent;
+ };
+
+ //! Holds a sheet of glyphs.
+ class CGUITTGlyphPage
+ {
+ public:
+ CGUITTGlyphPage(video::IVideoDriver* Driver, const io::path& texture_name) :texture(0), available_slots(0), used_slots(0), dirty(false), driver(Driver), name(texture_name) {}
+ ~CGUITTGlyphPage()
+ {
+ if (texture)
+ {
+ if (driver)
+ driver->removeTexture(texture);
+ else texture->drop();
+ }
+ }
+
+ //! Create the actual page texture,
+ bool createPageTexture(const u8& pixel_mode, const core::dimension2du& texture_size)
+ {
+ if( texture )
+ return false;
+
+ bool flgmip = driver->getTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS);
+ driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, false);
+
+ // Set the texture color format.
+ switch (pixel_mode)
+ {
+ case FT_PIXEL_MODE_MONO:
+ texture = driver->addTexture(texture_size, name, video::ECF_A1R5G5B5);
+ break;
+ case FT_PIXEL_MODE_GRAY:
+ default:
+ texture = driver->addTexture(texture_size, name, video::ECF_A8R8G8B8);
+ break;
+ }
+
+ // Restore our texture creation flags.
+ driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, flgmip);
+ return texture ? true : false;
+ }
+
+ //! Add the glyph to a list of glyphs to be paged.
+ //! This collection will be cleared after updateTexture is called.
+ void pushGlyphToBePaged(const SGUITTGlyph* glyph)
+ {
+ glyph_to_be_paged.push_back(glyph);
+ }
+
+ //! Updates the texture atlas with new glyphs.
+ void updateTexture()
+ {
+ if (!dirty) return;
+
+ void* ptr = texture->lock();
+ video::ECOLOR_FORMAT format = texture->getColorFormat();
+ core::dimension2du size = texture->getOriginalSize();
+ video::IImage* pageholder = driver->createImageFromData(format, size, ptr, true, false);
+
+ for (u32 i = 0; i < glyph_to_be_paged.size(); ++i)
+ {
+ const SGUITTGlyph* glyph = glyph_to_be_paged[i];
+ if (glyph && glyph->isLoaded)
+ {
+ if (glyph->surface)
+ {
+ glyph->surface->copyTo(pageholder, glyph->source_rect.UpperLeftCorner);
+ glyph->surface->drop();
+ glyph->surface = 0;
+ }
+ else
+ {
+ ; // TODO: add error message?
+ //currently, if we failed to create the image, just ignore this operation.
+ }
+ }
+ }
+
+ pageholder->drop();
+ texture->unlock();
+ glyph_to_be_paged.clear();
+ dirty = false;
+ }
+
+ video::ITexture* texture;
+ u32 available_slots;
+ u32 used_slots;
+ bool dirty;
+
+ core::array<core::vector2di> render_positions;
+ core::array<core::recti> render_source_rects;
+
+ private:
+ core::array<const SGUITTGlyph*> glyph_to_be_paged;
+ video::IVideoDriver* driver;
+ io::path name;
+ };
+
+ //! Class representing a TrueType font.
+ class CGUITTFont : public IGUIFont
+ {
+ public:
+ //! Creates a new TrueType font and returns a pointer to it. The pointer must be drop()'ed when finished.
+ //! \param env The IGUIEnvironment the font loads out of.
+ //! \param filename The filename of the font.
+ //! \param size The size of the font glyphs in pixels. Since this is the size of the individual glyphs, the true height of the font may change depending on the characters used.
+ //! \param antialias set the use_monochrome (opposite to antialias) flag
+ //! \param transparency set the use_transparency flag
+ //! \return Returns a pointer to a CGUITTFont. Will return 0 if the font failed to load.
+ static CGUITTFont* createTTFont(IGUIEnvironment *env, const io::path& filename, const u32 size, const bool antialias = true, const bool transparency = true);
+ static CGUITTFont* createTTFont(IrrlichtDevice *device, const io::path& filename, const u32 size, const bool antialias = true, const bool transparency = true);
+ static CGUITTFont* create(IGUIEnvironment *env, const io::path& filename, const u32 size, const bool antialias = true, const bool transparency = true);
+ static CGUITTFont* create(IrrlichtDevice *device, const io::path& filename, const u32 size, const bool antialias = true, const bool transparency = true);
+
+ //! Destructor
+ virtual ~CGUITTFont();
+
+ //! Sets the amount of glyphs to batch load.
+ virtual void setBatchLoadSize(u32 batch_size) { batch_load_size = batch_size; }
+
+ //! Sets the maximum texture size for a page of glyphs.
+ virtual void setMaxPageTextureSize(const core::dimension2du& texture_size) { max_page_texture_size = texture_size; }
+
+ //! Get the font size.
+ virtual u32 getFontSize() const { return size; }
+
+ //! Check the font's transparency.
+ virtual bool isTransparent() const { return use_transparency; }
+
+ //! Check if the font auto-hinting is enabled.
+ //! Auto-hinting is FreeType's built-in font hinting engine.
+ virtual bool useAutoHinting() const { return use_auto_hinting; }
+
+ //! Check if the font hinting is enabled.
+ virtual bool useHinting() const { return use_hinting; }
+
+ //! Check if the font is being loaded as a monochrome font.
+ //! The font can either be a 256 color grayscale font, or a 2 color monochrome font.
+ virtual bool useMonochrome() const { return use_monochrome; }
+
+ //! Tells the font to allow transparency when rendering.
+ //! Default: true.
+ //! \param flag If true, the font draws using transparency.
+ virtual void setTransparency(const bool flag);
+
+ //! Tells the font to use monochrome rendering.
+ //! Default: false.
+ //! \param flag If true, the font draws using a monochrome image. If false, the font uses a grayscale image.
+ virtual void setMonochrome(const bool flag);
+
+ //! Enables or disables font hinting.
+ //! Default: Hinting and auto-hinting true.
+ //! \param enable If false, font hinting is turned off. If true, font hinting is turned on.
+ //! \param enable_auto_hinting If true, FreeType uses its own auto-hinting algorithm. If false, it tries to use the algorithm specified by the font.
+ virtual void setFontHinting(const bool enable, const bool enable_auto_hinting = true);
+
+ //! Draws some text and clips it to the specified rectangle if wanted.
+ virtual void draw(const core::stringw& text, const core::rect<s32>& position,
+ video::SColor color, bool hcenter=false, bool vcenter=false,
+ const core::rect<s32>* clip=0);
+
+ //! Returns the dimension of a character produced by this font.
+ virtual core::dimension2d<u32> getCharDimension(const wchar_t ch) const;
+
+ //! Returns the dimension of a text string.
+ virtual core::dimension2d<u32> getDimension(const wchar_t* text) const;
+ virtual core::dimension2d<u32> getDimension(const core::ustring& text) const;
+
+ //! Calculates the index of the character in the text which is on a specific position.
+ virtual s32 getCharacterFromPos(const wchar_t* text, s32 pixel_x) const;
+ virtual s32 getCharacterFromPos(const core::ustring& text, s32 pixel_x) const;
+
+ //! Sets global kerning width for the font.
+ virtual void setKerningWidth(s32 kerning);
+
+ //! Sets global kerning height for the font.
+ virtual void setKerningHeight(s32 kerning);
+
+ //! Gets kerning values (distance between letters) for the font. If no parameters are provided,
+ virtual s32 getKerningWidth(const wchar_t* thisLetter=0, const wchar_t* previousLetter=0) const;
+ virtual s32 getKerningWidth(const uchar32_t thisLetter=0, const uchar32_t previousLetter=0) const;
+
+ //! Returns the distance between letters
+ virtual s32 getKerningHeight() const;
+
+ //! Define which characters should not be drawn by the font.
+ virtual void setInvisibleCharacters(const wchar_t *s);
+ virtual void setInvisibleCharacters(const core::ustring& s);
+
+ //! Get the last glyph page if there's still available slots.
+ //! If not, it will return zero.
+ CGUITTGlyphPage* getLastGlyphPage() const;
+
+ //! Create a new glyph page texture.
+ //! \param pixel_mode the pixel mode defined by FT_Pixel_Mode
+ //should be better typed. fix later.
+ CGUITTGlyphPage* createGlyphPage(const u8& pixel_mode);
+
+ //! Get the last glyph page's index.
+ u32 getLastGlyphPageIndex() const { return Glyph_Pages.size() - 1; }
+
+ //! Create corresponding character's software image copy from the font,
+ //! so you can use this data just like any ordinary video::IImage.
+ //! \param ch The character you need
+ virtual video::IImage* createTextureFromChar(const uchar32_t& ch);
+
+ //! This function is for debugging mostly. If the page doesn't exist it returns zero.
+ //! \param page_index Simply return the texture handle of a given page index.
+ virtual video::ITexture* getPageTextureByIndex(const u32& page_index) const;
+
+ //! Add a list of scene nodes generated by putting font textures on the 3D planes.
+ virtual core::array<scene::ISceneNode*> addTextSceneNode
+ (const wchar_t* text, scene::ISceneManager* smgr, scene::ISceneNode* parent = 0,
+ const video::SColor& color = video::SColor(255, 0, 0, 0), bool center = false );
+
+ protected:
+ bool use_monochrome;
+ bool use_transparency;
+ bool use_hinting;
+ bool use_auto_hinting;
+ u32 size;
+ u32 batch_load_size;
+ core::dimension2du max_page_texture_size;
+
+ private:
+ // Manages the FreeType library.
+ static FT_Library c_library;
+ static core::map<io::path, SGUITTFace*> c_faces;
+ static bool c_libraryLoaded;
+ static scene::IMesh* shared_plane_ptr_;
+ static scene::SMesh shared_plane_;
+
+ CGUITTFont(IGUIEnvironment *env);
+ bool load(const io::path& filename, const u32 size, const bool antialias, const bool transparency);
+ void reset_images();
+ void update_glyph_pages() const;
+ void update_load_flags()
+ {
+ // Set up our loading flags.
+ load_flags = FT_LOAD_DEFAULT | FT_LOAD_RENDER;
+ if (!useHinting()) load_flags |= FT_LOAD_NO_HINTING;
+ if (!useAutoHinting()) load_flags |= FT_LOAD_NO_AUTOHINT;
+ if (useMonochrome()) load_flags |= FT_LOAD_MONOCHROME | FT_LOAD_TARGET_MONO | FT_RENDER_MODE_MONO;
+ else load_flags |= FT_LOAD_TARGET_NORMAL;
+ }
+ u32 getWidthFromCharacter(wchar_t c) const;
+ u32 getWidthFromCharacter(uchar32_t c) const;
+ u32 getHeightFromCharacter(wchar_t c) const;
+ u32 getHeightFromCharacter(uchar32_t c) const;
+ u32 getGlyphIndexByChar(wchar_t c) const;
+ u32 getGlyphIndexByChar(uchar32_t c) const;
+ core::vector2di getKerning(const wchar_t thisLetter, const wchar_t previousLetter) const;
+ core::vector2di getKerning(const uchar32_t thisLetter, const uchar32_t previousLetter) const;
+ core::dimension2d<u32> getDimensionUntilEndOfLine(const wchar_t* p) const;
+
+ void createSharedPlane();
+
+ irr::IrrlichtDevice* Device;
+ gui::IGUIEnvironment* Environment;
+ video::IVideoDriver* Driver;
+ io::path filename;
+ FT_Face tt_face;
+ FT_Size_Metrics font_metrics;
+ FT_Int32 load_flags;
+
+ mutable core::array<CGUITTGlyphPage*> Glyph_Pages;
+ mutable core::array<SGUITTGlyph> Glyphs;
+
+ s32 GlobalKerningWidth;
+ s32 GlobalKerningHeight;
+ core::ustring Invisible;
+ };
+
+} // end namespace gui
+} // end namespace irr
+
+#endif // __C_GUI_TTFONT_H_INCLUDED__
diff --git a/src/cguittfont/CMakeLists.txt b/src/cguittfont/CMakeLists.txt
new file mode 100644
index 000000000..94d061462
--- /dev/null
+++ b/src/cguittfont/CMakeLists.txt
@@ -0,0 +1,17 @@
+include_directories(
+ ${IRRLICHT_INCLUDE_DIR}
+ ${FREETYPE_INCLUDE_DIRS}
+)
+
+# CGUITTFont authors, y u no include headers you use?
+# 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)
+
+target_link_libraries(
+ cguittfont
+ ${IRRLICHT_LIBRARY}
+ ${FREETYPE_LIBRARY}
+ ${ZLIB_LIBRARIES} # needed by freetype, repeated here for safety
+)
diff --git a/src/cguittfont/irrUString.h b/src/cguittfont/irrUString.h
new file mode 100644
index 000000000..f41fa1f7b
--- /dev/null
+++ b/src/cguittfont/irrUString.h
@@ -0,0 +1,3877 @@
+/*
+ Basic Unicode string class for Irrlicht.
+ Copyright (c) 2009-2011 John Norman
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any
+ damages arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any
+ purpose, including commercial applications, and to alter it and
+ redistribute it freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you
+ must not claim that you wrote the original software. If you use
+ this software in a product, an acknowledgment in the product
+ documentation would be appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and
+ must not be misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+
+ The original version of this class can be located at:
+ http://irrlicht.suckerfreegames.com/
+
+ John Norman
+ john@suckerfreegames.com
+*/
+
+#ifndef __IRR_USTRING_H_INCLUDED__
+#define __IRR_USTRING_H_INCLUDED__
+
+#if (__cplusplus > 199711L) || (_MSC_VER >= 1600) || defined(__GXX_EXPERIMENTAL_CXX0X__)
+# define USTRING_CPP0X
+# if defined(__GXX_EXPERIMENTAL_CXX0X__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 5)))
+# define USTRING_CPP0X_NEWLITERALS
+# endif
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#ifdef USTRING_CPP0X
+# include <utility>
+#endif
+
+#ifndef USTRING_NO_STL
+# include <string>
+# include <iterator>
+# include <ostream>
+#endif
+
+#include "irrTypes.h"
+#include "irrAllocator.h"
+#include "irrArray.h"
+#include "irrMath.h"
+#include "irrString.h"
+#include "path.h"
+
+//! UTF-16 surrogate start values.
+static const irr::u16 UTF16_HI_SURROGATE = 0xD800;
+static const irr::u16 UTF16_LO_SURROGATE = 0xDC00;
+
+//! Is a UTF-16 code point a surrogate?
+#define UTF16_IS_SURROGATE(c) (((c) & 0xF800) == 0xD800)
+#define UTF16_IS_SURROGATE_HI(c) (((c) & 0xFC00) == 0xD800)
+#define UTF16_IS_SURROGATE_LO(c) (((c) & 0xFC00) == 0xDC00)
+
+
+namespace irr
+{
+
+ // Define our character types.
+#ifdef USTRING_CPP0X_NEWLITERALS // C++0x
+ typedef char32_t uchar32_t;
+ typedef char16_t uchar16_t;
+ typedef char uchar8_t;
+#else
+ typedef u32 uchar32_t;
+ typedef u16 uchar16_t;
+ typedef u8 uchar8_t;
+#endif
+
+namespace core
+{
+
+namespace unicode
+{
+
+//! The unicode replacement character. Used to replace invalid characters.
+const irr::u16 UTF_REPLACEMENT_CHARACTER = 0xFFFD;
+
+//! Convert a UTF-16 surrogate pair into a UTF-32 character.
+//! \param high The high value of the pair.
+//! \param low The low value of the pair.
+//! \return The UTF-32 character expressed by the surrogate pair.
+inline uchar32_t toUTF32(uchar16_t high, uchar16_t low)
+{
+ // Convert the surrogate pair into a single UTF-32 character.
+ uchar32_t x = ((high & ((1 << 6) -1)) << 10) | (low & ((1 << 10) -1));
+ uchar32_t wu = ((high >> 6) & ((1 << 5) - 1)) + 1;
+ return (wu << 16) | x;
+}
+
+//! Swaps the endianness of a 16-bit value.
+//! \return The new value.
+inline uchar16_t swapEndian16(const uchar16_t& c)
+{
+ return ((c >> 8) & 0x00FF) | ((c << 8) & 0xFF00);
+}
+
+//! Swaps the endianness of a 32-bit value.
+//! \return The new value.
+inline uchar32_t swapEndian32(const uchar32_t& c)
+{
+ return ((c >> 24) & 0x000000FF) |
+ ((c >> 8) & 0x0000FF00) |
+ ((c << 8) & 0x00FF0000) |
+ ((c << 24) & 0xFF000000);
+}
+
+//! The Unicode byte order mark.
+const u16 BOM = 0xFEFF;
+
+//! The size of the Unicode byte order mark in terms of the Unicode character size.
+const u8 BOM_UTF8_LEN = 3;
+const u8 BOM_UTF16_LEN = 1;
+const u8 BOM_UTF32_LEN = 1;
+
+//! Unicode byte order marks for file operations.
+const u8 BOM_ENCODE_UTF8[3] = { 0xEF, 0xBB, 0xBF };
+const u8 BOM_ENCODE_UTF16_BE[2] = { 0xFE, 0xFF };
+const u8 BOM_ENCODE_UTF16_LE[2] = { 0xFF, 0xFE };
+const u8 BOM_ENCODE_UTF32_BE[4] = { 0x00, 0x00, 0xFE, 0xFF };
+const u8 BOM_ENCODE_UTF32_LE[4] = { 0xFF, 0xFE, 0x00, 0x00 };
+
+//! The size in bytes of the Unicode byte marks for file operations.
+const u8 BOM_ENCODE_UTF8_LEN = 3;
+const u8 BOM_ENCODE_UTF16_LEN = 2;
+const u8 BOM_ENCODE_UTF32_LEN = 4;
+
+//! Unicode encoding type.
+enum EUTF_ENCODE
+{
+ EUTFE_NONE = 0,
+ EUTFE_UTF8,
+ EUTFE_UTF16,
+ EUTFE_UTF16_LE,
+ EUTFE_UTF16_BE,
+ EUTFE_UTF32,
+ EUTFE_UTF32_LE,
+ EUTFE_UTF32_BE
+};
+
+//! Unicode endianness.
+enum EUTF_ENDIAN
+{
+ EUTFEE_NATIVE = 0,
+ EUTFEE_LITTLE,
+ EUTFEE_BIG
+};
+
+//! Returns the specified unicode byte order mark in a byte array.
+//! The byte order mark is the first few bytes in a text file that signifies its encoding.
+/** \param mode The Unicode encoding method that we want to get the byte order mark for.
+ If EUTFE_UTF16 or EUTFE_UTF32 is passed, it uses the native system endianness. **/
+//! \return An array that contains a byte order mark.
+inline core::array<u8> getUnicodeBOM(EUTF_ENCODE mode)
+{
+#define COPY_ARRAY(source, size) \
+ memcpy(ret.pointer(), source, size); \
+ ret.set_used(size)
+
+ core::array<u8> ret(4);
+ switch (mode)
+ {
+ case EUTFE_UTF8:
+ COPY_ARRAY(BOM_ENCODE_UTF8, BOM_ENCODE_UTF8_LEN);
+ break;
+ case EUTFE_UTF16:
+ #ifdef __BIG_ENDIAN__
+ COPY_ARRAY(BOM_ENCODE_UTF16_BE, BOM_ENCODE_UTF16_LEN);
+ #else
+ COPY_ARRAY(BOM_ENCODE_UTF16_LE, BOM_ENCODE_UTF16_LEN);
+ #endif
+ break;
+ case EUTFE_UTF16_BE:
+ COPY_ARRAY(BOM_ENCODE_UTF16_BE, BOM_ENCODE_UTF16_LEN);
+ break;
+ case EUTFE_UTF16_LE:
+ COPY_ARRAY(BOM_ENCODE_UTF16_LE, BOM_ENCODE_UTF16_LEN);
+ break;
+ case EUTFE_UTF32:
+ #ifdef __BIG_ENDIAN__
+ COPY_ARRAY(BOM_ENCODE_UTF32_BE, BOM_ENCODE_UTF32_LEN);
+ #else
+ COPY_ARRAY(BOM_ENCODE_UTF32_LE, BOM_ENCODE_UTF32_LEN);
+ #endif
+ break;
+ case EUTFE_UTF32_BE:
+ COPY_ARRAY(BOM_ENCODE_UTF32_BE, BOM_ENCODE_UTF32_LEN);
+ break;
+ case EUTFE_UTF32_LE:
+ COPY_ARRAY(BOM_ENCODE_UTF32_LE, BOM_ENCODE_UTF32_LEN);
+ break;
+ }
+ return ret;
+
+#undef COPY_ARRAY
+}
+
+//! Detects if the given data stream starts with a unicode BOM.
+//! \param data The data stream to check.
+//! \return The unicode BOM associated with the data stream, or EUTFE_NONE if none was found.
+inline EUTF_ENCODE determineUnicodeBOM(const char* data)
+{
+ if (memcmp(data, BOM_ENCODE_UTF8, 3) == 0) return EUTFE_UTF8;
+ if (memcmp(data, BOM_ENCODE_UTF16_BE, 2) == 0) return EUTFE_UTF16_BE;
+ if (memcmp(data, BOM_ENCODE_UTF16_LE, 2) == 0) return EUTFE_UTF16_LE;
+ if (memcmp(data, BOM_ENCODE_UTF32_BE, 4) == 0) return EUTFE_UTF32_BE;
+ if (memcmp(data, BOM_ENCODE_UTF32_LE, 4) == 0) return EUTFE_UTF32_LE;
+ return EUTFE_NONE;
+}
+
+} // end namespace unicode
+
+
+//! UTF-16 string class.
+template <typename TAlloc = irrAllocator<uchar16_t> >
+class ustring16
+{
+public:
+
+ ///------------------///
+ /// iterator classes ///
+ ///------------------///
+
+ //! Access an element in a unicode string, allowing one to change it.
+ class _ustring16_iterator_access
+ {
+ public:
+ _ustring16_iterator_access(const ustring16<TAlloc>* s, u32 p) : ref(s), pos(p) {}
+
+ //! Allow the class to be interpreted as a single UTF-32 character.
+ operator uchar32_t() const
+ {
+ return _get();
+ }
+
+ //! Allow one to change the character in the unicode string.
+ //! \param c The new character to use.
+ //! \return Myself.
+ _ustring16_iterator_access& operator=(const uchar32_t c)
+ {
+ _set(c);
+ return *this;
+ }
+
+ //! Increments the value by 1.
+ //! \return Myself.
+ _ustring16_iterator_access& operator++()
+ {
+ _set(_get() + 1);
+ return *this;
+ }
+
+ //! Increments the value by 1, returning the old value.
+ //! \return A unicode character.
+ uchar32_t operator++(int)
+ {
+ uchar32_t old = _get();
+ _set(old + 1);
+ return old;
+ }
+
+ //! Decrements the value by 1.
+ //! \return Myself.
+ _ustring16_iterator_access& operator--()
+ {
+ _set(_get() - 1);
+ return *this;
+ }
+
+ //! Decrements the value by 1, returning the old value.
+ //! \return A unicode character.
+ uchar32_t operator--(int)
+ {
+ uchar32_t old = _get();
+ _set(old - 1);
+ return old;
+ }
+
+ //! Adds to the value by a specified amount.
+ //! \param val The amount to add to this character.
+ //! \return Myself.
+ _ustring16_iterator_access& operator+=(int val)
+ {
+ _set(_get() + val);
+ return *this;
+ }
+
+ //! Subtracts from the value by a specified amount.
+ //! \param val The amount to subtract from this character.
+ //! \return Myself.
+ _ustring16_iterator_access& operator-=(int val)
+ {
+ _set(_get() - val);
+ return *this;
+ }
+
+ //! Multiples the value by a specified amount.
+ //! \param val The amount to multiply this character by.
+ //! \return Myself.
+ _ustring16_iterator_access& operator*=(int val)
+ {
+ _set(_get() * val);
+ return *this;
+ }
+
+ //! Divides the value by a specified amount.
+ //! \param val The amount to divide this character by.
+ //! \return Myself.
+ _ustring16_iterator_access& operator/=(int val)
+ {
+ _set(_get() / val);
+ return *this;
+ }
+
+ //! Modulos the value by a specified amount.
+ //! \param val The amount to modulo this character by.
+ //! \return Myself.
+ _ustring16_iterator_access& operator%=(int val)
+ {
+ _set(_get() % val);
+ return *this;
+ }
+
+ //! Adds to the value by a specified amount.
+ //! \param val The amount to add to this character.
+ //! \return A unicode character.
+ uchar32_t operator+(int val) const
+ {
+ return _get() + val;
+ }
+
+ //! Subtracts from the value by a specified amount.
+ //! \param val The amount to subtract from this character.
+ //! \return A unicode character.
+ uchar32_t operator-(int val) const
+ {
+ return _get() - val;
+ }
+
+ //! Multiplies the value by a specified amount.
+ //! \param val The amount to multiply this character by.
+ //! \return A unicode character.
+ uchar32_t operator*(int val) const
+ {
+ return _get() * val;
+ }
+
+ //! Divides the value by a specified amount.
+ //! \param val The amount to divide this character by.
+ //! \return A unicode character.
+ uchar32_t operator/(int val) const
+ {
+ return _get() / val;
+ }
+
+ //! Modulos the value by a specified amount.
+ //! \param val The amount to modulo this character by.
+ //! \return A unicode character.
+ uchar32_t operator%(int val) const
+ {
+ return _get() % val;
+ }
+
+ private:
+ //! Gets a uchar32_t from our current position.
+ uchar32_t _get() const
+ {
+ const uchar16_t* a = ref->c_str();
+ if (!UTF16_IS_SURROGATE(a[pos]))
+ return static_cast<uchar32_t>(a[pos]);
+ else
+ {
+ if (pos + 1 >= ref->size_raw())
+ return 0;
+
+ return unicode::toUTF32(a[pos], a[pos + 1]);
+ }
+ }
+
+ //! Sets a uchar32_t at our current position.
+ void _set(uchar32_t c)
+ {
+ ustring16<TAlloc>* ref2 = const_cast<ustring16<TAlloc>*>(ref);
+ const uchar16_t* a = ref2->c_str();
+ if (c > 0xFFFF)
+ {
+ // c will be multibyte, so split it up into the high and low surrogate pairs.
+ uchar16_t x = static_cast<uchar16_t>(c);
+ uchar16_t vh = UTF16_HI_SURROGATE | ((((c >> 16) & ((1 << 5) - 1)) - 1) << 6) | (x >> 10);
+ uchar16_t vl = UTF16_LO_SURROGATE | (x & ((1 << 10) - 1));
+
+ // If the previous position was a surrogate pair, just replace them. Else, insert the low pair.
+ if (UTF16_IS_SURROGATE_HI(a[pos]) && pos + 1 != ref2->size_raw())
+ ref2->replace_raw(vl, static_cast<u32>(pos) + 1);
+ else ref2->insert_raw(vl, static_cast<u32>(pos) + 1);
+
+ ref2->replace_raw(vh, static_cast<u32>(pos));
+ }
+ else
+ {
+ // c will be a single byte.
+ uchar16_t vh = static_cast<uchar16_t>(c);
+
+ // If the previous position was a surrogate pair, remove the extra byte.
+ if (UTF16_IS_SURROGATE_HI(a[pos]))
+ ref2->erase_raw(static_cast<u32>(pos) + 1);
+
+ ref2->replace_raw(vh, static_cast<u32>(pos));
+ }
+ }
+
+ const ustring16<TAlloc>* ref;
+ u32 pos;
+ };
+ typedef typename ustring16<TAlloc>::_ustring16_iterator_access access;
+
+
+ //! Iterator to iterate through a UTF-16 string.
+#ifndef USTRING_NO_STL
+ class _ustring16_const_iterator : public std::iterator<
+ std::bidirectional_iterator_tag, // iterator_category
+ access, // value_type
+ ptrdiff_t, // difference_type
+ const access, // pointer
+ const access // reference
+ >
+#else
+ class _ustring16_const_iterator
+#endif
+ {
+ public:
+ typedef _ustring16_const_iterator _Iter;
+ typedef std::iterator<std::bidirectional_iterator_tag, access, ptrdiff_t, const access, const access> _Base;
+ typedef const access const_pointer;
+ typedef const access const_reference;
+
+#ifndef USTRING_NO_STL
+ typedef typename _Base::value_type value_type;
+ typedef typename _Base::difference_type difference_type;
+ typedef typename _Base::difference_type distance_type;
+ typedef typename _Base::pointer pointer;
+ typedef const_reference reference;
+#else
+ typedef access value_type;
+ typedef u32 difference_type;
+ typedef u32 distance_type;
+ typedef const_pointer pointer;
+ typedef const_reference reference;
+#endif
+
+ //! Constructors.
+ _ustring16_const_iterator(const _Iter& i) : ref(i.ref), pos(i.pos) {}
+ _ustring16_const_iterator(const ustring16<TAlloc>& s) : ref(&s), pos(0) {}
+ _ustring16_const_iterator(const ustring16<TAlloc>& s, const u32 p) : ref(&s), pos(0)
+ {
+ if (ref->size_raw() == 0 || p == 0)
+ return;
+
+ // Go to the appropriate position.
+ u32 i = p;
+ u32 sr = ref->size_raw();
+ const uchar16_t* a = ref->c_str();
+ while (i != 0 && pos < sr)
+ {
+ if (UTF16_IS_SURROGATE_HI(a[pos]))
+ pos += 2;
+ else ++pos;
+ --i;
+ }
+ }
+
+ //! Test for equalness.
+ bool operator==(const _Iter& iter) const
+ {
+ if (ref == iter.ref && pos == iter.pos)
+ return true;
+ return false;
+ }
+
+ //! Test for unequalness.
+ bool operator!=(const _Iter& iter) const
+ {
+ if (ref != iter.ref || pos != iter.pos)
+ return true;
+ return false;
+ }
+
+ //! Switch to the next full character in the string.
+ _Iter& operator++()
+ { // ++iterator
+ if (pos == ref->size_raw()) return *this;
+ const uchar16_t* a = ref->c_str();
+ if (UTF16_IS_SURROGATE_HI(a[pos]))
+ pos += 2; // TODO: check for valid low surrogate?
+ else ++pos;
+ if (pos > ref->size_raw()) pos = ref->size_raw();
+ return *this;
+ }
+
+ //! Switch to the next full character in the string, returning the previous position.
+ _Iter operator++(int)
+ { // iterator++
+ _Iter _tmp(*this);
+ ++*this;
+ return _tmp;
+ }
+
+ //! Switch to the previous full character in the string.
+ _Iter& operator--()
+ { // --iterator
+ if (pos == 0) return *this;
+ const uchar16_t* a = ref->c_str();
+ --pos;
+ if (UTF16_IS_SURROGATE_LO(a[pos]) && pos != 0) // low surrogate, go back one more.
+ --pos;
+ return *this;
+ }
+
+ //! Switch to the previous full character in the string, returning the previous position.
+ _Iter operator--(int)
+ { // iterator--
+ _Iter _tmp(*this);
+ --*this;
+ return _tmp;
+ }
+
+ //! Advance a specified number of full characters in the string.
+ //! \return Myself.
+ _Iter& operator+=(const difference_type v)
+ {
+ if (v == 0) return *this;
+ if (v < 0) return operator-=(v * -1);
+
+ if (pos >= ref->size_raw())
+ return *this;
+
+ // Go to the appropriate position.
+ // TODO: Don't force u32 on an x64 OS. Make it agnostic.
+ u32 i = (u32)v;
+ u32 sr = ref->size_raw();
+ const uchar16_t* a = ref->c_str();
+ while (i != 0 && pos < sr)
+ {
+ if (UTF16_IS_SURROGATE_HI(a[pos]))
+ pos += 2;
+ else ++pos;
+ --i;
+ }
+ if (pos > sr)
+ pos = sr;
+
+ return *this;
+ }
+
+ //! Go back a specified number of full characters in the string.
+ //! \return Myself.
+ _Iter& operator-=(const difference_type v)
+ {
+ if (v == 0) return *this;
+ if (v > 0) return operator+=(v * -1);
+
+ if (pos == 0)
+ return *this;
+
+ // Go to the appropriate position.
+ // TODO: Don't force u32 on an x64 OS. Make it agnostic.
+ u32 i = (u32)v;
+ const uchar16_t* a = ref->c_str();
+ while (i != 0 && pos != 0)
+ {
+ --pos;
+ if (UTF16_IS_SURROGATE_LO(a[pos]) != 0 && pos != 0)
+ --pos;
+ --i;
+ }
+
+ return *this;
+ }
+
+ //! Return a new iterator that is a variable number of full characters forward from the current position.
+ _Iter operator+(const difference_type v) const
+ {
+ _Iter ret(*this);
+ ret += v;
+ return ret;
+ }
+
+ //! Return a new iterator that is a variable number of full characters backward from the current position.
+ _Iter operator-(const difference_type v) const
+ {
+ _Iter ret(*this);
+ ret -= v;
+ return ret;
+ }
+
+ //! Returns the distance between two iterators.
+ difference_type operator-(const _Iter& iter) const
+ {
+ // Make sure we reference the same object!
+ if (ref != iter.ref)
+ return difference_type();
+
+ _Iter i = iter;
+ difference_type ret;
+
+ // Walk up.
+ if (pos > i.pos)
+ {
+ while (pos > i.pos)
+ {
+ ++i;
+ ++ret;
+ }
+ return ret;
+ }
+
+ // Walk down.
+ while (pos < i.pos)
+ {
+ --i;
+ --ret;
+ }
+ return ret;
+ }
+
+ //! Accesses the full character at the iterator's position.
+ const_reference operator*() const
+ {
+ if (pos >= ref->size_raw())
+ {
+ const uchar16_t* a = ref->c_str();
+ u32 p = ref->size_raw();
+ if (UTF16_IS_SURROGATE_LO(a[p]))
+ --p;
+ reference ret(ref, p);
+ return ret;
+ }
+ const_reference ret(ref, pos);
+ return ret;
+ }
+
+ //! Accesses the full character at the iterator's position.
+ reference operator*()
+ {
+ if (pos >= ref->size_raw())
+ {
+ const uchar16_t* a = ref->c_str();
+ u32 p = ref->size_raw();
+ if (UTF16_IS_SURROGATE_LO(a[p]))
+ --p;
+ reference ret(ref, p);
+ return ret;
+ }
+ reference ret(ref, pos);
+ return ret;
+ }
+
+ //! Accesses the full character at the iterator's position.
+ const_pointer operator->() const
+ {
+ return operator*();
+ }
+
+ //! Accesses the full character at the iterator's position.
+ pointer operator->()
+ {
+ return operator*();
+ }
+
+ //! Is the iterator at the start of the string?
+ bool atStart() const
+ {
+ return pos == 0;
+ }
+
+ //! Is the iterator at the end of the string?
+ bool atEnd() const
+ {
+ const uchar16_t* a = ref->c_str();
+ if (UTF16_IS_SURROGATE(a[pos]))
+ return (pos + 1) >= ref->size_raw();
+ else return pos >= ref->size_raw();
+ }
+
+ //! Moves the iterator to the start of the string.
+ void toStart()
+ {
+ pos = 0;
+ }
+
+ //! Moves the iterator to the end of the string.
+ void toEnd()
+ {
+ const uchar16_t* a = ref->c_str();
+ pos = ref->size_raw();
+ }
+
+ //! Returns the iterator's position.
+ //! \return The iterator's position.
+ u32 getPos() const
+ {
+ return pos;
+ }
+
+ protected:
+ const ustring16<TAlloc>* ref;
+ u32 pos;
+ };
+
+ //! Iterator to iterate through a UTF-16 string.
+ class _ustring16_iterator : public _ustring16_const_iterator
+ {
+ public:
+ typedef _ustring16_iterator _Iter;
+ typedef _ustring16_const_iterator _Base;
+ typedef typename _Base::const_pointer const_pointer;
+ typedef typename _Base::const_reference const_reference;
+
+ typedef typename _Base::value_type value_type;
+ typedef typename _Base::difference_type difference_type;
+ typedef typename _Base::distance_type distance_type;
+ typedef access pointer;
+ typedef access reference;
+
+ using _Base::pos;
+ using _Base::ref;
+
+ //! Constructors.
+ _ustring16_iterator(const _Iter& i) : _ustring16_const_iterator(i) {}
+ _ustring16_iterator(const ustring16<TAlloc>& s) : _ustring16_const_iterator(s) {}
+ _ustring16_iterator(const ustring16<TAlloc>& s, const u32 p) : _ustring16_const_iterator(s, p) {}
+
+ //! Accesses the full character at the iterator's position.
+ reference operator*() const
+ {
+ if (pos >= ref->size_raw())
+ {
+ const uchar16_t* a = ref->c_str();
+ u32 p = ref->size_raw();
+ if (UTF16_IS_SURROGATE_LO(a[p]))
+ --p;
+ reference ret(ref, p);
+ return ret;
+ }
+ reference ret(ref, pos);
+ return ret;
+ }
+
+ //! Accesses the full character at the iterator's position.
+ reference operator*()
+ {
+ if (pos >= ref->size_raw())
+ {
+ const uchar16_t* a = ref->c_str();
+ u32 p = ref->size_raw();
+ if (UTF16_IS_SURROGATE_LO(a[p]))
+ --p;
+ reference ret(ref, p);
+ return ret;
+ }
+ reference ret(ref, pos);
+ return ret;
+ }
+
+ //! Accesses the full character at the iterator's position.
+ pointer operator->() const
+ {
+ return operator*();
+ }
+
+ //! Accesses the full character at the iterator's position.
+ pointer operator->()
+ {
+ return operator*();
+ }
+ };
+
+ typedef typename ustring16<TAlloc>::_ustring16_iterator iterator;
+ typedef typename ustring16<TAlloc>::_ustring16_const_iterator const_iterator;
+
+ ///----------------------///
+ /// end iterator classes ///
+ ///----------------------///
+
+ //! Default constructor
+ ustring16()
+ : array(0), allocated(1), used(0)
+ {
+#if __BIG_ENDIAN__
+ encoding = unicode::EUTFE_UTF16_BE;
+#else
+ encoding = unicode::EUTFE_UTF16_LE;
+#endif
+ array = allocator.allocate(1); // new u16[1];
+ array[0] = 0x0;
+ }
+
+
+ //! Constructor
+ ustring16(const ustring16<TAlloc>& other)
+ : array(0), allocated(0), used(0)
+ {
+#if __BIG_ENDIAN__
+ encoding = unicode::EUTFE_UTF16_BE;
+#else
+ encoding = unicode::EUTFE_UTF16_LE;
+#endif
+ *this = other;
+ }
+
+
+ //! Constructor from other string types
+ template <class B, class A>
+ ustring16(const string<B, A>& other)
+ : array(0), allocated(0), used(0)
+ {
+#if __BIG_ENDIAN__
+ encoding = unicode::EUTFE_UTF16_BE;
+#else
+ encoding = unicode::EUTFE_UTF16_LE;
+#endif
+ *this = other;
+ }
+
+
+#ifndef USTRING_NO_STL
+ //! Constructor from std::string
+ template <class B, class A, typename Alloc>
+ ustring16(const std::basic_string<B, A, Alloc>& other)
+ : array(0), allocated(0), used(0)
+ {
+#if __BIG_ENDIAN__
+ encoding = unicode::EUTFE_UTF16_BE;
+#else
+ encoding = unicode::EUTFE_UTF16_LE;
+#endif
+ *this = other.c_str();
+ }
+
+
+ //! Constructor from iterator.
+ template <typename Itr>
+ ustring16(Itr first, Itr last)
+ : array(0), allocated(0), used(0)
+ {
+#if __BIG_ENDIAN__
+ encoding = unicode::EUTFE_UTF16_BE;
+#else
+ encoding = unicode::EUTFE_UTF16_LE;
+#endif
+ reserve(std::distance(first, last));
+ array[used] = 0;
+
+ for (; first != last; ++first)
+ append((uchar32_t)*first);
+ }
+#endif
+
+
+#ifndef USTRING_CPP0X_NEWLITERALS
+ //! Constructor for copying a character string from a pointer.
+ ustring16(const char* const c)
+ : array(0), allocated(0), used(0)
+ {
+#if __BIG_ENDIAN__
+ encoding = unicode::EUTFE_UTF16_BE;
+#else
+ encoding = unicode::EUTFE_UTF16_LE;
+#endif
+
+ loadDataStream(c, strlen(c));
+ //append((uchar8_t*)c);
+ }
+
+
+ //! Constructor for copying a character string from a pointer with a given length.
+ ustring16(const char* const c, u32 length)
+ : array(0), allocated(0), used(0)
+ {
+#if __BIG_ENDIAN__
+ encoding = unicode::EUTFE_UTF16_BE;
+#else
+ encoding = unicode::EUTFE_UTF16_LE;
+#endif
+
+ loadDataStream(c, length);
+ }
+#endif
+
+
+ //! Constructor for copying a UTF-8 string from a pointer.
+ ustring16(const uchar8_t* const c)
+ : array(0), allocated(0), used(0)
+ {
+#if __BIG_ENDIAN__
+ encoding = unicode::EUTFE_UTF16_BE;
+#else
+ encoding = unicode::EUTFE_UTF16_LE;
+#endif
+
+ append(c);
+ }
+
+
+ //! Constructor for copying a UTF-8 string from a single char.
+ ustring16(const char c)
+ : array(0), allocated(0), used(0)
+ {
+#if __BIG_ENDIAN__
+ encoding = unicode::EUTFE_UTF16_BE;
+#else
+ encoding = unicode::EUTFE_UTF16_LE;
+#endif
+
+ append((uchar32_t)c);
+ }
+
+
+ //! Constructor for copying a UTF-8 string from a pointer with a given length.
+ ustring16(const uchar8_t* const c, u32 length)
+ : array(0), allocated(0), used(0)
+ {
+#if __BIG_ENDIAN__
+ encoding = unicode::EUTFE_UTF16_BE;
+#else
+ encoding = unicode::EUTFE_UTF16_LE;
+#endif
+
+ append(c, length);
+ }
+
+
+ //! Constructor for copying a UTF-16 string from a pointer.
+ ustring16(const uchar16_t* const c)
+ : array(0), allocated(0), used(0)
+ {
+#if __BIG_ENDIAN__
+ encoding = unicode::EUTFE_UTF16_BE;
+#else
+ encoding = unicode::EUTFE_UTF16_LE;
+#endif
+
+ append(c);
+ }
+
+
+ //! Constructor for copying a UTF-16 string from a pointer with a given length
+ ustring16(const uchar16_t* const c, u32 length)
+ : array(0), allocated(0), used(0)
+ {
+#if __BIG_ENDIAN__
+ encoding = unicode::EUTFE_UTF16_BE;
+#else
+ encoding = unicode::EUTFE_UTF16_LE;
+#endif
+
+ append(c, length);
+ }
+
+
+ //! Constructor for copying a UTF-32 string from a pointer.
+ ustring16(const uchar32_t* const c)
+ : array(0), allocated(0), used(0)
+ {
+#if __BIG_ENDIAN__
+ encoding = unicode::EUTFE_UTF16_BE;
+#else
+ encoding = unicode::EUTFE_UTF16_LE;
+#endif
+
+ append(c);
+ }
+
+
+ //! Constructor for copying a UTF-32 from a pointer with a given length.
+ ustring16(const uchar32_t* const c, u32 length)
+ : array(0), allocated(0), used(0)
+ {
+#if __BIG_ENDIAN__
+ encoding = unicode::EUTFE_UTF16_BE;
+#else
+ encoding = unicode::EUTFE_UTF16_LE;
+#endif
+
+ append(c, length);
+ }
+
+
+ //! Constructor for copying a wchar_t string from a pointer.
+ ustring16(const wchar_t* const c)
+ : array(0), allocated(0), used(0)
+ {
+#if __BIG_ENDIAN__
+ encoding = unicode::EUTFE_UTF16_BE;
+#else
+ encoding = unicode::EUTFE_UTF16_LE;
+#endif
+
+ if (sizeof(wchar_t) == 4)
+ append(reinterpret_cast<const uchar32_t* const>(c));
+ else if (sizeof(wchar_t) == 2)
+ append(reinterpret_cast<const uchar16_t* const>(c));
+ else if (sizeof(wchar_t) == 1)
+ append(reinterpret_cast<const uchar8_t* const>(c));
+ }
+
+
+ //! Constructor for copying a wchar_t string from a pointer with a given length.
+ ustring16(const wchar_t* const c, u32 length)
+ : array(0), allocated(0), used(0)
+ {
+#if __BIG_ENDIAN__
+ encoding = unicode::EUTFE_UTF16_BE;
+#else
+ encoding = unicode::EUTFE_UTF16_LE;
+#endif
+
+ if (sizeof(wchar_t) == 4)
+ append(reinterpret_cast<const uchar32_t* const>(c), length);
+ else if (sizeof(wchar_t) == 2)
+ append(reinterpret_cast<const uchar16_t* const>(c), length);
+ else if (sizeof(wchar_t) == 1)
+ append(reinterpret_cast<const uchar8_t* const>(c), length);
+ }
+
+
+#ifdef USTRING_CPP0X
+ //! Constructor for moving a ustring16
+ ustring16(ustring16<TAlloc>&& other)
+ : array(other.array), encoding(other.encoding), allocated(other.allocated), used(other.used)
+ {
+ //std::cout << "MOVE constructor" << std::endl;
+ other.array = 0;
+ other.allocated = 0;
+ other.used = 0;
+ }
+#endif
+
+
+ //! Destructor
+ ~ustring16()
+ {
+ allocator.deallocate(array); // delete [] array;
+ }
+
+
+ //! Assignment operator
+ ustring16& operator=(const ustring16<TAlloc>& other)
+ {
+ if (this == &other)
+ return *this;
+
+ used = other.size_raw();
+ if (used >= allocated)
+ {
+ allocator.deallocate(array); // delete [] array;
+ allocated = used + 1;
+ array = allocator.allocate(used + 1); //new u16[used];
+ }
+
+ const uchar16_t* p = other.c_str();
+ for (u32 i=0; i<=used; ++i, ++p)
+ array[i] = *p;
+
+ array[used] = 0;
+
+ // Validate our new UTF-16 string.
+ validate();
+
+ return *this;
+ }
+
+
+#ifdef USTRING_CPP0X
+ //! Move assignment operator
+ ustring16& operator=(ustring16<TAlloc>&& other)
+ {
+ if (this != &other)
+ {
+ //std::cout << "MOVE operator=" << std::endl;
+ allocator.deallocate(array);
+
+ array = other.array;
+ allocated = other.allocated;
+ encoding = other.encoding;
+ used = other.used;
+ other.array = 0;
+ other.used = 0;
+ }
+ return *this;
+ }
+#endif
+
+
+ //! Assignment operator for other string types
+ template <class B, class A>
+ ustring16<TAlloc>& operator=(const string<B, A>& other)
+ {
+ *this = other.c_str();
+ return *this;
+ }
+
+
+ //! Assignment operator for UTF-8 strings
+ ustring16<TAlloc>& operator=(const uchar8_t* const c)
+ {
+ if (!array)
+ {
+ array = allocator.allocate(1); //new u16[1];
+ allocated = 1;
+ }
+ used = 0;
+ array[used] = 0x0;
+ if (!c) return *this;
+
+ //! Append our string now.
+ append(c);
+ return *this;
+ }
+
+
+ //! Assignment operator for UTF-16 strings
+ ustring16<TAlloc>& operator=(const uchar16_t* const c)
+ {
+ if (!array)
+ {
+ array = allocator.allocate(1); //new u16[1];
+ allocated = 1;
+ }
+ used = 0;
+ array[used] = 0x0;
+ if (!c) return *this;
+
+ //! Append our string now.
+ append(c);
+ return *this;
+ }
+
+
+ //! Assignment operator for UTF-32 strings
+ ustring16<TAlloc>& operator=(const uchar32_t* const c)
+ {
+ if (!array)
+ {
+ array = allocator.allocate(1); //new u16[1];
+ allocated = 1;
+ }
+ used = 0;
+ array[used] = 0x0;
+ if (!c) return *this;
+
+ //! Append our string now.
+ append(c);
+ return *this;
+ }
+
+
+ //! Assignment operator for wchar_t strings.
+ /** Note that this assumes that a correct unicode string is stored in the wchar_t string.
+ Since wchar_t changes depending on its platform, it could either be a UTF-8, -16, or -32 string.
+ This function assumes you are storing the correct unicode encoding inside the wchar_t string. **/
+ ustring16<TAlloc>& operator=(const wchar_t* const c)
+ {
+ if (sizeof(wchar_t) == 4)
+ *this = reinterpret_cast<const uchar32_t* const>(c);
+ else if (sizeof(wchar_t) == 2)
+ *this = reinterpret_cast<const uchar16_t* const>(c);
+ else if (sizeof(wchar_t) == 1)
+ *this = reinterpret_cast<const uchar8_t* const>(c);
+
+ return *this;
+ }
+
+
+ //! Assignment operator for other strings.
+ /** Note that this assumes that a correct unicode string is stored in the string. **/
+ template <class B>
+ ustring16<TAlloc>& operator=(const B* const c)
+ {
+ if (sizeof(B) == 4)
+ *this = reinterpret_cast<const uchar32_t* const>(c);
+ else if (sizeof(B) == 2)
+ *this = reinterpret_cast<const uchar16_t* const>(c);
+ else if (sizeof(B) == 1)
+ *this = reinterpret_cast<const uchar8_t* const>(c);
+
+ return *this;
+ }
+
+
+ //! Direct access operator
+ access operator [](const u32 index)
+ {
+ _IRR_DEBUG_BREAK_IF(index>=size()) // bad index
+ iterator iter(*this, index);
+ return iter.operator*();
+ }
+
+
+ //! Direct access operator
+ const access operator [](const u32 index) const
+ {
+ _IRR_DEBUG_BREAK_IF(index>=size()) // bad index
+ const_iterator iter(*this, index);
+ return iter.operator*();
+ }
+
+
+ //! Equality operator
+ bool operator ==(const uchar16_t* const str) const
+ {
+ if (!str)
+ return false;
+
+ u32 i;
+ for(i=0; array[i] && str[i]; ++i)
+ if (array[i] != str[i])
+ return false;
+
+ return !array[i] && !str[i];
+ }
+
+
+ //! Equality operator
+ bool operator ==(const ustring16<TAlloc>& other) const
+ {
+ for(u32 i=0; array[i] && other.array[i]; ++i)
+ if (array[i] != other.array[i])
+ return false;
+
+ return used == other.used;
+ }
+
+
+ //! Is smaller comparator
+ bool operator <(const ustring16<TAlloc>& other) const
+ {
+ for(u32 i=0; array[i] && other.array[i]; ++i)
+ {
+ s32 diff = array[i] - other.array[i];
+ if ( diff )
+ return diff < 0;
+ }
+
+ return used < other.used;
+ }
+
+
+ //! Inequality operator
+ bool operator !=(const uchar16_t* const str) const
+ {
+ return !(*this == str);
+ }
+
+
+ //! Inequality operator
+ bool operator !=(const ustring16<TAlloc>& other) const
+ {
+ return !(*this == other);
+ }
+
+
+ //! Returns the length of a ustring16 in full characters.
+ //! \return Length of a ustring16 in full characters.
+ u32 size() const
+ {
+ const_iterator i(*this, 0);
+ u32 pos = 0;
+ while (!i.atEnd())
+ {
+ ++i;
+ ++pos;
+ }
+ return pos;
+ }
+
+
+ //! Informs if the ustring is empty or not.
+ //! \return True if the ustring is empty, false if not.
+ bool empty() const
+ {
+ return (size_raw() == 0);
+ }
+
+
+ //! Returns a pointer to the raw UTF-16 string data.
+ //! \return pointer to C-style NUL terminated array of UTF-16 code points.
+ const uchar16_t* c_str() const
+ {
+ return array;
+ }
+
+
+ //! Compares the first n characters of this string with another.
+ //! \param other Other string to compare to.
+ //! \param n Number of characters to compare.
+ //! \return True if the n first characters of both strings are equal.
+ bool equalsn(const ustring16<TAlloc>& other, u32 n) const
+ {
+ u32 i;
+ const uchar16_t* oa = other.c_str();
+ for(i=0; array[i] && oa[i] && i < n; ++i)
+ if (array[i] != oa[i])
+ return false;
+
+ // if one (or both) of the strings was smaller then they
+ // are only equal if they have the same length
+ return (i == n) || (used == other.used);
+ }
+
+
+ //! Compares the first n characters of this string with another.
+ //! \param str Other string to compare to.
+ //! \param n Number of characters to compare.
+ //! \return True if the n first characters of both strings are equal.
+ bool equalsn(const uchar16_t* const str, u32 n) const
+ {
+ if (!str)
+ return false;
+ u32 i;
+ for(i=0; array[i] && str[i] && i < n; ++i)
+ if (array[i] != str[i])
+ return false;
+
+ // if one (or both) of the strings was smaller then they
+ // are only equal if they have the same length
+ return (i == n) || (array[i] == 0 && str[i] == 0);
+ }
+
+
+ //! Appends a character to this ustring16
+ //! \param character The character to append.
+ //! \return A reference to our current string.
+ ustring16<TAlloc>& append(uchar32_t character)
+ {
+ if (used + 2 >= allocated)
+ reallocate(used + 2);
+
+ if (character > 0xFFFF)
+ {
+ used += 2;
+
+ // character will be multibyte, so split it up into a surrogate pair.
+ uchar16_t x = static_cast<uchar16_t>(character);
+ uchar16_t vh = UTF16_HI_SURROGATE | ((((character >> 16) & ((1 << 5) - 1)) - 1) << 6) | (x >> 10);
+ uchar16_t vl = UTF16_LO_SURROGATE | (x & ((1 << 10) - 1));
+ array[used-2] = vh;
+ array[used-1] = vl;
+ }
+ else
+ {
+ ++used;
+ array[used-1] = character;
+ }
+ array[used] = 0;
+
+ return *this;
+ }
+
+
+ //! Appends a UTF-8 string to this ustring16
+ //! \param other The UTF-8 string to append.
+ //! \param length The length of the string to append.
+ //! \return A reference to our current string.
+ ustring16<TAlloc>& append(const uchar8_t* const other, u32 length=0xffffffff)
+ {
+ if (!other)
+ return *this;
+
+ // Determine if the string is long enough for a BOM.
+ u32 len = 0;
+ const uchar8_t* p = other;
+ do
+ {
+ ++len;
+ } while (*p++ && len < unicode::BOM_ENCODE_UTF8_LEN);
+
+ // Check for BOM.
+ unicode::EUTF_ENCODE c_bom = unicode::EUTFE_NONE;
+ if (len == unicode::BOM_ENCODE_UTF8_LEN)
+ {
+ if (memcmp(other, unicode::BOM_ENCODE_UTF8, unicode::BOM_ENCODE_UTF8_LEN) == 0)
+ c_bom = unicode::EUTFE_UTF8;
+ }
+
+ // If a BOM was found, don't include it in the string.
+ const uchar8_t* c2 = other;
+ if (c_bom != unicode::EUTFE_NONE)
+ {
+ c2 = other + unicode::BOM_UTF8_LEN;
+ length -= unicode::BOM_UTF8_LEN;
+ }
+
+ // Calculate the size of the string to read in.
+ len = 0;
+ p = c2;
+ do
+ {
+ ++len;
+ } while(*p++ && len < length);
+ if (len > length)
+ len = length;
+
+ // If we need to grow the array, do it now.
+ if (used + len >= allocated)
+ reallocate(used + (len * 2));
+ u32 start = used;
+
+ // Convert UTF-8 to UTF-16.
+ u32 pos = start;
+ for (u32 l = 0; l<len;)
+ {
+ ++used;
+ if (((c2[l] >> 6) & 0x03) == 0x02)
+ { // Invalid continuation byte.
+ array[pos++] = unicode::UTF_REPLACEMENT_CHARACTER;
+ ++l;
+ }
+ else if (c2[l] == 0xC0 || c2[l] == 0xC1)
+ { // Invalid byte - overlong encoding.
+ array[pos++] = unicode::UTF_REPLACEMENT_CHARACTER;
+ ++l;
+ }
+ else if ((c2[l] & 0xF8) == 0xF0)
+ { // 4 bytes UTF-8, 2 bytes UTF-16.
+ // Check for a full string.
+ if ((l + 3) >= len)
+ {
+ array[pos++] = unicode::UTF_REPLACEMENT_CHARACTER;
+ l += 3;
+ break;
+ }
+
+ // Validate.
+ bool valid = true;
+ u8 l2 = 0;
+ if (valid && (((c2[l+1] >> 6) & 0x03) == 0x02)) ++l2; else valid = false;
+ if (valid && (((c2[l+2] >> 6) & 0x03) == 0x02)) ++l2; else valid = false;
+ if (valid && (((c2[l+3] >> 6) & 0x03) == 0x02)) ++l2; else valid = false;
+ if (!valid)
+ {
+ array[pos++] = unicode::UTF_REPLACEMENT_CHARACTER;
+ l += l2;
+ continue;
+ }
+
+ // Decode.
+ uchar8_t b1 = ((c2[l] & 0x7) << 2) | ((c2[l+1] >> 4) & 0x3);
+ uchar8_t b2 = ((c2[l+1] & 0xF) << 4) | ((c2[l+2] >> 2) & 0xF);
+ uchar8_t b3 = ((c2[l+2] & 0x3) << 6) | (c2[l+3] & 0x3F);
+ uchar32_t v = b3 | ((uchar32_t)b2 << 8) | ((uchar32_t)b1 << 16);
+
+ // Split v up into a surrogate pair.
+ uchar16_t x = static_cast<uchar16_t>(v);
+ uchar16_t vh = UTF16_HI_SURROGATE | ((((v >> 16) & ((1 << 5) - 1)) - 1) << 6) | (x >> 10);
+ uchar16_t vl = UTF16_LO_SURROGATE | (x & ((1 << 10) - 1));
+
+ array[pos++] = vh;
+ array[pos++] = vl;
+ l += 4;
+ ++used; // Using two shorts this time, so increase used by 1.
+ }
+ else if ((c2[l] & 0xF0) == 0xE0)
+ { // 3 bytes UTF-8, 1 byte UTF-16.
+ // Check for a full string.
+ if ((l + 2) >= len)
+ {
+ array[pos++] = unicode::UTF_REPLACEMENT_CHARACTER;
+ l += 2;
+ break;
+ }
+
+ // Validate.
+ bool valid = true;
+ u8 l2 = 0;
+ if (valid && (((c2[l+1] >> 6) & 0x03) == 0x02)) ++l2; else valid = false;
+ if (valid && (((c2[l+2] >> 6) & 0x03) == 0x02)) ++l2; else valid = false;
+ if (!valid)
+ {
+ array[pos++] = unicode::UTF_REPLACEMENT_CHARACTER;
+ l += l2;
+ continue;
+ }
+
+ // Decode.
+ uchar8_t b1 = ((c2[l] & 0xF) << 4) | ((c2[l+1] >> 2) & 0xF);
+ uchar8_t b2 = ((c2[l+1] & 0x3) << 6) | (c2[l+2] & 0x3F);
+ uchar16_t ch = b2 | ((uchar16_t)b1 << 8);
+ array[pos++] = ch;
+ l += 3;
+ }
+ else if ((c2[l] & 0xE0) == 0xC0)
+ { // 2 bytes UTF-8, 1 byte UTF-16.
+ // Check for a full string.
+ if ((l + 1) >= len)
+ {
+ array[pos++] = unicode::UTF_REPLACEMENT_CHARACTER;
+ l += 1;
+ break;
+ }
+
+ // Validate.
+ if (((c2[l+1] >> 6) & 0x03) != 0x02)
+ {
+ array[pos++] = unicode::UTF_REPLACEMENT_CHARACTER;
+ ++l;
+ continue;
+ }
+
+ // Decode.
+ uchar8_t b1 = (c2[l] >> 2) & 0x7;
+ uchar8_t b2 = ((c2[l] & 0x3) << 6) | (c2[l+1] & 0x3F);
+ uchar16_t ch = b2 | ((uchar16_t)b1 << 8);
+ array[pos++] = ch;
+ l += 2;
+ }
+ else
+ { // 1 byte UTF-8, 1 byte UTF-16.
+ // Validate.
+ if (c2[l] > 0x7F)
+ { // Values above 0xF4 are restricted and aren't used. By now, anything above 0x7F is invalid.
+ array[pos++] = unicode::UTF_REPLACEMENT_CHARACTER;
+ }
+ else array[pos++] = static_cast<uchar16_t>(c2[l]);
+ ++l;
+ }
+ }
+ array[used] = 0;
+
+ // Validate our new UTF-16 string.
+ validate();
+
+ return *this;
+ }
+
+
+ //! Appends a UTF-16 string to this ustring16
+ //! \param other The UTF-16 string to append.
+ //! \param length The length of the string to append.
+ //! \return A reference to our current string.
+ ustring16<TAlloc>& append(const uchar16_t* const other, u32 length=0xffffffff)
+ {
+ if (!other)
+ return *this;
+
+ // Determine if the string is long enough for a BOM.
+ u32 len = 0;
+ const uchar16_t* p = other;
+ do
+ {
+ ++len;
+ } while (*p++ && len < unicode::BOM_ENCODE_UTF16_LEN);
+
+ // Check for the BOM to determine the string's endianness.
+ unicode::EUTF_ENDIAN c_end = unicode::EUTFEE_NATIVE;
+ if (memcmp(other, unicode::BOM_ENCODE_UTF16_LE, unicode::BOM_ENCODE_UTF16_LEN) == 0)
+ c_end = unicode::EUTFEE_LITTLE;
+ else if (memcmp(other, unicode::BOM_ENCODE_UTF16_BE, unicode::BOM_ENCODE_UTF16_LEN) == 0)
+ c_end = unicode::EUTFEE_BIG;
+
+ // If a BOM was found, don't include it in the string.
+ const uchar16_t* c2 = other;
+ if (c_end != unicode::EUTFEE_NATIVE)
+ {
+ c2 = other + unicode::BOM_UTF16_LEN;
+ length -= unicode::BOM_UTF16_LEN;
+ }
+
+ // Calculate the size of the string to read in.
+ len = 0;
+ p = c2;
+ do
+ {
+ ++len;
+ } while(*p++ && len < length);
+ if (len > length)
+ len = length;
+
+ // If we need to grow the size of the array, do it now.
+ if (used + len >= allocated)
+ reallocate(used + (len * 2));
+ u32 start = used;
+ used += len;
+
+ // Copy the string now.
+ unicode::EUTF_ENDIAN m_end = getEndianness();
+ for (u32 l = start; l < start + len; ++l)
+ {
+ array[l] = (uchar16_t)c2[l];
+ if (c_end != unicode::EUTFEE_NATIVE && c_end != m_end)
+ array[l] = unicode::swapEndian16(array[l]);
+ }
+
+ array[used] = 0;
+
+ // Validate our new UTF-16 string.
+ validate();
+ return *this;
+ }
+
+
+ //! Appends a UTF-32 string to this ustring16
+ //! \param other The UTF-32 string to append.
+ //! \param length The length of the string to append.
+ //! \return A reference to our current string.
+ ustring16<TAlloc>& append(const uchar32_t* const other, u32 length=0xffffffff)
+ {
+ if (!other)
+ return *this;
+
+ // Check for the BOM to determine the string's endianness.
+ unicode::EUTF_ENDIAN c_end = unicode::EUTFEE_NATIVE;
+ if (memcmp(other, unicode::BOM_ENCODE_UTF32_LE, unicode::BOM_ENCODE_UTF32_LEN) == 0)
+ c_end = unicode::EUTFEE_LITTLE;
+ else if (memcmp(other, unicode::BOM_ENCODE_UTF32_BE, unicode::BOM_ENCODE_UTF32_LEN) == 0)
+ c_end = unicode::EUTFEE_BIG;
+
+ // If a BOM was found, don't include it in the string.
+ const uchar32_t* c2 = other;
+ if (c_end != unicode::EUTFEE_NATIVE)
+ {
+ c2 = other + unicode::BOM_UTF32_LEN;
+ length -= unicode::BOM_UTF32_LEN;
+ }
+
+ // Calculate the size of the string to read in.
+ u32 len = 0;
+ const uchar32_t* p = c2;
+ do
+ {
+ ++len;
+ } while(*p++ && len < length);
+ if (len > length)
+ len = length;
+
+ // If we need to grow the size of the array, do it now.
+ // In case all of the UTF-32 string is split into surrogate pairs, do len * 2.
+ if (used + (len * 2) >= allocated)
+ reallocate(used + ((len * 2) * 2));
+ u32 start = used;
+
+ // Convert UTF-32 to UTF-16.
+ unicode::EUTF_ENDIAN m_end = getEndianness();
+ u32 pos = start;
+ for (u32 l = 0; l<len; ++l)
+ {
+ ++used;
+
+ uchar32_t ch = c2[l];
+ if (c_end != unicode::EUTFEE_NATIVE && c_end != m_end)
+ ch = unicode::swapEndian32(ch);
+
+ if (ch > 0xFFFF)
+ {
+ // Split ch up into a surrogate pair as it is over 16 bits long.
+ uchar16_t x = static_cast<uchar16_t>(ch);
+ uchar16_t vh = UTF16_HI_SURROGATE | ((((ch >> 16) & ((1 << 5) - 1)) - 1) << 6) | (x >> 10);
+ uchar16_t vl = UTF16_LO_SURROGATE | (x & ((1 << 10) - 1));
+ array[pos++] = vh;
+ array[pos++] = vl;
+ ++used; // Using two shorts, so increased used again.
+ }
+ else if (ch >= 0xD800 && ch <= 0xDFFF)
+ {
+ // Between possible UTF-16 surrogates (invalid!)
+ array[pos++] = unicode::UTF_REPLACEMENT_CHARACTER;
+ }
+ else array[pos++] = static_cast<uchar16_t>(ch);
+ }
+ array[used] = 0;
+
+ // Validate our new UTF-16 string.
+ validate();
+
+ return *this;
+ }
+
+
+ //! Appends a ustring16 to this ustring16
+ //! \param other The string to append to this one.
+ //! \return A reference to our current string.
+ ustring16<TAlloc>& append(const ustring16<TAlloc>& other)
+ {
+ const uchar16_t* oa = other.c_str();
+
+ u32 len = other.size_raw();
+
+ if (used + len >= allocated)
+ reallocate(used + len);
+
+ for (u32 l=0; l<len; ++l)
+ array[used+l] = oa[l];
+
+ used += len;
+ array[used] = 0;
+
+ return *this;
+ }
+
+
+ //! Appends a certain amount of characters of a ustring16 to this ustring16.
+ //! \param other The string to append to this one.
+ //! \param length How many characters of the other string to add to this one.
+ //! \return A reference to our current string.
+ ustring16<TAlloc>& append(const ustring16<TAlloc>& other, u32 length)
+ {
+ if (other.size() == 0)
+ return *this;
+
+ if (other.size() < length)
+ {
+ append(other);
+ return *this;
+ }
+
+ if (used + length * 2 >= allocated)
+ reallocate(used + length * 2);
+
+ const_iterator iter(other, 0);
+ u32 l = length;
+ while (!iter.atEnd() && l)
+ {
+ uchar32_t c = *iter;
+ append(c);
+ ++iter;
+ --l;
+ }
+
+ return *this;
+ }
+
+
+ //! Reserves some memory.
+ //! \param count The amount of characters to reserve.
+ void reserve(u32 count)
+ {
+ if (count < allocated)
+ return;
+
+ reallocate(count);
+ }
+
+
+ //! Finds first occurrence of character.
+ //! \param c The character to search for.
+ //! \return Position where the character has been found, or -1 if not found.
+ s32 findFirst(uchar32_t c) const
+ {
+ const_iterator i(*this, 0);
+
+ s32 pos = 0;
+ while (!i.atEnd())
+ {
+ uchar32_t t = *i;
+ if (c == t)
+ return pos;
+ ++pos;
+ ++i;
+ }
+
+ return -1;
+ }
+
+ //! Finds first occurrence of a character of a list.
+ //! \param c A list of characters to find. For example if the method should find the first occurrence of 'a' or 'b', this parameter should be "ab".
+ //! \param count The amount of characters in the list. Usually, this should be strlen(c).
+ //! \return Position where one of the characters has been found, or -1 if not found.
+ s32 findFirstChar(const uchar32_t* const c, u32 count=1) const
+ {
+ if (!c || !count)
+ return -1;
+
+ const_iterator i(*this, 0);
+
+ s32 pos = 0;
+ while (!i.atEnd())
+ {
+ uchar32_t t = *i;
+ for (u32 j=0; j<count; ++j)
+ if (t == c[j])
+ return pos;
+ ++pos;
+ ++i;
+ }
+
+ return -1;
+ }
+
+
+ //! Finds first position of a character not in a given list.
+ //! \param c A list of characters to NOT find. For example if the method should find the first occurrence of a character not 'a' or 'b', this parameter should be "ab".
+ //! \param count The amount of characters in the list. Usually, this should be strlen(c).
+ //! \return Position where the character has been found, or -1 if not found.
+ s32 findFirstCharNotInList(const uchar32_t* const c, u32 count=1) const
+ {
+ if (!c || !count)
+ return -1;
+
+ const_iterator i(*this, 0);
+
+ s32 pos = 0;
+ while (!i.atEnd())
+ {
+ uchar32_t t = *i;
+ u32 j;
+ for (j=0; j<count; ++j)
+ if (t == c[j])
+ break;
+
+ if (j==count)
+ return pos;
+ ++pos;
+ ++i;
+ }
+
+ return -1;
+ }
+
+ //! Finds last position of a character not in a given list.
+ //! \param c A list of characters to NOT find. For example if the method should find the first occurrence of a character not 'a' or 'b', this parameter should be "ab".
+ //! \param count The amount of characters in the list. Usually, this should be strlen(c).
+ //! \return Position where the character has been found, or -1 if not found.
+ s32 findLastCharNotInList(const uchar32_t* const c, u32 count=1) const
+ {
+ if (!c || !count)
+ return -1;
+
+ const_iterator i(end());
+ --i;
+
+ s32 pos = size() - 1;
+ while (!i.atStart())
+ {
+ uchar32_t t = *i;
+ u32 j;
+ for (j=0; j<count; ++j)
+ if (t == c[j])
+ break;
+
+ if (j==count)
+ return pos;
+ --pos;
+ --i;
+ }
+
+ return -1;
+ }
+
+ //! Finds next occurrence of character.
+ //! \param c The character to search for.
+ //! \param startPos The position in the string to start searching.
+ //! \return Position where the character has been found, or -1 if not found.
+ s32 findNext(uchar32_t c, u32 startPos) const
+ {
+ const_iterator i(*this, startPos);
+
+ s32 pos = startPos;
+ while (!i.atEnd())
+ {
+ uchar32_t t = *i;
+ if (t == c)
+ return pos;
+ ++pos;
+ ++i;
+ }
+
+ return -1;
+ }
+
+
+ //! Finds last occurrence of character.
+ //! \param c The character to search for.
+ //! \param start The start position of the reverse search ( default = -1, on end ).
+ //! \return Position where the character has been found, or -1 if not found.
+ s32 findLast(uchar32_t c, s32 start = -1) const
+ {
+ u32 s = size();
+ start = core::clamp ( start < 0 ? (s32)s : start, 0, (s32)s ) - 1;
+
+ const_iterator i(*this, start);
+ u32 pos = start;
+ while (!i.atStart())
+ {
+ uchar32_t t = *i;
+ if (t == c)
+ return pos;
+ --pos;
+ --i;
+ }
+
+ return -1;
+ }
+
+ //! Finds last occurrence of a character in a list.
+ //! \param c A list of strings to find. For example if the method should find the last occurrence of 'a' or 'b', this parameter should be "ab".
+ //! \param count The amount of characters in the list. Usually, this should be strlen(c).
+ //! \return Position where one of the characters has been found, or -1 if not found.
+ s32 findLastChar(const uchar32_t* const c, u32 count=1) const
+ {
+ if (!c || !count)
+ return -1;
+
+ const_iterator i(end());
+ --i;
+
+ s32 pos = size();
+ while (!i.atStart())
+ {
+ uchar32_t t = *i;
+ for (u32 j=0; j<count; ++j)
+ if (t == c[j])
+ return pos;
+ --pos;
+ --i;
+ }
+
+ return -1;
+ }
+
+
+ //! Finds another ustring16 in this ustring16.
+ //! \param str The string to find.
+ //! \param start The start position of the search.
+ //! \return Positions where the ustring16 has been found, or -1 if not found.
+ s32 find(const ustring16<TAlloc>& str, const u32 start = 0) const
+ {
+ u32 my_size = size();
+ u32 their_size = str.size();
+
+ if (their_size == 0 || my_size - start < their_size)
+ return -1;
+
+ const_iterator i(*this, start);
+
+ s32 pos = start;
+ while (!i.atEnd())
+ {
+ const_iterator i2(i);
+ const_iterator j(str, 0);
+ uchar32_t t1 = (uchar32_t)*i2;
+ uchar32_t t2 = (uchar32_t)*j;
+ while (t1 == t2)
+ {
+ ++i2;
+ ++j;
+ if (j.atEnd())
+ return pos;
+ t1 = (uchar32_t)*i2;
+ t2 = (uchar32_t)*j;
+ }
+ ++i;
+ ++pos;
+ }
+
+ return -1;
+ }
+
+
+ //! Finds another ustring16 in this ustring16.
+ //! \param str The string to find.
+ //! \param start The start position of the search.
+ //! \return Positions where the string has been found, or -1 if not found.
+ s32 find_raw(const ustring16<TAlloc>& str, const u32 start = 0) const
+ {
+ const uchar16_t* data = str.c_str();
+ if (data && *data)
+ {
+ u32 len = 0;
+
+ while (data[len])
+ ++len;
+
+ if (len > used)
+ return -1;
+
+ for (u32 i=start; i<=used-len; ++i)
+ {
+ u32 j=0;
+
+ while(data[j] && array[i+j] == data[j])
+ ++j;
+
+ if (!data[j])
+ return i;
+ }
+ }
+
+ return -1;
+ }
+
+
+ //! Returns a substring.
+ //! \param begin: Start of substring.
+ //! \param length: Length of substring.
+ //! \return A reference to our current string.
+ ustring16<TAlloc> subString(u32 begin, s32 length) const
+ {
+ u32 len = size();
+ // if start after ustring16
+ // or no proper substring length
+ if ((length <= 0) || (begin>=len))
+ return ustring16<TAlloc>("");
+ // clamp length to maximal value
+ if ((length+begin) > len)
+ length = len-begin;
+
+ ustring16<TAlloc> o;
+ o.reserve((length+1) * 2);
+
+ const_iterator i(*this, begin);
+ while (!i.atEnd() && length)
+ {
+ o.append(*i);
+ ++i;
+ --length;
+ }
+
+ return o;
+ }
+
+
+ //! Appends a character to this ustring16.
+ //! \param c Character to append.
+ //! \return A reference to our current string.
+ ustring16<TAlloc>& operator += (char c)
+ {
+ append((uchar32_t)c);
+ return *this;
+ }
+
+
+ //! Appends a character to this ustring16.
+ //! \param c Character to append.
+ //! \return A reference to our current string.
+ ustring16<TAlloc>& operator += (uchar32_t c)
+ {
+ append(c);
+ return *this;
+ }
+
+
+ //! Appends a number to this ustring16.
+ //! \param c Number to append.
+ //! \return A reference to our current string.
+ ustring16<TAlloc>& operator += (short c)
+ {
+ append(core::stringc(c));
+ return *this;
+ }
+
+
+ //! Appends a number to this ustring16.
+ //! \param c Number to append.
+ //! \return A reference to our current string.
+ ustring16<TAlloc>& operator += (unsigned short c)
+ {
+ append(core::stringc(c));
+ return *this;
+ }
+
+
+#ifdef USTRING_CPP0X_NEWLITERALS
+ //! Appends a number to this ustring16.
+ //! \param c Number to append.
+ //! \return A reference to our current string.
+ ustring16<TAlloc>& operator += (int c)
+ {
+ append(core::stringc(c));
+ return *this;
+ }
+
+
+ //! Appends a number to this ustring16.
+ //! \param c Number to append.
+ //! \return A reference to our current string.
+ ustring16<TAlloc>& operator += (unsigned int c)
+ {
+ append(core::stringc(c));
+ return *this;
+ }
+#endif
+
+
+ //! Appends a number to this ustring16.
+ //! \param c Number to append.
+ //! \return A reference to our current string.
+ ustring16<TAlloc>& operator += (long c)
+ {
+ append(core::stringc(c));
+ return *this;
+ }
+
+
+ //! Appends a number to this ustring16.
+ //! \param c Number to append.
+ //! \return A reference to our current string.
+ ustring16<TAlloc>& operator += (unsigned long c)
+ {
+ append(core::stringc(c));
+ return *this;
+ }
+
+
+ //! Appends a number to this ustring16.
+ //! \param c Number to append.
+ //! \return A reference to our current string.
+ ustring16<TAlloc>& operator += (double c)
+ {
+ append(core::stringc(c));
+ return *this;
+ }
+
+
+ //! Appends a char ustring16 to this ustring16.
+ //! \param c Char ustring16 to append.
+ //! \return A reference to our current string.
+ ustring16<TAlloc>& operator += (const uchar16_t* const c)
+ {
+ append(c);
+ return *this;
+ }
+
+
+ //! Appends a ustring16 to this ustring16.
+ //! \param other ustring16 to append.
+ //! \return A reference to our current string.
+ ustring16<TAlloc>& operator += (const ustring16<TAlloc>& other)
+ {
+ append(other);
+ return *this;
+ }
+
+
+ //! Replaces all characters of a given type with another one.
+ //! \param toReplace Character to replace.
+ //! \param replaceWith Character replacing the old one.
+ //! \return A reference to our current string.
+ ustring16<TAlloc>& replace(uchar32_t toReplace, uchar32_t replaceWith)
+ {
+ iterator i(*this, 0);
+ while (!i.atEnd())
+ {
+ typename ustring16<TAlloc>::access a = *i;
+ if ((uchar32_t)a == toReplace)
+ a = replaceWith;
+ ++i;
+ }
+ return *this;
+ }
+
+
+ //! Replaces all instances of a string with another one.
+ //! \param toReplace The string to replace.
+ //! \param replaceWith The string replacing the old one.
+ //! \return A reference to our current string.
+ ustring16<TAlloc>& replace(const ustring16<TAlloc>& toReplace, const ustring16<TAlloc>& replaceWith)
+ {
+ if (toReplace.size() == 0)
+ return *this;
+
+ const uchar16_t* other = toReplace.c_str();
+ const uchar16_t* replace = replaceWith.c_str();
+ const u32 other_size = toReplace.size_raw();
+ const u32 replace_size = replaceWith.size_raw();
+
+ // Determine the delta. The algorithm will change depending on the delta.
+ s32 delta = replace_size - other_size;
+
+ // A character for character replace. The string will not shrink or grow.
+ if (delta == 0)
+ {
+ s32 pos = 0;
+ while ((pos = find_raw(other, pos)) != -1)
+ {
+ for (u32 i = 0; i < replace_size; ++i)
+ array[pos + i] = replace[i];
+ ++pos;
+ }
+ return *this;
+ }
+
+ // We are going to be removing some characters. The string will shrink.
+ if (delta < 0)
+ {
+ u32 i = 0;
+ for (u32 pos = 0; pos <= used; ++i, ++pos)
+ {
+ // Is this potentially a match?
+ if (array[pos] == *other)
+ {
+ // Check to see if we have a match.
+ u32 j;
+ for (j = 0; j < other_size; ++j)
+ {
+ if (array[pos + j] != other[j])
+ break;
+ }
+
+ // If we have a match, replace characters.
+ if (j == other_size)
+ {
+ for (j = 0; j < replace_size; ++j)
+ array[i + j] = replace[j];
+ i += replace_size - 1;
+ pos += other_size - 1;
+ continue;
+ }
+ }
+
+ // No match found, just copy characters.
+ array[i - 1] = array[pos];
+ }
+ array[i] = 0;
+ used = i;
+
+ return *this;
+ }
+
+ // We are going to be adding characters, so the string size will increase.
+ // Count the number of times toReplace exists in the string so we can allocate the new size.
+ u32 find_count = 0;
+ s32 pos = 0;
+ while ((pos = find_raw(other, pos)) != -1)
+ {
+ ++find_count;
+ ++pos;
+ }
+
+ // Re-allocate the string now, if needed.
+ u32 len = delta * find_count;
+ if (used + len >= allocated)
+ reallocate(used + len);
+
+ // Start replacing.
+ pos = 0;
+ while ((pos = find_raw(other, pos)) != -1)
+ {
+ uchar16_t* start = array + pos + other_size - 1;
+ uchar16_t* ptr = array + used;
+ uchar16_t* end = array + used + delta;
+
+ // Shift characters to make room for the string.
+ while (ptr != start)
+ {
+ *end = *ptr;
+ --ptr;
+ --end;
+ }
+
+ // Add the new string now.
+ for (u32 i = 0; i < replace_size; ++i)
+ array[pos + i] = replace[i];
+
+ pos += replace_size;
+ used += delta;
+ }
+
+ // Terminate the string and return ourself.
+ array[used] = 0;
+ return *this;
+ }
+
+
+ //! Removes characters from a ustring16..
+ //! \param c The character to remove.
+ //! \return A reference to our current string.
+ ustring16<TAlloc>& remove(uchar32_t c)
+ {
+ u32 pos = 0;
+ u32 found = 0;
+ u32 len = (c > 0xFFFF ? 2 : 1); // Remove characters equal to the size of c as a UTF-16 character.
+ for (u32 i=0; i<=used; ++i)
+ {
+ uchar32_t uc32 = 0;
+ if (!UTF16_IS_SURROGATE_HI(array[i]))
+ uc32 |= array[i];
+ else if (i + 1 <= used)
+ {
+ // Convert the surrogate pair into a single UTF-32 character.
+ uc32 = unicode::toUTF32(array[i], array[i + 1]);
+ }
+ u32 len2 = (uc32 > 0xFFFF ? 2 : 1);
+
+ if (uc32 == c)
+ {
+ found += len;
+ continue;
+ }
+
+ array[pos++] = array[i];
+ if (len2 == 2)
+ array[pos++] = array[++i];
+ }
+ used -= found;
+ array[used] = 0;
+ return *this;
+ }
+
+
+ //! Removes a ustring16 from the ustring16.
+ //! \param toRemove The string to remove.
+ //! \return A reference to our current string.
+ ustring16<TAlloc>& remove(const ustring16<TAlloc>& toRemove)
+ {
+ u32 size = toRemove.size_raw();
+ if (size == 0) return *this;
+
+ const uchar16_t* tra = toRemove.c_str();
+ u32 pos = 0;
+ u32 found = 0;
+ for (u32 i=0; i<=used; ++i)
+ {
+ u32 j = 0;
+ while (j < size)
+ {
+ if (array[i + j] != tra[j])
+ break;
+ ++j;
+ }
+ if (j == size)
+ {
+ found += size;
+ i += size - 1;
+ continue;
+ }
+
+ array[pos++] = array[i];
+ }
+ used -= found;
+ array[used] = 0;
+ return *this;
+ }
+
+
+ //! Removes characters from the ustring16.
+ //! \param characters The characters to remove.
+ //! \return A reference to our current string.
+ ustring16<TAlloc>& removeChars(const ustring16<TAlloc>& characters)
+ {
+ if (characters.size_raw() == 0)
+ return *this;
+
+ u32 pos = 0;
+ u32 found = 0;
+ const_iterator iter(characters);
+ for (u32 i=0; i<=used; ++i)
+ {
+ uchar32_t uc32 = 0;
+ if (!UTF16_IS_SURROGATE_HI(array[i]))
+ uc32 |= array[i];
+ else if (i + 1 <= used)
+ {
+ // Convert the surrogate pair into a single UTF-32 character.
+ uc32 = unicode::toUTF32(array[i], array[i+1]);
+ }
+ u32 len2 = (uc32 > 0xFFFF ? 2 : 1);
+
+ bool cont = false;
+ iter.toStart();
+ while (!iter.atEnd())
+ {
+ uchar32_t c = *iter;
+ if (uc32 == c)
+ {
+ found += (c > 0xFFFF ? 2 : 1); // Remove characters equal to the size of c as a UTF-16 character.
+ ++i;
+ cont = true;
+ break;
+ }
+ ++iter;
+ }
+ if (cont) continue;
+
+ array[pos++] = array[i];
+ if (len2 == 2)
+ array[pos++] = array[++i];
+ }
+ used -= found;
+ array[used] = 0;
+ return *this;
+ }
+
+
+ //! Trims the ustring16.
+ //! Removes the specified characters (by default, Latin-1 whitespace) from the begining and the end of the ustring16.
+ //! \param whitespace The characters that are to be considered as whitespace.
+ //! \return A reference to our current string.
+ ustring16<TAlloc>& trim(const ustring16<TAlloc>& whitespace = " \t\n\r")
+ {
+ core::array<uchar32_t> utf32white = whitespace.toUTF32();
+
+ // find start and end of the substring without the specified characters
+ const s32 begin = findFirstCharNotInList(utf32white.const_pointer(), whitespace.used + 1);
+ if (begin == -1)
+ return (*this="");
+
+ const s32 end = findLastCharNotInList(utf32white.const_pointer(), whitespace.used + 1);
+
+ return (*this = subString(begin, (end +1) - begin));
+ }
+
+
+ //! Erases a character from the ustring16.
+ //! May be slow, because all elements following after the erased element have to be copied.
+ //! \param index Index of element to be erased.
+ //! \return A reference to our current string.
+ ustring16<TAlloc>& erase(u32 index)
+ {
+ _IRR_DEBUG_BREAK_IF(index>used) // access violation
+
+ iterator i(*this, index);
+
+ uchar32_t t = *i;
+ u32 len = (t > 0xFFFF ? 2 : 1);
+
+ for (u32 j = static_cast<u32>(i.getPos()) + len; j <= used; ++j)
+ array[j - len] = array[j];
+
+ used -= len;
+ array[used] = 0;
+
+ return *this;
+ }
+
+
+ //! Validate the existing ustring16, checking for valid surrogate pairs and checking for proper termination.
+ //! \return A reference to our current string.
+ ustring16<TAlloc>& validate()
+ {
+ // Validate all unicode characters.
+ for (u32 i=0; i<allocated; ++i)
+ {
+ // Terminate on existing null.
+ if (array[i] == 0)
+ {
+ used = i;
+ return *this;
+ }
+ if (UTF16_IS_SURROGATE(array[i]))
+ {
+ if (((i+1) >= allocated) || UTF16_IS_SURROGATE_LO(array[i]))
+ array[i] = unicode::UTF_REPLACEMENT_CHARACTER;
+ else if (UTF16_IS_SURROGATE_HI(array[i]) && !UTF16_IS_SURROGATE_LO(array[i+1]))
+ array[i] = unicode::UTF_REPLACEMENT_CHARACTER;
+ ++i;
+ }
+ if (array[i] >= 0xFDD0 && array[i] <= 0xFDEF)
+ array[i] = unicode::UTF_REPLACEMENT_CHARACTER;
+ }
+
+ // terminate
+ used = 0;
+ if (allocated > 0)
+ {
+ used = allocated - 1;
+ array[used] = 0;
+ }
+ return *this;
+ }
+
+
+ //! Gets the last char of the ustring16, or 0.
+ //! \return The last char of the ustring16, or 0.
+ uchar32_t lastChar() const
+ {
+ if (used < 1)
+ return 0;
+
+ if (UTF16_IS_SURROGATE_LO(array[used-1]))
+ {
+ // Make sure we have a paired surrogate.
+ if (used < 2)
+ return 0;
+
+ // Check for an invalid surrogate.
+ if (!UTF16_IS_SURROGATE_HI(array[used-2]))
+ return 0;
+
+ // Convert the surrogate pair into a single UTF-32 character.
+ return unicode::toUTF32(array[used-2], array[used-1]);
+ }
+ else
+ {
+ return array[used-1];
+ }
+ }
+
+
+ //! Split the ustring16 into parts.
+ /** This method will split a ustring16 at certain delimiter characters
+ into the container passed in as reference. The type of the container
+ has to be given as template parameter. It must provide a push_back and
+ a size method.
+ \param ret The result container
+ \param c C-style ustring16 of delimiter characters
+ \param count Number of delimiter characters
+ \param ignoreEmptyTokens Flag to avoid empty substrings in the result
+ container. If two delimiters occur without a character in between, an
+ empty substring would be placed in the result. If this flag is set,
+ only non-empty strings are stored.
+ \param keepSeparators Flag which allows to add the separator to the
+ result ustring16. If this flag is true, the concatenation of the
+ substrings results in the original ustring16. Otherwise, only the
+ characters between the delimiters are returned.
+ \return The number of resulting substrings
+ */
+ template<class container>
+ u32 split(container& ret, const uchar32_t* const c, u32 count=1, bool ignoreEmptyTokens=true, bool keepSeparators=false) const
+ {
+ if (!c)
+ return 0;
+
+ const_iterator i(*this);
+ const u32 oldSize=ret.size();
+ u32 pos = 0;
+ u32 lastpos = 0;
+ u32 lastpospos = 0;
+ bool lastWasSeparator = false;
+ while (!i.atEnd())
+ {
+ uchar32_t ch = *i;
+ bool foundSeparator = false;
+ for (u32 j=0; j<count; ++j)
+ {
+ if (ch == c[j])
+ {
+ if ((!ignoreEmptyTokens || pos - lastpos != 0) &&
+ !lastWasSeparator)
+ ret.push_back(ustring16<TAlloc>(&array[lastpospos], pos - lastpos));
+ foundSeparator = true;
+ lastpos = (keepSeparators ? pos : pos + 1);
+ lastpospos = (keepSeparators ? i.getPos() : i.getPos() + 1);
+ break;
+ }
+ }
+ lastWasSeparator = foundSeparator;
+ ++pos;
+ ++i;
+ }
+ u32 s = size() + 1;
+ if (s > lastpos)
+ ret.push_back(ustring16<TAlloc>(&array[lastpospos], s - lastpos));
+ return ret.size()-oldSize;
+ }
+
+
+ //! Split the ustring16 into parts.
+ /** This method will split a ustring16 at certain delimiter characters
+ into the container passed in as reference. The type of the container
+ has to be given as template parameter. It must provide a push_back and
+ a size method.
+ \param ret The result container
+ \param c A unicode string of delimiter characters
+ \param ignoreEmptyTokens Flag to avoid empty substrings in the result
+ container. If two delimiters occur without a character in between, an
+ empty substring would be placed in the result. If this flag is set,
+ only non-empty strings are stored.
+ \param keepSeparators Flag which allows to add the separator to the
+ result ustring16. If this flag is true, the concatenation of the
+ substrings results in the original ustring16. Otherwise, only the
+ characters between the delimiters are returned.
+ \return The number of resulting substrings
+ */
+ template<class container>
+ u32 split(container& ret, const ustring16<TAlloc>& c, bool ignoreEmptyTokens=true, bool keepSeparators=false) const
+ {
+ core::array<uchar32_t> v = c.toUTF32();
+ return split(ret, v.pointer(), v.size(), ignoreEmptyTokens, keepSeparators);
+ }
+
+
+ //! Gets the size of the allocated memory buffer for the string.
+ //! \return The size of the allocated memory buffer.
+ u32 capacity() const
+ {
+ return allocated;
+ }
+
+
+ //! Returns the raw number of UTF-16 code points in the string which includes the individual surrogates.
+ //! \return The raw number of UTF-16 code points, excluding the trialing NUL.
+ u32 size_raw() const
+ {
+ return used;
+ }
+
+
+ //! Inserts a character into the string.
+ //! \param c The character to insert.
+ //! \param pos The position to insert the character.
+ //! \return A reference to our current string.
+ ustring16<TAlloc>& insert(uchar32_t c, u32 pos)
+ {
+ u8 len = (c > 0xFFFF ? 2 : 1);
+
+ if (used + len >= allocated)
+ reallocate(used + len);
+
+ used += len;
+
+ iterator iter(*this, pos);
+ for (u32 i = used - 2; i > iter.getPos(); --i)
+ array[i] = array[i - len];
+
+ if (c > 0xFFFF)
+ {
+ // c will be multibyte, so split it up into a surrogate pair.
+ uchar16_t x = static_cast<uchar16_t>(c);
+ uchar16_t vh = UTF16_HI_SURROGATE | ((((c >> 16) & ((1 << 5) - 1)) - 1) << 6) | (x >> 10);
+ uchar16_t vl = UTF16_LO_SURROGATE | (x & ((1 << 10) - 1));
+ array[iter.getPos()] = vh;
+ array[iter.getPos()+1] = vl;
+ }
+ else
+ {
+ array[iter.getPos()] = static_cast<uchar16_t>(c);
+ }
+ array[used] = 0;
+ return *this;
+ }
+
+
+ //! Inserts a string into the string.
+ //! \param c The string to insert.
+ //! \param pos The position to insert the string.
+ //! \return A reference to our current string.
+ ustring16<TAlloc>& insert(const ustring16<TAlloc>& c, u32 pos)
+ {
+ u32 len = c.size_raw();
+ if (len == 0) return *this;
+
+ if (used + len >= allocated)
+ reallocate(used + len);
+
+ used += len;
+
+ iterator iter(*this, pos);
+ for (u32 i = used - 2; i > iter.getPos() + len; --i)
+ array[i] = array[i - len];
+
+ const uchar16_t* s = c.c_str();
+ for (u32 i = 0; i < len; ++i)
+ {
+ array[pos++] = *s;
+ ++s;
+ }
+
+ array[used] = 0;
+ return *this;
+ }
+
+
+ //! Inserts a character into the string.
+ //! \param c The character to insert.
+ //! \param pos The position to insert the character.
+ //! \return A reference to our current string.
+ ustring16<TAlloc>& insert_raw(uchar16_t c, u32 pos)
+ {
+ if (used + 1 >= allocated)
+ reallocate(used + 1);
+
+ ++used;
+
+ for (u32 i = used - 1; i > pos; --i)
+ array[i] = array[i - 1];
+
+ array[pos] = c;
+ array[used] = 0;
+ return *this;
+ }
+
+
+ //! Removes a character from string.
+ //! \param pos Position of the character to remove.
+ //! \return A reference to our current string.
+ ustring16<TAlloc>& erase_raw(u32 pos)
+ {
+ for (u32 i=pos; i<=used; ++i)
+ {
+ array[i] = array[i + 1];
+ }
+ --used;
+ array[used] = 0;
+ return *this;
+ }
+
+
+ //! Replaces a character in the string.
+ //! \param c The new character.
+ //! \param pos The position of the character to replace.
+ //! \return A reference to our current string.
+ ustring16<TAlloc>& replace_raw(uchar16_t c, u32 pos)
+ {
+ array[pos] = c;
+ return *this;
+ }
+
+
+ //! Returns an iterator to the beginning of the string.
+ //! \return An iterator to the beginning of the string.
+ iterator begin()
+ {
+ iterator i(*this, 0);
+ return i;
+ }
+
+
+ //! Returns an iterator to the beginning of the string.
+ //! \return An iterator to the beginning of the string.
+ const_iterator begin() const
+ {
+ const_iterator i(*this, 0);
+ return i;
+ }
+
+
+ //! Returns an iterator to the beginning of the string.
+ //! \return An iterator to the beginning of the string.
+ const_iterator cbegin() const
+ {
+ const_iterator i(*this, 0);
+ return i;
+ }
+
+
+ //! Returns an iterator to the end of the string.
+ //! \return An iterator to the end of the string.
+ iterator end()
+ {
+ iterator i(*this, 0);
+ i.toEnd();
+ return i;
+ }
+
+
+ //! Returns an iterator to the end of the string.
+ //! \return An iterator to the end of the string.
+ const_iterator end() const
+ {
+ const_iterator i(*this, 0);
+ i.toEnd();
+ return i;
+ }
+
+
+ //! Returns an iterator to the end of the string.
+ //! \return An iterator to the end of the string.
+ const_iterator cend() const
+ {
+ const_iterator i(*this, 0);
+ i.toEnd();
+ return i;
+ }
+
+
+ //! Converts the string to a UTF-8 encoded string.
+ //! \param addBOM If true, the proper unicode byte-order mark will be prefixed to the string.
+ //! \return A string containing the UTF-8 encoded string.
+ core::string<uchar8_t> toUTF8_s(const bool addBOM = false) const
+ {
+ core::string<uchar8_t> ret;
+ ret.reserve(used * 4 + (addBOM ? unicode::BOM_UTF8_LEN : 0) + 1);
+ const_iterator iter(*this, 0);
+
+ // Add the byte order mark if the user wants it.
+ if (addBOM)
+ {
+ ret.append(unicode::BOM_ENCODE_UTF8[0]);
+ ret.append(unicode::BOM_ENCODE_UTF8[1]);
+ ret.append(unicode::BOM_ENCODE_UTF8[2]);
+ }
+
+ while (!iter.atEnd())
+ {
+ uchar32_t c = *iter;
+ if (c > 0xFFFF)
+ { // 4 bytes
+ uchar8_t b1 = (0x1E << 3) | ((c >> 18) & 0x7);
+ uchar8_t b2 = (0x2 << 6) | ((c >> 12) & 0x3F);
+ uchar8_t b3 = (0x2 << 6) | ((c >> 6) & 0x3F);
+ uchar8_t b4 = (0x2 << 6) | (c & 0x3F);
+ ret.append(b1);
+ ret.append(b2);
+ ret.append(b3);
+ ret.append(b4);
+ }
+ else if (c > 0x7FF)
+ { // 3 bytes
+ uchar8_t b1 = (0xE << 4) | ((c >> 12) & 0xF);
+ uchar8_t b2 = (0x2 << 6) | ((c >> 6) & 0x3F);
+ uchar8_t b3 = (0x2 << 6) | (c & 0x3F);
+ ret.append(b1);
+ ret.append(b2);
+ ret.append(b3);
+ }
+ else if (c > 0x7F)
+ { // 2 bytes
+ uchar8_t b1 = (0x6 << 5) | ((c >> 6) & 0x1F);
+ uchar8_t b2 = (0x2 << 6) | (c & 0x3F);
+ ret.append(b1);
+ ret.append(b2);
+ }
+ else
+ { // 1 byte
+ ret.append(static_cast<uchar8_t>(c));
+ }
+ ++iter;
+ }
+ return ret;
+ }
+
+
+ //! Converts the string to a UTF-8 encoded string array.
+ //! \param addBOM If true, the proper unicode byte-order mark will be prefixed to the string.
+ //! \return An array containing the UTF-8 encoded string.
+ core::array<uchar8_t> toUTF8(const bool addBOM = false) const
+ {
+ core::array<uchar8_t> ret(used * 4 + (addBOM ? unicode::BOM_UTF8_LEN : 0) + 1);
+ const_iterator iter(*this, 0);
+
+ // Add the byte order mark if the user wants it.
+ if (addBOM)
+ {
+ ret.push_back(unicode::BOM_ENCODE_UTF8[0]);
+ ret.push_back(unicode::BOM_ENCODE_UTF8[1]);
+ ret.push_back(unicode::BOM_ENCODE_UTF8[2]);
+ }
+
+ while (!iter.atEnd())
+ {
+ uchar32_t c = *iter;
+ if (c > 0xFFFF)
+ { // 4 bytes
+ uchar8_t b1 = (0x1E << 3) | ((c >> 18) & 0x7);
+ uchar8_t b2 = (0x2 << 6) | ((c >> 12) & 0x3F);
+ uchar8_t b3 = (0x2 << 6) | ((c >> 6) & 0x3F);
+ uchar8_t b4 = (0x2 << 6) | (c & 0x3F);
+ ret.push_back(b1);
+ ret.push_back(b2);
+ ret.push_back(b3);
+ ret.push_back(b4);
+ }
+ else if (c > 0x7FF)
+ { // 3 bytes
+ uchar8_t b1 = (0xE << 4) | ((c >> 12) & 0xF);
+ uchar8_t b2 = (0x2 << 6) | ((c >> 6) & 0x3F);
+ uchar8_t b3 = (0x2 << 6) | (c & 0x3F);
+ ret.push_back(b1);
+ ret.push_back(b2);
+ ret.push_back(b3);
+ }
+ else if (c > 0x7F)
+ { // 2 bytes
+ uchar8_t b1 = (0x6 << 5) | ((c >> 6) & 0x1F);
+ uchar8_t b2 = (0x2 << 6) | (c & 0x3F);
+ ret.push_back(b1);
+ ret.push_back(b2);
+ }
+ else
+ { // 1 byte
+ ret.push_back(static_cast<uchar8_t>(c));
+ }
+ ++iter;
+ }
+ ret.push_back(0);
+ return ret;
+ }
+
+
+#ifdef USTRING_CPP0X_NEWLITERALS // C++0x
+ //! Converts the string to a UTF-16 encoded string.
+ //! \param endian The desired endianness of the string.
+ //! \param addBOM If true, the proper unicode byte-order mark will be prefixed to the string.
+ //! \return A string containing the UTF-16 encoded string.
+ core::string<char16_t> toUTF16_s(const unicode::EUTF_ENDIAN endian = unicode::EUTFEE_NATIVE, const bool addBOM = false) const
+ {
+ core::string<char16_t> ret;
+ ret.reserve(used + (addBOM ? unicode::BOM_UTF16_LEN : 0) + 1);
+
+ // Add the BOM if specified.
+ if (addBOM)
+ {
+ if (endian == unicode::EUTFEE_NATIVE)
+ ret[0] = unicode::BOM;
+ else if (endian == unicode::EUTFEE_LITTLE)
+ {
+ uchar8_t* ptr8 = reinterpret_cast<uchar8_t*>(ret.c_str());
+ *ptr8++ = unicode::BOM_ENCODE_UTF16_LE[0];
+ *ptr8 = unicode::BOM_ENCODE_UTF16_LE[1];
+ }
+ else
+ {
+ uchar8_t* ptr8 = reinterpret_cast<uchar8_t*>(ret.c_str());
+ *ptr8++ = unicode::BOM_ENCODE_UTF16_BE[0];
+ *ptr8 = unicode::BOM_ENCODE_UTF16_BE[1];
+ }
+ }
+
+ ret.append(array);
+ if (endian != unicode::EUTFEE_NATIVE && getEndianness() != endian)
+ {
+ char16_t* ptr = ret.c_str();
+ for (u32 i = 0; i < ret.size(); ++i)
+ *ptr++ = unicode::swapEndian16(*ptr);
+ }
+ return ret;
+ }
+#endif
+
+
+ //! Converts the string to a UTF-16 encoded string array.
+ //! Unfortunately, no toUTF16_s() version exists due to limitations with Irrlicht's string class.
+ //! \param endian The desired endianness of the string.
+ //! \param addBOM If true, the proper unicode byte-order mark will be prefixed to the string.
+ //! \return An array containing the UTF-16 encoded string.
+ core::array<uchar16_t> toUTF16(const unicode::EUTF_ENDIAN endian = unicode::EUTFEE_NATIVE, const bool addBOM = false) const
+ {
+ core::array<uchar16_t> ret(used + (addBOM ? unicode::BOM_UTF16_LEN : 0) + 1);
+ uchar16_t* ptr = ret.pointer();
+
+ // Add the BOM if specified.
+ if (addBOM)
+ {
+ if (endian == unicode::EUTFEE_NATIVE)
+ *ptr = unicode::BOM;
+ else if (endian == unicode::EUTFEE_LITTLE)
+ {
+ uchar8_t* ptr8 = reinterpret_cast<uchar8_t*>(ptr);
+ *ptr8++ = unicode::BOM_ENCODE_UTF16_LE[0];
+ *ptr8 = unicode::BOM_ENCODE_UTF16_LE[1];
+ }
+ else
+ {
+ uchar8_t* ptr8 = reinterpret_cast<uchar8_t*>(ptr);
+ *ptr8++ = unicode::BOM_ENCODE_UTF16_BE[0];
+ *ptr8 = unicode::BOM_ENCODE_UTF16_BE[1];
+ }
+ ++ptr;
+ }
+
+ memcpy((void*)ptr, (void*)array, used * sizeof(uchar16_t));
+ if (endian != unicode::EUTFEE_NATIVE && getEndianness() != endian)
+ {
+ for (u32 i = 0; i <= used; ++i)
+ *ptr++ = unicode::swapEndian16(*ptr);
+ }
+ ret.set_used(used + (addBOM ? unicode::BOM_UTF16_LEN : 0));
+ ret.push_back(0);
+ return ret;
+ }
+
+
+#ifdef USTRING_CPP0X_NEWLITERALS // C++0x
+ //! Converts the string to a UTF-32 encoded string.
+ //! \param endian The desired endianness of the string.
+ //! \param addBOM If true, the proper unicode byte-order mark will be prefixed to the string.
+ //! \return A string containing the UTF-32 encoded string.
+ core::string<char32_t> toUTF32_s(const unicode::EUTF_ENDIAN endian = unicode::EUTFEE_NATIVE, const bool addBOM = false) const
+ {
+ core::string<char32_t> ret;
+ ret.reserve(size() + 1 + (addBOM ? unicode::BOM_UTF32_LEN : 0));
+ const_iterator iter(*this, 0);
+
+ // Add the BOM if specified.
+ if (addBOM)
+ {
+ if (endian == unicode::EUTFEE_NATIVE)
+ ret.append(unicode::BOM);
+ else
+ {
+ union
+ {
+ uchar32_t full;
+ u8 chunk[4];
+ } t;
+
+ if (endian == unicode::EUTFEE_LITTLE)
+ {
+ t.chunk[0] = unicode::BOM_ENCODE_UTF32_LE[0];
+ t.chunk[1] = unicode::BOM_ENCODE_UTF32_LE[1];
+ t.chunk[2] = unicode::BOM_ENCODE_UTF32_LE[2];
+ t.chunk[3] = unicode::BOM_ENCODE_UTF32_LE[3];
+ }
+ else
+ {
+ t.chunk[0] = unicode::BOM_ENCODE_UTF32_BE[0];
+ t.chunk[1] = unicode::BOM_ENCODE_UTF32_BE[1];
+ t.chunk[2] = unicode::BOM_ENCODE_UTF32_BE[2];
+ t.chunk[3] = unicode::BOM_ENCODE_UTF32_BE[3];
+ }
+ ret.append(t.full);
+ }
+ }
+
+ while (!iter.atEnd())
+ {
+ uchar32_t c = *iter;
+ if (endian != unicode::EUTFEE_NATIVE && getEndianness() != endian)
+ c = unicode::swapEndian32(c);
+ ret.append(c);
+ ++iter;
+ }
+ return ret;
+ }
+#endif
+
+
+ //! Converts the string to a UTF-32 encoded string array.
+ //! Unfortunately, no toUTF32_s() version exists due to limitations with Irrlicht's string class.
+ //! \param endian The desired endianness of the string.
+ //! \param addBOM If true, the proper unicode byte-order mark will be prefixed to the string.
+ //! \return An array containing the UTF-32 encoded string.
+ core::array<uchar32_t> toUTF32(const unicode::EUTF_ENDIAN endian = unicode::EUTFEE_NATIVE, const bool addBOM = false) const
+ {
+ core::array<uchar32_t> ret(size() + (addBOM ? unicode::BOM_UTF32_LEN : 0) + 1);
+ const_iterator iter(*this, 0);
+
+ // Add the BOM if specified.
+ if (addBOM)
+ {
+ if (endian == unicode::EUTFEE_NATIVE)
+ ret.push_back(unicode::BOM);
+ else
+ {
+ union
+ {
+ uchar32_t full;
+ u8 chunk[4];
+ } t;
+
+ if (endian == unicode::EUTFEE_LITTLE)
+ {
+ t.chunk[0] = unicode::BOM_ENCODE_UTF32_LE[0];
+ t.chunk[1] = unicode::BOM_ENCODE_UTF32_LE[1];
+ t.chunk[2] = unicode::BOM_ENCODE_UTF32_LE[2];
+ t.chunk[3] = unicode::BOM_ENCODE_UTF32_LE[3];
+ }
+ else
+ {
+ t.chunk[0] = unicode::BOM_ENCODE_UTF32_BE[0];
+ t.chunk[1] = unicode::BOM_ENCODE_UTF32_BE[1];
+ t.chunk[2] = unicode::BOM_ENCODE_UTF32_BE[2];
+ t.chunk[3] = unicode::BOM_ENCODE_UTF32_BE[3];
+ }
+ ret.push_back(t.full);
+ }
+ }
+ ret.push_back(0);
+
+ while (!iter.atEnd())
+ {
+ uchar32_t c = *iter;
+ if (endian != unicode::EUTFEE_NATIVE && getEndianness() != endian)
+ c = unicode::swapEndian32(c);
+ ret.push_back(c);
+ ++iter;
+ }
+ return ret;
+ }
+
+
+ //! Converts the string to a wchar_t encoded string.
+ /** The size of a wchar_t changes depending on the platform. This function will store a
+ correct UTF-8, -16, or -32 encoded string depending on the size of a wchar_t. **/
+ //! \param endian The desired endianness of the string.
+ //! \param addBOM If true, the proper unicode byte-order mark will be prefixed to the string.
+ //! \return A string containing the wchar_t encoded string.
+ core::string<wchar_t> toWCHAR_s(const unicode::EUTF_ENDIAN endian = unicode::EUTFEE_NATIVE, const bool addBOM = false) const
+ {
+ if (sizeof(wchar_t) == 4)
+ {
+ core::array<uchar32_t> a(toUTF32(endian, addBOM));
+ core::stringw ret(a.pointer());
+ return ret;
+ }
+ else if (sizeof(wchar_t) == 2)
+ {
+ if (endian == unicode::EUTFEE_NATIVE && addBOM == false)
+ {
+ core::stringw ret(array);
+ return ret;
+ }
+ else
+ {
+ core::array<uchar16_t> a(toUTF16(endian, addBOM));
+ core::stringw ret(a.pointer());
+ return ret;
+ }
+ }
+ else if (sizeof(wchar_t) == 1)
+ {
+ core::array<uchar8_t> a(toUTF8(addBOM));
+ core::stringw ret(a.pointer());
+ return ret;
+ }
+
+ // Shouldn't happen.
+ return core::stringw();
+ }
+
+
+ //! Converts the string to a wchar_t encoded string array.
+ /** The size of a wchar_t changes depending on the platform. This function will store a
+ correct UTF-8, -16, or -32 encoded string depending on the size of a wchar_t. **/
+ //! \param endian The desired endianness of the string.
+ //! \param addBOM If true, the proper unicode byte-order mark will be prefixed to the string.
+ //! \return An array containing the wchar_t encoded string.
+ core::array<wchar_t> toWCHAR(const unicode::EUTF_ENDIAN endian = unicode::EUTFEE_NATIVE, const bool addBOM = false) const
+ {
+ if (sizeof(wchar_t) == 4)
+ {
+ core::array<uchar32_t> a(toUTF32(endian, addBOM));
+ core::array<wchar_t> ret(a.size());
+ ret.set_used(a.size());
+ memcpy((void*)ret.pointer(), (void*)a.pointer(), a.size() * sizeof(uchar32_t));
+ return ret;
+ }
+ if (sizeof(wchar_t) == 2)
+ {
+ if (endian == unicode::EUTFEE_NATIVE && addBOM == false)
+ {
+ core::array<wchar_t> ret(used);
+ ret.set_used(used);
+ memcpy((void*)ret.pointer(), (void*)array, used * sizeof(uchar16_t));
+ return ret;
+ }
+ else
+ {
+ core::array<uchar16_t> a(toUTF16(endian, addBOM));
+ core::array<wchar_t> ret(a.size());
+ ret.set_used(a.size());
+ memcpy((void*)ret.pointer(), (void*)a.pointer(), a.size() * sizeof(uchar16_t));
+ return ret;
+ }
+ }
+ if (sizeof(wchar_t) == 1)
+ {
+ core::array<uchar8_t> a(toUTF8(addBOM));
+ core::array<wchar_t> ret(a.size());
+ ret.set_used(a.size());
+ memcpy((void*)ret.pointer(), (void*)a.pointer(), a.size() * sizeof(uchar8_t));
+ return ret;
+ }
+
+ // Shouldn't happen.
+ return core::array<wchar_t>();
+ }
+
+ //! Converts the string to a properly encoded io::path string.
+ //! \param endian The desired endianness of the string.
+ //! \param addBOM If true, the proper unicode byte-order mark will be prefixed to the string.
+ //! \return An io::path string containing the properly encoded string.
+ io::path toPATH_s(const unicode::EUTF_ENDIAN endian = unicode::EUTFEE_NATIVE, const bool addBOM = false) const
+ {
+#if defined(_IRR_WCHAR_FILESYSTEM)
+ return toWCHAR_s(endian, addBOM);
+#else
+ return toUTF8_s(addBOM);
+#endif
+ }
+
+ //! Loads an unknown stream of data.
+ //! Will attempt to determine if the stream is unicode data. Useful for loading from files.
+ //! \param data The data stream to load from.
+ //! \param data_size The length of the data string.
+ //! \return A reference to our current string.
+ ustring16<TAlloc>& loadDataStream(const char* data, size_t data_size)
+ {
+ // Clear our string.
+ *this = "";
+ if (!data)
+ return *this;
+
+ unicode::EUTF_ENCODE e = unicode::determineUnicodeBOM(data);
+ switch (e)
+ {
+ default:
+ case unicode::EUTFE_UTF8:
+ append((uchar8_t*)data, data_size);
+ break;
+
+ case unicode::EUTFE_UTF16:
+ case unicode::EUTFE_UTF16_BE:
+ case unicode::EUTFE_UTF16_LE:
+ append((uchar16_t*)data, data_size / 2);
+ break;
+
+ case unicode::EUTFE_UTF32:
+ case unicode::EUTFE_UTF32_BE:
+ case unicode::EUTFE_UTF32_LE:
+ append((uchar32_t*)data, data_size / 4);
+ break;
+ }
+
+ return *this;
+ }
+
+ //! Gets the encoding of the Unicode string this class contains.
+ //! \return An enum describing the current encoding of this string.
+ const unicode::EUTF_ENCODE getEncoding() const
+ {
+ return encoding;
+ }
+
+ //! Gets the endianness of the Unicode string this class contains.
+ //! \return An enum describing the endianness of this string.
+ const unicode::EUTF_ENDIAN getEndianness() const
+ {
+ if (encoding == unicode::EUTFE_UTF16_LE ||
+ encoding == unicode::EUTFE_UTF32_LE)
+ return unicode::EUTFEE_LITTLE;
+ else return unicode::EUTFEE_BIG;
+ }
+
+private:
+
+ //! Reallocate the string, making it bigger or smaller.
+ //! \param new_size The new size of the string.
+ void reallocate(u32 new_size)
+ {
+ uchar16_t* old_array = array;
+
+ array = allocator.allocate(new_size + 1); //new u16[new_size];
+ allocated = new_size + 1;
+ if (old_array == 0) return;
+
+ u32 amount = used < new_size ? used : new_size;
+ for (u32 i=0; i<=amount; ++i)
+ array[i] = old_array[i];
+
+ if (allocated <= used)
+ used = allocated - 1;
+
+ array[used] = 0;
+
+ allocator.deallocate(old_array); // delete [] old_array;
+ }
+
+ //--- member variables
+
+ uchar16_t* array;
+ unicode::EUTF_ENCODE encoding;
+ u32 allocated;
+ u32 used;
+ TAlloc allocator;
+ //irrAllocator<uchar16_t> allocator;
+};
+
+typedef ustring16<irrAllocator<uchar16_t> > ustring;
+
+
+//! Appends two ustring16s.
+template <typename TAlloc>
+inline ustring16<TAlloc> operator+(const ustring16<TAlloc>& left, const ustring16<TAlloc>& right)
+{
+ ustring16<TAlloc> ret(left);
+ ret += right;
+ return ret;
+}
+
+
+//! Appends a ustring16 and a null-terminated unicode string.
+template <typename TAlloc, class B>
+inline ustring16<TAlloc> operator+(const ustring16<TAlloc>& left, const B* const right)
+{
+ ustring16<TAlloc> ret(left);
+ ret += right;
+ return ret;
+}
+
+
+//! Appends a ustring16 and a null-terminated unicode string.
+template <class B, typename TAlloc>
+inline ustring16<TAlloc> operator+(const B* const left, const ustring16<TAlloc>& right)
+{
+ ustring16<TAlloc> ret(left);
+ ret += right;
+ return ret;
+}
+
+
+//! Appends a ustring16 and an Irrlicht string.
+template <typename TAlloc, typename B, typename BAlloc>
+inline ustring16<TAlloc> operator+(const ustring16<TAlloc>& left, const string<B, BAlloc>& right)
+{
+ ustring16<TAlloc> ret(left);
+ ret += right;
+ return ret;
+}
+
+
+//! Appends a ustring16 and an Irrlicht string.
+template <typename TAlloc, typename B, typename BAlloc>
+inline ustring16<TAlloc> operator+(const string<B, BAlloc>& left, const ustring16<TAlloc>& right)
+{
+ ustring16<TAlloc> ret(left);
+ ret += right;
+ return ret;
+}
+
+
+//! Appends a ustring16 and a std::basic_string.
+template <typename TAlloc, typename B, typename A, typename BAlloc>
+inline ustring16<TAlloc> operator+(const ustring16<TAlloc>& left, const std::basic_string<B, A, BAlloc>& right)
+{
+ ustring16<TAlloc> ret(left);
+ ret += right;
+ return ret;
+}
+
+
+//! Appends a ustring16 and a std::basic_string.
+template <typename TAlloc, typename B, typename A, typename BAlloc>
+inline ustring16<TAlloc> operator+(const std::basic_string<B, A, BAlloc>& left, const ustring16<TAlloc>& right)
+{
+ ustring16<TAlloc> ret(left);
+ ret += right;
+ return ret;
+}
+
+
+//! Appends a ustring16 and a char.
+template <typename TAlloc>
+inline ustring16<TAlloc> operator+(const ustring16<TAlloc>& left, const char right)
+{
+ ustring16<TAlloc> ret(left);
+ ret += right;
+ return ret;
+}
+
+
+//! Appends a ustring16 and a char.
+template <typename TAlloc>
+inline ustring16<TAlloc> operator+(const char left, const ustring16<TAlloc>& right)
+{
+ ustring16<TAlloc> ret(left);
+ ret += right;
+ return ret;
+}
+
+
+#ifdef USTRING_CPP0X_NEWLITERALS
+//! Appends a ustring16 and a uchar32_t.
+template <typename TAlloc>
+inline ustring16<TAlloc> operator+(const ustring16<TAlloc>& left, const uchar32_t right)
+{
+ ustring16<TAlloc> ret(left);
+ ret += right;
+ return ret;
+}
+
+
+//! Appends a ustring16 and a uchar32_t.
+template <typename TAlloc>
+inline ustring16<TAlloc> operator+(const uchar32_t left, const ustring16<TAlloc>& right)
+{
+ ustring16<TAlloc> ret(left);
+ ret += right;
+ return ret;
+}
+#endif
+
+
+//! Appends a ustring16 and a short.
+template <typename TAlloc>
+inline ustring16<TAlloc> operator+(const ustring16<TAlloc>& left, const short right)
+{
+ ustring16<TAlloc> ret(left);
+ ret += core::stringc(right);
+ return ret;
+}
+
+
+//! Appends a ustring16 and a short.
+template <typename TAlloc>
+inline ustring16<TAlloc> operator+(const short left, const ustring16<TAlloc>& right)
+{
+ ustring16<TAlloc> ret(core::stringc(left));
+ ret += right;
+ return ret;
+}
+
+
+//! Appends a ustring16 and an unsigned short.
+template <typename TAlloc>
+inline ustring16<TAlloc> operator+(const ustring16<TAlloc>& left, const unsigned short right)
+{
+ ustring16<TAlloc> ret(left);
+ ret += core::stringc(right);
+ return ret;
+}
+
+
+//! Appends a ustring16 and an unsigned short.
+template <typename TAlloc>
+inline ustring16<TAlloc> operator+(const unsigned short left, const ustring16<TAlloc>& right)
+{
+ ustring16<TAlloc> ret(core::stringc(left));
+ ret += right;
+ return ret;
+}
+
+
+//! Appends a ustring16 and an int.
+template <typename TAlloc>
+inline ustring16<TAlloc> operator+(const ustring16<TAlloc>& left, const int right)
+{
+ ustring16<TAlloc> ret(left);
+ ret += core::stringc(right);
+ return ret;
+}
+
+
+//! Appends a ustring16 and an int.
+template <typename TAlloc>
+inline ustring16<TAlloc> operator+(const int left, const ustring16<TAlloc>& right)
+{
+ ustring16<TAlloc> ret(core::stringc(left));
+ ret += right;
+ return ret;
+}
+
+
+//! Appends a ustring16 and an unsigned int.
+template <typename TAlloc>
+inline ustring16<TAlloc> operator+(const ustring16<TAlloc>& left, const unsigned int right)
+{
+ ustring16<TAlloc> ret(left);
+ ret += core::stringc(right);
+ return ret;
+}
+
+
+//! Appends a ustring16 and an unsigned int.
+template <typename TAlloc>
+inline ustring16<TAlloc> operator+(const unsigned int left, const ustring16<TAlloc>& right)
+{
+ ustring16<TAlloc> ret(core::stringc(left));
+ ret += right;
+ return ret;
+}
+
+
+//! Appends a ustring16 and a long.
+template <typename TAlloc>
+inline ustring16<TAlloc> operator+(const ustring16<TAlloc>& left, const long right)
+{
+ ustring16<TAlloc> ret(left);
+ ret += core::stringc(right);
+ return ret;
+}
+
+
+//! Appends a ustring16 and a long.
+template <typename TAlloc>
+inline ustring16<TAlloc> operator+(const long left, const ustring16<TAlloc>& right)
+{
+ ustring16<TAlloc> ret(core::stringc(left));
+ ret += right;
+ return ret;
+}
+
+
+//! Appends a ustring16 and an unsigned long.
+template <typename TAlloc>
+inline ustring16<TAlloc> operator+(const ustring16<TAlloc>& left, const unsigned long right)
+{
+ ustring16<TAlloc> ret(left);
+ ret += core::stringc(right);
+ return ret;
+}
+
+
+//! Appends a ustring16 and an unsigned long.
+template <typename TAlloc>
+inline ustring16<TAlloc> operator+(const unsigned long left, const ustring16<TAlloc>& right)
+{
+ ustring16<TAlloc> ret(core::stringc(left));
+ ret += right;
+ return ret;
+}
+
+
+//! Appends a ustring16 and a float.
+template <typename TAlloc>
+inline ustring16<TAlloc> operator+(const ustring16<TAlloc>& left, const float right)
+{
+ ustring16<TAlloc> ret(left);
+ ret += core::stringc(right);
+ return ret;
+}
+
+
+//! Appends a ustring16 and a float.
+template <typename TAlloc>
+inline ustring16<TAlloc> operator+(const float left, const ustring16<TAlloc>& right)
+{
+ ustring16<TAlloc> ret(core::stringc(left));
+ ret += right;
+ return ret;
+}
+
+
+//! Appends a ustring16 and a double.
+template <typename TAlloc>
+inline ustring16<TAlloc> operator+(const ustring16<TAlloc>& left, const double right)
+{
+ ustring16<TAlloc> ret(left);
+ ret += core::stringc(right);
+ return ret;
+}
+
+
+//! Appends a ustring16 and a double.
+template <typename TAlloc>
+inline ustring16<TAlloc> operator+(const double left, const ustring16<TAlloc>& right)
+{
+ ustring16<TAlloc> ret(core::stringc(left));
+ ret += right;
+ return ret;
+}
+
+
+#ifdef USTRING_CPP0X
+//! Appends two ustring16s.
+template <typename TAlloc>
+inline ustring16<TAlloc>&& operator+(const ustring16<TAlloc>& left, ustring16<TAlloc>&& right)
+{
+ //std::cout << "MOVE operator+(&, &&)" << std::endl;
+ right.insert(left, 0);
+ return std::move(right);
+}
+
+
+//! Appends two ustring16s.
+template <typename TAlloc>
+inline ustring16<TAlloc>&& operator+(ustring16<TAlloc>&& left, const ustring16<TAlloc>& right)
+{
+ //std::cout << "MOVE operator+(&&, &)" << std::endl;
+ left.append(right);
+ return std::move(left);
+}
+
+
+//! Appends two ustring16s.
+template <typename TAlloc>
+inline ustring16<TAlloc>&& operator+(ustring16<TAlloc>&& left, ustring16<TAlloc>&& right)
+{
+ //std::cout << "MOVE operator+(&&, &&)" << std::endl;
+ if ((right.size_raw() <= left.capacity() - left.size_raw()) ||
+ (right.capacity() - right.size_raw() < left.size_raw()))
+ {
+ left.append(right);
+ return std::move(left);
+ }
+ else
+ {
+ right.insert(left, 0);
+ return std::move(right);
+ }
+}
+
+
+//! Appends a ustring16 and a null-terminated unicode string.
+template <typename TAlloc, class B>
+inline ustring16<TAlloc>&& operator+(ustring16<TAlloc>&& left, const B* const right)
+{
+ //std::cout << "MOVE operator+(&&, B*)" << std::endl;
+ left.append(right);
+ return std::move(left);
+}
+
+
+//! Appends a ustring16 and a null-terminated unicode string.
+template <class B, typename TAlloc>
+inline ustring16<TAlloc>&& operator+(const B* const left, ustring16<TAlloc>&& right)
+{
+ //std::cout << "MOVE operator+(B*, &&)" << std::endl;
+ right.insert(left, 0);
+ return std::move(right);
+}
+
+
+//! Appends a ustring16 and an Irrlicht string.
+template <typename TAlloc, typename B, typename BAlloc>
+inline ustring16<TAlloc>&& operator+(const string<B, BAlloc>& left, ustring16<TAlloc>&& right)
+{
+ //std::cout << "MOVE operator+(&, &&)" << std::endl;
+ right.insert(left, 0);
+ return std::move(right);
+}
+
+
+//! Appends a ustring16 and an Irrlicht string.
+template <typename TAlloc, typename B, typename BAlloc>
+inline ustring16<TAlloc>&& operator+(ustring16<TAlloc>&& left, const string<B, BAlloc>& right)
+{
+ //std::cout << "MOVE operator+(&&, &)" << std::endl;
+ left.append(right);
+ return std::move(left);
+}
+
+
+//! Appends a ustring16 and a std::basic_string.
+template <typename TAlloc, typename B, typename A, typename BAlloc>
+inline ustring16<TAlloc>&& operator+(const std::basic_string<B, A, BAlloc>& left, ustring16<TAlloc>&& right)
+{
+ //std::cout << "MOVE operator+(&, &&)" << std::endl;
+ right.insert(core::ustring16<TAlloc>(left), 0);
+ return std::move(right);
+}
+
+
+//! Appends a ustring16 and a std::basic_string.
+template <typename TAlloc, typename B, typename A, typename BAlloc>
+inline ustring16<TAlloc>&& operator+(ustring16<TAlloc>&& left, const std::basic_string<B, A, BAlloc>& right)
+{
+ //std::cout << "MOVE operator+(&&, &)" << std::endl;
+ left.append(right);
+ return std::move(left);
+}
+
+
+//! Appends a ustring16 and a char.
+template <typename TAlloc>
+inline ustring16<TAlloc> operator+(ustring16<TAlloc>&& left, const char right)
+{
+ left.append((uchar32_t)right);
+ return std::move(left);
+}
+
+
+//! Appends a ustring16 and a char.
+template <typename TAlloc>
+inline ustring16<TAlloc> operator+(const char left, ustring16<TAlloc>&& right)
+{
+ right.insert((uchar32_t)left, 0);
+ return std::move(right);
+}
+
+
+#ifdef USTRING_CPP0X_NEWLITERALS
+//! Appends a ustring16 and a uchar32_t.
+template <typename TAlloc>
+inline ustring16<TAlloc> operator+(ustring16<TAlloc>&& left, const uchar32_t right)
+{
+ left.append(right);
+ return std::move(left);
+}
+
+
+//! Appends a ustring16 and a uchar32_t.
+template <typename TAlloc>
+inline ustring16<TAlloc> operator+(const uchar32_t left, ustring16<TAlloc>&& right)
+{
+ right.insert(left, 0);
+ return std::move(right);
+}
+#endif
+
+
+//! Appends a ustring16 and a short.
+template <typename TAlloc>
+inline ustring16<TAlloc> operator+(ustring16<TAlloc>&& left, const short right)
+{
+ left.append(core::stringc(right));
+ return std::move(left);
+}
+
+
+//! Appends a ustring16 and a short.
+template <typename TAlloc>
+inline ustring16<TAlloc> operator+(const short left, ustring16<TAlloc>&& right)
+{
+ right.insert(core::stringc(left), 0);
+ return std::move(right);
+}
+
+
+//! Appends a ustring16 and an unsigned short.
+template <typename TAlloc>
+inline ustring16<TAlloc> operator+(ustring16<TAlloc>&& left, const unsigned short right)
+{
+ left.append(core::stringc(right));
+ return std::move(left);
+}
+
+
+//! Appends a ustring16 and an unsigned short.
+template <typename TAlloc>
+inline ustring16<TAlloc> operator+(const unsigned short left, ustring16<TAlloc>&& right)
+{
+ right.insert(core::stringc(left), 0);
+ return std::move(right);
+}
+
+
+//! Appends a ustring16 and an int.
+template <typename TAlloc>
+inline ustring16<TAlloc> operator+(ustring16<TAlloc>&& left, const int right)
+{
+ left.append(core::stringc(right));
+ return std::move(left);
+}
+
+
+//! Appends a ustring16 and an int.
+template <typename TAlloc>
+inline ustring16<TAlloc> operator+(const int left, ustring16<TAlloc>&& right)
+{
+ right.insert(core::stringc(left), 0);
+ return std::move(right);
+}
+
+
+//! Appends a ustring16 and an unsigned int.
+template <typename TAlloc>
+inline ustring16<TAlloc> operator+(ustring16<TAlloc>&& left, const unsigned int right)
+{
+ left.append(core::stringc(right));
+ return std::move(left);
+}
+
+
+//! Appends a ustring16 and an unsigned int.
+template <typename TAlloc>
+inline ustring16<TAlloc> operator+(const unsigned int left, ustring16<TAlloc>&& right)
+{
+ right.insert(core::stringc(left), 0);
+ return std::move(right);
+}
+
+
+//! Appends a ustring16 and a long.
+template <typename TAlloc>
+inline ustring16<TAlloc> operator+(ustring16<TAlloc>&& left, const long right)
+{
+ left.append(core::stringc(right));
+ return std::move(left);
+}
+
+
+//! Appends a ustring16 and a long.
+template <typename TAlloc>
+inline ustring16<TAlloc> operator+(const long left, ustring16<TAlloc>&& right)
+{
+ right.insert(core::stringc(left), 0);
+ return std::move(right);
+}
+
+
+//! Appends a ustring16 and an unsigned long.
+template <typename TAlloc>
+inline ustring16<TAlloc> operator+(ustring16<TAlloc>&& left, const unsigned long right)
+{
+ left.append(core::stringc(right));
+ return std::move(left);
+}
+
+
+//! Appends a ustring16 and an unsigned long.
+template <typename TAlloc>
+inline ustring16<TAlloc> operator+(const unsigned long left, ustring16<TAlloc>&& right)
+{
+ right.insert(core::stringc(left), 0);
+ return std::move(right);
+}
+
+
+//! Appends a ustring16 and a float.
+template <typename TAlloc>
+inline ustring16<TAlloc> operator+(ustring16<TAlloc>&& left, const float right)
+{
+ left.append(core::stringc(right));
+ return std::move(left);
+}
+
+
+//! Appends a ustring16 and a float.
+template <typename TAlloc>
+inline ustring16<TAlloc> operator+(const float left, ustring16<TAlloc>&& right)
+{
+ right.insert(core::stringc(left), 0);
+ return std::move(right);
+}
+
+
+//! Appends a ustring16 and a double.
+template <typename TAlloc>
+inline ustring16<TAlloc> operator+(ustring16<TAlloc>&& left, const double right)
+{
+ left.append(core::stringc(right));
+ return std::move(left);
+}
+
+
+//! Appends a ustring16 and a double.
+template <typename TAlloc>
+inline ustring16<TAlloc> operator+(const double left, ustring16<TAlloc>&& right)
+{
+ right.insert(core::stringc(left), 0);
+ return std::move(right);
+}
+#endif
+
+
+#ifndef USTRING_NO_STL
+//! Writes a ustring16 to an ostream.
+template <typename TAlloc>
+inline std::ostream& operator<<(std::ostream& out, const ustring16<TAlloc>& in)
+{
+ out << in.toUTF8_s().c_str();
+ return out;
+}
+
+//! Writes a ustring16 to a wostream.
+template <typename TAlloc>
+inline std::wostream& operator<<(std::wostream& out, const ustring16<TAlloc>& in)
+{
+ out << in.toWCHAR_s().c_str();
+ return out;
+}
+#endif
+
+
+#ifndef USTRING_NO_STL
+
+namespace unicode
+{
+
+//! Hashing algorithm for hashing a ustring. Used for things like unordered_maps.
+//! Algorithm taken from std::hash<std::string>.
+class hash : public std::unary_function<core::ustring, size_t>
+{
+ public:
+ size_t operator()(const core::ustring& s) const
+ {
+ size_t ret = 2166136261U;
+ size_t index = 0;
+ size_t stride = 1 + s.size_raw() / 10;
+
+ core::ustring::const_iterator i = s.begin();
+ while (i != s.end())
+ {
+ // TODO: Don't force u32 on an x64 OS. Make it agnostic.
+ ret = 16777619U * ret ^ (size_t)s[(u32)index];
+ index += stride;
+ i += stride;
+ }
+ return (ret);
+ }
+};
+
+} // end namespace unicode
+
+#endif
+
+} // end namespace core
+} // end namespace irr
+
+#endif
diff --git a/src/cguittfont/xCGUITTFont.cpp b/src/cguittfont/xCGUITTFont.cpp
new file mode 100644
index 000000000..c51922e4c
--- /dev/null
+++ b/src/cguittfont/xCGUITTFont.cpp
@@ -0,0 +1,5 @@
+// A wrapper source file to avoid hack with gcc and modifying
+// the CGUITTFont files.
+
+#include "xCGUITTFont.h"
+#include "CGUITTFont.cpp"
diff --git a/src/cguittfont/xCGUITTFont.h b/src/cguittfont/xCGUITTFont.h
new file mode 100644
index 000000000..c3efe7f6f
--- /dev/null
+++ b/src/cguittfont/xCGUITTFont.h
@@ -0,0 +1,7 @@
+// A wrapper header to avoid hack with gcc and modifying
+// the CGUITTFont files.
+
+#include <algorithm>
+#include <stddef.h>
+#include "irrUString.h"
+#include "CGUITTFont.h"
diff --git a/src/client.cpp b/src/client.cpp
index 9969ef538..415f07311 100644
--- a/src/client.cpp
+++ b/src/client.cpp
@@ -1514,6 +1514,26 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
}
}
}
+ else if(command == TOCLIENT_MOVEMENT)
+ {
+ std::string datastring((char*)&data[2], datasize-2);
+ std::istringstream is(datastring, std::ios_base::binary);
+ Player *player = m_env.getLocalPlayer();
+ assert(player != NULL);
+
+ player->movement_acceleration_default = readF1000(is) * BS;
+ player->movement_acceleration_air = readF1000(is) * BS;
+ player->movement_acceleration_fast = readF1000(is) * BS;
+ player->movement_speed_walk = readF1000(is) * BS;
+ player->movement_speed_crouch = readF1000(is) * BS;
+ player->movement_speed_fast = readF1000(is) * BS;
+ player->movement_speed_climb = readF1000(is) * BS;
+ player->movement_speed_jump = readF1000(is) * BS;
+ player->movement_liquid_fluidity = readF1000(is) * BS;
+ player->movement_liquid_fluidity_smooth = readF1000(is) * BS;
+ player->movement_liquid_sink = readF1000(is) * BS;
+ player->movement_gravity = readF1000(is) * BS;
+ }
else if(command == TOCLIENT_HP)
{
std::string datastring((char*)&data[2], datasize-2);
diff --git a/src/clientmap.cpp b/src/clientmap.cpp
index 800549a3b..0b30453b8 100644
--- a/src/clientmap.cpp
+++ b/src/clientmap.cpp
@@ -864,7 +864,7 @@ void ClientMap::renderPostFx()
// - If the player is in liquid, draw a semi-transparent overlay.
const ContentFeatures& features = nodemgr->get(n);
video::SColor post_effect_color = features.post_effect_color;
- if(features.solidness == 2 && g_settings->getBool("free_move") == false)
+ if(features.solidness == 2 && !(g_settings->getBool("noclip") && m_gamedef->checkLocalPrivilege("noclip")))
{
post_effect_color = video::SColor(255, 0, 0, 0);
}
diff --git a/src/clientserver.h b/src/clientserver.h
index 52b9dc7b0..7fb3e83d2 100644
--- a/src/clientserver.h
+++ b/src/clientserver.h
@@ -364,6 +364,23 @@ enum ToClientCommand
u16 len
u8[len] formname
*/
+
+ TOCLIENT_MOVEMENT = 0x45,
+ /*
+ u16 command
+ f1000 movement_acceleration_default
+ f1000 movement_acceleration_air
+ f1000 movement_acceleration_fast
+ f1000 movement_speed_walk
+ f1000 movement_speed_crouch
+ f1000 movement_speed_fast
+ f1000 movement_speed_climb
+ f1000 movement_speed_jump
+ f1000 movement_liquid_fluidity
+ f1000 movement_liquid_fluidity_smooth
+ f1000 movement_liquid_sink
+ f1000 movement_gravity
+ */
};
enum ToServerCommand
diff --git a/src/clouds.cpp b/src/clouds.cpp
index 146eeb831..5b980a5ba 100644
--- a/src/clouds.cpp
+++ b/src/clouds.cpp
@@ -45,8 +45,7 @@ Clouds::Clouds(
//m_material.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
m_material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
- m_cloud_y = BS*100;
- //m_cloud_y = BS*50;
+ m_cloud_y = BS * g_settings->getS16("cloud_height");
m_box = core::aabbox3d<f32>(-BS*1000000,m_cloud_y-BS,-BS*1000000,
BS*1000000,m_cloud_y+BS,BS*1000000);
diff --git a/src/cmake_config.h.in b/src/cmake_config.h.in
index 4853d854f..51827cce6 100644
--- a/src/cmake_config.h.in
+++ b/src/cmake_config.h.in
@@ -9,6 +9,7 @@
#define CMAKE_USE_GETTEXT @USE_GETTEXT@
#define CMAKE_USE_CURL @USE_CURL@
#define CMAKE_USE_SOUND @USE_SOUND@
+#define CMAKE_USE_FREETYPE @USE_FREETYPE@
#define CMAKE_STATIC_SHAREDIR "@SHAREDIR@"
#ifdef NDEBUG
diff --git a/src/config.h b/src/config.h
index f37ec0fed..37dc6e0ef 100644
--- a/src/config.h
+++ b/src/config.h
@@ -12,6 +12,7 @@
#define USE_GETTEXT 0
#define USE_SOUND 0
#define USE_CURL 0
+#define USE_FREETYPE 0
#define STATIC_SHAREDIR ""
#define BUILD_INFO "non-cmake"
@@ -29,6 +30,8 @@
#define USE_SOUND CMAKE_USE_SOUND
#undef USE_CURL
#define USE_CURL CMAKE_USE_CURL
+ #undef USE_FREETYPE
+ #define USE_FREETYPE CMAKE_USE_FREETYPE
#undef STATIC_SHAREDIR
#define STATIC_SHAREDIR CMAKE_STATIC_SHAREDIR
#undef BUILD_INFO
diff --git a/src/content_abm.cpp b/src/content_abm.cpp
index 9e65a7ab0..ce1751117 100644
--- a/src/content_abm.cpp
+++ b/src/content_abm.cpp
@@ -80,7 +80,8 @@ public:
ServerMap *map = &env->getServerMap();
MapNode n_top = map->getNodeNoEx(p+v3s16(0,1,0));
- if(!ndef->get(n_top).light_propagates ||
+ if((!ndef->get(n_top).light_propagates &&
+ n_top.getContent() != CONTENT_IGNORE) ||
ndef->get(n_top).isLiquid())
{
n.setContent(ndef->getId("mapgen_dirt"));
diff --git a/src/content_mapblock.cpp b/src/content_mapblock.cpp
index aa3c061d6..290890490 100644
--- a/src/content_mapblock.cpp
+++ b/src/content_mapblock.cpp
@@ -733,7 +733,7 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
u16 l = getInteriorLight(n, 1, data);
video::SColor c = MapBlock_LightColor(255, l, decode_light(f.light_source));
- for(u32 j=0; j<4; j++)
+ for(u32 j=0; j<2; j++)
{
video::S3DVertex vertices[4] =
{
@@ -759,16 +759,6 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
for(u16 i=0; i<4; i++)
vertices[i].Pos.rotateXZBy(-45);
}
- else if(j == 2)
- {
- for(u16 i=0; i<4; i++)
- vertices[i].Pos.rotateXZBy(135);
- }
- else if(j == 3)
- {
- for(u16 i=0; i<4; i++)
- vertices[i].Pos.rotateXZBy(-135);
- }
for(u16 i=0; i<4; i++)
{
diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp
index ca5f33609..6cdaaa496 100644
--- a/src/defaultsettings.cpp
+++ b/src/defaultsettings.cpp
@@ -18,6 +18,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
*/
#include "settings.h"
+#include "filesys.h"
void set_default_settings(Settings *settings)
{
@@ -107,6 +108,7 @@ void set_default_settings(Settings *settings)
settings->setDefault("screenshot_path", ".");
settings->setDefault("view_bobbing_amount", "1.0");
settings->setDefault("enable_3d_clouds", "true");
+ settings->setDefault("cloud_height", "120");
settings->setDefault("opaque_water", "false");
settings->setDefault("console_color", "(0,0,0)");
settings->setDefault("console_alpha", "200");
@@ -128,6 +130,19 @@ void set_default_settings(Settings *settings)
settings->setDefault("media_fetch_threads", "8");
+ settings->setDefault("serverlist_url", "servers.minetest.net");
+ settings->setDefault("serverlist_file", "favoriteservers.txt");
+ settings->setDefault("server_announce", "false");
+ settings->setDefault("server_url", "");
+ settings->setDefault("server_address", "");
+ settings->setDefault("server_name", "");
+ settings->setDefault("server_description", "");
+
+ settings->setDefault("font_path", porting::getDataPath("fonts" DIR_DELIM "liberationsans.ttf"));
+ settings->setDefault("font_size", "13");
+ settings->setDefault("mono_font_path", porting::getDataPath("fonts" DIR_DELIM "liberationmono.ttf"));
+ settings->setDefault("mono_font_size", "13");
+
// Server stuff
// "map-dir" doesn't exist by default.
settings->setDefault("default_game", "minetest");
@@ -168,5 +183,42 @@ void set_default_settings(Settings *settings)
settings->setDefault("congestion_control_max_rate", "400");
settings->setDefault("congestion_control_min_rate", "10");
settings->setDefault("remote_media", "");
+ settings->setDefault("debug_log_level", "0");
+ // physics stuff
+ settings->setDefault("movement_acceleration_default", "3");
+ settings->setDefault("movement_acceleration_air", "2");
+ settings->setDefault("movement_acceleration_fast", "10");
+ settings->setDefault("movement_speed_walk", "4");
+ settings->setDefault("movement_speed_crouch", "1.35");
+ settings->setDefault("movement_speed_fast", "20");
+ settings->setDefault("movement_speed_climb", "2");
+ settings->setDefault("movement_speed_jump", "6.5");
+ settings->setDefault("movement_liquid_fluidity", "1");
+ settings->setDefault("movement_liquid_fluidity_smooth", "0.5");
+ settings->setDefault("movement_liquid_sink", "10");
+ settings->setDefault("movement_gravity", "9.81");
+
+ //mapgen related things
+ settings->setDefault("mg_name", "v6");
+ settings->setDefault("water_level", "1");
+ settings->setDefault("chunksize", "5");
+ settings->setDefault("mg_flags", "trees, caves, v6_biome_blend");
+ settings->setDefault("mgv6_freq_desert", "0.45");
+ settings->setDefault("mgv6_freq_beach", "0.15");
+
+ settings->setDefault("mgv6_np_terrain_base", "-4, 20, (250.0, 250, 250), 82341, 5, 0.6");
+ settings->setDefault("mgv6_np_terrain_higher", "20, 16, (500, 500, 500), 85039, 5, 0.6");
+ settings->setDefault("mgv6_np_steepness", "0.85, 0.5, (125, 125, 125), -932, 5, 0.7");
+ settings->setDefault("mgv6_np_height_select", "0.5, 1, (250, 250, 250), 4213, 5, 0.69");
+ settings->setDefault("mgv6_np_trees", "0, 1, (125, 125, 125), 2, 4, 0.66");
+ settings->setDefault("mgv6_np_mud", "4, 2, (200, 200, 200), 91013, 3, 0.55");
+ settings->setDefault("mgv6_np_beach", "0, 1, (250, 250, 250), 59420, 3, 0.50");
+ settings->setDefault("mgv6_np_biome", "0, 1, (250, 250, 250), 9130, 3, 0.50");
+ settings->setDefault("mgv6_np_cave", "6, 6, (250, 250, 250), 34329, 3, 0.50");
+
+ settings->setDefault("mgv7_np_terrain", "10, 12, (350, 350, 350), 82341, 5, 0.6");
+ settings->setDefault("mgv7_np_bgroup", "0.5, 0.3125, (350, 350, 350), 5923, 2, 0.6");
+ settings->setDefault("mgv7_np_heat", "25, 50, (500, 500, 500), 35293, 1, 0");
+ settings->setDefault("mgv7_np_humidity", "50, 31.25, (750, 750, 750), 12094, 2, 0.6");
}
diff --git a/src/environment.cpp b/src/environment.cpp
index 3f94484fe..ebf5e9a63 100644
--- a/src/environment.cpp
+++ b/src/environment.cpp
@@ -1301,6 +1301,7 @@ u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
return id;
}
+#if 0
bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
{
assert(obj);
@@ -1343,6 +1344,7 @@ bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
return succeeded;
}
+#endif
/*
Finds out what new objects have been added to
@@ -1563,13 +1565,15 @@ void ServerEnvironment::removeRemovedObjects()
*/
if(obj->m_static_exists && obj->m_removed)
{
- MapBlock *block = m_map->emergeBlock(obj->m_static_block);
- if(block)
- {
+ MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
+ if (block) {
block->m_static_objects.remove(id);
block->raiseModified(MOD_STATE_WRITE_NEEDED,
"removeRemovedObjects");
obj->m_static_exists = false;
+ } else {
+ infostream << "failed to emerge block from which "
+ "an object to be removed was loaded from. id="<<id<<std::endl;
}
}
@@ -1717,6 +1721,8 @@ void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
If force_delete is set, active object is deleted nevertheless. It
shall only be set so in the destructor of the environment.
+
+ If block wasn't generated (not in memory or on disk),
*/
void ServerEnvironment::deactivateFarObjects(bool force_delete)
{
@@ -2059,20 +2065,37 @@ void ClientEnvironment::step(float dtime)
{
// Gravity
v3f speed = lplayer->getSpeed();
- if(lplayer->swimming_up == false)
- speed.Y -= 9.81 * BS * dtime_part * 2;
+ if(lplayer->in_liquid == false)
+ speed.Y -= lplayer->movement_gravity * dtime_part * 2;
- // Water resistance
- if(lplayer->in_water_stable || lplayer->in_water)
- {
- f32 max_down = 2.0*BS;
- if(speed.Y < -max_down) speed.Y = -max_down;
+ // Liquid floating / sinking
+ if(lplayer->in_liquid && !lplayer->swimming_vertical)
+ speed.Y -= lplayer->movement_liquid_sink * dtime_part * 2;
- f32 max = 2.5*BS;
- if(speed.getLength() > max)
- {
- speed = speed / speed.getLength() * max;
- }
+ // Liquid resistance
+ if(lplayer->in_liquid_stable || lplayer->in_liquid)
+ {
+ // How much the node's viscosity blocks movement, ranges between 0 and 1
+ // Should match the scale at which viscosity increase affects other liquid attributes
+ const f32 viscosity_factor = 0.3;
+
+ v3f d_wanted = -speed / lplayer->movement_liquid_fluidity;
+ f32 dl = d_wanted.getLength();
+ if(dl > lplayer->movement_liquid_fluidity_smooth)
+ dl = lplayer->movement_liquid_fluidity_smooth;
+ dl *= (lplayer->liquid_viscosity * viscosity_factor) + (1 - viscosity_factor);
+
+ v3f d = d_wanted.normalize() * dl;
+ speed += d;
+
+#if 0 // old code
+ if(speed.X > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.X -= lplayer->movement_liquid_fluidity_smooth;
+ if(speed.X < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.X += lplayer->movement_liquid_fluidity_smooth;
+ if(speed.Y > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.Y -= lplayer->movement_liquid_fluidity_smooth;
+ if(speed.Y < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.Y += lplayer->movement_liquid_fluidity_smooth;
+ if(speed.Z > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.Z -= lplayer->movement_liquid_fluidity_smooth;
+ if(speed.Z < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.Z += lplayer->movement_liquid_fluidity_smooth;
+#endif
}
lplayer->setSpeed(speed);
diff --git a/src/environment.h b/src/environment.h
index d17edeacd..d1e61967f 100644
--- a/src/environment.h
+++ b/src/environment.h
@@ -241,8 +241,9 @@ public:
MapBlock.
Caller allocates memory, ServerEnvironment frees memory.
Return value: true if succeeded, false if failed.
+ (note: not used, pending removal from engine)
*/
- bool addActiveObjectAsStatic(ServerActiveObject *object);
+ //bool addActiveObjectAsStatic(ServerActiveObject *object);
/*
Find out what new objects have been added to
diff --git a/src/farmesh.cpp b/src/farmesh.cpp
index bd08acadc..23f3db5f6 100644
--- a/src/farmesh.cpp
+++ b/src/farmesh.cpp
@@ -120,14 +120,14 @@ HeightPoint ground_height(u64 seed, v2s16 p2d)
if(n)
return n->getValue();
HeightPoint hp;
- s16 level = mapgen::find_ground_level_from_noise(seed, p2d, 3);
+ s16 level = Mapgen::find_ground_level_from_noise(seed, p2d, 3);
hp.gh = (level-4)*BS;
hp.ma = (4)*BS;
/*hp.gh = BS*base_rock_level_2d(seed, p2d);
hp.ma = BS*get_mud_add_amount(seed, p2d);*/
- hp.have_sand = mapgen::get_have_beach(seed, p2d);
+ hp.have_sand = Mapgen::get_have_beach(seed, p2d);
if(hp.gh > BS*WATER_LEVEL)
- hp.tree_amount = mapgen::tree_amount_2d(seed, p2d);
+ hp.tree_amount = Mapgen::tree_amount_2d(seed, p2d);
else
hp.tree_amount = 0;
// No mud has been added if mud amount is less than 1
diff --git a/src/game.cpp b/src/game.cpp
index 588a0fef3..c77754b5f 100644
--- a/src/game.cpp
+++ b/src/game.cpp
@@ -1262,7 +1262,7 @@ void the_game(
gui::IGUIStaticText *guitext_info = guienv->addStaticText(
L"",
core::rect<s32>(0,0,400,text_height*5+5) + v2s32(100,200),
- false, false);
+ false, true);
// Status text (displays info when showing and hiding GUI stuff, etc.)
gui::IGUIStaticText *guitext_status = guienv->addStaticText(
@@ -2482,7 +2482,9 @@ void the_game(
// Sign special case, at least until formspec is properly implemented.
// Deprecated?
- if(meta && meta->getString("formspec") == "hack:sign_text_input" && !random_input)
+ if(meta && meta->getString("formspec") == "hack:sign_text_input"
+ && !random_input
+ && !input->isKeyDown(getKeySetting("keymap_sneak")))
{
infostream<<"Launching metadata text input"<<std::endl;
@@ -2497,7 +2499,8 @@ void the_game(
wtext))->drop();
}
// If metadata provides an inventory view, activate it
- else if(meta && meta->getString("formspec") != "" && !random_input)
+ else if(meta && meta->getString("formspec") != "" && !random_input
+ && !input->isKeyDown(getKeySetting("keymap_sneak")))
{
infostream<<"Launching custom inventory view"<<std::endl;
@@ -2528,7 +2531,8 @@ void the_game(
// make that happen
const ItemDefinition &def =
playeritem.getDefinition(itemdef);
- if(def.node_placement_prediction != "")
+ if(def.node_placement_prediction != ""
+ && !nodedef->get(map.getNode(nodepos)).rightclickable)
do{ // breakable
verbosestream<<"Node placement prediction for "
<<playeritem.name<<" is "
diff --git a/src/gettext.h b/src/gettext.h
index 54470cb0d..452787de4 100644
--- a/src/gettext.h
+++ b/src/gettext.h
@@ -12,6 +12,11 @@
#define gettext_noop(String) String
#define N_(String) gettext_noop (String)
+#if defined(_WIN32)
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#endif
+
inline void init_gettext(const char *path) {
#if USE_GETTEXT
// don't do this if MSVC compiler is used, it gives an assertion fail
@@ -20,14 +25,44 @@ inline void init_gettext(const char *path) {
#endif
bindtextdomain(PROJECT_NAME, path);
textdomain(PROJECT_NAME);
+#if defined(_WIN32)
+ // As linux is successfully switched to UTF-8 completely at about year 2005
+ // Windows still uses obsolete codepage based locales because you
+ // cannot recompile closed-source applications
+
+ // Set character encoding for Win32
+ char *tdomain = textdomain( (char *) NULL );
+ if( tdomain == NULL )
+ {
+ fprintf( stderr, "warning: domainname parameter is the null pointer, default domain is not set\n" );
+ tdomain = (char *) "messages";
+ }
+ /*char *codeset = */bind_textdomain_codeset( tdomain, "UTF-8" );
+ //fprintf( stdout, "%s: debug: domainname = %s; codeset = %s\n", argv[0], tdomain, codeset );
+#endif // defined(_WIN32)
#endif
}
inline wchar_t* chartowchar_t(const char *str)
{
+ wchar_t* nstr = 0;
+#if defined(_WIN32)
+ int nResult = MultiByteToWideChar( CP_UTF8, 0, (LPCSTR) str, -1, 0, 0 );
+ if( nResult == 0 )
+ {
+ fprintf( stderr, "error: MultiByteToWideChar returned null\n" );
+ }
+ else
+ {
+ nstr = new wchar_t[nResult];
+ MultiByteToWideChar( CP_UTF8, 0, (LPCSTR) str, -1, (WCHAR *) nstr, nResult );
+ }
+#else
size_t l = strlen(str)+1;
- wchar_t* nstr = new wchar_t[l];
+ nstr = new wchar_t[l];
mbstowcs(nstr, str, l);
+#endif
+
return nstr;
}
@@ -38,12 +73,12 @@ inline wchar_t* wgettext(const char *str)
inline void changeCtype(const char *l)
{
- char *ret = NULL;
+ /*char *ret = NULL;
ret = setlocale(LC_CTYPE, l);
if(ret == NULL)
infostream<<"locale could not be set"<<std::endl;
else
- infostream<<"locale has been set to:"<<ret<<std::endl;
+ infostream<<"locale has been set to:"<<ret<<std::endl;*/
}
#define GETTEXT_HEADER
#endif
diff --git a/src/guiChatConsole.cpp b/src/guiChatConsole.cpp
index 0101b99bb..13883901e 100644
--- a/src/guiChatConsole.cpp
+++ b/src/guiChatConsole.cpp
@@ -32,6 +32,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "gettext.h"
+#if USE_FREETYPE
+#include "xCGUITTFont.h"
+#endif
+
inline u32 clamp_u8(s32 value)
{
return (u32) MYMIN(MYMAX(value, 0), 255);
@@ -90,8 +94,14 @@ GUIChatConsole::GUIChatConsole(
// load the font
// FIXME should a custom texture_path be searched too?
+ #if USE_FREETYPE
+ std::string font_name = g_settings->get("mono_font_path");
+ u16 font_size = g_settings->getU16("mono_font_size");
+ m_font = gui::CGUITTFont::createTTFont(env, font_name.c_str(), font_size);
+ #else
std::string font_name = "fontdejavusansmono.png";
m_font = env->getFont(getTexturePath(font_name).c_str());
+ #endif
if (m_font == NULL)
{
dstream << "Unable to load font: " << font_name << std::endl;
diff --git a/src/guiConfigureWorld.cpp b/src/guiConfigureWorld.cpp
new file mode 100644
index 000000000..fdcd0d3e8
--- /dev/null
+++ b/src/guiConfigureWorld.cpp
@@ -0,0 +1,705 @@
+/*
+Minetest-c55
+Copyright (C) 2012 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 <iostream>
+#include <string>
+#include <map>
+
+#include "guiConfigureWorld.h"
+#include "guiMessageMenu.h"
+#include <IGUIButton.h>
+#include <IGUICheckBox.h>
+#include <IGUIListBox.h>
+#include <IGUIStaticText.h>
+#include <IGUITreeView.h>
+#include "gettext.h"
+#include "util/string.h"
+#include "settings.h"
+#include "filesys.h"
+
+enum
+{
+ GUI_ID_MOD_TREEVIEW = 101,
+ GUI_ID_ENABLED_CHECKBOX,
+ GUI_ID_ENABLEALL,
+ GUI_ID_DISABLEALL,
+ GUI_ID_DEPENDS_LISTBOX,
+ GUI_ID_RDEPENDS_LISTBOX,
+ GUI_ID_CANCEL,
+ GUI_ID_SAVE
+};
+
+#define QUESTIONMARK_STR L"?"
+#define CHECKMARK_STR L"\411"
+#define CROSS_STR L"\403"
+
+GUIConfigureWorld::GUIConfigureWorld(gui::IGUIEnvironment* env,
+ gui::IGUIElement* parent, s32 id,
+ IMenuManager *menumgr, WorldSpec wspec):
+ GUIModalMenu(env, parent, id, menumgr),
+ m_wspec(wspec),
+ m_gspec(findWorldSubgame(m_wspec.path)),
+ m_menumgr(menumgr)
+{
+ //will be initialized in regenerateGUI()
+ m_treeview=NULL;
+
+ // game mods
+ m_gamemods = flattenModTree(getModsInPath(m_gspec.gamemods_path));
+
+ // world mods
+ std::string worldmods_path = wspec.path + DIR_DELIM + "worldmods";
+ m_worldmods = flattenModTree(getModsInPath(worldmods_path));
+
+ // fill m_addontree with add-on mods
+ std::set<std::string> paths = m_gspec.addon_mods_paths;
+ for(std::set<std::string>::iterator it=paths.begin();
+ it != paths.end(); ++it)
+ {
+ std::map<std::string,ModSpec> mods = getModsInPath(*it);
+ m_addontree.insert(mods.begin(), mods.end());
+ }
+
+ // expand modpacks
+ m_addonmods = flattenModTree(m_addontree);
+
+ // collect reverse dependencies
+ for(std::map<std::string, ModSpec>::iterator it = m_addonmods.begin();
+ it != m_addonmods.end(); ++it)
+ {
+ std::string modname = (*it).first;
+ ModSpec mod = (*it).second;
+ for(std::set<std::string>::iterator dep_it = mod.depends.begin();
+ dep_it != mod.depends.end(); ++dep_it)
+ {
+ m_reverse_depends.insert(std::make_pair((*dep_it),modname));
+ }
+ }
+
+ m_settings.readConfigFile((m_wspec.path + DIR_DELIM + "world.mt").c_str());
+ std::vector<std::string> names = m_settings.getNames();
+
+ // mod_names contains the names of mods mentioned in the world.mt file
+ std::set<std::string> mod_names;
+ for(std::vector<std::string>::iterator it = names.begin();
+ it != names.end(); ++it)
+ {
+ std::string name = *it;
+ if (name.compare(0,9,"load_mod_")==0)
+ mod_names.insert(name.substr(9));
+ }
+
+ // find new mods (installed but not mentioned in world.mt)
+ for(std::map<std::string, ModSpec>::iterator it = m_addonmods.begin();
+ it != m_addonmods.end(); ++it)
+ {
+ std::string modname = (*it).first;
+ ModSpec mod = (*it).second;
+ // a mod is new if it is not a modpack, and does not occur in
+ // mod_names
+ if(!mod.is_modpack &&
+ mod_names.count(modname) == 0)
+ m_new_mod_names.insert(modname);
+ }
+ if(!m_new_mod_names.empty())
+ {
+ GUIMessageMenu *menu =
+ new GUIMessageMenu(Environment, Parent, -1, m_menumgr,
+ wgettext("Warning: Some mods are not configured yet.\n"
+ "They will be enabled by default when you save the configuration. "));
+ menu->drop();
+ }
+
+
+ // find missing mods (mentioned in world.mt, but not installed)
+ std::set<std::string> missing_mods;
+ for(std::set<std::string>::iterator it = mod_names.begin();
+ it != mod_names.end(); ++it)
+ {
+ std::string modname = *it;
+ if(m_addonmods.count(modname) == 0)
+ missing_mods.insert(modname);
+ }
+ if(!missing_mods.empty())
+ {
+ GUIMessageMenu *menu =
+ new GUIMessageMenu(Environment, Parent, -1, m_menumgr,
+ wgettext("Warning: Some configured mods are missing.\n"
+ "Their setting will be removed when you save the configuration. "));
+ for(std::set<std::string>::iterator it = missing_mods.begin();
+ it != missing_mods.end(); ++it)
+ m_settings.remove("load_mod_"+(*it));
+ menu->drop();
+ }
+}
+
+void GUIConfigureWorld::drawMenu()
+{
+ gui::IGUISkin* skin = Environment->getSkin();
+ if (!skin)
+ return;
+ video::IVideoDriver* driver = Environment->getVideoDriver();
+
+ video::SColor bgcolor(140,0,0,0);
+ driver->draw2DRectangle(bgcolor, AbsoluteRect, &AbsoluteClippingRect);
+
+ gui::IGUIElement::draw();
+}
+
+
+void GUIConfigureWorld::regenerateGui(v2u32 screensize)
+{
+
+ /*
+ Remove stuff
+ */
+ removeChildren();
+
+ /*
+ Calculate new sizes and positions
+ */
+ core::rect<s32> rect(
+ screensize.X/2 - 580/2,
+ screensize.Y/2 - 300/2,
+ screensize.X/2 + 580/2,
+ screensize.Y/2 + 300/2
+ );
+
+ DesiredRect = rect;
+ recalculateAbsolutePosition(false);
+
+ v2s32 size = rect.getSize();
+
+ v2s32 topleft = v2s32(10, 10);
+
+ /*
+ Add stuff
+ */
+ changeCtype("");
+ {
+ core::rect<s32> rect(0, 0, 200, 20);
+ rect += topleft;
+ //proper text is set below, when a mod is selected
+ m_modname_text = Environment->addStaticText(L"Mod: N/A", rect, false,
+ false, this, -1);
+ }
+ {
+ core::rect<s32> rect(0, 0, 200, 20);
+ rect += v2s32(0, 25) + topleft;
+ m_enabled_checkbox =
+ Environment->addCheckBox(false, rect, this, GUI_ID_ENABLED_CHECKBOX,
+ wgettext("enabled"));
+ m_enabled_checkbox->setVisible(false);
+ }
+ {
+ core::rect<s32> rect(0, 0, 85, 30);
+ rect = rect + v2s32(0, 25) + topleft;
+ m_enableall = Environment->addButton(rect, this, GUI_ID_ENABLEALL,
+ wgettext("Enable All"));
+ m_enableall->setVisible(false);
+ }
+ {
+ core::rect<s32> rect(0, 0, 85, 30);
+ rect = rect + v2s32(115, 25) + topleft;
+ m_disableall = Environment->addButton(rect, this, GUI_ID_DISABLEALL,
+ wgettext("Disable All"));
+ m_disableall->setVisible(false);
+ }
+ {
+ core::rect<s32> rect(0, 0, 200, 20);
+ rect += v2s32(0, 60) + topleft;
+ Environment->addStaticText(wgettext("depends on:"),
+ rect, false, false, this, -1);
+ }
+ {
+ core::rect<s32> rect(0, 0, 200, 85);
+ rect += v2s32(0, 80) + topleft;
+ m_dependencies_listbox =
+ Environment->addListBox(rect, this, GUI_ID_DEPENDS_LISTBOX, true);
+ }
+ {
+ core::rect<s32> rect(0, 0, 200, 20);
+ rect += v2s32(0, 175) + topleft;
+ Environment->addStaticText(wgettext("is required by:"),
+ rect, false, false, this, -1);
+ }
+ {
+ core::rect<s32> rect(0, 0, 200, 85);
+ rect += v2s32(0, 195) + topleft;
+ m_rdependencies_listbox =
+ Environment->addListBox(rect,this, GUI_ID_RDEPENDS_LISTBOX,true);
+ }
+ {
+ core::rect<s32> rect(0, 0, 340, 250);
+ rect += v2s32(220, 0) + topleft;
+ m_treeview = Environment->addTreeView(rect, this,
+ GUI_ID_MOD_TREEVIEW,true);
+ gui::IGUITreeViewNode* node
+ = m_treeview->getRoot()->addChildBack(L"Add-Ons");
+ buildTreeView(m_addontree, node);
+ }
+ {
+ core::rect<s32> rect(0, 0, 120, 30);
+ rect = rect + v2s32(330, 270) - topleft;
+ Environment->addButton(rect, this, GUI_ID_CANCEL,
+ wgettext("Cancel"));
+ }
+ {
+ core::rect<s32> rect(0, 0, 120, 30);
+ rect = rect + v2s32(460, 270) - topleft;
+ Environment->addButton(rect, this, GUI_ID_SAVE,
+ wgettext("Save"));
+ }
+ changeCtype("C");
+
+ // at start, none of the treeview nodes is selected, so we select
+ // the first element in the treeview of mods manually here.
+ if(m_treeview->getRoot()->hasChilds())
+ {
+ m_treeview->getRoot()->getFirstChild()->setExpanded(true);
+ m_treeview->getRoot()->getFirstChild()->setSelected(true);
+ // Because a manual ->setSelected() doesn't cause an event, we
+ // have to do this here:
+ adjustSidebar();
+ }
+}
+
+bool GUIConfigureWorld::OnEvent(const SEvent& event)
+{
+
+ gui::IGUITreeViewNode* selected_node = NULL;
+ if(m_treeview != NULL)
+ selected_node = m_treeview->getSelected();
+
+ if(event.EventType==EET_KEY_INPUT_EVENT && event.KeyInput.PressedDown)
+ {
+ switch (event.KeyInput.Key) {
+ case KEY_ESCAPE: {
+ quitMenu();
+ return true;
+ }
+ // irrlicht's built-in TreeView gui has no keyboard control,
+ // so we do it here: up/down to select prev/next node,
+ // left/right to collapse/expand nodes, space to toggle
+ // enabled/disabled.
+ case KEY_DOWN: {
+ if(selected_node != NULL)
+ {
+ gui::IGUITreeViewNode* node = selected_node->getNextVisible();
+ if(node != NULL)
+ {
+ node->setSelected(true);
+ adjustSidebar();
+ }
+ }
+ return true;
+ }
+ case KEY_UP: {
+ if(selected_node != NULL)
+ {
+ gui::IGUITreeViewNode* node = selected_node->getPrevSibling();
+ if(node!=NULL)
+ {
+ node->setSelected(true);
+ adjustSidebar();
+ }
+ else
+ {
+ gui::IGUITreeViewNode* parent = selected_node->getParent();
+ if(selected_node == parent->getFirstChild() &&
+ parent != m_treeview->getRoot())
+ {
+ parent->setSelected(true);
+ adjustSidebar();
+ }
+ }
+ }
+ return true;
+ }
+ case KEY_RIGHT: {
+ if(selected_node != NULL && selected_node->hasChilds())
+ selected_node->setExpanded(true);
+ return true;
+ }
+ case KEY_LEFT: {
+ if(selected_node != NULL && selected_node->hasChilds())
+ selected_node->setExpanded(false);
+ return true;
+ }
+ case KEY_SPACE: {
+ if(selected_node != NULL && !selected_node->hasChilds() &&
+ selected_node->getText() != NULL)
+ {
+ std::string modname = wide_to_narrow(selected_node->getText());
+ bool checked = m_enabled_checkbox->isChecked();
+ m_enabled_checkbox->setChecked(!checked);
+ setEnabled(modname,!checked);
+ }
+ return true;
+ }
+ default: {}
+ }
+ }
+ if(event.EventType==EET_GUI_EVENT)
+ {
+ if(event.GUIEvent.EventType==gui::EGET_ELEMENT_FOCUS_LOST
+ && isVisible())
+ {
+ if(!canTakeFocus(event.GUIEvent.Element))
+ {
+ dstream<<"GUIConfigureWorld: Not allowing focus change."
+ <<std::endl;
+ // Returning true disables focus change
+ return true;
+ }
+ }
+ if(event.GUIEvent.EventType==gui::EGET_BUTTON_CLICKED){
+ switch(event.GUIEvent.Caller->getID()){
+ case GUI_ID_CANCEL: {
+ quitMenu();
+ return true;
+ }
+ case GUI_ID_SAVE: {
+ for(std::set<std::string>::iterator it = m_new_mod_names.begin();
+ it!= m_new_mod_names.end(); ++it)
+ {
+ m_settings.setBool("load_mod_"+(*it),true);
+ }
+ std::string worldmtfile = m_wspec.path+DIR_DELIM+"world.mt";
+ m_settings.updateConfigFile(worldmtfile.c_str());
+
+ // The trailing spaces are because there seems to be a
+ // bug in the text-size calculation. if the trailing
+ // spaces are removed from the message text, the
+ // message gets wrapped and parts of it are cut off:
+ GUIMessageMenu *menu =
+ new GUIMessageMenu(Environment, Parent, -1, m_menumgr,
+ wgettext("Configuration saved. "));
+ menu->drop();
+
+ ModConfiguration modconf(m_wspec.path);
+ if(!modconf.isConsistent())
+ {
+ GUIMessageMenu *menu =
+ new GUIMessageMenu(Environment, Parent, -1, m_menumgr,
+ wgettext("Warning: Configuration not consistent. "));
+ menu->drop();
+ }
+
+ quitMenu();
+ return true;
+ }
+ case GUI_ID_ENABLEALL: {
+ if(selected_node != NULL && selected_node->getParent() == m_treeview->getRoot())
+ {
+ enableAllMods(m_addonmods,true);
+ }
+ else if(selected_node != NULL && selected_node->getText() != NULL)
+ {
+ std::string modname = wide_to_narrow(selected_node->getText());
+ std::map<std::string, ModSpec>::iterator mod_it = m_addonmods.find(modname);
+ if(mod_it != m_addonmods.end())
+ enableAllMods(mod_it->second.modpack_content,true);
+ }
+ return true;
+ }
+ case GUI_ID_DISABLEALL: {
+ if(selected_node != NULL && selected_node->getParent() == m_treeview->getRoot())
+ {
+ enableAllMods(m_addonmods,false);
+ }
+ if(selected_node != NULL && selected_node->getText() != NULL)
+ {
+ std::string modname = wide_to_narrow(selected_node->getText());
+ std::map<std::string, ModSpec>::iterator mod_it = m_addonmods.find(modname);
+ if(mod_it != m_addonmods.end())
+ enableAllMods(mod_it->second.modpack_content,false);
+ }
+ return true;
+ }
+ }
+ }
+ if(event.GUIEvent.EventType==gui::EGET_CHECKBOX_CHANGED &&
+ event.GUIEvent.Caller->getID() == GUI_ID_ENABLED_CHECKBOX)
+ {
+ if(selected_node != NULL && !selected_node->hasChilds() &&
+ selected_node->getText() != NULL)
+ {
+ std::string modname = wide_to_narrow(selected_node->getText());
+ setEnabled(modname, m_enabled_checkbox->isChecked());
+ }
+ return true;
+ }
+ if(event.GUIEvent.EventType==gui::EGET_TREEVIEW_NODE_SELECT &&
+ event.GUIEvent.Caller->getID() == GUI_ID_MOD_TREEVIEW)
+ {
+ selecting_dep = -1;
+ selecting_rdep = -1;
+ adjustSidebar();
+ return true;
+ }
+ if(event.GUIEvent.EventType==gui::EGET_LISTBOX_CHANGED &&
+ event.GUIEvent.Caller->getID() == GUI_ID_DEPENDS_LISTBOX)
+ {
+ selecting_dep = m_dependencies_listbox->getSelected();
+ selecting_rdep = -1;
+ return true;
+ }
+ if(event.GUIEvent.EventType==gui::EGET_LISTBOX_CHANGED &&
+ event.GUIEvent.Caller->getID() == GUI_ID_RDEPENDS_LISTBOX)
+ {
+ selecting_dep = -1;
+ selecting_rdep = m_rdependencies_listbox->getSelected();
+ return true;
+ }
+
+ //double click in a dependency listbox: find corresponding
+ //treeviewnode and select it:
+ if(event.GUIEvent.EventType==gui::EGET_LISTBOX_SELECTED_AGAIN)
+ {
+ gui::IGUIListBox* box = NULL;
+ if(event.GUIEvent.Caller->getID() == GUI_ID_DEPENDS_LISTBOX)
+ {
+ box = m_dependencies_listbox;
+ if(box->getSelected() != selecting_dep)
+ return true;
+ }
+ if(event.GUIEvent.Caller->getID() == GUI_ID_RDEPENDS_LISTBOX)
+ {
+ box = m_rdependencies_listbox;
+ if(box->getSelected() != selecting_rdep)
+ return true;
+ }
+ if(box != NULL && box->getSelected() != -1 &&
+ box->getListItem(box->getSelected()) != NULL)
+ {
+ std::string modname =
+ wide_to_narrow(box->getListItem(box->getSelected()));
+ std::map<std::string, gui::IGUITreeViewNode*>::iterator it =
+ m_nodes.find(modname);
+ if(it != m_nodes.end())
+ {
+ // select node and make sure node is visible by
+ // expanding all parents
+ gui::IGUITreeViewNode* node = (*it).second;
+ node->setSelected(true);
+ while(!node->isVisible() &&
+ node->getParent() != m_treeview->getRoot())
+ {
+ node = node->getParent();
+ node->setExpanded(true);
+ }
+ adjustSidebar();
+ }
+ }
+ return true;
+ }
+ }
+
+ return Parent ? Parent->OnEvent(event) : false;
+}
+
+void GUIConfigureWorld::buildTreeView(std::map<std::string, ModSpec> mods,
+ gui::IGUITreeViewNode* node)
+{
+ for(std::map<std::string,ModSpec>::iterator it = mods.begin();
+ it != mods.end(); ++it)
+ {
+ std::string modname = (*it).first;
+ ModSpec mod = (*it).second;
+ gui::IGUITreeViewNode* new_node =
+ node->addChildBack(narrow_to_wide(modname).c_str());
+ m_nodes.insert(std::make_pair(modname, new_node));
+ if(mod.is_modpack)
+ buildTreeView(mod.modpack_content, new_node);
+ else
+ {
+ // set icon for node: ? for new mods, x for disabled mods,
+ // checkmark for enabled mods
+ if(m_new_mod_names.count(modname) > 0)
+ {
+ new_node->setIcon(QUESTIONMARK_STR);
+ }
+ else
+ {
+ bool mod_enabled = true;
+ if(m_settings.exists("load_mod_"+modname))
+ mod_enabled = m_settings.getBool("load_mod_"+modname);
+ if(mod_enabled)
+ new_node->setIcon(CHECKMARK_STR);
+ else
+ new_node->setIcon(CROSS_STR);
+ }
+ }
+ }
+}
+
+
+void GUIConfigureWorld::adjustSidebar()
+{
+ gui::IGUITreeViewNode* node = m_treeview->getSelected();
+ std::wstring modname_w;
+ if(node->getText() != NULL)
+ modname_w = node->getText();
+ else
+ modname_w = L"N/A";
+ std::string modname = wide_to_narrow(modname_w);
+
+ ModSpec mspec;
+ std::map<std::string, ModSpec>::iterator it = m_addonmods.find(modname);
+ if(it != m_addonmods.end())
+ mspec = it->second;
+
+ m_dependencies_listbox->clear();
+ m_rdependencies_listbox->clear();
+
+ // if no mods installed, there is nothing to enable/disable, so we
+ // don't show buttons or checkbox on the sidebar
+ if(node->getParent() == m_treeview->getRoot() && !node->hasChilds())
+ {
+ m_disableall->setVisible(false);
+ m_enableall->setVisible(false);
+ m_enabled_checkbox->setVisible(false);
+ return;
+ }
+
+ // a modpack is not enabled/disabled by itself, only its cotnents
+ // are. so we show show enable/disable all buttons, but hide the
+ // checkbox
+ if(node->getParent() == m_treeview->getRoot() ||
+ mspec.is_modpack)
+ {
+ m_enabled_checkbox->setVisible(false);
+ m_disableall->setVisible(true);
+ m_enableall->setVisible(true);
+ m_modname_text->setText((L"Modpack: "+modname_w).c_str());
+ return;
+ }
+
+ // for a normal mod, we hide the enable/disable all buttons, but show the checkbox.
+ m_disableall->setVisible(false);
+ m_enableall->setVisible(false);
+ m_enabled_checkbox->setVisible(true);
+ m_modname_text->setText((L"Mod: "+modname_w).c_str());
+
+ // the mod is enabled unless it is disabled in the world.mt settings.
+ bool mod_enabled = true;
+ if(m_settings.exists("load_mod_"+modname))
+ mod_enabled = m_settings.getBool("load_mod_"+modname);
+ m_enabled_checkbox->setChecked(mod_enabled);
+
+ for(std::set<std::string>::iterator it=mspec.depends.begin();
+ it != mspec.depends.end(); ++it)
+ {
+ // check if it is an add-on mod or a game/world mod. We only
+ // want to show add-ons
+ std::string dependency = (*it);
+ if(m_gamemods.count(dependency) > 0)
+ dependency += " (" + m_gspec.id + ")";
+ else if(m_worldmods.count(dependency) > 0)
+ dependency += " (" + m_wspec.name + ")";
+ else if(m_addonmods.count(dependency) == 0)
+ dependency += " (missing)";
+ m_dependencies_listbox->addItem(narrow_to_wide(dependency).c_str());
+ }
+
+ // reverse dependencies of this mod:
+ std::pair< std::multimap<std::string, std::string>::iterator,
+ std::multimap<std::string, std::string>::iterator > rdep =
+ m_reverse_depends.equal_range(modname);
+ for(std::multimap<std::string,std::string>::iterator it = rdep.first;
+ it != rdep.second; ++it)
+ {
+ // check if it is an add-on mod or a game/world mod. We only
+ // want to show add-ons
+ std::string rdependency = (*it).second;
+ if(m_addonmods.count(rdependency) > 0)
+ m_rdependencies_listbox->addItem(narrow_to_wide(rdependency).c_str());
+ }
+}
+
+void GUIConfigureWorld::enableAllMods(std::map<std::string, ModSpec> mods,bool enable)
+{
+ for(std::map<std::string, ModSpec>::iterator it = mods.begin();
+ it != mods.end(); ++it)
+ {
+ ModSpec mod = (*it).second;
+ if(mod.is_modpack)
+ // a modpack, recursively enable all mods in it
+ enableAllMods(mod.modpack_content,enable);
+ else // not a modpack
+ setEnabled(mod.name, enable);
+
+ }
+}
+
+void GUIConfigureWorld::enableMod(std::string modname)
+{
+ std::map<std::string, ModSpec>::iterator mod_it = m_addonmods.find(modname);
+ if(mod_it == m_addonmods.end()){
+ errorstream << "enableMod() called with invalid mod name \"" << modname << "\"" << std::endl;
+ return;
+ }
+ ModSpec mspec = mod_it->second;
+ m_settings.setBool("load_mod_"+modname,true);
+ std::map<std::string,gui::IGUITreeViewNode*>::iterator it =
+ m_nodes.find(modname);
+ if(it != m_nodes.end())
+ (*it).second->setIcon(CHECKMARK_STR);
+ m_new_mod_names.erase(modname);
+ //also enable all dependencies
+ for(std::set<std::string>::iterator it=mspec.depends.begin();
+ it != mspec.depends.end(); ++it)
+ {
+ std::string dependency = *it;
+ // only enable it if it is an add-on mod
+ if(m_addonmods.count(dependency) > 0)
+ enableMod(dependency);
+ }
+}
+
+void GUIConfigureWorld::disableMod(std::string modname)
+{
+ std::map<std::string, ModSpec>::iterator mod_it = m_addonmods.find(modname);
+ if(mod_it == m_addonmods.end()){
+ errorstream << "disableMod() called with invalid mod name \"" << modname << "\"" << std::endl;
+ return;
+ }
+
+ m_settings.setBool("load_mod_"+modname,false);
+ std::map<std::string,gui::IGUITreeViewNode*>::iterator it =
+ m_nodes.find(modname);
+ if(it != m_nodes.end())
+ (*it).second->setIcon(CROSS_STR);
+ m_new_mod_names.erase(modname);
+ //also disable all mods that depend on this one
+ std::pair<std::multimap<std::string, std::string>::iterator,
+ std::multimap<std::string, std::string>::iterator > rdep =
+ m_reverse_depends.equal_range(modname);
+ for(std::multimap<std::string,std::string>::iterator it = rdep.first;
+ it != rdep.second; ++it)
+ {
+ std::string rdependency = (*it).second;
+ // only disable it if it is an add-on mod
+ if(m_addonmods.count(rdependency) > 0)
+ disableMod(rdependency);
+ }
+}
+
diff --git a/src/guiConfigureWorld.h b/src/guiConfigureWorld.h
new file mode 100644
index 000000000..2280c6dbe
--- /dev/null
+++ b/src/guiConfigureWorld.h
@@ -0,0 +1,110 @@
+/*
+Minetest-c55
+Copyright (C) 2012 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 GUICONFIGUREWORLD_HEADER
+#define GUICONFIGUREWORLD_HEADER
+
+#include "irrlichttypes_extrabloated.h"
+#include "modalMenu.h"
+#include "mods.h"
+#include "subgame.h"
+#include "settings.h"
+
+
+namespace irr{
+ namespace gui{
+ class IGUITreeViewNode;
+ }
+}
+
+class GUIConfigureWorld : public GUIModalMenu
+{
+public:
+ GUIConfigureWorld(gui::IGUIEnvironment* env,
+ gui::IGUIElement* parent, s32 id,
+ IMenuManager *menumgr, WorldSpec wspec);
+
+ void regenerateGui(v2u32 screensize);
+
+ void drawMenu();
+
+ bool OnEvent(const SEvent& event);
+
+private:
+ WorldSpec m_wspec;
+ SubgameSpec m_gspec;
+
+ // tree of installed add-on mods. key is the mod name, modpacks
+ // are not expanded.
+ std::map<std::string, ModSpec> m_addontree;
+
+ // like m_addontree, but modpacks are expanded.
+ std::map<std::string, ModSpec> m_addonmods;
+
+ // list of game mods (flattened)
+ std::map<std::string, ModSpec> m_gamemods;
+
+ // list of world mods (flattened)
+ std::map<std::string, ModSpec> m_worldmods;
+
+ // for each mod, the set of mods depending on it
+ std::multimap<std::string, std::string> m_reverse_depends;
+
+ // the settings in the world.mt file
+ Settings m_settings;
+
+ // mods that are installed but not mentioned in world.mt file
+ std::set<std::string> m_new_mod_names;
+
+ // maps modnames to nodes in m_treeview
+ std::map<std::string,gui::IGUITreeViewNode*> m_nodes;
+
+ gui::IGUIStaticText* m_modname_text;
+ gui::IGUITreeView* m_treeview;
+ gui::IGUIButton* m_enableall;
+ gui::IGUIButton* m_disableall;
+ gui::IGUICheckBox* m_enabled_checkbox;
+ gui::IGUIListBox* m_dependencies_listbox;
+ gui::IGUIListBox* m_rdependencies_listbox;
+ void buildTreeView(std::map<std::string,ModSpec> mods,
+ gui::IGUITreeViewNode* node);
+ void adjustSidebar();
+ void enableAllMods(std::map<std::string,ModSpec> mods, bool enable);
+ void setEnabled(std::string modname, bool enable)
+ {
+ if(enable)
+ enableMod(modname);
+ else
+ disableMod(modname);
+ };
+
+ void enableMod(std::string modname);
+ void disableMod(std::string modname);
+
+ // hack to work around wonky handling of double-click in
+ // irrlicht. store selected index of listbox items here so event
+ // handling can check whether it was a real double click on the
+ // same item. (irrlicht also reports a double click if you rapidly
+ // select two different items.)
+ int selecting_dep;
+ int selecting_rdep;
+
+ IMenuManager* m_menumgr;
+};
+#endif
diff --git a/src/guiKeyChangeMenu.cpp b/src/guiKeyChangeMenu.cpp
index 6b0412821..3f6d03ebb 100644
--- a/src/guiKeyChangeMenu.cpp
+++ b/src/guiKeyChangeMenu.cpp
@@ -369,21 +369,21 @@ void GUIKeyChangeMenu::add_key(int id, std::string button_name, std::string sett
void GUIKeyChangeMenu::init_keys()
{
- this->add_key(GUI_ID_KEY_FORWARD_BUTTON, "Forward", "keymap_forward");
- this->add_key(GUI_ID_KEY_BACKWARD_BUTTON, "Backward", "keymap_backward");
- this->add_key(GUI_ID_KEY_LEFT_BUTTON, "Left", "keymap_left");
- this->add_key(GUI_ID_KEY_RIGHT_BUTTON, "Right", "keymap_right");
- this->add_key(GUI_ID_KEY_USE_BUTTON, "Use", "keymap_special1");
- this->add_key(GUI_ID_KEY_JUMP_BUTTON, "Jump", "keymap_jump");
- this->add_key(GUI_ID_KEY_SNEAK_BUTTON, "Sneak", "keymap_sneak");
- this->add_key(GUI_ID_KEY_DROP_BUTTON, "Drop", "keymap_drop");
- this->add_key(GUI_ID_KEY_INVENTORY_BUTTON, "Inventory", "keymap_inventory");
- this->add_key(GUI_ID_KEY_CHAT_BUTTON, "Chat", "keymap_chat");
- this->add_key(GUI_ID_KEY_CMD_BUTTON, "Command", "keymap_cmd");
- this->add_key(GUI_ID_KEY_CONSOLE_BUTTON, "Console", "keymap_console");
- this->add_key(GUI_ID_KEY_FLY_BUTTON, "Toggle fly", "keymap_freemove");
- this->add_key(GUI_ID_KEY_FAST_BUTTON, "Toggle fast", "keymap_fastmove");
- this->add_key(GUI_ID_KEY_NOCLIP_BUTTON, "Toggle noclip", "keymap_noclip");
- this->add_key(GUI_ID_KEY_RANGE_BUTTON, "Range select", "keymap_rangeselect");
- this->add_key(GUI_ID_KEY_DUMP_BUTTON, "Print stacks", "keymap_print_debug_stacks");
+ this->add_key(GUI_ID_KEY_FORWARD_BUTTON, gettext("Forward"), "keymap_forward");
+ this->add_key(GUI_ID_KEY_BACKWARD_BUTTON, gettext("Backward"), "keymap_backward");
+ this->add_key(GUI_ID_KEY_LEFT_BUTTON, gettext("Left"), "keymap_left");
+ this->add_key(GUI_ID_KEY_RIGHT_BUTTON, gettext("Right"), "keymap_right");
+ this->add_key(GUI_ID_KEY_USE_BUTTON, gettext("Use"), "keymap_special1");
+ this->add_key(GUI_ID_KEY_JUMP_BUTTON, gettext("Jump"), "keymap_jump");
+ this->add_key(GUI_ID_KEY_SNEAK_BUTTON, gettext("Sneak"), "keymap_sneak");
+ this->add_key(GUI_ID_KEY_DROP_BUTTON, gettext("Drop"), "keymap_drop");
+ this->add_key(GUI_ID_KEY_INVENTORY_BUTTON, gettext("Inventory"), "keymap_inventory");
+ this->add_key(GUI_ID_KEY_CHAT_BUTTON, gettext("Chat"), "keymap_chat");
+ this->add_key(GUI_ID_KEY_CMD_BUTTON, gettext("Command"), "keymap_cmd");
+ this->add_key(GUI_ID_KEY_CONSOLE_BUTTON, gettext("Console"), "keymap_console");
+ this->add_key(GUI_ID_KEY_FLY_BUTTON, gettext("Toggle fly"), "keymap_freemove");
+ this->add_key(GUI_ID_KEY_FAST_BUTTON, gettext("Toggle fast"), "keymap_fastmove");
+ this->add_key(GUI_ID_KEY_NOCLIP_BUTTON, gettext("Toggle noclip"), "keymap_noclip");
+ this->add_key(GUI_ID_KEY_RANGE_BUTTON, gettext("Range select"), "keymap_rangeselect");
+ this->add_key(GUI_ID_KEY_DUMP_BUTTON, gettext("Print stacks"), "keymap_print_debug_stacks");
}
diff --git a/src/guiMainMenu.cpp b/src/guiMainMenu.cpp
index bac9052b9..ca334198f 100644
--- a/src/guiMainMenu.cpp
+++ b/src/guiMainMenu.cpp
@@ -20,6 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "guiMainMenu.h"
#include "guiKeyChangeMenu.h"
#include "guiCreateWorld.h"
+#include "guiConfigureWorld.h"
#include "guiMessageMenu.h"
#include "guiConfirmMenu.h"
#include "debug.h"
@@ -107,6 +108,7 @@ enum
GUI_ID_ENABLE_PARTICLES_CB,
GUI_ID_DAMAGE_CB,
GUI_ID_CREATIVE_CB,
+ GUI_ID_PUBLIC_CB,
GUI_ID_JOIN_GAME_BUTTON,
GUI_ID_CHANGE_KEYS_BUTTON,
GUI_ID_DELETE_WORLD_BUTTON,
@@ -114,6 +116,9 @@ enum
GUI_ID_CONFIGURE_WORLD_BUTTON,
GUI_ID_WORLD_LISTBOX,
GUI_ID_TAB_CONTROL,
+ GUI_ID_SERVERLIST,
+ GUI_ID_SERVERLIST_TOGGLE,
+ GUI_ID_SERVERLIST_DELETE,
};
enum
@@ -256,7 +261,7 @@ void GUIMainMenu::regenerateGui(v2u32 screensize)
//const wchar_t *text = L"H\nY\nB\nR\nI\nD";
const wchar_t *text = L"T\nA\nP\nE\n\nA\nN\nD\n\nG\nL\nU\nE";
gui::IGUIStaticText *t =
- Environment->addStaticText(text, rect, false, false, this, -1);
+ Environment->addStaticText(text, rect, false, true, this, -1);
t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER);
}
u32 bs = 5;
@@ -355,20 +360,20 @@ void GUIMainMenu::regenerateGui(v2u32 screensize)
rect += m_topleft_client + v2s32(15, 0);
const wchar_t *text = L"C\nL\nI\nE\nN\nT";
gui::IGUIStaticText *t =
- Environment->addStaticText(text, rect, false, false, this, -1);
+ Environment->addStaticText(text, rect, false, true, this, -1);
t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER);
}
// Nickname + password
{
core::rect<s32> rect(0, 0, 110, 20);
- rect += m_topleft_client + v2s32(35+30, 50+6);
+ rect += m_topleft_client + v2s32(m_size_client.X-60-100, 10+6);
Environment->addStaticText(wgettext("Name/Password"),
rect, false, true, this, -1);
}
changeCtype("C");
{
- core::rect<s32> rect(0, 0, 230, 30);
- rect += m_topleft_client + v2s32(160+30, 50);
+ core::rect<s32> rect(0, 0, 120, 30);
+ rect += m_topleft_client + v2s32(m_size_client.X-60-100, 50);
gui::IGUIElement *e =
Environment->addEditBox(m_data->name.c_str(), rect, true, this, GUI_ID_NAME_INPUT);
if(m_data->name == L"")
@@ -376,7 +381,7 @@ void GUIMainMenu::regenerateGui(v2u32 screensize)
}
{
core::rect<s32> rect(0, 0, 120, 30);
- rect += m_topleft_client + v2s32(m_size_client.X-60-100, 50);
+ rect += m_topleft_client + v2s32(m_size_client.X-60-100, 90);
gui::IGUIEditBox *e =
Environment->addEditBox(L"", rect, true, this, 264);
e->setPasswordBox(true);
@@ -385,17 +390,29 @@ void GUIMainMenu::regenerateGui(v2u32 screensize)
}
changeCtype("");
+ // Server List
+ {
+ core::rect<s32> rect(0, 0, 390, 160);
+ rect += m_topleft_client + v2s32(50, 10);
+ gui::IGUIListBox *e = Environment->addListBox(rect, this,
+ GUI_ID_SERVERLIST);
+ e->setDrawBackground(true);
+ if (m_data->serverlist_show_available == false)
+ m_data->servers = ServerList::getLocal();
+ updateGuiServerList();
+ e->setSelected(0);
+ }
// Address + port
{
core::rect<s32> rect(0, 0, 110, 20);
- rect += m_topleft_client + v2s32(35+30, 100+6);
+ rect += m_topleft_client + v2s32(50, m_size_client.Y-50-15+6);
Environment->addStaticText(wgettext("Address/Port"),
rect, false, true, this, -1);
}
changeCtype("C");
{
- core::rect<s32> rect(0, 0, 230, 30);
- rect += m_topleft_client + v2s32(160+30, 100);
+ core::rect<s32> rect(0, 0, 260, 30);
+ rect += m_topleft_client + v2s32(50, m_size_client.Y-25-15);
gui::IGUIElement *e =
Environment->addEditBox(m_data->address.c_str(), rect, true,
this, GUI_ID_ADDRESS_INPUT);
@@ -404,18 +421,43 @@ void GUIMainMenu::regenerateGui(v2u32 screensize)
}
{
core::rect<s32> rect(0, 0, 120, 30);
- rect += m_topleft_client + v2s32(m_size_client.X-60-100, 100);
+ rect += m_topleft_client + v2s32(50+260+10, m_size_client.Y-25-15);
Environment->addEditBox(m_data->port.c_str(), rect, true,
this, GUI_ID_PORT_INPUT);
}
changeCtype("");
+ #if USE_CURL
+ // Toggle Serverlist (Favorites/Online)
+ {
+ core::rect<s32> rect(0, 0, 260, 30);
+ rect += m_topleft_client + v2s32(50,
+ 180);
+ gui::IGUIButton *e = Environment->addButton(rect, this, GUI_ID_SERVERLIST_TOGGLE,
+ wgettext("Show Public"));
+ e->setIsPushButton(true);
+ if (m_data->serverlist_show_available)
+ {
+ e->setText(wgettext("Show Favorites"));
+ e->setPressed();
+ }
+ }
+ #endif
+ // Delete Local Favorite
+ {
+ core::rect<s32> rect(0, 0, 120, 30);
+ rect += m_topleft_client + v2s32(50+260+10, 180);
+ gui::IGUIButton *e = Environment->addButton(rect, this, GUI_ID_SERVERLIST_DELETE,
+ wgettext("Delete"));
+ if (m_data->serverlist_show_available) // Hidden on Show-Online mode
+ e->setVisible(false);
+ }
// Start game button
{
- core::rect<s32> rect(0, 0, 180, 30);
- rect += m_topleft_client + v2s32(m_size_client.X-180-30,
- m_size_client.Y-30-15);
+ core::rect<s32> rect(0, 0, 120, 30);
+ rect += m_topleft_client + v2s32(m_size_client.X-130-30,
+ m_size_client.Y-25-15);
Environment->addButton(rect, this, GUI_ID_JOIN_GAME_BUTTON,
- wgettext("Start Game / Connect"));
+ wgettext("Connect"));
}
changeCtype("C");
}
@@ -428,7 +470,7 @@ void GUIMainMenu::regenerateGui(v2u32 screensize)
rect += m_topleft_client + v2s32(15, 0);
const wchar_t *text = L"C\nL\nI\nE\nN\nT";
gui::IGUIStaticText *t =
- Environment->addStaticText(text, rect, false, false, this, -1);
+ Environment->addStaticText(text, rect, false, true, this, -1);
t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER);
}
// Nickname + password
@@ -505,7 +547,7 @@ void GUIMainMenu::regenerateGui(v2u32 screensize)
rect += m_topleft_server + v2s32(15, 0);
const wchar_t *text = L"S\nE\nR\nV\nE\nR";
gui::IGUIStaticText *t =
- Environment->addStaticText(text, rect, false, false, this, -1);
+ Environment->addStaticText(text, rect, false, true, this, -1);
t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER);
}
// Server parameters
@@ -521,6 +563,14 @@ void GUIMainMenu::regenerateGui(v2u32 screensize)
Environment->addCheckBox(m_data->enable_damage, rect, this, GUI_ID_DAMAGE_CB,
wgettext("Enable Damage"));
}
+ #if USE_CURL
+ {
+ core::rect<s32> rect(0, 0, 250, 30);
+ rect += m_topleft_server + v2s32(30+20+250+20, 60);
+ Environment->addCheckBox(m_data->enable_public, rect, this, GUI_ID_PUBLIC_CB,
+ wgettext("Public"));
+ }
+ #endif
// Delete world button
{
core::rect<s32> rect(0, 0, 130, 30);
@@ -557,7 +607,7 @@ void GUIMainMenu::regenerateGui(v2u32 screensize)
rect += m_topleft_client + v2s32(15, 0);
const wchar_t *text = L"S\nE\nT\nT\nI\nN\nG\nS";
gui::IGUIStaticText *t =
- Environment->addStaticText(text, rect, false, false, this, -1);
+ Environment->addStaticText(text, rect, false, true, this, -1);
t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER);
}
s32 option_x = 70;
@@ -660,7 +710,7 @@ void GUIMainMenu::regenerateGui(v2u32 screensize)
rect += m_topleft_client + v2s32(15, 0);
const wchar_t *text = L"C\nR\nE\nD\nI\nT\nS";
gui::IGUIStaticText *t =
- Environment->addStaticText(text, rect, false, false, this, -1);
+ Environment->addStaticText(text, rect, false, true, this, -1);
t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER);
}
{
@@ -801,6 +851,11 @@ void GUIMainMenu::readInput(MainMenuData *dst)
dst->enable_damage = ((gui::IGUICheckBox*)e)->isChecked();
}
{
+ gui::IGUIElement *e = getElementFromId(GUI_ID_PUBLIC_CB);
+ if(e != NULL && e->getType() == gui::EGUIET_CHECK_BOX)
+ dst->enable_public = ((gui::IGUICheckBox*)e)->isChecked();
+ }
+ {
gui::IGUIElement *e = getElementFromId(GUI_ID_FANCYTREE_CB);
if(e != NULL && e->getType() == gui::EGUIET_CHECK_BOX)
dst->fancy_trees = ((gui::IGUICheckBox*)e)->isChecked();
@@ -868,6 +923,12 @@ void GUIMainMenu::readInput(MainMenuData *dst)
if(e != NULL && e->getType() == gui::EGUIET_LIST_BOX)
dst->selected_world = ((gui::IGUIListBox*)e)->getSelected();
}
+ {
+ ServerListSpec server =
+ getServerListSpec(wide_to_narrow(dst->address), wide_to_narrow(dst->port));
+ dst->servername = server["name"].asString();
+ dst->serverdescription = server["description"].asString();
+ }
}
void GUIMainMenu::acceptInput()
@@ -912,6 +973,11 @@ bool GUIMainMenu::OnEvent(const SEvent& event)
regenerateGui(m_screensize_old);
return true;
}
+ if(event.GUIEvent.EventType==gui::EGET_LISTBOX_CHANGED && event.GUIEvent.Caller->getID() == GUI_ID_SERVERLIST)
+ {
+ serverListOnSelected();
+ return true;
+ }
if(event.GUIEvent.EventType==gui::EGET_BUTTON_CLICKED)
{
switch(event.GUIEvent.Caller->getID())
@@ -919,7 +985,8 @@ bool GUIMainMenu::OnEvent(const SEvent& event)
case GUI_ID_JOIN_GAME_BUTTON: {
MainMenuData cur;
readInput(&cur);
- if(cur.address == L"" && getTab() == TAB_MULTIPLAYER){
+ if (getTab() == TAB_MULTIPLAYER && cur.address == L"")
+ {
(new GUIMessageMenu(env, parent, -1, menumgr,
wgettext("Address required."))
)->drop();
@@ -981,12 +1048,62 @@ bool GUIMainMenu::OnEvent(const SEvent& event)
return true;
}
case GUI_ID_CONFIGURE_WORLD_BUTTON: {
- GUIMessageMenu *menu = new GUIMessageMenu(env, parent,
- -1, menumgr,
- wgettext("Nothing here"));
- menu->drop();
+ MainMenuData cur;
+ readInput(&cur);
+ if(cur.selected_world == -1)
+ {
+ (new GUIMessageMenu(env, parent, -1, menumgr,
+ wgettext("Cannot configure world: Nothing selected"))
+ )->drop();
+ }
+ else
+ {
+ WorldSpec wspec = m_data->worlds[cur.selected_world];
+ GUIConfigureWorld *menu = new GUIConfigureWorld(env, parent,
+ -1, menumgr, wspec);
+ menu->drop();
+ }
return true;
}
+ case GUI_ID_SERVERLIST_DELETE: {
+ gui::IGUIListBox *serverlist = (gui::IGUIListBox*)getElementFromId(GUI_ID_SERVERLIST);
+ s32 selected = ((gui::IGUIListBox*)serverlist)->getSelected();
+ if (selected == -1) return true;
+ ServerList::deleteEntry(m_data->servers[selected]);
+ m_data->servers = ServerList::getLocal();
+ updateGuiServerList();
+ if (selected > 0)
+ selected -= 1;
+ serverlist->setSelected(selected);
+ serverListOnSelected();
+ return true;
+ }
+ #if USE_CURL
+ case GUI_ID_SERVERLIST_TOGGLE: {
+ gui::IGUIElement *togglebutton = getElementFromId(GUI_ID_SERVERLIST_TOGGLE);
+ gui::IGUIElement *deletebutton = getElementFromId(GUI_ID_SERVERLIST_DELETE);
+ gui::IGUIListBox *serverlist = (gui::IGUIListBox*)getElementFromId(GUI_ID_SERVERLIST);
+ if (m_data->serverlist_show_available) // switch to favorite list
+ {
+ m_data->servers = ServerList::getLocal();
+ togglebutton->setText(wgettext("Show Public"));
+ deletebutton->setVisible(true);
+ updateGuiServerList();
+ serverlist->setSelected(0);
+ }
+ else // switch to online list
+ {
+ m_data->servers = ServerList::getOnline();
+ togglebutton->setText(wgettext("Show Favorites"));
+ deletebutton->setVisible(false);
+ updateGuiServerList();
+ serverlist->setSelected(0);
+ }
+ serverListOnSelected();
+
+ m_data->serverlist_show_available = !m_data->serverlist_show_available;
+ }
+ #endif
}
}
if(event.GUIEvent.EventType==gui::EGET_EDITBOX_ENTER)
@@ -1009,6 +1126,14 @@ bool GUIMainMenu::OnEvent(const SEvent& event)
m_data->address = L""; // Force local game
quitMenu();
return true;
+ case GUI_ID_SERVERLIST:
+ gui::IGUIListBox *serverlist = (gui::IGUIListBox*)getElementFromId(GUI_ID_SERVERLIST);
+ if (serverlist->getSelected() > -1)
+ {
+ acceptInput();
+ quitMenu();
+ return true;
+ }
}
}
}
@@ -1053,3 +1178,73 @@ void GUIMainMenu::displayMessageMenu(std::wstring msg)
{
(new GUIMessageMenu(env, parent, -1, menumgr, msg))->drop();
}
+
+void GUIMainMenu::updateGuiServerList()
+{
+ gui::IGUIListBox *serverlist = (gui::IGUIListBox *)getElementFromId(GUI_ID_SERVERLIST);
+ serverlist->clear();
+
+ for(std::vector<ServerListSpec>::iterator i = m_data->servers.begin();
+ i != m_data->servers.end(); i++)
+ {
+ std::string text;
+
+ if ((*i)["clients"].asString().size())
+ text += (*i)["clients"].asString();
+ if ((*i)["clients_max"].asString().size())
+ text += "/" + (*i)["clients_max"].asString();
+ text += " ";
+ if ((*i)["version"].asString().size())
+ text += (*i)["version"].asString() + " ";
+ if ((*i)["password"].asString().size())
+ text += "*";
+ if ((*i)["creative"].asString().size())
+ text += "C";
+ if ((*i)["damage"].asString().size())
+ text += "D";
+ if ((*i)["pvp"].asString().size())
+ text += "P";
+ text += " ";
+
+ if ((*i)["name"] != "" && (*i)["description"] != "")
+ text += (*i)["name"].asString() + " (" + (*i)["description"].asString() + ")";
+ else if ((*i)["name"] !="")
+ text += (*i)["name"].asString();
+ else
+ text += (*i)["address"].asString() + ":" + (*i)["port"].asString();
+
+ serverlist->addItem(narrow_to_wide(text).c_str());
+ }
+}
+
+void GUIMainMenu::serverListOnSelected()
+{
+ if (!m_data->servers.empty())
+ {
+ gui::IGUIListBox *serverlist = (gui::IGUIListBox*)getElementFromId(GUI_ID_SERVERLIST);
+ u16 id = serverlist->getSelected();
+ //if (id < 0) return; // u16>0!
+ ((gui::IGUIEditBox*)getElementFromId(GUI_ID_ADDRESS_INPUT))
+ ->setText(narrow_to_wide(m_data->servers[id]["address"].asString()).c_str());
+ ((gui::IGUIEditBox*)getElementFromId(GUI_ID_PORT_INPUT))
+ ->setText(narrow_to_wide(m_data->servers[id]["port"].asString()).c_str());
+ }
+}
+
+ServerListSpec GUIMainMenu::getServerListSpec(std::string address, std::string port)
+{
+ ServerListSpec server;
+ server["address"] = address;
+ server["port"] = port;
+ for(std::vector<ServerListSpec>::iterator i = m_data->servers.begin();
+ i != m_data->servers.end(); i++)
+ {
+ if ((*i)["address"] == address && (*i)["port"] == port)
+ {
+ server["description"] = (*i)["description"];
+ server["name"] = (*i)["name"];
+ break;
+ }
+ }
+ return server;
+}
diff --git a/src/guiMainMenu.h b/src/guiMainMenu.h
index f87ad0fdb..558a05d59 100644
--- a/src/guiMainMenu.h
+++ b/src/guiMainMenu.h
@@ -25,6 +25,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <string>
#include <list>
#include "subgame.h"
+#include "serverlist.h"
+
class IGameCallback;
struct MainMenuData
@@ -33,6 +35,8 @@ struct MainMenuData
// Generic
int selected_tab;
// Client options
+ std::string servername;
+ std::string serverdescription;
std::wstring address;
std::wstring port;
std::wstring name;
@@ -51,6 +55,7 @@ struct MainMenuData
// Server options
bool creative_mode;
bool enable_damage;
+ bool enable_public;
int selected_world;
bool simple_singleplayer_mode;
// Actions
@@ -58,8 +63,11 @@ struct MainMenuData
std::string create_world_gameid;
bool only_refresh;
+ bool serverlist_show_available; // if false show local favorites only
+
std::vector<WorldSpec> worlds;
std::vector<SubgameSpec> games;
+ std::vector<ServerListSpec> servers;
MainMenuData():
// Generic
@@ -70,10 +78,13 @@ struct MainMenuData
// Server opts
creative_mode(false),
enable_damage(false),
+ enable_public(false),
selected_world(0),
simple_singleplayer_mode(false),
// Actions
- only_refresh(false)
+ only_refresh(false),
+
+ serverlist_show_available(false)
{}
};
@@ -110,12 +121,15 @@ private:
gui::IGUIElement* parent;
s32 id;
IMenuManager *menumgr;
-
+
bool m_is_regenerating;
v2s32 m_topleft_client;
v2s32 m_size_client;
v2s32 m_topleft_server;
v2s32 m_size_server;
+ void updateGuiServerList();
+ void serverListOnSelected();
+ ServerListSpec getServerListSpec(std::string address, std::string port);
};
#endif
diff --git a/src/guiPauseMenu.cpp b/src/guiPauseMenu.cpp
index c800cf952..f6cbf248f 100644
--- a/src/guiPauseMenu.cpp
+++ b/src/guiPauseMenu.cpp
@@ -1,253 +1,253 @@
-/*
-Minetest-c55
-Copyright (C) 2010 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 "guiPauseMenu.h"
-#include "debug.h"
-#include "serialization.h"
-#include "porting.h"
-#include "config.h"
-#include "main.h"
-#include <IGUICheckBox.h>
-#include <IGUIEditBox.h>
-#include <IGUIButton.h>
-#include <IGUIStaticText.h>
-#include <IGUIFont.h>
-#include "gettext.h"
-#include "util/string.h"
-
-GUIPauseMenu::GUIPauseMenu(gui::IGUIEnvironment* env,
- gui::IGUIElement* parent, s32 id,
- IGameCallback *gamecallback,
- IMenuManager *menumgr,
- bool simple_singleplayer_mode):
- GUIModalMenu(env, parent, id, menumgr),
- m_gamecallback(gamecallback),
- m_simple_singleplayer_mode(simple_singleplayer_mode)
-{
-}
-
-GUIPauseMenu::~GUIPauseMenu()
-{
- removeChildren();
-}
-
-void GUIPauseMenu::removeChildren()
-{
- {
- gui::IGUIElement *e = getElementFromId(256);
- if(e != NULL)
- e->remove();
- }
- {
- gui::IGUIElement *e = getElementFromId(257);
- if(e != NULL)
- e->remove();
- }
- {
- gui::IGUIElement *e = getElementFromId(258);
- if(e != NULL)
- e->remove();
- }
- {
- gui::IGUIElement *e = getElementFromId(259);
- if(e != NULL)
- e->remove();
- }
- {
- gui::IGUIElement *e = getElementFromId(260);
- if(e != NULL)
- e->remove();
- }
- {
- gui::IGUIElement *e = getElementFromId(261);
- if(e != NULL)
- e->remove();
- }
-}
-
-void GUIPauseMenu::regenerateGui(v2u32 screensize)
-{
- /*
- Remove stuff
- */
- removeChildren();
-
- /*
- Calculate new sizes and positions
- */
- core::rect<s32> rect(
- screensize.X/2 - 580/2,
- screensize.Y/2 - 300/2,
- screensize.X/2 + 580/2,
- screensize.Y/2 + 300/2
- );
-
- DesiredRect = rect;
- recalculateAbsolutePosition(false);
-
- v2s32 size = rect.getSize();
-
- /*
- Add stuff
- */
- const s32 btn_height = 30;
- const s32 btn_gap = 20;
- const s32 btn_num = m_simple_singleplayer_mode ? 3 : 4;
- s32 btn_y = size.Y/2-((btn_num*btn_height+(btn_num-1)*btn_gap))/2;
- changeCtype("");
- {
- core::rect<s32> rect(0, 0, 140, btn_height);
- rect = rect + v2s32(size.X/2-140/2, btn_y);
- Environment->addButton(rect, this, 256,
- wgettext("Continue"));
- }
- btn_y += btn_height + btn_gap;
- if(!m_simple_singleplayer_mode)
- {
- {
- core::rect<s32> rect(0, 0, 140, btn_height);
- rect = rect + v2s32(size.X/2-140/2, btn_y);
- Environment->addButton(rect, this, 261,
- wgettext("Change Password"));
- }
- btn_y += btn_height + btn_gap;
- }
- {
- core::rect<s32> rect(0, 0, 140, btn_height);
- rect = rect + v2s32(size.X/2-140/2, btn_y);
- Environment->addButton(rect, this, 260,
- wgettext("Exit to Menu"));
- }
- btn_y += btn_height + btn_gap;
- {
- core::rect<s32> rect(0, 0, 140, btn_height);
- rect = rect + v2s32(size.X/2-140/2, btn_y);
- Environment->addButton(rect, this, 257,
- wgettext("Exit to OS"));
- }
-
- {
- core::rect<s32> rect(0, 0, 180, 240);
- rect = rect + v2s32(size.X/2 + 90, size.Y/2-rect.getHeight()/2);
- Environment->addStaticText(chartowchar_t(gettext(
- "Default Controls:\n"
- "- WASD: Walk\n"
- "- Mouse left: dig/hit\n"
- "- Mouse right: place/use\n"
- "- Mouse wheel: select item\n"
- "- 0...9: select item\n"
- "- Shift: sneak\n"
- "- R: Toggle viewing all loaded chunks\n"
- "- I: Inventory menu\n"
- "- ESC: This menu\n"
- "- T: Chat\n"
- )), rect, false, true, this, 258);
- }
- {
- core::rect<s32> rect(0, 0, 180, 220);
- rect = rect + v2s32(size.X/2 - 90 - rect.getWidth(), size.Y/2-rect.getHeight()/2);
-
- v2u32 max_texture_size;
- {
- video::IVideoDriver* driver = Environment->getVideoDriver();
- max_texture_size = driver->getMaxTextureSize();
- }
-
- std::ostringstream os;
- os<<"Minetest\n";
- os<<BUILD_INFO<<"\n";
- os<<"path_user = "<<wrap_rows(porting::path_user, 20)<<"\n";
-
- Environment->addStaticText(narrow_to_wide(os.str()).c_str(), rect, false, true, this, 259);
- }
- changeCtype("C");
-}
-
-void GUIPauseMenu::drawMenu()
-{
- gui::IGUISkin* skin = Environment->getSkin();
- if (!skin)
- return;
- video::IVideoDriver* driver = Environment->getVideoDriver();
-
- video::SColor bgcolor(140,0,0,0);
- driver->draw2DRectangle(bgcolor, AbsoluteRect, &AbsoluteClippingRect);
-
- gui::IGUIElement::draw();
-}
-
-bool GUIPauseMenu::OnEvent(const SEvent& event)
-{
-
- if(event.EventType==EET_KEY_INPUT_EVENT)
- {
- if(event.KeyInput.PressedDown)
- {
- if(event.KeyInput.Key==KEY_ESCAPE)
- {
- quitMenu();
- return true;
- }
- else if(event.KeyInput.Key==KEY_RETURN)
- {
- quitMenu();
- return true;
- }
- }
- }
- if(event.EventType==EET_GUI_EVENT)
- {
- if(event.GUIEvent.EventType==gui::EGET_ELEMENT_FOCUS_LOST
- && isVisible())
- {
- if(!canTakeFocus(event.GUIEvent.Element))
- {
- dstream<<"GUIPauseMenu: Not allowing focus change."
- <<std::endl;
- // Returning true disables focus change
- return true;
- }
- }
- if(event.GUIEvent.EventType==gui::EGET_BUTTON_CLICKED)
- {
- switch(event.GUIEvent.Caller->getID())
- {
- case 256: // continue
- quitMenu();
- // ALWAYS return immediately after quitMenu()
- return true;
- case 261:
- quitMenu();
- m_gamecallback->changePassword();
- return true;
- case 260: // disconnect
- m_gamecallback->disconnect();
- quitMenu();
- return true;
- case 257: // exit
- m_gamecallback->exitToOS();
- quitMenu();
- return true;
- }
- }
- }
-
- return Parent ? Parent->OnEvent(event) : false;
-}
-
+/*
+Minetest-c55
+Copyright (C) 2010 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 "guiPauseMenu.h"
+#include "debug.h"
+#include "serialization.h"
+#include "porting.h"
+#include "config.h"
+#include "main.h"
+#include <IGUICheckBox.h>
+#include <IGUIEditBox.h>
+#include <IGUIButton.h>
+#include <IGUIStaticText.h>
+#include <IGUIFont.h>
+#include "gettext.h"
+#include "util/string.h"
+
+GUIPauseMenu::GUIPauseMenu(gui::IGUIEnvironment* env,
+ gui::IGUIElement* parent, s32 id,
+ IGameCallback *gamecallback,
+ IMenuManager *menumgr,
+ bool simple_singleplayer_mode):
+ GUIModalMenu(env, parent, id, menumgr),
+ m_gamecallback(gamecallback),
+ m_simple_singleplayer_mode(simple_singleplayer_mode)
+{
+}
+
+GUIPauseMenu::~GUIPauseMenu()
+{
+ removeChildren();
+}
+
+void GUIPauseMenu::removeChildren()
+{
+ {
+ gui::IGUIElement *e = getElementFromId(256);
+ if(e != NULL)
+ e->remove();
+ }
+ {
+ gui::IGUIElement *e = getElementFromId(257);
+ if(e != NULL)
+ e->remove();
+ }
+ {
+ gui::IGUIElement *e = getElementFromId(258);
+ if(e != NULL)
+ e->remove();
+ }
+ {
+ gui::IGUIElement *e = getElementFromId(259);
+ if(e != NULL)
+ e->remove();
+ }
+ {
+ gui::IGUIElement *e = getElementFromId(260);
+ if(e != NULL)
+ e->remove();
+ }
+ {
+ gui::IGUIElement *e = getElementFromId(261);
+ if(e != NULL)
+ e->remove();
+ }
+}
+
+void GUIPauseMenu::regenerateGui(v2u32 screensize)
+{
+ /*
+ Remove stuff
+ */
+ removeChildren();
+
+ /*
+ Calculate new sizes and positions
+ */
+ core::rect<s32> rect(
+ screensize.X/2 - 580/2,
+ screensize.Y/2 - 300/2,
+ screensize.X/2 + 580/2,
+ screensize.Y/2 + 300/2
+ );
+
+ DesiredRect = rect;
+ recalculateAbsolutePosition(false);
+
+ v2s32 size = rect.getSize();
+
+ /*
+ Add stuff
+ */
+ const s32 btn_height = 30;
+ const s32 btn_gap = 20;
+ const s32 btn_num = m_simple_singleplayer_mode ? 3 : 4;
+ s32 btn_y = size.Y/2-((btn_num*btn_height+(btn_num-1)*btn_gap))/2;
+ changeCtype("");
+ {
+ core::rect<s32> rect(0, 0, 140, btn_height);
+ rect = rect + v2s32(size.X/2-140/2, btn_y);
+ Environment->addButton(rect, this, 256,
+ wgettext("Continue"));
+ }
+ btn_y += btn_height + btn_gap;
+ if(!m_simple_singleplayer_mode)
+ {
+ {
+ core::rect<s32> rect(0, 0, 140, btn_height);
+ rect = rect + v2s32(size.X/2-140/2, btn_y);
+ Environment->addButton(rect, this, 261,
+ wgettext("Change Password"));
+ }
+ btn_y += btn_height + btn_gap;
+ }
+ {
+ core::rect<s32> rect(0, 0, 140, btn_height);
+ rect = rect + v2s32(size.X/2-140/2, btn_y);
+ Environment->addButton(rect, this, 260,
+ wgettext("Exit to Menu"));
+ }
+ btn_y += btn_height + btn_gap;
+ {
+ core::rect<s32> rect(0, 0, 140, btn_height);
+ rect = rect + v2s32(size.X/2-140/2, btn_y);
+ Environment->addButton(rect, this, 257,
+ wgettext("Exit to OS"));
+ }
+
+ {
+ core::rect<s32> rect(0, 0, 180, 240);
+ rect = rect + v2s32(size.X/2 + 90, size.Y/2-rect.getHeight()/2);
+ Environment->addStaticText(chartowchar_t(gettext(
+ "Default Controls:\n"
+ "- WASD: Walk\n"
+ "- Mouse left: dig/hit\n"
+ "- Mouse right: place/use\n"
+ "- Mouse wheel: select item\n"
+ "- 0...9: select item\n"
+ "- Shift: sneak\n"
+ "- R: Toggle viewing all loaded chunks\n"
+ "- I: Inventory menu\n"
+ "- ESC: This menu\n"
+ "- T: Chat\n"
+ )), rect, false, true, this, 258);
+ }
+ {
+ core::rect<s32> rect(0, 0, 180, 220);
+ rect = rect + v2s32(size.X/2 - 90 - rect.getWidth(), size.Y/2-rect.getHeight()/2);
+
+ v2u32 max_texture_size;
+ {
+ video::IVideoDriver* driver = Environment->getVideoDriver();
+ max_texture_size = driver->getMaxTextureSize();
+ }
+
+ std::ostringstream os;
+ os<<"Minetest\n";
+ os<<BUILD_INFO<<"\n";
+ os<<"path_user = "<<wrap_rows(porting::path_user, 20)<<"\n";
+
+ Environment->addStaticText(narrow_to_wide(os.str()).c_str(), rect, false, true, this, 259);
+ }
+ changeCtype("C");
+}
+
+void GUIPauseMenu::drawMenu()
+{
+ gui::IGUISkin* skin = Environment->getSkin();
+ if (!skin)
+ return;
+ video::IVideoDriver* driver = Environment->getVideoDriver();
+
+ video::SColor bgcolor(140,0,0,0);
+ driver->draw2DRectangle(bgcolor, AbsoluteRect, &AbsoluteClippingRect);
+
+ gui::IGUIElement::draw();
+}
+
+bool GUIPauseMenu::OnEvent(const SEvent& event)
+{
+
+ if(event.EventType==EET_KEY_INPUT_EVENT)
+ {
+ if(event.KeyInput.PressedDown)
+ {
+ if(event.KeyInput.Key==KEY_ESCAPE)
+ {
+ quitMenu();
+ return true;
+ }
+ else if(event.KeyInput.Key==KEY_RETURN)
+ {
+ quitMenu();
+ return true;
+ }
+ }
+ }
+ if(event.EventType==EET_GUI_EVENT)
+ {
+ if(event.GUIEvent.EventType==gui::EGET_ELEMENT_FOCUS_LOST
+ && isVisible())
+ {
+ if(!canTakeFocus(event.GUIEvent.Element))
+ {
+ dstream<<"GUIPauseMenu: Not allowing focus change."
+ <<std::endl;
+ // Returning true disables focus change
+ return true;
+ }
+ }
+ if(event.GUIEvent.EventType==gui::EGET_BUTTON_CLICKED)
+ {
+ switch(event.GUIEvent.Caller->getID())
+ {
+ case 256: // continue
+ quitMenu();
+ // ALWAYS return immediately after quitMenu()
+ return true;
+ case 261:
+ quitMenu();
+ m_gamecallback->changePassword();
+ return true;
+ case 260: // disconnect
+ m_gamecallback->disconnect();
+ quitMenu();
+ return true;
+ case 257: // exit
+ m_gamecallback->exitToOS();
+ quitMenu();
+ return true;
+ }
+ }
+ }
+
+ return Parent ? Parent->OnEvent(event) : false;
+}
+
diff --git a/src/guiPauseMenu.h b/src/guiPauseMenu.h
index e28159a93..4b15fc74f 100644
--- a/src/guiPauseMenu.h
+++ b/src/guiPauseMenu.h
@@ -1,60 +1,60 @@
-/*
-Minetest-c55
-Copyright (C) 2010 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 GUIPAUSEMENU_HEADER
-#define GUIPAUSEMENU_HEADER
-
-#include "irrlichttypes_extrabloated.h"
-#include "modalMenu.h"
-
-class IGameCallback
-{
-public:
- virtual void exitToOS() = 0;
- virtual void disconnect() = 0;
- virtual void changePassword() = 0;
-};
-
-class GUIPauseMenu : public GUIModalMenu
-{
-public:
- GUIPauseMenu(gui::IGUIEnvironment* env,
- gui::IGUIElement* parent, s32 id,
- IGameCallback *gamecallback,
- IMenuManager *menumgr,
- bool simple_singleplayer_mode);
- ~GUIPauseMenu();
-
- void removeChildren();
- /*
- Remove and re-add (or reposition) stuff
- */
- void regenerateGui(v2u32 screensize);
-
- void drawMenu();
-
- bool OnEvent(const SEvent& event);
-
-private:
- IGameCallback *m_gamecallback;
- bool m_simple_singleplayer_mode;
-};
-
-#endif
-
+/*
+Minetest-c55
+Copyright (C) 2010 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 GUIPAUSEMENU_HEADER
+#define GUIPAUSEMENU_HEADER
+
+#include "irrlichttypes_extrabloated.h"
+#include "modalMenu.h"
+
+class IGameCallback
+{
+public:
+ virtual void exitToOS() = 0;
+ virtual void disconnect() = 0;
+ virtual void changePassword() = 0;
+};
+
+class GUIPauseMenu : public GUIModalMenu
+{
+public:
+ GUIPauseMenu(gui::IGUIEnvironment* env,
+ gui::IGUIElement* parent, s32 id,
+ IGameCallback *gamecallback,
+ IMenuManager *menumgr,
+ bool simple_singleplayer_mode);
+ ~GUIPauseMenu();
+
+ void removeChildren();
+ /*
+ Remove and re-add (or reposition) stuff
+ */
+ void regenerateGui(v2u32 screensize);
+
+ void drawMenu();
+
+ bool OnEvent(const SEvent& event);
+
+private:
+ IGameCallback *m_gamecallback;
+ bool m_simple_singleplayer_mode;
+};
+
+#endif
+
diff --git a/src/guiTextInputMenu.cpp b/src/guiTextInputMenu.cpp
index 094532a62..857c26a45 100644
--- a/src/guiTextInputMenu.cpp
+++ b/src/guiTextInputMenu.cpp
@@ -29,6 +29,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "gettext.h"
+#if USE_FREETYPE
+#include "intlGUIEditBox.h"
+#endif
+
GUITextInputMenu::GUITextInputMenu(gui::IGUIEnvironment* env,
gui::IGUIElement* parent, s32 id,
IMenuManager *menumgr,
@@ -105,8 +109,12 @@ void GUITextInputMenu::regenerateGui(v2u32 screensize)
{
core::rect<s32> rect(0, 0, 300, 30);
rect = rect + v2s32(size.X/2-300/2, size.Y/2-30/2-25);
- gui::IGUIElement *e =
- Environment->addEditBox(text.c_str(), rect, true, this, 256);
+ #if USE_FREETYPE
+ gui::IGUIElement *e = (gui::IGUIElement *) new gui::intlGUIEditBox(text.c_str(), true, Environment, this, 256, rect);
+ e->drop();
+ #else
+ gui::IGUIElement *e = Environment->addEditBox(text.c_str(), rect, true, this, 256);
+ #endif
Environment->setFocus(e);
irr::SEvent evt;
diff --git a/src/intlGUIEditBox.cpp b/src/intlGUIEditBox.cpp
new file mode 100644
index 000000000..4add61e20
--- /dev/null
+++ b/src/intlGUIEditBox.cpp
@@ -0,0 +1,1508 @@
+// 11.11.2011 11:11 ValkaTR
+//
+// This is a copy of intlGUIEditBox from the irrlicht, but with a
+// fix in the OnEvent function, which doesn't allowed input of
+// other keyboard layouts than latin-1
+//
+// Characters like: ä ö ü õ ы й ю я ъ № € ° ...
+//
+// This fix is only needed for linux, because of a bug
+// in the CIrrDeviceLinux.cpp:1014-1015 of the irrlicht
+//
+// Also locale in the programm should not be changed to
+// a "C", "POSIX" or whatever, it should be set to "",
+// or XLookupString will return nothing for the international
+// characters.
+//
+// From the "man setlocale":
+//
+// On startup of the main program, the portable "C" locale
+// is selected as default. A program may be made
+// portable to all locales by calling:
+//
+// setlocale(LC_ALL, "");
+//
+// after program initialization....
+//
+
+// Copyright (C) 2002-2010 Nikolaus Gebhardt
+// This file is part of the "Irrlicht Engine".
+// For conditions of distribution and use, see copyright notice in irrlicht.h
+
+#include "intlGUIEditBox.h"
+
+#ifdef _IRR_COMPILE_WITH_GUI_
+
+#include "IGUISkin.h"
+#include "IGUIEnvironment.h"
+#include "IGUIFont.h"
+#include "IVideoDriver.h"
+//#include "rect.h"
+//#include "irrlicht/os.cpp"
+#include "porting.h"
+//#include "Keycodes.h"
+
+/*
+ todo:
+ optional scrollbars
+ ctrl+left/right to select word
+ double click/ctrl click: word select + drag to select whole words, triple click to select line
+ optional? dragging selected text
+ numerical
+*/
+
+namespace irr
+{
+namespace gui
+{
+
+//! constructor
+intlGUIEditBox::intlGUIEditBox(const wchar_t* text, bool border,
+ IGUIEnvironment* environment, IGUIElement* parent, s32 id,
+ const core::rect<s32>& rectangle)
+ : IGUIEditBox(environment, parent, id, rectangle), MouseMarking(false),
+ Border(border), OverrideColorEnabled(false), MarkBegin(0), MarkEnd(0),
+ OverrideColor(video::SColor(101,255,255,255)), OverrideFont(0), LastBreakFont(0),
+ Operator(0), BlinkStartTime(0), CursorPos(0), HScrollPos(0), VScrollPos(0), Max(0),
+ WordWrap(false), MultiLine(false), AutoScroll(true), PasswordBox(false),
+ PasswordChar(L'*'), HAlign(EGUIA_UPPERLEFT), VAlign(EGUIA_CENTER),
+ CurrentTextRect(0,0,1,1), FrameRect(rectangle)
+{
+ #ifdef _DEBUG
+ setDebugName("intlintlGUIEditBox");
+ #endif
+
+ Text = text;
+
+ if (Environment)
+ Operator = Environment->getOSOperator();
+
+ if (Operator)
+ Operator->grab();
+
+ // this element can be tabbed to
+ setTabStop(true);
+ setTabOrder(-1);
+
+ IGUISkin *skin = 0;
+ if (Environment)
+ skin = Environment->getSkin();
+ if (Border && skin)
+ {
+ FrameRect.UpperLeftCorner.X += skin->getSize(EGDS_TEXT_DISTANCE_X)+1;
+ FrameRect.UpperLeftCorner.Y += skin->getSize(EGDS_TEXT_DISTANCE_Y)+1;
+ FrameRect.LowerRightCorner.X -= skin->getSize(EGDS_TEXT_DISTANCE_X)+1;
+ FrameRect.LowerRightCorner.Y -= skin->getSize(EGDS_TEXT_DISTANCE_Y)+1;
+ }
+
+ breakText();
+
+ calculateScrollPos();
+}
+
+
+//! destructor
+intlGUIEditBox::~intlGUIEditBox()
+{
+ if (OverrideFont)
+ OverrideFont->drop();
+
+ if (Operator)
+ Operator->drop();
+}
+
+
+//! Sets another skin independent font.
+void intlGUIEditBox::setOverrideFont(IGUIFont* font)
+{
+ if (OverrideFont == font)
+ return;
+
+ if (OverrideFont)
+ OverrideFont->drop();
+
+ OverrideFont = font;
+
+ if (OverrideFont)
+ OverrideFont->grab();
+
+ breakText();
+}
+
+IGUIFont * intlGUIEditBox::getOverrideFont() const
+{
+ return OverrideFont;
+}
+
+//! Get the font which is used right now for drawing
+IGUIFont* intlGUIEditBox::getActiveFont() const
+{
+ if ( OverrideFont )
+ return OverrideFont;
+ IGUISkin* skin = Environment->getSkin();
+ if (skin)
+ return skin->getFont();
+ return 0;
+}
+
+//! Sets another color for the text.
+void intlGUIEditBox::setOverrideColor(video::SColor color)
+{
+ OverrideColor = color;
+ OverrideColorEnabled = true;
+}
+
+video::SColor intlGUIEditBox::getOverrideColor() const
+{
+ return OverrideColor;
+}
+
+//! Turns the border on or off
+void intlGUIEditBox::setDrawBorder(bool border)
+{
+ Border = border;
+}
+
+//! Sets whether to draw the background
+void intlGUIEditBox::setDrawBackground(bool draw)
+{
+}
+
+//! Sets if the text should use the overide color or the color in the gui skin.
+void intlGUIEditBox::enableOverrideColor(bool enable)
+{
+ OverrideColorEnabled = enable;
+}
+
+bool intlGUIEditBox::isOverrideColorEnabled() const
+{
+ _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
+ return OverrideColorEnabled;
+}
+
+//! Enables or disables word wrap
+void intlGUIEditBox::setWordWrap(bool enable)
+{
+ WordWrap = enable;
+ breakText();
+}
+
+
+void intlGUIEditBox::updateAbsolutePosition()
+{
+ core::rect<s32> oldAbsoluteRect(AbsoluteRect);
+ IGUIElement::updateAbsolutePosition();
+ if ( oldAbsoluteRect != AbsoluteRect )
+ {
+ breakText();
+ }
+}
+
+
+//! Checks if word wrap is enabled
+bool intlGUIEditBox::isWordWrapEnabled() const
+{
+ _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
+ return WordWrap;
+}
+
+
+//! Enables or disables newlines.
+void intlGUIEditBox::setMultiLine(bool enable)
+{
+ MultiLine = enable;
+}
+
+
+//! Checks if multi line editing is enabled
+bool intlGUIEditBox::isMultiLineEnabled() const
+{
+ _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
+ return MultiLine;
+}
+
+
+void intlGUIEditBox::setPasswordBox(bool passwordBox, wchar_t passwordChar)
+{
+ PasswordBox = passwordBox;
+ if (PasswordBox)
+ {
+ PasswordChar = passwordChar;
+ setMultiLine(false);
+ setWordWrap(false);
+ BrokenText.clear();
+ }
+}
+
+
+bool intlGUIEditBox::isPasswordBox() const
+{
+ _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
+ return PasswordBox;
+}
+
+
+//! Sets text justification
+void intlGUIEditBox::setTextAlignment(EGUI_ALIGNMENT horizontal, EGUI_ALIGNMENT vertical)
+{
+ HAlign = horizontal;
+ VAlign = vertical;
+}
+
+
+//! called if an event happened.
+bool intlGUIEditBox::OnEvent(const SEvent& event)
+{
+ if (IsEnabled)
+ {
+
+ switch(event.EventType)
+ {
+ case EET_GUI_EVENT:
+ if (event.GUIEvent.EventType == EGET_ELEMENT_FOCUS_LOST)
+ {
+ if (event.GUIEvent.Caller == this)
+ {
+ MouseMarking = false;
+ setTextMarkers(0,0);
+ }
+ }
+ break;
+ case EET_KEY_INPUT_EVENT:
+ {
+#if defined(linux)
+ // ################################################################
+ // ValkaTR:
+ // This part is the difference from the original intlGUIEditBox
+ // It converts UTF-8 character into a UCS-2 (wchar_t)
+ wchar_t wc = L'_';
+ mbtowc( &wc, (char *) &event.KeyInput.Char, sizeof(event.KeyInput.Char) );
+
+ //printf( "char: %lc (%u) \r\n", wc, wc );
+
+ SEvent irrevent(event);
+ irrevent.KeyInput.Char = wc;
+ // ################################################################
+
+ if (processKey(irrevent))
+ return true;
+#else
+ if (processKey(event))
+ return true;
+#endif // defined(linux)
+
+ break;
+ }
+ case EET_MOUSE_INPUT_EVENT:
+ if (processMouse(event))
+ return true;
+ break;
+ default:
+ break;
+ }
+ }
+
+ return IGUIElement::OnEvent(event);
+}
+
+
+bool intlGUIEditBox::processKey(const SEvent& event)
+{
+ if (!event.KeyInput.PressedDown)
+ return false;
+
+ bool textChanged = false;
+ s32 newMarkBegin = MarkBegin;
+ s32 newMarkEnd = MarkEnd;
+
+ // control shortcut handling
+
+ if (event.KeyInput.Control)
+ {
+ // german backlash '\' entered with control + '?'
+ if ( event.KeyInput.Char == '\\' )
+ {
+ inputChar(event.KeyInput.Char);
+ return true;
+ }
+
+ switch(event.KeyInput.Key)
+ {
+ case KEY_KEY_A:
+ // select all
+ newMarkBegin = 0;
+ newMarkEnd = Text.size();
+ break;
+ case KEY_KEY_C:
+ // copy to clipboard
+ if (!PasswordBox && Operator && MarkBegin != MarkEnd)
+ {
+ const s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd;
+ const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;
+
+ core::stringc s;
+ s = Text.subString(realmbgn, realmend - realmbgn).c_str();
+ Operator->copyToClipboard(s.c_str());
+ }
+ break;
+ case KEY_KEY_X:
+ // cut to the clipboard
+ if (!PasswordBox && Operator && MarkBegin != MarkEnd)
+ {
+ const s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd;
+ const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;
+
+ // copy
+ core::stringc sc;
+ sc = Text.subString(realmbgn, realmend - realmbgn).c_str();
+ Operator->copyToClipboard(sc.c_str());
+
+ if (IsEnabled)
+ {
+ // delete
+ core::stringw s;
+ s = Text.subString(0, realmbgn);
+ s.append( Text.subString(realmend, Text.size()-realmend) );
+ Text = s;
+
+ CursorPos = realmbgn;
+ newMarkBegin = 0;
+ newMarkEnd = 0;
+ textChanged = true;
+ }
+ }
+ break;
+ case KEY_KEY_V:
+ if ( !IsEnabled )
+ break;
+
+ // paste from the clipboard
+ if (Operator)
+ {
+ const s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd;
+ const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;
+
+ // add new character
+ const c8* p = Operator->getTextFromClipboard();
+ if (p)
+ {
+ if (MarkBegin == MarkEnd)
+ {
+ // insert text
+ core::stringw s = Text.subString(0, CursorPos);
+ s.append(p);
+ s.append( Text.subString(CursorPos, Text.size()-CursorPos) );
+
+ if (!Max || s.size()<=Max) // thx to Fish FH for fix
+ {
+ Text = s;
+ s = p;
+ CursorPos += s.size();
+ }
+ }
+ else
+ {
+ // replace text
+
+ core::stringw s = Text.subString(0, realmbgn);
+ s.append(p);
+ s.append( Text.subString(realmend, Text.size()-realmend) );
+
+ if (!Max || s.size()<=Max) // thx to Fish FH for fix
+ {
+ Text = s;
+ s = p;
+ CursorPos = realmbgn + s.size();
+ }
+ }
+ }
+
+ newMarkBegin = 0;
+ newMarkEnd = 0;
+ textChanged = true;
+ }
+ break;
+ case KEY_HOME:
+ // move/highlight to start of text
+ if (event.KeyInput.Shift)
+ {
+ newMarkEnd = CursorPos;
+ newMarkBegin = 0;
+ CursorPos = 0;
+ }
+ else
+ {
+ CursorPos = 0;
+ newMarkBegin = 0;
+ newMarkEnd = 0;
+ }
+ break;
+ case KEY_END:
+ // move/highlight to end of text
+ if (event.KeyInput.Shift)
+ {
+ newMarkBegin = CursorPos;
+ newMarkEnd = Text.size();
+ CursorPos = 0;
+ }
+ else
+ {
+ CursorPos = Text.size();
+ newMarkBegin = 0;
+ newMarkEnd = 0;
+ }
+ break;
+ default:
+ return false;
+ }
+ }
+ // default keyboard handling
+ else
+ switch(event.KeyInput.Key)
+ {
+ case KEY_END:
+ {
+ s32 p = Text.size();
+ if (WordWrap || MultiLine)
+ {
+ p = getLineFromPos(CursorPos);
+ p = BrokenTextPositions[p] + (s32)BrokenText[p].size();
+ if (p > 0 && (Text[p-1] == L'\r' || Text[p-1] == L'\n' ))
+ p-=1;
+ }
+
+ if (event.KeyInput.Shift)
+ {
+ if (MarkBegin == MarkEnd)
+ newMarkBegin = CursorPos;
+
+ newMarkEnd = p;
+ }
+ else
+ {
+ newMarkBegin = 0;
+ newMarkEnd = 0;
+ }
+ CursorPos = p;
+ BlinkStartTime = porting::getTimeMs();
+ }
+ break;
+ case KEY_HOME:
+ {
+
+ s32 p = 0;
+ if (WordWrap || MultiLine)
+ {
+ p = getLineFromPos(CursorPos);
+ p = BrokenTextPositions[p];
+ }
+
+ if (event.KeyInput.Shift)
+ {
+ if (MarkBegin == MarkEnd)
+ newMarkBegin = CursorPos;
+ newMarkEnd = p;
+ }
+ else
+ {
+ newMarkBegin = 0;
+ newMarkEnd = 0;
+ }
+ CursorPos = p;
+ BlinkStartTime = porting::getTimeMs();
+ }
+ break;
+ case KEY_RETURN:
+ if (MultiLine)
+ {
+ inputChar(L'\n');
+ return true;
+ }
+ else
+ {
+ sendGuiEvent( EGET_EDITBOX_ENTER );
+ }
+ break;
+ case KEY_LEFT:
+
+ if (event.KeyInput.Shift)
+ {
+ if (CursorPos > 0)
+ {
+ if (MarkBegin == MarkEnd)
+ newMarkBegin = CursorPos;
+
+ newMarkEnd = CursorPos-1;
+ }
+ }
+ else
+ {
+ newMarkBegin = 0;
+ newMarkEnd = 0;
+ }
+
+ if (CursorPos > 0) CursorPos--;
+ BlinkStartTime = porting::getTimeMs();
+ break;
+
+ case KEY_RIGHT:
+ if (event.KeyInput.Shift)
+ {
+ if (Text.size() > (u32)CursorPos)
+ {
+ if (MarkBegin == MarkEnd)
+ newMarkBegin = CursorPos;
+
+ newMarkEnd = CursorPos+1;
+ }
+ }
+ else
+ {
+ newMarkBegin = 0;
+ newMarkEnd = 0;
+ }
+
+ if (Text.size() > (u32)CursorPos) CursorPos++;
+ BlinkStartTime = porting::getTimeMs();
+ break;
+ case KEY_UP:
+ if (MultiLine || (WordWrap && BrokenText.size() > 1) )
+ {
+ s32 lineNo = getLineFromPos(CursorPos);
+ s32 mb = (MarkBegin == MarkEnd) ? CursorPos : (MarkBegin > MarkEnd ? MarkBegin : MarkEnd);
+ if (lineNo > 0)
+ {
+ s32 cp = CursorPos - BrokenTextPositions[lineNo];
+ if ((s32)BrokenText[lineNo-1].size() < cp)
+ CursorPos = BrokenTextPositions[lineNo-1] + (s32)BrokenText[lineNo-1].size()-1;
+ else
+ CursorPos = BrokenTextPositions[lineNo-1] + cp;
+ }
+
+ if (event.KeyInput.Shift)
+ {
+ newMarkBegin = mb;
+ newMarkEnd = CursorPos;
+ }
+ else
+ {
+ newMarkBegin = 0;
+ newMarkEnd = 0;
+ }
+
+ }
+ else
+ {
+ return false;
+ }
+ break;
+ case KEY_DOWN:
+ if (MultiLine || (WordWrap && BrokenText.size() > 1) )
+ {
+ s32 lineNo = getLineFromPos(CursorPos);
+ s32 mb = (MarkBegin == MarkEnd) ? CursorPos : (MarkBegin < MarkEnd ? MarkBegin : MarkEnd);
+ if (lineNo < (s32)BrokenText.size()-1)
+ {
+ s32 cp = CursorPos - BrokenTextPositions[lineNo];
+ if ((s32)BrokenText[lineNo+1].size() < cp)
+ CursorPos = BrokenTextPositions[lineNo+1] + BrokenText[lineNo+1].size()-1;
+ else
+ CursorPos = BrokenTextPositions[lineNo+1] + cp;
+ }
+
+ if (event.KeyInput.Shift)
+ {
+ newMarkBegin = mb;
+ newMarkEnd = CursorPos;
+ }
+ else
+ {
+ newMarkBegin = 0;
+ newMarkEnd = 0;
+ }
+
+ }
+ else
+ {
+ return false;
+ }
+ break;
+
+ case KEY_BACK:
+ if ( !this->IsEnabled )
+ break;
+
+ if (Text.size())
+ {
+ core::stringw s;
+
+ if (MarkBegin != MarkEnd)
+ {
+ // delete marked text
+ const s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd;
+ const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;
+
+ s = Text.subString(0, realmbgn);
+ s.append( Text.subString(realmend, Text.size()-realmend) );
+ Text = s;
+
+ CursorPos = realmbgn;
+ }
+ else
+ {
+ // delete text behind cursor
+ if (CursorPos>0)
+ s = Text.subString(0, CursorPos-1);
+ else
+ s = L"";
+ s.append( Text.subString(CursorPos, Text.size()-CursorPos) );
+ Text = s;
+ --CursorPos;
+ }
+
+ if (CursorPos < 0)
+ CursorPos = 0;
+ BlinkStartTime = porting::getTimeMs();
+ newMarkBegin = 0;
+ newMarkEnd = 0;
+ textChanged = true;
+ }
+ break;
+ case KEY_DELETE:
+ if ( !this->IsEnabled )
+ break;
+
+ if (Text.size() != 0)
+ {
+ core::stringw s;
+
+ if (MarkBegin != MarkEnd)
+ {
+ // delete marked text
+ const s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd;
+ const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;
+
+ s = Text.subString(0, realmbgn);
+ s.append( Text.subString(realmend, Text.size()-realmend) );
+ Text = s;
+
+ CursorPos = realmbgn;
+ }
+ else
+ {
+ // delete text before cursor
+ s = Text.subString(0, CursorPos);
+ s.append( Text.subString(CursorPos+1, Text.size()-CursorPos-1) );
+ Text = s;
+ }
+
+ if (CursorPos > (s32)Text.size())
+ CursorPos = (s32)Text.size();
+
+ BlinkStartTime = porting::getTimeMs();
+ newMarkBegin = 0;
+ newMarkEnd = 0;
+ textChanged = true;
+ }
+ break;
+
+ case KEY_ESCAPE:
+ case KEY_TAB:
+ case KEY_SHIFT:
+ case KEY_F1:
+ case KEY_F2:
+ case KEY_F3:
+ case KEY_F4:
+ case KEY_F5:
+ case KEY_F6:
+ case KEY_F7:
+ case KEY_F8:
+ case KEY_F9:
+ case KEY_F10:
+ case KEY_F11:
+ case KEY_F12:
+ case KEY_F13:
+ case KEY_F14:
+ case KEY_F15:
+ case KEY_F16:
+ case KEY_F17:
+ case KEY_F18:
+ case KEY_F19:
+ case KEY_F20:
+ case KEY_F21:
+ case KEY_F22:
+ case KEY_F23:
+ case KEY_F24:
+ // ignore these keys
+ return false;
+
+ default:
+ inputChar(event.KeyInput.Char);
+ return true;
+ }
+
+ // Set new text markers
+ setTextMarkers( newMarkBegin, newMarkEnd );
+
+ // break the text if it has changed
+ if (textChanged)
+ {
+ breakText();
+ sendGuiEvent(EGET_EDITBOX_CHANGED);
+ }
+
+ calculateScrollPos();
+
+ return true;
+}
+
+
+//! draws the element and its children
+void intlGUIEditBox::draw()
+{
+ if (!IsVisible)
+ return;
+
+ const bool focus = Environment->hasFocus(this);
+
+ IGUISkin* skin = Environment->getSkin();
+ if (!skin)
+ return;
+
+ FrameRect = AbsoluteRect;
+
+ // draw the border
+
+ if (Border)
+ {
+ skin->draw3DSunkenPane(this, skin->getColor(EGDC_WINDOW),
+ false, true, FrameRect, &AbsoluteClippingRect);
+
+ FrameRect.UpperLeftCorner.X += skin->getSize(EGDS_TEXT_DISTANCE_X)+1;
+ FrameRect.UpperLeftCorner.Y += skin->getSize(EGDS_TEXT_DISTANCE_Y)+1;
+ FrameRect.LowerRightCorner.X -= skin->getSize(EGDS_TEXT_DISTANCE_X)+1;
+ FrameRect.LowerRightCorner.Y -= skin->getSize(EGDS_TEXT_DISTANCE_Y)+1;
+ }
+ core::rect<s32> localClipRect = FrameRect;
+ localClipRect.clipAgainst(AbsoluteClippingRect);
+
+ // draw the text
+
+ IGUIFont* font = OverrideFont;
+ if (!OverrideFont)
+ font = skin->getFont();
+
+ s32 cursorLine = 0;
+ s32 charcursorpos = 0;
+
+ if (font)
+ {
+ if (LastBreakFont != font)
+ {
+ breakText();
+ }
+
+ // calculate cursor pos
+
+ core::stringw *txtLine = &Text;
+ s32 startPos = 0;
+
+ core::stringw s, s2;
+
+ // get mark position
+ const bool ml = (!PasswordBox && (WordWrap || MultiLine));
+ const s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd;
+ const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;
+ const s32 hlineStart = ml ? getLineFromPos(realmbgn) : 0;
+ const s32 hlineCount = ml ? getLineFromPos(realmend) - hlineStart + 1 : 1;
+ const s32 lineCount = ml ? BrokenText.size() : 1;
+
+ // Save the override color information.
+ // Then, alter it if the edit box is disabled.
+ const bool prevOver = OverrideColorEnabled;
+ const video::SColor prevColor = OverrideColor;
+
+ if (Text.size())
+ {
+ if (!IsEnabled && !OverrideColorEnabled)
+ {
+ OverrideColorEnabled = true;
+ OverrideColor = skin->getColor(EGDC_GRAY_TEXT);
+ }
+
+ for (s32 i=0; i < lineCount; ++i)
+ {
+ setTextRect(i);
+
+ // clipping test - don't draw anything outside the visible area
+ core::rect<s32> c = localClipRect;
+ c.clipAgainst(CurrentTextRect);
+ if (!c.isValid())
+ continue;
+
+ // get current line
+ if (PasswordBox)
+ {
+ if (BrokenText.size() != 1)
+ {
+ BrokenText.clear();
+ BrokenText.push_back(core::stringw());
+ }
+ if (BrokenText[0].size() != Text.size())
+ {
+ BrokenText[0] = Text;
+ for (u32 q = 0; q < Text.size(); ++q)
+ {
+ BrokenText[0] [q] = PasswordChar;
+ }
+ }
+ txtLine = &BrokenText[0];
+ startPos = 0;
+ }
+ else
+ {
+ txtLine = ml ? &BrokenText[i] : &Text;
+ startPos = ml ? BrokenTextPositions[i] : 0;
+ }
+
+
+ // draw normal text
+ font->draw(txtLine->c_str(), CurrentTextRect,
+ OverrideColorEnabled ? OverrideColor : skin->getColor(EGDC_BUTTON_TEXT),
+ false, true, &localClipRect);
+
+ // draw mark and marked text
+ if (focus && MarkBegin != MarkEnd && i >= hlineStart && i < hlineStart + hlineCount)
+ {
+
+ s32 mbegin = 0, mend = 0;
+ s32 lineStartPos = 0, lineEndPos = txtLine->size();
+
+ if (i == hlineStart)
+ {
+ // highlight start is on this line
+ s = txtLine->subString(0, realmbgn - startPos);
+ mbegin = font->getDimension(s.c_str()).Width;
+
+ // deal with kerning
+ mbegin += font->getKerningWidth(
+ &((*txtLine)[realmbgn - startPos]),
+ realmbgn - startPos > 0 ? &((*txtLine)[realmbgn - startPos - 1]) : 0);
+
+ lineStartPos = realmbgn - startPos;
+ }
+ if (i == hlineStart + hlineCount - 1)
+ {
+ // highlight end is on this line
+ s2 = txtLine->subString(0, realmend - startPos);
+ mend = font->getDimension(s2.c_str()).Width;
+ lineEndPos = (s32)s2.size();
+ }
+ else
+ mend = font->getDimension(txtLine->c_str()).Width;
+
+ CurrentTextRect.UpperLeftCorner.X += mbegin;
+ CurrentTextRect.LowerRightCorner.X = CurrentTextRect.UpperLeftCorner.X + mend - mbegin;
+
+ // draw mark
+ skin->draw2DRectangle(this, skin->getColor(EGDC_HIGH_LIGHT), CurrentTextRect, &localClipRect);
+
+ // draw marked text
+ s = txtLine->subString(lineStartPos, lineEndPos - lineStartPos);
+
+ if (s.size())
+ font->draw(s.c_str(), CurrentTextRect,
+ OverrideColorEnabled ? OverrideColor : skin->getColor(EGDC_HIGH_LIGHT_TEXT),
+ false, true, &localClipRect);
+
+ }
+ }
+
+ // Return the override color information to its previous settings.
+ OverrideColorEnabled = prevOver;
+ OverrideColor = prevColor;
+ }
+
+ // draw cursor
+
+ if (WordWrap || MultiLine)
+ {
+ cursorLine = getLineFromPos(CursorPos);
+ txtLine = &BrokenText[cursorLine];
+ startPos = BrokenTextPositions[cursorLine];
+ }
+ s = txtLine->subString(0,CursorPos-startPos);
+ charcursorpos = font->getDimension(s.c_str()).Width +
+ font->getKerningWidth(L"_", CursorPos-startPos > 0 ? &((*txtLine)[CursorPos-startPos-1]) : 0);
+
+ if (focus && (porting::getTimeMs() - BlinkStartTime) % 700 < 350)
+ {
+ setTextRect(cursorLine);
+ CurrentTextRect.UpperLeftCorner.X += charcursorpos;
+
+ font->draw(L"_", CurrentTextRect,
+ OverrideColorEnabled ? OverrideColor : skin->getColor(EGDC_BUTTON_TEXT),
+ false, true, &localClipRect);
+ }
+ }
+
+ // draw children
+ IGUIElement::draw();
+}
+
+
+//! Sets the new caption of this element.
+void intlGUIEditBox::setText(const wchar_t* text)
+{
+ Text = text;
+ if (u32(CursorPos) > Text.size())
+ CursorPos = Text.size();
+ HScrollPos = 0;
+ breakText();
+}
+
+
+//! Enables or disables automatic scrolling with cursor position
+//! \param enable: If set to true, the text will move around with the cursor position
+void intlGUIEditBox::setAutoScroll(bool enable)
+{
+ AutoScroll = enable;
+}
+
+
+//! Checks to see if automatic scrolling is enabled
+//! \return true if automatic scrolling is enabled, false if not
+bool intlGUIEditBox::isAutoScrollEnabled() const
+{
+ _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
+ return AutoScroll;
+}
+
+
+//! Gets the area of the text in the edit box
+//! \return Returns the size in pixels of the text
+core::dimension2du intlGUIEditBox::getTextDimension()
+{
+ core::rect<s32> ret;
+
+ setTextRect(0);
+ ret = CurrentTextRect;
+
+ for (u32 i=1; i < BrokenText.size(); ++i)
+ {
+ setTextRect(i);
+ ret.addInternalPoint(CurrentTextRect.UpperLeftCorner);
+ ret.addInternalPoint(CurrentTextRect.LowerRightCorner);
+ }
+
+ return core::dimension2du(ret.getSize());
+}
+
+
+//! Sets the maximum amount of characters which may be entered in the box.
+//! \param max: Maximum amount of characters. If 0, the character amount is
+//! infinity.
+void intlGUIEditBox::setMax(u32 max)
+{
+ Max = max;
+
+ if (Text.size() > Max && Max != 0)
+ Text = Text.subString(0, Max);
+}
+
+
+//! Returns maximum amount of characters, previously set by setMax();
+u32 intlGUIEditBox::getMax() const
+{
+ return Max;
+}
+
+
+bool intlGUIEditBox::processMouse(const SEvent& event)
+{
+ switch(event.MouseInput.Event)
+ {
+ case irr::EMIE_LMOUSE_LEFT_UP:
+ if (Environment->hasFocus(this))
+ {
+ CursorPos = getCursorPos(event.MouseInput.X, event.MouseInput.Y);
+ if (MouseMarking)
+ {
+ setTextMarkers( MarkBegin, CursorPos );
+ }
+ MouseMarking = false;
+ calculateScrollPos();
+ return true;
+ }
+ break;
+ case irr::EMIE_MOUSE_MOVED:
+ {
+ if (MouseMarking)
+ {
+ CursorPos = getCursorPos(event.MouseInput.X, event.MouseInput.Y);
+ setTextMarkers( MarkBegin, CursorPos );
+ calculateScrollPos();
+ return true;
+ }
+ }
+ break;
+ case EMIE_LMOUSE_PRESSED_DOWN:
+ if (!Environment->hasFocus(this))
+ {
+ BlinkStartTime = porting::getTimeMs();
+ MouseMarking = true;
+ CursorPos = getCursorPos(event.MouseInput.X, event.MouseInput.Y);
+ setTextMarkers(CursorPos, CursorPos );
+ calculateScrollPos();
+ return true;
+ }
+ else
+ {
+ if (!AbsoluteClippingRect.isPointInside(
+ core::position2d<s32>(event.MouseInput.X, event.MouseInput.Y)))
+ {
+ return false;
+ }
+ else
+ {
+ // move cursor
+ CursorPos = getCursorPos(event.MouseInput.X, event.MouseInput.Y);
+
+ s32 newMarkBegin = MarkBegin;
+ if (!MouseMarking)
+ newMarkBegin = CursorPos;
+
+ MouseMarking = true;
+ setTextMarkers( newMarkBegin, CursorPos);
+ calculateScrollPos();
+ return true;
+ }
+ }
+ default:
+ break;
+ }
+
+ return false;
+}
+
+
+s32 intlGUIEditBox::getCursorPos(s32 x, s32 y)
+{
+ IGUIFont* font = OverrideFont;
+ IGUISkin* skin = Environment->getSkin();
+ if (!OverrideFont)
+ font = skin->getFont();
+
+ const u32 lineCount = (WordWrap || MultiLine) ? BrokenText.size() : 1;
+
+ core::stringw *txtLine=0;
+ s32 startPos=0;
+ x+=3;
+
+ for (u32 i=0; i < lineCount; ++i)
+ {
+ setTextRect(i);
+ if (i == 0 && y < CurrentTextRect.UpperLeftCorner.Y)
+ y = CurrentTextRect.UpperLeftCorner.Y;
+ if (i == lineCount - 1 && y > CurrentTextRect.LowerRightCorner.Y )
+ y = CurrentTextRect.LowerRightCorner.Y;
+
+ // is it inside this region?
+ if (y >= CurrentTextRect.UpperLeftCorner.Y && y <= CurrentTextRect.LowerRightCorner.Y)
+ {
+ // we've found the clicked line
+ txtLine = (WordWrap || MultiLine) ? &BrokenText[i] : &Text;
+ startPos = (WordWrap || MultiLine) ? BrokenTextPositions[i] : 0;
+ break;
+ }
+ }
+
+ if (x < CurrentTextRect.UpperLeftCorner.X)
+ x = CurrentTextRect.UpperLeftCorner.X;
+
+ s32 idx = font->getCharacterFromPos(Text.c_str(), x - CurrentTextRect.UpperLeftCorner.X);
+
+ // click was on or left of the line
+ if (idx != -1)
+ return idx + startPos;
+
+ // click was off the right edge of the line, go to end.
+ return txtLine->size() + startPos;
+}
+
+
+//! Breaks the single text line.
+void intlGUIEditBox::breakText()
+{
+ IGUISkin* skin = Environment->getSkin();
+
+ if ((!WordWrap && !MultiLine) || !skin)
+ return;
+
+ BrokenText.clear(); // need to reallocate :/
+ BrokenTextPositions.set_used(0);
+
+ IGUIFont* font = OverrideFont;
+ if (!OverrideFont)
+ font = skin->getFont();
+
+ if (!font)
+ return;
+
+ LastBreakFont = font;
+
+ core::stringw line;
+ core::stringw word;
+ core::stringw whitespace;
+ s32 lastLineStart = 0;
+ s32 size = Text.size();
+ s32 length = 0;
+ s32 elWidth = RelativeRect.getWidth() - 6;
+ wchar_t c;
+
+ for (s32 i=0; i<size; ++i)
+ {
+ c = Text[i];
+ bool lineBreak = false;
+
+ if (c == L'\r') // Mac or Windows breaks
+ {
+ lineBreak = true;
+ c = ' ';
+ if (Text[i+1] == L'\n') // Windows breaks
+ {
+ Text.erase(i+1);
+ --size;
+ }
+ }
+ else if (c == L'\n') // Unix breaks
+ {
+ lineBreak = true;
+ c = ' ';
+ }
+
+ // don't break if we're not a multi-line edit box
+ if (!MultiLine)
+ lineBreak = false;
+
+ if (c == L' ' || c == 0 || i == (size-1))
+ {
+ if (word.size())
+ {
+ // here comes the next whitespace, look if
+ // we can break the last word to the next line.
+ s32 whitelgth = font->getDimension(whitespace.c_str()).Width;
+ s32 worldlgth = font->getDimension(word.c_str()).Width;
+
+ if (WordWrap && length + worldlgth + whitelgth > elWidth)
+ {
+ // break to next line
+ length = worldlgth;
+ BrokenText.push_back(line);
+ BrokenTextPositions.push_back(lastLineStart);
+ lastLineStart = i - (s32)word.size();
+ line = word;
+ }
+ else
+ {
+ // add word to line
+ line += whitespace;
+ line += word;
+ length += whitelgth + worldlgth;
+ }
+
+ word = L"";
+ whitespace = L"";
+ }
+
+ whitespace += c;
+
+ // compute line break
+ if (lineBreak)
+ {
+ line += whitespace;
+ line += word;
+ BrokenText.push_back(line);
+ BrokenTextPositions.push_back(lastLineStart);
+ lastLineStart = i+1;
+ line = L"";
+ word = L"";
+ whitespace = L"";
+ length = 0;
+ }
+ }
+ else
+ {
+ // yippee this is a word..
+ word += c;
+ }
+ }
+
+ line += whitespace;
+ line += word;
+ BrokenText.push_back(line);
+ BrokenTextPositions.push_back(lastLineStart);
+}
+
+
+void intlGUIEditBox::setTextRect(s32 line)
+{
+ core::dimension2du d;
+
+ IGUISkin* skin = Environment->getSkin();
+ if (!skin)
+ return;
+
+ IGUIFont* font = OverrideFont ? OverrideFont : skin->getFont();
+
+ if (!font)
+ return;
+
+ // get text dimension
+ const u32 lineCount = (WordWrap || MultiLine) ? BrokenText.size() : 1;
+ if (WordWrap || MultiLine)
+ {
+ d = font->getDimension(BrokenText[line].c_str());
+ }
+ else
+ {
+ d = font->getDimension(Text.c_str());
+ d.Height = AbsoluteRect.getHeight();
+ }
+ d.Height += font->getKerningHeight();
+
+ // justification
+ switch (HAlign)
+ {
+ case EGUIA_CENTER:
+ // align to h centre
+ CurrentTextRect.UpperLeftCorner.X = (FrameRect.getWidth()/2) - (d.Width/2);
+ CurrentTextRect.LowerRightCorner.X = (FrameRect.getWidth()/2) + (d.Width/2);
+ break;
+ case EGUIA_LOWERRIGHT:
+ // align to right edge
+ CurrentTextRect.UpperLeftCorner.X = FrameRect.getWidth() - d.Width;
+ CurrentTextRect.LowerRightCorner.X = FrameRect.getWidth();
+ break;
+ default:
+ // align to left edge
+ CurrentTextRect.UpperLeftCorner.X = 0;
+ CurrentTextRect.LowerRightCorner.X = d.Width;
+
+ }
+
+ switch (VAlign)
+ {
+ case EGUIA_CENTER:
+ // align to v centre
+ CurrentTextRect.UpperLeftCorner.Y =
+ (FrameRect.getHeight()/2) - (lineCount*d.Height)/2 + d.Height*line;
+ break;
+ case EGUIA_LOWERRIGHT:
+ // align to bottom edge
+ CurrentTextRect.UpperLeftCorner.Y =
+ FrameRect.getHeight() - lineCount*d.Height + d.Height*line;
+ break;
+ default:
+ // align to top edge
+ CurrentTextRect.UpperLeftCorner.Y = d.Height*line;
+ break;
+ }
+
+ CurrentTextRect.UpperLeftCorner.X -= HScrollPos;
+ CurrentTextRect.LowerRightCorner.X -= HScrollPos;
+ CurrentTextRect.UpperLeftCorner.Y -= VScrollPos;
+ CurrentTextRect.LowerRightCorner.Y = CurrentTextRect.UpperLeftCorner.Y + d.Height;
+
+ CurrentTextRect += FrameRect.UpperLeftCorner;
+
+}
+
+
+s32 intlGUIEditBox::getLineFromPos(s32 pos)
+{
+ if (!WordWrap && !MultiLine)
+ return 0;
+
+ s32 i=0;
+ while (i < (s32)BrokenTextPositions.size())
+ {
+ if (BrokenTextPositions[i] > pos)
+ return i-1;
+ ++i;
+ }
+ return (s32)BrokenTextPositions.size() - 1;
+}
+
+
+void intlGUIEditBox::inputChar(wchar_t c)
+{
+ if (!IsEnabled)
+ return;
+
+ if (c != 0)
+ {
+ if (Text.size() < Max || Max == 0)
+ {
+ core::stringw s;
+
+ if (MarkBegin != MarkEnd)
+ {
+ // replace marked text
+ const s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd;
+ const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;
+
+ s = Text.subString(0, realmbgn);
+ s.append(c);
+ s.append( Text.subString(realmend, Text.size()-realmend) );
+ Text = s;
+ CursorPos = realmbgn+1;
+ }
+ else
+ {
+ // add new character
+ s = Text.subString(0, CursorPos);
+ s.append(c);
+ s.append( Text.subString(CursorPos, Text.size()-CursorPos) );
+ Text = s;
+ ++CursorPos;
+ }
+
+ BlinkStartTime = porting::getTimeMs();
+ setTextMarkers(0, 0);
+ }
+ }
+ breakText();
+ sendGuiEvent(EGET_EDITBOX_CHANGED);
+ calculateScrollPos();
+}
+
+
+void intlGUIEditBox::calculateScrollPos()
+{
+ if (!AutoScroll)
+ return;
+
+ // calculate horizontal scroll position
+ s32 cursLine = getLineFromPos(CursorPos);
+ setTextRect(cursLine);
+
+ // don't do horizontal scrolling when wordwrap is enabled.
+ if (!WordWrap)
+ {
+ // get cursor position
+ IGUISkin* skin = Environment->getSkin();
+ if (!skin)
+ return;
+ IGUIFont* font = OverrideFont ? OverrideFont : skin->getFont();
+ if (!font)
+ return;
+
+ core::stringw *txtLine = MultiLine ? &BrokenText[cursLine] : &Text;
+ s32 cPos = MultiLine ? CursorPos - BrokenTextPositions[cursLine] : CursorPos;
+
+ s32 cStart = CurrentTextRect.UpperLeftCorner.X + HScrollPos +
+ font->getDimension(txtLine->subString(0, cPos).c_str()).Width;
+
+ s32 cEnd = cStart + font->getDimension(L"_ ").Width;
+
+ if (FrameRect.LowerRightCorner.X < cEnd)
+ HScrollPos = cEnd - FrameRect.LowerRightCorner.X;
+ else if (FrameRect.UpperLeftCorner.X > cStart)
+ HScrollPos = cStart - FrameRect.UpperLeftCorner.X;
+ else
+ HScrollPos = 0;
+
+ // todo: adjust scrollbar
+ }
+
+ // vertical scroll position
+ if (FrameRect.LowerRightCorner.Y < CurrentTextRect.LowerRightCorner.Y + VScrollPos)
+ VScrollPos = CurrentTextRect.LowerRightCorner.Y - FrameRect.LowerRightCorner.Y + VScrollPos;
+
+ else if (FrameRect.UpperLeftCorner.Y > CurrentTextRect.UpperLeftCorner.Y + VScrollPos)
+ VScrollPos = CurrentTextRect.UpperLeftCorner.Y - FrameRect.UpperLeftCorner.Y + VScrollPos;
+ else
+ VScrollPos = 0;
+
+ // todo: adjust scrollbar
+}
+
+//! set text markers
+void intlGUIEditBox::setTextMarkers(s32 begin, s32 end)
+{
+ if ( begin != MarkBegin || end != MarkEnd )
+ {
+ MarkBegin = begin;
+ MarkEnd = end;
+ sendGuiEvent(EGET_EDITBOX_MARKING_CHANGED);
+ }
+}
+
+//! send some gui event to parent
+void intlGUIEditBox::sendGuiEvent(EGUI_EVENT_TYPE type)
+{
+ if ( Parent )
+ {
+ SEvent e;
+ e.EventType = EET_GUI_EVENT;
+ e.GUIEvent.Caller = this;
+ e.GUIEvent.Element = 0;
+ e.GUIEvent.EventType = type;
+
+ Parent->OnEvent(e);
+ }
+}
+
+//! Writes attributes of the element.
+void intlGUIEditBox::serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options=0) const
+{
+ // IGUIEditBox::serializeAttributes(out,options);
+
+ out->addBool ("OverrideColorEnabled",OverrideColorEnabled );
+ out->addColor ("OverrideColor", OverrideColor);
+ // out->addFont("OverrideFont",OverrideFont);
+ out->addInt ("MaxChars", Max);
+ out->addBool ("WordWrap", WordWrap);
+ out->addBool ("MultiLine", MultiLine);
+ out->addBool ("AutoScroll", AutoScroll);
+ out->addBool ("PasswordBox", PasswordBox);
+ core::stringw ch = L" ";
+ ch[0] = PasswordChar;
+ out->addString("PasswordChar", ch.c_str());
+ out->addEnum ("HTextAlign", HAlign, GUIAlignmentNames);
+ out->addEnum ("VTextAlign", VAlign, GUIAlignmentNames);
+
+ IGUIEditBox::serializeAttributes(out,options);
+}
+
+
+//! Reads attributes of the element
+void intlGUIEditBox::deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options=0)
+{
+ IGUIEditBox::deserializeAttributes(in,options);
+
+ setOverrideColor(in->getAttributeAsColor("OverrideColor"));
+ enableOverrideColor(in->getAttributeAsBool("OverrideColorEnabled"));
+ setMax(in->getAttributeAsInt("MaxChars"));
+ setWordWrap(in->getAttributeAsBool("WordWrap"));
+ setMultiLine(in->getAttributeAsBool("MultiLine"));
+ setAutoScroll(in->getAttributeAsBool("AutoScroll"));
+ core::stringw ch = in->getAttributeAsStringW("PasswordChar");
+
+ if (!ch.size())
+ setPasswordBox(in->getAttributeAsBool("PasswordBox"));
+ else
+ setPasswordBox(in->getAttributeAsBool("PasswordBox"), ch[0]);
+
+ setTextAlignment( (EGUI_ALIGNMENT) in->getAttributeAsEnumeration("HTextAlign", GUIAlignmentNames),
+ (EGUI_ALIGNMENT) in->getAttributeAsEnumeration("VTextAlign", GUIAlignmentNames));
+
+ // setOverrideFont(in->getAttributeAsFont("OverrideFont"));
+}
+
+
+} // end namespace gui
+} // end namespace irr
+
+#endif // _IRR_COMPILE_WITH_GUI_
diff --git a/src/intlGUIEditBox.h b/src/intlGUIEditBox.h
new file mode 100644
index 000000000..f888fb620
--- /dev/null
+++ b/src/intlGUIEditBox.h
@@ -0,0 +1,178 @@
+// Copyright (C) 2002-2010 Nikolaus Gebhardt
+// This file is part of the "Irrlicht Engine".
+// For conditions of distribution and use, see copyright notice in irrlicht.h
+
+#ifndef __C_INTL_GUI_EDIT_BOX_H_INCLUDED__
+#define __C_INTL_GUI_EDIT_BOX_H_INCLUDED__
+
+#include "IrrCompileConfig.h"
+//#ifdef _IRR_COMPILE_WITH_GUI_
+
+#include "IGUIEditBox.h"
+#include "irrArray.h"
+#include "IOSOperator.h"
+
+namespace irr
+{
+namespace gui
+{
+ class intlGUIEditBox : public IGUIEditBox
+ {
+ public:
+
+ //! constructor
+ intlGUIEditBox(const wchar_t* text, bool border, IGUIEnvironment* environment,
+ IGUIElement* parent, s32 id, const core::rect<s32>& rectangle);
+
+ //! destructor
+ virtual ~intlGUIEditBox();
+
+ //! Sets another skin independent font.
+ virtual void setOverrideFont(IGUIFont* font=0);
+
+ //! Gets the override font (if any)
+ /** \return The override font (may be 0) */
+ virtual IGUIFont* getOverrideFont() const;
+
+ //! Get the font which is used right now for drawing
+ /** Currently this is the override font when one is set and the
+ font of the active skin otherwise */
+ virtual IGUIFont* getActiveFont() const;
+
+ //! Sets another color for the text.
+ virtual void setOverrideColor(video::SColor color);
+
+ //! Gets the override color
+ virtual video::SColor getOverrideColor() const;
+
+ //! Sets if the text should use the overide color or the
+ //! color in the gui skin.
+ virtual void enableOverrideColor(bool enable);
+
+ //! Checks if an override color is enabled
+ /** \return true if the override color is enabled, false otherwise */
+ virtual bool isOverrideColorEnabled(void) const;
+
+ //! Sets whether to draw the background
+ virtual void setDrawBackground(bool draw);
+
+ //! Turns the border on or off
+ virtual void setDrawBorder(bool border);
+
+ //! Enables or disables word wrap for using the edit box as multiline text editor.
+ virtual void setWordWrap(bool enable);
+
+ //! Checks if word wrap is enabled
+ //! \return true if word wrap is enabled, false otherwise
+ virtual bool isWordWrapEnabled() const;
+
+ //! Enables or disables newlines.
+ /** \param enable: If set to true, the EGET_EDITBOX_ENTER event will not be fired,
+ instead a newline character will be inserted. */
+ virtual void setMultiLine(bool enable);
+
+ //! Checks if multi line editing is enabled
+ //! \return true if mult-line is enabled, false otherwise
+ virtual bool isMultiLineEnabled() const;
+
+ //! Enables or disables automatic scrolling with cursor position
+ //! \param enable: If set to true, the text will move around with the cursor position
+ virtual void setAutoScroll(bool enable);
+
+ //! Checks to see if automatic scrolling is enabled
+ //! \return true if automatic scrolling is enabled, false if not
+ virtual bool isAutoScrollEnabled() const;
+
+ //! Gets the size area of the text in the edit box
+ //! \return Returns the size in pixels of the text
+ virtual core::dimension2du getTextDimension();
+
+ //! Sets text justification
+ virtual void setTextAlignment(EGUI_ALIGNMENT horizontal, EGUI_ALIGNMENT vertical);
+
+ //! called if an event happened.
+ virtual bool OnEvent(const SEvent& event);
+
+ //! draws the element and its children
+ virtual void draw();
+
+ //! Sets the new caption of this element.
+ virtual void setText(const wchar_t* text);
+
+ //! Sets the maximum amount of characters which may be entered in the box.
+ //! \param max: Maximum amount of characters. If 0, the character amount is
+ //! infinity.
+ virtual void setMax(u32 max);
+
+ //! Returns maximum amount of characters, previously set by setMax();
+ virtual u32 getMax() const;
+
+ //! Sets whether the edit box is a password box. Setting this to true will
+ /** disable MultiLine, WordWrap and the ability to copy with ctrl+c or ctrl+x
+ \param passwordBox: true to enable password, false to disable
+ \param passwordChar: the character that is displayed instead of letters */
+ virtual void setPasswordBox(bool passwordBox, wchar_t passwordChar = L'*');
+
+ //! Returns true if the edit box is currently a password box.
+ virtual bool isPasswordBox() const;
+
+ //! Updates the absolute position, splits text if required
+ virtual void updateAbsolutePosition();
+
+ //! Writes attributes of the element.
+ virtual void serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options) const;
+
+ //! Reads attributes of the element
+ virtual void deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options);
+
+ protected:
+ //! Breaks the single text line.
+ void breakText();
+ //! sets the area of the given line
+ void setTextRect(s32 line);
+ //! returns the line number that the cursor is on
+ s32 getLineFromPos(s32 pos);
+ //! adds a letter to the edit box
+ void inputChar(wchar_t c);
+ //! calculates the current scroll position
+ void calculateScrollPos();
+ //! send some gui event to parent
+ void sendGuiEvent(EGUI_EVENT_TYPE type);
+ //! set text markers
+ void setTextMarkers(s32 begin, s32 end);
+
+ bool processKey(const SEvent& event);
+ bool processMouse(const SEvent& event);
+ s32 getCursorPos(s32 x, s32 y);
+
+ bool MouseMarking;
+ bool Border;
+ bool OverrideColorEnabled;
+ s32 MarkBegin;
+ s32 MarkEnd;
+
+ video::SColor OverrideColor;
+ gui::IGUIFont *OverrideFont, *LastBreakFont;
+ IOSOperator* Operator;
+
+ u32 BlinkStartTime;
+ s32 CursorPos;
+ s32 HScrollPos, VScrollPos; // scroll position in characters
+ u32 Max;
+
+ bool WordWrap, MultiLine, AutoScroll, PasswordBox;
+ wchar_t PasswordChar;
+ EGUI_ALIGNMENT HAlign, VAlign;
+
+ core::array< core::stringw > BrokenText;
+ core::array< s32 > BrokenTextPositions;
+
+ core::rect<s32> CurrentTextRect, FrameRect; // temporary values
+ };
+
+
+} // end namespace gui
+} // end namespace irr
+
+//#endif // _IRR_COMPILE_WITH_GUI_
+#endif // __C_GUI_EDIT_BOX_H_INCLUDED__
diff --git a/src/irrlichttypes.h b/src/irrlichttypes.h
index 998992e05..2db744a48 100644
--- a/src/irrlichttypes.h
+++ b/src/irrlichttypes.h
@@ -28,12 +28,13 @@ using namespace irr;
#if (IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 8)
#ifdef _MSC_VER
// Windows
+ typedef long long s64;
typedef unsigned long long u64;
#else
// Posix
#include <stdint.h>
+ typedef int64_t s64;
typedef uint64_t u64;
- //typedef unsigned long long u64;
#endif
#endif
diff --git a/src/itemdef.cpp b/src/itemdef.cpp
index 260baadc0..a4f3895e0 100644
--- a/src/itemdef.cpp
+++ b/src/itemdef.cpp
@@ -100,6 +100,7 @@ void ItemDefinition::reset()
wield_scale = v3f(1.0, 1.0, 1.0);
stack_max = 99;
usable = false;
+ rightclickable = false;
liquids_pointable = false;
if(tool_capabilities)
{
diff --git a/src/itemdef.h b/src/itemdef.h
index 699d727bd..ae3600868 100644
--- a/src/itemdef.h
+++ b/src/itemdef.h
@@ -62,6 +62,8 @@ struct ItemDefinition
*/
s16 stack_max;
bool usable;
+ // If true, don't use node placement prediction
+ bool rightclickable;
bool liquids_pointable;
// May be NULL. If non-NULL, deleted by destructor
ToolCapabilities *tool_capabilities;
diff --git a/src/json/CMakeLists.txt b/src/json/CMakeLists.txt
new file mode 100644
index 000000000..0957799aa
--- /dev/null
+++ b/src/json/CMakeLists.txt
@@ -0,0 +1,14 @@
+if( UNIX )
+ set(json_SRCS jsoncpp.cpp)
+ set(json_platform_LIBS "")
+else( UNIX )
+ set(json_SRCS jsoncpp.cpp)
+ set(json_platform_LIBS "")
+endif( UNIX )
+
+add_library(json ${json_SRCS})
+
+target_link_libraries(
+ json
+ ${json_platform_LIBS}
+)
diff --git a/src/json/UPDATING b/src/json/UPDATING
new file mode 100644
index 000000000..d00076601
--- /dev/null
+++ b/src/json/UPDATING
@@ -0,0 +1,16 @@
+#!/bin/sh
+cd ..
+svn co https://jsoncpp.svn.sourceforge.net/svnroot/jsoncpp/trunk/jsoncpp jsoncpp
+svn up jsoncpp
+cd jsoncpp
+python amalgamate.py
+cp -R dist/json ..
+cp dist/jsoncpp.cpp ../json
+
+# maybe you need to patch:
+# src/json/jsoncpp.cpp:
+# -#include <json/json.h>
+# +#include "json/json.h"
+
+#svn export --force https://jsoncpp.svn.sourceforge.net/svnroot/jsoncpp/trunk/jsoncpp/src/lib_json json
+#svn export --force https://jsoncpp.svn.sourceforge.net/svnroot/jsoncpp/trunk/jsoncpp/include/json json
diff --git a/src/json/json.h b/src/json/json.h
new file mode 100644
index 000000000..396aafa82
--- /dev/null
+++ b/src/json/json.h
@@ -0,0 +1,1914 @@
+/// Json-cpp amalgated header (http://jsoncpp.sourceforge.net/).
+/// It is intented to be used with #include <json/json.h>
+
+// //////////////////////////////////////////////////////////////////////
+// Beginning of content of file: LICENSE
+// //////////////////////////////////////////////////////////////////////
+
+/*
+The JsonCpp library's source code, including accompanying documentation,
+tests and demonstration applications, are licensed under the following
+conditions...
+
+The author (Baptiste Lepilleur) explicitly disclaims copyright in all
+jurisdictions which recognize such a disclaimer. In such jurisdictions,
+this software is released into the Public Domain.
+
+In jurisdictions which do not recognize Public Domain property (e.g. Germany as of
+2010), this software is Copyright (c) 2007-2010 by Baptiste Lepilleur, and is
+released under the terms of the MIT License (see below).
+
+In jurisdictions which recognize Public Domain property, the user of this
+software may choose to accept it either as 1) Public Domain, 2) under the
+conditions of the MIT License (see below), or 3) under the terms of dual
+Public Domain/MIT License conditions described here, as they choose.
+
+The MIT License is about as close to Public Domain as a license can get, and is
+described in clear, concise terms at:
+
+ http://en.wikipedia.org/wiki/MIT_License
+
+The full text of the MIT License follows:
+
+========================================================================
+Copyright (c) 2007-2010 Baptiste Lepilleur
+
+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.
+========================================================================
+(END LICENSE TEXT)
+
+The MIT license is compatible with both the GPL and commercial
+software, affording one all of the rights of Public Domain with the
+minor nuisance of being required to keep the above copyright notice
+and license text in the source code. Note also that by accepting the
+Public Domain "license" you can re-license your copy using whatever
+license you like.
+
+*/
+
+// //////////////////////////////////////////////////////////////////////
+// End of content of file: LICENSE
+// //////////////////////////////////////////////////////////////////////
+
+
+
+
+
+#ifndef JSON_AMALGATED_H_INCLUDED
+# define JSON_AMALGATED_H_INCLUDED
+/// If defined, indicates that the source file is amalgated
+/// to prevent private header inclusion.
+#define JSON_IS_AMALGAMATION
+
+// //////////////////////////////////////////////////////////////////////
+// Beginning of content of file: include/json/config.h
+// //////////////////////////////////////////////////////////////////////
+
+// Copyright 2007-2010 Baptiste Lepilleur
+// Distributed under MIT license, or public domain if desired and
+// recognized in your jurisdiction.
+// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
+
+#ifndef JSON_CONFIG_H_INCLUDED
+# define JSON_CONFIG_H_INCLUDED
+
+/// If defined, indicates that json library is embedded in CppTL library.
+//# define JSON_IN_CPPTL 1
+
+/// If defined, indicates that json may leverage CppTL library
+//# define JSON_USE_CPPTL 1
+/// If defined, indicates that cpptl vector based map should be used instead of std::map
+/// as Value container.
+//# define JSON_USE_CPPTL_SMALLMAP 1
+/// If defined, indicates that Json specific container should be used
+/// (hash table & simple deque container with customizable allocator).
+/// THIS FEATURE IS STILL EXPERIMENTAL! There is know bugs: See #3177332
+//# define JSON_VALUE_USE_INTERNAL_MAP 1
+/// Force usage of standard new/malloc based allocator instead of memory pool based allocator.
+/// The memory pools allocator used optimization (initializing Value and ValueInternalLink
+/// as if it was a POD) that may cause some validation tool to report errors.
+/// Only has effects if JSON_VALUE_USE_INTERNAL_MAP is defined.
+//# define JSON_USE_SIMPLE_INTERNAL_ALLOCATOR 1
+
+// If non-zero, the library uses exceptions to report bad input instead of C
+// assertion macros. The default is to use exceptions.
+# ifndef JSON_USE_EXCEPTION
+# define JSON_USE_EXCEPTION 1
+# endif
+
+/// If defined, indicates that the source file is amalgated
+/// to prevent private header inclusion.
+/// Remarks: it is automatically defined in the generated amalgated header.
+// #define JSON_IS_AMALGAMATION
+
+
+# ifdef JSON_IN_CPPTL
+# include <cpptl/config.h>
+# ifndef JSON_USE_CPPTL
+# define JSON_USE_CPPTL 1
+# endif
+# endif
+
+# ifdef JSON_IN_CPPTL
+# define JSON_API CPPTL_API
+# elif defined(JSON_DLL_BUILD)
+# define JSON_API __declspec(dllexport)
+# elif defined(JSON_DLL)
+# define JSON_API __declspec(dllimport)
+# else
+# define JSON_API
+# endif
+
+// If JSON_NO_INT64 is defined, then Json only support C++ "int" type for integer
+// Storages, and 64 bits integer support is disabled.
+// #define JSON_NO_INT64 1
+
+#if defined(_MSC_VER) && _MSC_VER <= 1200 // MSVC 6
+// Microsoft Visual Studio 6 only support conversion from __int64 to double
+// (no conversion from unsigned __int64).
+#define JSON_USE_INT64_DOUBLE_CONVERSION 1
+#endif // if defined(_MSC_VER) && _MSC_VER < 1200 // MSVC 6
+
+#if defined(_MSC_VER) && _MSC_VER >= 1500 // MSVC 2008
+/// Indicates that the following function is deprecated.
+# define JSONCPP_DEPRECATED(message) __declspec(deprecated(message))
+#endif
+
+#if !defined(JSONCPP_DEPRECATED)
+# define JSONCPP_DEPRECATED(message)
+#endif // if !defined(JSONCPP_DEPRECATED)
+
+namespace Json {
+ typedef int Int;
+ typedef unsigned int UInt;
+# if defined(JSON_NO_INT64)
+ typedef int LargestInt;
+ typedef unsigned int LargestUInt;
+# undef JSON_HAS_INT64
+# else // if defined(JSON_NO_INT64)
+ // For Microsoft Visual use specific types as long long is not supported
+# if defined(_MSC_VER) // Microsoft Visual Studio
+ typedef __int64 Int64;
+ typedef unsigned __int64 UInt64;
+# else // if defined(_MSC_VER) // Other platforms, use long long
+ typedef long long int Int64;
+ typedef unsigned long long int UInt64;
+# endif // if defined(_MSC_VER)
+ typedef Int64 LargestInt;
+ typedef UInt64 LargestUInt;
+# define JSON_HAS_INT64
+# endif // if defined(JSON_NO_INT64)
+} // end namespace Json
+
+
+#endif // JSON_CONFIG_H_INCLUDED
+
+// //////////////////////////////////////////////////////////////////////
+// End of content of file: include/json/config.h
+// //////////////////////////////////////////////////////////////////////
+
+
+
+
+
+
+// //////////////////////////////////////////////////////////////////////
+// Beginning of content of file: include/json/forwards.h
+// //////////////////////////////////////////////////////////////////////
+
+// Copyright 2007-2010 Baptiste Lepilleur
+// Distributed under MIT license, or public domain if desired and
+// recognized in your jurisdiction.
+// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
+
+#ifndef JSON_FORWARDS_H_INCLUDED
+# define JSON_FORWARDS_H_INCLUDED
+
+#if !defined(JSON_IS_AMALGAMATION)
+# include "config.h"
+#endif // if !defined(JSON_IS_AMALGAMATION)
+
+namespace Json {
+
+ // writer.h
+ class FastWriter;
+ class StyledWriter;
+
+ // reader.h
+ class Reader;
+
+ // features.h
+ class Features;
+
+ // value.h
+ typedef unsigned int ArrayIndex;
+ class StaticString;
+ class Path;
+ class PathArgument;
+ class Value;
+ class ValueIteratorBase;
+ class ValueIterator;
+ class ValueConstIterator;
+#ifdef JSON_VALUE_USE_INTERNAL_MAP
+ class ValueMapAllocator;
+ class ValueInternalLink;
+ class ValueInternalArray;
+ class ValueInternalMap;
+#endif // #ifdef JSON_VALUE_USE_INTERNAL_MAP
+
+} // namespace Json
+
+
+#endif // JSON_FORWARDS_H_INCLUDED
+
+// //////////////////////////////////////////////////////////////////////
+// End of content of file: include/json/forwards.h
+// //////////////////////////////////////////////////////////////////////
+
+
+
+
+
+
+// //////////////////////////////////////////////////////////////////////
+// Beginning of content of file: include/json/features.h
+// //////////////////////////////////////////////////////////////////////
+
+// Copyright 2007-2010 Baptiste Lepilleur
+// Distributed under MIT license, or public domain if desired and
+// recognized in your jurisdiction.
+// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
+
+#ifndef CPPTL_JSON_FEATURES_H_INCLUDED
+# define CPPTL_JSON_FEATURES_H_INCLUDED
+
+#if !defined(JSON_IS_AMALGAMATION)
+# include "forwards.h"
+#endif // if !defined(JSON_IS_AMALGAMATION)
+
+namespace Json {
+
+ /** \brief Configuration passed to reader and writer.
+ * This configuration object can be used to force the Reader or Writer
+ * to behave in a standard conforming way.
+ */
+ class JSON_API Features
+ {
+ public:
+ /** \brief A configuration that allows all features and assumes all strings are UTF-8.
+ * - C & C++ comments are allowed
+ * - Root object can be any JSON value
+ * - Assumes Value strings are encoded in UTF-8
+ */
+ static Features all();
+
+ /** \brief A configuration that is strictly compatible with the JSON specification.
+ * - Comments are forbidden.
+ * - Root object must be either an array or an object value.
+ * - Assumes Value strings are encoded in UTF-8
+ */
+ static Features strictMode();
+
+ /** \brief Initialize the configuration like JsonConfig::allFeatures;
+ */
+ Features();
+
+ /// \c true if comments are allowed. Default: \c true.
+ bool allowComments_;
+
+ /// \c true if root must be either an array or an object value. Default: \c false.
+ bool strictRoot_;
+ };
+
+} // namespace Json
+
+#endif // CPPTL_JSON_FEATURES_H_INCLUDED
+
+// //////////////////////////////////////////////////////////////////////
+// End of content of file: include/json/features.h
+// //////////////////////////////////////////////////////////////////////
+
+
+
+
+
+
+// //////////////////////////////////////////////////////////////////////
+// Beginning of content of file: include/json/value.h
+// //////////////////////////////////////////////////////////////////////
+
+// Copyright 2007-2010 Baptiste Lepilleur
+// Distributed under MIT license, or public domain if desired and
+// recognized in your jurisdiction.
+// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
+
+#ifndef CPPTL_JSON_H_INCLUDED
+# define CPPTL_JSON_H_INCLUDED
+
+#if !defined(JSON_IS_AMALGAMATION)
+# include "forwards.h"
+#endif // if !defined(JSON_IS_AMALGAMATION)
+# include <string>
+# include <vector>
+
+# ifndef JSON_USE_CPPTL_SMALLMAP
+# include <map>
+# else
+# include <cpptl/smallmap.h>
+# endif
+# ifdef JSON_USE_CPPTL
+# include <cpptl/forwards.h>
+# endif
+
+/** \brief JSON (JavaScript Object Notation).
+ */
+namespace Json {
+
+ /** \brief Type of the value held by a Value object.
+ */
+ enum ValueType
+ {
+ nullValue = 0, ///< 'null' value
+ intValue, ///< signed integer value
+ uintValue, ///< unsigned integer value
+ realValue, ///< double value
+ stringValue, ///< UTF-8 string value
+ booleanValue, ///< bool value
+ arrayValue, ///< array value (ordered list)
+ objectValue ///< object value (collection of name/value pairs).
+ };
+
+ enum CommentPlacement
+ {
+ commentBefore = 0, ///< a comment placed on the line before a value
+ commentAfterOnSameLine, ///< a comment just after a value on the same line
+ commentAfter, ///< a comment on the line after a value (only make sense for root value)
+ numberOfCommentPlacement
+ };
+
+//# ifdef JSON_USE_CPPTL
+// typedef CppTL::AnyEnumerator<const char *> EnumMemberNames;
+// typedef CppTL::AnyEnumerator<const Value &> EnumValues;
+//# endif
+
+ /** \brief Lightweight wrapper to tag static string.
+ *
+ * Value constructor and objectValue member assignement takes advantage of the
+ * StaticString and avoid the cost of string duplication when storing the
+ * string or the member name.
+ *
+ * Example of usage:
+ * \code
+ * Json::Value aValue( StaticString("some text") );
+ * Json::Value object;
+ * static const StaticString code("code");
+ * object[code] = 1234;
+ * \endcode
+ */
+ class JSON_API StaticString
+ {
+ public:
+ explicit StaticString( const char *czstring )
+ : str_( czstring )
+ {
+ }
+
+ operator const char *() const
+ {
+ return str_;
+ }
+
+ const char *c_str() const
+ {
+ return str_;
+ }
+
+ private:
+ const char *str_;
+ };
+
+ /** \brief Represents a <a HREF="http://www.json.org">JSON</a> value.
+ *
+ * This class is a discriminated union wrapper that can represents a:
+ * - signed integer [range: Value::minInt - Value::maxInt]
+ * - unsigned integer (range: 0 - Value::maxUInt)
+ * - double
+ * - UTF-8 string
+ * - boolean
+ * - 'null'
+ * - an ordered list of Value
+ * - collection of name/value pairs (javascript object)
+ *
+ * The type of the held value is represented by a #ValueType and
+ * can be obtained using type().
+ *
+ * values of an #objectValue or #arrayValue can be accessed using operator[]() methods.
+ * Non const methods will automatically create the a #nullValue element
+ * if it does not exist.
+ * The sequence of an #arrayValue will be automatically resize and initialized
+ * with #nullValue. resize() can be used to enlarge or truncate an #arrayValue.
+ *
+ * The get() methods can be used to obtanis default value in the case the required element
+ * does not exist.
+ *
+ * It is possible to iterate over the list of a #objectValue values using
+ * the getMemberNames() method.
+ */
+ class JSON_API Value
+ {
+ friend class ValueIteratorBase;
+# ifdef JSON_VALUE_USE_INTERNAL_MAP
+ friend class ValueInternalLink;
+ friend class ValueInternalMap;
+# endif
+ public:
+ typedef std::vector<std::string> Members;
+ typedef ValueIterator iterator;
+ typedef ValueConstIterator const_iterator;
+ typedef Json::UInt UInt;
+ typedef Json::Int Int;
+# if defined(JSON_HAS_INT64)
+ typedef Json::UInt64 UInt64;
+ typedef Json::Int64 Int64;
+#endif // defined(JSON_HAS_INT64)
+ typedef Json::LargestInt LargestInt;
+ typedef Json::LargestUInt LargestUInt;
+ typedef Json::ArrayIndex ArrayIndex;
+
+ static const Value null;
+ /// Minimum signed integer value that can be stored in a Json::Value.
+ static const LargestInt minLargestInt;
+ /// Maximum signed integer value that can be stored in a Json::Value.
+ static const LargestInt maxLargestInt;
+ /// Maximum unsigned integer value that can be stored in a Json::Value.
+ static const LargestUInt maxLargestUInt;
+
+ /// Minimum signed int value that can be stored in a Json::Value.
+ static const Int minInt;
+ /// Maximum signed int value that can be stored in a Json::Value.
+ static const Int maxInt;
+ /// Maximum unsigned int value that can be stored in a Json::Value.
+ static const UInt maxUInt;
+
+# if defined(JSON_HAS_INT64)
+ /// Minimum signed 64 bits int value that can be stored in a Json::Value.
+ static const Int64 minInt64;
+ /// Maximum signed 64 bits int value that can be stored in a Json::Value.
+ static const Int64 maxInt64;
+ /// Maximum unsigned 64 bits int value that can be stored in a Json::Value.
+ static const UInt64 maxUInt64;
+#endif // defined(JSON_HAS_INT64)
+
+ private:
+#ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION
+# ifndef JSON_VALUE_USE_INTERNAL_MAP
+ class CZString
+ {
+ public:
+ enum DuplicationPolicy
+ {
+ noDuplication = 0,
+ duplicate,
+ duplicateOnCopy
+ };
+ CZString( ArrayIndex index );
+ CZString( const char *cstr, DuplicationPolicy allocate );
+ CZString( const CZString &other );
+ ~CZString();
+ CZString &operator =( const CZString &other );
+ bool operator<( const CZString &other ) const;
+ bool operator==( const CZString &other ) const;
+ ArrayIndex index() const;
+ const char *c_str() const;
+ bool isStaticString() const;
+ private:
+ void swap( CZString &other );
+ const char *cstr_;
+ ArrayIndex index_;
+ };
+
+ public:
+# ifndef JSON_USE_CPPTL_SMALLMAP
+ typedef std::map<CZString, Value> ObjectValues;
+# else
+ typedef CppTL::SmallMap<CZString, Value> ObjectValues;
+# endif // ifndef JSON_USE_CPPTL_SMALLMAP
+# endif // ifndef JSON_VALUE_USE_INTERNAL_MAP
+#endif // ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION
+
+ public:
+ /** \brief Create a default Value of the given type.
+
+ This is a very useful constructor.
+ To create an empty array, pass arrayValue.
+ To create an empty object, pass objectValue.
+ Another Value can then be set to this one by assignment.
+ This is useful since clear() and resize() will not alter types.
+
+ Examples:
+ \code
+ Json::Value null_value; // null
+ Json::Value arr_value(Json::arrayValue); // []
+ Json::Value obj_value(Json::objectValue); // {}
+ \endcode
+ */
+ Value( ValueType type = nullValue );
+ Value( Int value );
+ Value( UInt value );
+#if defined(JSON_HAS_INT64)
+ Value( Int64 value );
+ Value( UInt64 value );
+#endif // if defined(JSON_HAS_INT64)
+ Value( double value );
+ Value( const char *value );
+ Value( const char *beginValue, const char *endValue );
+ /** \brief Constructs a value from a static string.
+
+ * Like other value string constructor but do not duplicate the string for
+ * internal storage. The given string must remain alive after the call to this
+ * constructor.
+ * Example of usage:
+ * \code
+ * Json::Value aValue( StaticString("some text") );
+ * \endcode
+ */
+ Value( const StaticString &value );
+ Value( const std::string &value );
+# ifdef JSON_USE_CPPTL
+ Value( const CppTL::ConstString &value );
+# endif
+ Value( bool value );
+ Value( const Value &other );
+ ~Value();
+
+ Value &operator=( const Value &other );
+ /// Swap values.
+ /// \note Currently, comments are intentionally not swapped, for
+ /// both logic and efficiency.
+ void swap( Value &other );
+
+ ValueType type() const;
+
+ bool operator <( const Value &other ) const;
+ bool operator <=( const Value &other ) const;
+ bool operator >=( const Value &other ) const;
+ bool operator >( const Value &other ) const;
+
+ bool operator ==( const Value &other ) const;
+ bool operator !=( const Value &other ) const;
+
+ int compare( const Value &other ) const;
+
+ const char *asCString() const;
+ std::string asString() const;
+# ifdef JSON_USE_CPPTL
+ CppTL::ConstString asConstString() const;
+# endif
+ Int asInt() const;
+ UInt asUInt() const;
+#if defined(JSON_HAS_INT64)
+ Int64 asInt64() const;
+ UInt64 asUInt64() const;
+#endif // if defined(JSON_HAS_INT64)
+ LargestInt asLargestInt() const;
+ LargestUInt asLargestUInt() const;
+ float asFloat() const;
+ double asDouble() const;
+ bool asBool() const;
+
+ bool isNull() const;
+ bool isBool() const;
+ bool isInt() const;
+ bool isInt64() const;
+ bool isUInt() const;
+ bool isUInt64() const;
+ bool isIntegral() const;
+ bool isDouble() const;
+ bool isNumeric() const;
+ bool isString() const;
+ bool isArray() const;
+ bool isObject() const;
+
+ bool isConvertibleTo( ValueType other ) const;
+
+ /// Number of values in array or object
+ ArrayIndex size() const;
+
+ /// \brief Return true if empty array, empty object, or null;
+ /// otherwise, false.
+ bool empty() const;
+
+ /// Return isNull()
+ bool operator!() const;
+
+ /// Remove all object members and array elements.
+ /// \pre type() is arrayValue, objectValue, or nullValue
+ /// \post type() is unchanged
+ void clear();
+
+ /// Resize the array to size elements.
+ /// New elements are initialized to null.
+ /// May only be called on nullValue or arrayValue.
+ /// \pre type() is arrayValue or nullValue
+ /// \post type() is arrayValue
+ void resize( ArrayIndex size );
+
+ /// Access an array element (zero based index ).
+ /// If the array contains less than index element, then null value are inserted
+ /// in the array so that its size is index+1.
+ /// (You may need to say 'value[0u]' to get your compiler to distinguish
+ /// this from the operator[] which takes a string.)
+ Value &operator[]( ArrayIndex index );
+
+ /// Access an array element (zero based index ).
+ /// If the array contains less than index element, then null value are inserted
+ /// in the array so that its size is index+1.
+ /// (You may need to say 'value[0u]' to get your compiler to distinguish
+ /// this from the operator[] which takes a string.)
+ Value &operator[]( int index );
+
+ /// Access an array element (zero based index )
+ /// (You may need to say 'value[0u]' to get your compiler to distinguish
+ /// this from the operator[] which takes a string.)
+ const Value &operator[]( ArrayIndex index ) const;
+
+ /// Access an array element (zero based index )
+ /// (You may need to say 'value[0u]' to get your compiler to distinguish
+ /// this from the operator[] which takes a string.)
+ const Value &operator[]( int index ) const;
+
+ /// If the array contains at least index+1 elements, returns the element value,
+ /// otherwise returns defaultValue.
+ Value get( ArrayIndex index,
+ const Value &defaultValue ) const;
+ /// Return true if index < size().
+ bool isValidIndex( ArrayIndex index ) const;
+ /// \brief Append value to array at the end.
+ ///
+ /// Equivalent to jsonvalue[jsonvalue.size()] = value;
+ Value &append( const Value &value );
+
+ /// Access an object value by name, create a null member if it does not exist.
+ Value &operator[]( const char *key );
+ /// Access an object value by name, returns null if there is no member with that name.
+ const Value &operator[]( const char *key ) const;
+ /// Access an object value by name, create a null member if it does not exist.
+ Value &operator[]( const std::string &key );
+ /// Access an object value by name, returns null if there is no member with that name.
+ const Value &operator[]( const std::string &key ) const;
+ /** \brief Access an object value by name, create a null member if it does not exist.
+
+ * If the object as no entry for that name, then the member name used to store
+ * the new entry is not duplicated.
+ * Example of use:
+ * \code
+ * Json::Value object;
+ * static const StaticString code("code");
+ * object[code] = 1234;
+ * \endcode
+ */
+ Value &operator[]( const StaticString &key );
+# ifdef JSON_USE_CPPTL
+ /// Access an object value by name, create a null member if it does not exist.
+ Value &operator[]( const CppTL::ConstString &key );
+ /// Access an object value by name, returns null if there is no member with that name.
+ const Value &operator[]( const CppTL::ConstString &key ) const;
+# endif
+ /// Return the member named key if it exist, defaultValue otherwise.
+ Value get( const char *key,
+ const Value &defaultValue ) const;
+ /// Return the member named key if it exist, defaultValue otherwise.
+ Value get( const std::string &key,
+ const Value &defaultValue ) const;
+# ifdef JSON_USE_CPPTL
+ /// Return the member named key if it exist, defaultValue otherwise.
+ Value get( const CppTL::ConstString &key,
+ const Value &defaultValue ) const;
+# endif
+ /// \brief Remove and return the named member.
+ ///
+ /// Do nothing if it did not exist.
+ /// \return the removed Value, or null.
+ /// \pre type() is objectValue or nullValue
+ /// \post type() is unchanged
+ Value removeMember( const char* key );
+ /// Same as removeMember(const char*)
+ Value removeMember( const std::string &key );
+
+ /// Return true if the object has a member named key.
+ bool isMember( const char *key ) const;
+ /// Return true if the object has a member named key.
+ bool isMember( const std::string &key ) const;
+# ifdef JSON_USE_CPPTL
+ /// Return true if the object has a member named key.
+ bool isMember( const CppTL::ConstString &key ) const;
+# endif
+
+ /// \brief Return a list of the member names.
+ ///
+ /// If null, return an empty list.
+ /// \pre type() is objectValue or nullValue
+ /// \post if type() was nullValue, it remains nullValue
+ Members getMemberNames() const;
+
+//# ifdef JSON_USE_CPPTL
+// EnumMemberNames enumMemberNames() const;
+// EnumValues enumValues() const;
+//# endif
+
+ /// Comments must be //... or /* ... */
+ void setComment( const char *comment,
+ CommentPlacement placement );
+ /// Comments must be //... or /* ... */
+ void setComment( const std::string &comment,
+ CommentPlacement placement );
+ bool hasComment( CommentPlacement placement ) const;
+ /// Include delimiters and embedded newlines.
+ std::string getComment( CommentPlacement placement ) const;
+
+ std::string toStyledString() const;
+
+ const_iterator begin() const;
+ const_iterator end() const;
+
+ iterator begin();
+ iterator end();
+
+ private:
+ Value &resolveReference( const char *key,
+ bool isStatic );
+
+# ifdef JSON_VALUE_USE_INTERNAL_MAP
+ inline bool isItemAvailable() const
+ {
+ return itemIsUsed_ == 0;
+ }
+
+ inline void setItemUsed( bool isUsed = true )
+ {
+ itemIsUsed_ = isUsed ? 1 : 0;
+ }
+
+ inline bool isMemberNameStatic() const
+ {
+ return memberNameIsStatic_ == 0;
+ }
+
+ inline void setMemberNameIsStatic( bool isStatic )
+ {
+ memberNameIsStatic_ = isStatic ? 1 : 0;
+ }
+# endif // # ifdef JSON_VALUE_USE_INTERNAL_MAP
+
+ private:
+ struct CommentInfo
+ {
+ CommentInfo();
+ ~CommentInfo();
+
+ void setComment( const char *text );
+
+ char *comment_;
+ };
+
+ //struct MemberNamesTransform
+ //{
+ // typedef const char *result_type;
+ // const char *operator()( const CZString &name ) const
+ // {
+ // return name.c_str();
+ // }
+ //};
+
+ union ValueHolder
+ {
+ LargestInt int_;
+ LargestUInt uint_;
+ double real_;
+ bool bool_;
+ char *string_;
+# ifdef JSON_VALUE_USE_INTERNAL_MAP
+ ValueInternalArray *array_;
+ ValueInternalMap *map_;
+#else
+ ObjectValues *map_;
+# endif
+ } value_;
+ ValueType type_ : 8;
+ int allocated_ : 1; // Notes: if declared as bool, bitfield is useless.
+# ifdef JSON_VALUE_USE_INTERNAL_MAP
+ unsigned int itemIsUsed_ : 1; // used by the ValueInternalMap container.
+ int memberNameIsStatic_ : 1; // used by the ValueInternalMap container.
+# endif
+ CommentInfo *comments_;
+ };
+
+
+ /** \brief Experimental and untested: represents an element of the "path" to access a node.
+ */
+ class PathArgument
+ {
+ public:
+ friend class Path;
+
+ PathArgument();
+ PathArgument( ArrayIndex index );
+ PathArgument( const char *key );
+ PathArgument( const std::string &key );
+
+ private:
+ enum Kind
+ {
+ kindNone = 0,
+ kindIndex,
+ kindKey
+ };
+ std::string key_;
+ ArrayIndex index_;
+ Kind kind_;
+ };
+
+ /** \brief Experimental and untested: represents a "path" to access a node.
+ *
+ * Syntax:
+ * - "." => root node
+ * - ".[n]" => elements at index 'n' of root node (an array value)
+ * - ".name" => member named 'name' of root node (an object value)
+ * - ".name1.name2.name3"
+ * - ".[0][1][2].name1[3]"
+ * - ".%" => member name is provided as parameter
+ * - ".[%]" => index is provied as parameter
+ */
+ class Path
+ {
+ public:
+ Path( const std::string &path,
+ const PathArgument &a1 = PathArgument(),
+ const PathArgument &a2 = PathArgument(),
+ const PathArgument &a3 = PathArgument(),
+ const PathArgument &a4 = PathArgument(),
+ const PathArgument &a5 = PathArgument() );
+
+ const Value &resolve( const Value &root ) const;
+ Value resolve( const Value &root,
+ const Value &defaultValue ) const;
+ /// Creates the "path" to access the specified node and returns a reference on the node.
+ Value &make( Value &root ) const;
+
+ private:
+ typedef std::vector<const PathArgument *> InArgs;
+ typedef std::vector<PathArgument> Args;
+
+ void makePath( const std::string &path,
+ const InArgs &in );
+ void addPathInArg( const std::string &path,
+ const InArgs &in,
+ InArgs::const_iterator &itInArg,
+ PathArgument::Kind kind );
+ void invalidPath( const std::string &path,
+ int location );
+
+ Args args_;
+ };
+
+
+
+#ifdef JSON_VALUE_USE_INTERNAL_MAP
+ /** \brief Allocator to customize Value internal map.
+ * Below is an example of a simple implementation (default implementation actually
+ * use memory pool for speed).
+ * \code
+ class DefaultValueMapAllocator : public ValueMapAllocator
+ {
+ public: // overridden from ValueMapAllocator
+ virtual ValueInternalMap *newMap()
+ {
+ return new ValueInternalMap();
+ }
+
+ virtual ValueInternalMap *newMapCopy( const ValueInternalMap &other )
+ {
+ return new ValueInternalMap( other );
+ }
+
+ virtual void destructMap( ValueInternalMap *map )
+ {
+ delete map;
+ }
+
+ virtual ValueInternalLink *allocateMapBuckets( unsigned int size )
+ {
+ return new ValueInternalLink[size];
+ }
+
+ virtual void releaseMapBuckets( ValueInternalLink *links )
+ {
+ delete [] links;
+ }
+
+ virtual ValueInternalLink *allocateMapLink()
+ {
+ return new ValueInternalLink();
+ }
+
+ virtual void releaseMapLink( ValueInternalLink *link )
+ {
+ delete link;
+ }
+ };
+ * \endcode
+ */
+ class JSON_API ValueMapAllocator
+ {
+ public:
+ virtual ~ValueMapAllocator();
+ virtual ValueInternalMap *newMap() = 0;
+ virtual ValueInternalMap *newMapCopy( const ValueInternalMap &other ) = 0;
+ virtual void destructMap( ValueInternalMap *map ) = 0;
+ virtual ValueInternalLink *allocateMapBuckets( unsigned int size ) = 0;
+ virtual void releaseMapBuckets( ValueInternalLink *links ) = 0;
+ virtual ValueInternalLink *allocateMapLink() = 0;
+ virtual void releaseMapLink( ValueInternalLink *link ) = 0;
+ };
+
+ /** \brief ValueInternalMap hash-map bucket chain link (for internal use only).
+ * \internal previous_ & next_ allows for bidirectional traversal.
+ */
+ class JSON_API ValueInternalLink
+ {
+ public:
+ enum { itemPerLink = 6 }; // sizeof(ValueInternalLink) = 128 on 32 bits architecture.
+ enum InternalFlags {
+ flagAvailable = 0,
+ flagUsed = 1
+ };
+
+ ValueInternalLink();
+
+ ~ValueInternalLink();
+
+ Value items_[itemPerLink];
+ char *keys_[itemPerLink];
+ ValueInternalLink *previous_;
+ ValueInternalLink *next_;
+ };
+
+
+ /** \brief A linked page based hash-table implementation used internally by Value.
+ * \internal ValueInternalMap is a tradional bucket based hash-table, with a linked
+ * list in each bucket to handle collision. There is an addional twist in that
+ * each node of the collision linked list is a page containing a fixed amount of
+ * value. This provides a better compromise between memory usage and speed.
+ *
+ * Each bucket is made up of a chained list of ValueInternalLink. The last
+ * link of a given bucket can be found in the 'previous_' field of the following bucket.
+ * The last link of the last bucket is stored in tailLink_ as it has no following bucket.
+ * Only the last link of a bucket may contains 'available' item. The last link always
+ * contains at least one element unless is it the bucket one very first link.
+ */
+ class JSON_API ValueInternalMap
+ {
+ friend class ValueIteratorBase;
+ friend class Value;
+ public:
+ typedef unsigned int HashKey;
+ typedef unsigned int BucketIndex;
+
+# ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION
+ struct IteratorState
+ {
+ IteratorState()
+ : map_(0)
+ , link_(0)
+ , itemIndex_(0)
+ , bucketIndex_(0)
+ {
+ }
+ ValueInternalMap *map_;
+ ValueInternalLink *link_;
+ BucketIndex itemIndex_;
+ BucketIndex bucketIndex_;
+ };
+# endif // ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION
+
+ ValueInternalMap();
+ ValueInternalMap( const ValueInternalMap &other );
+ ValueInternalMap &operator =( const ValueInternalMap &other );
+ ~ValueInternalMap();
+
+ void swap( ValueInternalMap &other );
+
+ BucketIndex size() const;
+
+ void clear();
+
+ bool reserveDelta( BucketIndex growth );
+
+ bool reserve( BucketIndex newItemCount );
+
+ const Value *find( const char *key ) const;
+
+ Value *find( const char *key );
+
+ Value &resolveReference( const char *key,
+ bool isStatic );
+
+ void remove( const char *key );
+
+ void doActualRemove( ValueInternalLink *link,
+ BucketIndex index,
+ BucketIndex bucketIndex );
+
+ ValueInternalLink *&getLastLinkInBucket( BucketIndex bucketIndex );
+
+ Value &setNewItem( const char *key,
+ bool isStatic,
+ ValueInternalLink *link,
+ BucketIndex index );
+
+ Value &unsafeAdd( const char *key,
+ bool isStatic,
+ HashKey hashedKey );
+
+ HashKey hash( const char *key ) const;
+
+ int compare( const ValueInternalMap &other ) const;
+
+ private:
+ void makeBeginIterator( IteratorState &it ) const;
+ void makeEndIterator( IteratorState &it ) const;
+ static bool equals( const IteratorState &x, const IteratorState &other );
+ static void increment( IteratorState &iterator );
+ static void incrementBucket( IteratorState &iterator );
+ static void decrement( IteratorState &iterator );
+ static const char *key( const IteratorState &iterator );
+ static const char *key( const IteratorState &iterator, bool &isStatic );
+ static Value &value( const IteratorState &iterator );
+ static int distance( const IteratorState &x, const IteratorState &y );
+
+ private:
+ ValueInternalLink *buckets_;
+ ValueInternalLink *tailLink_;
+ BucketIndex bucketsSize_;
+ BucketIndex itemCount_;
+ };
+
+ /** \brief A simplified deque implementation used internally by Value.
+ * \internal
+ * It is based on a list of fixed "page", each page contains a fixed number of items.
+ * Instead of using a linked-list, a array of pointer is used for fast item look-up.
+ * Look-up for an element is as follow:
+ * - compute page index: pageIndex = itemIndex / itemsPerPage
+ * - look-up item in page: pages_[pageIndex][itemIndex % itemsPerPage]
+ *
+ * Insertion is amortized constant time (only the array containing the index of pointers
+ * need to be reallocated when items are appended).
+ */
+ class JSON_API ValueInternalArray
+ {
+ friend class Value;
+ friend class ValueIteratorBase;
+ public:
+ enum { itemsPerPage = 8 }; // should be a power of 2 for fast divide and modulo.
+ typedef Value::ArrayIndex ArrayIndex;
+ typedef unsigned int PageIndex;
+
+# ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION
+ struct IteratorState // Must be a POD
+ {
+ IteratorState()
+ : array_(0)
+ , currentPageIndex_(0)
+ , currentItemIndex_(0)
+ {
+ }
+ ValueInternalArray *array_;
+ Value **currentPageIndex_;
+ unsigned int currentItemIndex_;
+ };
+# endif // ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION
+
+ ValueInternalArray();
+ ValueInternalArray( const ValueInternalArray &other );
+ ValueInternalArray &operator =( const ValueInternalArray &other );
+ ~ValueInternalArray();
+ void swap( ValueInternalArray &other );
+
+ void clear();
+ void resize( ArrayIndex newSize );
+
+ Value &resolveReference( ArrayIndex index );
+
+ Value *find( ArrayIndex index ) const;
+
+ ArrayIndex size() const;
+
+ int compare( const ValueInternalArray &other ) const;
+
+ private:
+ static bool equals( const IteratorState &x, const IteratorState &other );
+ static void increment( IteratorState &iterator );
+ static void decrement( IteratorState &iterator );
+ static Value &dereference( const IteratorState &iterator );
+ static Value &unsafeDereference( const IteratorState &iterator );
+ static int distance( const IteratorState &x, const IteratorState &y );
+ static ArrayIndex indexOf( const IteratorState &iterator );
+ void makeBeginIterator( IteratorState &it ) const;
+ void makeEndIterator( IteratorState &it ) const;
+ void makeIterator( IteratorState &it, ArrayIndex index ) const;
+
+ void makeIndexValid( ArrayIndex index );
+
+ Value **pages_;
+ ArrayIndex size_;
+ PageIndex pageCount_;
+ };
+
+ /** \brief Experimental: do not use. Allocator to customize Value internal array.
+ * Below is an example of a simple implementation (actual implementation use
+ * memory pool).
+ \code
+class DefaultValueArrayAllocator : public ValueArrayAllocator
+{
+public: // overridden from ValueArrayAllocator
+ virtual ~DefaultValueArrayAllocator()
+ {
+ }
+
+ virtual ValueInternalArray *newArray()
+ {
+ return new ValueInternalArray();
+ }
+
+ virtual ValueInternalArray *newArrayCopy( const ValueInternalArray &other )
+ {
+ return new ValueInternalArray( other );
+ }
+
+ virtual void destruct( ValueInternalArray *array )
+ {
+ delete array;
+ }
+
+ virtual void reallocateArrayPageIndex( Value **&indexes,
+ ValueInternalArray::PageIndex &indexCount,
+ ValueInternalArray::PageIndex minNewIndexCount )
+ {
+ ValueInternalArray::PageIndex newIndexCount = (indexCount*3)/2 + 1;
+ if ( minNewIndexCount > newIndexCount )
+ newIndexCount = minNewIndexCount;
+ void *newIndexes = realloc( indexes, sizeof(Value*) * newIndexCount );
+ if ( !newIndexes )
+ throw std::bad_alloc();
+ indexCount = newIndexCount;
+ indexes = static_cast<Value **>( newIndexes );
+ }
+ virtual void releaseArrayPageIndex( Value **indexes,
+ ValueInternalArray::PageIndex indexCount )
+ {
+ if ( indexes )
+ free( indexes );
+ }
+
+ virtual Value *allocateArrayPage()
+ {
+ return static_cast<Value *>( malloc( sizeof(Value) * ValueInternalArray::itemsPerPage ) );
+ }
+
+ virtual void releaseArrayPage( Value *value )
+ {
+ if ( value )
+ free( value );
+ }
+};
+ \endcode
+ */
+ class JSON_API ValueArrayAllocator
+ {
+ public:
+ virtual ~ValueArrayAllocator();
+ virtual ValueInternalArray *newArray() = 0;
+ virtual ValueInternalArray *newArrayCopy( const ValueInternalArray &other ) = 0;
+ virtual void destructArray( ValueInternalArray *array ) = 0;
+ /** \brief Reallocate array page index.
+ * Reallocates an array of pointer on each page.
+ * \param indexes [input] pointer on the current index. May be \c NULL.
+ * [output] pointer on the new index of at least
+ * \a minNewIndexCount pages.
+ * \param indexCount [input] current number of pages in the index.
+ * [output] number of page the reallocated index can handle.
+ * \b MUST be >= \a minNewIndexCount.
+ * \param minNewIndexCount Minimum number of page the new index must be able to
+ * handle.
+ */
+ virtual void reallocateArrayPageIndex( Value **&indexes,
+ ValueInternalArray::PageIndex &indexCount,
+ ValueInternalArray::PageIndex minNewIndexCount ) = 0;
+ virtual void releaseArrayPageIndex( Value **indexes,
+ ValueInternalArray::PageIndex indexCount ) = 0;
+ virtual Value *allocateArrayPage() = 0;
+ virtual void releaseArrayPage( Value *value ) = 0;
+ };
+#endif // #ifdef JSON_VALUE_USE_INTERNAL_MAP
+
+
+ /** \brief base class for Value iterators.
+ *
+ */
+ class ValueIteratorBase
+ {
+ public:
+ typedef unsigned int size_t;
+ typedef int difference_type;
+ typedef ValueIteratorBase SelfType;
+
+ ValueIteratorBase();
+#ifndef JSON_VALUE_USE_INTERNAL_MAP
+ explicit ValueIteratorBase( const Value::ObjectValues::iterator &current );
+#else
+ ValueIteratorBase( const ValueInternalArray::IteratorState &state );
+ ValueIteratorBase( const ValueInternalMap::IteratorState &state );
+#endif
+
+ bool operator ==( const SelfType &other ) const
+ {
+ return isEqual( other );
+ }
+
+ bool operator !=( const SelfType &other ) const
+ {
+ return !isEqual( other );
+ }
+
+ difference_type operator -( const SelfType &other ) const
+ {
+ return computeDistance( other );
+ }
+
+ /// Return either the index or the member name of the referenced value as a Value.
+ Value key() const;
+
+ /// Return the index of the referenced Value. -1 if it is not an arrayValue.
+ UInt index() const;
+
+ /// Return the member name of the referenced Value. "" if it is not an objectValue.
+ const char *memberName() const;
+
+ protected:
+ Value &deref() const;
+
+ void increment();
+
+ void decrement();
+
+ difference_type computeDistance( const SelfType &other ) const;
+
+ bool isEqual( const SelfType &other ) const;
+
+ void copy( const SelfType &other );
+
+ private:
+#ifndef JSON_VALUE_USE_INTERNAL_MAP
+ Value::ObjectValues::iterator current_;
+ // Indicates that iterator is for a null value.
+ bool isNull_;
+#else
+ union
+ {
+ ValueInternalArray::IteratorState array_;
+ ValueInternalMap::IteratorState map_;
+ } iterator_;
+ bool isArray_;
+#endif
+ };
+
+ /** \brief const iterator for object and array value.
+ *
+ */
+ class ValueConstIterator : public ValueIteratorBase
+ {
+ friend class Value;
+ public:
+ typedef unsigned int size_t;
+ typedef int difference_type;
+ typedef const Value &reference;
+ typedef const Value *pointer;
+ typedef ValueConstIterator SelfType;
+
+ ValueConstIterator();
+ private:
+ /*! \internal Use by Value to create an iterator.
+ */
+#ifndef JSON_VALUE_USE_INTERNAL_MAP
+ explicit ValueConstIterator( const Value::ObjectValues::iterator &current );
+#else
+ ValueConstIterator( const ValueInternalArray::IteratorState &state );
+ ValueConstIterator( const ValueInternalMap::IteratorState &state );
+#endif
+ public:
+ SelfType &operator =( const ValueIteratorBase &other );
+
+ SelfType operator++( int )
+ {
+ SelfType temp( *this );
+ ++*this;
+ return temp;
+ }
+
+ SelfType operator--( int )
+ {
+ SelfType temp( *this );
+ --*this;
+ return temp;
+ }
+
+ SelfType &operator--()
+ {
+ decrement();
+ return *this;
+ }
+
+ SelfType &operator++()
+ {
+ increment();
+ return *this;
+ }
+
+ reference operator *() const
+ {
+ return deref();
+ }
+ };
+
+
+ /** \brief Iterator for object and array value.
+ */
+ class ValueIterator : public ValueIteratorBase
+ {
+ friend class Value;
+ public:
+ typedef unsigned int size_t;
+ typedef int difference_type;
+ typedef Value &reference;
+ typedef Value *pointer;
+ typedef ValueIterator SelfType;
+
+ ValueIterator();
+ ValueIterator( const ValueConstIterator &other );
+ ValueIterator( const ValueIterator &other );
+ private:
+ /*! \internal Use by Value to create an iterator.
+ */
+#ifndef JSON_VALUE_USE_INTERNAL_MAP
+ explicit ValueIterator( const Value::ObjectValues::iterator &current );
+#else
+ ValueIterator( const ValueInternalArray::IteratorState &state );
+ ValueIterator( const ValueInternalMap::IteratorState &state );
+#endif
+ public:
+
+ SelfType &operator =( const SelfType &other );
+
+ SelfType operator++( int )
+ {
+ SelfType temp( *this );
+ ++*this;
+ return temp;
+ }
+
+ SelfType operator--( int )
+ {
+ SelfType temp( *this );
+ --*this;
+ return temp;
+ }
+
+ SelfType &operator--()
+ {
+ decrement();
+ return *this;
+ }
+
+ SelfType &operator++()
+ {
+ increment();
+ return *this;
+ }
+
+ reference operator *() const
+ {
+ return deref();
+ }
+ };
+
+
+} // namespace Json
+
+
+#endif // CPPTL_JSON_H_INCLUDED
+
+// //////////////////////////////////////////////////////////////////////
+// End of content of file: include/json/value.h
+// //////////////////////////////////////////////////////////////////////
+
+
+
+
+
+
+// //////////////////////////////////////////////////////////////////////
+// Beginning of content of file: include/json/reader.h
+// //////////////////////////////////////////////////////////////////////
+
+// Copyright 2007-2010 Baptiste Lepilleur
+// Distributed under MIT license, or public domain if desired and
+// recognized in your jurisdiction.
+// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
+
+#ifndef CPPTL_JSON_READER_H_INCLUDED
+# define CPPTL_JSON_READER_H_INCLUDED
+
+#if !defined(JSON_IS_AMALGAMATION)
+# include "features.h"
+# include "value.h"
+#endif // if !defined(JSON_IS_AMALGAMATION)
+# include <deque>
+# include <stack>
+# include <string>
+
+namespace Json {
+
+ /** \brief Unserialize a <a HREF="http://www.json.org">JSON</a> document into a Value.
+ *
+ */
+ class JSON_API Reader
+ {
+ public:
+ typedef char Char;
+ typedef const Char *Location;
+
+ /** \brief Constructs a Reader allowing all features
+ * for parsing.
+ */
+ Reader();
+
+ /** \brief Constructs a Reader allowing the specified feature set
+ * for parsing.
+ */
+ Reader( const Features &features );
+
+ /** \brief Read a Value from a <a HREF="http://www.json.org">JSON</a> document.
+ * \param document UTF-8 encoded string containing the document to read.
+ * \param root [out] Contains the root value of the document if it was
+ * successfully parsed.
+ * \param collectComments \c true to collect comment and allow writing them back during
+ * serialization, \c false to discard comments.
+ * This parameter is ignored if Features::allowComments_
+ * is \c false.
+ * \return \c true if the document was successfully parsed, \c false if an error occurred.
+ */
+ bool parse( const std::string &document,
+ Value &root,
+ bool collectComments = true );
+
+ /** \brief Read a Value from a <a HREF="http://www.json.org">JSON</a> document.
+ * \param beginDoc Pointer on the beginning of the UTF-8 encoded string of the document to read.
+ * \param endDoc Pointer on the end of the UTF-8 encoded string of the document to read.
+ \ Must be >= beginDoc.
+ * \param root [out] Contains the root value of the document if it was
+ * successfully parsed.
+ * \param collectComments \c true to collect comment and allow writing them back during
+ * serialization, \c false to discard comments.
+ * This parameter is ignored if Features::allowComments_
+ * is \c false.
+ * \return \c true if the document was successfully parsed, \c false if an error occurred.
+ */
+ bool parse( const char *beginDoc, const char *endDoc,
+ Value &root,
+ bool collectComments = true );
+
+ /// \brief Parse from input stream.
+ /// \see Json::operator>>(std::istream&, Json::Value&).
+ bool parse( std::istream &is,
+ Value &root,
+ bool collectComments = true );
+
+ /** \brief Returns a user friendly string that list errors in the parsed document.
+ * \return Formatted error message with the list of errors with their location in
+ * the parsed document. An empty string is returned if no error occurred
+ * during parsing.
+ * \deprecated Use getFormattedErrorMessages() instead (typo fix).
+ */
+ JSONCPP_DEPRECATED("Use getFormattedErrorMessages instead")
+ std::string getFormatedErrorMessages() const;
+
+ /** \brief Returns a user friendly string that list errors in the parsed document.
+ * \return Formatted error message with the list of errors with their location in
+ * the parsed document. An empty string is returned if no error occurred
+ * during parsing.
+ */
+ std::string getFormattedErrorMessages() const;
+
+ private:
+ enum TokenType
+ {
+ tokenEndOfStream = 0,
+ tokenObjectBegin,
+ tokenObjectEnd,
+ tokenArrayBegin,
+ tokenArrayEnd,
+ tokenString,
+ tokenNumber,
+ tokenTrue,
+ tokenFalse,
+ tokenNull,
+ tokenArraySeparator,
+ tokenMemberSeparator,
+ tokenComment,
+ tokenError
+ };
+
+ class Token
+ {
+ public:
+ TokenType type_;
+ Location start_;
+ Location end_;
+ };
+
+ class ErrorInfo
+ {
+ public:
+ Token token_;
+ std::string message_;
+ Location extra_;
+ };
+
+ typedef std::deque<ErrorInfo> Errors;
+
+ bool expectToken( TokenType type, Token &token, const char *message );
+ bool readToken( Token &token );
+ void skipSpaces();
+ bool match( Location pattern,
+ int patternLength );
+ bool readComment();
+ bool readCStyleComment();
+ bool readCppStyleComment();
+ bool readString();
+ void readNumber();
+ bool readValue();
+ bool readObject( Token &token );
+ bool readArray( Token &token );
+ bool decodeNumber( Token &token );
+ bool decodeString( Token &token );
+ bool decodeString( Token &token, std::string &decoded );
+ bool decodeDouble( Token &token );
+ bool decodeUnicodeCodePoint( Token &token,
+ Location &current,
+ Location end,
+ unsigned int &unicode );
+ bool decodeUnicodeEscapeSequence( Token &token,
+ Location &current,
+ Location end,
+ unsigned int &unicode );
+ bool addError( const std::string &message,
+ Token &token,
+ Location extra = 0 );
+ bool recoverFromError( TokenType skipUntilToken );
+ bool addErrorAndRecover( const std::string &message,
+ Token &token,
+ TokenType skipUntilToken );
+ void skipUntilSpace();
+ Value &currentValue();
+ Char getNextChar();
+ void getLocationLineAndColumn( Location location,
+ int &line,
+ int &column ) const;
+ std::string getLocationLineAndColumn( Location location ) const;
+ void addComment( Location begin,
+ Location end,
+ CommentPlacement placement );
+ void skipCommentTokens( Token &token );
+
+ typedef std::stack<Value *> Nodes;
+ Nodes nodes_;
+ Errors errors_;
+ std::string document_;
+ Location begin_;
+ Location end_;
+ Location current_;
+ Location lastValueEnd_;
+ Value *lastValue_;
+ std::string commentsBefore_;
+ Features features_;
+ bool collectComments_;
+ };
+
+ /** \brief Read from 'sin' into 'root'.
+
+ Always keep comments from the input JSON.
+
+ This can be used to read a file into a particular sub-object.
+ For example:
+ \code
+ Json::Value root;
+ cin >> root["dir"]["file"];
+ cout << root;
+ \endcode
+ Result:
+ \verbatim
+ {
+ "dir": {
+ "file": {
+ // The input stream JSON would be nested here.
+ }
+ }
+ }
+ \endverbatim
+ \throw std::exception on parse error.
+ \see Json::operator<<()
+ */
+ std::istream& operator>>( std::istream&, Value& );
+
+} // namespace Json
+
+#endif // CPPTL_JSON_READER_H_INCLUDED
+
+// //////////////////////////////////////////////////////////////////////
+// End of content of file: include/json/reader.h
+// //////////////////////////////////////////////////////////////////////
+
+
+
+
+
+
+// //////////////////////////////////////////////////////////////////////
+// Beginning of content of file: include/json/writer.h
+// //////////////////////////////////////////////////////////////////////
+
+// Copyright 2007-2010 Baptiste Lepilleur
+// Distributed under MIT license, or public domain if desired and
+// recognized in your jurisdiction.
+// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
+
+#ifndef JSON_WRITER_H_INCLUDED
+# define JSON_WRITER_H_INCLUDED
+
+#if !defined(JSON_IS_AMALGAMATION)
+# include "value.h"
+#endif // if !defined(JSON_IS_AMALGAMATION)
+# include <vector>
+# include <string>
+
+namespace Json {
+
+ class Value;
+
+ /** \brief Abstract class for writers.
+ */
+ class JSON_API Writer
+ {
+ public:
+ virtual ~Writer();
+
+ virtual std::string write( const Value &root ) = 0;
+ };
+
+ /** \brief Outputs a Value in <a HREF="http://www.json.org">JSON</a> format without formatting (not human friendly).
+ *
+ * The JSON document is written in a single line. It is not intended for 'human' consumption,
+ * but may be usefull to support feature such as RPC where bandwith is limited.
+ * \sa Reader, Value
+ */
+ class JSON_API FastWriter : public Writer
+ {
+ public:
+ FastWriter();
+ virtual ~FastWriter(){}
+
+ void enableYAMLCompatibility();
+
+ /** \brief Drop the "null" string from the writer's output for nullValues.
+ * Strictly speaking, this is not valid JSON. But when the output is being
+ * fed to a browser's Javascript, it makes for smaller output and the
+ * browser can handle the output just fine.
+ */
+ void dropNullPlaceholders();
+
+ public: // overridden from Writer
+ virtual std::string write( const Value &root );
+
+ private:
+ void writeValue( const Value &value );
+
+ std::string document_;
+ bool yamlCompatiblityEnabled_;
+ bool dropNullPlaceholders_;
+ };
+
+ /** \brief Writes a Value in <a HREF="http://www.json.org">JSON</a> format in a human friendly way.
+ *
+ * The rules for line break and indent are as follow:
+ * - Object value:
+ * - if empty then print {} without indent and line break
+ * - if not empty the print '{', line break & indent, print one value per line
+ * and then unindent and line break and print '}'.
+ * - Array value:
+ * - if empty then print [] without indent and line break
+ * - if the array contains no object value, empty array or some other value types,
+ * and all the values fit on one lines, then print the array on a single line.
+ * - otherwise, it the values do not fit on one line, or the array contains
+ * object or non empty array, then print one value per line.
+ *
+ * If the Value have comments then they are outputed according to their #CommentPlacement.
+ *
+ * \sa Reader, Value, Value::setComment()
+ */
+ class JSON_API StyledWriter: public Writer
+ {
+ public:
+ StyledWriter();
+ virtual ~StyledWriter(){}
+
+ public: // overridden from Writer
+ /** \brief Serialize a Value in <a HREF="http://www.json.org">JSON</a> format.
+ * \param root Value to serialize.
+ * \return String containing the JSON document that represents the root value.
+ */
+ virtual std::string write( const Value &root );
+
+ private:
+ void writeValue( const Value &value );
+ void writeArrayValue( const Value &value );
+ bool isMultineArray( const Value &value );
+ void pushValue( const std::string &value );
+ void writeIndent();
+ void writeWithIndent( const std::string &value );
+ void indent();
+ void unindent();
+ void writeCommentBeforeValue( const Value &root );
+ void writeCommentAfterValueOnSameLine( const Value &root );
+ bool hasCommentForValue( const Value &value );
+ static std::string normalizeEOL( const std::string &text );
+
+ typedef std::vector<std::string> ChildValues;
+
+ ChildValues childValues_;
+ std::string document_;
+ std::string indentString_;
+ int rightMargin_;
+ int indentSize_;
+ bool addChildValues_;
+ };
+
+ /** \brief Writes a Value in <a HREF="http://www.json.org">JSON</a> format in a human friendly way,
+ to a stream rather than to a string.
+ *
+ * The rules for line break and indent are as follow:
+ * - Object value:
+ * - if empty then print {} without indent and line break
+ * - if not empty the print '{', line break & indent, print one value per line
+ * and then unindent and line break and print '}'.
+ * - Array value:
+ * - if empty then print [] without indent and line break
+ * - if the array contains no object value, empty array or some other value types,
+ * and all the values fit on one lines, then print the array on a single line.
+ * - otherwise, it the values do not fit on one line, or the array contains
+ * object or non empty array, then print one value per line.
+ *
+ * If the Value have comments then they are outputed according to their #CommentPlacement.
+ *
+ * \param indentation Each level will be indented by this amount extra.
+ * \sa Reader, Value, Value::setComment()
+ */
+ class JSON_API StyledStreamWriter
+ {
+ public:
+ StyledStreamWriter( std::string indentation="\t" );
+ ~StyledStreamWriter(){}
+
+ public:
+ /** \brief Serialize a Value in <a HREF="http://www.json.org">JSON</a> format.
+ * \param out Stream to write to. (Can be ostringstream, e.g.)
+ * \param root Value to serialize.
+ * \note There is no point in deriving from Writer, since write() should not return a value.
+ */
+ void write( std::ostream &out, const Value &root );
+
+ private:
+ void writeValue( const Value &value );
+ void writeArrayValue( const Value &value );
+ bool isMultineArray( const Value &value );
+ void pushValue( const std::string &value );
+ void writeIndent();
+ void writeWithIndent( const std::string &value );
+ void indent();
+ void unindent();
+ void writeCommentBeforeValue( const Value &root );
+ void writeCommentAfterValueOnSameLine( const Value &root );
+ bool hasCommentForValue( const Value &value );
+ static std::string normalizeEOL( const std::string &text );
+
+ typedef std::vector<std::string> ChildValues;
+
+ ChildValues childValues_;
+ std::ostream* document_;
+ std::string indentString_;
+ int rightMargin_;
+ std::string indentation_;
+ bool addChildValues_;
+ };
+
+# if defined(JSON_HAS_INT64)
+ std::string JSON_API valueToString( Int value );
+ std::string JSON_API valueToString( UInt value );
+# endif // if defined(JSON_HAS_INT64)
+ std::string JSON_API valueToString( LargestInt value );
+ std::string JSON_API valueToString( LargestUInt value );
+ std::string JSON_API valueToString( double value );
+ std::string JSON_API valueToString( bool value );
+ std::string JSON_API valueToQuotedString( const char *value );
+
+ /// \brief Output using the StyledStreamWriter.
+ /// \see Json::operator>>()
+ std::ostream& operator<<( std::ostream&, const Value &root );
+
+} // namespace Json
+
+
+
+#endif // JSON_WRITER_H_INCLUDED
+
+// //////////////////////////////////////////////////////////////////////
+// End of content of file: include/json/writer.h
+// //////////////////////////////////////////////////////////////////////
+
+
+
+
+
+
+// //////////////////////////////////////////////////////////////////////
+// Beginning of content of file: include/json/assertions.h
+// //////////////////////////////////////////////////////////////////////
+
+// Copyright 2007-2010 Baptiste Lepilleur
+// Distributed under MIT license, or public domain if desired and
+// recognized in your jurisdiction.
+// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
+
+#ifndef CPPTL_JSON_ASSERTIONS_H_INCLUDED
+# define CPPTL_JSON_ASSERTIONS_H_INCLUDED
+
+#include <stdlib.h>
+
+#if !defined(JSON_IS_AMALGAMATION)
+# include <json/config.h>
+#endif // if !defined(JSON_IS_AMALGAMATION)
+
+#if JSON_USE_EXCEPTION
+#define JSON_ASSERT( condition ) assert( condition ); // @todo <= change this into an exception throw
+#define JSON_FAIL_MESSAGE( message ) throw std::runtime_error( message );
+#else // JSON_USE_EXCEPTION
+#define JSON_ASSERT( condition ) assert( condition );
+
+// The call to assert() will show the failure message in debug builds. In
+// release bugs we write to invalid memory in order to crash hard, so that a
+// debugger or crash reporter gets the chance to take over. We still call exit()
+// afterward in order to tell the compiler that this macro doesn't return.
+#define JSON_FAIL_MESSAGE( message ) { assert(false && message); strcpy(reinterpret_cast<char*>(666), message); exit(123); }
+
+#endif
+
+#define JSON_ASSERT_MESSAGE( condition, message ) if (!( condition )) { JSON_FAIL_MESSAGE( message ) }
+
+#endif // CPPTL_JSON_ASSERTIONS_H_INCLUDED
+
+// //////////////////////////////////////////////////////////////////////
+// End of content of file: include/json/assertions.h
+// //////////////////////////////////////////////////////////////////////
+
+
+
+
+
+#endif //ifndef JSON_AMALGATED_H_INCLUDED
diff --git a/src/json/jsoncpp.cpp b/src/json/jsoncpp.cpp
new file mode 100644
index 000000000..7a04736de
--- /dev/null
+++ b/src/json/jsoncpp.cpp
@@ -0,0 +1,4367 @@
+/// Json-cpp amalgated source (http://jsoncpp.sourceforge.net/).
+/// It is intented to be used with #include <json/json.h>
+
+// //////////////////////////////////////////////////////////////////////
+// Beginning of content of file: LICENSE
+// //////////////////////////////////////////////////////////////////////
+
+/*
+The JsonCpp library's source code, including accompanying documentation,
+tests and demonstration applications, are licensed under the following
+conditions...
+
+The author (Baptiste Lepilleur) explicitly disclaims copyright in all
+jurisdictions which recognize such a disclaimer. In such jurisdictions,
+this software is released into the Public Domain.
+
+In jurisdictions which do not recognize Public Domain property (e.g. Germany as of
+2010), this software is Copyright (c) 2007-2010 by Baptiste Lepilleur, and is
+released under the terms of the MIT License (see below).
+
+In jurisdictions which recognize Public Domain property, the user of this
+software may choose to accept it either as 1) Public Domain, 2) under the
+conditions of the MIT License (see below), or 3) under the terms of dual
+Public Domain/MIT License conditions described here, as they choose.
+
+The MIT License is about as close to Public Domain as a license can get, and is
+described in clear, concise terms at:
+
+ http://en.wikipedia.org/wiki/MIT_License
+
+The full text of the MIT License follows:
+
+========================================================================
+Copyright (c) 2007-2010 Baptiste Lepilleur
+
+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.
+========================================================================
+(END LICENSE TEXT)
+
+The MIT license is compatible with both the GPL and commercial
+software, affording one all of the rights of Public Domain with the
+minor nuisance of being required to keep the above copyright notice
+and license text in the source code. Note also that by accepting the
+Public Domain "license" you can re-license your copy using whatever
+license you like.
+
+*/
+
+// //////////////////////////////////////////////////////////////////////
+// End of content of file: LICENSE
+// //////////////////////////////////////////////////////////////////////
+
+
+
+
+
+
+#include "json.h"
+
+
+// //////////////////////////////////////////////////////////////////////
+// Beginning of content of file: src/lib_json/json_tool.h
+// //////////////////////////////////////////////////////////////////////
+
+// Copyright 2007-2010 Baptiste Lepilleur
+// Distributed under MIT license, or public domain if desired and
+// recognized in your jurisdiction.
+// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
+
+#ifndef LIB_JSONCPP_JSON_TOOL_H_INCLUDED
+# define LIB_JSONCPP_JSON_TOOL_H_INCLUDED
+
+/* This header provides common string manipulation support, such as UTF-8,
+ * portable conversion from/to string...
+ *
+ * It is an internal header that must not be exposed.
+ */
+
+namespace Json {
+
+/// Converts a unicode code-point to UTF-8.
+static inline std::string
+codePointToUTF8(unsigned int cp)
+{
+ std::string result;
+
+ // based on description from http://en.wikipedia.org/wiki/UTF-8
+
+ if (cp <= 0x7f)
+ {
+ result.resize(1);
+ result[0] = static_cast<char>(cp);
+ }
+ else if (cp <= 0x7FF)
+ {
+ result.resize(2);
+ result[1] = static_cast<char>(0x80 | (0x3f & cp));
+ result[0] = static_cast<char>(0xC0 | (0x1f & (cp >> 6)));
+ }
+ else if (cp <= 0xFFFF)
+ {
+ result.resize(3);
+ result[2] = static_cast<char>(0x80 | (0x3f & cp));
+ result[1] = 0x80 | static_cast<char>((0x3f & (cp >> 6)));
+ result[0] = 0xE0 | static_cast<char>((0xf & (cp >> 12)));
+ }
+ else if (cp <= 0x10FFFF)
+ {
+ result.resize(4);
+ result[3] = static_cast<char>(0x80 | (0x3f & cp));
+ result[2] = static_cast<char>(0x80 | (0x3f & (cp >> 6)));
+ result[1] = static_cast<char>(0x80 | (0x3f & (cp >> 12)));
+ result[0] = static_cast<char>(0xF0 | (0x7 & (cp >> 18)));
+ }
+
+ return result;
+}
+
+
+/// Returns true if ch is a control character (in range [0,32[).
+static inline bool
+isControlCharacter(char ch)
+{
+ return ch > 0 && ch <= 0x1F;
+}
+
+
+enum {
+ /// Constant that specify the size of the buffer that must be passed to uintToString.
+ uintToStringBufferSize = 3*sizeof(LargestUInt)+1
+};
+
+// Defines a char buffer for use with uintToString().
+typedef char UIntToStringBuffer[uintToStringBufferSize];
+
+
+/** Converts an unsigned integer to string.
+ * @param value Unsigned interger to convert to string
+ * @param current Input/Output string buffer.
+ * Must have at least uintToStringBufferSize chars free.
+ */
+static inline void
+uintToString( LargestUInt value,
+ char *&current )
+{
+ *--current = 0;
+ do
+ {
+ *--current = char(value % 10) + '0';
+ value /= 10;
+ }
+ while ( value != 0 );
+}
+
+} // namespace Json {
+
+#endif // LIB_JSONCPP_JSON_TOOL_H_INCLUDED
+
+// //////////////////////////////////////////////////////////////////////
+// End of content of file: src/lib_json/json_tool.h
+// //////////////////////////////////////////////////////////////////////
+
+
+
+
+
+
+// //////////////////////////////////////////////////////////////////////
+// Beginning of content of file: src/lib_json/json_reader.cpp
+// //////////////////////////////////////////////////////////////////////
+
+// Copyright 2007-2011 Baptiste Lepilleur
+// Distributed under MIT license, or public domain if desired and
+// recognized in your jurisdiction.
+// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
+
+#if !defined(JSON_IS_AMALGAMATION)
+# include <json/assertions.h>
+# include <json/reader.h>
+# include <json/value.h>
+# include "json_tool.h"
+#endif // if !defined(JSON_IS_AMALGAMATION)
+#include <utility>
+#include <cstdio>
+#include <cassert>
+#include <cstring>
+#include <stdexcept>
+
+#if defined(_MSC_VER) && _MSC_VER >= 1400 // VC++ 8.0
+#pragma warning( disable : 4996 ) // disable warning about strdup being deprecated.
+#endif
+
+namespace Json {
+
+// Implementation of class Features
+// ////////////////////////////////
+
+Features::Features()
+ : allowComments_( true )
+ , strictRoot_( false )
+{
+}
+
+
+Features
+Features::all()
+{
+ return Features();
+}
+
+
+Features
+Features::strictMode()
+{
+ Features features;
+ features.allowComments_ = false;
+ features.strictRoot_ = true;
+ return features;
+}
+
+// Implementation of class Reader
+// ////////////////////////////////
+
+
+static inline bool
+in( Reader::Char c, Reader::Char c1, Reader::Char c2, Reader::Char c3, Reader::Char c4 )
+{
+ return c == c1 || c == c2 || c == c3 || c == c4;
+}
+
+static inline bool
+in( Reader::Char c, Reader::Char c1, Reader::Char c2, Reader::Char c3, Reader::Char c4, Reader::Char c5 )
+{
+ return c == c1 || c == c2 || c == c3 || c == c4 || c == c5;
+}
+
+
+static bool
+containsNewLine( Reader::Location begin,
+ Reader::Location end )
+{
+ for ( ;begin < end; ++begin )
+ if ( *begin == '\n' || *begin == '\r' )
+ return true;
+ return false;
+}
+
+
+// Class Reader
+// //////////////////////////////////////////////////////////////////
+
+Reader::Reader()
+ : errors_(),
+ document_(),
+ begin_(),
+ end_(),
+ current_(),
+ lastValueEnd_(),
+ lastValue_(),
+ commentsBefore_(),
+ features_( Features::all() ),
+ collectComments_()
+{
+}
+
+
+Reader::Reader( const Features &features )
+ : errors_(),
+ document_(),
+ begin_(),
+ end_(),
+ current_(),
+ lastValueEnd_(),
+ lastValue_(),
+ commentsBefore_(),
+ features_( features ),
+ collectComments_()
+{
+}
+
+
+bool
+Reader::parse( const std::string &document,
+ Value &root,
+ bool collectComments )
+{
+ document_ = document;
+ const char *begin = document_.c_str();
+ const char *end = begin + document_.length();
+ return parse( begin, end, root, collectComments );
+}
+
+
+bool
+Reader::parse( std::istream& sin,
+ Value &root,
+ bool collectComments )
+{
+ //std::istream_iterator<char> begin(sin);
+ //std::istream_iterator<char> end;
+ // Those would allow streamed input from a file, if parse() were a
+ // template function.
+
+ // Since std::string is reference-counted, this at least does not
+ // create an extra copy.
+ std::string doc;
+ std::getline(sin, doc, (char)EOF);
+ return parse( doc, root, collectComments );
+}
+
+bool
+Reader::parse( const char *beginDoc, const char *endDoc,
+ Value &root,
+ bool collectComments )
+{
+ if ( !features_.allowComments_ )
+ {
+ collectComments = false;
+ }
+
+ begin_ = beginDoc;
+ end_ = endDoc;
+ collectComments_ = collectComments;
+ current_ = begin_;
+ lastValueEnd_ = 0;
+ lastValue_ = 0;
+ commentsBefore_ = "";
+ errors_.clear();
+ while ( !nodes_.empty() )
+ nodes_.pop();
+ nodes_.push( &root );
+
+ bool successful = readValue();
+ Token token;
+ skipCommentTokens( token );
+ if ( collectComments_ && !commentsBefore_.empty() )
+ root.setComment( commentsBefore_, commentAfter );
+ if ( features_.strictRoot_ )
+ {
+ if ( !root.isArray() && !root.isObject() )
+ {
+ // Set error location to start of doc, ideally should be first token found in doc
+ token.type_ = tokenError;
+ token.start_ = beginDoc;
+ token.end_ = endDoc;
+ addError( "A valid JSON document must be either an array or an object value.",
+ token );
+ return false;
+ }
+ }
+ return successful;
+}
+
+
+bool
+Reader::readValue()
+{
+ Token token;
+ skipCommentTokens( token );
+ bool successful = true;
+
+ if ( collectComments_ && !commentsBefore_.empty() )
+ {
+ currentValue().setComment( commentsBefore_, commentBefore );
+ commentsBefore_ = "";
+ }
+
+
+ switch ( token.type_ )
+ {
+ case tokenObjectBegin:
+ successful = readObject( token );
+ break;
+ case tokenArrayBegin:
+ successful = readArray( token );
+ break;
+ case tokenNumber:
+ successful = decodeNumber( token );
+ break;
+ case tokenString:
+ successful = decodeString( token );
+ break;
+ case tokenTrue:
+ currentValue() = true;
+ break;
+ case tokenFalse:
+ currentValue() = false;
+ break;
+ case tokenNull:
+ currentValue() = Value();
+ break;
+ default:
+ return addError( "Syntax error: value, object or array expected.", token );
+ }
+
+ if ( collectComments_ )
+ {
+ lastValueEnd_ = current_;
+ lastValue_ = &currentValue();
+ }
+
+ return successful;
+}
+
+
+void
+Reader::skipCommentTokens( Token &token )
+{
+ if ( features_.allowComments_ )
+ {
+ do
+ {
+ readToken( token );
+ }
+ while ( token.type_ == tokenComment );
+ }
+ else
+ {
+ readToken( token );
+ }
+}
+
+
+bool
+Reader::expectToken( TokenType type, Token &token, const char *message )
+{
+ readToken( token );
+ if ( token.type_ != type )
+ return addError( message, token );
+ return true;
+}
+
+
+bool
+Reader::readToken( Token &token )
+{
+ skipSpaces();
+ token.start_ = current_;
+ Char c = getNextChar();
+ bool ok = true;
+ switch ( c )
+ {
+ case '{':
+ token.type_ = tokenObjectBegin;
+ break;
+ case '}':
+ token.type_ = tokenObjectEnd;
+ break;
+ case '[':
+ token.type_ = tokenArrayBegin;
+ break;
+ case ']':
+ token.type_ = tokenArrayEnd;
+ break;
+ case '"':
+ token.type_ = tokenString;
+ ok = readString();
+ break;
+ case '/':
+ token.type_ = tokenComment;
+ ok = readComment();
+ break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ case '-':
+ token.type_ = tokenNumber;
+ readNumber();
+ break;
+ case 't':
+ token.type_ = tokenTrue;
+ ok = match( "rue", 3 );
+ break;
+ case 'f':
+ token.type_ = tokenFalse;
+ ok = match( "alse", 4 );
+ break;
+ case 'n':
+ token.type_ = tokenNull;
+ ok = match( "ull", 3 );
+ break;
+ case ',':
+ token.type_ = tokenArraySeparator;
+ break;
+ case ':':
+ token.type_ = tokenMemberSeparator;
+ break;
+ case 0:
+ token.type_ = tokenEndOfStream;
+ break;
+ default:
+ ok = false;
+ break;
+ }
+ if ( !ok )
+ token.type_ = tokenError;
+ token.end_ = current_;
+ return true;
+}
+
+
+void
+Reader::skipSpaces()
+{
+ while ( current_ != end_ )
+ {
+ Char c = *current_;
+ if ( c == ' ' || c == '\t' || c == '\r' || c == '\n' )
+ ++current_;
+ else
+ break;
+ }
+}
+
+
+bool
+Reader::match( Location pattern,
+ int patternLength )
+{
+ if ( end_ - current_ < patternLength )
+ return false;
+ int index = patternLength;
+ while ( index-- )
+ if ( current_[index] != pattern[index] )
+ return false;
+ current_ += patternLength;
+ return true;
+}
+
+
+bool
+Reader::readComment()
+{
+ Location commentBegin = current_ - 1;
+ Char c = getNextChar();
+ bool successful = false;
+ if ( c == '*' )
+ successful = readCStyleComment();
+ else if ( c == '/' )
+ successful = readCppStyleComment();
+ if ( !successful )
+ return false;
+
+ if ( collectComments_ )
+ {
+ CommentPlacement placement = commentBefore;
+ if ( lastValueEnd_ && !containsNewLine( lastValueEnd_, commentBegin ) )
+ {
+ if ( c != '*' || !containsNewLine( commentBegin, current_ ) )
+ placement = commentAfterOnSameLine;
+ }
+
+ addComment( commentBegin, current_, placement );
+ }
+ return true;
+}
+
+
+void
+Reader::addComment( Location begin,
+ Location end,
+ CommentPlacement placement )
+{
+ assert( collectComments_ );
+ if ( placement == commentAfterOnSameLine )
+ {
+ assert( lastValue_ != 0 );
+ lastValue_->setComment( std::string( begin, end ), placement );
+ }
+ else
+ {
+ if ( !commentsBefore_.empty() )
+ commentsBefore_ += "\n";
+ commentsBefore_ += std::string( begin, end );
+ }
+}
+
+
+bool
+Reader::readCStyleComment()
+{
+ while ( current_ != end_ )
+ {
+ Char c = getNextChar();
+ if ( c == '*' && *current_ == '/' )
+ break;
+ }
+ return getNextChar() == '/';
+}
+
+
+bool
+Reader::readCppStyleComment()
+{
+ while ( current_ != end_ )
+ {
+ Char c = getNextChar();
+ if ( c == '\r' || c == '\n' )
+ break;
+ }
+ return true;
+}
+
+
+void
+Reader::readNumber()
+{
+ while ( current_ != end_ )
+ {
+ if ( !(*current_ >= '0' && *current_ <= '9') &&
+ !in( *current_, '.', 'e', 'E', '+', '-' ) )
+ break;
+ ++current_;
+ }
+}
+
+bool
+Reader::readString()
+{
+ Char c = 0;
+ while ( current_ != end_ )
+ {
+ c = getNextChar();
+ if ( c == '\\' )
+ getNextChar();
+ else if ( c == '"' )
+ break;
+ }
+ return c == '"';
+}
+
+
+bool
+Reader::readObject( Token &/*tokenStart*/ )
+{
+ Token tokenName;
+ std::string name;
+ currentValue() = Value( objectValue );
+ while ( readToken( tokenName ) )
+ {
+ bool initialTokenOk = true;
+ while ( tokenName.type_ == tokenComment && initialTokenOk )
+ initialTokenOk = readToken( tokenName );
+ if ( !initialTokenOk )
+ break;
+ if ( tokenName.type_ == tokenObjectEnd && name.empty() ) // empty object
+ return true;
+ if ( tokenName.type_ != tokenString )
+ break;
+
+ name = "";
+ if ( !decodeString( tokenName, name ) )
+ return recoverFromError( tokenObjectEnd );
+
+ Token colon;
+ if ( !readToken( colon ) || colon.type_ != tokenMemberSeparator )
+ {
+ return addErrorAndRecover( "Missing ':' after object member name",
+ colon,
+ tokenObjectEnd );
+ }
+ Value &value = currentValue()[ name ];
+ nodes_.push( &value );
+ bool ok = readValue();
+ nodes_.pop();
+ if ( !ok ) // error already set
+ return recoverFromError( tokenObjectEnd );
+
+ Token comma;
+ if ( !readToken( comma )
+ || ( comma.type_ != tokenObjectEnd &&
+ comma.type_ != tokenArraySeparator &&
+ comma.type_ != tokenComment ) )
+ {
+ return addErrorAndRecover( "Missing ',' or '}' in object declaration",
+ comma,
+ tokenObjectEnd );
+ }
+ bool finalizeTokenOk = true;
+ while ( comma.type_ == tokenComment &&
+ finalizeTokenOk )
+ finalizeTokenOk = readToken( comma );
+ if ( comma.type_ == tokenObjectEnd )
+ return true;
+ }
+ return addErrorAndRecover( "Missing '}' or object member name",
+ tokenName,
+ tokenObjectEnd );
+}
+
+
+bool
+Reader::readArray( Token &/*tokenStart*/ )
+{
+ currentValue() = Value( arrayValue );
+ skipSpaces();
+ if ( *current_ == ']' ) // empty array
+ {
+ Token endArray;
+ readToken( endArray );
+ return true;
+ }
+ int index = 0;
+ for (;;)
+ {
+ Value &value = currentValue()[ index++ ];
+ nodes_.push( &value );
+ bool ok = readValue();
+ nodes_.pop();
+ if ( !ok ) // error already set
+ return recoverFromError( tokenArrayEnd );
+
+ Token token;
+ // Accept Comment after last item in the array.
+ ok = readToken( token );
+ while ( token.type_ == tokenComment && ok )
+ {
+ ok = readToken( token );
+ }
+ bool badTokenType = ( token.type_ != tokenArraySeparator &&
+ token.type_ != tokenArrayEnd );
+ if ( !ok || badTokenType )
+ {
+ return addErrorAndRecover( "Missing ',' or ']' in array declaration",
+ token,
+ tokenArrayEnd );
+ }
+ if ( token.type_ == tokenArrayEnd )
+ break;
+ }
+ return true;
+}
+
+
+bool
+Reader::decodeNumber( Token &token )
+{
+ bool isDouble = false;
+ for ( Location inspect = token.start_; inspect != token.end_; ++inspect )
+ {
+ isDouble = isDouble
+ || in( *inspect, '.', 'e', 'E', '+' )
+ || ( *inspect == '-' && inspect != token.start_ );
+ }
+ if ( isDouble )
+ return decodeDouble( token );
+ // Attempts to parse the number as an integer. If the number is
+ // larger than the maximum supported value of an integer then
+ // we decode the number as a double.
+ Location current = token.start_;
+ bool isNegative = *current == '-';
+ if ( isNegative )
+ ++current;
+ Value::LargestUInt maxIntegerValue = isNegative ? Value::LargestUInt(-Value::minLargestInt)
+ : Value::maxLargestUInt;
+ Value::LargestUInt threshold = maxIntegerValue / 10;
+ Value::LargestUInt value = 0;
+ while ( current < token.end_ )
+ {
+ Char c = *current++;
+ if ( c < '0' || c > '9' )
+ return addError( "'" + std::string( token.start_, token.end_ ) + "' is not a number.", token );
+ Value::UInt digit(c - '0');
+ if ( value >= threshold )
+ {
+ // We've hit or exceeded the max value divided by 10 (rounded down). If
+ // a) we've only just touched the limit, b) this is the last digit, and
+ // c) it's small enough to fit in that rounding delta, we're okay.
+ // Otherwise treat this number as a double to avoid overflow.
+ if (value > threshold ||
+ current != token.end_ ||
+ digit > maxIntegerValue % 10)
+ {
+ return decodeDouble( token );
+ }
+ }
+ value = value * 10 + digit;
+ }
+ if ( isNegative )
+ currentValue() = -Value::LargestInt( value );
+ else if ( value <= Value::LargestUInt(Value::maxInt) )
+ currentValue() = Value::LargestInt( value );
+ else
+ currentValue() = value;
+ return true;
+}
+
+
+bool
+Reader::decodeDouble( Token &token )
+{
+ double value = 0;
+ const int bufferSize = 32;
+ int count;
+ int length = int(token.end_ - token.start_);
+
+ // Sanity check to avoid buffer overflow exploits.
+ if (length < 0) {
+ return addError( "Unable to parse token length", token );
+ }
+
+ // Avoid using a string constant for the format control string given to
+ // sscanf, as this can cause hard to debug crashes on OS X. See here for more
+ // info:
+ //
+ // http://developer.apple.com/library/mac/#DOCUMENTATION/DeveloperTools/gcc-4.0.1/gcc/Incompatibilities.html
+ char format[] = "%lf";
+
+ if ( length <= bufferSize )
+ {
+ Char buffer[bufferSize+1];
+ memcpy( buffer, token.start_, length );
+ buffer[length] = 0;
+ count = sscanf( buffer, format, &value );
+ }
+ else
+ {
+ std::string buffer( token.start_, token.end_ );
+ count = sscanf( buffer.c_str(), format, &value );
+ }
+
+ if ( count != 1 )
+ return addError( "'" + std::string( token.start_, token.end_ ) + "' is not a number.", token );
+ currentValue() = value;
+ return true;
+}
+
+
+bool
+Reader::decodeString( Token &token )
+{
+ std::string decoded;
+ if ( !decodeString( token, decoded ) )
+ return false;
+ currentValue() = decoded;
+ return true;
+}
+
+
+bool
+Reader::decodeString( Token &token, std::string &decoded )
+{
+ decoded.reserve( token.end_ - token.start_ - 2 );
+ Location current = token.start_ + 1; // skip '"'
+ Location end = token.end_ - 1; // do not include '"'
+ while ( current != end )
+ {
+ Char c = *current++;
+ if ( c == '"' )
+ break;
+ else if ( c == '\\' )
+ {
+ if ( current == end )
+ return addError( "Empty escape sequence in string", token, current );
+ Char escape = *current++;
+ switch ( escape )
+ {
+ case '"': decoded += '"'; break;
+ case '/': decoded += '/'; break;
+ case '\\': decoded += '\\'; break;
+ case 'b': decoded += '\b'; break;
+ case 'f': decoded += '\f'; break;
+ case 'n': decoded += '\n'; break;
+ case 'r': decoded += '\r'; break;
+ case 't': decoded += '\t'; break;
+ case 'u':
+ {
+ unsigned int unicode;
+ if ( !decodeUnicodeCodePoint( token, current, end, unicode ) )
+ return false;
+ decoded += codePointToUTF8(unicode);
+ }
+ break;
+ default:
+ return addError( "Bad escape sequence in string", token, current );
+ }
+ }
+ else
+ {
+ decoded += c;
+ }
+ }
+ return true;
+}
+
+bool
+Reader::decodeUnicodeCodePoint( Token &token,
+ Location &current,
+ Location end,
+ unsigned int &unicode )
+{
+
+ if ( !decodeUnicodeEscapeSequence( token, current, end, unicode ) )
+ return false;
+ if (unicode >= 0xD800 && unicode <= 0xDBFF)
+ {
+ // surrogate pairs
+ if (end - current < 6)
+ return addError( "additional six characters expected to parse unicode surrogate pair.", token, current );
+ unsigned int surrogatePair;
+ if (*(current++) == '\\' && *(current++)== 'u')
+ {
+ if (decodeUnicodeEscapeSequence( token, current, end, surrogatePair ))
+ {
+ unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF);
+ }
+ else
+ return false;
+ }
+ else
+ return addError( "expecting another \\u token to begin the second half of a unicode surrogate pair", token, current );
+ }
+ return true;
+}
+
+bool
+Reader::decodeUnicodeEscapeSequence( Token &token,
+ Location &current,
+ Location end,
+ unsigned int &unicode )
+{
+ if ( end - current < 4 )
+ return addError( "Bad unicode escape sequence in string: four digits expected.", token, current );
+ unicode = 0;
+ for ( int index =0; index < 4; ++index )
+ {
+ Char c = *current++;
+ unicode *= 16;
+ if ( c >= '0' && c <= '9' )
+ unicode += c - '0';
+ else if ( c >= 'a' && c <= 'f' )
+ unicode += c - 'a' + 10;
+ else if ( c >= 'A' && c <= 'F' )
+ unicode += c - 'A' + 10;
+ else
+ return addError( "Bad unicode escape sequence in string: hexadecimal digit expected.", token, current );
+ }
+ return true;
+}
+
+
+bool
+Reader::addError( const std::string &message,
+ Token &token,
+ Location extra )
+{
+ ErrorInfo info;
+ info.token_ = token;
+ info.message_ = message;
+ info.extra_ = extra;
+ errors_.push_back( info );
+ return false;
+}
+
+
+bool
+Reader::recoverFromError( TokenType skipUntilToken )
+{
+ int errorCount = int(errors_.size());
+ Token skip;
+ for (;;)
+ {
+ if ( !readToken(skip) )
+ errors_.resize( errorCount ); // discard errors caused by recovery
+ if ( skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream )
+ break;
+ }
+ errors_.resize( errorCount );
+ return false;
+}
+
+
+bool
+Reader::addErrorAndRecover( const std::string &message,
+ Token &token,
+ TokenType skipUntilToken )
+{
+ addError( message, token );
+ return recoverFromError( skipUntilToken );
+}
+
+
+Value &
+Reader::currentValue()
+{
+ return *(nodes_.top());
+}
+
+
+Reader::Char
+Reader::getNextChar()
+{
+ if ( current_ == end_ )
+ return 0;
+ return *current_++;
+}
+
+
+void
+Reader::getLocationLineAndColumn( Location location,
+ int &line,
+ int &column ) const
+{
+ Location current = begin_;
+ Location lastLineStart = current;
+ line = 0;
+ while ( current < location && current != end_ )
+ {
+ Char c = *current++;
+ if ( c == '\r' )
+ {
+ if ( *current == '\n' )
+ ++current;
+ lastLineStart = current;
+ ++line;
+ }
+ else if ( c == '\n' )
+ {
+ lastLineStart = current;
+ ++line;
+ }
+ }
+ // column & line start at 1
+ column = int(location - lastLineStart) + 1;
+ ++line;
+}
+
+
+std::string
+Reader::getLocationLineAndColumn( Location location ) const
+{
+ int line, column;
+ getLocationLineAndColumn( location, line, column );
+ char buffer[18+16+16+1];
+ sprintf( buffer, "Line %d, Column %d", line, column );
+ return buffer;
+}
+
+
+// Deprecated. Preserved for backward compatibility
+std::string
+Reader::getFormatedErrorMessages() const
+{
+ return getFormattedErrorMessages();
+}
+
+
+std::string
+Reader::getFormattedErrorMessages() const
+{
+ std::string formattedMessage;
+ for ( Errors::const_iterator itError = errors_.begin();
+ itError != errors_.end();
+ ++itError )
+ {
+ const ErrorInfo &error = *itError;
+ formattedMessage += "* " + getLocationLineAndColumn( error.token_.start_ ) + "\n";
+ formattedMessage += " " + error.message_ + "\n";
+ if ( error.extra_ )
+ formattedMessage += "See " + getLocationLineAndColumn( error.extra_ ) + " for detail.\n";
+ }
+ return formattedMessage;
+}
+
+
+std::istream& operator>>( std::istream &sin, Value &root )
+{
+ Json::Reader reader;
+ bool ok = reader.parse(sin, root, true);
+ if (!ok) {
+ fprintf(
+ stderr,
+ "Error from reader: %s",
+ reader.getFormattedErrorMessages().c_str());
+
+ JSON_FAIL_MESSAGE("reader error");
+ }
+ return sin;
+}
+
+
+} // namespace Json
+
+// //////////////////////////////////////////////////////////////////////
+// End of content of file: src/lib_json/json_reader.cpp
+// //////////////////////////////////////////////////////////////////////
+
+
+
+
+
+
+// //////////////////////////////////////////////////////////////////////
+// Beginning of content of file: src/lib_json/json_batchallocator.h
+// //////////////////////////////////////////////////////////////////////
+
+// Copyright 2007-2010 Baptiste Lepilleur
+// Distributed under MIT license, or public domain if desired and
+// recognized in your jurisdiction.
+// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
+
+#ifndef JSONCPP_BATCHALLOCATOR_H_INCLUDED
+# define JSONCPP_BATCHALLOCATOR_H_INCLUDED
+
+# include <stdlib.h>
+# include <assert.h>
+
+# ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION
+
+namespace Json {
+
+/* Fast memory allocator.
+ *
+ * This memory allocator allocates memory for a batch of object (specified by
+ * the page size, the number of object in each page).
+ *
+ * It does not allow the destruction of a single object. All the allocated objects
+ * can be destroyed at once. The memory can be either released or reused for future
+ * allocation.
+ *
+ * The in-place new operator must be used to construct the object using the pointer
+ * returned by allocate.
+ */
+template<typename AllocatedType
+ ,const unsigned int objectPerAllocation>
+class BatchAllocator
+{
+public:
+ BatchAllocator( unsigned int objectsPerPage = 255 )
+ : freeHead_( 0 )
+ , objectsPerPage_( objectsPerPage )
+ {
+// printf( "Size: %d => %s\n", sizeof(AllocatedType), typeid(AllocatedType).name() );
+ assert( sizeof(AllocatedType) * objectPerAllocation >= sizeof(AllocatedType *) ); // We must be able to store a slist in the object free space.
+ assert( objectsPerPage >= 16 );
+ batches_ = allocateBatch( 0 ); // allocated a dummy page
+ currentBatch_ = batches_;
+ }
+
+ ~BatchAllocator()
+ {
+ for ( BatchInfo *batch = batches_; batch; )
+ {
+ BatchInfo *nextBatch = batch->next_;
+ free( batch );
+ batch = nextBatch;
+ }
+ }
+
+ /// allocate space for an array of objectPerAllocation object.
+ /// @warning it is the responsability of the caller to call objects constructors.
+ AllocatedType *allocate()
+ {
+ if ( freeHead_ ) // returns node from free list.
+ {
+ AllocatedType *object = freeHead_;
+ freeHead_ = *(AllocatedType **)object;
+ return object;
+ }
+ if ( currentBatch_->used_ == currentBatch_->end_ )
+ {
+ currentBatch_ = currentBatch_->next_;
+ while ( currentBatch_ && currentBatch_->used_ == currentBatch_->end_ )
+ currentBatch_ = currentBatch_->next_;
+
+ if ( !currentBatch_ ) // no free batch found, allocate a new one
+ {
+ currentBatch_ = allocateBatch( objectsPerPage_ );
+ currentBatch_->next_ = batches_; // insert at the head of the list
+ batches_ = currentBatch_;
+ }
+ }
+ AllocatedType *allocated = currentBatch_->used_;
+ currentBatch_->used_ += objectPerAllocation;
+ return allocated;
+ }
+
+ /// Release the object.
+ /// @warning it is the responsability of the caller to actually destruct the object.
+ void release( AllocatedType *object )
+ {
+ assert( object != 0 );
+ *(AllocatedType **)object = freeHead_;
+ freeHead_ = object;
+ }
+
+private:
+ struct BatchInfo
+ {
+ BatchInfo *next_;
+ AllocatedType *used_;
+ AllocatedType *end_;
+ AllocatedType buffer_[objectPerAllocation];
+ };
+
+ // disabled copy constructor and assignement operator.
+ BatchAllocator( const BatchAllocator & );
+ void operator =( const BatchAllocator &);
+
+ static BatchInfo *allocateBatch( unsigned int objectsPerPage )
+ {
+ const unsigned int mallocSize = sizeof(BatchInfo) - sizeof(AllocatedType)* objectPerAllocation
+ + sizeof(AllocatedType) * objectPerAllocation * objectsPerPage;
+ BatchInfo *batch = static_cast<BatchInfo*>( malloc( mallocSize ) );
+ batch->next_ = 0;
+ batch->used_ = batch->buffer_;
+ batch->end_ = batch->buffer_ + objectsPerPage;
+ return batch;
+ }
+
+ BatchInfo *batches_;
+ BatchInfo *currentBatch_;
+ /// Head of a single linked list within the allocated space of freeed object
+ AllocatedType *freeHead_;
+ unsigned int objectsPerPage_;
+};
+
+
+} // namespace Json
+
+# endif // ifndef JSONCPP_DOC_INCLUDE_IMPLEMENTATION
+
+#endif // JSONCPP_BATCHALLOCATOR_H_INCLUDED
+
+// //////////////////////////////////////////////////////////////////////
+// End of content of file: src/lib_json/json_batchallocator.h
+// //////////////////////////////////////////////////////////////////////
+
+
+
+
+
+
+// //////////////////////////////////////////////////////////////////////
+// Beginning of content of file: src/lib_json/json_valueiterator.inl
+// //////////////////////////////////////////////////////////////////////
+
+// Copyright 2007-2010 Baptiste Lepilleur
+// Distributed under MIT license, or public domain if desired and
+// recognized in your jurisdiction.
+// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
+
+// included by json_value.cpp
+
+namespace Json {
+
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// class ValueIteratorBase
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+
+ValueIteratorBase::ValueIteratorBase()
+#ifndef JSON_VALUE_USE_INTERNAL_MAP
+ : current_()
+ , isNull_( true )
+{
+}
+#else
+ : isArray_( true )
+ , isNull_( true )
+{
+ iterator_.array_ = ValueInternalArray::IteratorState();
+}
+#endif
+
+
+#ifndef JSON_VALUE_USE_INTERNAL_MAP
+ValueIteratorBase::ValueIteratorBase( const Value::ObjectValues::iterator &current )
+ : current_( current )
+ , isNull_( false )
+{
+}
+#else
+ValueIteratorBase::ValueIteratorBase( const ValueInternalArray::IteratorState &state )
+ : isArray_( true )
+{
+ iterator_.array_ = state;
+}
+
+
+ValueIteratorBase::ValueIteratorBase( const ValueInternalMap::IteratorState &state )
+ : isArray_( false )
+{
+ iterator_.map_ = state;
+}
+#endif
+
+Value &
+ValueIteratorBase::deref() const
+{
+#ifndef JSON_VALUE_USE_INTERNAL_MAP
+ return current_->second;
+#else
+ if ( isArray_ )
+ return ValueInternalArray::dereference( iterator_.array_ );
+ return ValueInternalMap::value( iterator_.map_ );
+#endif
+}
+
+
+void
+ValueIteratorBase::increment()
+{
+#ifndef JSON_VALUE_USE_INTERNAL_MAP
+ ++current_;
+#else
+ if ( isArray_ )
+ ValueInternalArray::increment( iterator_.array_ );
+ ValueInternalMap::increment( iterator_.map_ );
+#endif
+}
+
+
+void
+ValueIteratorBase::decrement()
+{
+#ifndef JSON_VALUE_USE_INTERNAL_MAP
+ --current_;
+#else
+ if ( isArray_ )
+ ValueInternalArray::decrement( iterator_.array_ );
+ ValueInternalMap::decrement( iterator_.map_ );
+#endif
+}
+
+
+ValueIteratorBase::difference_type
+ValueIteratorBase::computeDistance( const SelfType &other ) const
+{
+#ifndef JSON_VALUE_USE_INTERNAL_MAP
+# ifdef JSON_USE_CPPTL_SMALLMAP
+ return current_ - other.current_;
+# else
+ // Iterator for null value are initialized using the default
+ // constructor, which initialize current_ to the default
+ // std::map::iterator. As begin() and end() are two instance
+ // of the default std::map::iterator, they can not be compared.
+ // To allow this, we handle this comparison specifically.
+ if ( isNull_ && other.isNull_ )
+ {
+ return 0;
+ }
+
+
+ // Usage of std::distance is not portable (does not compile with Sun Studio 12 RogueWave STL,
+ // which is the one used by default).
+ // Using a portable hand-made version for non random iterator instead:
+ // return difference_type( std::distance( current_, other.current_ ) );
+ difference_type myDistance = 0;
+ for ( Value::ObjectValues::iterator it = current_; it != other.current_; ++it )
+ {
+ ++myDistance;
+ }
+ return myDistance;
+# endif
+#else
+ if ( isArray_ )
+ return ValueInternalArray::distance( iterator_.array_, other.iterator_.array_ );
+ return ValueInternalMap::distance( iterator_.map_, other.iterator_.map_ );
+#endif
+}
+
+
+bool
+ValueIteratorBase::isEqual( const SelfType &other ) const
+{
+#ifndef JSON_VALUE_USE_INTERNAL_MAP
+ if ( isNull_ )
+ {
+ return other.isNull_;
+ }
+ return current_ == other.current_;
+#else
+ if ( isArray_ )
+ return ValueInternalArray::equals( iterator_.array_, other.iterator_.array_ );
+ return ValueInternalMap::equals( iterator_.map_, other.iterator_.map_ );
+#endif
+}
+
+
+void
+ValueIteratorBase::copy( const SelfType &other )
+{
+#ifndef JSON_VALUE_USE_INTERNAL_MAP
+ current_ = other.current_;
+#else
+ if ( isArray_ )
+ iterator_.array_ = other.iterator_.array_;
+ iterator_.map_ = other.iterator_.map_;
+#endif
+}
+
+
+Value
+ValueIteratorBase::key() const
+{
+#ifndef JSON_VALUE_USE_INTERNAL_MAP
+ const Value::CZString czstring = (*current_).first;
+ if ( czstring.c_str() )
+ {
+ if ( czstring.isStaticString() )
+ return Value( StaticString( czstring.c_str() ) );
+ return Value( czstring.c_str() );
+ }
+ return Value( czstring.index() );
+#else
+ if ( isArray_ )
+ return Value( ValueInternalArray::indexOf( iterator_.array_ ) );
+ bool isStatic;
+ const char *memberName = ValueInternalMap::key( iterator_.map_, isStatic );
+ if ( isStatic )
+ return Value( StaticString( memberName ) );
+ return Value( memberName );
+#endif
+}
+
+
+UInt
+ValueIteratorBase::index() const
+{
+#ifndef JSON_VALUE_USE_INTERNAL_MAP
+ const Value::CZString czstring = (*current_).first;
+ if ( !czstring.c_str() )
+ return czstring.index();
+ return Value::UInt( -1 );
+#else
+ if ( isArray_ )
+ return Value::UInt( ValueInternalArray::indexOf( iterator_.array_ ) );
+ return Value::UInt( -1 );
+#endif
+}
+
+
+const char *
+ValueIteratorBase::memberName() const
+{
+#ifndef JSON_VALUE_USE_INTERNAL_MAP
+ const char *name = (*current_).first.c_str();
+ return name ? name : "";
+#else
+ if ( !isArray_ )
+ return ValueInternalMap::key( iterator_.map_ );
+ return "";
+#endif
+}
+
+
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// class ValueConstIterator
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+
+ValueConstIterator::ValueConstIterator()
+{
+}
+
+
+#ifndef JSON_VALUE_USE_INTERNAL_MAP
+ValueConstIterator::ValueConstIterator( const Value::ObjectValues::iterator &current )
+ : ValueIteratorBase( current )
+{
+}
+#else
+ValueConstIterator::ValueConstIterator( const ValueInternalArray::IteratorState &state )
+ : ValueIteratorBase( state )
+{
+}
+
+ValueConstIterator::ValueConstIterator( const ValueInternalMap::IteratorState &state )
+ : ValueIteratorBase( state )
+{
+}
+#endif
+
+ValueConstIterator &
+ValueConstIterator::operator =( const ValueIteratorBase &other )
+{
+ copy( other );
+ return *this;
+}
+
+
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// class ValueIterator
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+
+ValueIterator::ValueIterator()
+{
+}
+
+
+#ifndef JSON_VALUE_USE_INTERNAL_MAP
+ValueIterator::ValueIterator( const Value::ObjectValues::iterator &current )
+ : ValueIteratorBase( current )
+{
+}
+#else
+ValueIterator::ValueIterator( const ValueInternalArray::IteratorState &state )
+ : ValueIteratorBase( state )
+{
+}
+
+ValueIterator::ValueIterator( const ValueInternalMap::IteratorState &state )
+ : ValueIteratorBase( state )
+{
+}
+#endif
+
+ValueIterator::ValueIterator( const ValueConstIterator &other )
+ : ValueIteratorBase( other )
+{
+}
+
+ValueIterator::ValueIterator( const ValueIterator &other )
+ : ValueIteratorBase( other )
+{
+}
+
+ValueIterator &
+ValueIterator::operator =( const SelfType &other )
+{
+ copy( other );
+ return *this;
+}
+
+} // namespace Json
+
+// //////////////////////////////////////////////////////////////////////
+// End of content of file: src/lib_json/json_valueiterator.inl
+// //////////////////////////////////////////////////////////////////////
+
+
+
+
+
+
+// //////////////////////////////////////////////////////////////////////
+// Beginning of content of file: src/lib_json/json_value.cpp
+// //////////////////////////////////////////////////////////////////////
+
+// Copyright 2011 Baptiste Lepilleur
+// Distributed under MIT license, or public domain if desired and
+// recognized in your jurisdiction.
+// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
+
+#if !defined(JSON_IS_AMALGAMATION)
+# include <json/assertions.h>
+# include <json/value.h>
+# include <json/writer.h>
+# ifndef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR
+# include "json_batchallocator.h"
+# endif // #ifndef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR
+#endif // if !defined(JSON_IS_AMALGAMATION)
+#include <math.h>
+#include <sstream>
+#include <utility>
+#include <stdexcept>
+#include <cstring>
+#include <cassert>
+#ifdef JSON_USE_CPPTL
+# include <cpptl/conststring.h>
+#endif
+#include <cstddef> // size_t
+
+#define JSON_ASSERT_UNREACHABLE assert( false )
+
+namespace Json {
+
+const Value Value::null;
+const Int Value::minInt = Int( ~(UInt(-1)/2) );
+const Int Value::maxInt = Int( UInt(-1)/2 );
+const UInt Value::maxUInt = UInt(-1);
+# if defined(JSON_HAS_INT64)
+const Int64 Value::minInt64 = Int64( ~(UInt64(-1)/2) );
+const Int64 Value::maxInt64 = Int64( UInt64(-1)/2 );
+const UInt64 Value::maxUInt64 = UInt64(-1);
+// The constant is hard-coded because some compiler have trouble
+// converting Value::maxUInt64 to a double correctly (AIX/xlC).
+// Assumes that UInt64 is a 64 bits integer.
+static const double maxUInt64AsDouble = 18446744073709551615.0;
+#endif // defined(JSON_HAS_INT64)
+const LargestInt Value::minLargestInt = LargestInt( ~(LargestUInt(-1)/2) );
+const LargestInt Value::maxLargestInt = LargestInt( LargestUInt(-1)/2 );
+const LargestUInt Value::maxLargestUInt = LargestUInt(-1);
+
+
+/// Unknown size marker
+static const unsigned int unknown = (unsigned)-1;
+
+#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
+template <typename T, typename U>
+static inline bool InRange(double d, T min, U max) {
+ return d >= min && d <= max;
+}
+#else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
+static inline double integerToDouble( Json::UInt64 value )
+{
+ return static_cast<double>( Int64(value/2) ) * 2.0 + Int64(value & 1);
+}
+
+template<typename T>
+static inline double integerToDouble( T value )
+{
+ return static_cast<double>( value );
+}
+
+template <typename T, typename U>
+static inline bool InRange(double d, T min, U max) {
+ return d >= integerToDouble(min) && d <= integerToDouble(max);
+}
+#endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
+
+
+/** Duplicates the specified string value.
+ * @param value Pointer to the string to duplicate. Must be zero-terminated if
+ * length is "unknown".
+ * @param length Length of the value. if equals to unknown, then it will be
+ * computed using strlen(value).
+ * @return Pointer on the duplicate instance of string.
+ */
+static inline char *
+duplicateStringValue( const char *value,
+ unsigned int length = unknown )
+{
+ if ( length == unknown )
+ length = (unsigned int)strlen(value);
+
+ // Avoid an integer overflow in the call to malloc below by limiting length
+ // to a sane value.
+ if (length >= (unsigned)Value::maxInt)
+ length = Value::maxInt - 1;
+
+ char *newString = static_cast<char *>( malloc( length + 1 ) );
+ JSON_ASSERT_MESSAGE( newString != 0, "Failed to allocate string value buffer" );
+ memcpy( newString, value, length );
+ newString[length] = 0;
+ return newString;
+}
+
+
+/** Free the string duplicated by duplicateStringValue().
+ */
+static inline void
+releaseStringValue( char *value )
+{
+ if ( value )
+ free( value );
+}
+
+} // namespace Json
+
+
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// ValueInternals...
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+#if !defined(JSON_IS_AMALGAMATION)
+# ifdef JSON_VALUE_USE_INTERNAL_MAP
+# include "json_internalarray.inl"
+# include "json_internalmap.inl"
+# endif // JSON_VALUE_USE_INTERNAL_MAP
+
+# include "json_valueiterator.inl"
+#endif // if !defined(JSON_IS_AMALGAMATION)
+
+namespace Json {
+
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// class Value::CommentInfo
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+
+
+Value::CommentInfo::CommentInfo()
+ : comment_( 0 )
+{
+}
+
+Value::CommentInfo::~CommentInfo()
+{
+ if ( comment_ )
+ releaseStringValue( comment_ );
+}
+
+
+void
+Value::CommentInfo::setComment( const char *text )
+{
+ if ( comment_ )
+ releaseStringValue( comment_ );
+ JSON_ASSERT( text != 0 );
+ JSON_ASSERT_MESSAGE( text[0]=='\0' || text[0]=='/', "Comments must start with /");
+ // It seems that /**/ style comments are acceptable as well.
+ comment_ = duplicateStringValue( text );
+}
+
+
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// class Value::CZString
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+# ifndef JSON_VALUE_USE_INTERNAL_MAP
+
+// Notes: index_ indicates if the string was allocated when
+// a string is stored.
+
+Value::CZString::CZString( ArrayIndex index )
+ : cstr_( 0 )
+ , index_( index )
+{
+}
+
+Value::CZString::CZString( const char *cstr, DuplicationPolicy allocate )
+ : cstr_( allocate == duplicate ? duplicateStringValue(cstr)
+ : cstr )
+ , index_( allocate )
+{
+}
+
+Value::CZString::CZString( const CZString &other )
+: cstr_( other.index_ != noDuplication && other.cstr_ != 0
+ ? duplicateStringValue( other.cstr_ )
+ : other.cstr_ )
+ , index_( other.cstr_ ? (other.index_ == noDuplication ? noDuplication : duplicate)
+ : other.index_ )
+{
+}
+
+Value::CZString::~CZString()
+{
+ if ( cstr_ && index_ == duplicate )
+ releaseStringValue( const_cast<char *>( cstr_ ) );
+}
+
+void
+Value::CZString::swap( CZString &other )
+{
+ std::swap( cstr_, other.cstr_ );
+ std::swap( index_, other.index_ );
+}
+
+Value::CZString &
+Value::CZString::operator =( const CZString &other )
+{
+ CZString temp( other );
+ swap( temp );
+ return *this;
+}
+
+bool
+Value::CZString::operator<( const CZString &other ) const
+{
+ if ( cstr_ )
+ return strcmp( cstr_, other.cstr_ ) < 0;
+ return index_ < other.index_;
+}
+
+bool
+Value::CZString::operator==( const CZString &other ) const
+{
+ if ( cstr_ )
+ return strcmp( cstr_, other.cstr_ ) == 0;
+ return index_ == other.index_;
+}
+
+
+ArrayIndex
+Value::CZString::index() const
+{
+ return index_;
+}
+
+
+const char *
+Value::CZString::c_str() const
+{
+ return cstr_;
+}
+
+bool
+Value::CZString::isStaticString() const
+{
+ return index_ == noDuplication;
+}
+
+#endif // ifndef JSON_VALUE_USE_INTERNAL_MAP
+
+
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// class Value::Value
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+
+/*! \internal Default constructor initialization must be equivalent to:
+ * memset( this, 0, sizeof(Value) )
+ * This optimization is used in ValueInternalMap fast allocator.
+ */
+Value::Value( ValueType type )
+ : type_( type )
+ , allocated_( false )
+# ifdef JSON_VALUE_USE_INTERNAL_MAP
+ , itemIsUsed_( 0 )
+#endif
+ , comments_( 0 )
+{
+ switch ( type )
+ {
+ case nullValue:
+ break;
+ case intValue:
+ case uintValue:
+ value_.int_ = 0;
+ break;
+ case realValue:
+ value_.real_ = 0.0;
+ break;
+ case stringValue:
+ value_.string_ = 0;
+ break;
+#ifndef JSON_VALUE_USE_INTERNAL_MAP
+ case arrayValue:
+ case objectValue:
+ value_.map_ = new ObjectValues();
+ break;
+#else
+ case arrayValue:
+ value_.array_ = arrayAllocator()->newArray();
+ break;
+ case objectValue:
+ value_.map_ = mapAllocator()->newMap();
+ break;
+#endif
+ case booleanValue:
+ value_.bool_ = false;
+ break;
+ default:
+ JSON_ASSERT_UNREACHABLE;
+ }
+}
+
+
+Value::Value( UInt value )
+ : type_( uintValue )
+ , allocated_( false )
+# ifdef JSON_VALUE_USE_INTERNAL_MAP
+ , itemIsUsed_( 0 )
+#endif
+ , comments_( 0 )
+{
+ value_.uint_ = value;
+}
+
+Value::Value( Int value )
+ : type_( intValue )
+ , allocated_( false )
+# ifdef JSON_VALUE_USE_INTERNAL_MAP
+ , itemIsUsed_( 0 )
+#endif
+ , comments_( 0 )
+{
+ value_.int_ = value;
+}
+
+
+# if defined(JSON_HAS_INT64)
+Value::Value( Int64 value )
+ : type_( intValue )
+ , allocated_( false )
+# ifdef JSON_VALUE_USE_INTERNAL_MAP
+ , itemIsUsed_( 0 )
+#endif
+ , comments_( 0 )
+{
+ value_.int_ = value;
+}
+
+
+Value::Value( UInt64 value )
+ : type_( uintValue )
+ , allocated_( false )
+# ifdef JSON_VALUE_USE_INTERNAL_MAP
+ , itemIsUsed_( 0 )
+#endif
+ , comments_( 0 )
+{
+ value_.uint_ = value;
+}
+#endif // defined(JSON_HAS_INT64)
+
+Value::Value( double value )
+ : type_( realValue )
+ , allocated_( false )
+# ifdef JSON_VALUE_USE_INTERNAL_MAP
+ , itemIsUsed_( 0 )
+#endif
+ , comments_( 0 )
+{
+ value_.real_ = value;
+}
+
+Value::Value( const char *value )
+ : type_( stringValue )
+ , allocated_( true )
+# ifdef JSON_VALUE_USE_INTERNAL_MAP
+ , itemIsUsed_( 0 )
+#endif
+ , comments_( 0 )
+{
+ value_.string_ = duplicateStringValue( value );
+}
+
+
+Value::Value( const char *beginValue,
+ const char *endValue )
+ : type_( stringValue )
+ , allocated_( true )
+# ifdef JSON_VALUE_USE_INTERNAL_MAP
+ , itemIsUsed_( 0 )
+#endif
+ , comments_( 0 )
+{
+ value_.string_ = duplicateStringValue( beginValue,
+ (unsigned int)(endValue - beginValue) );
+}
+
+
+Value::Value( const std::string &value )
+ : type_( stringValue )
+ , allocated_( true )
+# ifdef JSON_VALUE_USE_INTERNAL_MAP
+ , itemIsUsed_( 0 )
+#endif
+ , comments_( 0 )
+{
+ value_.string_ = duplicateStringValue( value.c_str(),
+ (unsigned int)value.length() );
+
+}
+
+Value::Value( const StaticString &value )
+ : type_( stringValue )
+ , allocated_( false )
+# ifdef JSON_VALUE_USE_INTERNAL_MAP
+ , itemIsUsed_( 0 )
+#endif
+ , comments_( 0 )
+{
+ value_.string_ = const_cast<char *>( value.c_str() );
+}
+
+
+# ifdef JSON_USE_CPPTL
+Value::Value( const CppTL::ConstString &value )
+ : type_( stringValue )
+ , allocated_( true )
+# ifdef JSON_VALUE_USE_INTERNAL_MAP
+ , itemIsUsed_( 0 )
+#endif
+ , comments_( 0 )
+{
+ value_.string_ = duplicateStringValue( value, value.length() );
+}
+# endif
+
+Value::Value( bool value )
+ : type_( booleanValue )
+ , allocated_( false )
+# ifdef JSON_VALUE_USE_INTERNAL_MAP
+ , itemIsUsed_( 0 )
+#endif
+ , comments_( 0 )
+{
+ value_.bool_ = value;
+}
+
+
+Value::Value( const Value &other )
+ : type_( other.type_ )
+ , allocated_( false )
+# ifdef JSON_VALUE_USE_INTERNAL_MAP
+ , itemIsUsed_( 0 )
+#endif
+ , comments_( 0 )
+{
+ switch ( type_ )
+ {
+ case nullValue:
+ case intValue:
+ case uintValue:
+ case realValue:
+ case booleanValue:
+ value_ = other.value_;
+ break;
+ case stringValue:
+ if ( other.value_.string_ )
+ {
+ value_.string_ = duplicateStringValue( other.value_.string_ );
+ allocated_ = true;
+ }
+ else
+ value_.string_ = 0;
+ break;
+#ifndef JSON_VALUE_USE_INTERNAL_MAP
+ case arrayValue:
+ case objectValue:
+ value_.map_ = new ObjectValues( *other.value_.map_ );
+ break;
+#else
+ case arrayValue:
+ value_.array_ = arrayAllocator()->newArrayCopy( *other.value_.array_ );
+ break;
+ case objectValue:
+ value_.map_ = mapAllocator()->newMapCopy( *other.value_.map_ );
+ break;
+#endif
+ default:
+ JSON_ASSERT_UNREACHABLE;
+ }
+ if ( other.comments_ )
+ {
+ comments_ = new CommentInfo[numberOfCommentPlacement];
+ for ( int comment =0; comment < numberOfCommentPlacement; ++comment )
+ {
+ const CommentInfo &otherComment = other.comments_[comment];
+ if ( otherComment.comment_ )
+ comments_[comment].setComment( otherComment.comment_ );
+ }
+ }
+}
+
+
+Value::~Value()
+{
+ switch ( type_ )
+ {
+ case nullValue:
+ case intValue:
+ case uintValue:
+ case realValue:
+ case booleanValue:
+ break;
+ case stringValue:
+ if ( allocated_ )
+ releaseStringValue( value_.string_ );
+ break;
+#ifndef JSON_VALUE_USE_INTERNAL_MAP
+ case arrayValue:
+ case objectValue:
+ delete value_.map_;
+ break;
+#else
+ case arrayValue:
+ arrayAllocator()->destructArray( value_.array_ );
+ break;
+ case objectValue:
+ mapAllocator()->destructMap( value_.map_ );
+ break;
+#endif
+ default:
+ JSON_ASSERT_UNREACHABLE;
+ }
+
+ if ( comments_ )
+ delete[] comments_;
+}
+
+Value &
+Value::operator=( const Value &other )
+{
+ Value temp( other );
+ swap( temp );
+ return *this;
+}
+
+void
+Value::swap( Value &other )
+{
+ ValueType temp = type_;
+ type_ = other.type_;
+ other.type_ = temp;
+ std::swap( value_, other.value_ );
+ int temp2 = allocated_;
+ allocated_ = other.allocated_;
+ other.allocated_ = temp2;
+}
+
+ValueType
+Value::type() const
+{
+ return type_;
+}
+
+
+int
+Value::compare( const Value &other ) const
+{
+ if ( *this < other )
+ return -1;
+ if ( *this > other )
+ return 1;
+ return 0;
+}
+
+
+bool
+Value::operator <( const Value &other ) const
+{
+ int typeDelta = type_ - other.type_;
+ if ( typeDelta )
+ return typeDelta < 0 ? true : false;
+ switch ( type_ )
+ {
+ case nullValue:
+ return false;
+ case intValue:
+ return value_.int_ < other.value_.int_;
+ case uintValue:
+ return value_.uint_ < other.value_.uint_;
+ case realValue:
+ return value_.real_ < other.value_.real_;
+ case booleanValue:
+ return value_.bool_ < other.value_.bool_;
+ case stringValue:
+ return ( value_.string_ == 0 && other.value_.string_ )
+ || ( other.value_.string_
+ && value_.string_
+ && strcmp( value_.string_, other.value_.string_ ) < 0 );
+#ifndef JSON_VALUE_USE_INTERNAL_MAP
+ case arrayValue:
+ case objectValue:
+ {
+ int delta = int( value_.map_->size() - other.value_.map_->size() );
+ if ( delta )
+ return delta < 0;
+ return (*value_.map_) < (*other.value_.map_);
+ }
+#else
+ case arrayValue:
+ return value_.array_->compare( *(other.value_.array_) ) < 0;
+ case objectValue:
+ return value_.map_->compare( *(other.value_.map_) ) < 0;
+#endif
+ default:
+ JSON_ASSERT_UNREACHABLE;
+ }
+ return false; // unreachable
+}
+
+bool
+Value::operator <=( const Value &other ) const
+{
+ return !(other < *this);
+}
+
+bool
+Value::operator >=( const Value &other ) const
+{
+ return !(*this < other);
+}
+
+bool
+Value::operator >( const Value &other ) const
+{
+ return other < *this;
+}
+
+bool
+Value::operator ==( const Value &other ) const
+{
+ //if ( type_ != other.type_ )
+ // GCC 2.95.3 says:
+ // attempt to take address of bit-field structure member `Json::Value::type_'
+ // Beats me, but a temp solves the problem.
+ int temp = other.type_;
+ if ( type_ != temp )
+ return false;
+ switch ( type_ )
+ {
+ case nullValue:
+ return true;
+ case intValue:
+ return value_.int_ == other.value_.int_;
+ case uintValue:
+ return value_.uint_ == other.value_.uint_;
+ case realValue:
+ return value_.real_ == other.value_.real_;
+ case booleanValue:
+ return value_.bool_ == other.value_.bool_;
+ case stringValue:
+ return ( value_.string_ == other.value_.string_ )
+ || ( other.value_.string_
+ && value_.string_
+ && strcmp( value_.string_, other.value_.string_ ) == 0 );
+#ifndef JSON_VALUE_USE_INTERNAL_MAP
+ case arrayValue:
+ case objectValue:
+ return value_.map_->size() == other.value_.map_->size()
+ && (*value_.map_) == (*other.value_.map_);
+#else
+ case arrayValue:
+ return value_.array_->compare( *(other.value_.array_) ) == 0;
+ case objectValue:
+ return value_.map_->compare( *(other.value_.map_) ) == 0;
+#endif
+ default:
+ JSON_ASSERT_UNREACHABLE;
+ }
+ return false; // unreachable
+}
+
+bool
+Value::operator !=( const Value &other ) const
+{
+ return !( *this == other );
+}
+
+const char *
+Value::asCString() const
+{
+ JSON_ASSERT( type_ == stringValue );
+ return value_.string_;
+}
+
+
+std::string
+Value::asString() const
+{
+ switch ( type_ )
+ {
+ case nullValue:
+ return "";
+ case stringValue:
+ return value_.string_ ? value_.string_ : "";
+ case booleanValue:
+ return value_.bool_ ? "true" : "false";
+ case intValue:
+ return valueToString( value_.int_ );
+ case uintValue:
+ return valueToString( value_.uint_ );
+ case realValue:
+ return valueToString( value_.real_ );
+ default:
+ JSON_FAIL_MESSAGE( "Type is not convertible to string" );
+ }
+}
+
+# ifdef JSON_USE_CPPTL
+CppTL::ConstString
+Value::asConstString() const
+{
+ return CppTL::ConstString( asString().c_str() );
+}
+# endif
+
+
+Value::Int
+Value::asInt() const
+{
+ switch ( type_ )
+ {
+ case intValue:
+ JSON_ASSERT_MESSAGE(isInt(), "LargestInt out of Int range");
+ return Int(value_.int_);
+ case uintValue:
+ JSON_ASSERT_MESSAGE(isInt(), "LargestUInt out of Int range");
+ return Int(value_.uint_);
+ case realValue:
+ JSON_ASSERT_MESSAGE(InRange(value_.real_, minInt, maxInt), "double out of Int range");
+ return Int(value_.real_);
+ case nullValue:
+ return 0;
+ case booleanValue:
+ return value_.bool_ ? 1 : 0;
+ default:
+ break;
+ }
+ JSON_FAIL_MESSAGE("Value is not convertible to Int.");
+}
+
+
+Value::UInt
+Value::asUInt() const
+{
+ switch ( type_ )
+ {
+ case intValue:
+ JSON_ASSERT_MESSAGE(isUInt(), "LargestInt out of UInt range");
+ return UInt(value_.int_);
+ case uintValue:
+ JSON_ASSERT_MESSAGE(isUInt(), "LargestUInt out of UInt range");
+ return UInt(value_.uint_);
+ case realValue:
+ JSON_ASSERT_MESSAGE(InRange(value_.real_, 0, maxUInt), "double out of UInt range");
+ return UInt( value_.real_ );
+ case nullValue:
+ return 0;
+ case booleanValue:
+ return value_.bool_ ? 1 : 0;
+ default:
+ break;
+ }
+ JSON_FAIL_MESSAGE("Value is not convertible to UInt.");
+}
+
+
+# if defined(JSON_HAS_INT64)
+
+Value::Int64
+Value::asInt64() const
+{
+ switch ( type_ )
+ {
+ case intValue:
+ return Int64(value_.int_);
+ case uintValue:
+ JSON_ASSERT_MESSAGE(isInt64(), "LargestUInt out of Int64 range");
+ return Int64(value_.uint_);
+ case realValue:
+ JSON_ASSERT_MESSAGE(InRange(value_.real_, minInt64, maxInt64), "double out of Int64 range");
+ return Int64(value_.real_);
+ case nullValue:
+ return 0;
+ case booleanValue:
+ return value_.bool_ ? 1 : 0;
+ default:
+ break;
+ }
+ JSON_FAIL_MESSAGE("Value is not convertible to Int64.");
+}
+
+
+Value::UInt64
+Value::asUInt64() const
+{
+ switch ( type_ )
+ {
+ case intValue:
+ JSON_ASSERT_MESSAGE(isUInt64(), "LargestInt out of UInt64 range");
+ return UInt64(value_.int_);
+ case uintValue:
+ return UInt64(value_.uint_);
+ case realValue:
+ JSON_ASSERT_MESSAGE(InRange(value_.real_, 0, maxUInt64), "double out of UInt64 range");
+ return UInt64( value_.real_ );
+ case nullValue:
+ return 0;
+ case booleanValue:
+ return value_.bool_ ? 1 : 0;
+ default:
+ break;
+ }
+ JSON_FAIL_MESSAGE("Value is not convertible to UInt64.");
+}
+# endif // if defined(JSON_HAS_INT64)
+
+
+LargestInt
+Value::asLargestInt() const
+{
+#if defined(JSON_NO_INT64)
+ return asInt();
+#else
+ return asInt64();
+#endif
+}
+
+
+LargestUInt
+Value::asLargestUInt() const
+{
+#if defined(JSON_NO_INT64)
+ return asUInt();
+#else
+ return asUInt64();
+#endif
+}
+
+
+double
+Value::asDouble() const
+{
+ switch ( type_ )
+ {
+ case intValue:
+ return static_cast<double>( value_.int_ );
+ case uintValue:
+#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
+ return static_cast<double>( value_.uint_ );
+#else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
+ return integerToDouble( value_.uint_ );
+#endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
+ case realValue:
+ return value_.real_;
+ case nullValue:
+ return 0.0;
+ case booleanValue:
+ return value_.bool_ ? 1.0 : 0.0;
+ default:
+ break;
+ }
+ JSON_FAIL_MESSAGE("Value is not convertible to double.");
+}
+
+float
+Value::asFloat() const
+{
+ switch ( type_ )
+ {
+ case intValue:
+ return static_cast<float>( value_.int_ );
+ case uintValue:
+#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
+ return static_cast<float>( value_.uint_ );
+#else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
+ return integerToDouble( value_.uint_ );
+#endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
+ case realValue:
+ return static_cast<float>( value_.real_ );
+ case nullValue:
+ return 0.0;
+ case booleanValue:
+ return value_.bool_ ? 1.0f : 0.0f;
+ default:
+ break;
+ }
+ JSON_FAIL_MESSAGE("Value is not convertible to float.");
+}
+
+bool
+Value::asBool() const
+{
+ switch ( type_ )
+ {
+ case booleanValue:
+ return value_.bool_;
+ case nullValue:
+ return false;
+ case intValue:
+ return value_.int_ ? true : false;
+ case uintValue:
+ return value_.uint_ ? true : false;
+ case realValue:
+ return value_.real_ ? true : false;
+ default:
+ break;
+ }
+ JSON_FAIL_MESSAGE("Value is not convertible to bool.");
+}
+
+
+bool
+Value::isConvertibleTo( ValueType other ) const
+{
+ switch ( other )
+ {
+ case nullValue:
+ return ( isNumeric() && asDouble() == 0.0 )
+ || ( type_ == booleanValue && value_.bool_ == false )
+ || ( type_ == stringValue && asString() == "" )
+ || ( type_ == arrayValue && value_.map_->size() == 0 )
+ || ( type_ == objectValue && value_.map_->size() == 0 )
+ || type_ == nullValue;
+ case intValue:
+ return isInt()
+ || (type_ == realValue && InRange(value_.real_, minInt, maxInt))
+ || type_ == booleanValue
+ || type_ == nullValue;
+ case uintValue:
+ return isUInt()
+ || (type_ == realValue && InRange(value_.real_, 0, maxUInt))
+ || type_ == booleanValue
+ || type_ == nullValue;
+ case realValue:
+ return isNumeric()
+ || type_ == booleanValue
+ || type_ == nullValue;
+ case booleanValue:
+ return isNumeric()
+ || type_ == booleanValue
+ || type_ == nullValue;
+ case stringValue:
+ return isNumeric()
+ || type_ == booleanValue
+ || type_ == stringValue
+ || type_ == nullValue;
+ case arrayValue:
+ return type_ == arrayValue
+ || type_ == nullValue;
+ case objectValue:
+ return type_ == objectValue
+ || type_ == nullValue;
+ }
+ JSON_ASSERT_UNREACHABLE;
+ return false;
+}
+
+
+/// Number of values in array or object
+ArrayIndex
+Value::size() const
+{
+ switch ( type_ )
+ {
+ case nullValue:
+ case intValue:
+ case uintValue:
+ case realValue:
+ case booleanValue:
+ case stringValue:
+ return 0;
+#ifndef JSON_VALUE_USE_INTERNAL_MAP
+ case arrayValue: // size of the array is highest index + 1
+ if ( !value_.map_->empty() )
+ {
+ ObjectValues::const_iterator itLast = value_.map_->end();
+ --itLast;
+ return (*itLast).first.index()+1;
+ }
+ return 0;
+ case objectValue:
+ return ArrayIndex( value_.map_->size() );
+#else
+ case arrayValue:
+ return Int( value_.array_->size() );
+ case objectValue:
+ return Int( value_.map_->size() );
+#endif
+ }
+ JSON_ASSERT_UNREACHABLE;
+ return 0; // unreachable;
+}
+
+
+bool
+Value::empty() const
+{
+ if ( isNull() || isArray() || isObject() )
+ return size() == 0u;
+ else
+ return false;
+}
+
+
+bool
+Value::operator!() const
+{
+ return isNull();
+}
+
+
+void
+Value::clear()
+{
+ JSON_ASSERT( type_ == nullValue || type_ == arrayValue || type_ == objectValue );
+
+ switch ( type_ )
+ {
+#ifndef JSON_VALUE_USE_INTERNAL_MAP
+ case arrayValue:
+ case objectValue:
+ value_.map_->clear();
+ break;
+#else
+ case arrayValue:
+ value_.array_->clear();
+ break;
+ case objectValue:
+ value_.map_->clear();
+ break;
+#endif
+ default:
+ break;
+ }
+}
+
+void
+Value::resize( ArrayIndex newSize )
+{
+ JSON_ASSERT( type_ == nullValue || type_ == arrayValue );
+ if ( type_ == nullValue )
+ *this = Value( arrayValue );
+#ifndef JSON_VALUE_USE_INTERNAL_MAP
+ ArrayIndex oldSize = size();
+ if ( newSize == 0 )
+ clear();
+ else if ( newSize > oldSize )
+ (*this)[ newSize - 1 ];
+ else
+ {
+ for ( ArrayIndex index = newSize; index < oldSize; ++index )
+ {
+ value_.map_->erase( index );
+ }
+ assert( size() == newSize );
+ }
+#else
+ value_.array_->resize( newSize );
+#endif
+}
+
+
+Value &
+Value::operator[]( ArrayIndex index )
+{
+ JSON_ASSERT( type_ == nullValue || type_ == arrayValue );
+ if ( type_ == nullValue )
+ *this = Value( arrayValue );
+#ifndef JSON_VALUE_USE_INTERNAL_MAP
+ CZString key( index );
+ ObjectValues::iterator it = value_.map_->lower_bound( key );
+ if ( it != value_.map_->end() && (*it).first == key )
+ return (*it).second;
+
+ ObjectValues::value_type defaultValue( key, null );
+ it = value_.map_->insert( it, defaultValue );
+ return (*it).second;
+#else
+ return value_.array_->resolveReference( index );
+#endif
+}
+
+
+Value &
+Value::operator[]( int index )
+{
+ JSON_ASSERT( index >= 0 );
+ return (*this)[ ArrayIndex(index) ];
+}
+
+
+const Value &
+Value::operator[]( ArrayIndex index ) const
+{
+ JSON_ASSERT( type_ == nullValue || type_ == arrayValue );
+ if ( type_ == nullValue )
+ return null;
+#ifndef JSON_VALUE_USE_INTERNAL_MAP
+ CZString key( index );
+ ObjectValues::const_iterator it = value_.map_->find( key );
+ if ( it == value_.map_->end() )
+ return null;
+ return (*it).second;
+#else
+ Value *value = value_.array_->find( index );
+ return value ? *value : null;
+#endif
+}
+
+
+const Value &
+Value::operator[]( int index ) const
+{
+ JSON_ASSERT( index >= 0 );
+ return (*this)[ ArrayIndex(index) ];
+}
+
+
+Value &
+Value::operator[]( const char *key )
+{
+ return resolveReference( key, false );
+}
+
+
+Value &
+Value::resolveReference( const char *key,
+ bool isStatic )
+{
+ JSON_ASSERT( type_ == nullValue || type_ == objectValue );
+ if ( type_ == nullValue )
+ *this = Value( objectValue );
+#ifndef JSON_VALUE_USE_INTERNAL_MAP
+ CZString actualKey( key, isStatic ? CZString::noDuplication
+ : CZString::duplicateOnCopy );
+ ObjectValues::iterator it = value_.map_->lower_bound( actualKey );
+ if ( it != value_.map_->end() && (*it).first == actualKey )
+ return (*it).second;
+
+ ObjectValues::value_type defaultValue( actualKey, null );
+ it = value_.map_->insert( it, defaultValue );
+ Value &value = (*it).second;
+ return value;
+#else
+ return value_.map_->resolveReference( key, isStatic );
+#endif
+}
+
+
+Value
+Value::get( ArrayIndex index,
+ const Value &defaultValue ) const
+{
+ const Value *value = &((*this)[index]);
+ return value == &null ? defaultValue : *value;
+}
+
+
+bool
+Value::isValidIndex( ArrayIndex index ) const
+{
+ return index < size();
+}
+
+
+
+const Value &
+Value::operator[]( const char *key ) const
+{
+ JSON_ASSERT( type_ == nullValue || type_ == objectValue );
+ if ( type_ == nullValue )
+ return null;
+#ifndef JSON_VALUE_USE_INTERNAL_MAP
+ CZString actualKey( key, CZString::noDuplication );
+ ObjectValues::const_iterator it = value_.map_->find( actualKey );
+ if ( it == value_.map_->end() )
+ return null;
+ return (*it).second;
+#else
+ const Value *value = value_.map_->find( key );
+ return value ? *value : null;
+#endif
+}
+
+
+Value &
+Value::operator[]( const std::string &key )
+{
+ return (*this)[ key.c_str() ];
+}
+
+
+const Value &
+Value::operator[]( const std::string &key ) const
+{
+ return (*this)[ key.c_str() ];
+}
+
+Value &
+Value::operator[]( const StaticString &key )
+{
+ return resolveReference( key, true );
+}
+
+
+# ifdef JSON_USE_CPPTL
+Value &
+Value::operator[]( const CppTL::ConstString &key )
+{
+ return (*this)[ key.c_str() ];
+}
+
+
+const Value &
+Value::operator[]( const CppTL::ConstString &key ) const
+{
+ return (*this)[ key.c_str() ];
+}
+# endif
+
+
+Value &
+Value::append( const Value &value )
+{
+ return (*this)[size()] = value;
+}
+
+
+Value
+Value::get( const char *key,
+ const Value &defaultValue ) const
+{
+ const Value *value = &((*this)[key]);
+ return value == &null ? defaultValue : *value;
+}
+
+
+Value
+Value::get( const std::string &key,
+ const Value &defaultValue ) const
+{
+ return get( key.c_str(), defaultValue );
+}
+
+Value
+Value::removeMember( const char* key )
+{
+ JSON_ASSERT( type_ == nullValue || type_ == objectValue );
+ if ( type_ == nullValue )
+ return null;
+#ifndef JSON_VALUE_USE_INTERNAL_MAP
+ CZString actualKey( key, CZString::noDuplication );
+ ObjectValues::iterator it = value_.map_->find( actualKey );
+ if ( it == value_.map_->end() )
+ return null;
+ Value old(it->second);
+ value_.map_->erase(it);
+ return old;
+#else
+ Value *value = value_.map_->find( key );
+ if (value){
+ Value old(*value);
+ value_.map_.remove( key );
+ return old;
+ } else {
+ return null;
+ }
+#endif
+}
+
+Value
+Value::removeMember( const std::string &key )
+{
+ return removeMember( key.c_str() );
+}
+
+# ifdef JSON_USE_CPPTL
+Value
+Value::get( const CppTL::ConstString &key,
+ const Value &defaultValue ) const
+{
+ return get( key.c_str(), defaultValue );
+}
+# endif
+
+bool
+Value::isMember( const char *key ) const
+{
+ const Value *value = &((*this)[key]);
+ return value != &null;
+}
+
+
+bool
+Value::isMember( const std::string &key ) const
+{
+ return isMember( key.c_str() );
+}
+
+
+# ifdef JSON_USE_CPPTL
+bool
+Value::isMember( const CppTL::ConstString &key ) const
+{
+ return isMember( key.c_str() );
+}
+#endif
+
+Value::Members
+Value::getMemberNames() const
+{
+ JSON_ASSERT( type_ == nullValue || type_ == objectValue );
+ if ( type_ == nullValue )
+ return Value::Members();
+ Members members;
+ members.reserve( value_.map_->size() );
+#ifndef JSON_VALUE_USE_INTERNAL_MAP
+ ObjectValues::const_iterator it = value_.map_->begin();
+ ObjectValues::const_iterator itEnd = value_.map_->end();
+ for ( ; it != itEnd; ++it )
+ members.push_back( std::string( (*it).first.c_str() ) );
+#else
+ ValueInternalMap::IteratorState it;
+ ValueInternalMap::IteratorState itEnd;
+ value_.map_->makeBeginIterator( it );
+ value_.map_->makeEndIterator( itEnd );
+ for ( ; !ValueInternalMap::equals( it, itEnd ); ValueInternalMap::increment(it) )
+ members.push_back( std::string( ValueInternalMap::key( it ) ) );
+#endif
+ return members;
+}
+//
+//# ifdef JSON_USE_CPPTL
+//EnumMemberNames
+//Value::enumMemberNames() const
+//{
+// if ( type_ == objectValue )
+// {
+// return CppTL::Enum::any( CppTL::Enum::transform(
+// CppTL::Enum::keys( *(value_.map_), CppTL::Type<const CZString &>() ),
+// MemberNamesTransform() ) );
+// }
+// return EnumMemberNames();
+//}
+//
+//
+//EnumValues
+//Value::enumValues() const
+//{
+// if ( type_ == objectValue || type_ == arrayValue )
+// return CppTL::Enum::anyValues( *(value_.map_),
+// CppTL::Type<const Value &>() );
+// return EnumValues();
+//}
+//
+//# endif
+
+static bool IsIntegral(double d) {
+ double integral_part;
+ return modf(d, &integral_part) == 0.0;
+}
+
+
+bool
+Value::isNull() const
+{
+ return type_ == nullValue;
+}
+
+
+bool
+Value::isBool() const
+{
+ return type_ == booleanValue;
+}
+
+
+bool
+Value::isInt() const
+{
+ switch ( type_ )
+ {
+ case intValue:
+ return value_.int_ >= minInt && value_.int_ <= maxInt;
+ case uintValue:
+ return value_.uint_ <= UInt(maxInt);
+ case realValue:
+ return value_.real_ >= minInt &&
+ value_.real_ <= maxInt &&
+ IsIntegral(value_.real_);
+ default:
+ break;
+ }
+ return false;
+}
+
+
+bool
+Value::isUInt() const
+{
+ switch ( type_ )
+ {
+ case intValue:
+ return value_.int_ >= 0 && LargestUInt(value_.int_) <= LargestUInt(maxUInt);
+ case uintValue:
+ return value_.uint_ <= maxUInt;
+ case realValue:
+ return value_.real_ >= 0 &&
+ value_.real_ <= maxUInt &&
+ IsIntegral(value_.real_);
+ default:
+ break;
+ }
+ return false;
+}
+
+bool
+Value::isInt64() const
+{
+# if defined(JSON_HAS_INT64)
+ switch ( type_ )
+ {
+ case intValue:
+ return true;
+ case uintValue:
+ return value_.uint_ <= UInt64(maxInt64);
+ case realValue:
+ // Note that maxInt64 (= 2^63 - 1) is not exactly representable as a
+ // double, so double(maxInt64) will be rounded up to 2^63. Therefore we
+ // require the value to be strictly less than the limit.
+ return value_.real_ >= double(minInt64) &&
+ value_.real_ < double(maxInt64) &&
+ IsIntegral(value_.real_);
+ default:
+ break;
+ }
+# endif // JSON_HAS_INT64
+ return false;
+}
+
+bool
+Value::isUInt64() const
+{
+# if defined(JSON_HAS_INT64)
+ switch ( type_ )
+ {
+ case intValue:
+ return value_.int_ >= 0;
+ case uintValue:
+ return true;
+ case realValue:
+ // Note that maxUInt64 (= 2^64 - 1) is not exactly representable as a
+ // double, so double(maxUInt64) will be rounded up to 2^64. Therefore we
+ // require the value to be strictly less than the limit.
+ return value_.real_ >= 0 &&
+ value_.real_ < maxUInt64AsDouble &&
+ IsIntegral(value_.real_);
+ default:
+ break;
+ }
+# endif // JSON_HAS_INT64
+ return false;
+}
+
+
+bool
+Value::isIntegral() const
+{
+#if defined(JSON_HAS_INT64)
+ return isInt64() || isUInt64();
+#else
+ return isInt() || isUInt();
+#endif
+}
+
+
+bool
+Value::isDouble() const
+{
+ return type_ == realValue || isIntegral();
+}
+
+
+bool
+Value::isNumeric() const
+{
+ return isIntegral() || isDouble();
+}
+
+
+bool
+Value::isString() const
+{
+ return type_ == stringValue;
+}
+
+
+bool
+Value::isArray() const
+{
+ return type_ == arrayValue;
+}
+
+
+bool
+Value::isObject() const
+{
+ return type_ == objectValue;
+}
+
+
+void
+Value::setComment( const char *comment,
+ CommentPlacement placement )
+{
+ if ( !comments_ )
+ comments_ = new CommentInfo[numberOfCommentPlacement];
+ comments_[placement].setComment( comment );
+}
+
+
+void
+Value::setComment( const std::string &comment,
+ CommentPlacement placement )
+{
+ setComment( comment.c_str(), placement );
+}
+
+
+bool
+Value::hasComment( CommentPlacement placement ) const
+{
+ return comments_ != 0 && comments_[placement].comment_ != 0;
+}
+
+std::string
+Value::getComment( CommentPlacement placement ) const
+{
+ if ( hasComment(placement) )
+ return comments_[placement].comment_;
+ return "";
+}
+
+
+std::string
+Value::toStyledString() const
+{
+ StyledWriter writer;
+ return writer.write( *this );
+}
+
+
+Value::const_iterator
+Value::begin() const
+{
+ switch ( type_ )
+ {
+#ifdef JSON_VALUE_USE_INTERNAL_MAP
+ case arrayValue:
+ if ( value_.array_ )
+ {
+ ValueInternalArray::IteratorState it;
+ value_.array_->makeBeginIterator( it );
+ return const_iterator( it );
+ }
+ break;
+ case objectValue:
+ if ( value_.map_ )
+ {
+ ValueInternalMap::IteratorState it;
+ value_.map_->makeBeginIterator( it );
+ return const_iterator( it );
+ }
+ break;
+#else
+ case arrayValue:
+ case objectValue:
+ if ( value_.map_ )
+ return const_iterator( value_.map_->begin() );
+ break;
+#endif
+ default:
+ break;
+ }
+ return const_iterator();
+}
+
+Value::const_iterator
+Value::end() const
+{
+ switch ( type_ )
+ {
+#ifdef JSON_VALUE_USE_INTERNAL_MAP
+ case arrayValue:
+ if ( value_.array_ )
+ {
+ ValueInternalArray::IteratorState it;
+ value_.array_->makeEndIterator( it );
+ return const_iterator( it );
+ }
+ break;
+ case objectValue:
+ if ( value_.map_ )
+ {
+ ValueInternalMap::IteratorState it;
+ value_.map_->makeEndIterator( it );
+ return const_iterator( it );
+ }
+ break;
+#else
+ case arrayValue:
+ case objectValue:
+ if ( value_.map_ )
+ return const_iterator( value_.map_->end() );
+ break;
+#endif
+ default:
+ break;
+ }
+ return const_iterator();
+}
+
+
+Value::iterator
+Value::begin()
+{
+ switch ( type_ )
+ {
+#ifdef JSON_VALUE_USE_INTERNAL_MAP
+ case arrayValue:
+ if ( value_.array_ )
+ {
+ ValueInternalArray::IteratorState it;
+ value_.array_->makeBeginIterator( it );
+ return iterator( it );
+ }
+ break;
+ case objectValue:
+ if ( value_.map_ )
+ {
+ ValueInternalMap::IteratorState it;
+ value_.map_->makeBeginIterator( it );
+ return iterator( it );
+ }
+ break;
+#else
+ case arrayValue:
+ case objectValue:
+ if ( value_.map_ )
+ return iterator( value_.map_->begin() );
+ break;
+#endif
+ default:
+ break;
+ }
+ return iterator();
+}
+
+Value::iterator
+Value::end()
+{
+ switch ( type_ )
+ {
+#ifdef JSON_VALUE_USE_INTERNAL_MAP
+ case arrayValue:
+ if ( value_.array_ )
+ {
+ ValueInternalArray::IteratorState it;
+ value_.array_->makeEndIterator( it );
+ return iterator( it );
+ }
+ break;
+ case objectValue:
+ if ( value_.map_ )
+ {
+ ValueInternalMap::IteratorState it;
+ value_.map_->makeEndIterator( it );
+ return iterator( it );
+ }
+ break;
+#else
+ case arrayValue:
+ case objectValue:
+ if ( value_.map_ )
+ return iterator( value_.map_->end() );
+ break;
+#endif
+ default:
+ break;
+ }
+ return iterator();
+}
+
+
+// class PathArgument
+// //////////////////////////////////////////////////////////////////
+
+PathArgument::PathArgument()
+ : key_()
+ , index_()
+ , kind_( kindNone )
+{
+}
+
+
+PathArgument::PathArgument( ArrayIndex index )
+ : key_()
+ , index_( index )
+ , kind_( kindIndex )
+{
+}
+
+
+PathArgument::PathArgument( const char *key )
+ : key_( key )
+ , index_()
+ , kind_( kindKey )
+{
+}
+
+
+PathArgument::PathArgument( const std::string &key )
+ : key_( key.c_str() )
+ , index_()
+ , kind_( kindKey )
+{
+}
+
+// class Path
+// //////////////////////////////////////////////////////////////////
+
+Path::Path( const std::string &path,
+ const PathArgument &a1,
+ const PathArgument &a2,
+ const PathArgument &a3,
+ const PathArgument &a4,
+ const PathArgument &a5 )
+{
+ InArgs in;
+ in.push_back( &a1 );
+ in.push_back( &a2 );
+ in.push_back( &a3 );
+ in.push_back( &a4 );
+ in.push_back( &a5 );
+ makePath( path, in );
+}
+
+
+void
+Path::makePath( const std::string &path,
+ const InArgs &in )
+{
+ const char *current = path.c_str();
+ const char *end = current + path.length();
+ InArgs::const_iterator itInArg = in.begin();
+ while ( current != end )
+ {
+ if ( *current == '[' )
+ {
+ ++current;
+ if ( *current == '%' )
+ addPathInArg( path, in, itInArg, PathArgument::kindIndex );
+ else
+ {
+ ArrayIndex index = 0;
+ for ( ; current != end && *current >= '0' && *current <= '9'; ++current )
+ index = index * 10 + ArrayIndex(*current - '0');
+ args_.push_back( index );
+ }
+ if ( current == end || *current++ != ']' )
+ invalidPath( path, int(current - path.c_str()) );
+ }
+ else if ( *current == '%' )
+ {
+ addPathInArg( path, in, itInArg, PathArgument::kindKey );
+ ++current;
+ }
+ else if ( *current == '.' )
+ {
+ ++current;
+ }
+ else
+ {
+ const char *beginName = current;
+ while ( current != end && !strchr( "[.", *current ) )
+ ++current;
+ args_.push_back( std::string( beginName, current ) );
+ }
+ }
+}
+
+
+void
+Path::addPathInArg( const std::string &path,
+ const InArgs &in,
+ InArgs::const_iterator &itInArg,
+ PathArgument::Kind kind )
+{
+ if ( itInArg == in.end() )
+ {
+ // Error: missing argument %d
+ }
+ else if ( (*itInArg)->kind_ != kind )
+ {
+ // Error: bad argument type
+ }
+ else
+ {
+ args_.push_back( **itInArg );
+ }
+}
+
+
+void
+Path::invalidPath( const std::string &path,
+ int location )
+{
+ // Error: invalid path.
+}
+
+
+const Value &
+Path::resolve( const Value &root ) const
+{
+ const Value *node = &root;
+ for ( Args::const_iterator it = args_.begin(); it != args_.end(); ++it )
+ {
+ const PathArgument &arg = *it;
+ if ( arg.kind_ == PathArgument::kindIndex )
+ {
+ if ( !node->isArray() || !node->isValidIndex( arg.index_ ) )
+ {
+ // Error: unable to resolve path (array value expected at position...
+ }
+ node = &((*node)[arg.index_]);
+ }
+ else if ( arg.kind_ == PathArgument::kindKey )
+ {
+ if ( !node->isObject() )
+ {
+ // Error: unable to resolve path (object value expected at position...)
+ }
+ node = &((*node)[arg.key_]);
+ if ( node == &Value::null )
+ {
+ // Error: unable to resolve path (object has no member named '' at position...)
+ }
+ }
+ }
+ return *node;
+}
+
+
+Value
+Path::resolve( const Value &root,
+ const Value &defaultValue ) const
+{
+ const Value *node = &root;
+ for ( Args::const_iterator it = args_.begin(); it != args_.end(); ++it )
+ {
+ const PathArgument &arg = *it;
+ if ( arg.kind_ == PathArgument::kindIndex )
+ {
+ if ( !node->isArray() || !node->isValidIndex( arg.index_ ) )
+ return defaultValue;
+ node = &((*node)[arg.index_]);
+ }
+ else if ( arg.kind_ == PathArgument::kindKey )
+ {
+ if ( !node->isObject() )
+ return defaultValue;
+ node = &((*node)[arg.key_]);
+ if ( node == &Value::null )
+ return defaultValue;
+ }
+ }
+ return *node;
+}
+
+
+Value &
+Path::make( Value &root ) const
+{
+ Value *node = &root;
+ for ( Args::const_iterator it = args_.begin(); it != args_.end(); ++it )
+ {
+ const PathArgument &arg = *it;
+ if ( arg.kind_ == PathArgument::kindIndex )
+ {
+ if ( !node->isArray() )
+ {
+ // Error: node is not an array at position ...
+ }
+ node = &((*node)[arg.index_]);
+ }
+ else if ( arg.kind_ == PathArgument::kindKey )
+ {
+ if ( !node->isObject() )
+ {
+ // Error: node is not an object at position...
+ }
+ node = &((*node)[arg.key_]);
+ }
+ }
+ return *node;
+}
+
+
+} // namespace Json
+
+// //////////////////////////////////////////////////////////////////////
+// End of content of file: src/lib_json/json_value.cpp
+// //////////////////////////////////////////////////////////////////////
+
+
+
+
+
+
+// //////////////////////////////////////////////////////////////////////
+// Beginning of content of file: src/lib_json/json_writer.cpp
+// //////////////////////////////////////////////////////////////////////
+
+// Copyright 2011 Baptiste Lepilleur
+// Distributed under MIT license, or public domain if desired and
+// recognized in your jurisdiction.
+// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
+
+#if !defined(JSON_IS_AMALGAMATION)
+# include <json/writer.h>
+# include "json_tool.h"
+#endif // if !defined(JSON_IS_AMALGAMATION)
+#include <utility>
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+#include <sstream>
+#include <iomanip>
+
+#if defined(_MSC_VER) && _MSC_VER >= 1400 // VC++ 8.0
+#pragma warning( disable : 4996 ) // disable warning about strdup being deprecated.
+#endif
+
+namespace Json {
+
+static bool containsControlCharacter( const char* str )
+{
+ while ( *str )
+ {
+ if ( isControlCharacter( *(str++) ) )
+ return true;
+ }
+ return false;
+}
+
+
+std::string valueToString( LargestInt value )
+{
+ UIntToStringBuffer buffer;
+ char *current = buffer + sizeof(buffer);
+ bool isNegative = value < 0;
+ if ( isNegative )
+ value = -value;
+ uintToString( LargestUInt(value), current );
+ if ( isNegative )
+ *--current = '-';
+ assert( current >= buffer );
+ return current;
+}
+
+
+std::string valueToString( LargestUInt value )
+{
+ UIntToStringBuffer buffer;
+ char *current = buffer + sizeof(buffer);
+ uintToString( value, current );
+ assert( current >= buffer );
+ return current;
+}
+
+#if defined(JSON_HAS_INT64)
+
+std::string valueToString( Int value )
+{
+ return valueToString( LargestInt(value) );
+}
+
+
+std::string valueToString( UInt value )
+{
+ return valueToString( LargestUInt(value) );
+}
+
+#endif // # if defined(JSON_HAS_INT64)
+
+
+std::string valueToString( double value )
+{
+ char buffer[32];
+#if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__) // Use secure version with visual studio 2005 to avoid warning.
+ sprintf_s(buffer, sizeof(buffer), "%#.16g", value);
+#else
+ sprintf(buffer, "%#.16g", value);
+#endif
+ char* ch = buffer + strlen(buffer) - 1;
+ if (*ch != '0') return buffer; // nothing to truncate, so save time
+ while(ch > buffer && *ch == '0'){
+ --ch;
+ }
+ char* last_nonzero = ch;
+ while(ch >= buffer){
+ switch(*ch){
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ --ch;
+ continue;
+ case '.':
+ // Truncate zeroes to save bytes in output, but keep one.
+ *(last_nonzero+2) = '\0';
+ return buffer;
+ default:
+ return buffer;
+ }
+ }
+ return buffer;
+}
+
+
+std::string valueToString( bool value )
+{
+ return value ? "true" : "false";
+}
+
+std::string valueToQuotedString( const char *value )
+{
+ if (value == NULL)
+ return "";
+ // Not sure how to handle unicode...
+ if (strpbrk(value, "\"\\\b\f\n\r\t") == NULL && !containsControlCharacter( value ))
+ return std::string("\"") + value + "\"";
+ // We have to walk value and escape any special characters.
+ // Appending to std::string is not efficient, but this should be rare.
+ // (Note: forward slashes are *not* rare, but I am not escaping them.)
+ std::string::size_type maxsize = strlen(value)*2 + 3; // allescaped+quotes+NULL
+ std::string result;
+ result.reserve(maxsize); // to avoid lots of mallocs
+ result += "\"";
+ for (const char* c=value; *c != 0; ++c)
+ {
+ switch(*c)
+ {
+ case '\"':
+ result += "\\\"";
+ break;
+ case '\\':
+ result += "\\\\";
+ break;
+ case '\b':
+ result += "\\b";
+ break;
+ case '\f':
+ result += "\\f";
+ break;
+ case '\n':
+ result += "\\n";
+ break;
+ case '\r':
+ result += "\\r";
+ break;
+ case '\t':
+ result += "\\t";
+ break;
+ //case '/':
+ // Even though \/ is considered a legal escape in JSON, a bare
+ // slash is also legal, so I see no reason to escape it.
+ // (I hope I am not misunderstanding something.
+ // blep notes: actually escaping \/ may be useful in javascript to avoid </
+ // sequence.
+ // Should add a flag to allow this compatibility mode and prevent this
+ // sequence from occurring.
+ default:
+ if ( isControlCharacter( *c ) )
+ {
+ std::ostringstream oss;
+ oss << "\\u" << std::hex << std::uppercase << std::setfill('0') << std::setw(4) << static_cast<int>(*c);
+ result += oss.str();
+ }
+ else
+ {
+ result += *c;
+ }
+ break;
+ }
+ }
+ result += "\"";
+ return result;
+}
+
+// Class Writer
+// //////////////////////////////////////////////////////////////////
+Writer::~Writer()
+{
+}
+
+
+// Class FastWriter
+// //////////////////////////////////////////////////////////////////
+
+FastWriter::FastWriter()
+ : yamlCompatiblityEnabled_( false ),
+ dropNullPlaceholders_( false )
+{
+}
+
+
+void
+FastWriter::enableYAMLCompatibility()
+{
+ yamlCompatiblityEnabled_ = true;
+}
+
+
+void
+FastWriter::dropNullPlaceholders()
+{
+ dropNullPlaceholders_ = true;
+}
+
+
+std::string
+FastWriter::write( const Value &root )
+{
+ document_ = "";
+ writeValue( root );
+ document_ += "\n";
+ return document_;
+}
+
+
+void
+FastWriter::writeValue( const Value &value )
+{
+ switch ( value.type() )
+ {
+ case nullValue:
+ if (!dropNullPlaceholders_) document_ += "null";
+ break;
+ case intValue:
+ document_ += valueToString( value.asLargestInt() );
+ break;
+ case uintValue:
+ document_ += valueToString( value.asLargestUInt() );
+ break;
+ case realValue:
+ document_ += valueToString( value.asDouble() );
+ break;
+ case stringValue:
+ document_ += valueToQuotedString( value.asCString() );
+ break;
+ case booleanValue:
+ document_ += valueToString( value.asBool() );
+ break;
+ case arrayValue:
+ {
+ document_ += "[";
+ int size = value.size();
+ for ( int index =0; index < size; ++index )
+ {
+ if ( index > 0 )
+ document_ += ",";
+ writeValue( value[index] );
+ }
+ document_ += "]";
+ }
+ break;
+ case objectValue:
+ {
+ Value::Members members( value.getMemberNames() );
+ document_ += "{";
+ for ( Value::Members::iterator it = members.begin();
+ it != members.end();
+ ++it )
+ {
+ const std::string &name = *it;
+ if ( it != members.begin() )
+ document_ += ",";
+ document_ += valueToQuotedString( name.c_str() );
+ document_ += yamlCompatiblityEnabled_ ? ": "
+ : ":";
+ writeValue( value[name] );
+ }
+ document_ += "}";
+ }
+ break;
+ }
+}
+
+
+// Class StyledWriter
+// //////////////////////////////////////////////////////////////////
+
+StyledWriter::StyledWriter()
+ : rightMargin_( 74 )
+ , indentSize_( 3 )
+ , addChildValues_()
+{
+}
+
+
+std::string
+StyledWriter::write( const Value &root )
+{
+ document_ = "";
+ addChildValues_ = false;
+ indentString_ = "";
+ writeCommentBeforeValue( root );
+ writeValue( root );
+ writeCommentAfterValueOnSameLine( root );
+ document_ += "\n";
+ return document_;
+}
+
+
+void
+StyledWriter::writeValue( const Value &value )
+{
+ switch ( value.type() )
+ {
+ case nullValue:
+ pushValue( "null" );
+ break;
+ case intValue:
+ pushValue( valueToString( value.asLargestInt() ) );
+ break;
+ case uintValue:
+ pushValue( valueToString( value.asLargestUInt() ) );
+ break;
+ case realValue:
+ pushValue( valueToString( value.asDouble() ) );
+ break;
+ case stringValue:
+ pushValue( valueToQuotedString( value.asCString() ) );
+ break;
+ case booleanValue:
+ pushValue( valueToString( value.asBool() ) );
+ break;
+ case arrayValue:
+ writeArrayValue( value);
+ break;
+ case objectValue:
+ {
+ Value::Members members( value.getMemberNames() );
+ if ( members.empty() )
+ pushValue( "{}" );
+ else
+ {
+ writeWithIndent( "{" );
+ indent();
+ Value::Members::iterator it = members.begin();
+ for (;;)
+ {
+ const std::string &name = *it;
+ const Value &childValue = value[name];
+ writeCommentBeforeValue( childValue );
+ writeWithIndent( valueToQuotedString( name.c_str() ) );
+ document_ += " : ";
+ writeValue( childValue );
+ if ( ++it == members.end() )
+ {
+ writeCommentAfterValueOnSameLine( childValue );
+ break;
+ }
+ document_ += ",";
+ writeCommentAfterValueOnSameLine( childValue );
+ }
+ unindent();
+ writeWithIndent( "}" );
+ }
+ }
+ break;
+ }
+}
+
+
+void
+StyledWriter::writeArrayValue( const Value &value )
+{
+ unsigned size = value.size();
+ if ( size == 0 )
+ pushValue( "[]" );
+ else
+ {
+ bool isArrayMultiLine = isMultineArray( value );
+ if ( isArrayMultiLine )
+ {
+ writeWithIndent( "[" );
+ indent();
+ bool hasChildValue = !childValues_.empty();
+ unsigned index =0;
+ for (;;)
+ {
+ const Value &childValue = value[index];
+ writeCommentBeforeValue( childValue );
+ if ( hasChildValue )
+ writeWithIndent( childValues_[index] );
+ else
+ {
+ writeIndent();
+ writeValue( childValue );
+ }
+ if ( ++index == size )
+ {
+ writeCommentAfterValueOnSameLine( childValue );
+ break;
+ }
+ document_ += ",";
+ writeCommentAfterValueOnSameLine( childValue );
+ }
+ unindent();
+ writeWithIndent( "]" );
+ }
+ else // output on a single line
+ {
+ assert( childValues_.size() == size );
+ document_ += "[ ";
+ for ( unsigned index =0; index < size; ++index )
+ {
+ if ( index > 0 )
+ document_ += ", ";
+ document_ += childValues_[index];
+ }
+ document_ += " ]";
+ }
+ }
+}
+
+
+bool
+StyledWriter::isMultineArray( const Value &value )
+{
+ int size = value.size();
+ bool isMultiLine = size*3 >= rightMargin_ ;
+ childValues_.clear();
+ for ( int index =0; index < size && !isMultiLine; ++index )
+ {
+ const Value &childValue = value[index];
+ isMultiLine = isMultiLine ||
+ ( (childValue.isArray() || childValue.isObject()) &&
+ childValue.size() > 0 );
+ }
+ if ( !isMultiLine ) // check if line length > max line length
+ {
+ childValues_.reserve( size );
+ addChildValues_ = true;
+ int lineLength = 4 + (size-1)*2; // '[ ' + ', '*n + ' ]'
+ for ( int index =0; index < size && !isMultiLine; ++index )
+ {
+ writeValue( value[index] );
+ lineLength += int( childValues_[index].length() );
+ isMultiLine = isMultiLine && hasCommentForValue( value[index] );
+ }
+ addChildValues_ = false;
+ isMultiLine = isMultiLine || lineLength >= rightMargin_;
+ }
+ return isMultiLine;
+}
+
+
+void
+StyledWriter::pushValue( const std::string &value )
+{
+ if ( addChildValues_ )
+ childValues_.push_back( value );
+ else
+ document_ += value;
+}
+
+
+void
+StyledWriter::writeIndent()
+{
+ if ( !document_.empty() )
+ {
+ char last = document_[document_.length()-1];
+ if ( last == ' ' ) // already indented
+ return;
+ if ( last != '\n' ) // Comments may add new-line
+ document_ += '\n';
+ }
+ document_ += indentString_;
+}
+
+
+void
+StyledWriter::writeWithIndent( const std::string &value )
+{
+ writeIndent();
+ document_ += value;
+}
+
+
+void
+StyledWriter::indent()
+{
+ indentString_ += std::string( indentSize_, ' ' );
+}
+
+
+void
+StyledWriter::unindent()
+{
+ assert( int(indentString_.size()) >= indentSize_ );
+ indentString_.resize( indentString_.size() - indentSize_ );
+}
+
+
+void
+StyledWriter::writeCommentBeforeValue( const Value &root )
+{
+ if ( !root.hasComment( commentBefore ) )
+ return;
+ document_ += normalizeEOL( root.getComment( commentBefore ) );
+ document_ += "\n";
+}
+
+
+void
+StyledWriter::writeCommentAfterValueOnSameLine( const Value &root )
+{
+ if ( root.hasComment( commentAfterOnSameLine ) )
+ document_ += " " + normalizeEOL( root.getComment( commentAfterOnSameLine ) );
+
+ if ( root.hasComment( commentAfter ) )
+ {
+ document_ += "\n";
+ document_ += normalizeEOL( root.getComment( commentAfter ) );
+ document_ += "\n";
+ }
+}
+
+
+bool
+StyledWriter::hasCommentForValue( const Value &value )
+{
+ return value.hasComment( commentBefore )
+ || value.hasComment( commentAfterOnSameLine )
+ || value.hasComment( commentAfter );
+}
+
+
+std::string
+StyledWriter::normalizeEOL( const std::string &text )
+{
+ std::string normalized;
+ normalized.reserve( text.length() );
+ const char *begin = text.c_str();
+ const char *end = begin + text.length();
+ const char *current = begin;
+ while ( current != end )
+ {
+ char c = *current++;
+ if ( c == '\r' ) // mac or dos EOL
+ {
+ if ( *current == '\n' ) // convert dos EOL
+ ++current;
+ normalized += '\n';
+ }
+ else // handle unix EOL & other char
+ normalized += c;
+ }
+ return normalized;
+}
+
+
+// Class StyledStreamWriter
+// //////////////////////////////////////////////////////////////////
+
+StyledStreamWriter::StyledStreamWriter( std::string indentation )
+ : document_(NULL)
+ , rightMargin_( 74 )
+ , indentation_( indentation )
+ , addChildValues_()
+{
+}
+
+
+void
+StyledStreamWriter::write( std::ostream &out, const Value &root )
+{
+ document_ = &out;
+ addChildValues_ = false;
+ indentString_ = "";
+ writeCommentBeforeValue( root );
+ writeValue( root );
+ writeCommentAfterValueOnSameLine( root );
+ *document_ << "\n";
+ document_ = NULL; // Forget the stream, for safety.
+}
+
+
+void
+StyledStreamWriter::writeValue( const Value &value )
+{
+ switch ( value.type() )
+ {
+ case nullValue:
+ pushValue( "null" );
+ break;
+ case intValue:
+ pushValue( valueToString( value.asLargestInt() ) );
+ break;
+ case uintValue:
+ pushValue( valueToString( value.asLargestUInt() ) );
+ break;
+ case realValue:
+ pushValue( valueToString( value.asDouble() ) );
+ break;
+ case stringValue:
+ pushValue( valueToQuotedString( value.asCString() ) );
+ break;
+ case booleanValue:
+ pushValue( valueToString( value.asBool() ) );
+ break;
+ case arrayValue:
+ writeArrayValue( value);
+ break;
+ case objectValue:
+ {
+ Value::Members members( value.getMemberNames() );
+ if ( members.empty() )
+ pushValue( "{}" );
+ else
+ {
+ writeWithIndent( "{" );
+ indent();
+ Value::Members::iterator it = members.begin();
+ for (;;)
+ {
+ const std::string &name = *it;
+ const Value &childValue = value[name];
+ writeCommentBeforeValue( childValue );
+ writeWithIndent( valueToQuotedString( name.c_str() ) );
+ *document_ << " : ";
+ writeValue( childValue );
+ if ( ++it == members.end() )
+ {
+ writeCommentAfterValueOnSameLine( childValue );
+ break;
+ }
+ *document_ << ",";
+ writeCommentAfterValueOnSameLine( childValue );
+ }
+ unindent();
+ writeWithIndent( "}" );
+ }
+ }
+ break;
+ }
+}
+
+
+void
+StyledStreamWriter::writeArrayValue( const Value &value )
+{
+ unsigned size = value.size();
+ if ( size == 0 )
+ pushValue( "[]" );
+ else
+ {
+ bool isArrayMultiLine = isMultineArray( value );
+ if ( isArrayMultiLine )
+ {
+ writeWithIndent( "[" );
+ indent();
+ bool hasChildValue = !childValues_.empty();
+ unsigned index =0;
+ for (;;)
+ {
+ const Value &childValue = value[index];
+ writeCommentBeforeValue( childValue );
+ if ( hasChildValue )
+ writeWithIndent( childValues_[index] );
+ else
+ {
+ writeIndent();
+ writeValue( childValue );
+ }
+ if ( ++index == size )
+ {
+ writeCommentAfterValueOnSameLine( childValue );
+ break;
+ }
+ *document_ << ",";
+ writeCommentAfterValueOnSameLine( childValue );
+ }
+ unindent();
+ writeWithIndent( "]" );
+ }
+ else // output on a single line
+ {
+ assert( childValues_.size() == size );
+ *document_ << "[ ";
+ for ( unsigned index =0; index < size; ++index )
+ {
+ if ( index > 0 )
+ *document_ << ", ";
+ *document_ << childValues_[index];
+ }
+ *document_ << " ]";
+ }
+ }
+}
+
+
+bool
+StyledStreamWriter::isMultineArray( const Value &value )
+{
+ int size = value.size();
+ bool isMultiLine = size*3 >= rightMargin_ ;
+ childValues_.clear();
+ for ( int index =0; index < size && !isMultiLine; ++index )
+ {
+ const Value &childValue = value[index];
+ isMultiLine = isMultiLine ||
+ ( (childValue.isArray() || childValue.isObject()) &&
+ childValue.size() > 0 );
+ }
+ if ( !isMultiLine ) // check if line length > max line length
+ {
+ childValues_.reserve( size );
+ addChildValues_ = true;
+ int lineLength = 4 + (size-1)*2; // '[ ' + ', '*n + ' ]'
+ for ( int index =0; index < size && !isMultiLine; ++index )
+ {
+ writeValue( value[index] );
+ lineLength += int( childValues_[index].length() );
+ isMultiLine = isMultiLine && hasCommentForValue( value[index] );
+ }
+ addChildValues_ = false;
+ isMultiLine = isMultiLine || lineLength >= rightMargin_;
+ }
+ return isMultiLine;
+}
+
+
+void
+StyledStreamWriter::pushValue( const std::string &value )
+{
+ if ( addChildValues_ )
+ childValues_.push_back( value );
+ else
+ *document_ << value;
+}
+
+
+void
+StyledStreamWriter::writeIndent()
+{
+ /*
+ Some comments in this method would have been nice. ;-)
+
+ if ( !document_.empty() )
+ {
+ char last = document_[document_.length()-1];
+ if ( last == ' ' ) // already indented
+ return;
+ if ( last != '\n' ) // Comments may add new-line
+ *document_ << '\n';
+ }
+ */
+ *document_ << '\n' << indentString_;
+}
+
+
+void
+StyledStreamWriter::writeWithIndent( const std::string &value )
+{
+ writeIndent();
+ *document_ << value;
+}
+
+
+void
+StyledStreamWriter::indent()
+{
+ indentString_ += indentation_;
+}
+
+
+void
+StyledStreamWriter::unindent()
+{
+ assert( indentString_.size() >= indentation_.size() );
+ indentString_.resize( indentString_.size() - indentation_.size() );
+}
+
+
+void
+StyledStreamWriter::writeCommentBeforeValue( const Value &root )
+{
+ if ( !root.hasComment( commentBefore ) )
+ return;
+ *document_ << normalizeEOL( root.getComment( commentBefore ) );
+ *document_ << "\n";
+}
+
+
+void
+StyledStreamWriter::writeCommentAfterValueOnSameLine( const Value &root )
+{
+ if ( root.hasComment( commentAfterOnSameLine ) )
+ *document_ << " " + normalizeEOL( root.getComment( commentAfterOnSameLine ) );
+
+ if ( root.hasComment( commentAfter ) )
+ {
+ *document_ << "\n";
+ *document_ << normalizeEOL( root.getComment( commentAfter ) );
+ *document_ << "\n";
+ }
+}
+
+
+bool
+StyledStreamWriter::hasCommentForValue( const Value &value )
+{
+ return value.hasComment( commentBefore )
+ || value.hasComment( commentAfterOnSameLine )
+ || value.hasComment( commentAfter );
+}
+
+
+std::string
+StyledStreamWriter::normalizeEOL( const std::string &text )
+{
+ std::string normalized;
+ normalized.reserve( text.length() );
+ const char *begin = text.c_str();
+ const char *end = begin + text.length();
+ const char *current = begin;
+ while ( current != end )
+ {
+ char c = *current++;
+ if ( c == '\r' ) // mac or dos EOL
+ {
+ if ( *current == '\n' ) // convert dos EOL
+ ++current;
+ normalized += '\n';
+ }
+ else // handle unix EOL & other char
+ normalized += c;
+ }
+ return normalized;
+}
+
+
+std::ostream& operator<<( std::ostream &sout, const Value &root )
+{
+ Json::StyledStreamWriter writer;
+ writer.write(sout, root);
+ return sout;
+}
+
+
+} // namespace Json
+
+// //////////////////////////////////////////////////////////////////////
+// End of content of file: src/lib_json/json_writer.cpp
+// //////////////////////////////////////////////////////////////////////
+
+
+
+
+
diff --git a/src/localplayer.cpp b/src/localplayer.cpp
index b6dd0f42e..8b6d7e2f6 100644
--- a/src/localplayer.cpp
+++ b/src/localplayer.cpp
@@ -90,37 +90,39 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d,
*/
/*
- Check if player is in water (the oscillating value)
+ Check if player is in liquid (the oscillating value)
*/
try{
- // If in water, the threshold of coming out is at higher y
- if(in_water)
+ // If in liquid, the threshold of coming out is at higher y
+ if(in_liquid)
{
v3s16 pp = floatToInt(position + v3f(0,BS*0.1,0), BS);
- in_water = nodemgr->get(map.getNode(pp).getContent()).isLiquid();
+ in_liquid = nodemgr->get(map.getNode(pp).getContent()).isLiquid();
+ liquid_viscosity = nodemgr->get(map.getNode(pp).getContent()).liquid_viscosity;
}
- // If not in water, the threshold of going in is at lower y
+ // If not in liquid, the threshold of going in is at lower y
else
{
v3s16 pp = floatToInt(position + v3f(0,BS*0.5,0), BS);
- in_water = nodemgr->get(map.getNode(pp).getContent()).isLiquid();
+ in_liquid = nodemgr->get(map.getNode(pp).getContent()).isLiquid();
+ liquid_viscosity = nodemgr->get(map.getNode(pp).getContent()).liquid_viscosity;
}
}
catch(InvalidPositionException &e)
{
- in_water = false;
+ in_liquid = false;
}
/*
- Check if player is in water (the stable value)
+ Check if player is in liquid (the stable value)
*/
try{
v3s16 pp = floatToInt(position + v3f(0,0,0), BS);
- in_water_stable = nodemgr->get(map.getNode(pp).getContent()).isLiquid();
+ in_liquid_stable = nodemgr->get(map.getNode(pp).getContent()).isLiquid();
}
catch(InvalidPositionException &e)
{
- in_water_stable = false;
+ in_liquid_stable = false;
}
/*
@@ -159,7 +161,7 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d,
If sneaking, keep in range from the last walked node and don't
fall off from it
*/
- if(control.sneak && m_sneak_node_exists && !g_settings->getBool("free_move"))
+ if(control.sneak && m_sneak_node_exists && !(fly_allowed && g_settings->getBool("free_move")) && !in_liquid)
{
f32 maxd = 0.5*BS + sneak_max;
v3f lwn_f = intToFloat(m_sneak_node, BS);
@@ -315,7 +317,7 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d,
}
if(bouncy_jump && control.jump){
- m_speed.Y += 6.5*BS;
+ m_speed.Y += movement_speed_jump*BS;
touching_ground = false;
MtEvent *e = new SimpleTriggerEvent("PlayerJump");
m_gamedef->event()->put(e);
@@ -348,7 +350,7 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d,
*/
const ContentFeatures &f = nodemgr->get(map.getNodeNoEx(getStandingNodePos()));
// Determine if jumping is possible
- m_can_jump = touching_ground;
+ m_can_jump = touching_ground && !in_liquid;
if(itemgroup_get(f.groups, "disable_jump"))
m_can_jump = false;
}
@@ -361,12 +363,8 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d)
void LocalPlayer::applyControl(float dtime)
{
// Clear stuff
- swimming_up = false;
+ swimming_vertical = false;
- // Random constants
- f32 walk_acceleration = 4.0 * BS;
- f32 walkspeed_max = 4.0 * BS;
-
setPitch(control.pitch);
setYaw(control.yaw);
@@ -380,22 +378,17 @@ void LocalPlayer::applyControl(float dtime)
v3f move_direction = v3f(0,0,1);
move_direction.rotateXZBy(getYaw());
- v3f speed = v3f(0,0,0);
+ v3f speedH = v3f(0,0,0); // Horizontal (X, Z)
+ v3f speedV = v3f(0,0,0); // Vertical (Y)
bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
bool fast_allowed = m_gamedef->checkLocalPrivilege("fast");
bool free_move = fly_allowed && g_settings->getBool("free_move");
bool fast_move = fast_allowed && g_settings->getBool("fast_move");
+ bool fast_or_aux1_descends = (fast_move && control.aux1) || (fast_move && g_settings->getBool("aux1_descends"));
bool continuous_forward = g_settings->getBool("continuous_forward");
- if(free_move || is_climbing)
- {
- v3f speed = getSpeed();
- speed.Y = 0;
- setSpeed(speed);
- }
-
// Whether superspeed mode is used or not
bool superspeed = false;
@@ -415,18 +408,21 @@ void LocalPlayer::applyControl(float dtime)
if(free_move)
{
// In free movement mode, aux1 descends
- v3f speed = getSpeed();
if(fast_move)
- speed.Y = -20*BS;
+ speedV.Y = -movement_speed_fast;
else
- speed.Y = -walkspeed_max;
- setSpeed(speed);
+ speedV.Y = -movement_speed_walk;
+ }
+ else if(in_liquid || in_liquid_stable)
+ {
+ // Always use fast when aux1_descends & fast_move are enabled in liquid, since the aux1 button would mean both turbo and "swim down" causing a conflict
+ speedV.Y = -movement_speed_fast;
+ swimming_vertical = true;
}
else if(is_climbing)
{
- v3f speed = getSpeed();
- speed.Y = -3*BS;
- setSpeed(speed);
+ // Always use fast when aux1_descends & fast_move are enabled when climbing, since the aux1 button would mean both turbo and "descend" causing a conflict
+ speedV.Y = -movement_speed_fast;
}
else
{
@@ -456,66 +452,69 @@ void LocalPlayer::applyControl(float dtime)
if(free_move)
{
// In free movement mode, sneak descends
- v3f speed = getSpeed();
- if(fast_move && (control.aux1 ||
- g_settings->getBool("always_fly_fast")))
- speed.Y = -20*BS;
+ if(fast_move && (control.aux1 || g_settings->getBool("always_fly_fast")))
+ speedV.Y = -movement_speed_fast;
else
- speed.Y = -walkspeed_max;
- setSpeed(speed);
+ speedV.Y = -movement_speed_walk;
+ }
+ else if(in_liquid || in_liquid_stable)
+ {
+ if(fast_or_aux1_descends)
+ // Always use fast when aux1_descends & fast_move are enabled in liquid, since the aux1 button would mean both turbo and "swim down" causing a conflict
+ speedV.Y = -movement_speed_fast;
+ else
+ speedV.Y = -movement_speed_walk;
+ swimming_vertical = true;
}
else if(is_climbing)
{
- v3f speed = getSpeed();
- speed.Y = -3*BS;
- setSpeed(speed);
+ if(fast_or_aux1_descends)
+ // Always use fast when aux1_descends & fast_move are enabled when climbing, since the aux1 button would mean both turbo and "descend" causing a conflict
+ speedV.Y = -movement_speed_fast;
+ else
+ speedV.Y = -movement_speed_climb;
}
}
}
if(continuous_forward)
- speed += move_direction;
+ speedH += move_direction;
if(control.up)
{
if(continuous_forward)
superspeed = true;
else
- speed += move_direction;
+ speedH += move_direction;
}
if(control.down)
{
- speed -= move_direction;
+ speedH -= move_direction;
}
if(control.left)
{
- speed += move_direction.crossProduct(v3f(0,1,0));
+ speedH += move_direction.crossProduct(v3f(0,1,0));
}
if(control.right)
{
- speed += move_direction.crossProduct(v3f(0,-1,0));
+ speedH += move_direction.crossProduct(v3f(0,-1,0));
}
if(control.jump)
{
if(free_move)
- {
- v3f speed = getSpeed();
-
- if(g_settings->getBool("aux1_descends") ||
- g_settings->getBool("always_fly_fast"))
+ {
+ if(g_settings->getBool("aux1_descends") || g_settings->getBool("always_fly_fast"))
{
if(fast_move)
- speed.Y = 20*BS;
+ speedV.Y = movement_speed_fast;
else
- speed.Y = walkspeed_max;
+ speedV.Y = movement_speed_walk;
} else {
if(fast_move && control.aux1)
- speed.Y = 20*BS;
+ speedV.Y = movement_speed_fast;
else
- speed.Y = walkspeed_max;
+ speedV.Y = movement_speed_walk;
}
-
- setSpeed(speed);
}
else if(m_can_jump)
{
@@ -524,49 +523,66 @@ void LocalPlayer::applyControl(float dtime)
raising the height at which the jump speed is kept
at its starting value
*/
- v3f speed = getSpeed();
- if(speed.Y >= -0.5*BS)
+ v3f speedJ = getSpeed();
+ if(speedJ.Y >= -0.5 * BS)
{
- speed.Y = 6.5*BS;
- setSpeed(speed);
+ speedJ.Y = movement_speed_jump;
+ setSpeed(speedJ);
MtEvent *e = new SimpleTriggerEvent("PlayerJump");
m_gamedef->event()->put(e);
}
}
- // Use the oscillating value for getting out of water
- // (so that the player doesn't fly on the surface)
- else if(in_water)
+ else if(in_liquid)
{
- v3f speed = getSpeed();
- speed.Y = 1.5*BS;
- setSpeed(speed);
- swimming_up = true;
+ if(fast_or_aux1_descends)
+ // Always use fast when aux1_descends & fast_move are enabled in liquid, since the aux1 button would mean both turbo and "swim down" causing a conflict
+ speedV.Y = movement_speed_fast;
+ else
+ speedV.Y = movement_speed_walk;
+ swimming_vertical = true;
}
else if(is_climbing)
{
- v3f speed = getSpeed();
- speed.Y = 3*BS;
- setSpeed(speed);
+ if(fast_or_aux1_descends)
+ // Always use fast when aux1_descends & fast_move are enabled when climbing, since the aux1 button would mean both turbo and "descend" causing a conflict
+ speedV.Y = movement_speed_fast;
+ else
+ speedV.Y = movement_speed_climb;
}
}
// The speed of the player (Y is ignored)
- if(superspeed)
- speed = speed.normalize() * walkspeed_max * 5.0;
- else if(control.sneak && !free_move)
- speed = speed.normalize() * walkspeed_max / 3.0;
+ if(superspeed || (is_climbing && fast_or_aux1_descends) || ((in_liquid || in_liquid_stable) && fast_or_aux1_descends))
+ speedH = speedH.normalize() * movement_speed_fast;
+ else if(control.sneak && !free_move && !in_liquid && !in_liquid_stable)
+ speedH = speedH.normalize() * movement_speed_crouch;
else
- speed = speed.normalize() * walkspeed_max;
-
- f32 inc = walk_acceleration * BS * dtime;
-
- // Faster acceleration if fast and free movement
- if(free_move && fast_move && superspeed)
- inc = walk_acceleration * BS * dtime * 10;
-
+ speedH = speedH.normalize() * movement_speed_walk;
+
+ // Acceleration increase
+ f32 incH = 0; // Horizontal (X, Z)
+ f32 incV = 0; // Vertical (Y)
+ if((!touching_ground && !free_move && !is_climbing && !in_liquid) || (!free_move && m_can_jump && control.jump))
+ {
+ // Jumping and falling
+ if(superspeed || (fast_move && control.aux1))
+ incH = movement_acceleration_fast * BS * dtime;
+ else
+ incH = movement_acceleration_air * BS * dtime;
+ incV = 0; // No vertical acceleration in air
+ }
+ else if(superspeed || (fast_move && control.aux1))
+ incH = incV = movement_acceleration_fast * BS * dtime;
+ else if ((in_liquid || in_liquid_stable) && fast_or_aux1_descends)
+ // Always use fast when aux1_descends & fast_move are enabled in liquid, since the aux1 button would mean both turbo and "swim down" causing a conflict
+ incH = incV = movement_acceleration_fast * BS * dtime;
+ else
+ incH = incV = movement_acceleration_default * BS * dtime;
+
// Accelerate to target speed with maximum increment
- accelerate(speed, inc);
+ accelerateHorizontal(speedH, incH);
+ accelerateVertical(speedV, incV);
}
v3s16 LocalPlayer::getStandingNodePos()
diff --git a/src/main.cpp b/src/main.cpp
index 0af9d113c..9287ec760 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -68,9 +68,13 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "profiler.h"
#include "log.h"
#include "mods.h"
+#if USE_FREETYPE
+#include "xCGUITTFont.h"
+#endif
#include "util/string.h"
#include "subgame.h"
#include "quicktune.h"
+#include "serverlist.h"
/*
Settings.
@@ -766,11 +770,19 @@ int main(int argc, char *argv[])
log_register_thread("main");
- // Set locale. This is for forcing '.' as the decimal point.
- std::locale::global(std::locale("C"));
- // This enables printing all characters in bitmap font
- setlocale(LC_CTYPE, "en_US");
+ // This enables internatonal characters input
+ if( setlocale(LC_ALL, "") == NULL )
+ {
+ fprintf( stderr, "%s: warning: could not set default locale\n", argv[0] );
+ }
+ // Set locale. This is for forcing '.' as the decimal point.
+ try {
+ std::locale::global(std::locale(std::locale(""), "C", std::locale::numeric));
+ setlocale(LC_NUMERIC, "C");
+ } catch (const std::exception& ex) {
+ errorstream<<"Could not set numeric locale to C"<<std::endl;
+ }
/*
Parse command line
*/
@@ -874,23 +886,8 @@ int main(int argc, char *argv[])
// Create user data directory
fs::CreateDir(porting::path_user);
- init_gettext((porting::path_share+DIR_DELIM+".."+DIR_DELIM+"locale").c_str());
-
- // Initialize debug streams
-#define DEBUGFILE "debug.txt"
-#if RUN_IN_PLACE
- std::string logfile = DEBUGFILE;
-#else
- std::string logfile = porting::path_user+DIR_DELIM+DEBUGFILE;
-#endif
- if(cmd_args.exists("logfile"))
- logfile = cmd_args.get("logfile");
- if(logfile != "")
- debugstreams_init(false, logfile.c_str());
- else
- debugstreams_init(false, NULL);
+ init_gettext((porting::path_share + DIR_DELIM + "locale").c_str());
- infostream<<"logfile = "<<logfile<<std::endl;
infostream<<"path_share = "<<porting::path_share<<std::endl;
infostream<<"path_user = "<<porting::path_user<<std::endl;
@@ -983,6 +980,31 @@ int main(int argc, char *argv[])
if(configpath == "")
configpath = filenames[0];
}
+
+ // Initialize debug streams
+#define DEBUGFILE "debug.txt"
+#if RUN_IN_PLACE
+ std::string logfile = DEBUGFILE;
+#else
+ std::string logfile = 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);
+ int loglevel = g_settings->getS32("debug_log_level");
+
+ if (loglevel == 0) //no logging
+ logfile = "";
+ else if (loglevel > 0 && loglevel <= LMT_NUM_VALUES)
+ log_add_output_maxlev(&main_dstream_no_stderr_log_out, (LogMessageLevel)(loglevel - 1));
+
+ if(logfile != "")
+ debugstreams_init(false, logfile.c_str());
+ else
+ debugstreams_init(false, NULL);
+
+ infostream<<"logfile = "<<logfile<<std::endl;
// Initialize random seed
srand(time(0));
@@ -1083,6 +1105,7 @@ int main(int argc, char *argv[])
#else
bool run_dedicated_server = cmd_args.getFlag("server");
#endif
+ g_settings->set("server_dedicated", run_dedicated_server ? "true" : "false");
if(run_dedicated_server)
{
DSTACK("Dedicated server branch");
@@ -1328,7 +1351,13 @@ int main(int argc, char *argv[])
guienv = device->getGUIEnvironment();
gui::IGUISkin* skin = guienv->getSkin();
+ #if USE_FREETYPE
+ std::string font_path = g_settings->get("font_path");
+ u16 font_size = g_settings->getU16("font_size");
+ gui::IGUIFont *font = gui::CGUITTFont::createTTFont(guienv, font_path.c_str(), font_size);
+ #else
gui::IGUIFont* font = guienv->getFont(getTexturePath("fontlucida.png").c_str());
+ #endif
if(font)
skin->setFont(font);
else
@@ -1575,13 +1604,14 @@ int main(int argc, char *argv[])
g_settings->set("creative_mode", itos(menudata.creative_mode));
g_settings->set("enable_damage", itos(menudata.enable_damage));
+ g_settings->set("server_announce", itos(menudata.enable_public));
g_settings->set("name", playername);
g_settings->set("address", address);
g_settings->set("port", itos(port));
if(menudata.selected_world != -1)
g_settings->set("selected_world_path",
worldspecs[menudata.selected_world].path);
-
+
// Break out of menu-game loop to shut down cleanly
if(device->run() == false || kill == true)
break;
@@ -1598,6 +1628,15 @@ int main(int argc, char *argv[])
current_address = "";
current_port = 30011;
}
+ else if (address != "")
+ {
+ ServerListSpec server;
+ server["name"] = menudata.servername;
+ server["address"] = wide_to_narrow(menudata.address);
+ server["port"] = wide_to_narrow(menudata.port);
+ server["description"] = menudata.serverdescription;
+ ServerList::insert(server);
+ }
// Set world path to selected one
if(menudata.selected_world != -1){
diff --git a/src/map.cpp b/src/map.cpp
index 0e9b121d7..717b0cf9b 100644
--- a/src/map.cpp
+++ b/src/map.cpp
@@ -33,19 +33,20 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "gamedef.h"
#include "util/directiontables.h"
#include "rollback_interface.h"
+#include "mapgen_v6.h"
#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
/*
SQLite format specification:
- Initially only replaces sectors/ and sectors2/
-
+
If map.sqlite does not exist in the save dir
or the block was not found in the database
the map will try to load from sectors folder.
In either case, map.sqlite will be created
and all future saves will save there.
-
+
Structure of map.sqlite:
Tables:
blocks
@@ -108,14 +109,14 @@ MapSector * Map::getSectorNoGenerateNoExNoLock(v2s16 p)
MapSector * sector = m_sector_cache;
return sector;
}
-
+
core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p);
-
+
if(n == NULL)
return NULL;
-
+
MapSector *sector = n->getValue();
-
+
// Cache the last result
m_sector_cache_p = p;
m_sector_cache = sector;
@@ -133,7 +134,7 @@ MapSector * Map::getSectorNoGenerate(v2s16 p)
MapSector *sector = getSectorNoGenerateNoEx(p);
if(sector == NULL)
throw InvalidPositionException();
-
+
return sector;
}
@@ -148,7 +149,7 @@ MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
}
MapBlock * Map::getBlockNoCreate(v3s16 p3d)
-{
+{
MapBlock *block = getBlockNoCreateNoEx(p3d);
if(block == NULL)
throw InvalidPositionException();
@@ -248,10 +249,10 @@ void Map::unspreadLight(enum LightBank bank,
v3s16(0,-1,0), // bottom
v3s16(-1,0,0), // left
};
-
+
if(from_nodes.size() == 0)
return;
-
+
u32 blockchangecount = 0;
core::map<v3s16, u8> unlighted_nodes;
@@ -265,12 +266,12 @@ void Map::unspreadLight(enum LightBank bank,
MapBlock *block = NULL;
// Cache this a bit, too
bool block_checked_in_modified = false;
-
+
for(; j.atEnd() == false; j++)
{
v3s16 pos = j.getNode()->getKey();
v3s16 blockpos = getNodeBlockPos(pos);
-
+
// Only fetch a new block if the block position has changed
try{
if(block == NULL || blockpos != blockpos_last){
@@ -693,7 +694,7 @@ void Map::updateLighting(enum LightBank bank,
core::map<v3s16, u8> unlight_from;
int num_bottom_invalid = 0;
-
+
{
//TimeTaker t("first stuff");
@@ -847,7 +848,7 @@ void Map::updateLighting(enum LightBank bank,
#if 0
{
//MapVoxelManipulator vmanip(this);
-
+
// Make a manual voxel manipulator and load all the blocks
// that touch the requested blocks
ManualMapVoxelManipulator vmanip(this);
@@ -938,7 +939,7 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n,
/*PrintInfo(m_dout);
m_dout<<DTIME<<"Map::addNodeAndUpdate(): p=("
<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
-
+
/*
From this node to nodes underneath:
If lighting is sunlight (1.0), unlight neighbours and
@@ -951,7 +952,7 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n,
bool node_under_sunlight = true;
core::map<v3s16, bool> light_sources;
-
+
/*
Collect old node for rollback
*/
@@ -1440,10 +1441,10 @@ void Map::timerUpdate(float dtime, float unload_timeout,
core::list<v3s16> *unloaded_blocks)
{
bool save_before_unloading = (mapType() == MAPTYPE_SERVER);
-
+
// Profile modified reasons
Profiler modprofiler;
-
+
core::list<v2s16> sector_deletion_queue;
u32 deleted_blocks_count = 0;
u32 saved_blocks_count = 0;
@@ -1461,12 +1462,12 @@ void Map::timerUpdate(float dtime, float unload_timeout,
core::list<MapBlock*> blocks;
sector->getBlocks(blocks);
-
+
for(core::list<MapBlock*>::Iterator i = blocks.begin();
i != blocks.end(); i++)
{
MapBlock *block = (*i);
-
+
block->incrementUsageTimer(dtime);
if(block->refGet() == 0 && block->getUsageTimer() > unload_timeout)
@@ -1503,10 +1504,10 @@ void Map::timerUpdate(float dtime, float unload_timeout,
}
}
endSave();
-
+
// Finally delete the empty sectors
deleteSectors(sector_deletion_queue);
-
+
if(deleted_blocks_count != 0)
{
PrintInfo(infostream); // ServerMap/ClientMap:
@@ -1560,7 +1561,7 @@ void Map::unloadUnusedData(float timeout,
i != blocks.end(); i++)
{
MapBlock *block = (*i);
-
+
if(block->getUsageTimer() > timeout)
{
// Save if modified
@@ -1629,7 +1630,7 @@ void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
// list of nodes that due to viscosity have not reached their max level height
UniqueQueue<v3s16> must_reflow;
-
+
// List of MapBlocks that will require a lighting update (due to lava)
core::map<v3s16, MapBlock*> lighting_modified_blocks;
@@ -1714,7 +1715,7 @@ void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
}
break;
case LIQUID_SOURCE:
- // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
+ // 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) {
@@ -1824,7 +1825,7 @@ void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
}
n0.setContent(new_node_content);
-
+
// Find out whether there is a suspect for this action
std::string suspect;
if(m_gamedef->rollback()){
@@ -1993,8 +1994,7 @@ void Map::removeNodeTimer(v3s16 p)
/*
ServerMap
*/
-
-ServerMap::ServerMap(std::string savedir, IGameDef *gamedef):
+ServerMap::ServerMap(std::string savedir, IGameDef *gamedef, EmergeManager *emerge):
Map(dout_server, gamedef),
m_seed(0),
m_map_metadata_changed(true),
@@ -2004,18 +2004,20 @@ ServerMap::ServerMap(std::string savedir, IGameDef *gamedef):
{
verbosestream<<__FUNCTION_NAME<<std::endl;
- //m_chunksize = 8; // Takes a few seconds
+ m_emerge = emerge;
+ m_mgparams = m_emerge->getParamsFromSettings(g_settings);
+ if (!m_mgparams)
+ m_mgparams = new MapgenV6Params();
+
+ m_seed = m_mgparams->seed;
if (g_settings->get("fixed_map_seed").empty())
{
- m_seed = (((u64)(myrand()%0xffff)<<0)
- + ((u64)(myrand()%0xffff)<<16)
- + ((u64)(myrand()%0xffff)<<32)
- + ((u64)(myrand()%0xffff)<<48));
- }
- else
- {
- m_seed = g_settings->getU64("fixed_map_seed");
+ m_seed = (((u64)(myrand() & 0xffff) << 0)
+ | ((u64)(myrand() & 0xffff) << 16)
+ | ((u64)(myrand() & 0xffff) << 32)
+ | ((u64)(myrand() & 0xffff) << 48));
+ m_mgparams->seed = m_seed;
}
/*
@@ -2050,6 +2052,10 @@ ServerMap::ServerMap(std::string savedir, IGameDef *gamedef):
// Load map metadata (seed, chunksize)
loadMapMeta();
}
+ catch(SettingNotFoundException &e){
+ infostream<<"ServerMap: Some metadata not found."
+ <<" Using default settings."<<std::endl;
+ }
catch(FileNotGoodException &e){
infostream<<"WARNING: Could not load map metadata"
//<<" Disabling chunk-based generator."
@@ -2136,7 +2142,7 @@ ServerMap::~ServerMap()
#endif
}
-void ServerMap::initBlockMake(mapgen::BlockMakeData *data, v3s16 blockpos)
+void ServerMap::initBlockMake(BlockMakeData *data, v3s16 blockpos)
{
bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
if(enable_mapgen_debug_info)
@@ -2144,7 +2150,7 @@ void ServerMap::initBlockMake(mapgen::BlockMakeData *data, v3s16 blockpos)
<<"("<<blockpos.X<<","<<blockpos.Y<<","<<blockpos.Z<<") - "
<<"("<<blockpos.X<<","<<blockpos.Y<<","<<blockpos.Z<<")"
<<std::endl;
-
+
//s16 chunksize = 3;
//v3s16 chunk_offset(-1,-1,-1);
//s16 chunksize = 4;
@@ -2167,7 +2173,7 @@ void ServerMap::initBlockMake(mapgen::BlockMakeData *data, v3s16 blockpos)
data->no_op = true;
return;
}
-
+
data->no_op = false;
data->seed = m_seed;
data->blockpos_min = blockpos_min;
@@ -2180,7 +2186,7 @@ void ServerMap::initBlockMake(mapgen::BlockMakeData *data, v3s16 blockpos)
*/
{
//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;
@@ -2208,7 +2214,7 @@ void ServerMap::initBlockMake(mapgen::BlockMakeData *data, v3s16 blockpos)
Refer to the map generator heuristics.
*/
- bool ug = mapgen::block_is_underground(data->seed, p);
+ bool ug = m_emerge->isBlockUnderground(p);
block->setIsUnderground(ug);
}
@@ -2219,18 +2225,18 @@ void ServerMap::initBlockMake(mapgen::BlockMakeData *data, v3s16 blockpos)
}
}
}
-
+
/*
Now we have a big empty area.
Make a ManualMapVoxelManipulator that contains this and the
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 ManualMapVoxelManipulator(this);
//data->vmanip->setMap(this);
@@ -2243,7 +2249,7 @@ void ServerMap::initBlockMake(mapgen::BlockMakeData *data, v3s16 blockpos)
// Data is ready now.
}
-MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data,
+MapBlock* ServerMap::finishBlockMake(BlockMakeData *data,
core::map<v3s16, MapBlock*> &changed_blocks)
{
v3s16 blockpos_min = data->blockpos_min;
@@ -2314,7 +2320,7 @@ MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data,
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++)
@@ -2331,7 +2337,7 @@ MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data,
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
@@ -2385,7 +2391,7 @@ MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data,
assert(block);
block->setGenerated(true);
}
-
+
/*
Save changed parts of map
NOTE: Will be saved later.
@@ -2429,14 +2435,14 @@ ServerMapSector * ServerMap::createSector(v2s16 p2d)
DSTACKF("%s: p2d=(%d,%d)",
__FUNCTION_NAME,
p2d.X, p2d.Y);
-
+
/*
Check if it exists already in memory
*/
ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
if(sector != NULL)
return sector;
-
+
/*
Try to load it from disk (with blocks)
*/
@@ -2469,9 +2475,9 @@ ServerMapSector * ServerMap::createSector(v2s16 p2d)
/*
Generate blank sector
*/
-
+
sector = new ServerMapSector(this, p2d, m_gamedef);
-
+
// Sector position on map in nodes
v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
@@ -2479,10 +2485,11 @@ ServerMapSector * ServerMap::createSector(v2s16 p2d)
Insert to container
*/
m_sectors.insert(p2d, sector);
-
+
return sector;
}
+#if 0
/*
This is a quick-hand function for calling makeBlock().
*/
@@ -2492,20 +2499,20 @@ MapBlock * ServerMap::generateBlock(
)
{
DSTACKF("%s: p=(%d,%d,%d)", __FUNCTION_NAME, p.X, p.Y, p.Z);
-
+
/*infostream<<"generateBlock(): "
<<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
<<std::endl;*/
-
+
bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
TimeTaker timer("generateBlock");
-
+
//MapBlock *block = original_dummy;
-
+
v2s16 p2d(p.X, p.Z);
v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
-
+
/*
Do not generate over-limit
*/
@@ -2518,7 +2525,7 @@ MapBlock * ServerMap::generateBlock(
/*
Create block make data
*/
- mapgen::BlockMakeData data;
+ BlockMakeData data;
initBlockMake(&data, p);
/*
@@ -2526,7 +2533,8 @@ MapBlock * ServerMap::generateBlock(
*/
{
TimeTaker t("mapgen::make_block()");
- mapgen::make_block(&data);
+ mapgen->makeChunk(&data);
+ //mapgen::make_block(&data);
if(enable_mapgen_debug_info == false)
t.stop(true); // Hide output
@@ -2595,12 +2603,13 @@ MapBlock * ServerMap::generateBlock(
return block;
}
+#endif
MapBlock * ServerMap::createBlock(v3s16 p)
{
DSTACKF("%s: p=(%d,%d,%d)",
__FUNCTION_NAME, p.X, p.Y, p.Z);
-
+
/*
Do not create over-limit
*/
@@ -2611,7 +2620,7 @@ MapBlock * ServerMap::createBlock(v3s16 p)
|| p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
|| p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
throw InvalidPositionException("createBlock(): pos. over limit");
-
+
v2s16 p2d(p.X, p.Z);
s16 block_y = p.Y;
/*
@@ -2656,15 +2665,16 @@ MapBlock * ServerMap::createBlock(v3s16 p)
}
// Create blank
block = sector->createBlankBlock(block_y);
+
return block;
}
-MapBlock * ServerMap::emergeBlock(v3s16 p, bool allow_generate)
+MapBlock * ServerMap::emergeBlock(v3s16 p, bool create_blank)
{
- DSTACKF("%s: p=(%d,%d,%d), allow_generate=%d",
+ DSTACKF("%s: p=(%d,%d,%d), create_blank=%d",
__FUNCTION_NAME,
- p.X, p.Y, p.Z, allow_generate);
-
+ p.X, p.Y, p.Z, create_blank);
+
{
MapBlock *block = getBlockNoCreateNoEx(p);
if(block && block->isDummy() == false)
@@ -2677,7 +2687,13 @@ MapBlock * ServerMap::emergeBlock(v3s16 p, bool allow_generate)
return block;
}
- if(allow_generate)
+ if (create_blank) {
+ ServerMapSector *sector = createSector(v2s16(p.X, p.Z));
+ MapBlock *block = sector->createBlankBlock(p.Y);
+
+ return block;
+ }
+ /*if(allow_generate)
{
core::map<v3s16, MapBlock*> modified_blocks;
MapBlock *block = generateBlock(p, modified_blocks);
@@ -2697,10 +2713,10 @@ MapBlock * ServerMap::emergeBlock(v3s16 p, bool allow_generate)
// Queue event
dispatchEvent(&event);
-
+
return block;
}
- }
+ }*/
return NULL;
}
@@ -2741,8 +2757,8 @@ plan_b:
/*
Determine from map generator noise functions
*/
-
- s16 level = mapgen::find_ground_level_from_noise(m_seed, p2d, 1);
+
+ s16 level = m_emerge->getGroundLevelAtPoint(p2d);
return level;
//double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
@@ -2767,48 +2783,48 @@ void ServerMap::createDatabase() {
void ServerMap::verifyDatabase() {
if(m_database)
return;
-
+
{
std::string dbp = m_savedir + DIR_DELIM + "map.sqlite";
bool needs_create = false;
int d;
-
+
/*
Open the database connection
*/
-
+
createDirs(m_savedir);
-
+
if(!fs::PathExists(dbp))
needs_create = true;
-
+
d = sqlite3_open_v2(dbp.c_str(), &m_database, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
if(d != SQLITE_OK) {
infostream<<"WARNING: Database failed to open: "<<sqlite3_errmsg(m_database)<<std::endl;
throw FileNotGoodException("Cannot open database file");
}
-
+
if(needs_create)
createDatabase();
-
+
d = sqlite3_prepare(m_database, "SELECT `data` FROM `blocks` WHERE `pos`=? LIMIT 1", -1, &m_database_read, NULL);
if(d != SQLITE_OK) {
infostream<<"WARNING: Database read statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
throw FileNotGoodException("Cannot prepare read statement");
}
-
+
d = sqlite3_prepare(m_database, "REPLACE INTO `blocks` VALUES(?, ?)", -1, &m_database_write, NULL);
if(d != SQLITE_OK) {
infostream<<"WARNING: Database write statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
throw FileNotGoodException("Cannot prepare write statement");
}
-
+
d = sqlite3_prepare(m_database, "SELECT `pos` FROM `blocks`", -1, &m_database_list, NULL);
if(d != SQLITE_OK) {
infostream<<"WARNING: Database list statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
throw FileNotGoodException("Cannot prepare read statement");
}
-
+
infostream<<"ServerMap: Database opened"<<std::endl;
}
}
@@ -2913,11 +2929,11 @@ void ServerMap::save(ModifiedState save_level)
infostream<<"WARNING: Not saving map, saving disabled."<<std::endl;
return;
}
-
+
if(save_level == MOD_STATE_CLEAN)
infostream<<"ServerMap: Saving whole map, this can take time."
<<std::endl;
-
+
if(m_map_metadata_changed || save_level == MOD_STATE_CLEAN)
{
saveMapMeta();
@@ -2925,11 +2941,11 @@ void ServerMap::save(ModifiedState save_level)
// Profile modified reasons
Profiler modprofiler;
-
+
u32 sector_meta_count = 0;
u32 block_count = 0;
u32 block_count_all = 0; // Number of blocks in memory
-
+
// Don't do anything with sqlite unless something is really saved
bool save_started = false;
@@ -2938,7 +2954,7 @@ void ServerMap::save(ModifiedState save_level)
{
ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
assert(sector->getId() == MAPSECTOR_SERVER);
-
+
if(sector->differs_from_disk || save_level == MOD_STATE_CLEAN)
{
saveSectorMeta(sector);
@@ -2947,11 +2963,11 @@ void ServerMap::save(ModifiedState save_level)
core::list<MapBlock*> blocks;
sector->getBlocks(blocks);
core::list<MapBlock*>::Iterator j;
-
+
for(j=blocks.begin(); j!=blocks.end(); j++)
{
MapBlock *block = *j;
-
+
block_count_all++;
if(block->getModified() >= save_level)
@@ -3027,10 +3043,10 @@ void ServerMap::listAllLoadableBlocks(core::list<v3s16> &dst)
errorstream<<"Map::listAllLoadableBlocks(): Result will be missing "
<<"all blocks that are stored in flat files"<<std::endl;
}
-
+
{
verifyDatabase();
-
+
while(sqlite3_step(m_database_list) == SQLITE_ROW)
{
sqlite3_int64 block_i = sqlite3_column_int64(m_database_list, 0);
@@ -3044,13 +3060,13 @@ void ServerMap::listAllLoadableBlocks(core::list<v3s16> &dst)
void ServerMap::saveMapMeta()
{
DSTACK(__FUNCTION_NAME);
-
+
/*infostream<<"ServerMap::saveMapMeta(): "
<<"seed="<<m_seed
<<std::endl;*/
createDirs(m_savedir);
-
+
std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
std::ofstream os(fullpath.c_str(), std::ios_base::binary);
if(os.good() == false)
@@ -3059,21 +3075,21 @@ void ServerMap::saveMapMeta()
<<"could not open"<<fullpath<<std::endl;
throw FileNotGoodException("Cannot open chunk metadata");
}
-
+
Settings params;
- params.setU64("seed", m_seed);
+ m_emerge->setParamsToSettings(&params);
params.writeLines(os);
os<<"[end_of_params]\n";
-
+
m_map_metadata_changed = false;
}
void ServerMap::loadMapMeta()
{
DSTACK(__FUNCTION_NAME);
-
+
/*infostream<<"ServerMap::loadMapMeta(): Loading map metadata"
<<std::endl;*/
@@ -3101,7 +3117,18 @@ void ServerMap::loadMapMeta()
params.parseConfigLine(line);
}
- m_seed = params.getU64("seed");
+ MapgenParams *mgparams = m_emerge->getParamsFromSettings(&params);
+ if (mgparams) {
+ if (m_mgparams)
+ delete m_mgparams;
+ m_mgparams = mgparams;
+ m_seed = mgparams->seed;
+ } else {
+ if (params.exists("seed")) {
+ m_seed = params.getU64("seed");
+ m_mgparams->seed = m_seed;
+ }
+ }
verbosestream<<"ServerMap::loadMapMeta(): "<<"seed="<<m_seed<<std::endl;
}
@@ -3115,14 +3142,14 @@ void ServerMap::saveSectorMeta(ServerMapSector *sector)
v2s16 pos = sector->getPos();
std::string dir = getSectorDir(pos);
createDirs(dir);
-
+
std::string fullpath = dir + DIR_DELIM + "meta";
std::ofstream o(fullpath.c_str(), std::ios_base::binary);
if(o.good() == false)
throw FileNotGoodException("Cannot open sector metafile");
sector->serialize(o, version);
-
+
sector->differs_from_disk = false;
}
@@ -3161,7 +3188,7 @@ MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load
if(save_after_load)
saveSectorMeta(sector);
}
-
+
sector->differs_from_disk = false;
return sector;
@@ -3206,7 +3233,7 @@ bool ServerMap::loadSectorMeta(v2s16 p2d)
{
return false;
}
-
+
return true;
}
@@ -3250,7 +3277,7 @@ bool ServerMap::loadSectorFull(v2s16 p2d)
{
return false;
}
-
+
/*
Load blocks
*/
@@ -3312,8 +3339,8 @@ void ServerMap::saveBlock(MapBlock *block)
u8 version = SER_FMT_VER_HIGHEST;
// Get destination
v3s16 p3d = block->getPos();
-
-
+
+
#if 0
v2s16 p2d(p3d.X, p3d.Z);
std::string sectordir = getSectorDir(p2d);
@@ -3329,34 +3356,42 @@ void ServerMap::saveBlock(MapBlock *block)
[0] u8 serialization version
[1] data
*/
-
+
verifyDatabase();
-
+
std::ostringstream o(std::ios_base::binary);
-
+
o.write((char*)&version, 1);
-
+
// Write basic data
block->serialize(o, version, true);
-
+
// Write block to database
-
+
std::string tmp = o.str();
const char *bytes = tmp.c_str();
-
- if(sqlite3_bind_int64(m_database_write, 1, getBlockAsInteger(p3d)) != SQLITE_OK)
+
+ bool success = true;
+ if(sqlite3_bind_int64(m_database_write, 1, getBlockAsInteger(p3d)) != SQLITE_OK) {
infostream<<"WARNING: Block position failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
- if(sqlite3_bind_blob(m_database_write, 2, (void *)bytes, o.tellp(), NULL) != SQLITE_OK) // TODO this mught not be the right length
+ success = false;
+ }
+ if(sqlite3_bind_blob(m_database_write, 2, (void *)bytes, o.tellp(), NULL) != SQLITE_OK) { // TODO this mught not be the right length
infostream<<"WARNING: Block data failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
+ success = false;
+ }
int written = sqlite3_step(m_database_write);
- if(written != SQLITE_DONE)
- infostream<<"WARNING: Block failed to save ("<<p3d.X<<", "<<p3d.Y<<", "<<p3d.Z<<") "
- <<sqlite3_errmsg(m_database)<<std::endl;
+ if(written != SQLITE_DONE) {
+ errorstream<<"WARNING: Block failed to save ("<<p3d.X<<", "<<p3d.Y<<", "<<p3d.Z<<") "
+ <<sqlite3_errmsg(m_database)<<std::endl;
+ success = false;
+ }
// Make ready for later reuse
sqlite3_reset(m_database_write);
-
+
// We just wrote it to the disk so clear modified flag
- block->resetModified();
+ if (success)
+ block->resetModified();
}
void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load)
@@ -3369,12 +3404,12 @@ void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSecto
std::ifstream is(fullpath.c_str(), std::ios_base::binary);
if(is.good() == false)
throw FileNotGoodException("Cannot open block file");
-
+
v3s16 p3d = getBlockPos(sectordir, blockfile);
v2s16 p2d(p3d.X, p3d.Z);
-
+
assert(sector->getPos() == p2d);
-
+
u8 version = SER_FMT_VER_INVALID;
is.read((char*)&version, 1);
@@ -3397,14 +3432,14 @@ void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSecto
block = sector->createBlankBlockNoInsert(p3d.Y);
created_new = true;
}
-
+
// Read basic data
block->deSerialize(is, version, true);
// If it's a new block, insert it to the map
if(created_new)
sector->insertBlock(block);
-
+
/*
Save blocks loaded in old format in new format
*/
@@ -3412,11 +3447,11 @@ void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSecto
if(version < SER_FMT_VER_HIGHEST || save_after_load)
{
saveBlock(block);
-
+
// Should be in database now, so delete the old file
fs::RecursiveDelete(fullpath);
}
-
+
// We just loaded it from the disk, so it's up-to-date.
block->resetModified();
@@ -3441,7 +3476,7 @@ void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool
try {
std::istringstream is(*blob, std::ios_base::binary);
-
+
u8 version = SER_FMT_VER_INVALID;
is.read((char*)&version, 1);
@@ -3464,14 +3499,14 @@ void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool
block = sector->createBlankBlockNoInsert(p3d.Y);
created_new = true;
}
-
+
// Read basic data
block->deSerialize(is, version, true);
-
+
// If it's a new block, insert it to the map
if(created_new)
sector->insertBlock(block);
-
+
/*
Save blocks loaded in old format in new format
*/
@@ -3480,7 +3515,7 @@ void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool
// Only save if asked to; no need to update version
if(save_after_load)
saveBlock(block);
-
+
// We just loaded it from, so it's up-to-date.
block->resetModified();
@@ -3490,7 +3525,7 @@ void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool
errorstream<<"Invalid block data in database"
<<" ("<<p3d.X<<","<<p3d.Y<<","<<p3d.Z<<")"
<<" (SerializationError): "<<e.what()<<std::endl;
-
+
// TODO: Block should be marked as invalid in memory so that it is
// not touched but the game can run
@@ -3512,7 +3547,7 @@ MapBlock* ServerMap::loadBlock(v3s16 blockpos)
if(!loadFromFolders()) {
verifyDatabase();
-
+
if(sqlite3_bind_int64(m_database_read, 1, getBlockAsInteger(blockpos)) != SQLITE_OK)
infostream<<"WARNING: Could not bind block position for load: "
<<sqlite3_errmsg(m_database)<<std::endl;
@@ -3521,15 +3556,15 @@ MapBlock* ServerMap::loadBlock(v3s16 blockpos)
Make sure sector is loaded
*/
MapSector *sector = createSector(p2d);
-
+
/*
Load block
*/
const char * data = (const char *)sqlite3_column_blob(m_database_read, 0);
size_t len = sqlite3_column_bytes(m_database_read, 0);
-
+
std::string datastr(data, len);
-
+
loadBlock(&datastr, blockpos, sector, false);
sqlite3_step(m_database_read);
@@ -3539,7 +3574,7 @@ MapBlock* ServerMap::loadBlock(v3s16 blockpos)
return getBlockNoCreateNoEx(blockpos);
}
sqlite3_reset(m_database_read);
-
+
// Not found in database, try the files
}
@@ -3560,7 +3595,7 @@ MapBlock* ServerMap::loadBlock(v3s16 blockpos)
loadlayout = 2;
sectordir = getSectorDir(p2d, 2);
}
-
+
/*
Make sure sector is loaded
*/
@@ -3583,7 +3618,7 @@ MapBlock* ServerMap::loadBlock(v3s16 blockpos)
return NULL;
}
}
-
+
/*
Make sure file exists
*/
@@ -3641,7 +3676,7 @@ void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
n = m_loaded_blocks.find(p);
if(n != NULL)
continue;
-
+
bool block_data_inexistent = false;
try
{
@@ -3652,7 +3687,7 @@ void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
<<" wanted area: ";
a.print(infostream);
infostream<<std::endl;*/
-
+
MapBlock *block = m_map->getBlockNoCreate(p);
if(block->isDummy())
block_data_inexistent = true;
@@ -3692,12 +3727,12 @@ void MapVoxelManipulator::blitBack
{
if(m_area.getExtent() == v3s16(0,0,0))
return;
-
+
//TimeTaker timer1("blitBack");
/*infostream<<"blitBack(): m_loaded_blocks.size()="
<<m_loaded_blocks.size()<<std::endl;*/
-
+
/*
Initialize block cache
*/
@@ -3716,9 +3751,9 @@ void MapVoxelManipulator::blitBack
continue;
MapNode &n = m_data[m_area.index(p)];
-
+
v3s16 blockpos = getNodeBlockPos(p);
-
+
try
{
// Get block
@@ -3727,7 +3762,7 @@ void MapVoxelManipulator::blitBack
blockpos_last = blockpos;
block_checked_in_modified = false;
}
-
+
// Calculate relative position in block
v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
@@ -3737,7 +3772,7 @@ void MapVoxelManipulator::blitBack
//m_map->setNode(m_area.MinEdge + p, n);
block->setNode(relpos, n);
-
+
/*
Make sure block is in modified_blocks
*/
@@ -3780,7 +3815,7 @@ void ManualMapVoxelManipulator::initialEmerge(
VoxelArea block_area_nodes
(p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
-
+
u32 size_MB = block_area_nodes.getVolume()*4/1000000;
if(size_MB >= 1)
{
@@ -3801,7 +3836,7 @@ void ManualMapVoxelManipulator::initialEmerge(
n = m_loaded_blocks.find(p);
if(n != NULL)
continue;
-
+
bool block_data_inexistent = false;
try
{
@@ -3842,7 +3877,7 @@ void ManualMapVoxelManipulator::blitBackAll(
{
if(m_area.getExtent() == v3s16(0,0,0))
return;
-
+
/*
Copy data of all blocks
*/
diff --git a/src/map.h b/src/map.h
index 30cf626bb..420fc29ca 100644
--- a/src/map.h
+++ b/src/map.h
@@ -30,6 +30,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "mapnode.h"
#include "constants.h"
#include "voxel.h"
+#include "mapgen.h" //for BlockMakeData and EmergeManager
#include "modifiedstate.h"
#include "util/container.h"
#include "nodetimer.h"
@@ -46,9 +47,6 @@ class NodeMetadata;
class IGameDef;
class IRollbackReportSink;
-namespace mapgen{
- struct BlockMakeData;
-};
/*
MapEditEvent
@@ -83,7 +81,7 @@ struct MapEditEvent
already_known_by_peer(0)
{
}
-
+
MapEditEvent * clone()
{
MapEditEvent *event = new MapEditEvent();
@@ -157,7 +155,7 @@ public:
{
return MAPTYPE_BASE;
}
-
+
/*
Drop (client) or delete (server) the map.
*/
@@ -170,7 +168,7 @@ public:
void removeEventReceiver(MapEventReceiver *event_receiver);
// event shall be deleted by caller after the call.
void dispatchEvent(MapEditEvent *event);
-
+
// On failure returns NULL
MapSector * getSectorNoGenerateNoExNoLock(v2s16 p2d);
// Same as the above (there exists no lock anymore)
@@ -192,22 +190,22 @@ public:
MapBlock * getBlockNoCreate(v3s16 p);
// Returns NULL if not found
MapBlock * getBlockNoCreateNoEx(v3s16 p);
-
+
/* Server overrides */
virtual MapBlock * emergeBlock(v3s16 p, bool allow_generate=true)
{ return getBlockNoCreateNoEx(p); }
// Returns InvalidPositionException if not found
bool isNodeUnderground(v3s16 p);
-
+
bool isValidPosition(v3s16 p);
-
+
// throws InvalidPositionException if not found
MapNode getNode(v3s16 p);
// throws InvalidPositionException if not found
void setNode(v3s16 p, MapNode & n);
-
+
// Returns a CONTENT_IGNORE node if not found
MapNode getNodeNoEx(v3s16 p);
@@ -220,11 +218,11 @@ public:
v3s16 pos, u8 lightwas,
core::map<v3s16, bool> & light_sources,
core::map<v3s16, MapBlock*> & modified_blocks);
-
+
void spreadLight(enum LightBank bank,
core::map<v3s16, bool> & from_nodes,
core::map<v3s16, MapBlock*> & modified_blocks);
-
+
void lightNeighbors(enum LightBank bank,
v3s16 pos,
core::map<v3s16, MapBlock*> & modified_blocks);
@@ -233,14 +231,14 @@ public:
s16 propagateSunlight(v3s16 start,
core::map<v3s16, MapBlock*> & modified_blocks);
-
+
void updateLighting(enum LightBank bank,
core::map<v3s16, MapBlock*> & a_blocks,
core::map<v3s16, MapBlock*> & modified_blocks);
-
+
void updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
core::map<v3s16, MapBlock*> & modified_blocks);
-
+
/*
These handle lighting but not faces.
*/
@@ -256,7 +254,7 @@ public:
*/
bool addNodeWithEvent(v3s16 p, MapNode n);
bool removeNodeWithEvent(v3s16 p);
-
+
/*
Takes the blocks at the edges into account
*/
@@ -269,9 +267,9 @@ public:
// Call these before and after saving of many blocks
virtual void beginSave() {return;};
virtual void endSave() {return;};
-
+
virtual void save(ModifiedState save_level){assert(0);};
-
+
// Server implements this.
// Client leaves it as no-op.
virtual void saveBlock(MapBlock *block){};
@@ -282,7 +280,7 @@ public:
*/
void timerUpdate(float dtime, float unload_timeout,
core::list<v3s16> *unloaded_blocks=NULL);
-
+
// Deletes sectors and their blocks from memory
// Takes cache into account
// If deleted sector is in sector cache, clears cache
@@ -300,14 +298,14 @@ public:
// For debug printing. Prints "Map: ", "ServerMap: " or "ClientMap: "
virtual void PrintInfo(std::ostream &out);
-
+
void transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks);
/*
Node metadata
These are basically coordinate wrappers to MapBlock
*/
-
+
NodeMetadata* getNodeMetadata(v3s16 p);
void setNodeMetadata(v3s16 p, NodeMetadata *meta);
void removeNodeMetadata(v3s16 p);
@@ -316,7 +314,7 @@ public:
Node Timers
These are basically coordinate wrappers to MapBlock
*/
-
+
NodeTimer getNodeTimer(v3s16 p);
void setNodeTimer(v3s16 p, NodeTimer t);
void removeNodeTimer(v3s16 p);
@@ -329,7 +327,7 @@ public:
/*
Variables
*/
-
+
protected:
std::ostream &m_dout; // A bit deprecated, could be removed
@@ -340,7 +338,7 @@ protected:
core::map<v2s16, MapSector*> m_sectors;
- // Be sure to set this to NULL when the cached sector is deleted
+ // Be sure to set this to NULL when the cached sector is deleted
MapSector *m_sector_cache;
v2s16 m_sector_cache_p;
@@ -360,7 +358,7 @@ public:
/*
savedir: directory to which map data should be saved
*/
- ServerMap(std::string savedir, IGameDef *gamedef);
+ ServerMap(std::string savedir, IGameDef *gamedef, EmergeManager *emerge);
~ServerMap();
s32 mapType() const
@@ -379,16 +377,16 @@ public:
/*
Blocks are generated by using these and makeBlock().
*/
- void initBlockMake(mapgen::BlockMakeData *data, v3s16 blockpos);
- MapBlock* finishBlockMake(mapgen::BlockMakeData *data,
+ void initBlockMake(BlockMakeData *data, v3s16 blockpos);
+ MapBlock* finishBlockMake(BlockMakeData *data,
core::map<v3s16, MapBlock*> &changed_blocks);
-
- // A non-threaded wrapper to the above
- MapBlock * generateBlock(
+
+ // A non-threaded wrapper to the above - DEFUNCT
+/* MapBlock * generateBlock(
v3s16 p,
core::map<v3s16, MapBlock*> &modified_blocks
- );
-
+ );*/
+
/*
Get a block from somewhere.
- Memory
@@ -400,10 +398,11 @@ public:
Forcefully get a block from somewhere.
- Memory
- Load from disk
- - Generate
+ - Create blank filled with CONTENT_IGNORE
+
*/
- MapBlock * emergeBlock(v3s16 p, bool allow_generate=true);
-
+ MapBlock * emergeBlock(v3s16 p, bool create_blank=true);
+
// Helper for placing objects on ground level
s16 findGroundLevel(v2s16 p2d);
@@ -439,32 +438,32 @@ public:
void save(ModifiedState save_level);
//void loadAll();
-
+
void listAllLoadableBlocks(core::list<v3s16> &dst);
-
+
// Saves map seed and possibly other stuff
void saveMapMeta();
void loadMapMeta();
-
+
/*void saveChunkMeta();
void loadChunkMeta();*/
-
+
// The sector mutex should be locked when calling most of these
-
+
// This only saves sector-specific data such as the heightmap
// (no MapBlocks)
// DEPRECATED? Sectors have no metadata anymore.
void saveSectorMeta(ServerMapSector *sector);
MapSector* loadSectorMeta(std::string dirname, bool save_after_load);
bool loadSectorMeta(v2s16 p2d);
-
+
// Full load of a sector including all blocks.
// returns true on success, false on failure.
bool loadSectorFull(v2s16 p2d);
// If sector is not found in memory, try to load it from disk.
// Returns true if sector now resides in memory
//bool deFlushSector(v2s16 p2d);
-
+
void saveBlock(MapBlock *block);
// This will generate a sector with getSector if not found.
void loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load=false);
@@ -479,10 +478,17 @@ public:
u64 getSeed(){ return m_seed; }
+ MapgenParams *getMapgenParams(){ return m_mgparams; }
+
+ // Parameters fed to the Mapgen
+ MapgenParams *m_mgparams;
private:
// Seed used for all kinds of randomness in generation
u64 m_seed;
+ // Emerge manager
+ EmergeManager *m_emerge;
+
std::string m_savedir;
bool m_map_saving_enabled;
@@ -499,7 +505,7 @@ private:
This is reset to false when written on disk.
*/
bool m_map_metadata_changed;
-
+
/*
SQLite database and statements
*/
@@ -514,7 +520,7 @@ class MapVoxelManipulator : public VoxelManipulator
public:
MapVoxelManipulator(Map *map);
virtual ~MapVoxelManipulator();
-
+
virtual void clear()
{
VoxelManipulator::clear();
@@ -542,11 +548,11 @@ public:
void setMap(Map *map)
{m_map = map;}
-
+
virtual void emerge(VoxelArea a, s32 caller_id=-1);
void initialEmerge(v3s16 blockpos_min, v3s16 blockpos_max);
-
+
// This is much faster with big chunks of generated data
void blitBackAll(core::map<v3s16, MapBlock*> * modified_blocks);
diff --git a/src/mapgen.cpp b/src/mapgen.cpp
index ae0c551cb..b19073e90 100644
--- a/src/mapgen.cpp
+++ b/src/mapgen.cpp
@@ -20,7 +20,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "mapgen.h"
#include "voxel.h"
#include "noise.h"
+#include "biome.h"
#include "mapblock.h"
+#include "mapnode.h"
#include "map.h"
//#include "serverobject.h"
#include "content_sao.h"
@@ -28,9 +30,233 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "content_mapnode.h" // For content_mapnode_get_new_name
#include "voxelalgorithms.h"
#include "profiler.h"
+#include "settings.h" // For g_settings
#include "main.h" // For g_profiler
#include "treegen.h"
+#include "mapgen_v6.h"
+
+FlagDesc flagdesc_mapgen[] = {
+ {"trees", MG_TREES},
+ {"caves", MG_CAVES},
+ {"dungeons", MG_DUNGEONS},
+ {"v6_forests", MGV6_FORESTS},
+ {"v6_biome_blend", MGV6_BIOME_BLEND},
+ {"flat", MG_FLAT},
+ {NULL, 0}
+};
+
+///////////////////////////////////////////////////////////////////////////////
+/////////////////////////////// Emerge Manager ////////////////////////////////
+
+
+EmergeManager::EmergeManager(IGameDef *gamedef, BiomeDefManager *bdef) {
+ //register built-in mapgens
+ registerMapgen("v6", new MapgenFactoryV6());
+
+ //the order of these assignments is pretty important
+ this->biomedef = bdef ? bdef : new BiomeDefManager(gamedef);
+ this->params = NULL;
+ this->mapgen = NULL;
+}
+
+
+EmergeManager::~EmergeManager() {
+ delete biomedef;
+ delete mapgen;
+ delete params;
+}
+
+
+void EmergeManager::initMapgens(MapgenParams *mgparams) {
+ if (mapgen)
+ return;
+
+ this->params = mgparams;
+ this->mapgen = getMapgen(); //only one mapgen for now!
+}
+
+Mapgen *EmergeManager::getMapgen() {
+ if (!mapgen) {
+ mapgen = createMapgen(params->mg_name, 0, params, this);
+ if (!mapgen) {
+ infostream << "EmergeManager: falling back to mapgen v6" << std::endl;
+ delete params;
+ params = createMapgenParams("v6");
+ mapgen = createMapgen("v6", 0, params, this);
+ }
+ }
+ return mapgen;
+}
+
+void EmergeManager::addBlockToQueue() {
+ //STUB
+}
+
+
+int EmergeManager::getGroundLevelAtPoint(v2s16 p) {
+ if (!mapgen)
+ return 0;
+ return mapgen->getGroundLevelAtPoint(p);
+}
+
+
+bool EmergeManager::isBlockUnderground(v3s16 blockpos) {
+ /*
+ 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);
+ */
+
+ //yuck, but then again, should i bother being accurate?
+ //the height of the nodes in a single block is quite variable
+ return blockpos.Y * (MAP_BLOCKSIZE + 1) <= params->water_level;
+}
+
+
+u32 EmergeManager::getBlockSeed(v3s16 p) {
+ return (u32)(params->seed & 0xFFFFFFFF) +
+ p.Z * 38134234 +
+ p.Y * 42123 +
+ p.Y * 23;
+}
+
+
+Mapgen *EmergeManager::createMapgen(std::string mgname, int mgid,
+ MapgenParams *mgparams, EmergeManager *emerge) {
+ std::map<std::string, MapgenFactory *>::const_iterator iter = mglist.find(mgname);
+ if (iter == mglist.end()) {
+ errorstream << "EmergeManager; mapgen " << mgname <<
+ " not registered" << std::endl;
+ return NULL;
+ }
+
+ MapgenFactory *mgfactory = iter->second;
+ return mgfactory->createMapgen(mgid, mgparams, emerge);
+}
+
+
+MapgenParams *EmergeManager::createMapgenParams(std::string mgname) {
+ std::map<std::string, MapgenFactory *>::const_iterator iter = mglist.find(mgname);
+ if (iter == mglist.end()) {
+ errorstream << "EmergeManager: mapgen " << mgname <<
+ " not registered" << std::endl;
+ return NULL;
+ }
+
+ MapgenFactory *mgfactory = iter->second;
+ return mgfactory->createMapgenParams();
+}
+
+
+MapgenParams *EmergeManager::getParamsFromSettings(Settings *settings) {
+ std::string mg_name = settings->get("mg_name");
+ MapgenParams *mgparams = createMapgenParams(mg_name);
+
+ mgparams->mg_name = mg_name;
+ mgparams->seed = settings->getU64(settings == g_settings ? "fixed_map_seed" : "seed");
+ mgparams->water_level = settings->getS16("water_level");
+ mgparams->chunksize = settings->getS16("chunksize");
+ mgparams->flags = settings->getFlagStr("mg_flags", flagdesc_mapgen);
+
+ if (!mgparams->readParams(settings)) {
+ delete mgparams;
+ return NULL;
+ }
+ return mgparams;
+}
+
+
+void EmergeManager::setParamsToSettings(Settings *settings) {
+ settings->set("mg_name", params->mg_name);
+ settings->setU64("seed", params->seed);
+ settings->setS16("water_level", params->water_level);
+ settings->setS16("chunksize", params->chunksize);
+ settings->setFlagStr("mg_flags", params->flags, flagdesc_mapgen);
+
+ params->writeParams(settings);
+}
+
+
+void EmergeManager::registerMapgen(std::string mgname, MapgenFactory *mgfactory) {
+ mglist.insert(std::make_pair(mgname, mgfactory));
+ infostream << "EmergeManager: registered mapgen " << mgname << std::endl;
+}
+
+
+/////////////////////
+
+bool MapgenV6Params::readParams(Settings *settings) {
+ freq_desert = settings->getFloat("mgv6_freq_desert");
+ freq_beach = settings->getFloat("mgv6_freq_beach");
+
+ np_terrain_base = settings->getNoiseParams("mgv6_np_terrain_base");
+ np_terrain_higher = settings->getNoiseParams("mgv6_np_terrain_higher");
+ np_steepness = settings->getNoiseParams("mgv6_np_steepness");
+ np_height_select = settings->getNoiseParams("mgv6_np_height_select");
+ np_trees = settings->getNoiseParams("mgv6_np_trees");
+ np_mud = settings->getNoiseParams("mgv6_np_mud");
+ np_beach = settings->getNoiseParams("mgv6_np_beach");
+ np_biome = settings->getNoiseParams("mgv6_np_biome");
+ np_cave = settings->getNoiseParams("mgv6_np_cave");
+
+ bool success =
+ np_terrain_base && np_terrain_higher && np_steepness &&
+ np_height_select && np_trees && np_mud &&
+ np_beach && np_biome && np_cave;
+ return success;
+}
+
+
+void MapgenV6Params::writeParams(Settings *settings) {
+ settings->setFloat("mgv6_freq_desert", freq_desert);
+ settings->setFloat("mgv6_freq_beach", freq_beach);
+
+ settings->setNoiseParams("mgv6_np_terrain_base", np_terrain_base);
+ settings->setNoiseParams("mgv6_np_terrain_higher", np_terrain_higher);
+ settings->setNoiseParams("mgv6_np_steepness", np_steepness);
+ settings->setNoiseParams("mgv6_np_height_select", np_height_select);
+ settings->setNoiseParams("mgv6_np_trees", np_trees);
+ settings->setNoiseParams("mgv6_np_mud", np_mud);
+ settings->setNoiseParams("mgv6_np_beach", np_beach);
+ settings->setNoiseParams("mgv6_np_biome", np_biome);
+ settings->setNoiseParams("mgv6_np_cave", np_cave);
+}
+
+
+/////////////////////////////////// legacy static functions for farmesh
+
+
+s16 Mapgen::find_ground_level_from_noise(u64 seed, v2s16 p2d, s16 precision) {
+ //just need to return something
+ s16 level = 5;
+ return level;
+}
+
+
+bool Mapgen::get_have_beach(u64 seed, v2s16 p2d) {
+ double sandnoise = noise2d_perlin(
+ 0.2+(float)p2d.X/250, 0.7+(float)p2d.Y/250,
+ seed+59420, 3, 0.50);
+
+ return (sandnoise > 0.15);
+}
+
+
+double Mapgen::tree_amount_2d(u64 seed, v2s16 p) {
+ double noise = noise2d_perlin(
+ 0.5+(float)p.X/125, 0.5+(float)p.Y/125,
+ seed+2, 4, 0.66);
+ double zeroval = -0.39;
+ if(noise < zeroval)
+ return 0;
+ else
+ return 0.04 * (noise-zeroval) / (1.0-zeroval);
+}
+
+
+#if 0 /// BIG COMMENT
namespace mapgen
{
@@ -121,6 +347,7 @@ static s16 find_stone_level(VoxelManipulator &vmanip, v2s16 p2d,
}
#endif
+
#if 0
static void make_papyrus(VoxelManipulator &vmanip, v3s16 p0,
@@ -190,7 +417,7 @@ static void make_room1(VoxelManipulator &vmanip, v3s16 roomsize, v3s16 roomplace
vmanip.m_data[vi] = MapNode(ndef->getId("mapgen_cobble"));
}
}
-
+
// Make +-Z walls
for(s16 x=0; x<roomsize.X; x++)
for(s16 y=0; y<roomsize.Y; y++)
@@ -214,7 +441,7 @@ static void make_room1(VoxelManipulator &vmanip, v3s16 roomsize, v3s16 roomplace
vmanip.m_data[vi] = MapNode(ndef->getId("mapgen_cobble"));
}
}
-
+
// Make +-Y walls (floor and ceiling)
for(s16 z=0; z<roomsize.Z; z++)
for(s16 x=0; x<roomsize.X; x++)
@@ -238,7 +465,7 @@ static void make_room1(VoxelManipulator &vmanip, v3s16 roomsize, v3s16 roomplace
vmanip.m_data[vi] = MapNode(ndef->getId("mapgen_cobble"));
}
}
-
+
// Fill with air
for(s16 z=1; z<roomsize.Z-1; z++)
for(s16 y=1; y<roomsize.Y-1; y++)
@@ -401,9 +628,9 @@ static void make_corridor(VoxelManipulator &vmanip, v3s16 doorplace,
if(partcount >= partlength)
{
partcount = 0;
-
+
dir = random_turn(random, dir);
-
+
partlength = random.range(1,length);
make_stairs = 0;
@@ -443,7 +670,7 @@ public:
{
m_dir = dir;
}
-
+
bool findPlaceForDoor(v3s16 &result_place, v3s16 &result_dir)
{
for(u32 i=0; i<100; i++)
@@ -540,7 +767,7 @@ public:
if(doordir == v3s16(0,0,-1)) // Z-
roomplace = doorplace + v3s16(-roomsize.X/2,-1,-roomsize.Z+1);
#endif
-
+
// Check fit
bool fits = true;
for(s16 z=1; z<roomsize.Z-1; z++)
@@ -587,7 +814,7 @@ static void make_dungeon1(VoxelManipulator &vmanip, PseudoRandom &random,
v3s16 areasize = vmanip.m_area.getExtent();
v3s16 roomsize;
v3s16 roomplace;
-
+
/*
Find place for first room
*/
@@ -627,20 +854,20 @@ static void make_dungeon1(VoxelManipulator &vmanip, PseudoRandom &random,
// No place found
if(fits == false)
return;
-
+
/*
Stores the center position of the last room made, so that
a new corridor can be started from the last room instead of
the new room, if chosen so.
*/
v3s16 last_room_center = roomplace+v3s16(roomsize.X/2,1,roomsize.Z/2);
-
+
u32 room_count = random.range(2,7);
for(u32 i=0; i<room_count; i++)
{
// Make a room to the determined place
make_room1(vmanip, roomsize, roomplace, ndef);
-
+
v3s16 room_center = roomplace + v3s16(roomsize.X/2,1,roomsize.Z/2);
// Place torch at room center (for testing)
@@ -649,7 +876,7 @@ static void make_dungeon1(VoxelManipulator &vmanip, PseudoRandom &random,
// Quit if last room
if(i == room_count-1)
break;
-
+
// Determine walker start position
bool start_in_last_room = (random.range(0,2)!=0);
@@ -667,7 +894,7 @@ static void make_dungeon1(VoxelManipulator &vmanip, PseudoRandom &random,
// Store center of current room as the last one
last_room_center = room_center;
}
-
+
// Create walker and find a place for a door
RoomWalker walker(vmanip, walker_start_place, random, ndef);
v3s16 doorplace;
@@ -675,20 +902,20 @@ static void make_dungeon1(VoxelManipulator &vmanip, PseudoRandom &random,
bool r = walker.findPlaceForDoor(doorplace, doordir);
if(r == false)
return;
-
+
if(random.range(0,1)==0)
// Make the door
make_door1(vmanip, doorplace, doordir, ndef);
else
// Don't actually make a door
doorplace -= doordir;
-
+
// Make a random corridor starting from the door
v3s16 corridor_end;
v3s16 corridor_end_dir;
make_corridor(vmanip, doorplace, doordir, corridor_end,
corridor_end_dir, random, ndef);
-
+
// Find a place for a random sized room
roomsize = v3s16(random.range(4,8),random.range(4,6),random.range(4,8));
walker.setPos(corridor_end);
@@ -703,7 +930,7 @@ static void make_dungeon1(VoxelManipulator &vmanip, PseudoRandom &random,
else
// Don't actually make a door
roomplace -= doordir;
-
+
}
}
#endif
@@ -926,7 +1153,7 @@ s16 find_ground_level_from_noise(u64 seed, v2s16 p2d, s16 precision)
}
}
}
-
+
// This is more like the actual ground level
level += dec[i-1]/2;
@@ -1024,7 +1251,7 @@ bool block_is_underground(u64 seed, v3s16 blockpos)
seed, v2s16(blockpos.X, blockpos.Z));*/
// Nah, this is just a heuristic, just return something
s16 minimum_groundlevel = WATER_LEVEL;
-
+
if(blockpos.Y*MAP_BLOCKSIZE + MAP_BLOCKSIZE <= minimum_groundlevel)
return true;
else
@@ -1132,9 +1359,9 @@ BiomeType get_biome(u64 seed, v2s16 p2d)
double d = noise2d_perlin(
0.6+(float)p2d.X/250, 0.2+(float)p2d.Y/250,
seed+9130, 3, 0.50);
- if(d > 0.45)
+ if(d > 0.45)
return BT_DESERT;
- if(d > 0.35 && (noise2d( p2d.X, p2d.Y, int(seed) ) + 1.0) > ( 0.45 - d ) * 20.0 )
+ if(d > 0.35 && (noise2d( p2d.X, p2d.Y, int(seed) ) + 1.0) > ( 0.45 - d ) * 20.0 )
return BT_DESERT;
return BT_NORMAL;
};
@@ -1169,7 +1396,7 @@ void make_block(BlockMakeData *data)
// Hack: use minimum block coordinates for old code that assumes
// a single block
v3s16 blockpos = data->blockpos_requested;
-
+
/*dstream<<"makeBlock(): ("<<blockpos.X<<","<<blockpos.Y<<","
<<blockpos.Z<<")"<<std::endl;*/
@@ -1177,7 +1404,7 @@ void make_block(BlockMakeData *data)
v3s16 blockpos_max = data->blockpos_max;
v3s16 blockpos_full_min = blockpos_min - v3s16(1,1,1);
v3s16 blockpos_full_max = blockpos_max + v3s16(1,1,1);
-
+
ManualMapVoxelManipulator &vmanip = *(data->vmanip);
// Area of central chunk
v3s16 node_min = blockpos_min*MAP_BLOCKSIZE;
@@ -1193,10 +1420,10 @@ void make_block(BlockMakeData *data)
int volume_blocks = (blockpos_max.X - blockpos_min.X + 1)
* (blockpos_max.Y - blockpos_min.Y + 1)
* (blockpos_max.Z - blockpos_max.Z + 1);
-
+
int volume_nodes = volume_blocks *
MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
-
+
// Generated surface area
//double gen_area_nodes = MAP_BLOCKSIZE*MAP_BLOCKSIZE * rel_volume;
@@ -1207,7 +1434,7 @@ void make_block(BlockMakeData *data)
Create a block-specific seed
*/
u32 blockseed = get_blockseed(data->seed, full_node_min);
-
+
/*
Cache some ground type values for speed
*/
@@ -1253,13 +1480,13 @@ void make_block(BlockMakeData *data)
{
#if 1
TimeTaker timer1("Generating ground level");
-
+
for(s16 x=node_min.X; x<=node_max.X; x++)
for(s16 z=node_min.Z; z<=node_max.Z; z++)
{
// Node position
v2s16 p2d = v2s16(x,z);
-
+
/*
Skip of already generated
*/
@@ -1274,7 +1501,7 @@ void make_block(BlockMakeData *data)
// Use perlin noise for ground height
surface_y_f = base_rock_level_2d(data->seed, p2d);
-
+
/*// Experimental stuff
{
float a = highlands_level_2d(data->seed, p2d);
@@ -1284,7 +1511,7 @@ void make_block(BlockMakeData *data)
// Convert to integer
s16 surface_y = (s16)surface_y_f;
-
+
// Log it
if(surface_y > stone_surface_max_y)
stone_surface_max_y = surface_y;
@@ -1316,9 +1543,9 @@ void make_block(BlockMakeData *data)
}
}
#endif
-
+
}//timer1
-
+
// Limit dirt flow area by 1 because mud is flown into neighbors.
assert(central_area_size.X == central_area_size.Z);
s16 mudflow_minpos = 0-max_spread_amount+1;
@@ -1375,7 +1602,7 @@ void make_block(BlockMakeData *data)
tunnel_routepoints = ps.range(10, ps.range(15,30));
}
bool large_cave_is_flat = (ps.range(0,1) == 0);
-
+
v3f main_direction(0,0,0);
// Allowed route area size in nodes
@@ -1391,7 +1618,7 @@ void make_block(BlockMakeData *data)
s16 more = max_spread_amount - max_tunnel_diameter/2 - insure;
ar += v3s16(1,0,1) * more * 2;
of -= v3s16(1,0,1) * more;
-
+
s16 route_y_min = 0;
// Allow half a diameter + 7 over stone surface
s16 route_y_max = -of.Y + stone_surface_max_y + max_tunnel_diameter/2 + 7;
@@ -1434,7 +1661,7 @@ void make_block(BlockMakeData *data)
if(coming_from_surface)
route_start_y_min = -of.Y + stone_surface_max_y + 10;
}*/
-
+
route_start_y_min = rangelim(route_start_y_min, 0, ar.Y-1);
route_start_y_max = rangelim(route_start_y_max, route_start_y_min, ar.Y-1);
@@ -1451,11 +1678,11 @@ void make_block(BlockMakeData *data)
MapNode airnode(CONTENT_AIR);
MapNode waternode(c_water_source);
MapNode lavanode(c_lava_source);
-
+
/*
Generate some tunnel starting from orp
*/
-
+
for(u16 j=0; j<tunnel_routepoints; j++)
{
if(j%dswitchint==0 && large_cave == false)
@@ -1467,12 +1694,12 @@ void make_block(BlockMakeData *data)
);
main_direction *= (float)ps.range(0, 10)/10;
}
-
+
// Randomize size
s16 min_d = min_tunnel_diameter;
s16 max_d = max_tunnel_diameter;
s16 rs = ps.range(min_d, max_d);
-
+
// Every second section is rough
bool randomize_xz = (ps2.range(1,2) == 1);
@@ -1495,13 +1722,13 @@ void make_block(BlockMakeData *data)
}
v3f vec;
-
+
vec = v3f(
(float)(ps.next()%(maxlen.X*1))-(float)maxlen.X/2,
(float)(ps.next()%(maxlen.Y*1))-(float)maxlen.Y/2,
(float)(ps.next()%(maxlen.Z*1))-(float)maxlen.Z/2
);
-
+
// Jump downward sometimes
if(!large_cave && ps.range(0,12) == 0)
{
@@ -1511,7 +1738,7 @@ void make_block(BlockMakeData *data)
(float)(ps.next()%(maxlen.Z*1))-(float)maxlen.Z/2
);
}
-
+
/*if(large_cave){
v3f p = orp + vec;
s16 h = find_ground_level_clever(vmanip,
@@ -1573,12 +1800,12 @@ void make_block(BlockMakeData *data)
s16 x = cp.X + x0;
v3s16 p(x,y,z);
p += of;
-
+
if(vmanip.m_area.contains(p) == false)
continue;
-
+
u32 i = vmanip.m_area.index(p);
-
+
if(large_cave)
{
if(full_node_min.Y < WATER_LEVEL &&
@@ -1602,7 +1829,7 @@ void make_block(BlockMakeData *data)
vmanip.m_data[i].getContent() == c_water_source ||
vmanip.m_data[i].getContent() == c_lava_source)
continue;
-
+
vmanip.m_data[i] = airnode;
// Set tunnel flag
@@ -1615,12 +1842,12 @@ void make_block(BlockMakeData *data)
orp = rp;
}
-
+
}
}//timer1
#endif
-
+
#if 1
{
// 15ms @cs=8
@@ -1629,13 +1856,13 @@ void make_block(BlockMakeData *data)
/*
Add mud to the central chunk
*/
-
+
for(s16 x=node_min.X; x<=node_max.X; x++)
for(s16 z=node_min.Z; z<=node_max.Z; z++)
{
// Node position in 2d
v2s16 p2d = v2s16(x,z);
-
+
// Randomize mud amount
s16 mud_add_amount = get_mud_add_amount(data->seed, p2d) / 2.0 + 0.5;
@@ -1660,7 +1887,7 @@ void make_block(BlockMakeData *data)
surface_y + mud_add_amount <= WATER_LEVEL+2){
addnode = MapNode(c_sand);
}
-
+
if(bt == BT_DESERT){
if(surface_y > 20){
mud_add_amount = MYMAX(0, mud_add_amount - (surface_y - 20)/5);
@@ -1691,7 +1918,7 @@ void make_block(BlockMakeData *data)
{
if(mudcount >= mud_add_amount)
break;
-
+
MapNode &n = vmanip.m_data[i];
n = addnode;
mudcount++;
@@ -1756,7 +1983,7 @@ void make_block(BlockMakeData *data)
/*
Flow mud away from steep edges
*/
-
+
// Iterate a few times
for(s16 k=0; k<3; k++)
{
@@ -1773,7 +2000,7 @@ void make_block(BlockMakeData *data)
// Node position in 2d
v2s16 p2d = v2s16(node_min.X, node_min.Z) + v2s16(x,z);
-
+
v3s16 em = vmanip.m_area.getExtent();
u32 i = vmanip.m_area.index(v3s16(p2d.X, node_max.Y, p2d.Y));
s16 y=node_max.Y;
@@ -1794,7 +2021,7 @@ void make_block(BlockMakeData *data)
n->getContent() == c_dirt_with_grass ||
n->getContent() == c_gravel)
break;
-
+
vmanip.m_area.add_y(em, i, -1);
}
@@ -1813,7 +2040,7 @@ void make_block(BlockMakeData *data)
{
// Make it exactly mud
n->setContent(c_dirt);
-
+
/*
Don't flow it if the stuff under it is not mud
*/
@@ -1851,7 +2078,7 @@ void make_block(BlockMakeData *data)
}
// Drop mud on side
-
+
for(u32 di=0; di<4; di++)
{
v3s16 dirp = dirs4[di];
@@ -1894,7 +2121,7 @@ void make_block(BlockMakeData *data)
// Loop one up so that we're in air
vmanip.m_area.add_y(em, i2, 1);
n2 = &vmanip.m_data[i2];
-
+
bool old_is_water = (n->getContent() == c_water_source);
// Move mud to new place
if(!dropped_to_unknown) {
@@ -1912,7 +2139,7 @@ void make_block(BlockMakeData *data)
}
}
}
-
+
}
}//timer1
@@ -1940,7 +2167,7 @@ void make_block(BlockMakeData *data)
for(s16 y=full_node_max.Y; y>=full_node_min.Y; y--)
{
if(y == full_node_max.Y){
- water_found =
+ water_found =
(vmanip.m_data[i].getContent() == c_water_source ||
vmanip.m_data[i].getContent() == c_lava_source);
}
@@ -1982,7 +2209,7 @@ void make_block(BlockMakeData *data)
{
// Node position in 2d
v2s16 p2d = v2s16(x,z);
-
+
/*
Find the lowest surface to which enough light ends up
to make grass grow.
@@ -2008,7 +2235,7 @@ void make_block(BlockMakeData *data)
else
surface_y = full_node_min.Y;
}
-
+
u32 i = vmanip.m_area.index(p2d.X, surface_y, p2d.Y);
MapNode *n = &vmanip.m_data[i];
if(n->getContent() == c_dirt){
@@ -2113,7 +2340,7 @@ void make_block(BlockMakeData *data)
else
vmanip.m_data[i] = n_stone;
}
-
+
vmanip->m_area.add_y(em, i, 1);
}
}
@@ -2168,7 +2395,7 @@ void make_block(BlockMakeData *data)
/*
Add dungeons
*/
-
+
//if(node_min.Y < approx_groundlevel)
//if(myrand() % 3 == 0)
//if(myrand() % 3 == 0 && node_min.Y < approx_groundlevel)
@@ -2182,7 +2409,7 @@ void make_block(BlockMakeData *data)
// Dungeon generator doesn't modify places which have this set
vmanip->clearFlag(VMANIP_FLAG_DUNGEON_INSIDE
| VMANIP_FLAG_DUNGEON_PRESERVE);
-
+
// Set all air and water to be untouchable to make dungeons open
// to caves and open air
for(s16 x=full_node_min.X; x<=full_node_max.X; x++)
@@ -2204,12 +2431,12 @@ void make_block(BlockMakeData *data)
}
}
}
-
+
PseudoRandom random(blockseed+2);
// Add it
make_dungeon1(vmanip, random, ndef);
-
+
// Convert some cobble to mossy cobble
for(s16 x=full_node_min.X; x<=full_node_max.X; x++)
for(s16 z=full_node_min.Z; z<=full_node_max.Z; z++)
@@ -2257,7 +2484,7 @@ void make_block(BlockMakeData *data)
make_nc(vmanip, ncrandom, ndef);
}
}
-
+
/*
Add top and bottom side of water to transforming_liquid queue
*/
@@ -2346,7 +2573,7 @@ void make_block(BlockMakeData *data)
if(current_depth == 0 && y <= WATER_LEVEL+2
&& possibly_have_sand)
have_sand = true;
-
+
if(current_depth < 4)
{
if(have_sand)
@@ -2384,7 +2611,7 @@ void make_block(BlockMakeData *data)
/*
Calculate some stuff
*/
-
+
float surface_humidity = surface_humidity_2d(data->seed, p2d_center);
bool is_jungle = surface_humidity > 0.75;
// Amount of trees
@@ -2521,7 +2748,7 @@ void make_block(BlockMakeData *data)
/*
Add some kind of random stones
*/
-
+
u32 random_stone_count = gen_area_nodes *
randomstone_amount_2d(data->seed, p2d_center);
// Put in random places on part of division
@@ -2555,7 +2782,7 @@ void make_block(BlockMakeData *data)
/*
Add larger stones
*/
-
+
u32 large_stone_count = gen_area_nodes *
largestone_amount_2d(data->seed, p2d_center);
//u32 large_stone_count = 1;
@@ -2612,7 +2839,7 @@ void make_block(BlockMakeData *data)
if(mineralrandom.next()%8 == 0)
vmanip.m_data[vi] = MapNode(c_mese);
}
-
+
}
}
/*
@@ -2742,7 +2969,7 @@ void make_block(BlockMakeData *data)
voxalgo::clearLightAndCollectSources(vmanip, a, bank, ndef,
light_sources, unlight_from);
-
+
bool inexistent_top_provides_sunlight = !block_is_underground;
voxalgo::SunlightPropagateResult res = voxalgo::propagateSunlight(
vmanip, a, inexistent_top_provides_sunlight,
@@ -2756,6 +2983,8 @@ void make_block(BlockMakeData *data)
}
}
+#endif ///BIG COMMENT
+
BlockMakeData::BlockMakeData():
no_op(false),
vmanip(NULL),
@@ -2768,6 +2997,6 @@ BlockMakeData::~BlockMakeData()
delete vmanip;
}
-}; // namespace mapgen
+//}; // namespace mapgen
diff --git a/src/mapgen.h b/src/mapgen.h
index 8986ddab0..4f1ab4ebd 100644
--- a/src/mapgen.h
+++ b/src/mapgen.h
@@ -22,48 +22,116 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "irrlichttypes_extrabloated.h"
#include "util/container.h" // UniqueQueue
+#include "gamedef.h"
+#include "mapnode.h"
+#include "noise.h"
+#include "settings.h"
+#include <map>
-struct BlockMakeData;
+/////////////////// Mapgen flags
+#define MG_TREES 0x01
+#define MG_CAVES 0x02
+#define MG_DUNGEONS 0x04
+#define MGV6_FORESTS 0x08
+#define MGV6_BIOME_BLEND 0x10
+#define MG_FLAT 0x20
+
+class BiomeDefManager;
+class Biome;
+class EmergeManager;
class MapBlock;
class ManualMapVoxelManipulator;
+class VoxelManipulator;
class INodeDefManager;
-namespace mapgen
-{
- // Finds precise ground level at any position
- s16 find_ground_level_from_noise(u64 seed, v2s16 p2d, s16 precision);
+struct BlockMakeData {
+ bool no_op;
+ ManualMapVoxelManipulator *vmanip;
+ u64 seed;
+ v3s16 blockpos_min;
+ v3s16 blockpos_max;
+ v3s16 blockpos_requested;
+ UniqueQueue<v3s16> transforming_liquid;
+ INodeDefManager *nodedef;
+
+ BlockMakeData();
+ ~BlockMakeData();
+};
+
+struct MapgenParams {
+ std::string mg_name;
+ int chunksize;
+ u64 seed;
+ int water_level;
+ u32 flags;
+
+ MapgenParams() {
+ mg_name = "v6";
+ seed = 0;
+ water_level = 1;
+ chunksize = 5;
+ flags = MG_TREES | MG_CAVES | MGV6_BIOME_BLEND;
+ }
+
+ virtual bool readParams(Settings *settings) = 0;
+ virtual void writeParams(Settings *settings) {};
+};
+
+class Mapgen {
+public:
+ int seed;
+ int water_level;
+ bool generating;
+ int id;
+
+ virtual void makeChunk(BlockMakeData *data) {};
+ virtual int getGroundLevelAtPoint(v2s16 p) = 0;
- // Find out if block is completely underground
- bool block_is_underground(u64 seed, v3s16 blockpos);
+ //Legacy functions for Farmesh (pending removal)
+ static bool get_have_beach(u64 seed, v2s16 p2d);
+ static double tree_amount_2d(u64 seed, v2s16 p);
+ static s16 find_ground_level_from_noise(u64 seed, v2s16 p2d, s16 precision);
+};
- // Get a pseudorandom seed for a position on the map
- u32 get_blockseed(u64 seed, v3s16 p);
+struct MapgenFactory {
+ virtual Mapgen *createMapgen(int mgid, MapgenParams *params,
+ EmergeManager *emerge) = 0;
+ virtual MapgenParams *createMapgenParams() = 0;
+};
- // Main map generation routine
- void make_block(BlockMakeData *data);
+class EmergeManager {
+public:
+ std::map<std::string, MapgenFactory *> mglist;
+
+ //settings
+ MapgenParams *params;
+
+ //mapgen objects here
+ Mapgen *mapgen;
+
+ //biome manager
+ BiomeDefManager *biomedef;
+
+ EmergeManager(IGameDef *gamedef, BiomeDefManager *bdef);
+ ~EmergeManager();
+
+ void initMapgens(MapgenParams *mgparams);
+ Mapgen *createMapgen(std::string mgname, int mgid,
+ MapgenParams *mgparams, EmergeManager *emerge);
+ MapgenParams *createMapgenParams(std::string mgname);
+ Mapgen *getMapgen();
+ void addBlockToQueue();
+
+ void registerMapgen(std::string name, MapgenFactory *mgfactory);
+ MapgenParams *getParamsFromSettings(Settings *settings);
+ void setParamsToSettings(Settings *settings);
- /*
- These are used by FarMesh
- */
- bool get_have_beach(u64 seed, v2s16 p2d);
- double tree_amount_2d(u64 seed, v2s16 p);
-
- struct BlockMakeData
- {
- bool no_op;
- ManualMapVoxelManipulator *vmanip; // Destructor deletes
- u64 seed;
- v3s16 blockpos_min;
- v3s16 blockpos_max;
- v3s16 blockpos_requested;
- UniqueQueue<v3s16> transforming_liquid;
- INodeDefManager *nodedef;
-
- BlockMakeData();
- ~BlockMakeData();
- };
-
-}; // namespace mapgen
+ //mapgen helper methods
+ Biome *getBiomeAtPoint(v3s16 p);
+ int getGroundLevelAtPoint(v2s16 p);
+ bool isBlockUnderground(v3s16 blockpos);
+ u32 getBlockSeed(v3s16 p);
+};
#endif
diff --git a/src/mapgen_v6.cpp b/src/mapgen_v6.cpp
new file mode 100644
index 000000000..3a5e10930
--- /dev/null
+++ b/src/mapgen_v6.cpp
@@ -0,0 +1,1438 @@
+/*
+Minetest-c55
+Copyright (C) 2010-2011 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 "mapgen.h"
+#include "voxel.h"
+#include "noise.h"
+#include "mapblock.h"
+#include "mapnode.h"
+#include "map.h"
+//#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"
+#include "settings.h" // For g_settings
+#include "main.h" // For g_profiler
+#include "mapgen_v6.h"
+
+/////////////////// Mapgen V6 perlin noise default values
+NoiseParams nparams_v6_def_terrain_base =
+ {-AVERAGE_MUD_AMOUNT, 20.0, v3f(250.0, 250.0, 250.0), 82341, 5, 0.6};
+NoiseParams nparams_v6_def_terrain_higher =
+ {20.0, 16.0, v3f(500.0, 500.0, 500.0), 85039, 5, 0.6};
+NoiseParams nparams_v6_def_steepness =
+ {0.85, 0.5, v3f(125.0, 125.0, 125.0), -932, 5, 0.7};
+NoiseParams nparams_v6_def_height_select =
+ {0.5, 1.0, v3f(250.0, 250.0, 250.0), 4213, 5, 0.69};
+NoiseParams nparams_v6_def_trees =
+ {0.0, 1.0, v3f(125.0, 125.0, 125.0), 2, 4, 0.66};
+NoiseParams nparams_v6_def_mud =
+ {AVERAGE_MUD_AMOUNT, 2.0, v3f(200.0, 200.0, 200.0), 91013, 3, 0.55};
+NoiseParams nparams_v6_def_beach =
+ {0.0, 1.0, v3f(250.0, 250.0, 250.0), 59420, 3, 0.50};
+NoiseParams nparams_v6_def_biome =
+ {0.0, 1.0, v3f(250.0, 250.0, 250.0), 9130, 3, 0.50};
+NoiseParams nparams_v6_def_cave =
+ {6.0, 6.0, v3f(250.0, 250.0, 250.0), 34329, 3, 0.50};
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+
+MapgenV6::MapgenV6(int mapgenid, MapgenV6Params *params) {
+ this->generating = false;
+ this->id = mapgenid;
+
+ this->seed = (int)params->seed;
+ this->water_level = params->water_level;
+ this->flags = params->flags;
+ this->csize = v3s16(1, 1, 1) * params->chunksize * MAP_BLOCKSIZE;
+
+ this->freq_desert = params->freq_desert;
+ this->freq_beach = params->freq_beach;
+
+ this->ystride = csize.X; //////fix this
+
+ np_cave = params->np_cave;
+
+ noise_terrain_base = new Noise(params->np_terrain_base, seed, csize.X, csize.Y);
+ noise_terrain_higher = new Noise(params->np_terrain_higher, seed, csize.X, csize.Y);
+ noise_steepness = new Noise(params->np_steepness, seed, csize.X, csize.Y);
+ noise_height_select = new Noise(params->np_height_select, seed, csize.X, csize.Y);
+ noise_trees = new Noise(params->np_trees, seed, csize.X, csize.Y);
+ noise_mud = new Noise(params->np_mud, seed, csize.X, csize.Y);
+ noise_beach = new Noise(params->np_beach, seed, csize.X, csize.Y);
+ noise_biome = new Noise(params->np_biome, seed, csize.X, csize.Y);
+
+ map_terrain_base = noise_terrain_base->result;
+ map_terrain_higher = noise_terrain_higher->result;
+ map_steepness = noise_steepness->result;
+ map_height_select = noise_height_select->result;
+ map_trees = noise_trees->result;
+ map_mud = noise_mud->result;
+ map_beach = noise_beach->result;
+ map_biome = noise_biome->result;
+}
+
+
+MapgenV6::~MapgenV6() {
+ delete noise_terrain_base;
+ delete noise_terrain_higher;
+ delete noise_steepness;
+ delete noise_height_select;
+ delete noise_trees;
+ delete noise_mud;
+ delete noise_beach;
+ delete noise_biome;
+}
+
+
+/*
+ Some helper functions for the map generator
+*/
+
+#if 1
+// Returns Y one under area minimum if not found
+s16 MapgenV6::find_ground_level(VoxelManipulator &vmanip, v2s16 p2d,
+ INodeDefManager *ndef)
+{
+ v3s16 em = vmanip.m_area.getExtent();
+ s16 y_nodes_max = vmanip.m_area.MaxEdge.Y;
+ s16 y_nodes_min = vmanip.m_area.MinEdge.Y;
+ u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
+ s16 y;
+ for(y=y_nodes_max; y>=y_nodes_min; y--)
+ {
+ MapNode &n = vmanip.m_data[i];
+ if(ndef->get(n).walkable)
+ break;
+
+ vmanip.m_area.add_y(em, i, -1);
+ }
+ if(y >= y_nodes_min)
+ return y;
+ else
+ return y_nodes_min - 1;
+}
+
+// Returns Y one under area minimum if not found
+s16 MapgenV6::find_stone_level(VoxelManipulator &vmanip, v2s16 p2d,
+ INodeDefManager *ndef)
+{
+ v3s16 em = vmanip.m_area.getExtent();
+ s16 y_nodes_max = vmanip.m_area.MaxEdge.Y;
+ s16 y_nodes_min = vmanip.m_area.MinEdge.Y;
+ u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
+ s16 y;
+ content_t c_stone = ndef->getId("mapgen_stone");
+ content_t c_desert_stone = ndef->getId("mapgen_desert_stone");
+ for(y=y_nodes_max; y>=y_nodes_min; y--)
+ {
+ MapNode &n = vmanip.m_data[i];
+ content_t c = n.getContent();
+ if(c != CONTENT_IGNORE && (
+ c == c_stone || c == c_desert_stone))
+ break;
+
+ vmanip.m_area.add_y(em, i, -1);
+ }
+ if(y >= y_nodes_min)
+ return y;
+ else
+ return y_nodes_min - 1;
+}
+#endif
+
+void MapgenV6::make_tree(ManualMapVoxelManipulator &vmanip, v3s16 p0,
+ bool is_apple_tree, INodeDefManager *ndef)
+{
+ MapNode treenode(ndef->getId("mapgen_tree"));
+ MapNode leavesnode(ndef->getId("mapgen_leaves"));
+ MapNode applenode(ndef->getId("mapgen_apple"));
+
+ s16 trunk_h = myrand_range(4, 5);
+ v3s16 p1 = p0;
+ for(s16 ii=0; ii<trunk_h; ii++)
+ {
+ if(vmanip.m_area.contains(p1))
+ vmanip.m_data[vmanip.m_area.index(p1)] = treenode;
+ p1.Y++;
+ }
+
+ // p1 is now the last piece of the trunk
+ p1.Y -= 1;
+
+ VoxelArea leaves_a(v3s16(-2,-1,-2), v3s16(2,2,2));
+ //SharedPtr<u8> leaves_d(new u8[leaves_a.getVolume()]);
+ Buffer<u8> leaves_d(leaves_a.getVolume());
+ for(s32 i=0; i<leaves_a.getVolume(); i++)
+ leaves_d[i] = 0;
+
+ // Force leaves at near the end of the trunk
+ {
+ s16 d = 1;
+ for(s16 z=-d; z<=d; z++)
+ for(s16 y=-d; y<=d; y++)
+ for(s16 x=-d; x<=d; x++)
+ {
+ leaves_d[leaves_a.index(v3s16(x,y,z))] = 1;
+ }
+ }
+
+ // Add leaves randomly
+ for(u32 iii=0; iii<7; iii++)
+ {
+ s16 d = 1;
+
+ v3s16 p(
+ myrand_range(leaves_a.MinEdge.X, leaves_a.MaxEdge.X-d),
+ myrand_range(leaves_a.MinEdge.Y, leaves_a.MaxEdge.Y-d),
+ myrand_range(leaves_a.MinEdge.Z, leaves_a.MaxEdge.Z-d)
+ );
+
+ for(s16 z=0; z<=d; z++)
+ for(s16 y=0; y<=d; y++)
+ for(s16 x=0; x<=d; x++)
+ {
+ leaves_d[leaves_a.index(p+v3s16(x,y,z))] = 1;
+ }
+ }
+
+ // Blit leaves to vmanip
+ for(s16 z=leaves_a.MinEdge.Z; z<=leaves_a.MaxEdge.Z; z++)
+ for(s16 y=leaves_a.MinEdge.Y; y<=leaves_a.MaxEdge.Y; y++)
+ for(s16 x=leaves_a.MinEdge.X; x<=leaves_a.MaxEdge.X; x++)
+ {
+ v3s16 p(x,y,z);
+ p += p1;
+ if(vmanip.m_area.contains(p) == false)
+ continue;
+ u32 vi = vmanip.m_area.index(p);
+ if(vmanip.m_data[vi].getContent() != CONTENT_AIR
+ && vmanip.m_data[vi].getContent() != CONTENT_IGNORE)
+ continue;
+ u32 i = leaves_a.index(x,y,z);
+ if(leaves_d[i] == 1) {
+ bool is_apple = myrand_range(0,99) < 10;
+ if(is_apple_tree && is_apple) {
+ vmanip.m_data[vi] = applenode;
+ } else {
+ vmanip.m_data[vi] = leavesnode;
+ }
+ }
+ }
+}
+
+
+/*
+ Noise functions. Make sure seed is mangled differently in each one.
+*/
+
+
+// Amount of trees per area in nodes
+double MapgenV6::tree_amount_2d(u64 seed, v2s16 p)
+{
+ /*double noise = noise2d_perlin(
+ 0.5+(float)p.X/125, 0.5+(float)p.Y/125,
+ seed+2, 4, 0.66);*/
+ double noise = map_trees[(p.Y - node_min.Z) * ystride + (p.X - node_min.X)];
+ double zeroval = -0.39;
+ if(noise < zeroval)
+ return 0;
+ else
+ return 0.04 * (noise-zeroval) / (1.0-zeroval);
+}
+
+// Required by mapgen.h
+bool MapgenV6::block_is_underground(u64 seed, v3s16 blockpos)
+{
+ /*s16 minimum_groundlevel = (s16)get_sector_minimum_ground_level(
+ seed, v2s16(blockpos.X, blockpos.Z));*/
+ // Nah, this is just a heuristic, just return something
+ s16 minimum_groundlevel = water_level;
+
+ if(blockpos.Y*MAP_BLOCKSIZE + MAP_BLOCKSIZE <= minimum_groundlevel)
+ return true;
+ else
+ return false;
+}
+
+
+double MapgenV6::base_rock_level_2d(u64 seed, v2s16 p)
+{
+ if (flags & MG_FLAT)
+ return water_level;
+
+ int index = (p.Y - node_min.Z) * ystride + (p.X - node_min.X);
+
+ // The base ground level
+ /*double base = (double)WATER_LEVEL - (double)AVERAGE_MUD_AMOUNT
+ + 20. * noise2d_perlin(
+ 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
+ seed+82341, 5, 0.6);*/
+ double base = water_level + map_terrain_base[index];
+
+ // Higher ground level
+ /*double higher = (double)WATER_LEVEL + 20. + 16. * noise2d_perlin(
+ 0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
+ seed+85039, 5, 0.6);*/
+ double higher = water_level + map_terrain_higher[index];
+
+ // Limit higher to at least base
+ if(higher < base)
+ higher = base;
+
+ // Steepness factor of cliffs
+ /*double b = 0.85 + 0.5 * noise2d_perlin(
+ 0.5+(float)p.X/125., 0.5+(float)p.Y/125.,
+ seed-932, 5, 0.7);*/
+ double b = map_steepness[index];
+ b = rangelim(b, 0.0, 1000.0);
+ b = pow(b, 7);
+ b *= 5;
+ b = rangelim(b, 0.5, 1000.0);
+
+ // Values 1.5...100 give quite horrible looking slopes
+ if(b > 1.5 && b < 100.0){
+ if(b < 10.0)
+ b = 1.5;
+ else
+ b = 100.0;
+ }
+
+ // Offset to more low
+ double a_off = -0.20;
+
+ // High/low selector
+ /*double a = (double)0.5 + b * (a_off + noise2d_perlin(
+ 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
+ seed+4213, 5, 0.69));*/
+ double a = 0.5 + b * (a_off + map_height_select[index]);
+
+ // Limit
+ a = rangelim(a, 0.0, 1.0);
+
+ double h = base*(1.0-a) + higher*a;
+
+ return h;
+}
+
+double MapgenV6::baseRockLevelFromNoise(v2s16 p) {
+ if (flags & MG_FLAT)
+ return water_level;
+
+ double base = water_level +
+ NoisePerlin2DPosOffset(noise_terrain_base->np, p.X, 0.5, p.Y, 0.5, seed);
+ double higher = water_level +
+ NoisePerlin2DPosOffset(noise_terrain_higher->np, p.X, 0.5, p.Y, 0.5, seed);
+
+ if (higher < base)
+ higher = base;
+
+ double b = NoisePerlin2DPosOffset(noise_steepness->np, p.X, 0.5, p.Y, 0.5, seed);
+ b = rangelim(b, 0.0, 1000.0);
+ b = b*b*b*b*b*b*b;
+ b *= 5;
+ b = rangelim(b, 0.5, 1000.0);
+
+ if(b > 1.5 && b < 100.0){
+ if(b < 10.0)
+ b = 1.5;
+ else
+ b = 100.0;
+ }
+
+ double a_off = -0.20;
+ double a = 0.5 + b * (a_off + NoisePerlin2DNoTxfmPosOffset(
+ noise_height_select->np, p.X, 0.5, p.Y, 0.5, seed));
+ a = rangelim(a, 0.0, 1.0);
+
+ return base * (1.0 - a) + higher * a;
+}
+
+
+s16 MapgenV6::find_ground_level_from_noise(u64 seed, v2s16 p2d, s16 precision)
+{
+ return baseRockLevelFromNoise(p2d) + AVERAGE_MUD_AMOUNT;
+}
+
+double MapgenV6::get_mud_add_amount(u64 seed, v2s16 p)
+{
+ if (flags & MG_FLAT)
+ return AVERAGE_MUD_AMOUNT;
+
+ /*return ((float)AVERAGE_MUD_AMOUNT + 2.0 * noise2d_perlin(
+ 0.5+(float)p.X/200, 0.5+(float)p.Y/200,
+ seed+91013, 3, 0.55));*/
+ int index = (p.Y - node_min.Z) * ystride + (p.X - node_min.X);
+ return map_mud[index];
+}
+
+bool MapgenV6::get_have_beach(u64 seed, v2s16 p2d)
+{
+ // Determine whether to have sand here
+ /*double sandnoise = noise2d_perlin(
+ 0.2+(float)p2d.X/250, 0.7+(float)p2d.Y/250,
+ seed+59420, 3, 0.50);*/
+ int index = (p2d.Y - node_min.Z) * ystride + (p2d.X - node_min.X);
+ double sandnoise = map_beach[index];
+
+ return (sandnoise > freq_beach);
+}
+
+BiomeType MapgenV6::get_biome(u64 seed, v2s16 p2d)
+{
+ // Just do something very simple as for now
+ /*double d = noise2d_perlin(
+ 0.6+(float)p2d.X/250, 0.2+(float)p2d.Y/250,
+ seed+9130, 3, 0.50);*/
+ int index = (p2d.Y - node_min.Z) * ystride + (p2d.X - node_min.X);
+ double d = map_biome[index];
+ if(d > freq_desert)
+ return BT_DESERT;
+ if (flags & MGV6_BIOME_BLEND) {
+ if(d > freq_desert - 0.10 &&
+ (noise2d(p2d.X, p2d.Y, seed) + 1.0) > (freq_desert - d) * 20.0)
+ return BT_DESERT;
+ }
+ return BT_NORMAL;
+};
+
+u32 MapgenV6::get_blockseed(u64 seed, v3s16 p)
+{
+ s32 x=p.X, y=p.Y, z=p.Z;
+ return (u32)(seed%0x100000000ULL) + z*38134234 + y*42123 + x*23;
+}
+
+
+int MapgenV6::getGroundLevelAtPoint(v2s16 p) {
+ return baseRockLevelFromNoise(p) + AVERAGE_MUD_AMOUNT;
+}
+
+#define VMANIP_FLAG_CAVE VOXELFLAG_CHECKED1
+
+void MapgenV6::makeChunk(BlockMakeData *data)
+{
+ if(data->no_op)
+ {
+ //dstream<<"makeBlock: no-op"<<std::endl;
+ return;
+ }
+
+ this->generating = true;
+
+ 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);
+
+ INodeDefManager *ndef = data->nodedef;
+
+ // Hack: use minimum block coordinates for old code that assumes
+ // a single block
+ v3s16 blockpos = data->blockpos_requested;
+
+ /*dstream<<"makeBlock(): ("<<blockpos.X<<","<<blockpos.Y<<","
+ <<blockpos.Z<<")"<<std::endl;*/
+
+ v3s16 blockpos_min = data->blockpos_min;
+ v3s16 blockpos_max = data->blockpos_max;
+ v3s16 blockpos_full_min = blockpos_min - v3s16(1,1,1);
+ v3s16 blockpos_full_max = blockpos_max + v3s16(1,1,1);
+
+ ManualMapVoxelManipulator &vmanip = *(data->vmanip);
+ // Area of central chunk
+ node_min = blockpos_min*MAP_BLOCKSIZE;
+ node_max = (blockpos_max+v3s16(1,1,1))*MAP_BLOCKSIZE-v3s16(1,1,1);
+ // Full allocated area
+ v3s16 full_node_min = (blockpos_min-1)*MAP_BLOCKSIZE;
+ v3s16 full_node_max = (blockpos_max+2)*MAP_BLOCKSIZE-v3s16(1,1,1);
+
+ v3s16 central_area_size = node_max - node_min + v3s16(1,1,1);
+
+ const s16 max_spread_amount = MAP_BLOCKSIZE;
+
+ int volume_blocks = (blockpos_max.X - blockpos_min.X + 1)
+ * (blockpos_max.Y - blockpos_min.Y + 1)
+ * (blockpos_max.Z - blockpos_max.Z + 1);
+
+ int volume_nodes = volume_blocks *
+ MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
+
+ // Generated surface area
+ //double gen_area_nodes = MAP_BLOCKSIZE*MAP_BLOCKSIZE * rel_volume;
+
+ // Horribly wrong heuristic, but better than nothing
+ bool block_is_underground = (water_level > node_max.Y);
+
+ /*
+ Create a block-specific seed
+ */
+ u32 blockseed = get_blockseed(data->seed, full_node_min);
+
+ /*
+ Make some noise
+ */
+ {
+ int x = node_min.X;
+ int z = node_min.Z;
+
+ // Need to adjust for the original implementation's +.5 offset...
+ if (!(flags & MG_FLAT)) {
+ noise_terrain_base->perlinMap2D(
+ x + 0.5 * noise_terrain_base->np->spread.X,
+ z + 0.5 * noise_terrain_base->np->spread.Z);
+ noise_terrain_base->transformNoiseMap();
+
+ noise_terrain_higher->perlinMap2D(
+ x + 0.5 * noise_terrain_higher->np->spread.X,
+ z + 0.5 * noise_terrain_higher->np->spread.Z);
+ noise_terrain_higher->transformNoiseMap();
+
+ noise_steepness->perlinMap2D(
+ x + 0.5 * noise_steepness->np->spread.X,
+ z + 0.5 * noise_steepness->np->spread.Z);
+ noise_steepness->transformNoiseMap();
+
+ noise_height_select->perlinMap2D(
+ x + 0.5 * noise_height_select->np->spread.X,
+ z + 0.5 * noise_height_select->np->spread.Z);
+ }
+
+ noise_trees->perlinMap2D(
+ x + 0.5 * noise_trees->np->spread.X,
+ z + 0.5 * noise_trees->np->spread.Z);
+
+ if (!(flags & MG_FLAT)) {
+ noise_mud->perlinMap2D(
+ x + 0.5 * noise_mud->np->spread.X,
+ z + 0.5 * noise_mud->np->spread.Z);
+ noise_mud->transformNoiseMap();
+ }
+ noise_beach->perlinMap2D(
+ x + 0.2 * noise_beach->np->spread.X,
+ z + 0.7 * noise_beach->np->spread.Z);
+
+ noise_biome->perlinMap2D(
+ x + 0.6 * noise_biome->np->spread.X,
+ z + 0.2 * noise_biome->np->spread.Z);
+ }
+
+
+ /*
+ Cache some ground type values for speed
+ */
+
+// Creates variables c_name=id and n_name=node
+#define CONTENT_VARIABLE(ndef, name)\
+ content_t c_##name = ndef->getId("mapgen_" #name);\
+ MapNode n_##name(c_##name);
+// Default to something else if was CONTENT_IGNORE
+#define CONTENT_VARIABLE_FALLBACK(name, dname)\
+ if(c_##name == CONTENT_IGNORE){\
+ c_##name = c_##dname;\
+ n_##name = n_##dname;\
+ }
+
+ CONTENT_VARIABLE(ndef, stone);
+ CONTENT_VARIABLE(ndef, air);
+ CONTENT_VARIABLE(ndef, water_source);
+ CONTENT_VARIABLE(ndef, dirt);
+ CONTENT_VARIABLE(ndef, sand);
+ CONTENT_VARIABLE(ndef, gravel);
+ CONTENT_VARIABLE(ndef, clay);
+ CONTENT_VARIABLE(ndef, lava_source);
+ CONTENT_VARIABLE(ndef, cobble);
+ CONTENT_VARIABLE(ndef, mossycobble);
+ CONTENT_VARIABLE(ndef, dirt_with_grass);
+ CONTENT_VARIABLE(ndef, junglegrass);
+ CONTENT_VARIABLE(ndef, stone_with_coal);
+ CONTENT_VARIABLE(ndef, stone_with_iron);
+ CONTENT_VARIABLE(ndef, mese);
+ CONTENT_VARIABLE(ndef, desert_sand);
+ CONTENT_VARIABLE_FALLBACK(desert_sand, sand);
+ CONTENT_VARIABLE(ndef, desert_stone);
+ CONTENT_VARIABLE_FALLBACK(desert_stone, stone);
+
+ // Maximum height of the stone surface and obstacles.
+ // This is used to guide the cave generation
+ s16 stone_surface_max_y = 0;
+
+ /*
+ Generate general ground level to full area
+ */
+ {
+#if 1
+ TimeTaker timer1("Generating ground level");
+
+ for(s16 x=node_min.X; x<=node_max.X; x++)
+ for(s16 z=node_min.Z; z<=node_max.Z; z++)
+ {
+ // Node position
+ v2s16 p2d = v2s16(x,z);
+
+ /*
+ Skip of already generated
+ */
+ /*{
+ v3s16 p(p2d.X, node_min.Y, p2d.Y);
+ if(vmanip.m_data[vmanip.m_area.index(p)].d != CONTENT_AIR)
+ continue;
+ }*/
+
+ // Ground height at this point
+ float surface_y_f = 0.0;
+
+ // Use perlin noise for ground height
+ surface_y_f = base_rock_level_2d(data->seed, p2d);
+
+ /*// Experimental stuff
+ {
+ float a = highlands_level_2d(data->seed, p2d);
+ if(a > surface_y_f)
+ surface_y_f = a;
+ }*/
+
+ // Convert to integer
+ s16 surface_y = (s16)surface_y_f;
+
+ // Log it
+ if(surface_y > stone_surface_max_y)
+ stone_surface_max_y = surface_y;
+
+ BiomeType bt = get_biome(data->seed, p2d);
+ /*
+ Fill ground with stone
+ */
+ {
+ // Use fast index incrementing
+ v3s16 em = vmanip.m_area.getExtent();
+ u32 i = vmanip.m_area.index(v3s16(p2d.X, node_min.Y, p2d.Y));
+ for(s16 y=node_min.Y; y<=node_max.Y; y++)
+ {
+ if(vmanip.m_data[i].getContent() == CONTENT_IGNORE){
+ if(y <= surface_y){
+ if(y > water_level && bt == BT_DESERT)
+ vmanip.m_data[i] = n_desert_stone;
+ else
+ vmanip.m_data[i] = n_stone;
+ } else if(y <= water_level){
+ vmanip.m_data[i] = MapNode(c_water_source);
+ } else {
+ vmanip.m_data[i] = MapNode(c_air);
+ }
+ }
+ vmanip.m_area.add_y(em, i, 1);
+ }
+ }
+ }
+#endif
+
+ }//timer1
+
+ // Limit dirt flow area by 1 because mud is flown into neighbors.
+ assert(central_area_size.X == central_area_size.Z);
+ s16 mudflow_minpos = 0-max_spread_amount+1;
+ s16 mudflow_maxpos = central_area_size.X+max_spread_amount-2;
+
+ /*
+ Loop this part, it will make stuff look older and newer nicely
+ */
+
+ /*double cave_amount = 6.0 + 6.0 * noise2d_perlin(
+ 0.5+(double)node_min.X/250, 0.5+(double)node_min.Y/250,
+ data->seed+34329, 3, 0.50);*/
+
+ double cave_amount = NoisePerlin2D(np_cave, node_min.X, node_min.Y, data->seed);
+
+ const u32 age_loops = 2;
+ for(u32 i_age=0; i_age<age_loops; i_age++)
+ { // Aging loop
+ /******************************
+ BEGINNING OF AGING LOOP
+ ******************************/
+
+#if 1
+ {
+ // 24ms @cs=8
+ //TimeTaker timer1("caves");
+
+ /*
+ Make caves (this code is relatively horrible)
+ */
+ cave_amount = MYMAX(0.0, cave_amount);
+ u32 caves_count = cave_amount * volume_nodes / 50000;
+ u32 bruises_count = 1;
+ PseudoRandom ps(blockseed+21343);
+ PseudoRandom ps2(blockseed+1032);
+ if(ps.range(1, 6) == 1)
+ bruises_count = ps.range(0, ps.range(0, 2));
+ if(get_biome(data->seed, v2s16(node_min.X, node_min.Z)) == BT_DESERT){
+ caves_count /= 3;
+ bruises_count /= 3;
+ }
+ for(u32 jj=0; jj<caves_count+bruises_count; jj++)
+ {
+ if (!(flags & MG_CAVES))
+ continue;
+
+ /*int avg_height = (int)
+ ((base_rock_level_2d(data->seed, v2s16(node_min.X, node_min.Z)) +
+ base_rock_level_2d(data->seed, v2s16(node_max.X, node_max.Z))) / 2);
+ if ((node_max.Y + node_min.Y) / 2 > avg_height)
+ break;*/
+
+ bool large_cave = (jj >= caves_count);
+ s16 min_tunnel_diameter = 2;
+ s16 max_tunnel_diameter = ps.range(2,6);
+ int dswitchint = ps.range(1,14);
+ u16 tunnel_routepoints = 0;
+ int part_max_length_rs = 0;
+ if(large_cave){
+ part_max_length_rs = ps.range(2,4);
+ tunnel_routepoints = ps.range(5, ps.range(15,30));
+ min_tunnel_diameter = 5;
+ max_tunnel_diameter = ps.range(7, ps.range(8,24));
+ } else {
+ part_max_length_rs = ps.range(2,9);
+ tunnel_routepoints = ps.range(10, ps.range(15,30));
+ }
+ bool large_cave_is_flat = (ps.range(0,1) == 0);
+
+ v3f main_direction(0,0,0);
+
+ // Allowed route area size in nodes
+ v3s16 ar = central_area_size;
+
+ // Area starting point in nodes
+ v3s16 of = node_min;
+
+ // Allow a bit more
+ //(this should be more than the maximum radius of the tunnel)
+ s16 insure = 10;
+ s16 more = max_spread_amount - max_tunnel_diameter/2 - insure;
+ ar += v3s16(1,0,1) * more * 2;
+ of -= v3s16(1,0,1) * more;
+
+ s16 route_y_min = 0;
+ // Allow half a diameter + 7 over stone surface
+ s16 route_y_max = -of.Y + stone_surface_max_y + max_tunnel_diameter/2 + 7;
+
+ // Limit maximum to area
+ route_y_max = rangelim(route_y_max, 0, ar.Y-1);
+
+ if(large_cave)
+ {
+ s16 min = 0;
+ if(node_min.Y < water_level && node_max.Y > water_level)
+ {
+ min = water_level - max_tunnel_diameter/3 - of.Y;
+ route_y_max = water_level + max_tunnel_diameter/3 - of.Y;
+ }
+ route_y_min = ps.range(min, min + max_tunnel_diameter);
+ route_y_min = rangelim(route_y_min, 0, route_y_max);
+ }
+
+ s16 route_start_y_min = route_y_min;
+ s16 route_start_y_max = route_y_max;
+
+ route_start_y_min = rangelim(route_start_y_min, 0, ar.Y-1);
+ route_start_y_max = rangelim(route_start_y_max, route_start_y_min, ar.Y-1);
+
+ // Randomize starting position
+ v3f orp(
+ (float)(ps.next()%ar.X)+0.5,
+ (float)(ps.range(route_start_y_min, route_start_y_max))+0.5,
+ (float)(ps.next()%ar.Z)+0.5
+ );
+
+ v3s16 startp(orp.X, orp.Y, orp.Z);
+ startp += of;
+
+ MapNode airnode(CONTENT_AIR);
+ MapNode waternode(c_water_source);
+ MapNode lavanode(c_lava_source);
+
+ /*
+ Generate some tunnel starting from orp
+ */
+
+ for(u16 j=0; j<tunnel_routepoints; j++)
+ {
+ if(j%dswitchint==0 && large_cave == false)
+ {
+ main_direction = v3f(
+ ((float)(ps.next()%20)-(float)10)/10,
+ ((float)(ps.next()%20)-(float)10)/30,
+ ((float)(ps.next()%20)-(float)10)/10
+ );
+ main_direction *= (float)ps.range(0, 10)/10;
+ }
+
+ // Randomize size
+ s16 min_d = min_tunnel_diameter;
+ s16 max_d = max_tunnel_diameter;
+ s16 rs = ps.range(min_d, max_d);
+
+ // Every second section is rough
+ bool randomize_xz = (ps2.range(1,2) == 1);
+
+ v3s16 maxlen;
+ if(large_cave)
+ {
+ maxlen = v3s16(
+ rs*part_max_length_rs,
+ rs*part_max_length_rs/2,
+ rs*part_max_length_rs
+ );
+ }
+ else
+ {
+ maxlen = v3s16(
+ rs*part_max_length_rs,
+ ps.range(1, rs*part_max_length_rs),
+ rs*part_max_length_rs
+ );
+ }
+
+ v3f vec;
+
+ vec = v3f(
+ (float)(ps.next()%(maxlen.X*1))-(float)maxlen.X/2,
+ (float)(ps.next()%(maxlen.Y*1))-(float)maxlen.Y/2,
+ (float)(ps.next()%(maxlen.Z*1))-(float)maxlen.Z/2
+ );
+
+ // Jump downward sometimes
+ if(!large_cave && ps.range(0,12) == 0)
+ {
+ vec = v3f(
+ (float)(ps.next()%(maxlen.X*1))-(float)maxlen.X/2,
+ (float)(ps.next()%(maxlen.Y*2))-(float)maxlen.Y*2/2,
+ (float)(ps.next()%(maxlen.Z*1))-(float)maxlen.Z/2
+ );
+ }
+
+ /*if(large_cave){
+ v3f p = orp + vec;
+ s16 h = find_ground_level_clever(vmanip,
+ v2s16(p.X, p.Z), ndef);
+ route_y_min = h - rs/3;
+ route_y_max = h + rs;
+ }*/
+
+ vec += main_direction;
+
+ v3f rp = orp + vec;
+ if(rp.X < 0)
+ rp.X = 0;
+ else if(rp.X >= ar.X)
+ rp.X = ar.X-1;
+ if(rp.Y < route_y_min)
+ rp.Y = route_y_min;
+ else if(rp.Y >= route_y_max)
+ rp.Y = route_y_max-1;
+ if(rp.Z < 0)
+ rp.Z = 0;
+ else if(rp.Z >= ar.Z)
+ rp.Z = ar.Z-1;
+ vec = rp - orp;
+
+ for(float f=0; f<1.0; f+=1.0/vec.getLength())
+ {
+ v3f fp = orp + vec * f;
+ fp.X += 0.1*ps.range(-10,10);
+ fp.Z += 0.1*ps.range(-10,10);
+ v3s16 cp(fp.X, fp.Y, fp.Z);
+
+ s16 d0 = -rs/2;
+ s16 d1 = d0 + rs;
+ if(randomize_xz){
+ d0 += ps.range(-1,1);
+ d1 += ps.range(-1,1);
+ }
+ for(s16 z0=d0; z0<=d1; z0++)
+ {
+ s16 si = rs/2 - MYMAX(0, abs(z0)-rs/7-1);
+ for(s16 x0=-si-ps.range(0,1); x0<=si-1+ps.range(0,1); x0++)
+ {
+ s16 maxabsxz = MYMAX(abs(x0), abs(z0));
+ s16 si2 = rs/2 - MYMAX(0, maxabsxz-rs/7-1);
+ for(s16 y0=-si2; y0<=si2; y0++)
+ {
+ /*// Make better floors in small caves
+ if(y0 <= -rs/2 && rs<=7)
+ continue;*/
+ if(large_cave_is_flat){
+ // Make large caves not so tall
+ if(rs > 7 && abs(y0) >= rs/3)
+ continue;
+ }
+
+ s16 z = cp.Z + z0;
+ s16 y = cp.Y + y0;
+ s16 x = cp.X + x0;
+ v3s16 p(x,y,z);
+ p += of;
+
+ if(vmanip.m_area.contains(p) == false)
+ continue;
+
+ u32 i = vmanip.m_area.index(p);
+
+ if(large_cave)
+ {
+ if(full_node_min.Y < water_level &&
+ full_node_max.Y > water_level){
+ if(p.Y <= water_level)
+ vmanip.m_data[i] = waternode;
+ else
+ vmanip.m_data[i] = airnode;
+ } else if(full_node_max.Y < water_level){
+ if(p.Y < startp.Y - 2)
+ vmanip.m_data[i] = lavanode;
+ else
+ vmanip.m_data[i] = airnode;
+ } else {
+ vmanip.m_data[i] = airnode;
+ }
+ } else {
+ // Don't replace air or water or lava or ignore
+ if(vmanip.m_data[i].getContent() == CONTENT_IGNORE ||
+ vmanip.m_data[i].getContent() == CONTENT_AIR ||
+ vmanip.m_data[i].getContent() == c_water_source ||
+ vmanip.m_data[i].getContent() == c_lava_source)
+ continue;
+
+ vmanip.m_data[i] = airnode;
+
+ // Set tunnel flag
+ vmanip.m_flags[i] |= VMANIP_FLAG_CAVE;
+ }
+ }
+ }
+ }
+ }
+
+ orp = rp;
+ }
+
+ }
+
+ }//timer1
+#endif
+
+#if 1
+ {
+ // 15ms @cs=8
+ TimeTaker timer1("add mud");
+
+ /*
+ Add mud to the central chunk
+ */
+
+ for(s16 x=node_min.X; x<=node_max.X; x++)
+ for(s16 z=node_min.Z; z<=node_max.Z; z++)
+ {
+ // Node position in 2d
+ v2s16 p2d = v2s16(x,z);
+
+ // Randomize mud amount
+ s16 mud_add_amount = get_mud_add_amount(data->seed, p2d) / 2.0 + 0.5;
+
+ // Find ground level
+ s16 surface_y = find_stone_level(vmanip, p2d, ndef);
+ // Handle area not found
+ if(surface_y == vmanip.m_area.MinEdge.Y - 1)
+ continue;
+
+ MapNode addnode(c_dirt);
+ BiomeType bt = get_biome(data->seed, p2d);
+
+ if(bt == BT_DESERT)
+ addnode = MapNode(c_desert_sand);
+
+ if(bt == BT_DESERT && surface_y + mud_add_amount <= water_level+1){
+ addnode = MapNode(c_sand);
+ } else if(mud_add_amount <= 0){
+ mud_add_amount = 1 - mud_add_amount;
+ addnode = MapNode(c_gravel);
+ } else if(bt == BT_NORMAL && get_have_beach(data->seed, p2d) &&
+ surface_y + mud_add_amount <= water_level+2){
+ addnode = MapNode(c_sand);
+ }
+
+ if(bt == BT_DESERT){
+ if(surface_y > 20){
+ mud_add_amount = MYMAX(0, mud_add_amount - (surface_y - 20)/5);
+ }
+ }
+
+ /*
+ If topmost node is grass, change it to mud.
+ It might be if it was flown to there from a neighboring
+ chunk and then converted.
+ */
+ {
+ u32 i = vmanip.m_area.index(v3s16(p2d.X, surface_y, p2d.Y));
+ MapNode *n = &vmanip.m_data[i];
+ if(n->getContent() == c_dirt_with_grass)
+ *n = MapNode(c_dirt);
+ }
+
+ /*
+ Add mud on ground
+ */
+ {
+ s16 mudcount = 0;
+ v3s16 em = vmanip.m_area.getExtent();
+ s16 y_start = surface_y+1;
+ u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
+ for(s16 y=y_start; y<=node_max.Y; y++)
+ {
+ if(mudcount >= mud_add_amount)
+ break;
+
+ MapNode &n = vmanip.m_data[i];
+ n = addnode;
+ mudcount++;
+
+ vmanip.m_area.add_y(em, i, 1);
+ }
+ }
+
+ }
+
+ }//timer1
+#endif
+
+ /*
+ Add blobs of dirt and gravel underground
+ */
+ if(get_biome(data->seed, v2s16(node_min.X, node_min.Z)) == BT_NORMAL)
+ {
+ PseudoRandom pr(blockseed+983);
+ for(int i=0; i<volume_nodes/10/10/10; i++)
+ {
+ bool only_fill_cave = (myrand_range(0,1) != 0);
+ v3s16 size(
+ pr.range(1, 8),
+ pr.range(1, 8),
+ pr.range(1, 8)
+ );
+ v3s16 p0(
+ pr.range(node_min.X, node_max.X)-size.X/2,
+ pr.range(node_min.Y, node_max.Y)-size.Y/2,
+ pr.range(node_min.Z, node_max.Z)-size.Z/2
+ );
+ MapNode n1;
+ if(p0.Y > -32 && pr.range(0,1) == 0)
+ n1 = MapNode(c_dirt);
+ else
+ n1 = MapNode(c_gravel);
+ for(int x1=0; x1<size.X; x1++)
+ for(int y1=0; y1<size.Y; y1++)
+ for(int z1=0; z1<size.Z; z1++)
+ {
+ v3s16 p = p0 + v3s16(x1,y1,z1);
+ u32 i = vmanip.m_area.index(p);
+ if(!vmanip.m_area.contains(i))
+ continue;
+ // Cancel if not stone and not cave air
+ if(vmanip.m_data[i].getContent() != c_stone &&
+ !(vmanip.m_flags[i] & VMANIP_FLAG_CAVE))
+ continue;
+ if(only_fill_cave && !(vmanip.m_flags[i] & VMANIP_FLAG_CAVE))
+ continue;
+ vmanip.m_data[i] = n1;
+ }
+ }
+ }
+
+#if 1
+ {
+ // 340ms @cs=8
+ TimeTaker timer1("flow mud");
+
+ /*
+ Flow mud away from steep edges
+ */
+
+ // Iterate a few times
+ for(s16 k=0; k<3; k++)
+ {
+
+ for(s16 x=mudflow_minpos; x<=mudflow_maxpos; x++)
+ for(s16 z=mudflow_minpos; z<=mudflow_maxpos; z++)
+ {
+ // Invert coordinates every 2nd iteration
+ if(k%2 == 0)
+ {
+ x = mudflow_maxpos - (x-mudflow_minpos);
+ z = mudflow_maxpos - (z-mudflow_minpos);
+ }
+
+ // Node position in 2d
+ v2s16 p2d = v2s16(node_min.X, node_min.Z) + v2s16(x,z);
+
+ v3s16 em = vmanip.m_area.getExtent();
+ u32 i = vmanip.m_area.index(v3s16(p2d.X, node_max.Y, p2d.Y));
+ s16 y=node_max.Y;
+
+ while(y >= node_min.Y)
+ {
+
+ for(;; y--)
+ {
+ MapNode *n = NULL;
+ // Find mud
+ for(; y>=node_min.Y; y--)
+ {
+ n = &vmanip.m_data[i];
+ //if(content_walkable(n->d))
+ // break;
+ if(n->getContent() == c_dirt ||
+ n->getContent() == c_dirt_with_grass ||
+ n->getContent() == c_gravel)
+ break;
+
+ vmanip.m_area.add_y(em, i, -1);
+ }
+
+ // Stop if out of area
+ //if(vmanip.m_area.contains(i) == false)
+ if(y < node_min.Y)
+ break;
+
+ /*// If not mud, do nothing to it
+ MapNode *n = &vmanip.m_data[i];
+ if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS)
+ continue;*/
+
+ if(n->getContent() == c_dirt ||
+ n->getContent() == c_dirt_with_grass)
+ {
+ // Make it exactly mud
+ n->setContent(c_dirt);
+
+ /*
+ Don't flow it if the stuff under it is not mud
+ */
+ {
+ u32 i2 = i;
+ vmanip.m_area.add_y(em, i2, -1);
+ // Cancel if out of area
+ if(vmanip.m_area.contains(i2) == false)
+ continue;
+ MapNode *n2 = &vmanip.m_data[i2];
+ if(n2->getContent() != c_dirt &&
+ n2->getContent() != c_dirt_with_grass)
+ continue;
+ }
+ }
+
+ /*s16 recurse_count = 0;
+ mudflow_recurse:*/
+
+ v3s16 dirs4[4] = {
+ v3s16(0,0,1), // back
+ v3s16(1,0,0), // right
+ v3s16(0,0,-1), // front
+ v3s16(-1,0,0), // left
+ };
+
+ // Theck that upper is air or doesn't exist.
+ // Cancel dropping if upper keeps it in place
+ u32 i3 = i;
+ vmanip.m_area.add_y(em, i3, 1);
+ if(vmanip.m_area.contains(i3) == true
+ && ndef->get(vmanip.m_data[i3]).walkable)
+ {
+ continue;
+ }
+
+ // Drop mud on side
+
+ for(u32 di=0; di<4; di++)
+ {
+ v3s16 dirp = dirs4[di];
+ u32 i2 = i;
+ // Move to side
+ vmanip.m_area.add_p(em, i2, dirp);
+ // Fail if out of area
+ if(vmanip.m_area.contains(i2) == false)
+ continue;
+ // Check that side is air
+ MapNode *n2 = &vmanip.m_data[i2];
+ if(ndef->get(*n2).walkable)
+ continue;
+ // Check that under side is air
+ vmanip.m_area.add_y(em, i2, -1);
+ if(vmanip.m_area.contains(i2) == false)
+ continue;
+ n2 = &vmanip.m_data[i2];
+ if(ndef->get(*n2).walkable)
+ continue;
+ /*// Check that under that is air (need a drop of 2)
+ vmanip.m_area.add_y(em, i2, -1);
+ if(vmanip.m_area.contains(i2) == false)
+ continue;
+ n2 = &vmanip.m_data[i2];
+ if(content_walkable(n2->d))
+ continue;*/
+ // Loop further down until not air
+ bool dropped_to_unknown = false;
+ do{
+ vmanip.m_area.add_y(em, i2, -1);
+ n2 = &vmanip.m_data[i2];
+ // if out of known area
+ if(vmanip.m_area.contains(i2) == false
+ || n2->getContent() == CONTENT_IGNORE){
+ dropped_to_unknown = true;
+ break;
+ }
+ }while(ndef->get(*n2).walkable == false);
+ // Loop one up so that we're in air
+ vmanip.m_area.add_y(em, i2, 1);
+ n2 = &vmanip.m_data[i2];
+
+ bool old_is_water = (n->getContent() == c_water_source);
+ // Move mud to new place
+ if(!dropped_to_unknown) {
+ *n2 = *n;
+ // Set old place to be air (or water)
+ if(old_is_water)
+ *n = MapNode(c_water_source);
+ else
+ *n = MapNode(CONTENT_AIR);
+ }
+
+ // Done
+ break;
+ }
+ }
+ }
+ }
+
+ }
+
+ }//timer1
+#endif
+
+ } // Aging loop
+ /***********************
+ END OF AGING LOOP
+ ************************/
+
+ /*
+ Add top and bottom side of water to transforming_liquid queue
+ */
+
+ for(s16 x=full_node_min.X; x<=full_node_max.X; x++)
+ for(s16 z=full_node_min.Z; z<=full_node_max.Z; z++)
+ {
+ // Node position
+ v2s16 p2d(x,z);
+ {
+ bool water_found = false;
+ // Use fast index incrementing
+ v3s16 em = vmanip.m_area.getExtent();
+ u32 i = vmanip.m_area.index(v3s16(p2d.X, full_node_max.Y, p2d.Y));
+ for(s16 y=full_node_max.Y; y>=full_node_min.Y; y--)
+ {
+ if(y == full_node_max.Y){
+ water_found =
+ (vmanip.m_data[i].getContent() == c_water_source ||
+ vmanip.m_data[i].getContent() == c_lava_source);
+ }
+ else if(water_found == false)
+ {
+ if(vmanip.m_data[i].getContent() == c_water_source ||
+ vmanip.m_data[i].getContent() == c_lava_source)
+ {
+ v3s16 p = v3s16(p2d.X, y, p2d.Y);
+ data->transforming_liquid.push_back(p);
+ water_found = true;
+ }
+ }
+ else
+ {
+ // This can be done because water_found can only
+ // turn to true and end up here after going through
+ // a single block.
+ if(vmanip.m_data[i+1].getContent() != c_water_source ||
+ vmanip.m_data[i+1].getContent() != c_lava_source)
+ {
+ v3s16 p = v3s16(p2d.X, y+1, p2d.Y);
+ data->transforming_liquid.push_back(p);
+ water_found = false;
+ }
+ }
+
+ vmanip.m_area.add_y(em, i, -1);
+ }
+ }
+ }
+
+ /*
+ Grow grass
+ */
+
+ for(s16 x=full_node_min.X; x<=full_node_max.X; x++)
+ for(s16 z=full_node_min.Z; z<=full_node_max.Z; z++)
+ {
+ // Node position in 2d
+ v2s16 p2d = v2s16(x,z);
+
+ /*
+ Find the lowest surface to which enough light ends up
+ to make grass grow.
+
+ Basically just wait until not air and not leaves.
+ */
+ s16 surface_y = 0;
+ {
+ v3s16 em = vmanip.m_area.getExtent();
+ u32 i = vmanip.m_area.index(v3s16(p2d.X, node_max.Y, p2d.Y));
+ s16 y;
+ // Go to ground level
+ for(y=node_max.Y; y>=full_node_min.Y; y--)
+ {
+ MapNode &n = vmanip.m_data[i];
+ if(ndef->get(n).param_type != CPT_LIGHT
+ || ndef->get(n).liquid_type != LIQUID_NONE)
+ break;
+ vmanip.m_area.add_y(em, i, -1);
+ }
+ if(y >= full_node_min.Y)
+ surface_y = y;
+ else
+ surface_y = full_node_min.Y;
+ }
+
+ u32 i = vmanip.m_area.index(p2d.X, surface_y, p2d.Y);
+ MapNode *n = &vmanip.m_data[i];
+ if(n->getContent() == c_dirt){
+ // Well yeah, this can't be overground...
+ if(surface_y < water_level - 20)
+ continue;
+ n->setContent(c_dirt_with_grass);
+ }
+ }
+
+ /*
+ Generate some trees
+ */
+ assert(central_area_size.X == central_area_size.Z);
+ if (flags & MG_TREES) {
+ // Divide area into parts
+ s16 div = 8;
+ s16 sidelen = central_area_size.X / div;
+ double area = sidelen * sidelen;
+ for(s16 x0=0; x0<div; x0++)
+ for(s16 z0=0; z0<div; z0++)
+ {
+ // Center position of part of division
+ v2s16 p2d_center(
+ node_min.X + sidelen/2 + sidelen*x0,
+ node_min.Z + sidelen/2 + sidelen*z0
+ );
+ // Minimum edge of part of division
+ v2s16 p2d_min(
+ node_min.X + sidelen*x0,
+ node_min.Z + sidelen*z0
+ );
+ // Maximum edge of part of division
+ v2s16 p2d_max(
+ node_min.X + sidelen + sidelen*x0 - 1,
+ node_min.Z + sidelen + sidelen*z0 - 1
+ );
+ // Amount of trees
+ u32 tree_count = area * tree_amount_2d(data->seed, p2d_center);
+ // Put trees in random places on part of division
+ for(u32 i=0; i<tree_count; i++)
+ {
+ s16 x = myrand_range(p2d_min.X, p2d_max.X);
+ s16 z = myrand_range(p2d_min.Y, p2d_max.Y);
+ s16 y = find_ground_level(vmanip, v2s16(x,z), ndef);
+ // Don't make a tree under water level
+ if(y < water_level)
+ continue;
+ // Don't make a tree so high that it doesn't fit
+ if(y > node_max.Y - 6)
+ continue;
+ v3s16 p(x,y,z);
+ /*
+ Trees grow only on mud and grass
+ */
+ {
+ u32 i = vmanip.m_area.index(v3s16(p));
+ MapNode *n = &vmanip.m_data[i];
+ if(n->getContent() != c_dirt
+ && n->getContent() != c_dirt_with_grass)
+ continue;
+ }
+ p.Y++;
+ // Make a tree
+ make_tree(vmanip, p, false, ndef);
+ }
+ }
+ }
+
+
+ /*
+ Calculate lighting
+ */
+ {
+ ScopeProfiler sp(g_profiler, "EmergeThread: mapgen lighting update",
+ SPT_AVG);
+ //VoxelArea a(node_min, node_max);
+ VoxelArea a(node_min-v3s16(1,0,1)*MAP_BLOCKSIZE,
+ node_max+v3s16(1,0,1)*MAP_BLOCKSIZE);
+ /*VoxelArea a(node_min-v3s16(1,0,1)*MAP_BLOCKSIZE/2,
+ node_max+v3s16(1,0,1)*MAP_BLOCKSIZE/2);*/
+ enum LightBank banks[2] = {LIGHTBANK_DAY, LIGHTBANK_NIGHT};
+ for(int i=0; i<2; i++)
+ {
+ enum LightBank bank = banks[i];
+
+ core::map<v3s16, bool> light_sources;
+ core::map<v3s16, u8> unlight_from;
+
+ voxalgo::clearLightAndCollectSources(vmanip, a, bank, ndef,
+ light_sources, unlight_from);
+
+ bool inexistent_top_provides_sunlight = !block_is_underground;
+ voxalgo::SunlightPropagateResult res = voxalgo::propagateSunlight(
+ vmanip, a, inexistent_top_provides_sunlight,
+ light_sources, ndef);
+ // TODO: Do stuff according to bottom_sunlight_valid
+
+ vmanip.unspreadLight(bank, unlight_from, light_sources, ndef);
+
+ vmanip.spreadLight(bank, light_sources, ndef);
+ }
+ }
+}
diff --git a/src/mapgen_v6.h b/src/mapgen_v6.h
new file mode 100644
index 000000000..9a2a0287d
--- /dev/null
+++ b/src/mapgen_v6.h
@@ -0,0 +1,141 @@
+/*
+Minetest-c55
+Copyright (C) 2010-2011 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 MAPGENV6_HEADER
+#define MAPGENV6_HEADER
+
+#include "mapgen.h"
+
+#define AVERAGE_MUD_AMOUNT 4
+
+enum BiomeType
+{
+ BT_NORMAL,
+ BT_DESERT
+};
+
+extern NoiseParams nparams_v6_def_terrain_base;
+extern NoiseParams nparams_v6_def_terrain_higher;
+extern NoiseParams nparams_v6_def_steepness;
+extern NoiseParams nparams_v6_def_height_select;
+extern NoiseParams nparams_v6_def_trees;
+extern NoiseParams nparams_v6_def_mud;
+extern NoiseParams nparams_v6_def_beach;
+extern NoiseParams nparams_v6_def_biome;
+extern NoiseParams nparams_v6_def_cave;
+
+struct MapgenV6Params : public MapgenParams {
+ float freq_desert;
+ float freq_beach;
+ NoiseParams *np_terrain_base;
+ NoiseParams *np_terrain_higher;
+ NoiseParams *np_steepness;
+ NoiseParams *np_height_select;
+ NoiseParams *np_trees;
+ NoiseParams *np_mud;
+ NoiseParams *np_beach;
+ NoiseParams *np_biome;
+ NoiseParams *np_cave;
+
+ MapgenV6Params() {
+ freq_desert = 0.45;
+ freq_beach = 0.15;
+ np_terrain_base = &nparams_v6_def_terrain_base;
+ np_terrain_higher = &nparams_v6_def_terrain_higher;
+ np_steepness = &nparams_v6_def_steepness;
+ np_height_select = &nparams_v6_def_height_select;
+ np_trees = &nparams_v6_def_trees;
+ np_mud = &nparams_v6_def_mud;
+ np_beach = &nparams_v6_def_beach;
+ np_biome = &nparams_v6_def_biome;
+ np_cave = &nparams_v6_def_cave;
+ }
+
+ bool readParams(Settings *settings);
+ void writeParams(Settings *settings);
+};
+
+class MapgenV6 : public Mapgen {
+public:
+ //ManualMapVoxelManipulator &vmanip;
+
+ int ystride;
+ v3s16 csize;
+
+ v3s16 node_min;
+ v3s16 node_max;
+
+ Noise *noise_terrain_base;
+ Noise *noise_terrain_higher;
+ Noise *noise_steepness;
+ Noise *noise_height_select;
+ Noise *noise_trees;
+ Noise *noise_mud;
+ Noise *noise_beach;
+ Noise *noise_biome;
+
+ float *map_terrain_base;
+ float *map_terrain_higher;
+ float *map_steepness;
+ float *map_height_select;
+ float *map_trees;
+ float *map_mud;
+ float *map_beach;
+ float *map_biome;
+
+ NoiseParams *np_cave;
+
+ u32 flags;
+ float freq_desert;
+ float freq_beach;
+
+ MapgenV6(int mapgenid, MapgenV6Params *params);
+ ~MapgenV6();
+
+ void makeChunk(BlockMakeData *data);
+ int getGroundLevelAtPoint(v2s16 p);
+
+ double baseRockLevelFromNoise(v2s16 p);
+ static s16 find_ground_level(VoxelManipulator &vmanip,
+ v2s16 p2d, INodeDefManager *ndef);
+ static s16 find_stone_level(VoxelManipulator &vmanip,
+ v2s16 p2d, INodeDefManager *ndef);
+ void make_tree(ManualMapVoxelManipulator &vmanip, v3s16 p0,
+ bool is_apple_tree, INodeDefManager *ndef);
+ double tree_amount_2d(u64 seed, v2s16 p);
+ bool block_is_underground(u64 seed, v3s16 blockpos);
+ double base_rock_level_2d(u64 seed, v2s16 p);
+ s16 find_ground_level_from_noise(u64 seed, v2s16 p2d, s16 precision);
+ double get_mud_add_amount(u64 seed, v2s16 p);
+ bool get_have_beach(u64 seed, v2s16 p2d);
+ BiomeType get_biome(u64 seed, v2s16 p2d);
+ u32 get_blockseed(u64 seed, v3s16 p);
+};
+
+struct MapgenFactoryV6 : public MapgenFactory {
+ Mapgen *createMapgen(int mgid, MapgenParams *params, EmergeManager *emerge) {
+ return new MapgenV6(mgid, (MapgenV6Params *)params);
+ };
+
+ MapgenParams *createMapgenParams() {
+ return new MapgenV6Params();
+ };
+};
+
+#endif
diff --git a/src/mods.cpp b/src/mods.cpp
index 08e8e276f..f6e3d58d7 100644
--- a/src/mods.cpp
+++ b/src/mods.cpp
@@ -18,23 +18,16 @@ with this program; if not, write to the Free Software Foundation, Inc.,
*/
#include "mods.h"
-#include <queue>
-#include <fstream>
-#include <sstream>
-#include <map>
#include "filesys.h"
#include "strfnd.h"
#include "log.h"
+#include "subgame.h"
+#include "settings.h"
-static void collectMods(const std::string &modspath,
- std::queue<ModSpec> &mods_satisfied,
- core::list<ModSpec> &mods_unsorted,
- std::map<std::string, std::string> &mod_names,
- std::string indentation)
+std::map<std::string, ModSpec> getModsInPath(std::string path)
{
- TRACESTREAM(<<indentation<<"collectMods(\""<<modspath<<"\")"<<std::endl);
- TRACEDO(indentation += " ");
- std::vector<fs::DirListNode> dirlist = fs::GetDirListing(modspath);
+ std::map<std::string, ModSpec> result;
+ std::vector<fs::DirListNode> dirlist = fs::GetDirListing(path);
for(u32 j=0; j<dirlist.size(); j++){
if(!dirlist[j].dir)
continue;
@@ -43,119 +36,187 @@ static void collectMods(const std::string &modspath,
// VCS directories like ".git" or ".svn"
if(modname[0] == '.')
continue;
- std::string modpath = modspath + DIR_DELIM + modname;
- TRACESTREAM(<<indentation<<"collectMods: "<<modname<<" at \""<<modpath<<"\""<<std::endl);
+ std::string modpath = path + DIR_DELIM + modname;
+
// Handle modpacks (defined by containing modpack.txt)
- {
- std::ifstream is((modpath+DIR_DELIM+"modpack.txt").c_str(),
+ std::ifstream modpack_is((modpath+DIR_DELIM+"modpack.txt").c_str(),
std::ios_base::binary);
- if(is.good()){
- // Is a modpack
- is.close(); // We don't actually need the file
- // Recurse into it
- collectMods(modpath, mods_satisfied, mods_unsorted, mod_names, indentation);
- continue;
- }
- }
- // Detect mod name conflicts
+ if(modpack_is.good()) //a modpack, recursively get the mods in it
{
- std::map<std::string, std::string>::const_iterator i;
- i = mod_names.find(modname);
- if(i != mod_names.end()){
- std::string s;
- infostream<<indentation<<"WARNING: Mod name conflict detected: "
- <<std::endl
- <<"Already loaded: "<<i->second<<std::endl
- <<"Will not load: "<<modpath<<std::endl;
- continue;
- }
+ modpack_is.close(); // We don't actually need the file
+ ModSpec spec(modname,modpath);
+ spec.modpack_content = getModsInPath(modpath);
+ spec.is_modpack = true;
+ result.insert(std::make_pair(modname,spec));
}
- std::set<std::string> depends;
- std::ifstream is((modpath+DIR_DELIM+"depends.txt").c_str(),
+ else // not a modpack, add the modspec
+ {
+ std::set<std::string> depends;
+ std::ifstream is((modpath+DIR_DELIM+"depends.txt").c_str(),
std::ios_base::binary);
- while(is.good()){
- std::string dep;
- std::getline(is, dep);
- dep = trim(dep);
- if(dep != "")
- depends.insert(dep);
+ while(is.good())
+ {
+ std::string dep;
+ std::getline(is, dep);
+ dep = trim(dep);
+ if(dep != "")
+ depends.insert(dep);
+ }
+
+ ModSpec spec(modname, modpath, depends);
+ result.insert(std::make_pair(modname,spec));
}
- ModSpec spec(modname, modpath, depends);
- mods_unsorted.push_back(spec);
- if(depends.empty())
- mods_satisfied.push(spec);
- mod_names[modname] = modpath;
}
+ return result;
}
-// Get a dependency-sorted list of ModSpecs
-core::list<ModSpec> getMods(core::list<std::string> &modspaths)
- throw(ModError)
+std::map<std::string, ModSpec> flattenModTree(std::map<std::string, ModSpec> mods)
{
- std::queue<ModSpec> mods_satisfied;
- core::list<ModSpec> mods_unsorted;
- core::list<ModSpec> mods_sorted;
- // name, path: For detecting name conflicts
- std::map<std::string, std::string> mod_names;
- for(core::list<std::string>::Iterator i = modspaths.begin();
- i != modspaths.end(); i++){
- std::string modspath = *i;
- collectMods(modspath, mods_satisfied, mods_unsorted, mod_names, "");
+ std::map<std::string, ModSpec> result;
+ for(std::map<std::string,ModSpec>::iterator it = mods.begin();
+ it != mods.end(); ++it)
+ {
+ ModSpec mod = (*it).second;
+ if(mod.is_modpack)
+ {
+ std::map<std::string, ModSpec> content =
+ flattenModTree(mod.modpack_content);
+ result.insert(content.begin(),content.end());
+ result.insert(std::make_pair(mod.name,mod));
+ }
+ else //not a modpack
+ {
+ result.insert(std::make_pair(mod.name,mod));
+ }
}
- // Sort by depencencies
- while(!mods_satisfied.empty()){
- ModSpec mod = mods_satisfied.front();
- mods_satisfied.pop();
- mods_sorted.push_back(mod);
- for(core::list<ModSpec>::Iterator i = mods_unsorted.begin();
- i != mods_unsorted.end(); i++){
- ModSpec &mod2 = *i;
- if(mod2.unsatisfied_depends.empty())
- continue;
- mod2.unsatisfied_depends.erase(mod.name);
- if(!mod2.unsatisfied_depends.empty())
- continue;
- mods_satisfied.push(mod2);
+ return result;
+}
+
+std::vector<ModSpec> flattenMods(std::map<std::string, ModSpec> mods)
+{
+ std::vector<ModSpec> result;
+ for(std::map<std::string,ModSpec>::iterator it = mods.begin();
+ it != mods.end(); ++it)
+ {
+ ModSpec mod = (*it).second;
+ if(mod.is_modpack)
+ {
+ std::vector<ModSpec> content = flattenMods(mod.modpack_content);
+ result.reserve(result.size() + content.size());
+ result.insert(result.end(),content.begin(),content.end());
+
+ }
+ else //not a modpack
+ {
+ // infostream << "inserting mod " << mod.name << std::endl;
+ result.push_back(mod);
}
}
- std::ostringstream errs(std::ios::binary);
- // Check unsatisfied dependencies
- for(core::list<ModSpec>::Iterator i = mods_unsorted.begin();
- i != mods_unsorted.end(); i++){
- ModSpec &mod = *i;
- if(mod.unsatisfied_depends.empty())
- continue;
- errs<<"mod \""<<mod.name
- <<"\" has unsatisfied dependencies:";
- for(std::set<std::string>::iterator
- i = mod.unsatisfied_depends.begin();
- i != mod.unsatisfied_depends.end(); i++){
- errs<<" \""<<(*i)<<"\"";
+ return result;
+}
+
+std::vector<ModSpec> filterMods(std::vector<ModSpec> mods,
+ std::set<std::string> exclude_mod_names)
+{
+ std::vector<ModSpec> result;
+ for(std::vector<ModSpec>::iterator it = mods.begin();
+ it != mods.end(); ++it)
+ {
+ ModSpec& mod = *it;
+ if(exclude_mod_names.count(mod.name) == 0)
+ result.push_back(mod);
+ }
+ return result;
+}
+
+void ModConfiguration::addModsInPathFiltered(std::string path, std::set<std::string> exclude_mods)
+{
+ addMods(filterMods(flattenMods(getModsInPath(path)),exclude_mods));
+}
+
+
+void ModConfiguration::addMods(std::vector<ModSpec> new_mods)
+{
+ // Step 1: remove mods in sorted_mods from unmet dependencies
+ // of new_mods. new mods without unmet dependencies are
+ // temporarily stored in satisfied_mods
+ std::vector<ModSpec> satisfied_mods;
+ for(std::vector<ModSpec>::iterator it = m_sorted_mods.begin();
+ it != m_sorted_mods.end(); ++it)
+ {
+ ModSpec mod = *it;
+ for(std::vector<ModSpec>::iterator it_new = new_mods.begin();
+ it_new != new_mods.end(); ++it_new)
+ {
+ ModSpec& mod_new = *it_new;
+ //infostream << "erasing dependency " << mod.name << " from " << mod_new.name << std::endl;
+ mod_new.unsatisfied_depends.erase(mod.name);
}
- errs<<"."<<std::endl;
- mods_sorted.push_back(mod);
}
- // If an error occurred, throw an exception
- if(errs.str().size() != 0){
- throw ModError(errs.str());
+
+ // split new mods into satisfied and unsatisfied
+ for(std::vector<ModSpec>::iterator it = new_mods.begin();
+ it != new_mods.end(); ++it)
+ {
+ ModSpec mod_new = *it;
+ if(mod_new.unsatisfied_depends.empty())
+ satisfied_mods.push_back(mod_new);
+ else
+ m_unsatisfied_mods.push_back(mod_new);
}
- // Print out some debug info
- TRACESTREAM(<<"Detected mods in load order:"<<std::endl);
- for(core::list<ModSpec>::Iterator i = mods_sorted.begin();
- i != mods_sorted.end(); i++){
- ModSpec &mod = *i;
- TRACESTREAM(<<"name=\""<<mod.name<<"\" path=\""<<mod.path<<"\"");
- TRACESTREAM(<<" depends=[");
- for(std::set<std::string>::iterator i = mod.depends.begin();
- i != mod.depends.end(); i++)
- TRACESTREAM(<<" "<<(*i));
- TRACESTREAM(<<" ] unsatisfied_depends=[");
- for(std::set<std::string>::iterator i = mod.unsatisfied_depends.begin();
- i != mod.unsatisfied_depends.end(); i++)
- TRACESTREAM(<<" "<<(*i));
- TRACESTREAM(<<" ]"<<std::endl);
+
+ // Step 2: mods without unmet dependencies can be appended to
+ // the sorted list.
+ while(!satisfied_mods.empty())
+ {
+ ModSpec mod = satisfied_mods.back();
+ m_sorted_mods.push_back(mod);
+ satisfied_mods.pop_back();
+ for(std::list<ModSpec>::iterator it = m_unsatisfied_mods.begin();
+ it != m_unsatisfied_mods.end(); )
+ {
+ ModSpec& mod2 = *it;
+ mod2.unsatisfied_depends.erase(mod.name);
+ if(mod2.unsatisfied_depends.empty())
+ {
+ satisfied_mods.push_back(mod2);
+ it = m_unsatisfied_mods.erase(it);
+ }
+ else
+ ++it;
+ }
}
- return mods_sorted;
}
+ModConfiguration::ModConfiguration(std::string worldpath)
+{
+ // Add all world mods and all game mods
+ addModsInPath(worldpath + DIR_DELIM + "worldmods");
+ SubgameSpec gamespec = findWorldSubgame(worldpath);
+ addModsInPath(gamespec.gamemods_path);
+
+ // check world.mt file for mods explicitely declared to be
+ // loaded or not by a load_mod_<modname> = ... line.
+ std::string worldmt = worldpath+DIR_DELIM+"world.mt";
+ Settings worldmt_settings;
+ worldmt_settings.readConfigFile(worldmt.c_str());
+ std::vector<std::string> names = worldmt_settings.getNames();
+ std::set<std::string> exclude_mod_names;
+ for(std::vector<std::string>::iterator it = names.begin();
+ it != names.end(); ++it)
+ {
+ std::string name = *it;
+ // for backwards compatibility: exclude only mods which are
+ // explicitely excluded. if mod is not mentioned at all, it is
+ // enabled. So by default, all installed mods are enabled.
+ if (name.compare(0,9,"load_mod_") == 0 &&
+ !worldmt_settings.getBool(name))
+ {
+ exclude_mod_names.insert(name.substr(9));
+ }
+ }
+ for(std::set<std::string>::const_iterator i = gamespec.addon_mods_paths.begin();
+ i != gamespec.addon_mods_paths.end(); ++i)
+ addModsInPathFiltered((*i),exclude_mod_names);
+}
diff --git a/src/mods.h b/src/mods.h
index d55dd6c92..59dffdade 100644
--- a/src/mods.h
+++ b/src/mods.h
@@ -22,8 +22,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "irrlichttypes.h"
#include <irrList.h>
+#include <list>
#include <set>
+#include <vector>
#include <string>
+#include <map>
#include <exception>
class ModError : public std::exception
@@ -47,21 +50,96 @@ struct ModSpec
{
std::string name;
std::string path;
+ //if normal mod:
std::set<std::string> depends;
std::set<std::string> unsatisfied_depends;
- ModSpec(const std::string &name_="", const std::string path_="",
- const std::set<std::string> &depends_=std::set<std::string>()):
+ bool is_modpack;
+ // if modpack:
+ std::map<std::string,ModSpec> modpack_content;
+ ModSpec(const std::string name_="", const std::string path_="",
+ const std::set<std::string> depends_=std::set<std::string>()):
name(name_),
path(path_),
depends(depends_),
- unsatisfied_depends(depends_)
+ unsatisfied_depends(depends_),
+ is_modpack(false),
+ modpack_content()
{}
};
-// Get a dependency-sorted list of ModSpecs
-core::list<ModSpec> getMods(core::list<std::string> &modspaths)
- throw(ModError);
+
+std::map<std::string,ModSpec> getModsInPath(std::string path);
+
+// expands modpack contents, but does not replace them.
+std::map<std::string, ModSpec> flattenModTree(std::map<std::string, ModSpec> mods);
+
+// replaces modpack Modspecs with their content
+std::vector<ModSpec> flattenMods(std::map<std::string,ModSpec> mods);
+
+// removes Mods mentioned in exclude_mod_names
+std::vector<ModSpec> filterMods(std::vector<ModSpec> mods,
+ std::set<std::string> exclude_mod_names);
+
+// a ModConfiguration is a subset of installed mods, expected to have
+// all dependencies fullfilled, so it can be used as a list of mods to
+// load when the game starts.
+class ModConfiguration
+{
+public:
+ ModConfiguration():
+ m_unsatisfied_mods(),
+ m_sorted_mods()
+ {}
+
+
+ ModConfiguration(std::string worldpath);
+
+ // adds all mods in the given path. used for games, modpacks
+ // and world-specific mods (worldmods-folders)
+ void addModsInPath(std::string path)
+ {
+ addMods(flattenMods(getModsInPath(path)));
+ }
+
+ // adds all mods in the given path whose name does not appear
+ // in the exclude_mods set.
+ void addModsInPathFiltered(std::string path, std::set<std::string> exclude_mods);
+
+ // adds all mods in the set.
+ void addMods(std::vector<ModSpec> mods);
+
+ // checks if all dependencies are fullfilled.
+ bool isConsistent()
+ {
+ return m_unsatisfied_mods.empty();
+ }
+
+ std::vector<ModSpec> getMods()
+ {
+ return m_sorted_mods;
+ }
+
+ std::list<ModSpec> getUnsatisfiedMods()
+ {
+ return m_unsatisfied_mods;
+ }
+
+private:
+
+ // mods with unmet dependencies. This is a list and not a
+ // vector because we want easy removal of elements at every
+ // position.
+ std::list<ModSpec> m_unsatisfied_mods;
+
+ // list of mods sorted such that they can be loaded in the
+ // given order with all dependencies being fullfilled. I.e.,
+ // every mod in this list has only dependencies on mods which
+ // appear earlier in the vector.
+ std::vector<ModSpec> m_sorted_mods;
+
+};
+
#endif
diff --git a/src/nodedef.cpp b/src/nodedef.cpp
index 36fa798fd..12d9238ad 100644
--- a/src/nodedef.cpp
+++ b/src/nodedef.cpp
@@ -106,7 +106,7 @@ void NodeBox::deSerialize(std::istream &is)
/*
TileDef
*/
-
+
void TileDef::serialize(std::ostream &os) const
{
writeU8(os, 0); // version
@@ -173,7 +173,7 @@ void ContentFeatures::reset()
has_after_destruct = false;
/*
Actual data
-
+
NOTE: Most of this is always overridden by the default values given
in builtin.lua
*/
@@ -199,6 +199,7 @@ void ContentFeatures::reset()
diggable = true;
climbable = false;
buildable_to = false;
+ rightclickable = true;
liquid_type = LIQUID_NONE;
liquid_alternative_flowing = "";
liquid_alternative_source = "";
@@ -271,6 +272,7 @@ void ContentFeatures::serialize(std::ostream &os, u16 protocol_version)
serializeSimpleSoundSpec(sound_dug, os);
// Stuff below should be moved to correct place in a version that otherwise changes
// the protocol version
+ writeU8(os, rightclickable);
}
void ContentFeatures::deSerialize(std::istream &is)
@@ -334,6 +336,7 @@ void ContentFeatures::deSerialize(std::istream &is)
try{
// Stuff below should be moved to correct place in a version that
// otherwise changes the protocol version
+ rightclickable = readU8(is);
}catch(SerializationError &e) {};
}
@@ -354,7 +357,7 @@ public:
ContentFeatures &f = m_content_features[i];
f.reset(); // Reset to defaults
}
-
+
// Set CONTENT_AIR
{
ContentFeatures f;
@@ -541,11 +544,11 @@ public:
bool new_style_water = g_settings->getBool("new_style_water");
bool new_style_leaves = g_settings->getBool("new_style_leaves");
bool opaque_water = g_settings->getBool("opaque_water");
-
+
for(u16 i=0; i<=MAX_CONTENT; i++)
{
ContentFeatures *f = &m_content_features[i];
-
+
// Figure out the actual tiles to use
TileDef tiledef[6];
for(u32 j=0; j<6; j++)
@@ -604,9 +607,12 @@ public:
}
}
break;
+ case NDT_PLANTLIKE:
+ f->solidness = 0;
+ f->backface_culling = false;
+ break;
case NDT_TORCHLIKE:
case NDT_SIGNLIKE:
- case NDT_PLANTLIKE:
case NDT_FENCELIKE:
case NDT_RAILLIKE:
case NDT_NODEBOX:
diff --git a/src/nodedef.h b/src/nodedef.h
index 8588caeab..fa0c1f2e8 100644
--- a/src/nodedef.h
+++ b/src/nodedef.h
@@ -199,6 +199,8 @@ struct ContentFeatures
bool climbable;
// Player can build on these
bool buildable_to;
+ // Player cannot build to these (placement prediction disabled)
+ bool rightclickable;
// Whether the node is non-liquid, source liquid or flowing liquid
enum LiquidType liquid_type;
// If the content is liquid, this is the flowing version of the liquid.
diff --git a/src/noise.cpp b/src/noise.cpp
index e75fbf4bd..bfb1960c8 100644
--- a/src/noise.cpp
+++ b/src/noise.cpp
@@ -21,89 +21,116 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "noise.h"
#include <iostream>
#include "debug.h"
+#include "util/numeric.h"
-#define NOISE_MAGIC_X 1619
-#define NOISE_MAGIC_Y 31337
-#define NOISE_MAGIC_Z 52591
+#define NOISE_MAGIC_X 1619
+#define NOISE_MAGIC_Y 31337
+#define NOISE_MAGIC_Z 52591
#define NOISE_MAGIC_SEED 1013
-double cos_lookup[16] = {
- 1.0,0.9238,0.7071,0.3826,0,-0.3826,-0.7071,-0.9238,
- 1.0,-0.9238,-0.7071,-0.3826,0,0.3826,0.7071,0.9238
+float cos_lookup[16] = {
+ 1.0, 0.9238, 0.7071, 0.3826, 0, -0.3826, -0.7071, -0.9238,
+ 1.0, -0.9238, -0.7071, -0.3826, 0, 0.3826, 0.7071, 0.9238
};
-double dotProduct(double vx, double vy, double wx, double wy){
- return vx*wx+vy*wy;
+
+///////////////////////////////////////////////////////////////////////////////
+
+
+//noise poly: p(n) = 60493n^3 + 19990303n + 137612589
+float noise2d(int x, int y, int seed) {
+ 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;
}
-
-double easeCurve(double t){
- return t * t * t * (6. * t * t - 15. * t + 10.);
+
+
+float noise3d(int x, int y, int z, int seed) {
+ 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;
}
-
-double linearInterpolation(double x0, double x1, double t){
- return x0+(x1-x0)*t;
+
+
+float dotProduct(float vx, float vy, float wx, float wy) {
+ return vx * wx + vy * wy;
}
-
-double biLinearInterpolation(double x0y0, double x1y0, double x0y1, double x1y1, double x, double y){
- double tx = easeCurve(x);
- double ty = easeCurve(y);
- /*double tx = x;
- double ty = y;*/
- double u = linearInterpolation(x0y0,x1y0,tx);
- double v = linearInterpolation(x0y1,x1y1,tx);
- return linearInterpolation(u,v,ty);
+
+
+inline float linearInterpolation(float v0, float v1, float t) {
+ return v0 + (v1 - v0) * t;
}
-double triLinearInterpolation(
- double v000, double v100, double v010, double v110,
- double v001, double v101, double v011, double v111,
- double x, double y, double z)
-{
- /*double tx = easeCurve(x);
- double ty = easeCurve(y);
- double tz = easeCurve(z);*/
- double tx = x;
- double ty = y;
- double tz = z;
- return(
- v000*(1-tx)*(1-ty)*(1-tz) +
- v100*tx*(1-ty)*(1-tz) +
- v010*(1-tx)*ty*(1-tz) +
- v110*tx*ty*(1-tz) +
- v001*(1-tx)*(1-ty)*tz +
- v101*tx*(1-ty)*tz +
- v011*(1-tx)*ty*tz +
- v111*tx*ty*tz
- );
+
+float biLinearInterpolation(float v00, float v10,
+ float v01, float v11,
+ float x, float y) {
+ float tx = easeCurve(x);
+ float ty = easeCurve(y);
+ float u = linearInterpolation(v00, v10, tx);
+ float v = linearInterpolation(v01, v11, tx);
+ return linearInterpolation(u, v, ty);
}
-double noise2d(int x, int y, int seed)
-{
- 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.0 - (double)n/1073741824;
+
+float biLinearInterpolationNoEase(float x0y0, float x1y0,
+ float x0y1, float x1y1,
+ float x, float y) {
+ float u = linearInterpolation(x0y0, x1y0, x);
+ float v = linearInterpolation(x0y1, x1y1, x);
+ return linearInterpolation(u, v, y);
+}
+
+
+float triLinearInterpolation(
+ float v000, float v100, float v010, float v110,
+ float v001, float v101, float v011, float v111,
+ float x, float y, float z) {
+ float u = biLinearInterpolationNoEase(v000, v100, v010, v110, x, y);
+ float v = biLinearInterpolationNoEase(v001, v101, v011, v111, x, y);
+ return linearInterpolation(u, v, z);
}
-double noise3d(int x, int y, int z, int seed)
+
+#if 0
+float triLinearInterpolation(
+ float v000, float v100, float v010, float v110,
+ float v001, float v101, float v011, float v111,
+ float x, float y, float z)
{
- 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.0 - (double)n/1073741824;
+ /*float tx = easeCurve(x);
+ float ty = easeCurve(y);
+ float tz = easeCurve(z);*/
+ float tx = x;
+ float ty = y;
+ float tz = z;
+ return(
+ v000 * (1 - tx) * (1 - ty) * (1 - tz) +
+ v100 * tx * (1 - ty) * (1 - tz) +
+ v010 * (1 - tx) * ty * (1 - tz) +
+ v110 * tx * ty * (1 - tz) +
+ v001 * (1 - tx) * (1 - ty) * tz +
+ v101 * tx * (1 - ty) * tz +
+ v011 * (1 - tx) * ty * tz +
+ v111 * tx * ty * tz
+ );
}
+#endif
+
#if 0
-double noise2d_gradient(double x, double y, int seed)
+float noise2d_gradient(float x, float y, int seed)
{
// Calculate the integer coordinates
int x0 = (x > 0.0 ? (int)x : (int)x - 1);
int y0 = (y > 0.0 ? (int)y : (int)y - 1);
// Calculate the remaining part of the coordinates
- double xl = x - (double)x0;
- double yl = y - (double)y0;
+ float xl = x - (float)x0;
+ float yl = y - (float)y0;
// Calculate random cosine lookup table indices for the integer corners.
// They are looked up as unit vector gradients from the lookup table.
int n00 = (int)((noise2d(x0, y0, seed)+1)*8);
@@ -111,119 +138,126 @@ double noise2d_gradient(double x, double y, int seed)
int n01 = (int)((noise2d(x0, y0+1, seed)+1)*8);
int n11 = (int)((noise2d(x0+1, y0+1, seed)+1)*8);
// Make a dot product for the gradients and the positions, to get the values
- double s = dotProduct(cos_lookup[n00], cos_lookup[(n00+12)%16], xl, yl);
- double u = dotProduct(-cos_lookup[n10], cos_lookup[(n10+12)%16], 1.-xl, yl);
- double v = dotProduct(cos_lookup[n01], -cos_lookup[(n01+12)%16], xl, 1.-yl);
- double w = dotProduct(-cos_lookup[n11], -cos_lookup[(n11+12)%16], 1.-xl, 1.-yl);
+ float s = dotProduct(cos_lookup[n00], cos_lookup[(n00+12)%16], xl, yl);
+ float u = dotProduct(-cos_lookup[n10], cos_lookup[(n10+12)%16], 1.-xl, yl);
+ float v = dotProduct(cos_lookup[n01], -cos_lookup[(n01+12)%16], xl, 1.-yl);
+ float w = dotProduct(-cos_lookup[n11], -cos_lookup[(n11+12)%16], 1.-xl, 1.-yl);
// Interpolate between the values
return biLinearInterpolation(s,u,v,w,xl,yl);
}
#endif
-#if 1
-double noise2d_gradient(double x, double y, int seed)
+
+float noise2d_gradient(float x, float y, int seed)
{
// Calculate the integer coordinates
- int x0 = (x > 0.0 ? (int)x : (int)x - 1);
- int y0 = (y > 0.0 ? (int)y : (int)y - 1);
+ int x0 = myfloor(x);
+ int y0 = myfloor(y);
// Calculate the remaining part of the coordinates
- double xl = x - (double)x0;
- double yl = y - (double)y0;
- // Get values for corners of cube
- double v00 = noise2d(x0, y0, seed);
- double v10 = noise2d(x0+1, y0, seed);
- double v01 = noise2d(x0, y0+1, seed);
- double v11 = noise2d(x0+1, y0+1, seed);
+ float xl = x - (float)x0;
+ float yl = y - (float)y0;
+ // Get values for corners of square
+ float v00 = noise2d(x0, y0, seed);
+ float v10 = noise2d(x0+1, y0, seed);
+ float v01 = noise2d(x0, y0+1, seed);
+ float v11 = noise2d(x0+1, y0+1, seed);
// Interpolate
return biLinearInterpolation(v00,v10,v01,v11,xl,yl);
}
-#endif
-double noise3d_gradient(double x, double y, double z, int seed)
+
+float noise3d_gradient(float x, float y, float z, int seed)
{
// Calculate the integer coordinates
- int x0 = (x > 0.0 ? (int)x : (int)x - 1);
- int y0 = (y > 0.0 ? (int)y : (int)y - 1);
- int z0 = (z > 0.0 ? (int)z : (int)z - 1);
+ int x0 = myfloor(x);
+ int y0 = myfloor(y);
+ int z0 = myfloor(z);
// Calculate the remaining part of the coordinates
- double xl = x - (double)x0;
- double yl = y - (double)y0;
- double zl = z - (double)z0;
+ float xl = x - (float)x0;
+ float yl = y - (float)y0;
+ float zl = z - (float)z0;
// Get values for corners of cube
- double v000 = noise3d(x0, y0, z0, seed);
- double v100 = noise3d(x0+1, y0, z0, seed);
- double v010 = noise3d(x0, y0+1, z0, seed);
- double v110 = noise3d(x0+1, y0+1, z0, seed);
- double v001 = noise3d(x0, y0, z0+1, seed);
- double v101 = noise3d(x0+1, y0, z0+1, seed);
- double v011 = noise3d(x0, y0+1, z0+1, seed);
- double v111 = noise3d(x0+1, y0+1, z0+1, seed);
+ float v000 = noise3d(x0, y0, z0, seed);
+ float v100 = noise3d(x0 + 1, y0, z0, seed);
+ float v010 = noise3d(x0, y0 + 1, z0, seed);
+ float v110 = noise3d(x0 + 1, y0 + 1, z0, seed);
+ float v001 = noise3d(x0, y0, z0 + 1, seed);
+ float v101 = noise3d(x0 + 1, y0, z0 + 1, seed);
+ float v011 = noise3d(x0, y0 + 1, z0 + 1, seed);
+ float v111 = noise3d(x0 + 1, y0 + 1, z0 + 1, seed);
// Interpolate
- return triLinearInterpolation(v000,v100,v010,v110,v001,v101,v011,v111,xl,yl,zl);
+ return triLinearInterpolation(v000, v100, v010, v110,
+ v001, v101, v011, v111,
+ xl, yl, zl);
}
-double noise2d_perlin(double x, double y, int seed,
- int octaves, double persistence)
+
+float noise2d_perlin(float x, float y, int seed,
+ int octaves, float persistence)
{
- double a = 0;
- double f = 1.0;
- double g = 1.0;
- for(int i=0; i<octaves; i++)
+ float a = 0;
+ float f = 1.0;
+ float g = 1.0;
+ for (int i = 0; i < octaves; i++)
{
- a += g * noise2d_gradient(x*f, y*f, seed+i);
+ a += g * noise2d_gradient(x * f, y * f, seed + i);
f *= 2.0;
g *= persistence;
}
return a;
}
-double noise2d_perlin_abs(double x, double y, int seed,
- int octaves, double persistence)
+
+float noise2d_perlin_abs(float x, float y, int seed,
+ int octaves, float persistence)
{
- double a = 0;
- double f = 1.0;
- double g = 1.0;
- for(int i=0; i<octaves; i++)
+ float a = 0;
+ float f = 1.0;
+ float g = 1.0;
+ for (int i = 0; i < octaves; i++)
{
- a += g * fabs(noise2d_gradient(x*f, y*f, seed+i));
+ a += g * fabs(noise2d_gradient(x * f, y * f, seed + i));
f *= 2.0;
g *= persistence;
}
return a;
}
-double noise3d_perlin(double x, double y, double z, int seed,
- int octaves, double persistence)
+
+float noise3d_perlin(float x, float y, float z, int seed,
+ int octaves, float persistence)
{
- double a = 0;
- double f = 1.0;
- double g = 1.0;
- for(int i=0; i<octaves; i++)
+ float a = 0;
+ float f = 1.0;
+ float g = 1.0;
+ for (int i = 0; i < octaves; i++)
{
- a += g * noise3d_gradient(x*f, y*f, z*f, seed+i);
+ a += g * noise3d_gradient(x * f, y * f, z * f, seed + i);
f *= 2.0;
g *= persistence;
}
return a;
}
-double noise3d_perlin_abs(double x, double y, double z, int seed,
- int octaves, double persistence)
+
+float noise3d_perlin_abs(float x, float y, float z, int seed,
+ int octaves, float persistence)
{
- double a = 0;
- double f = 1.0;
- double g = 1.0;
- for(int i=0; i<octaves; i++)
+ float a = 0;
+ float f = 1.0;
+ float g = 1.0;
+ for (int i = 0; i < octaves; i++)
{
- a += g * fabs(noise3d_gradient(x*f, y*f, z*f, seed+i));
+ a += g * fabs(noise3d_gradient(x * f, y * f, z * f, seed + i));
f *= 2.0;
g *= persistence;
}
return a;
}
+
// -1->0, 0->1, 1->0
-double contour(double v)
+float contour(float v)
{
v = fabs(v);
if(v >= 1.0)
@@ -231,195 +265,318 @@ double contour(double v)
return (1.0-v);
}
-double noise3d_param(const NoiseParams &param, double x, double y, double z)
-{
- double s = param.pos_scale;
- x /= s;
- y /= s;
- z /= s;
- if(param.type == NOISE_CONSTANT_ONE)
- {
- return 1.0;
- }
- else if(param.type == NOISE_PERLIN)
- {
- return param.noise_scale*noise3d_perlin(x,y,z, param.seed,
- param.octaves,
- param.persistence);
- }
- else if(param.type == NOISE_PERLIN_ABS)
- {
- return param.noise_scale*noise3d_perlin_abs(x,y,z, param.seed,
- param.octaves,
- param.persistence);
- }
- else if(param.type == NOISE_PERLIN_CONTOUR)
- {
- return contour(param.noise_scale*noise3d_perlin(x,y,z,
- param.seed, param.octaves,
- param.persistence));
- }
- else if(param.type == NOISE_PERLIN_CONTOUR_FLIP_YZ)
- {
- return contour(param.noise_scale*noise3d_perlin(x,z,y,
- param.seed, param.octaves,
- param.persistence));
- }
- else assert(0);
+///////////////////////// [ New perlin stuff ] ////////////////////////////
+
+
+Noise::Noise(NoiseParams *np, int seed, int sx, int sy) {
+ init(np, seed, sx, sy, 1);
}
-/*
- NoiseBuffer
-*/
-NoiseBuffer::NoiseBuffer():
- m_data(NULL)
-{
+Noise::Noise(NoiseParams *np, int seed, int sx, int sy, int sz) {
+ init(np, seed, sx, sy, sz);
}
-NoiseBuffer::~NoiseBuffer()
-{
- clear();
+
+void Noise::init(NoiseParams *np, int seed, int sx, int sy, int sz) {
+ this->np = np;
+ this->seed = seed;
+ this->sx = sx;
+ this->sy = sy;
+ this->sz = sz;
+
+ this->noisebuf = NULL;
+ resizeNoiseBuf(sz > 1);
+
+ this->buf = new float[sx * sy * sz];
+ this->result = new float[sx * sy * sz];
}
-void NoiseBuffer::clear()
-{
- if(m_data)
- delete[] m_data;
- m_data = NULL;
- m_size_x = 0;
- m_size_y = 0;
- m_size_z = 0;
+
+Noise::~Noise() {
+ delete[] buf;
+ delete[] result;
+ delete[] noisebuf;
}
-void NoiseBuffer::create(const NoiseParams &param,
- double first_x, double first_y, double first_z,
- double last_x, double last_y, double last_z,
- double samplelength_x, double samplelength_y, double samplelength_z)
-{
- clear();
-
- m_start_x = first_x - samplelength_x;
- m_start_y = first_y - samplelength_y;
- m_start_z = first_z - samplelength_z;
- m_samplelength_x = samplelength_x;
- m_samplelength_y = samplelength_y;
- m_samplelength_z = samplelength_z;
-
- m_size_x = (last_x - m_start_x)/samplelength_x + 2;
- m_size_y = (last_y - m_start_y)/samplelength_y + 2;
- m_size_z = (last_z - m_start_z)/samplelength_z + 2;
-
- m_data = new double[m_size_x*m_size_y*m_size_z];
-
- for(int x=0; x<m_size_x; x++)
- for(int y=0; y<m_size_y; y++)
- for(int z=0; z<m_size_z; z++)
- {
- double xd = (m_start_x + (double)x*m_samplelength_x);
- double yd = (m_start_y + (double)y*m_samplelength_y);
- double zd = (m_start_z + (double)z*m_samplelength_z);
- double a = noise3d_param(param, xd,yd,zd);
- intSet(x,y,z, a);
- }
+
+void Noise::setSize(int sx, int sy) {
+ setSize(sx, sy, 1);
}
-void NoiseBuffer::multiply(const NoiseParams &param)
-{
- assert(m_data != NULL);
- for(int x=0; x<m_size_x; x++)
- for(int y=0; y<m_size_y; y++)
- for(int z=0; z<m_size_z; z++)
- {
- double xd = (m_start_x + (double)x*m_samplelength_x);
- double yd = (m_start_y + (double)y*m_samplelength_y);
- double zd = (m_start_z + (double)z*m_samplelength_z);
- double a = noise3d_param(param, xd,yd,zd);
- intMultiply(x,y,z, a);
- }
+void Noise::setSize(int sx, int sy, int sz) {
+ this->sx = sx;
+ this->sy = sy;
+ this->sz = sz;
+
+ this->noisebuf = NULL;
+ resizeNoiseBuf(sz > 1);
+
+ delete[] buf;
+ delete[] result;
+ this->buf = new float[sx * sy * sz];
+ this->result = new float[sx * sy * sz];
}
-// Deprecated
-void NoiseBuffer::create(int seed, int octaves, double persistence,
- bool abs,
- double first_x, double first_y, double first_z,
- double last_x, double last_y, double last_z,
- double samplelength_x, double samplelength_y, double samplelength_z)
-{
- NoiseParams param;
- param.type = abs ? NOISE_PERLIN_ABS : NOISE_PERLIN;
- param.seed = seed;
- param.octaves = octaves;
- param.persistence = persistence;
-
- create(param, first_x, first_y, first_z,
- last_x, last_y, last_z,
- samplelength_x, samplelength_y, samplelength_z);
+
+void Noise::setSpreadFactor(v3f spread) {
+ this->np->spread = spread;
+
+ resizeNoiseBuf(sz > 1);
}
-void NoiseBuffer::intSet(int x, int y, int z, double d)
-{
- int i = m_size_x*m_size_y*z + m_size_x*y + x;
- assert(i >= 0);
- assert(i < m_size_x*m_size_y*m_size_z);
- m_data[i] = d;
+
+void Noise::setOctaves(int octaves) {
+ this->np->octaves = octaves;
+
+ resizeNoiseBuf(sz > 1);
}
-void NoiseBuffer::intMultiply(int x, int y, int z, double d)
-{
- int i = m_size_x*m_size_y*z + m_size_x*y + x;
- assert(i >= 0);
- assert(i < m_size_x*m_size_y*m_size_z);
- m_data[i] = m_data[i] * d;
+
+void Noise::resizeNoiseBuf(bool is3d) {
+ int nlx, nly, nlz;
+ float ofactor;
+
+ //maximum possible spread value factor
+ ofactor = (float)(1 << (np->octaves - 1));
+
+ //noise lattice point count
+ //(int)(sz * spread * ofactor) is # of lattice points crossed due to length
+ // + 2 for the two initial endpoints
+ // + 1 for potentially crossing a boundary due to offset
+ nlx = (int)(sx * ofactor / np->spread.X) + 3;
+ nly = (int)(sy * ofactor / np->spread.Y) + 3;
+ nlz = is3d ? (int)(sz * ofactor / np->spread.Z) + 3 : 1;
+
+ if (noisebuf)
+ delete[] noisebuf;
+ noisebuf = new float[nlx * nly * nlz];
}
-double NoiseBuffer::intGet(int x, int y, int z)
-{
- int i = m_size_x*m_size_y*z + m_size_x*y + x;
- assert(i >= 0);
- assert(i < m_size_x*m_size_y*m_size_z);
- return m_data[i];
+
+/*
+ * NB: This algorithm is not optimal in terms of space complexity. The entire
+ * integer lattice of noise points could be done as 2 lines instead, and for 3D,
+ * 2 lines + 2 planes.
+ * However, this would require the noise calls to be interposed with the
+ * interpolation loops, which may trash the icache, leading to lower overall
+ * performance.
+ * Another optimization that could save half as many noise calls is to carry over
+ * values from the previous noise lattice as midpoints in the new lattice for the
+ * next octave.
+ */
+#define idx(x, y) ((y) * nlx + (x))
+void Noise::gradientMap2D(float x, float y, float step_x, float step_y, int seed) {
+ float v00, v01, v10, v11, u, v, orig_u;
+ int index, i, j, x0, y0, noisex, noisey;
+ int nlx, nly;
+
+ x0 = floor(x);
+ y0 = floor(y);
+ u = x - (float)x0;
+ v = y - (float)y0;
+ orig_u = u;
+
+ //calculate noise point lattice
+ nlx = (int)(u + sx * step_x) + 2;
+ nly = (int)(v + sy * step_y) + 2;
+ index = 0;
+ for (j = 0; j != nly; j++)
+ for (i = 0; i != nlx; i++)
+ noisebuf[index++] = noise2d(x0 + i, y0 + j, seed);
+
+ //calculate interpolations
+ index = 0;
+ noisey = 0;
+ for (j = 0; j != sy; j++) {
+ v00 = noisebuf[idx(0, noisey)];
+ v10 = noisebuf[idx(1, noisey)];
+ v01 = noisebuf[idx(0, noisey + 1)];
+ v11 = noisebuf[idx(1, noisey + 1)];
+
+ u = orig_u;
+ noisex = 0;
+ for (i = 0; i != sx; i++) {
+ buf[index++] = biLinearInterpolation(v00, v10, v01, v11, u, v);
+ u += step_x;
+ if (u >= 1.0) {
+ u -= 1.0;
+ noisex++;
+ v00 = v10;
+ v01 = v11;
+ v10 = noisebuf[idx(noisex + 1, noisey)];
+ v11 = noisebuf[idx(noisex + 1, noisey + 1)];
+ }
+ }
+
+ v += step_y;
+ if (v >= 1.0) {
+ v -= 1.0;
+ noisey++;
+ }
+ }
+}
+#undef idx
+
+
+#define idx(x, y, z) ((z) * nly * nlx + (y) * nlx + (x))
+void Noise::gradientMap3D(float x, float y, float z,
+ float step_x, float step_y, float step_z,
+ int seed) {
+ float v000, v010, v100, v110;
+ float v001, v011, v101, v111;
+ float u, v, w, orig_u, orig_v;
+ int index, i, j, k, x0, y0, z0, noisex, noisey, noisez;
+ int nlx, nly, nlz;
+
+ x0 = floor(x);
+ y0 = floor(y);
+ z0 = floor(z);
+ u = x - (float)x0;
+ v = y - (float)y0;
+ w = z - (float)z0;
+ orig_u = u;
+ orig_v = v;
+
+ //calculate noise point lattice
+ nlx = (int)(u + sx * step_x) + 2;
+ nly = (int)(v + sy * step_y) + 2;
+ nlz = (int)(w + sz * step_z) + 2;
+ index = 0;
+ for (k = 0; k != nlz; k++)
+ for (j = 0; j != nly; j++)
+ for (i = 0; i != nlx; i++)
+ noisebuf[index++] = noise3d(x0 + i, y0 + j, z0 + k, seed);
+
+ //calculate interpolations
+ index = 0;
+ noisey = 0;
+ noisez = 0;
+ for (k = 0; k != sz; k++) {
+ v = orig_v;
+ noisey = 0;
+ for (j = 0; j != sy; j++) {
+ v000 = noisebuf[idx(0, noisey, noisez)];
+ v100 = noisebuf[idx(1, noisey, noisez)];
+ v010 = noisebuf[idx(0, noisey + 1, noisez)];
+ v110 = noisebuf[idx(1, noisey + 1, noisez)];
+ v001 = noisebuf[idx(0, noisey, noisez + 1)];
+ v101 = noisebuf[idx(1, noisey, noisez + 1)];
+ v011 = noisebuf[idx(0, noisey + 1, noisez + 1)];
+ v111 = noisebuf[idx(1, noisey + 1, noisez + 1)];
+
+ u = orig_u;
+ noisex = 0;
+ for (i = 0; i != sx; i++) {
+ buf[index++] = triLinearInterpolation(
+ v000, v100, v010, v110,
+ v001, v101, v011, v111,
+ u, v, w);
+ u += step_x;
+ if (u >= 1.0) {
+ u -= 1.0;
+ noisex++;
+ v000 = v100;
+ v010 = v110;
+ v100 = noisebuf[idx(noisex + 1, noisey, noisez)];
+ v110 = noisebuf[idx(noisex + 1, noisey + 1, noisez)];
+ v001 = v101;
+ v011 = v111;
+ v101 = noisebuf[idx(noisex + 1, noisey, noisez + 1)];
+ v111 = noisebuf[idx(noisex + 1, noisey + 1, noisez + 1)];
+ }
+ }
+
+ v += step_y;
+ if (v >= 1.0) {
+ v -= 1.0;
+ noisey++;
+ }
+ }
+
+ w += step_z;
+ if (w >= 1.0) {
+ w -= 1.0;
+ noisez++;
+ }
+ }
}
+#undef idx
-double NoiseBuffer::get(double x, double y, double z)
-{
- x -= m_start_x;
- y -= m_start_y;
- z -= m_start_z;
- x /= m_samplelength_x;
- y /= m_samplelength_y;
- z /= m_samplelength_z;
- // Calculate the integer coordinates
- int x0 = (x > 0.0 ? (int)x : (int)x - 1);
- int y0 = (y > 0.0 ? (int)y : (int)y - 1);
- int z0 = (z > 0.0 ? (int)z : (int)z - 1);
- // Calculate the remaining part of the coordinates
- double xl = x - (double)x0;
- double yl = y - (double)y0;
- double zl = z - (double)z0;
- // Get values for corners of cube
- double v000 = intGet(x0, y0, z0);
- double v100 = intGet(x0+1, y0, z0);
- double v010 = intGet(x0, y0+1, z0);
- double v110 = intGet(x0+1, y0+1, z0);
- double v001 = intGet(x0, y0, z0+1);
- double v101 = intGet(x0+1, y0, z0+1);
- double v011 = intGet(x0, y0+1, z0+1);
- double v111 = intGet(x0+1, y0+1, z0+1);
- // Interpolate
- return triLinearInterpolation(v000,v100,v010,v110,v001,v101,v011,v111,xl,yl,zl);
+
+float *Noise::perlinMap2D(float x, float y) {
+ float a = 0.0, f = 1.0, g = 1.0;
+ int i, j, index, oct;
+
+ x /= np->spread.X;
+ y /= np->spread.Y;
+
+ memset(result, 0, sizeof(float) * sx * sy);
+
+ for (oct = 0; oct < np->octaves; oct++) {
+ gradientMap2D(x * f, y * f,
+ f / np->spread.X, f / np->spread.Y,
+ seed + np->seed + oct);
+
+ index = 0;
+ for (j = 0; j != sy; j++) {
+ for (i = 0; i != sx; i++) {
+ result[index] += g * buf[index];
+ index++;
+ }
+ }
+
+ f *= 2.0;
+ g *= np->persist;
+ }
+
+ return result;
}
-/*bool NoiseBuffer::contains(double x, double y, double z)
-{
- x -= m_start_x;
- y -= m_start_y;
- z -= m_start_z;
- x /= m_samplelength_x;
- y /= m_samplelength_y;
- z /= m_samplelength_z;
- if(x <= 0.0 || x >= m_size_x)
-}*/
+float *Noise::perlinMap3D(float x, float y, float z) {
+ float a = 0.0, f = 1.0, g = 1.0;
+ int i, j, k, index, oct;
+
+ x /= np->spread.X;
+ y /= np->spread.Y;
+ z /= np->spread.Z;
+
+ memset(result, 0, sizeof(float) * sx * sy * sz);
+
+ for (oct = 0; oct < np->octaves; oct++) {
+ gradientMap3D(x * f, y * f, z * f,
+ f / np->spread.X, f / np->spread.Y, f / np->spread.Z,
+ seed + np->seed + oct);
+
+ index = 0;
+ for (k = 0; k != sz; k++) {
+ for (j = 0; j != sy; j++) {
+ for (i = 0; i != sx; i++) {
+ result[index] += g * buf[index];
+ index++;
+ }
+ }
+ }
+
+ f *= 2.0;
+ g *= np->persist;
+ }
+
+ return result;
+}
+
+
+void Noise::transformNoiseMap() {
+ int i = 0;
+ for (int z = 0; z != sz; z++) {
+ for (int y = 0; y != sy; y++) {
+ for (int x = 0; x != sx; x++) {
+ result[i] = result[i] * np->scale + np->offset;
+ i++;
+ }
+ }
+ }
+}
diff --git a/src/noise.h b/src/noise.h
index 670c2eddc..e725b4e47 100644
--- a/src/noise.h
+++ b/src/noise.h
@@ -21,6 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#define NOISE_HEADER
#include "debug.h"
+#include "irr_v3d.h"
class PseudoRandom
{
@@ -59,91 +60,105 @@ private:
int m_next;
};
-double easeCurve(double t);
-
-// Return value: -1 ... 1
-double noise2d(int x, int y, int seed);
-double noise3d(int x, int y, int z, int seed);
-
-double noise2d_gradient(double x, double y, int seed);
-double noise3d_gradient(double x, double y, double z, int seed);
-
-double noise2d_perlin(double x, double y, int seed,
- int octaves, double persistence);
-
-double noise2d_perlin_abs(double x, double y, int seed,
- int octaves, double persistence);
-
-double noise3d_perlin(double x, double y, double z, int seed,
- int octaves, double persistence);
-
-double noise3d_perlin_abs(double x, double y, double z, int seed,
- int octaves, double persistence);
-
-enum NoiseType
-{
- NOISE_CONSTANT_ONE,
- NOISE_PERLIN,
- NOISE_PERLIN_ABS,
- NOISE_PERLIN_CONTOUR,
- NOISE_PERLIN_CONTOUR_FLIP_YZ,
-};
-
-struct NoiseParams
-{
- NoiseType type;
+struct NoiseParams {
+ float offset;
+ float scale;
+ v3f spread;
int seed;
int octaves;
- double persistence;
- double pos_scale;
- double noise_scale; // Useful for contour noises
-
- NoiseParams(NoiseType type_=NOISE_PERLIN, int seed_=0,
- int octaves_=3, double persistence_=0.5,
- double pos_scale_=100.0, double noise_scale_=1.0):
- type(type_),
- seed(seed_),
- octaves(octaves_),
- persistence(persistence_),
- pos_scale(pos_scale_),
- noise_scale(noise_scale_)
- {
- }
+ float persist;
};
-double noise3d_param(const NoiseParams &param, double x, double y, double z);
-class NoiseBuffer
-{
-public:
- NoiseBuffer();
- ~NoiseBuffer();
-
- void clear();
- void create(const NoiseParams &param,
- double first_x, double first_y, double first_z,
- double last_x, double last_y, double last_z,
- double samplelength_x, double samplelength_y, double samplelength_z);
- void multiply(const NoiseParams &param);
- // Deprecated
- void create(int seed, int octaves, double persistence,
- bool abs,
- double first_x, double first_y, double first_z,
- double last_x, double last_y, double last_z,
- double samplelength_x, double samplelength_y, double samplelength_z);
-
- void intSet(int x, int y, int z, double d);
- void intMultiply(int x, int y, int z, double d);
- double intGet(int x, int y, int z);
- double get(double x, double y, double z);
- //bool contains(double x, double y, double z);
+// Convenience macros for getting/setting NoiseParams in Settings
+#define getNoiseParams(x) getStruct<NoiseParams>((x), "f,f,v3,s32,s32,f")
+#define setNoiseParams(x, y) setStruct((x), "f,f,v3,s32,s32,f", (y))
-private:
- double *m_data;
- double m_start_x, m_start_y, m_start_z;
- double m_samplelength_x, m_samplelength_y, m_samplelength_z;
- int m_size_x, m_size_y, m_size_z;
+class Noise {
+public:
+ NoiseParams *np;
+ int seed;
+ int sx;
+ int sy;
+ int sz;
+ float *noisebuf;
+ float *buf;
+ float *result;
+
+ Noise(NoiseParams *np, int seed, int sx, int sy);
+ Noise(NoiseParams *np, int seed, int sx, int sy, int sz);
+ ~Noise();
+
+ void init(NoiseParams *np, int seed, int sx, int sy, int sz);
+ void setSize(int sx, int sy);
+ void setSize(int sx, int sy, int sz);
+ void setSpreadFactor(v3f spread);
+ void setOctaves(int octaves);
+ void resizeNoiseBuf(bool is3d);
+
+ void gradientMap2D(
+ float x, float y,
+ float step_x, float step_y,
+ int seed);
+ void gradientMap3D(
+ float x, float y, float z,
+ float step_x, float step_y, float step_z,
+ int seed);
+ float *perlinMap2D(float x, float y);
+ float *perlinMap3D(float x, float y, float z);
+ void transformNoiseMap();
};
+// Return value: -1 ... 1
+float noise2d(int x, int y, int seed);
+float noise3d(int x, int y, int z, int seed);
+
+float noise2d_gradient(float x, float y, int seed);
+float noise3d_gradient(float x, float y, float z, int seed);
+
+float noise2d_perlin(float x, float y, int seed,
+ int octaves, float persistence);
+
+float noise2d_perlin_abs(float x, float y, int seed,
+ int octaves, float persistence);
+
+float noise3d_perlin(float x, float y, float z, int seed,
+ int octaves, float persistence);
+
+float noise3d_perlin_abs(float x, float y, float z, int seed,
+ int octaves, float persistence);
+
+inline float easeCurve(float t) {
+ return t * t * t * (t * (6.f * t - 15.f) + 10.f);
+}
+
+#define NoisePerlin2D(np, x, y, s) \
+ ((np)->offset + (np)->scale * noise2d_perlin( \
+ (float)(x) / (np)->spread.X, \
+ (float)(y) / (np)->spread.Y, \
+ (s) + (np)->seed, (np)->octaves, (np)->persist))
+
+#define NoisePerlin2DNoTxfm(np, x, y, s) \
+ (noise2d_perlin( \
+ (float)(x) / (np)->spread.X, \
+ (float)(y) / (np)->spread.Y, \
+ (s) + (np)->seed, (np)->octaves, (np)->persist))
+
+#define NoisePerlin2DPosOffset(np, x, xoff, y, yoff, s) \
+ ((np)->offset + (np)->scale * noise2d_perlin( \
+ (float)(xoff) + (float)(x) / (np)->spread.X, \
+ (float)(yoff) + (float)(y) / (np)->spread.Y, \
+ (s) + (np)->seed, (np)->octaves, (np)->persist))
+
+#define NoisePerlin2DNoTxfmPosOffset(np, x, xoff, y, yoff, s) \
+ (noise2d_perlin( \
+ (float)(xoff) + (float)(x) / (np)->spread.X, \
+ (float)(yoff) + (float)(y) / (np)->spread.Y, \
+ (s) + (np)->seed, (np)->octaves, (np)->persist))
+
+#define NoisePerlin3D(np, x, y, z, s) ((np)->offset + (np)->scale * \
+ noise2d_perlin((float)(x) / (np)->spread.X, (float)(y) / (np)->spread.Y, \
+ (float)(z) / (np)->spread.Z, (s) + (np)->seed, (np)->octaves, (np)->persist))
+
#endif
diff --git a/src/player.cpp b/src/player.cpp
index 36f12c77b..86d3ae184 100644
--- a/src/player.cpp
+++ b/src/player.cpp
@@ -27,10 +27,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
Player::Player(IGameDef *gamedef):
touching_ground(false),
- in_water(false),
- in_water_stable(false),
+ in_liquid(false),
+ in_liquid_stable(false),
+ liquid_viscosity(0),
is_climbing(false),
- swimming_up(false),
+ swimming_vertical(false),
camera_barely_in_ceiling(false),
inventory(gamedef->idef()),
hp(PLAYER_MAX_HP),
@@ -56,19 +57,35 @@ Player::Player(IGameDef *gamedef):
"list[current_player;main;0,3.5;8,4;]"
"list[current_player;craft;3,0;3,3;]"
"list[current_player;craftpreview;7,1;1,1;]";
+
+ // Initialize movement settings at default values, so movement can work if the server fails to send them
+ movement_acceleration_default = 3 * BS;
+ movement_acceleration_air = 2 * BS;
+ movement_acceleration_fast = 10 * BS;
+ movement_speed_walk = 4 * BS;
+ movement_speed_crouch = 1.35 * BS;
+ movement_speed_fast = 20 * BS;
+ movement_speed_climb = 2 * BS;
+ movement_speed_jump = 6.5 * BS;
+ movement_liquid_fluidity = 1 * BS;
+ movement_liquid_fluidity_smooth = 0.5 * BS;
+ movement_liquid_sink = 10 * BS;
+ movement_gravity = 9.81 * BS;
}
Player::~Player()
{
}
-// Y direction is ignored
-void Player::accelerate(v3f target_speed, f32 max_increase)
+// 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_wanted = d_wanted.getLength();
- f32 dl = dl_wanted;
+ f32 dl = d_wanted.getLength();
if(dl > max_increase)
dl = max_increase;
@@ -76,7 +93,6 @@ void Player::accelerate(v3f target_speed, f32 max_increase)
m_speed.X += d.X;
m_speed.Z += d.Z;
- //m_speed += d;
#if 0 // old code
if(m_speed.X < target_speed.X - max_increase)
@@ -99,6 +115,32 @@ void Player::accelerate(v3f target_speed, f32 max_increase)
#endif
}
+// 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;
+
+#if 0 // old code
+ if(m_speed.Y < target_speed.Y - max_increase)
+ m_speed.Y += max_increase;
+ else if(m_speed.Y > target_speed.Y + max_increase)
+ m_speed.Y -= max_increase;
+ else if(m_speed.Y < target_speed.Y)
+ m_speed.Y = target_speed.Y;
+ else if(m_speed.Y > target_speed.Y)
+ m_speed.Y = target_speed.Y;
+#endif
+}
+
v3s16 Player::getLightPosition() const
{
return floatToInt(m_position + v3f(0,BS+BS/2,0), BS);
diff --git a/src/player.h b/src/player.h
index 67b02c344..770afdb37 100644
--- a/src/player.h
+++ b/src/player.h
@@ -108,8 +108,8 @@ public:
m_speed = speed;
}
- // Y direction is ignored
- void accelerate(v3f target_speed, f32 max_increase);
+ void accelerateHorizontal(v3f target_speed, f32 max_increase);
+ void accelerateVertical(v3f target_speed, f32 max_increase);
v3f getPosition()
{
@@ -196,17 +196,32 @@ public:
bool touching_ground;
// This oscillates so that the player jumps a bit above the surface
- bool in_water;
+ bool in_liquid;
// This is more stable and defines the maximum speed of the player
- bool in_water_stable;
+ bool in_liquid_stable;
+ // Gets the viscosity of water to calculate friction
+ u8 liquid_viscosity;
bool is_climbing;
- bool swimming_up;
+ bool swimming_vertical;
bool camera_barely_in_ceiling;
u8 light;
Inventory inventory;
+ f32 movement_acceleration_default;
+ f32 movement_acceleration_air;
+ f32 movement_acceleration_fast;
+ f32 movement_speed_walk;
+ f32 movement_speed_crouch;
+ f32 movement_speed_fast;
+ f32 movement_speed_climb;
+ f32 movement_speed_jump;
+ f32 movement_liquid_fluidity;
+ f32 movement_liquid_fluidity_smooth;
+ f32 movement_liquid_sink;
+ f32 movement_gravity;
+
u16 hp;
float hurt_tilt_timer;
diff --git a/src/porting.cpp b/src/porting.cpp
index 945aea6eb..f8a2cca5c 100644
--- a/src/porting.cpp
+++ b/src/porting.cpp
@@ -23,6 +23,17 @@ with this program; if not, write to the Free Software Foundation, Inc.,
See comments in porting.h
*/
+#if defined(linux)
+ #include <unistd.h>
+#elif defined(__APPLE__)
+ #include <unistd.h>
+ #include <mach-o/dyld.h>
+#elif defined(__FreeBSD__)
+ #include <unistd.h>
+ #include <sys/types.h>
+ #include <sys/sysctl.h>
+#endif
+
#include "porting.h"
#include "config.h"
#include "debug.h"
@@ -58,7 +69,7 @@ void sigint_handler(int sig)
{
dstream<<DTIME<<"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;
@@ -79,8 +90,7 @@ void signal_handler_init(void)
#else // _WIN32
#include <signal.h>
- #include <windows.h>
-
+
BOOL WINAPI event_handler(DWORD sig)
{
switch(sig)
@@ -110,10 +120,10 @@ void signal_handler_init(void)
case CTRL_BREAK_EVENT:
break;
}
-
+
return TRUE;
}
-
+
void signal_handler_init(void)
{
SetConsoleCtrlHandler( (PHANDLER_ROUTINE)event_handler,TRUE);
@@ -166,17 +176,16 @@ void initializePaths()
Windows
*/
#if defined(_WIN32)
- #include <windows.h>
const DWORD buflen = 1000;
char buf[buflen];
DWORD len;
-
+
// Find path of executable and set path_share relative to it
len = GetModuleFileName(GetModuleHandle(NULL), buf, buflen);
assert(len < buflen);
pathRemoveFile(buf, '\\');
-
+
if(detectMSVCBuildDir(buf)){
infostream<<"MSVC build directory detected"<<std::endl;
path_share = std::string(buf) + "\\..\\..";
@@ -191,25 +200,57 @@ void initializePaths()
Linux
*/
#elif defined(linux)
- #include <unistd.h>
-
+
char buf[BUFSIZ];
memset(buf, 0, BUFSIZ);
// Get path to executable
assert(readlink("/proc/self/exe", buf, BUFSIZ-1) != -1);
-
+
pathRemoveFile(buf, '/');
path_share = std::string(buf) + "/..";
path_user = std::string(buf) + "/..";
-
+
/*
OS X
*/
- #elif defined(__APPLE__) || defined(__FreeBSD__)
-
+ #elif defined(__APPLE__)
+
+ //https://developer.apple.com/library/mac/#documentation/Darwin/Reference/ManPages/man3/dyld.3.html
+ //TODO: Test this code
+ char buf[BUFSIZ];
+ uint32_t len = sizeof(buf);
+ assert(_NSGetExecutablePath(buf, &len) != -1);
+
+ pathRemoveFile(buf, '/');
+
+ path_share = std::string(buf) + "/..";
+ path_user = std::string(buf) + "/..";
+
+ /*
+ FreeBSD
+ */
+ #elif defined(__FreeBSD__)
+
+ int mib[4];
+ char buf[BUFSIZ];
+ size_t len = sizeof(buf);
+
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_PROC;
+ mib[2] = KERN_PROC_PATHNAME;
+ mib[3] = -1;
+ assert(sysctl(mib, 4, buf, &len, NULL, 0) != -1);
+
+ pathRemoveFile(buf, '/');
+
+ path_share = std::string(buf) + "/..";
+ path_user = std::string(buf) + "/..";
+
+ #else
+
//TODO: Get path of executable. This assumes working directory is bin/
- dstream<<"WARNING: Relative path not properly supported on OS X and FreeBSD"
+ dstream<<"WARNING: Relative path not properly supported on this platform"
<<std::endl;
path_share = std::string("..");
path_user = std::string("..");
@@ -228,20 +269,19 @@ void initializePaths()
Windows
*/
#if defined(_WIN32)
- #include <windows.h>
const DWORD buflen = 1000;
char buf[buflen];
DWORD len;
-
+
// Find path of executable and set path_share relative to it
len = GetModuleFileName(GetModuleHandle(NULL), buf, buflen);
assert(len < buflen);
pathRemoveFile(buf, '\\');
-
+
// Use ".\bin\.."
path_share = std::string(buf) + "\\..";
-
+
// Use "C:\Documents and Settings\user\Application Data\<PROJECT_NAME>"
len = GetEnvironmentVariable("APPDATA", buf, buflen);
assert(len < buflen);
@@ -251,8 +291,7 @@ void initializePaths()
Linux
*/
#elif defined(linux)
- #include <unistd.h>
-
+
// Get path to executable
std::string bindir = "";
{
@@ -271,7 +310,7 @@ void initializePaths()
trylist.push_back(static_sharedir);
trylist.push_back(bindir + "/../share/" + PROJECT_NAME);
trylist.push_back(bindir + "/..");
-
+
for(std::list<std::string>::const_iterator i = trylist.begin();
i != trylist.end(); i++)
{
@@ -289,14 +328,13 @@ void initializePaths()
path_share = trypath;
break;
}
-
+
path_user = std::string(getenv("HOME")) + "/." + PROJECT_NAME;
/*
OS X
*/
#elif defined(__APPLE__)
- #include <unistd.h>
// Code based on
// http://stackoverflow.com/questions/516200/relative-paths-not-working-in-xcode-c
@@ -315,14 +353,14 @@ void initializePaths()
dstream<<"WARNING: Could not determine bundle resource path"<<std::endl;
}
CFRelease(resources_url);
-
+
path_user = std::string(getenv("HOME")) + "/Library/Application Support/" + PROJECT_NAME;
#elif defined(__FreeBSD__)
path_share = STATIC_SHAREDIR;
path_user = std::string(getenv("HOME")) + "/." + PROJECT_NAME;
-
+
#endif
#endif // RUN_IN_PLACE
diff --git a/src/porting.h b/src/porting.h
index 2f9d43aca..9ba3394bb 100644
--- a/src/porting.h
+++ b/src/porting.h
@@ -35,14 +35,38 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#define SWPRINTF_CHARSTRING L"%s"
#endif
+//currently not needed
+//template<typename T> struct alignment_trick { char c; T member; };
+//#define ALIGNOF(type) offsetof (alignment_trick<type>, member)
+
#ifdef _WIN32
#include <windows.h>
+
#define sleep_ms(x) Sleep(x)
#else
#include <unistd.h>
+ #include <stdint.h> //for uintptr_t
+
#define sleep_ms(x) usleep(x*1000)
#endif
+#ifdef _MSC_VER
+ #define ALIGNOF(x) __alignof(x)
+ #define strtok_r(x, y, z) strtok_s(x, y, z)
+ #define strtof(x, y) (float)strtod(x, y)
+ #define strtoll(x, y, z) _strtoi64(x, y, z)
+ #define strtoull(x, y, z) _strtoui64(x, y, z)
+ #define strcasecmp(x, y) stricmp(x, y)
+#else
+ #define ALIGNOF(x) __alignof__(x)
+#endif
+
+#ifdef __MINGW32__
+ #define strtok_r(x, y, z) mystrtok_r(x, y, z)
+#endif
+
+#define PADDING(x, y) ((ALIGNOF(y) - ((uintptr_t)(x) & (ALIGNOF(y) - 1))) & (ALIGNOF(y) - 1))
+
namespace porting
{
diff --git a/src/scriptapi.cpp b/src/scriptapi.cpp
index 8e4a43266..66654813e 100644
--- a/src/scriptapi.cpp
+++ b/src/scriptapi.cpp
@@ -37,6 +37,7 @@ extern "C" {
#include "content_sao.h" // For LuaEntitySAO and PlayerSAO
#include "itemdef.h"
#include "nodedef.h"
+#include "biome.h"
#include "craftdef.h"
#include "main.h" // For g_settings
#include "settings.h" // For accessing g_settings
@@ -436,6 +437,16 @@ struct EnumString es_TileAnimationType[] =
{0, NULL},
};
+struct EnumString es_BiomeTerrainType[] =
+{
+ {BIOME_TERRAIN_NORMAL, "normal"},
+ {BIOME_TERRAIN_LIQUID, "liquid"},
+ {BIOME_TERRAIN_NETHER, "nether"},
+ {BIOME_TERRAIN_AETHER, "aether"},
+ {BIOME_TERRAIN_FLAT, "flat"},
+ {0, NULL},
+};
+
/*
C struct <-> Lua table converter functions
*/
@@ -683,6 +694,30 @@ static NodeBox read_nodebox(lua_State *L, int index)
}
/*
+ NoiseParams
+*/
+static NoiseParams *read_noiseparams(lua_State *L, int index)
+{
+ if (index < 0)
+ index = lua_gettop(L) + 1 + index;
+
+ if (!lua_istable(L, index))
+ return NULL;
+
+ NoiseParams *np = new NoiseParams;
+
+ np->offset = getfloatfield_default(L, index, "offset", 0.0);
+ np->scale = getfloatfield_default(L, index, "scale", 0.0);
+ lua_getfield(L, index, "spread");
+ np->spread = read_v3f(L, -1);
+ np->seed = getintfield_default(L, index, "seed", 0);
+ np->octaves = getintfield_default(L, index, "octaves", 0);
+ np->persist = getfloatfield_default(L, index, "persist", 0.0);
+
+ return np;
+}
+
+/*
Groups
*/
static void read_groups(lua_State *L, int index,
@@ -988,7 +1023,7 @@ static void read_object_properties(lua_State *L, int index,
if(lua_istable(L, -1))
prop->initial_sprite_basepos = read_v2s16(L, -1);
lua_pop(L, 1);
-
+
getboolfield(L, -1, "is_visible", prop->is_visible);
getboolfield(L, -1, "makes_footstep_sound", prop->makes_footstep_sound);
getfloatfield(L, -1, "automatic_rotate", prop->automatic_rotate);
@@ -1028,11 +1063,15 @@ static ItemDefinition read_item_definition(lua_State *L, int index,
def.usable = lua_isfunction(L, -1);
lua_pop(L, 1);
+ lua_getfield(L, index, "on_rightclick");
+ def.rightclickable = lua_isfunction(L, -1);
+ lua_pop(L, 1);
+
getboolfield(L, index, "liquids_pointable", def.liquids_pointable);
warn_if_field_exists(L, index, "tool_digging_properties",
"deprecated: use tool_capabilities");
-
+
lua_getfield(L, index, "tool_capabilities");
if(lua_istable(L, -1)){
def.tool_capabilities = new ToolCapabilities(
@@ -1067,7 +1106,7 @@ static TileDef read_tiledef(lua_State *L, int index)
{
if(index < 0)
index = lua_gettop(L) + 1 + index;
-
+
TileDef tiledef;
// key at index -2 and value at index
@@ -1113,7 +1152,7 @@ static ContentFeatures read_content_features(lua_State *L, int index)
index = lua_gettop(L) + 1 + index;
ContentFeatures f;
-
+
/* Cache existence of some callbacks */
lua_getfield(L, index, "on_construct");
if(!lua_isnil(L, -1)) f.has_on_construct = true;
@@ -1138,7 +1177,7 @@ static ContentFeatures read_content_features(lua_State *L, int index)
f.drawtype = (NodeDrawType)getenumfield(L, index, "drawtype", es_DrawType,
NDT_NORMAL);
getfloatfield(L, index, "visual_scale", f.visual_scale);
-
+
// tiles = {}
lua_getfield(L, index, "tiles");
// If nil, try the deprecated name "tile_images" instead
@@ -1173,7 +1212,7 @@ static ContentFeatures read_content_features(lua_State *L, int index)
}
}
lua_pop(L, 1);
-
+
// special_tiles = {}
lua_getfield(L, index, "special_tiles");
// If nil, try the deprecated name "special_materials" instead
@@ -1204,7 +1243,7 @@ static ContentFeatures read_content_features(lua_State *L, int index)
f.alpha = getintfield_default(L, index, "alpha", 255);
/* Other stuff */
-
+
lua_getfield(L, index, "post_effect_color");
if(!lua_isnil(L, -1))
f.post_effect_color = readARGB8(L, -1);
@@ -1228,7 +1267,7 @@ static ContentFeatures read_content_features(lua_State *L, int index)
"deprecated: use 'drop' field");
warn_if_field_exists(L, index, "metadata_name",
"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);
f.light_propagates = (f.param_type == CPT_LIGHT);
@@ -1264,7 +1303,7 @@ static ContentFeatures read_content_features(lua_State *L, int index)
"light_source", f.light_source);
f.damage_per_second = getintfield_default(L, index,
"damage_per_second", f.damage_per_second);
-
+
lua_getfield(L, index, "node_box");
if(lua_istable(L, -1))
f.node_box = read_nodebox(L, -1);
@@ -1279,7 +1318,7 @@ static ContentFeatures read_content_features(lua_State *L, int index)
getboolfield(L, index, "legacy_facedir_simple", f.legacy_facedir_simple);
// Set to true if wall_mounted used to be set to true
getboolfield(L, index, "legacy_wallmounted", f.legacy_wallmounted);
-
+
// Sound table
lua_getfield(L, index, "sounds");
if(lua_istable(L, -1)){
@@ -1368,7 +1407,7 @@ private:
static const luaL_reg methods[];
// Exported functions
-
+
// garbage collector
static int gc_object(lua_State *L)
{
@@ -1621,7 +1660,7 @@ public:
{
return m_stack;
}
-
+
// LuaItemStack(itemstack or itemstring or table or nil)
// Creates an LuaItemStack and leaves it on top of stack
static int create_object(lua_State *L)
@@ -1810,7 +1849,7 @@ private:
if(!ud) luaL_typerror(L, narg, className);
return *(InvRef**)ud; // unbox pointer
}
-
+
static Inventory* getinv(lua_State *L, InvRef *ref)
{
return get_server(L)->getInventory(ref->m_loc);
@@ -1830,9 +1869,9 @@ private:
// Inform other things that the inventory has changed
get_server(L)->setInventoryModified(ref->m_loc);
}
-
+
// Exported functions
-
+
// garbage collector
static int gc_object(lua_State *L) {
InvRef *o = *(InvRef **)(lua_touserdata(L, 1));
@@ -2185,7 +2224,7 @@ private:
if(!ud) luaL_typerror(L, narg, className);
return *(NodeMetaRef**)ud; // unbox pointer
}
-
+
static NodeMetadata* getmeta(NodeMetaRef *ref, bool auto_create)
{
NodeMetadata *meta = ref->m_env->getMap().getNodeMetadata(ref->m_p);
@@ -2212,9 +2251,9 @@ private:
block->raiseModified(MOD_STATE_WRITE_NEEDED,
"NodeMetaRef::reportMetadataChange");
}
-
+
// Exported functions
-
+
// garbage collector
static int gc_object(lua_State *L) {
NodeMetaRef *o = *(NodeMetaRef **)(lua_touserdata(L, 1));
@@ -2327,7 +2366,7 @@ private:
InvRef::createNodeMeta(L, ref->m_p);
return 1;
}
-
+
// to_table(self)
static int l_to_table(lua_State *L)
{
@@ -2373,7 +2412,7 @@ private:
{
NodeMetaRef *ref = checkobject(L, 1);
int base = 2;
-
+
if(lua_isnil(L, base)){
// No metadata
ref->m_env->getMap().removeNodeMetadata(ref->m_p);
@@ -2497,7 +2536,7 @@ public:
if(!ud) luaL_typerror(L, narg, className);
return *(ObjectRef**)ud; // unbox pointer
}
-
+
static ServerActiveObject* getobject(ObjectRef *ref)
{
ServerActiveObject *co = ref->m_object;
@@ -2513,7 +2552,7 @@ private:
return NULL;
return (LuaEntitySAO*)obj;
}
-
+
static PlayerSAO* getplayersao(ObjectRef *ref)
{
ServerActiveObject *obj = getobject(ref);
@@ -2523,7 +2562,7 @@ private:
return NULL;
return (PlayerSAO*)obj;
}
-
+
static Player* getplayer(ObjectRef *ref)
{
PlayerSAO *playersao = getplayersao(ref);
@@ -2531,9 +2570,9 @@ private:
return NULL;
return playersao->getPlayer();
}
-
+
// Exported functions
-
+
// garbage collector
static int gc_object(lua_State *L) {
ObjectRef *o = *(ObjectRef **)(lua_touserdata(L, 1));
@@ -2552,7 +2591,7 @@ private:
co->m_removed = true;
return 0;
}
-
+
// getpos(self)
// returns: {x=num, y=num, z=num}
static int l_getpos(lua_State *L)
@@ -2570,7 +2609,7 @@ private:
lua_setfield(L, -2, "z");
return 1;
}
-
+
// setpos(self, pos)
static int l_setpos(lua_State *L)
{
@@ -2584,7 +2623,7 @@ private:
co->setPos(pos);
return 0;
}
-
+
// moveto(self, pos, continuous=false)
static int l_moveto(lua_State *L)
{
@@ -2856,7 +2895,7 @@ private:
co->setVelocity(pos);
return 0;
}
-
+
// getvelocity(self)
static int l_getvelocity(lua_State *L)
{
@@ -2868,7 +2907,7 @@ private:
pushFloatPos(L, v);
return 1;
}
-
+
// setacceleration(self, {x=num, y=num, z=num})
static int l_setacceleration(lua_State *L)
{
@@ -2881,7 +2920,7 @@ private:
co->setAcceleration(pos);
return 0;
}
-
+
// getacceleration(self)
static int l_getacceleration(lua_State *L)
{
@@ -2893,7 +2932,7 @@ private:
pushFloatPos(L, v);
return 1;
}
-
+
// setyaw(self, radians)
static int l_setyaw(lua_State *L)
{
@@ -2905,7 +2944,7 @@ private:
co->setYaw(yaw);
return 0;
}
-
+
// getyaw(self)
static int l_getyaw(lua_State *L)
{
@@ -2917,7 +2956,7 @@ private:
lua_pushnumber(L, yaw);
return 1;
}
-
+
// settexturemod(self, mod)
static int l_settexturemod(lua_State *L)
{
@@ -2929,7 +2968,7 @@ private:
co->setTextureMod(mod);
return 0;
}
-
+
// setsprite(self, p={x=0,y=0}, num_frames=1, framelength=0.2,
// select_horiz_by_yawpitch=false)
static int l_setsprite(lua_State *L)
@@ -2966,7 +3005,7 @@ private:
lua_pushstring(L, name.c_str());
return 1;
}
-
+
// get_luaentity(self)
static int l_get_luaentity(lua_State *L)
{
@@ -2977,7 +3016,7 @@ private:
luaentity_get(L, co->getId());
return 1;
}
-
+
/* Player-only */
// is_player(self)
@@ -2988,7 +3027,7 @@ private:
lua_pushboolean(L, (player != NULL));
return 1;
}
-
+
// get_player_name(self)
static int l_get_player_name(lua_State *L)
{
@@ -3002,7 +3041,7 @@ private:
lua_pushstring(L, player->getName());
return 1;
}
-
+
// get_look_dir(self)
static int l_get_look_dir(lua_State *L)
{
@@ -3144,7 +3183,7 @@ public:
ObjectRef *o = checkobject(L, -1);
o->m_object = NULL;
}
-
+
static void Register(lua_State *L)
{
lua_newtable(L);
@@ -3230,18 +3269,13 @@ static void objectref_get_or_create(lua_State *L,
}
}
-
-/*
- PerlinNoise
- */
-
class LuaPerlinNoise
{
private:
int seed;
int octaves;
- double persistence;
- double scale;
+ float persistence;
+ float scale;
static const char className[];
static const luaL_reg methods[];
@@ -3273,8 +3307,8 @@ private:
}
public:
- LuaPerlinNoise(int a_seed, int a_octaves, double a_persistence,
- double a_scale):
+ LuaPerlinNoise(int a_seed, int a_octaves, float a_persistence,
+ float a_scale):
seed(a_seed),
octaves(a_octaves),
persistence(a_persistence),
@@ -3292,8 +3326,8 @@ public:
{
int seed = luaL_checkint(L, 1);
int octaves = luaL_checkint(L, 2);
- double persistence = luaL_checknumber(L, 3);
- double scale = luaL_checknumber(L, 4);
+ float persistence = luaL_checknumber(L, 3);
+ float scale = luaL_checknumber(L, 4);
LuaPerlinNoise *o = new LuaPerlinNoise(seed, octaves, persistence, scale);
*(void **)(lua_newuserdata(L, sizeof(void *))) = o;
luaL_getmetatable(L, className);
@@ -3345,6 +3379,145 @@ const luaL_reg LuaPerlinNoise::methods[] = {
};
/*
+ PerlinNoiseMap
+ */
+class LuaPerlinNoiseMap
+{
+private:
+ Noise *noise;
+ static const char className[];
+ static const luaL_reg methods[];
+
+ static int gc_object(lua_State *L)
+ {
+ LuaPerlinNoiseMap *o = *(LuaPerlinNoiseMap **)(lua_touserdata(L, 1));
+ delete o;
+ return 0;
+ }
+
+ static int l_get2dMap(lua_State *L)
+ {
+ int i = 0;
+
+ LuaPerlinNoiseMap *o = checkobject(L, 1);
+ v2f p = read_v2f(L, 2);
+
+ Noise *n = o->noise;
+ n->perlinMap2D(p.X, p.Y);
+
+ lua_newtable(L);
+ for (int y = 0; y != n->sy; y++) {
+ lua_newtable(L);
+ for (int x = 0; x != n->sx; x++) {
+ float noiseval = n->np->offset + n->np->scale * n->result[i++];
+ lua_pushnumber(L, noiseval);
+ lua_rawseti(L, -2, x + 1);
+ }
+ lua_rawseti(L, -2, y + 1);
+ }
+ return 1;
+ }
+
+ static int l_get3dMap(lua_State *L)
+ {
+ int i = 0;
+
+ LuaPerlinNoiseMap *o = checkobject(L, 1);
+ v3f p = read_v3f(L, 2);
+
+ Noise *n = o->noise;
+ n->perlinMap3D(p.X, p.Y, p.Z);
+
+ lua_newtable(L);
+ for (int z = 0; z != n->sz; z++) {
+ lua_newtable(L);
+ for (int y = 0; y != n->sy; y++) {
+ lua_newtable(L);
+ for (int x = 0; x != n->sx; x++) {
+ lua_pushnumber(L, n->np->offset + n->np->scale * n->result[i++]);
+ lua_rawseti(L, -2, x + 1);
+ }
+ lua_rawseti(L, -2, y + 1);
+ }
+ lua_rawseti(L, -2, z + 1);
+ }
+ return 1;
+ }
+
+public:
+ LuaPerlinNoiseMap(NoiseParams *np, int seed, v3s16 size) {
+ noise = new Noise(np, seed, size.X, size.Y, size.Z);
+ }
+
+ ~LuaPerlinNoiseMap()
+ {
+ delete noise->np;
+ delete noise;
+ }
+
+ // LuaPerlinNoiseMap(np, size)
+ // Creates an LuaPerlinNoiseMap and leaves it on top of stack
+ static int create_object(lua_State *L)
+ {
+ NoiseParams *np = read_noiseparams(L, 1);
+ if (!np)
+ return 0;
+ v3s16 size = read_v3s16(L, 2);
+
+ LuaPerlinNoiseMap *o = new LuaPerlinNoiseMap(np, 0, size);
+ *(void **)(lua_newuserdata(L, sizeof(void *))) = o;
+ luaL_getmetatable(L, className);
+ lua_setmetatable(L, -2);
+ return 1;
+ }
+
+ static LuaPerlinNoiseMap *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 *(LuaPerlinNoiseMap **)ud; // unbox pointer
+ }
+
+ static void 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); // hide metatable from Lua getmetatable()
+
+ 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); // drop metatable
+
+ luaL_openlib(L, 0, methods, 0); // fill methodtable
+ lua_pop(L, 1); // drop methodtable
+
+ // Can be created from Lua (PerlinNoiseMap(np, size)
+ lua_register(L, className, create_object);
+ }
+};
+const char LuaPerlinNoiseMap::className[] = "PerlinNoiseMap";
+const luaL_reg LuaPerlinNoiseMap::methods[] = {
+ method(LuaPerlinNoiseMap, get2dMap),
+ method(LuaPerlinNoiseMap, get3dMap),
+ {0,0}
+};
+
+/*
NodeTimerRef
*/
@@ -3370,7 +3543,7 @@ private:
if(!ud) luaL_typerror(L, narg, className);
return *(NodeTimerRef**)ud; // unbox pointer
}
-
+
static int l_set(lua_State *L)
{
NodeTimerRef *o = checkobject(L, 1);
@@ -3381,7 +3554,7 @@ private:
env->getMap().setNodeTimer(o->m_p,NodeTimer(t,e));
return 0;
}
-
+
static int l_start(lua_State *L)
{
NodeTimerRef *o = checkobject(L, 1);
@@ -3391,7 +3564,7 @@ private:
env->getMap().setNodeTimer(o->m_p,NodeTimer(t,0));
return 0;
}
-
+
static int l_stop(lua_State *L)
{
NodeTimerRef *o = checkobject(L, 1);
@@ -3400,7 +3573,7 @@ private:
env->getMap().removeNodeTimer(o->m_p);
return 0;
}
-
+
static int l_is_started(lua_State *L)
{
NodeTimerRef *o = checkobject(L, 1);
@@ -3411,7 +3584,7 @@ private:
lua_pushboolean(L,(t.timeout != 0));
return 1;
}
-
+
static int l_get_timeout(lua_State *L)
{
NodeTimerRef *o = checkobject(L, 1);
@@ -3422,7 +3595,7 @@ private:
lua_pushnumber(L,t.timeout);
return 1;
}
-
+
static int l_get_elapsed(lua_State *L)
{
NodeTimerRef *o = checkobject(L, 1);
@@ -3460,7 +3633,7 @@ public:
NodeTimerRef *o = checkobject(L, -1);
o->m_env = NULL;
}
-
+
static void Register(lua_State *L)
{
lua_newtable(L);
@@ -3525,7 +3698,7 @@ private:
if(!ud) luaL_typerror(L, narg, className);
return *(EnvRef**)ud; // unbox pointer
}
-
+
// Exported functions
// EnvRef:set_node(pos, node)
@@ -3969,7 +4142,7 @@ private:
lua_getglobal(L, "table");
lua_getfield(L, -1, "insert");
int table_insert = lua_gettop(L);
-
+
lua_newtable(L);
int table = lua_gettop(L);
for(s16 x=minp.X; x<=maxp.X; x++)
@@ -3999,8 +4172,8 @@ private:
int seeddiff = luaL_checkint(L, 2);
int octaves = luaL_checkint(L, 3);
- double persistence = luaL_checknumber(L, 4);
- double scale = luaL_checknumber(L, 5);
+ float persistence = luaL_checknumber(L, 4);
+ float scale = luaL_checknumber(L, 5);
LuaPerlinNoise *n = new LuaPerlinNoise(seeddiff + int(env->getServerMap().getSeed()), octaves, persistence, scale);
*(void **)(lua_newuserdata(L, sizeof(void *))) = n;
@@ -4008,6 +4181,28 @@ private:
lua_setmetatable(L, -2);
return 1;
}
+
+ // EnvRef:get_perlin_map(noiseparams, size)
+ // returns world-specific PerlinNoiseMap
+ static int l_get_perlin_map(lua_State *L)
+ {
+ EnvRef *o = checkobject(L, 1);
+ ServerEnvironment *env = o->m_env;
+ if (env == NULL)
+ return 0;
+
+ NoiseParams *np = read_noiseparams(L, 2);
+ if (!np)
+ return 0;
+ v3s16 size = read_v3s16(L, 3);
+
+ int seed = (int)(env->getServerMap().getSeed());
+ LuaPerlinNoiseMap *n = new LuaPerlinNoiseMap(np, seed, size);
+ *(void **)(lua_newuserdata(L, sizeof(void *))) = n;
+ luaL_getmetatable(L, "PerlinNoiseMap");
+ lua_setmetatable(L, -2);
+ return 1;
+ }
// EnvRef:clear_objects()
// clear all objects in the environment
@@ -4094,7 +4289,7 @@ public:
EnvRef *o = checkobject(L, -1);
o->m_env = NULL;
}
-
+
static void Register(lua_State *L)
{
lua_newtable(L);
@@ -4147,6 +4342,7 @@ const luaL_reg EnvRef::methods[] = {
method(EnvRef, find_node_near),
method(EnvRef, find_nodes_in_area),
method(EnvRef, get_perlin),
+ method(EnvRef, get_perlin_map),
method(EnvRef, clear_objects),
method(EnvRef, spawn_tree),
{0,0}
@@ -4166,7 +4362,7 @@ private:
static const luaL_reg methods[];
// Exported functions
-
+
// garbage collector
static int gc_object(lua_State *L)
{
@@ -4217,7 +4413,7 @@ public:
{
return m_pseudo;
}
-
+
// LuaPseudoRandom(seed)
// Creates an LuaPseudoRandom and leaves it on top of stack
static int create_object(lua_State *L)
@@ -4321,7 +4517,7 @@ public:
u32 active_object_count, u32 active_object_count_wider)
{
lua_State *L = m_lua;
-
+
realitycheck(L);
assert(lua_checkstack(L, 20));
StackUnroller stack_unroller(L);
@@ -4337,7 +4533,7 @@ public:
lua_gettable(L, registered_abms);
if(lua_isnil(L, -1))
assert(0);
-
+
// Call action
luaL_checktype(L, -1, LUA_TTABLE);
lua_getfield(L, -1, "action");
@@ -4415,8 +4611,8 @@ static int l_log(lua_State *L)
}
else
{
- std::string levelname = lua_tostring(L, 1);
- text = lua_tostring(L, 2);
+ std::string levelname = luaL_checkstring(L, 1);
+ text = luaL_checkstring(L, 2);
if(levelname == "error")
level = LMT_ERROR;
else if(levelname == "action")
@@ -4442,6 +4638,84 @@ static int l_get_server_status(lua_State *L)
return 1;
}
+// register_biome_groups({frequencies})
+static int l_register_biome_groups(lua_State *L)
+{
+ luaL_checktype(L, 1, LUA_TTABLE);
+ int index = 1;
+ if (!lua_istable(L, index))
+ throw LuaError(L, "register_biome_groups: parameter is not a table");
+
+ BiomeDefManager *bmgr = get_server(L)->getBiomeDef();
+ if (!bmgr) {
+ verbosestream << "register_biome_groups: BiomeDefManager not active" << std::endl;
+ return 0;
+ }
+
+ lua_pushnil(L);
+ for (int i = 1; lua_next(L, index) != 0; i++) {
+ bmgr->addBiomeGroup(lua_tonumber(L, -1));
+ lua_pop(L, 1);
+ }
+ lua_pop(L, 1);
+
+ return 0;
+}
+
+// register_biome({lots of stuff})
+static int l_register_biome(lua_State *L)
+{
+ luaL_checktype(L, 1, LUA_TTABLE);
+ int index = 1, groupid;
+ std::string nodename;
+
+ IWritableNodeDefManager *ndef = get_server(L)->getWritableNodeDefManager();
+ BiomeDefManager *bmgr = get_server(L)->getBiomeDef();
+ if (!bmgr) {
+ verbosestream << "register_biome: BiomeDefManager not active" << std::endl;
+ return 0;
+ }
+
+ groupid = getintfield_default(L, index, "group_id", 0);
+
+ enum BiomeTerrainType terrain = (BiomeTerrainType)getenumfield(L, index,
+ "terrain_type", es_BiomeTerrainType, BIOME_TERRAIN_NORMAL);
+ Biome *b = bmgr->createBiome(terrain);
+
+ b->name = getstringfield_default(L, index, "name", "");
+
+ if (getstringfield(L, index, "node_top", nodename))
+ b->n_top = MapNode(ndef->getId(nodename));
+ else
+ b->n_top = MapNode(CONTENT_IGNORE);
+
+ if (getstringfield(L, index, "node_filler", nodename))
+ b->n_filler = MapNode(ndef->getId(nodename));
+ else
+ b->n_filler = b->n_top;
+
+ b->ntopnodes = getintfield_default(L, index, "num_top_nodes", 0);
+
+ b->height_min = getintfield_default(L, index, "height_min", 0);
+ b->height_max = getintfield_default(L, index, "height_max", 0);
+ b->heat_min = getfloatfield_default(L, index, "heat_min", 0.);
+ b->heat_max = getfloatfield_default(L, index, "heat_max", 0.);
+ b->humidity_min = getfloatfield_default(L, index, "humidity_min", 0.);
+ b->humidity_max = getfloatfield_default(L, index, "humidity_max", 0.);
+
+ b->np = new NoiseParams; // should read an entire NoiseParams later on...
+ getfloatfield(L, index, "scale", b->np->scale);
+ getfloatfield(L, index, "offset", b->np->offset);
+
+ b->groupid = (s8)groupid;
+ b->flags = 0; //reserved
+
+ bmgr->addBiome(b);
+
+ verbosestream << "register_biome: " << b->name << std::endl;
+ return 0;
+}
+
// register_item_raw({lots of stuff})
static int l_register_item_raw(lua_State *L)
{
@@ -4476,12 +4750,12 @@ static int l_register_item_raw(lua_State *L)
// Default to having client-side placement prediction for nodes
// ("" in item definition sets it off)
if(def.node_placement_prediction == "__default"){
- if(def.type == ITEM_NODE)
+ if(def.type == ITEM_NODE && !def.rightclickable)
def.node_placement_prediction = name;
else
def.node_placement_prediction = "";
}
-
+
// Register item definition
idef->registerItem(def);
@@ -4504,9 +4778,9 @@ static int l_register_alias_raw(lua_State *L)
// Get the writable item definition manager from the server
IWritableItemDefManager *idef =
get_server(L)->getWritableItemDefManager();
-
+
idef->registerAlias(name, convert_to);
-
+
return 0; /* number of results */
}
@@ -4615,7 +4889,7 @@ static int l_register_craft(lua_State *L)
// Get the writable craft definition manager from the server
IWritableCraftDefManager *craftdef =
get_server(L)->getWritableCraftDefManager();
-
+
std::string type = getstringfield_default(L, table, "type", "shaped");
/*
@@ -4790,6 +5064,13 @@ static int l_setting_getbool(lua_State *L)
return 1;
}
+// setting_save()
+static int l_setting_save(lua_State *L)
+{
+ get_server(L)->saveConfig();
+ return 0;
+}
+
// chat_send_all(text)
static int l_chat_send_all(lua_State *L)
{
@@ -4899,7 +5180,7 @@ static int l_get_inventory(lua_State *L)
std::string name = checkstringfield(L, 1, "name");
loc.setDetached(name);
}
-
+
if(get_server(L)->getInventory(loc) != NULL)
InvRef::create(L, loc);
else
@@ -5116,7 +5397,7 @@ static int l_get_craft_result(lua_State *L)
lua_getfield(L, input_i, "items");
std::vector<ItemStack> items = read_items(L, -1);
lua_pop(L, 1); // items
-
+
IGameDef *gdef = get_server(L);
ICraftDefManager *cdef = gdef->cdef();
CraftInput input(method, width, items);
@@ -5151,7 +5432,7 @@ static int l_get_craft_recipe(lua_State *L)
char tmp[20];
int input_i = 1;
std::string o_item = luaL_checkstring(L,input_i);
-
+
IGameDef *gdef = get_server(L);
ICraftDefManager *cdef = gdef->cdef();
CraftInput input;
@@ -5254,9 +5535,12 @@ static const struct luaL_Reg minetest_f [] = {
{"register_item_raw", l_register_item_raw},
{"register_alias_raw", l_register_alias_raw},
{"register_craft", l_register_craft},
+ {"register_biome", l_register_biome},
+ {"register_biome_groups", l_register_biome_groups},
{"setting_set", l_setting_set},
{"setting_get", l_setting_get},
{"setting_getbool", l_setting_getbool},
+ {"setting_save",l_setting_save},
{"chat_send_all", l_chat_send_all},
{"chat_send_player", l_chat_send_player},
{"get_player_privs", l_get_player_privs},
@@ -5304,7 +5588,7 @@ void scriptapi_export(lua_State *L, Server *server)
lua_newtable(L);
luaL_register(L, NULL, minetest_f);
lua_setglobal(L, "minetest");
-
+
// Get the main minetest table
lua_getglobal(L, "minetest");
@@ -5323,6 +5607,7 @@ void scriptapi_export(lua_State *L, Server *server)
EnvRef::Register(L);
LuaPseudoRandom::Register(L);
LuaPerlinNoise::Register(L);
+ LuaPerlinNoiseMap::Register(L);
}
bool scriptapi_loadmod(lua_State *L, const std::string &scriptpath,
@@ -5337,7 +5622,7 @@ bool scriptapi_loadmod(lua_State *L, const std::string &scriptpath,
<<"Only chararacters [a-z0-9_] are allowed."<<std::endl;
return false;
}
-
+
bool success = false;
try{
@@ -5381,7 +5666,7 @@ void scriptapi_add_environment(lua_State *L, ServerEnvironment *env)
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);
@@ -5432,7 +5717,7 @@ void scriptapi_add_environment(lua_State *L, ServerEnvironment *env)
LuaABM *abm = new LuaABM(L, id, trigger_contents,
required_neighbors, trigger_interval, trigger_chance);
-
+
env->addActiveBlockModifier(abm);
// removes value, keeps key for next iteration
@@ -5476,7 +5761,7 @@ void scriptapi_add_object_reference(lua_State *L, ServerActiveObject *cobj)
lua_getfield(L, -1, "object_refs");
luaL_checktype(L, -1, LUA_TTABLE);
int objectstable = lua_gettop(L);
-
+
// object_refs[id] = object
lua_pushnumber(L, cobj->getId()); // Push id
lua_pushvalue(L, object); // Copy object to top of stack
@@ -5495,7 +5780,7 @@ void scriptapi_rm_object_reference(lua_State *L, ServerActiveObject *cobj)
lua_getfield(L, -1, "object_refs");
luaL_checktype(L, -1, LUA_TTABLE);
int objectstable = lua_gettop(L);
-
+
// Get object_refs[id]
lua_pushnumber(L, cobj->getId()); // Push id
lua_gettable(L, objectstable);
@@ -5591,16 +5876,16 @@ static void scriptapi_run_callbacks(lua_State *L, int nargs,
lua_replace(L, rv);
else if(mode == RUN_CALLBACKS_MODE_AND ||
mode == RUN_CALLBACKS_MODE_AND_SC){
- if(lua_toboolean(L, rv) == true &&
- lua_toboolean(L, -1) == false)
+ if((bool)lua_toboolean(L, rv) == true &&
+ (bool)lua_toboolean(L, -1) == false)
lua_replace(L, rv);
else
lua_pop(L, 1);
}
else if(mode == RUN_CALLBACKS_MODE_OR ||
mode == RUN_CALLBACKS_MODE_OR_SC){
- if(lua_toboolean(L, rv) == false &&
- lua_toboolean(L, -1) == true)
+ if((bool)lua_toboolean(L, rv) == false &&
+ (bool)lua_toboolean(L, -1) == true)
lua_replace(L, rv);
else
lua_pop(L, 1);
@@ -5611,10 +5896,10 @@ static void scriptapi_run_callbacks(lua_State *L, int nargs,
// Handle short circuit modes
if(mode == RUN_CALLBACKS_MODE_AND_SC &&
- lua_toboolean(L, rv) == false)
+ (bool)lua_toboolean(L, rv) == false)
break;
else if(mode == RUN_CALLBACKS_MODE_OR_SC &&
- lua_toboolean(L, rv) == true)
+ (bool)lua_toboolean(L, rv) == true)
break;
// value removed, keep key for next iteration
@@ -5759,7 +6044,7 @@ bool scriptapi_get_auth(lua_State *L, const std::string &playername,
realitycheck(L);
assert(lua_checkstack(L, 20));
StackUnroller stack_unroller(L);
-
+
get_auth_handler(L);
lua_getfield(L, -1, "get_auth");
if(lua_type(L, -1) != LUA_TFUNCTION)
@@ -5767,12 +6052,12 @@ bool scriptapi_get_auth(lua_State *L, const std::string &playername,
lua_pushstring(L, playername.c_str());
if(lua_pcall(L, 1, 1, 0))
script_error(L, "error: %s", lua_tostring(L, -1));
-
+
// nil = login not allowed
if(lua_isnil(L, -1))
return false;
luaL_checktype(L, -1, LUA_TTABLE);
-
+
std::string password;
bool found = getstringfield(L, -1, "password", password);
if(!found)
@@ -5787,7 +6072,7 @@ bool scriptapi_get_auth(lua_State *L, const std::string &playername,
if(dst_privs)
read_privileges(L, -1, *dst_privs);
lua_pop(L, 1);
-
+
return true;
}
@@ -5797,7 +6082,7 @@ void scriptapi_create_auth(lua_State *L, const std::string &playername,
realitycheck(L);
assert(lua_checkstack(L, 20));
StackUnroller stack_unroller(L);
-
+
get_auth_handler(L);
lua_getfield(L, -1, "create_auth");
if(lua_type(L, -1) != LUA_TFUNCTION)
@@ -5814,7 +6099,7 @@ bool scriptapi_set_password(lua_State *L, const std::string &playername,
realitycheck(L);
assert(lua_checkstack(L, 20));
StackUnroller stack_unroller(L);
-
+
get_auth_handler(L);
lua_getfield(L, -1, "set_password");
if(lua_type(L, -1) != LUA_TFUNCTION)
@@ -5830,7 +6115,7 @@ bool scriptapi_set_password(lua_State *L, const std::string &playername,
player
*/
-void scriptapi_on_player_receive_fields(lua_State *L,
+void scriptapi_on_player_receive_fields(lua_State *L,
ServerActiveObject *player,
const std::string &formname,
const std::map<std::string, std::string> &fields)
@@ -6095,9 +6380,9 @@ bool scriptapi_node_on_timer(lua_State *L, v3s16 p, MapNode node, f32 dtime)
lua_pushnumber(L,dtime);
if(lua_pcall(L, 2, 1, 0))
script_error(L, "error: %s", lua_tostring(L, -1));
- if(lua_isboolean(L,-1) && lua_toboolean(L,-1) == true)
+ if((bool)lua_isboolean(L,-1) && (bool)lua_toboolean(L,-1) == true)
return true;
-
+
return false;
}
@@ -6111,7 +6396,7 @@ void scriptapi_node_on_receive_fields(lua_State *L, v3s16 p,
StackUnroller stack_unroller(L);
INodeDefManager *ndef = get_server(L)->ndef();
-
+
// If node doesn't exist, we don't know what callback to call
MapNode node = get_env(L)->getMap().getNodeNoEx(p);
if(node.getContent() == CONTENT_IGNORE)
@@ -6679,7 +6964,7 @@ bool scriptapi_luaentity_add(lua_State *L, u16 id, const char *name)
verbosestream<<"scriptapi_luaentity_add: id="<<id<<" name=\""
<<name<<"\""<<std::endl;
StackUnroller stack_unroller(L);
-
+
// Get minetest.registered_entities[name]
lua_getglobal(L, "minetest");
lua_getfield(L, -1, "registered_entities");
@@ -6694,7 +6979,7 @@ bool scriptapi_luaentity_add(lua_State *L, u16 id, const char *name)
}
int prototype_table = lua_gettop(L);
//dump2(L, "prototype_table");
-
+
// Create entity object
lua_newtable(L);
int object = lua_gettop(L);
@@ -6702,7 +6987,7 @@ bool scriptapi_luaentity_add(lua_State *L, u16 id, const char *name)
// Set object metatable
lua_pushvalue(L, prototype_table);
lua_setmetatable(L, -2);
-
+
// Add object reference
// This should be userdata with metatable ObjectRef
objectref_get(L, id);
@@ -6718,7 +7003,7 @@ bool scriptapi_luaentity_add(lua_State *L, u16 id, const char *name)
lua_pushnumber(L, id); // Push id
lua_pushvalue(L, object); // Copy object to top of stack
lua_settable(L, -3);
-
+
return true;
}
@@ -6729,11 +7014,11 @@ void scriptapi_luaentity_activate(lua_State *L, u16 id,
assert(lua_checkstack(L, 20));
verbosestream<<"scriptapi_luaentity_activate: id="<<id<<std::endl;
StackUnroller stack_unroller(L);
-
+
// Get minetest.luaentities[id]
luaentity_get(L, id);
int object = lua_gettop(L);
-
+
// Get on_activate function
lua_pushvalue(L, object);
lua_getfield(L, -1, "on_activate");
@@ -6760,12 +7045,12 @@ void scriptapi_luaentity_rm(lua_State *L, u16 id)
lua_getfield(L, -1, "luaentities");
luaL_checktype(L, -1, LUA_TTABLE);
int objectstable = lua_gettop(L);
-
+
// Set luaentities[id] = nil
lua_pushnumber(L, id); // Push id
lua_pushnil(L);
lua_settable(L, objectstable);
-
+
lua_pop(L, 2); // pop luaentities, minetest
}
@@ -6779,20 +7064,20 @@ std::string scriptapi_luaentity_get_staticdata(lua_State *L, u16 id)
// Get minetest.luaentities[id]
luaentity_get(L, id);
int object = lua_gettop(L);
-
+
// Get get_staticdata function
lua_pushvalue(L, object);
lua_getfield(L, -1, "get_staticdata");
if(lua_isnil(L, -1))
return "";
-
+
luaL_checktype(L, -1, LUA_TFUNCTION);
lua_pushvalue(L, object); // self
// Call with 1 arguments, 1 results
if(lua_pcall(L, 1, 1, 0))
script_error(L, "error running function get_staticdata: %s\n",
lua_tostring(L, -1));
-
+
size_t len=0;
const char *s = lua_tolstring(L, -1, &len);
return std::string(s, len);
@@ -6812,9 +7097,9 @@ void scriptapi_luaentity_get_properties(lua_State *L, u16 id,
// Set default values that differ from ObjectProperties defaults
prop->hp_max = 10;
-
+
/* Read stuff */
-
+
prop->hp_max = getintfield_default(L, -1, "hp_max", 10);
getboolfield(L, -1, "physical", prop->physical);
@@ -6829,10 +7114,10 @@ void scriptapi_luaentity_get_properties(lua_State *L, u16 id,
getstringfield(L, -1, "visual", prop->visual);
getstringfield(L, -1, "mesh", prop->mesh);
-
+
// Deprecated: read object properties directly
read_object_properties(L, -1, prop);
-
+
// Read initial_properties
lua_getfield(L, -1, "initial_properties");
read_object_properties(L, -1, prop);
diff --git a/src/server.cpp b/src/server.cpp
index 0a8288324..94a4787f8 100644
--- a/src/server.cpp
+++ b/src/server.cpp
@@ -39,6 +39,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "itemdef.h"
#include "craftdef.h"
#include "mapgen.h"
+#include "biome.h"
#include "content_mapnode.h"
#include "content_nodemeta.h"
#include "content_abm.h"
@@ -50,6 +51,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "sound.h" // dummySoundManager
#include "event_manager.h"
#include "hex.h"
+#include "serverlist.h"
#include "util/string.h"
#include "util/pointedthing.h"
#include "util/mathconstants.h"
@@ -80,7 +82,7 @@ public:
*m_flag = false;
}
}
-
+
private:
bool *m_flag;
};
@@ -105,7 +107,7 @@ public:
*m_ignorevariable = VoxelArea();
}
}
-
+
private:
VoxelArea *m_ignorevariable;
};
@@ -129,7 +131,7 @@ void * ServerThread::Thread()
//TimeTaker timer("AsyncRunStep()");
m_server->AsyncRunStep();
}
-
+
//infostream<<"Running m_server->Receive()"<<std::endl;
m_server->Receive();
}
@@ -149,7 +151,7 @@ void * ServerThread::Thread()
m_server->setAsyncFatalError(e.what());
}
}
-
+
END_DEBUG_EXCEPTION_HANDLER(errorstream)
return NULL;
@@ -168,7 +170,11 @@ void * EmergeThread::Thread()
bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
v3s16 last_tried_pos(-32768,-32768,-32768); // For error output
-
+
+ ServerMap &map = ((ServerMap&)m_server->m_env->getMap());
+ EmergeManager *emerge = m_server->m_emerge;
+ Mapgen *mapgen = emerge->getMapgen();
+
/*
Get block info from queue, emerge them and send them
to clients.
@@ -180,12 +186,12 @@ void * EmergeThread::Thread()
QueuedBlockEmerge *qptr = m_server->m_emerge_queue.pop();
if(qptr == NULL)
break;
-
+
SharedPtr<QueuedBlockEmerge> q(qptr);
v3s16 &p = q->pos;
v2s16 p2d(p.X,p.Z);
-
+
last_tried_pos = p;
/*
@@ -193,18 +199,18 @@ void * EmergeThread::Thread()
*/
if(blockpos_over_limit(p))
continue;
-
+
//infostream<<"EmergeThread::Thread(): running"<<std::endl;
//TimeTaker timer("block emerge");
-
+
/*
Try to emerge it from somewhere.
If it is only wanted as optional, only loading from disk
will be allowed.
*/
-
+
/*
Check if any peer wants it as non-optional. In that case it
will be generated.
@@ -224,17 +230,17 @@ void * EmergeThread::Thread()
u8 flags = i.getNode()->getValue();
if((flags & BLOCK_EMERGE_FLAG_FROMDISK) == false)
only_from_disk = false;
-
+
}
}
-
+
if(enable_mapgen_debug_info)
infostream<<"EmergeThread: p="
<<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
<<"only_from_disk="<<only_from_disk<<std::endl;
-
- ServerMap &map = ((ServerMap&)m_server->m_env->getMap());
-
+
+
+
MapBlock *block = NULL;
bool got_block = true;
core::map<v3s16, MapBlock*> modified_blocks;
@@ -243,17 +249,17 @@ void * EmergeThread::Thread()
Try to fetch block from memory or disk.
If not found and asked to generate, initialize generator.
*/
-
+
bool started_generate = false;
- mapgen::BlockMakeData data;
+ BlockMakeData data;
{
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
block = map.getBlockNoCreateNoEx(p);
if(!block || block->isDummy() || !block->isGenerated())
@@ -264,7 +270,7 @@ void * EmergeThread::Thread()
block = map.loadBlock(p);
}
-
+
// If could not load and allowed to generate, start generation
// inside this same envlock
if(only_from_disk == false &&
@@ -287,16 +293,17 @@ void * EmergeThread::Thread()
SPT_AVG);
TimeTaker t("mapgen::make_block()");
- mapgen::make_block(&data);
+ mapgen->makeChunk(&data);
+ //mapgen::make_block(&data);
if(enable_mapgen_debug_info == false)
t.stop(true); // Hide output
}
-
+
do{ // enable break
// Lock environment again to access the map
JMutexAutoLock envlock(m_server->m_env_mutex);
-
+
ScopeProfiler sp(g_profiler, "EmergeThread: after "
"mapgen::make_block (envlock)", SPT_AVG);
@@ -306,7 +313,7 @@ void * EmergeThread::Thread()
// Get central block
block = map.getBlockNoCreateNoEx(p);
-
+
// If block doesn't exist, don't try doing anything with it
// This happens if the block is not in generation boundaries
if(!block)
@@ -319,7 +326,7 @@ void * EmergeThread::Thread()
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
@@ -332,11 +339,11 @@ void * EmergeThread::Thread()
{
TimeTaker timer("on_generated");
scriptapi_environment_on_generated(m_server->m_lua,
- minp, maxp, mapgen::get_blockseed(data.seed, minp));
+ minp, maxp, emerge->getBlockSeed(minp));
/*int t = timer.stop(true);
dstream<<"on_generated took "<<t<<"ms"<<std::endl;*/
}
-
+
if(enable_mapgen_debug_info)
infostream<<"EmergeThread: ended up with: "
<<analyze_block(block)<<std::endl;
@@ -348,11 +355,11 @@ void * EmergeThread::Thread()
if(block == NULL)
got_block = false;
-
+
/*
Set sent status of modified blocks on clients
*/
-
+
// NOTE: Server's clients are also behind the connection mutex
JMutexAutoLock lock(m_server->m_con_mutex);
@@ -363,17 +370,17 @@ void * EmergeThread::Thread()
{
modified_blocks.insert(p, block);
}
-
+
/*
Set the modified blocks unsent for all the clients
*/
-
+
for(core::map<u16, RemoteClient*>::Iterator
i = m_server->m_clients.getIterator();
i.atEnd() == false; i++)
{
RemoteClient *client = i.getNode()->getValue();
-
+
if(modified_blocks.size() > 0)
{
// Remove block from sent history
@@ -434,14 +441,14 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
core::array<PrioritySortedBlockTransfer> &dest)
{
DSTACK(__FUNCTION_NAME);
-
+
/*u32 timer_result;
TimeTaker timer("RemoteClient::GetNextBlocks", &timer_result);*/
-
+
// Increment timers
m_nothing_to_send_pause_timer -= dtime;
m_nearest_unsent_reset_timer += dtime;
-
+
if(m_nothing_to_send_pause_timer >= 0)
return;
@@ -459,7 +466,7 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
}
//TimeTaker timer("RemoteClient::GetNextBlocks");
-
+
v3f playerpos = player->getPosition();
v3f playerspeed = player->getSpeed();
v3f playerspeeddir(0,0,0);
@@ -471,7 +478,7 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
v3s16 center_nodepos = floatToInt(playerpos_predicted, BS);
v3s16 center = getNodeBlockPos(center_nodepos);
-
+
// Camera position and direction
v3f camera_pos = player->getEyePosition();
v3f camera_dir = v3f(0,0,1);
@@ -484,7 +491,7 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
/*
Get the starting value of the block finder radius.
*/
-
+
if(m_last_center != center)
{
m_nearest_unsent_d = 0;
@@ -493,7 +500,7 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
/*infostream<<"m_nearest_unsent_reset_timer="
<<m_nearest_unsent_reset_timer<<std::endl;*/
-
+
// Reset periodically to workaround for some bugs or stuff
if(m_nearest_unsent_reset_timer > 20.0)
{
@@ -514,7 +521,7 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
/*
Check the time from last addNode/removeNode.
-
+
Decrease send rate if player is building stuff.
*/
m_time_from_building += dtime;
@@ -524,12 +531,12 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
max_simul_sends_usually
= LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
}
-
+
/*
Number of blocks sending + number of blocks selected for sending
*/
u32 num_blocks_selected = m_blocks_sending.size();
-
+
/*
next time d will be continued from the d from which the nearest
unsent block was found this time.
@@ -541,28 +548,28 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
s16 d_max = g_settings->getS16("max_block_send_distance");
s16 d_max_gen = g_settings->getS16("max_block_generate_distance");
-
+
// Don't loop very much at a time
s16 max_d_increment_at_time = 2;
if(d_max > d_start + max_d_increment_at_time)
d_max = d_start + max_d_increment_at_time;
/*if(d_max_gen > d_start+2)
d_max_gen = d_start+2;*/
-
+
//infostream<<"Starting from "<<d_start<<std::endl;
s32 nearest_emerged_d = -1;
s32 nearest_emergefull_d = -1;
s32 nearest_sent_d = -1;
bool queue_is_full = false;
-
+
s16 d;
for(d = d_start; d <= d_max; d++)
{
/*errorstream<<"checking d="<<d<<" for "
<<server->getPlayerName(peer_id)<<std::endl;*/
//infostream<<"RemoteClient::SendBlocks(): d="<<d<<std::endl;
-
+
/*
If m_nearest_unsent_d was changed by the EmergeThread
(it can change it to 0 through SetBlockNotSent),
@@ -581,12 +588,12 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
*/
core::list<v3s16> list;
getFacePositions(list, d);
-
+
core::list<v3s16>::Iterator li;
for(li=list.begin(); li!=list.end(); li++)
{
v3s16 p = *li + center;
-
+
/*
Send throttling
- Don't allow too many simultaneous transfers
@@ -594,10 +601,10 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
Also, don't send blocks that are already flying.
*/
-
+
// Start with the usual maximum
u16 max_simul_dynamic = max_simul_sends_usually;
-
+
// If block is very close, allow full maximum
if(d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D)
max_simul_dynamic = max_simul_sends_setting;
@@ -608,11 +615,11 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
queue_is_full = true;
goto queue_full_break;
}
-
+
// Don't send blocks that are currently being transferred
if(m_blocks_sending.find(p) != NULL)
continue;
-
+
/*
Do not go over-limit
*/
@@ -623,10 +630,10 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
|| p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
|| p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
continue;
-
+
// If this is true, inexistent block will be made from scratch
bool generate = d <= d_max_gen;
-
+
{
/*// Limit the generating area vertically to 2/3
if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
@@ -655,7 +662,7 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
v2s16 p2d_nodes_center(
MAP_BLOCKSIZE*p.X,
MAP_BLOCKSIZE*p.Z);
-
+
// Get ground height in nodes
s16 gh = server->m_env->getServerMap().findGroundLevel(
p2d_nodes_center);
@@ -697,7 +704,7 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
Check if map has this block
*/
MapBlock *block = server->m_env->getMap().getBlockNoCreateNoEx(p);
-
+
bool surely_not_found_on_disk = false;
bool block_is_invalid = false;
if(block != NULL)
@@ -711,13 +718,13 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
{
surely_not_found_on_disk = true;
}
-
+
// Block is valid if lighting is up-to-date and data exists
if(block->isValid() == false)
{
block_is_invalid = true;
}
-
+
/*if(block->isFullyGenerated() == false)
{
block_is_invalid = true;
@@ -775,13 +782,13 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
if(server->m_emerge_queue.peerItemCount(peer_id) < max_emerge)
{
//infostream<<"Adding block to emerge queue"<<std::endl;
-
+
// Add it to the emerge queue and trigger the thread
-
+
u8 flags = 0;
if(generate == false)
flags |= BLOCK_EMERGE_FLAG_FROMDISK;
-
+
server->m_emerge_queue.addBlock(peer_id, p, flags);
server->m_emergethread.trigger();
@@ -790,8 +797,9 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
} else {
if(nearest_emergefull_d == -1)
nearest_emergefull_d = d;
+ goto queue_full_break;
}
-
+
// get next one.
continue;
}
@@ -816,7 +824,7 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
queue_full_break:
//infostream<<"Stopped at "<<d<<std::endl;
-
+
// If nothing was found for sending and nothing was queued for
// emerging, continue next time browsing from here
if(nearest_emerged_d != -1){
@@ -871,7 +879,7 @@ void RemoteClient::SentBlock(v3s16 p)
void RemoteClient::SetBlockNotSent(v3s16 p)
{
m_nearest_unsent_d = 0;
-
+
if(m_blocks_sending.find(p) != NULL)
m_blocks_sending.remove(p);
if(m_blocks_sent.find(p) != NULL)
@@ -881,7 +889,7 @@ void RemoteClient::SetBlockNotSent(v3s16 p)
void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks)
{
m_nearest_unsent_d = 0;
-
+
for(core::map<v3s16, MapBlock*>::Iterator
i = blocks.getIterator();
i.atEnd()==false; i++)
@@ -937,6 +945,8 @@ Server::Server(
m_rollback(NULL),
m_rollback_sink_enabled(true),
m_enable_rollback_recording(false),
+ m_emerge(NULL),
+ m_biomedef(NULL),
m_lua(NULL),
m_itemdef(createItemDefManager()),
m_nodedef(createNodeDefManager()),
@@ -952,10 +962,12 @@ Server::Server(
{
m_liquid_transform_timer = 0.0;
m_print_info_timer = 0.0;
+ m_masterserver_timer = 0.0;
m_objectdata_timer = 0.0;
m_emergethread_trigger_timer = 0.0;
m_savemap_timer = 0.0;
-
+ m_clients_number = 0;
+
m_env_mutex.Init();
m_con_mutex.Init();
m_step_dtime_mutex.Init();
@@ -963,10 +975,10 @@ Server::Server(
if(path_world == "")
throw ServerError("Supplied empty world path");
-
+
if(!gamespec.isValid())
throw ServerError("Supplied invalid gamespec");
-
+
infostream<<"Server created for gameid \""<<m_gamespec.id<<"\"";
if(m_simple_singleplayer_mode)
infostream<<" in simple singleplayer mode"<<std::endl;
@@ -976,37 +988,78 @@ Server::Server(
infostream<<"- config: "<<m_path_config<<std::endl;
infostream<<"- game: "<<m_gamespec.path<<std::endl;
+ // Create biome definition manager
+ m_biomedef = new BiomeDefManager(this);
+
// Create rollback manager
std::string rollback_path = m_path_world+DIR_DELIM+"rollback.txt";
m_rollback = createRollbackManager(rollback_path, this);
- // Add world mod search path
- m_modspaths.push_front(m_path_world + DIR_DELIM + "worldmods");
- // Add addon mod search path
- for(std::set<std::string>::const_iterator i = m_gamespec.mods_paths.begin();
- i != m_gamespec.mods_paths.end(); i++)
- m_modspaths.push_front((*i));
+ // Create world if it doesn't exist
+ if(!initializeWorld(m_path_world, m_gamespec.id))
+ throw ServerError("Failed to initialize world");
- // Print out mod search paths
- for(core::list<std::string>::Iterator i = m_modspaths.begin();
- i != m_modspaths.end(); i++){
- std::string modspath = *i;
- infostream<<"- mods: "<<modspath<<std::endl;
+ ModConfiguration modconf(m_path_world);
+ m_mods = modconf.getMods();
+ std::list<ModSpec> unsatisfied_mods = modconf.getUnsatisfiedMods();
+ // complain about mods with unsatisfied dependencies
+ if(!modconf.isConsistent())
+ {
+ for(std::list<ModSpec>::iterator it = unsatisfied_mods.begin();
+ it != unsatisfied_mods.end(); ++it)
+ {
+ ModSpec mod = *it;
+ errorstream << "mod \"" << mod.name << "\" has unsatisfied dependencies: ";
+ for(std::set<std::string>::iterator dep_it = mod.unsatisfied_depends.begin();
+ dep_it != mod.unsatisfied_depends.end(); ++dep_it)
+ errorstream << " \"" << *dep_it << "\"";
+ errorstream << std::endl;
+ }
}
-
+
+ Settings worldmt_settings;
+ std::string worldmt = m_path_world + DIR_DELIM + "world.mt";
+ worldmt_settings.readConfigFile(worldmt.c_str());
+ std::vector<std::string> names = worldmt_settings.getNames();
+ std::set<std::string> exclude_mod_names;
+ std::set<std::string> load_mod_names;
+ for(std::vector<std::string>::iterator it = names.begin();
+ it != names.end(); ++it)
+ {
+ std::string name = *it;
+ if (name.compare(0,9,"load_mod_")==0)
+ {
+ if(worldmt_settings.getBool(name))
+ load_mod_names.insert(name.substr(9));
+ else
+ exclude_mod_names.insert(name.substr(9));
+ }
+ }
+ // complain about mods declared to be loaded, but not found
+ for(std::vector<ModSpec>::iterator it = m_mods.begin();
+ it != m_mods.end(); ++it)
+ load_mod_names.erase((*it).name);
+ for(std::list<ModSpec>::iterator it = unsatisfied_mods.begin();
+ it != unsatisfied_mods.end(); ++it)
+ load_mod_names.erase((*it).name);
+ if(!load_mod_names.empty())
+ {
+ errorstream << "The following mods could not be found:";
+ for(std::set<std::string>::iterator it = load_mod_names.begin();
+ it != load_mod_names.end(); ++it)
+ errorstream << " \"" << (*it) << "\"";
+ errorstream << std::endl;
+ }
+
// Path to builtin.lua
std::string builtinpath = getBuiltinLuaPath() + DIR_DELIM + "builtin.lua";
- // Create world if it doesn't exist
- if(!initializeWorld(m_path_world, m_gamespec.id))
- throw ServerError("Failed to initialize world");
-
// Lock environment
JMutexAutoLock envlock(m_env_mutex);
JMutexAutoLock conlock(m_con_mutex);
// Initialize scripting
-
+
infostream<<"Server: Initializing Lua"<<std::endl;
m_lua = script_init();
assert(m_lua);
@@ -1021,18 +1074,16 @@ Server::Server(
<<builtinpath<<std::endl;
throw ModError("Failed to load and run "+builtinpath);
}
- // Find mods in mod search paths
- m_mods = getMods(m_modspaths);
// Print 'em
infostream<<"Server: Loading mods: ";
- for(core::list<ModSpec>::Iterator i = m_mods.begin();
+ for(std::vector<ModSpec>::iterator i = m_mods.begin();
i != m_mods.end(); i++){
const ModSpec &mod = *i;
infostream<<mod.name<<" ";
}
infostream<<std::endl;
// Load and run "mod" scripts
- for(core::list<ModSpec>::Iterator i = m_mods.begin();
+ for(std::vector<ModSpec>::iterator i = m_mods.begin();
i != m_mods.end(); i++){
const ModSpec &mod = *i;
std::string scriptpath = mod.path + DIR_DELIM + "init.lua";
@@ -1045,23 +1096,30 @@ Server::Server(
throw ModError("Failed to load and run "+scriptpath);
}
}
-
+
// Read Textures and calculate sha1 sums
fillMediaCache();
// Apply item aliases in the node definition manager
m_nodedef->updateAliases(m_itemdef);
+ // Add default biomes after nodedef had its aliases added
+ m_biomedef->addDefaultBiomes();
+
+ // Create emerge manager
+ m_emerge = new EmergeManager(this, m_biomedef);
+
// Initialize Environment
+ ServerMap *servermap = new ServerMap(path_world, this, m_emerge);
+ m_env = new ServerEnvironment(servermap, m_lua, this, this);
- m_env = new ServerEnvironment(new ServerMap(path_world, this), m_lua,
- this, this);
-
+ m_emerge->initMapgens(servermap->getMapgenParams());
+
// Give environment reference to scripting api
scriptapi_add_environment(m_lua, m_env);
-
+
// Register us to receive map edit events
- m_env->getMap().addEventReceiver(this);
+ servermap->addEventReceiver(this);
// If file exists, load environment metadata
if(fs::PathExists(m_path_world+DIR_DELIM+"env_meta.txt"))
@@ -1089,7 +1147,7 @@ Server::~Server()
*/
{
JMutexAutoLock conlock(m_con_mutex);
-
+
std::wstring line = L"*** Server shutting down";
/*
@@ -1138,12 +1196,12 @@ Server::~Server()
infostream<<"Server: Saving environment metadata"<<std::endl;
m_env->saveMeta(m_path_world);
}
-
+
/*
Stop threads
*/
stop();
-
+
/*
Delete clients
*/
@@ -1154,19 +1212,21 @@ Server::~Server()
i = m_clients.getIterator();
i.atEnd() == false; i++)
{
+
// Delete client
delete i.getNode()->getValue();
}
}
-
+
// Delete things in the reverse order of creation
delete m_env;
delete m_rollback;
+ delete m_emerge;
delete m_event;
delete m_itemdef;
delete m_nodedef;
delete m_craftdef;
-
+
// Deinitialize scripting
infostream<<"Server: Deinitializing scripting"<<std::endl;
script_deinit(m_lua);
@@ -1188,7 +1248,7 @@ void Server::start(unsigned short port)
// Stop thread if already running
m_thread.stop();
-
+
// Initialize connection
m_con.SetTimeoutMs(30);
m_con.Serve(port);
@@ -1196,7 +1256,7 @@ void Server::start(unsigned short port)
// Start thread
m_thread.setRun(true);
m_thread.Start();
-
+
// ASCII art for the win!
actionstream
<<" .__ __ __ "<<std::endl
@@ -1213,7 +1273,7 @@ void Server::start(unsigned short port)
void Server::stop()
{
DSTACK(__FUNCTION_NAME);
-
+
infostream<<"Server: Stopping and waiting threads"<<std::endl;
// Stop threads (set run=false first so both start stopping)
@@ -1221,7 +1281,7 @@ void Server::stop()
m_emergethread.setRun(false);
m_thread.stop();
m_emergethread.stop();
-
+
infostream<<"Server: Threads stopped"<<std::endl;
}
@@ -1245,28 +1305,28 @@ void Server::step(float dtime)
void Server::AsyncRunStep()
{
DSTACK(__FUNCTION_NAME);
-
+
g_profiler->add("Server::AsyncRunStep (num)", 1);
-
+
float dtime;
{
JMutexAutoLock lock1(m_step_dtime_mutex);
dtime = m_step_dtime;
}
-
+
{
// Send blocks to clients
SendBlocks(dtime);
}
-
+
if(dtime < 0.001)
return;
-
+
g_profiler->add("Server::AsyncRunStep with dtime (num)", 1);
//infostream<<"Server steps "<<dtime<<std::endl;
//infostream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
-
+
{
JMutexAutoLock lock1(m_step_dtime_mutex);
m_step_dtime -= dtime;
@@ -1278,14 +1338,14 @@ void Server::AsyncRunStep()
{
m_uptime.set(m_uptime.get() + dtime);
}
-
+
{
// Process connection's timeouts
JMutexAutoLock lock2(m_con_mutex);
ScopeProfiler sp(g_profiler, "Server: connection timeout processing");
m_con.RunTimeouts(dtime);
}
-
+
{
// This has to be called so that the client list gets synced
// with the peer list of the connection
@@ -1332,7 +1392,7 @@ void Server::AsyncRunStep()
ScopeProfiler sp2(g_profiler, "SEnv step avg", SPT_AVG);
m_env->step(dtime);
}
-
+
const float map_timer_and_unload_dtime = 2.92;
if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
{
@@ -1342,7 +1402,7 @@ void Server::AsyncRunStep()
m_env->getMap().timerUpdate(map_timer_and_unload_dtime,
g_settings->getFloat("server_unload_unused_data_timeout"));
}
-
+
/*
Do background stuff
*/
@@ -1389,27 +1449,27 @@ void Server::AsyncRunStep()
}
}
}
-
+
/* Transform liquids */
m_liquid_transform_timer += dtime;
if(m_liquid_transform_timer >= 1.00)
{
m_liquid_transform_timer -= 1.00;
-
+
JMutexAutoLock lock(m_env_mutex);
ScopeProfiler sp(g_profiler, "Server: liquid transform");
core::map<v3s16, MapBlock*> modified_blocks;
m_env->getMap().transformLiquids(modified_blocks);
-#if 0
+#if 0
/*
Update lighting
*/
core::map<v3s16, MapBlock*> lighting_modified_blocks;
ServerMap &map = ((ServerMap&)m_env->getMap());
map.updateLighting(modified_blocks, lighting_modified_blocks);
-
+
// Add blocks modified by lighting to modified_blocks
for(core::map<v3s16, MapBlock*>::Iterator
i = lighting_modified_blocks.getIterator();
@@ -1422,7 +1482,7 @@ void Server::AsyncRunStep()
/*
Set the modified blocks unsent for all the clients
*/
-
+
JMutexAutoLock lock2(m_con_mutex);
for(core::map<u16, RemoteClient*>::Iterator
@@ -1430,7 +1490,7 @@ void Server::AsyncRunStep()
i.atEnd() == false; i++)
{
RemoteClient *client = i.getNode()->getValue();
-
+
if(modified_blocks.size() > 0)
{
// Remove block from sent history
@@ -1448,7 +1508,7 @@ void Server::AsyncRunStep()
counter = 0.0;
JMutexAutoLock lock2(m_con_mutex);
-
+ m_clients_number = 0;
if(m_clients.size() != 0)
infostream<<"Players:"<<std::endl;
for(core::map<u16, RemoteClient*>::Iterator
@@ -1462,10 +1522,25 @@ void Server::AsyncRunStep()
continue;
infostream<<"* "<<player->getName()<<"\t";
client->PrintInfo(infostream);
+ ++m_clients_number;
}
}
}
+
+#if USE_CURL
+ // send masterserver announce
+ {
+ float &counter = m_masterserver_timer;
+ if((!counter || counter >= 300.0) && g_settings->getBool("server_announce") == true)
+ {
+ ServerList::sendAnnounce(!counter ? "start" : "update", m_clients_number);
+ counter = 0.01;
+ }
+ counter += dtime;
+ }
+#endif
+
//if(g_settings->getBool("enable_experimental"))
{
@@ -1511,18 +1586,18 @@ void Server::AsyncRunStep()
client->m_known_objects, removed_objects);
m_env->getAddedActiveObjects(pos, radius,
client->m_known_objects, added_objects);
-
+
// Ignore if nothing happened
if(removed_objects.size() == 0 && added_objects.size() == 0)
{
//infostream<<"active objects: none changed"<<std::endl;
continue;
}
-
+
std::string data_buffer;
char buf[4];
-
+
// Handle removed objects
writeU16((u8*)buf, removed_objects.size());
data_buffer.append(buf, 2);
@@ -1537,7 +1612,7 @@ void Server::AsyncRunStep()
// Add to data buffer for sending
writeU16((u8*)buf, i.getNode()->getKey());
data_buffer.append(buf, 2);
-
+
// Remove from known objects
client->m_known_objects.remove(i.getNode()->getKey());
@@ -1555,7 +1630,7 @@ void Server::AsyncRunStep()
// Get object
u16 id = i.getNode()->getKey();
ServerActiveObject* obj = m_env->getActiveObject(id);
-
+
// Get object type
u8 type = ACTIVEOBJECT_TYPE_INVALID;
if(obj == NULL)
@@ -1569,7 +1644,7 @@ void Server::AsyncRunStep()
data_buffer.append(buf, 2);
writeU8((u8*)buf, type);
data_buffer.append(buf, 1);
-
+
if(obj)
data_buffer.append(serializeLongString(
obj->getClientInitializationData(client->net_proto_version)));
@@ -1619,7 +1694,7 @@ void Server::AsyncRunStep()
all_known_objects[id] = true;
}
}
-
+
m_env->setKnownActiveObjects(whatever);
#endif
@@ -1644,7 +1719,7 @@ void Server::AsyncRunStep()
ActiveObjectMessage aom = m_env->getActiveObjectMessage();
if(aom.id == 0)
break;
-
+
core::list<ActiveObjectMessage>* message_list = NULL;
core::map<u16, core::list<ActiveObjectMessage>* >::Node *n;
n = buffered_messages.find(aom.id);
@@ -1659,7 +1734,7 @@ void Server::AsyncRunStep()
}
message_list->push_back(aom);
}
-
+
// Route data to every client
for(core::map<u16, RemoteClient*>::Iterator
i = m_clients.getIterator();
@@ -1766,7 +1841,7 @@ void Server::AsyncRunStep()
while(m_unsent_map_edit_queue.size() != 0)
{
MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
-
+
// Players far away from the change are stored here.
// Instead of sending the changes, MapBlocks are set not sent
// for them.
@@ -1818,7 +1893,7 @@ void Server::AsyncRunStep()
infostream<<"WARNING: Server: Unknown MapEditEvent "
<<((u32)event->type)<<std::endl;
}
-
+
/*
Set blocks not sent to far players
*/
@@ -1862,7 +1937,7 @@ void Server::AsyncRunStep()
verbosestream<<"Server: MapEditEvents:"<<std::endl;
prof.print(verbosestream);
}
-
+
}
/*
@@ -1875,7 +1950,7 @@ void Server::AsyncRunStep()
if(counter >= 2.0)
{
counter = 0.0;
-
+
m_emergethread.trigger();
// Update m_enable_rollback_recording here too
@@ -1898,13 +1973,13 @@ void Server::AsyncRunStep()
//Ban stuff
if(m_banmanager.isModified())
m_banmanager.save();
-
+
// Save changed parts of map
m_env->getMap().save(MOD_STATE_WRITE_NEEDED);
// Save players
m_env->serializePlayers(m_path_world);
-
+
// Save environment metadata
m_env->saveMeta(m_path_world);
}
@@ -1938,7 +2013,7 @@ void Server::Receive()
catch(con::PeerNotFoundException &e)
{
//NOTE: This is not needed anymore
-
+
// The peer has been disconnected.
// Find the associated player and remove it.
@@ -1958,9 +2033,9 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
// Environment is locked first.
JMutexAutoLock envlock(m_env_mutex);
JMutexAutoLock conlock(m_con_mutex);
-
+
ScopeProfiler sp(g_profiler, "Server::ProcessData");
-
+
try{
Address address = m_con.GetPeerAddress(peer_id);
std::string addr_s = address.serializeString();
@@ -1984,7 +2059,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
<<peer_id<<" not found"<<std::endl;
return;
}
-
+
std::string addr_s = m_con.GetPeerAddress(peer_id).serializeString();
u8 peer_ser_ver = getClient(peer_id)->serialization_version;
@@ -1996,7 +2071,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
return;
ToServerCommand command = (ToServerCommand)readU16(&data[0]);
-
+
if(command == TOSERVER_INIT)
{
// [0] u16 TOSERVER_INIT
@@ -2022,7 +2097,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
//peer->serialization_version = deployed;
getClient(peer_id)->pending_serialization_version = deployed;
-
+
if(deployed == SER_FMT_VER_INVALID)
{
actionstream<<"Server: A mismatched client tried to connect from "
@@ -2037,7 +2112,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
);
return;
}
-
+
/*
Read and check network protocol version
*/
@@ -2093,7 +2168,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
);
return;
}
-
+
if(g_settings->getBool("strict_protocol_version_checking"))
{
if(net_proto_version != LATEST_PROTOCOL_VERSION)
@@ -2118,7 +2193,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
/*
Set up player
*/
-
+
// Get player name
char playername[PLAYERNAME_SIZE];
for(u32 i=0; i<PLAYERNAME_SIZE-1; i++)
@@ -2126,7 +2201,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
playername[i] = data[3+i];
}
playername[PLAYERNAME_SIZE-1] = 0;
-
+
if(playername[0]=='\0')
{
actionstream<<"Server: Player with an empty name "
@@ -2170,10 +2245,10 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
SendAccessDenied(m_con, peer_id, L"Invalid password hash");
return;
}
-
+
std::string checkpwd; // Password hash to check against
bool has_auth = scriptapi_get_auth(m_lua, playername, &checkpwd, NULL);
-
+
// If no authentication info exists for user, create it
if(!has_auth){
if(!isSingleplayer() &&
@@ -2194,7 +2269,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
scriptapi_create_auth(m_lua, playername, initial_password);
}
-
+
has_auth = scriptapi_get_auth(m_lua, playername, &checkpwd, NULL);
if(!has_auth){
@@ -2219,7 +2294,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
L"Running in simple singleplayer mode.");
return;
}
-
+
// Enforce user limit.
// Don't enforce for users that have some admin right
if(m_clients.size() >= g_settings->getU16("max_users") &&
@@ -2257,7 +2332,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
writeV3S16(&reply[2+1], floatToInt(playersao->getPlayer()->getPosition()+v3f(0,BS/2,0), BS));
writeU64(&reply[2+1+6], m_env->getServerMap().getSeed());
writeF1000(&reply[2+1+6+8], g_settings->getFloat("dedicated_server_step"));
-
+
// Send as reliable
m_con.Send(peer_id, 0, reply, true);
}
@@ -2293,18 +2368,21 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
infostream<<"Server: Sending content to "
<<getPlayerName(peer_id)<<std::endl;
+ // Send player movement settings
+ SendMovement(m_con, peer_id);
+
// Send item definitions
SendItemDef(m_con, peer_id, m_itemdef);
-
+
// Send node definitions
SendNodeDef(m_con, peer_id, m_nodedef, client->net_proto_version);
-
+
// Send media announcement
sendMediaAnnouncement(peer_id);
-
+
// Send privileges
SendPlayerPrivileges(peer_id);
-
+
// Send inventory formspec
SendPlayerInventoryFormspec(peer_id);
@@ -2315,10 +2393,10 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
// Send HP
if(g_settings->getBool("enable_damage"))
SendPlayerHP(peer_id);
-
+
// Send detached inventories
sendDetachedInventories(peer_id);
-
+
// Show death screen if necessary
if(player->hp == 0)
SendDeathscreen(m_con, peer_id, false, v3f(0,0,0));
@@ -2329,20 +2407,20 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
m_env->getTimeOfDay(), g_settings->getFloat("time_speed"));
m_con.Send(peer_id, 0, data, true);
}
-
+
// Note things in chat if not in simple singleplayer mode
if(!m_simple_singleplayer_mode)
{
// Send information about server to player in chat
SendChatMessage(peer_id, getStatusString());
-
+
// Send information about joining in chat
{
std::wstring name = L"unknown";
Player *player = m_env->getPlayer(peer_id);
if(player != NULL)
name = narrow_to_wide(player->getName());
-
+
std::wstring message;
message += L"*** ";
message += name;
@@ -2350,7 +2428,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
BroadcastChatMessage(message);
}
}
-
+
// Warnings about protocol version can be issued here
if(getClient(peer_id)->net_proto_version < LATEST_PROTOCOL_VERSION)
{
@@ -2393,7 +2471,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
" Skipping incoming command="<<command<<std::endl;
return;
}
-
+
Player *player = m_env->getPlayer(peer_id);
if(player == NULL){
infostream<<"Server::ProcessData(): Cancelling: "
@@ -2414,7 +2492,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
{
if(datasize < 2+12+12+4+4)
return;
-
+
u32 start = 0;
v3s32 ps = readV3S32(&data[start+2]);
v3s32 ss = readV3S32(&data[start+2+12]);
@@ -2442,7 +2520,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
player->control.sneak = (bool)(keyPressed&64);
player->control.LMB = (bool)(keyPressed&128);
player->control.RMB = (bool)(keyPressed&256);
-
+
/*infostream<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
<<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
<<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
@@ -2451,7 +2529,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
{
if(datasize < 2+1)
return;
-
+
/*
[0] u16 command
[2] u8 count
@@ -2477,7 +2555,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
{
if(datasize < 2+1)
return;
-
+
/*
[0] u16 command
[2] u8 count
@@ -2658,7 +2736,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
return;
}
}
-
+
// Do the action
a->apply(this, playersao, this);
// Eat the action
@@ -2674,11 +2752,11 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
u8 buf[6];
std::string datastring((char*)&data[2], datasize-2);
std::istringstream is(datastring, std::ios_base::binary);
-
+
// Read stuff
is.read((char*)buf, 2);
u16 len = readU16(buf);
-
+
std::wstring message;
for(u16 i=0; i<len; i++)
{
@@ -2692,21 +2770,21 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
// Get player name of this client
std::wstring name = narrow_to_wide(player->getName());
-
+
// Run script hook
bool ate = scriptapi_on_chat_message(m_lua, 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 = false;
// Whether to send to other players
bool send_to_others = false;
-
+
// Commands are implemented in Lua, so only catch invalid
// commands that were not "eaten" and send an error back
if(message[0] == L'/')
@@ -2731,7 +2809,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
send_to_sender = true;
}
}
-
+
if(line != L"")
{
if(send_to_others)
@@ -2858,9 +2936,9 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
{
if(player->hp != 0 || !g_settings->getBool("enable_damage"))
return;
-
+
RespawnPlayer(peer_id);
-
+
actionstream<<player->getName()<<" respawns at "
<<PP(player->getPosition()/BS)<<std::endl;
@@ -2870,7 +2948,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
else if(command == TOSERVER_REQUEST_MEDIA) {
std::string datastring((char*)&data[2], datasize-2);
std::istringstream is(datastring, std::ios_base::binary);
-
+
core::list<MediaRequest> tosend;
u16 numfiles = readU16(is);
@@ -3164,7 +3242,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
}
}
} // action == 2
-
+
/*
3: place block or right-click object
*/
@@ -3200,7 +3278,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
// Apply returned ItemStack
playersao->setWieldedItem(item);
}
-
+
// If item has node placement prediction, always send the above
// node to make sure the client knows what exactly happened
if(item.getDefinition(m_itemdef).node_placement_prediction != ""){
@@ -3261,7 +3339,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
{
std::string datastring((char*)&data[2], datasize-2);
std::istringstream is(datastring, std::ios_base::binary);
-
+
v3s16 p = readV3S16(is);
std::string formname = deSerializeString(is);
int num = readU16(is);
@@ -3294,7 +3372,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
{
std::string datastring((char*)&data[2], datasize-2);
std::istringstream is(datastring, std::ios_base::binary);
-
+
std::string formname = deSerializeString(is);
int num = readU16(is);
std::map<std::string, std::string> fields;
@@ -3311,7 +3389,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
infostream<<"Server::ProcessData(): Ignoring "
"unknown command "<<command<<std::endl;
}
-
+
} //try
catch(SendFailedException &e)
{
@@ -3397,7 +3475,7 @@ void Server::setInventoryModified(const InventoryLocation &loc)
MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
if(block)
block->raiseModified(MOD_STATE_WRITE_NEEDED);
-
+
setBlockNotSent(blockpos);
}
break;
@@ -3416,11 +3494,11 @@ core::list<PlayerInfo> Server::getPlayerInfo()
DSTACK(__FUNCTION_NAME);
JMutexAutoLock envlock(m_env_mutex);
JMutexAutoLock conlock(m_con_mutex);
-
+
core::list<PlayerInfo> list;
core::list<Player*> players = m_env->getPlayers();
-
+
core::list<Player*>::Iterator i;
for(i = players.begin();
i != players.end(); i++)
@@ -3458,7 +3536,7 @@ void Server::peerAdded(con::Peer *peer)
DSTACK(__FUNCTION_NAME);
verbosestream<<"Server::peerAdded(): peer->id="
<<peer->id<<std::endl;
-
+
PeerChange c;
c.type = PEER_ADDED;
c.peer_id = peer->id;
@@ -3471,7 +3549,7 @@ void Server::deletingPeer(con::Peer *peer, bool timeout)
DSTACK(__FUNCTION_NAME);
verbosestream<<"Server::deletingPeer(): peer->id="
<<peer->id<<", timeout="<<timeout<<std::endl;
-
+
PeerChange c;
c.type = PEER_REMOVED;
c.peer_id = peer->id;
@@ -3483,6 +3561,32 @@ void Server::deletingPeer(con::Peer *peer, bool timeout)
Static send methods
*/
+void Server::SendMovement(con::Connection &con, u16 peer_id)
+{
+ DSTACK(__FUNCTION_NAME);
+ std::ostringstream os(std::ios_base::binary);
+
+ writeU16(os, TOCLIENT_MOVEMENT);
+ writeF1000(os, g_settings->getFloat("movement_acceleration_default"));
+ writeF1000(os, g_settings->getFloat("movement_acceleration_air"));
+ writeF1000(os, g_settings->getFloat("movement_acceleration_fast"));
+ writeF1000(os, g_settings->getFloat("movement_speed_walk"));
+ writeF1000(os, g_settings->getFloat("movement_speed_crouch"));
+ writeF1000(os, g_settings->getFloat("movement_speed_fast"));
+ writeF1000(os, g_settings->getFloat("movement_speed_climb"));
+ writeF1000(os, g_settings->getFloat("movement_speed_jump"));
+ writeF1000(os, g_settings->getFloat("movement_liquid_fluidity"));
+ writeF1000(os, g_settings->getFloat("movement_liquid_fluidity_smooth"));
+ writeF1000(os, g_settings->getFloat("movement_liquid_sink"));
+ writeF1000(os, g_settings->getFloat("movement_gravity"));
+
+ // Make data buffer
+ std::string s = os.str();
+ SharedBuffer<u8> data((u8*)s.c_str(), s.size());
+ // Send as reliable
+ con.Send(peer_id, 0, data, true);
+}
+
void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
{
DSTACK(__FUNCTION_NAME);
@@ -3592,7 +3696,7 @@ void Server::SendNodeDef(con::Connection &con, u16 peer_id,
void Server::SendInventory(u16 peer_id)
{
DSTACK(__FUNCTION_NAME);
-
+
PlayerSAO *playersao = getPlayerSAO(peer_id);
assert(playersao);
@@ -3606,11 +3710,11 @@ void Server::SendInventory(u16 peer_id)
playersao->getInventory()->serialize(os);
std::string s = os.str();
-
+
SharedBuffer<u8> data(s.size()+2);
writeU16(&data[0], TOCLIENT_INVENTORY);
memcpy(&data[2], s.c_str(), s.size());
-
+
// Send as reliable
m_con.Send(peer_id, 0, data, true);
}
@@ -3618,18 +3722,18 @@ void Server::SendInventory(u16 peer_id)
void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
{
DSTACK(__FUNCTION_NAME);
-
+
std::ostringstream os(std::ios_base::binary);
u8 buf[12];
-
+
// Write command
writeU16(buf, TOCLIENT_CHAT_MESSAGE);
os.write((char*)buf, 2);
-
+
// Write length
writeU16(buf, message.size());
os.write((char*)buf, 2);
-
+
// Write string
for(u32 i=0; i<message.size(); i++)
{
@@ -3637,7 +3741,7 @@ void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
writeU16(buf, w);
os.write((char*)buf, 2);
}
-
+
// Make data buffer
std::string s = os.str();
SharedBuffer<u8> data((u8*)s.c_str(), s.size());
@@ -3700,7 +3804,7 @@ void Server::SendMovePlayer(u16 peer_id)
writeV3F1000(os, player->getPosition());
writeF1000(os, player->getPitch());
writeF1000(os, player->getYaw());
-
+
{
v3f pos = player->getPosition();
f32 pitch = player->getPitch();
@@ -3728,7 +3832,7 @@ void Server::SendPlayerPrivileges(u16 peer_id)
std::set<std::string> privs;
scriptapi_get_auth(m_lua, player->getName(), NULL, &privs);
-
+
std::ostringstream os(std::ios_base::binary);
writeU16(os, TOCLIENT_PRIVILEGES);
writeU16(os, privs.size());
@@ -3890,7 +3994,7 @@ void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
// Don't send if it's the same one
if(client->peer_id == ignore_id)
continue;
-
+
if(far_players)
{
// Get player
@@ -3978,7 +4082,7 @@ void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
DSTACK(__FUNCTION_NAME);
v3s16 p = block->getPos();
-
+
#if 0
// Analyze it a bit
bool completely_air = true;
@@ -4003,7 +4107,7 @@ void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
/*
Create a packet with the block in the right format
*/
-
+
std::ostringstream os(std::ios_base::binary);
block->serialize(os, ver, false);
std::string s = os.str();
@@ -4019,7 +4123,7 @@ void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
/*infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
<<": \tpacket size: "<<replysize<<std::endl;*/
-
+
/*
Send packet
*/
@@ -4038,7 +4142,7 @@ void Server::SendBlocks(float dtime)
core::array<PrioritySortedBlockTransfer> queue;
s32 total_sending = 0;
-
+
{
ScopeProfiler sp(g_profiler, "Server: selecting blocks for sending");
@@ -4055,10 +4159,10 @@ void Server::SendBlocks(float dtime)
continue;
total_sending += client->SendingCount();
-
+
if(client->serialization_version == SER_FMT_VER_INVALID)
continue;
-
+
client->GetNextBlocks(this, dtime, queue);
}
}
@@ -4074,7 +4178,7 @@ void Server::SendBlocks(float dtime)
if(total_sending >= g_settings->getS32
("max_simultaneous_block_sends_server_total"))
break;
-
+
PrioritySortedBlockTransfer q = queue[i];
MapBlock *block = NULL;
@@ -4102,10 +4206,10 @@ void Server::fillMediaCache()
DSTACK(__FUNCTION_NAME);
infostream<<"Server: Calculating media file checksums"<<std::endl;
-
+
// Collect all media file paths
std::list<std::string> paths;
- for(core::list<ModSpec>::Iterator i = m_mods.begin();
+ for(std::vector<ModSpec>::iterator i = m_mods.begin();
i != m_mods.end(); i++){
const ModSpec &mod = *i;
paths.push_back(mod.path + DIR_DELIM + "textures");
@@ -4115,7 +4219,7 @@ void Server::fillMediaCache()
}
std::string path_all = "textures";
paths.push_back(path_all + DIR_DELIM + "all");
-
+
// Collect media file information from paths into cache
for(std::list<std::string>::iterator i = paths.begin();
i != paths.end(); i++)
@@ -4235,7 +4339,7 @@ void Server::sendMediaAnnouncement(u16 peer_id)
string sha1_digest
}
*/
-
+
writeU16(os, TOCLIENT_ANNOUNCE_MEDIA);
writeU16(os, file_announcements.size());
@@ -4436,7 +4540,7 @@ void Server::sendDetachedInventories(u16 peer_id)
void Server::DiePlayer(u16 peer_id)
{
DSTACK(__FUNCTION_NAME);
-
+
PlayerSAO *playersao = getPlayerSAO(peer_id);
assert(playersao);
@@ -4476,7 +4580,7 @@ void Server::RespawnPlayer(u16 peer_id)
void Server::UpdateCrafting(u16 peer_id)
{
DSTACK(__FUNCTION_NAME);
-
+
Player* player = m_env->getPlayer(peer_id);
assert(player);
@@ -4672,7 +4776,7 @@ bool Server::rollbackRevertActions(const std::list<RollbackAction> &actions,
ServerMap *map = (ServerMap*)(&m_env->getMap());
// Disable rollback report sink while reverting
BoolScopeSet rollback_scope_disable(&m_rollback_sink_enabled, false);
-
+
// Fail if no actions to handle
if(actions.empty()){
log->push_back("Nothing to do.");
@@ -4681,7 +4785,7 @@ bool Server::rollbackRevertActions(const std::list<RollbackAction> &actions,
int num_tried = 0;
int num_failed = 0;
-
+
for(std::list<RollbackAction>::const_iterator
i = actions.begin();
i != actions.end(); i++)
@@ -4704,7 +4808,7 @@ bool Server::rollbackRevertActions(const std::list<RollbackAction> &actions,
log->push_back(os.str());
}
}
-
+
infostream<<"Map::rollbackRevertActions(): "<<num_failed<<"/"<<num_tried
<<" failed"<<std::endl;
@@ -4770,7 +4874,7 @@ IWritableCraftDefManager* Server::getWritableCraftDefManager()
const ModSpec* Server::getModSpec(const std::string &modname)
{
- for(core::list<ModSpec>::Iterator i = m_mods.begin();
+ for(std::vector<ModSpec>::iterator i = m_mods.begin();
i != m_mods.end(); i++){
const ModSpec &mod = *i;
if(mod.name == modname)
@@ -4780,7 +4884,7 @@ const ModSpec* Server::getModSpec(const std::string &modname)
}
void Server::getModNames(core::list<std::string> &modlist)
{
- for(core::list<ModSpec>::Iterator i = m_mods.begin(); i != m_mods.end(); i++)
+ for(std::vector<ModSpec>::iterator i = m_mods.begin(); i != m_mods.end(); i++)
{
modlist.push_back((*i).name);
}
@@ -4795,13 +4899,15 @@ v3f findSpawnPos(ServerMap &map)
//return v3f(50,50,50)*BS;
v3s16 nodepos;
-
+
#if 0
nodepos = v2s16(0,0);
groundheight = 20;
#endif
#if 1
+ s16 water_level = map.m_mgparams->water_level;
+
// Try to find a good place a few times
for(s32 i=0; i<1000; i++)
{
@@ -4813,18 +4919,18 @@ v3f findSpawnPos(ServerMap &map)
// Get ground height at point (fallbacks to heightmap function)
s16 groundheight = map.findGroundLevel(nodepos2d);
// Don't go underwater
- if(groundheight < WATER_LEVEL)
+ if(groundheight <= water_level)
{
//infostream<<"-> Underwater"<<std::endl;
continue;
}
// Don't go to high places
- if(groundheight > WATER_LEVEL + 4)
+ if(groundheight > water_level + 6)
{
//infostream<<"-> Underwater"<<std::endl;
continue;
}
-
+
nodepos = v3s16(nodepos2d.X, groundheight-2, nodepos2d.Y);
bool is_good = false;
s32 air_count = 0;
@@ -4849,7 +4955,7 @@ v3f findSpawnPos(ServerMap &map)
}
}
#endif
-
+
return intToFloat(nodepos, BS);
}
@@ -4922,7 +5028,7 @@ void Server::handlePeerChange(PeerChange &c)
{
JMutexAutoLock envlock(m_env_mutex);
JMutexAutoLock conlock(m_con_mutex);
-
+
if(c.type == PEER_ADDED)
{
/*
@@ -4952,7 +5058,7 @@ void Server::handlePeerChange(PeerChange &c)
n = m_clients.find(c.peer_id);
// The client should exist
assert(n != NULL);
-
+
/*
Mark objects to be not known by the client
*/
@@ -4965,7 +5071,7 @@ void Server::handlePeerChange(PeerChange &c)
// Get object
u16 id = i.getNode()->getKey();
ServerActiveObject* obj = m_env->getActiveObject(id);
-
+
if(obj && obj->m_known_by_count > 0)
obj->m_known_by_count--;
}
@@ -5000,7 +5106,7 @@ void Server::handlePeerChange(PeerChange &c)
message += L" (timed out)";
}
}
-
+
/* Run scripts and remove from environment */
{
if(player != NULL)
@@ -5043,18 +5149,18 @@ void Server::handlePeerChange(PeerChange &c)
<<os.str()<<std::endl;
}
}
-
+
// Delete client
delete m_clients[c.peer_id];
m_clients.remove(c.peer_id);
// Send player info to all remaining clients
//SendPlayerInfos();
-
+
// Send leave chat message to all remaining clients
if(message.length() != 0)
BroadcastChatMessage(message);
-
+
} // PEER_REMOVED
else
{
@@ -5079,7 +5185,7 @@ void Server::handlePeerChanges()
void dedicated_server_loop(Server &server, bool &kill)
{
DSTACK(__FUNCTION_NAME);
-
+
verbosestream<<"dedicated_server_loop()"<<std::endl;
IntervalLimiter m_profiler_interval;
@@ -5098,6 +5204,10 @@ void dedicated_server_loop(Server &server, bool &kill)
if(server.getShutdownRequested() || kill)
{
infostream<<"Dedicated server quitting"<<std::endl;
+#if USE_CURL
+ if(g_settings->getBool("server_announce") == true)
+ ServerList::sendAnnounce("delete");
+#endif
break;
}
diff --git a/src/server.h b/src/server.h
index 43beb167a..22c7cf2bb 100644
--- a/src/server.h
+++ b/src/server.h
@@ -103,14 +103,14 @@ public:
delete q;
}
}
-
+
/*
peer_id=0 adds with nobody to send to
*/
void addBlock(u16 peer_id, v3s16 pos, u8 flags)
{
DSTACK(__FUNCTION_NAME);
-
+
JMutexAutoLock lock(m_mutex);
if(peer_id != 0)
@@ -130,7 +130,7 @@ public:
}
}
}
-
+
/*
Add the block
*/
@@ -160,7 +160,7 @@ public:
JMutexAutoLock lock(m_mutex);
return m_queue.size();
}
-
+
u32 peerItemCount(u16 peer_id)
{
JMutexAutoLock lock(m_mutex);
@@ -238,7 +238,7 @@ struct PlayerInfo
/*
Used for queueing and sorting block transfers in containers
-
+
Lower priority number means higher priority.
*/
struct PrioritySortedBlockTransfer
@@ -303,7 +303,7 @@ struct ServerSoundParams
max_hear_distance(32*BS),
loop(false)
{}
-
+
v3f getPos(ServerEnvironment *env, bool *pos_exists) const;
};
@@ -347,7 +347,7 @@ public:
~RemoteClient()
{
}
-
+
/*
Finds block that should be sent next to the client.
Environment should be locked when this is called.
@@ -367,7 +367,7 @@ public:
{
return m_blocks_sending.size();
}
-
+
// Increments timeouts and removes timed-out blocks from list
// NOTE: This doesn't fix the server-not-sending-block bug
// because it is related to emerging, not sending.
@@ -386,13 +386,13 @@ public:
// Time from last placing or removing blocks
float m_time_from_building;
-
+
/*JMutex m_dig_mutex;
float m_dig_time_remaining;
// -1 = not digging
s16 m_dig_tool_item;
v3s16 m_dig_position;*/
-
+
/*
List of active objects that the client knows of.
Value is dummy.
@@ -405,7 +405,7 @@ private:
- These don't have to be sent again.
- A block is cleared from here when client says it has
deleted it from it's memory
-
+
Key is position, value is dummy.
No MapBlock* is stored here because the blocks can get deleted.
*/
@@ -413,7 +413,7 @@ private:
s16 m_nearest_unsent_d;
v3s16 m_last_center;
float m_nearest_unsent_reset_timer;
-
+
/*
Blocks that are currently on the line.
This is used for throttling the sending of blocks.
@@ -432,7 +432,7 @@ private:
This is resetted by PrintInfo()
*/
u32 m_excess_gotblocks;
-
+
// CPU usage optimization
u32 m_nothing_to_send_counter;
float m_nothing_to_send_pause_timer;
@@ -446,7 +446,7 @@ public:
/*
NOTE: Every public method should be thread-safe
*/
-
+
Server(
const std::string &path_world,
const std::string &path_config,
@@ -477,7 +477,7 @@ public:
{
return m_shutdown_requested;
}
-
+
/*
Shall be called with the environment locked.
This is accessed by the map, which is inside the environment,
@@ -503,7 +503,7 @@ public:
// Envlock + conlock
s32 playSound(const SimpleSoundSpec &spec, const ServerSoundParams &params);
void stopSound(s32 handle);
-
+
// Envlock + conlock
std::set<std::string> getPlayerEffectivePrivs(const std::string &name);
bool checkPriv(const std::string &name, const std::string &priv);
@@ -534,26 +534,32 @@ public:
{
return m_con.GetPeerAddress(peer_id);
}
-
+
// Envlock and conlock should be locked when calling this
void notifyPlayer(const char *name, const std::wstring msg);
void notifyPlayers(const std::wstring msg);
void queueBlockEmerge(v3s16 blockpos, bool allow_generate);
-
+
// Creates or resets inventory
Inventory* createDetachedInventory(const std::string &name);
-
+
// Envlock and conlock should be locked when using Lua
lua_State *getLua(){ return m_lua; }
// Envlock should be locked when using the rollback manager
IRollbackManager *getRollbackManager(){ return m_rollback; }
+
+ //TODO: determine what should be locked when accessing the emerge manager
+ EmergeManager *getEmergeManager(){ return m_emerge; }
+
+ BiomeDefManager *getBiomeDef(){ return m_biomedef; }
+
// actions: time-reversed list
// Return value: success/failure
bool rollbackRevertActions(const std::list<RollbackAction> &actions,
std::list<std::string> *log);
-
+
// IGameDef interface
// Under envlock
virtual IItemDefManager* getItemDefManager();
@@ -565,7 +571,7 @@ public:
virtual ISoundManager* getSoundManager();
virtual MtEventManager* getEventManager();
virtual IRollbackReportSink* getRollbackReportSink();
-
+
IWritableItemDefManager* getWritableItemDefManager();
IWritableNodeDefManager* getWritableNodeDefManager();
IWritableCraftDefManager* getWritableCraftDefManager();
@@ -573,7 +579,7 @@ public:
const ModSpec* getModSpec(const std::string &modname);
void getModNames(core::list<std::string> &modlist);
std::string getBuiltinLuaPath();
-
+
std::string getWorldPath(){ return m_path_world; }
bool isSingleplayer(){ return m_simple_singleplayer_mode; }
@@ -591,11 +597,12 @@ private:
// As of now, these create and remove clients and players.
void peerAdded(con::Peer *peer);
void deletingPeer(con::Peer *peer, bool timeout);
-
+
/*
Static send methods
*/
-
+
+ static void SendMovement(con::Connection &con, u16 peer_id);
static void SendHP(con::Connection &con, u16 peer_id, u8 hp);
static void SendAccessDenied(con::Connection &con, u16 peer_id,
const std::wstring &reason);
@@ -605,7 +612,7 @@ private:
IItemDefManager *itemdef);
static void SendNodeDef(con::Connection &con, u16 peer_id,
INodeDefManager *nodedef, u16 protocol_version);
-
+
/*
Non-static send methods.
Conlock should be always used.
@@ -633,18 +640,18 @@ private:
void sendAddNode(v3s16 p, MapNode n, u16 ignore_id=0,
core::list<u16> *far_players=NULL, float far_d_nodes=100);
void setBlockNotSent(v3s16 p);
-
+
// Environment and Connection must be locked when called
void SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver);
-
+
// Sends blocks to clients (locks env and con on its own)
void SendBlocks(float dtime);
-
+
void fillMediaCache();
void sendMediaAnnouncement(u16 peer_id);
void sendRequestedMedia(u16 peer_id,
const core::list<MediaRequest> &tosend);
-
+
void sendDetachedInventory(const std::string &name, u16 peer_id);
void sendDetachedInventoryToAll(const std::string &name);
void sendDetachedInventories(u16 peer_id);
@@ -652,15 +659,15 @@ private:
/*
Something random
*/
-
+
void DiePlayer(u16 peer_id);
void RespawnPlayer(u16 peer_id);
-
+
void UpdateCrafting(u16 peer_id);
-
+
// When called, connection mutex should be locked
RemoteClient* getClient(u16 peer_id);
-
+
// When called, environment mutex should be locked
std::string getPlayerName(u16 peer_id)
{
@@ -687,7 +694,7 @@ private:
Call with env and con locked.
*/
PlayerSAO *emergePlayer(const char *name, u16 peer_id);
-
+
// Locks environment and connection by its own
struct PeerChange;
void handlePeerChange(PeerChange &c);
@@ -696,7 +703,7 @@ private:
/*
Variables
*/
-
+
// World directory
std::string m_path_world;
// Path to user's configuration file ("" = no configuration file)
@@ -709,27 +716,29 @@ private:
// Thread can set; step() will throw as ServerError
MutexedVariable<std::string> m_async_fatal_error;
-
+
// Some timers
float m_liquid_transform_timer;
float m_print_info_timer;
+ float m_masterserver_timer;
float m_objectdata_timer;
float m_emergethread_trigger_timer;
float m_savemap_timer;
IntervalLimiter m_map_timer_and_unload_interval;
-
+
// NOTE: If connection and environment are both to be locked,
// environment shall be locked first.
// Environment
ServerEnvironment *m_env;
JMutex m_env_mutex;
-
+
// Connection
con::Connection m_con;
JMutex m_con_mutex;
// Connected clients (behind the con mutex)
core::map<u16, RemoteClient*> m_clients;
+ u16 m_clients_number; //for announcing masterserver
// Bann checking
BanManager m_banmanager;
@@ -739,29 +748,35 @@ private:
bool m_rollback_sink_enabled;
bool m_enable_rollback_recording; // Updated once in a while
+ // Emerge manager
+ EmergeManager *m_emerge;
+
+ // Biome Definition Manager
+ BiomeDefManager *m_biomedef;
+
// Scripting
// Envlock and conlock should be locked when using Lua
lua_State *m_lua;
// Item definition manager
IWritableItemDefManager *m_itemdef;
-
+
// Node definition manager
IWritableNodeDefManager *m_nodedef;
-
+
// Craft definition manager
IWritableCraftDefManager *m_craftdef;
-
+
// Event manager
EventManager *m_event;
-
+
// Mods
- core::list<ModSpec> m_mods;
-
+ std::vector<ModSpec> m_mods;
+
/*
Threads
*/
-
+
// A buffer for time steps
// step() increments and AsyncRunStep() run by m_thread reads it.
float m_step_dtime;
@@ -773,7 +788,7 @@ private:
EmergeThread m_emergethread;
// Queue of block coordinates to be processed by the emerge thread
BlockEmergeQueue m_emerge_queue;
-
+
/*
Time related stuff
*/
@@ -782,7 +797,7 @@ private:
float m_time_of_day_send_timer;
// Uptime of server in seconds
MutexedVariable<double> m_uptime;
-
+
/*
Peer change queue.
Queues stuff from peerAdded() and deletingPeer() to
@@ -804,7 +819,7 @@ private:
/*
Random stuff
*/
-
+
// Mod parent directory paths
core::list<std::string> m_modspaths;
diff --git a/src/serverlist.cpp b/src/serverlist.cpp
new file mode 100644
index 000000000..b2f49ae72
--- /dev/null
+++ b/src/serverlist.cpp
@@ -0,0 +1,276 @@
+/*
+Minetest-c55
+Copyright (C) 2011 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 <iostream>
+#include <sstream>
+#include <algorithm>
+
+#include "main.h" // for g_settings
+#include "settings.h"
+#include "serverlist.h"
+#include "filesys.h"
+#include "porting.h"
+#include "log.h"
+#include "json/json.h"
+#if USE_CURL
+#include <curl/curl.h>
+#endif
+
+namespace ServerList
+{
+std::string getFilePath()
+{
+ std::string serverlist_file = g_settings->get("serverlist_file");
+
+ std::string rel_path = std::string("client") + DIR_DELIM
+ + "serverlist" + DIR_DELIM
+ + serverlist_file;
+ std::string path = porting::path_share + DIR_DELIM + rel_path;
+ return path;
+}
+
+std::vector<ServerListSpec> getLocal()
+{
+ std::string path = ServerList::getFilePath();
+ std::string liststring;
+ if(fs::PathExists(path))
+ {
+ std::ifstream istream(path.c_str(), std::ios::binary);
+ if(istream.is_open())
+ {
+ std::ostringstream ostream;
+ ostream << istream.rdbuf();
+ liststring = ostream.str();
+ istream.close();
+ }
+ }
+
+ return ServerList::deSerialize(liststring);
+}
+
+
+#if USE_CURL
+
+static size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp)
+{
+ ((std::string*)userp)->append((char*)contents, size * nmemb);
+ return size * nmemb;
+}
+
+
+std::vector<ServerListSpec> getOnline()
+{
+ std::string liststring;
+ CURL *curl;
+
+ curl = curl_easy_init();
+ if (curl)
+ {
+ CURLcode res;
+
+ curl_easy_setopt(curl, CURLOPT_URL, (g_settings->get("serverlist_url")+"/list").c_str());
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, ServerList::WriteCallback);
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, &liststring);
+
+ res = curl_easy_perform(curl);
+ if (res != CURLE_OK)
+ errorstream<<"Serverlist at url "<<g_settings->get("serverlist_url")<<" not found (internet connection?)"<<std::endl;
+ curl_easy_cleanup(curl);
+ }
+ return ServerList::deSerializeJson(liststring);
+}
+
+#endif
+
+/*
+ Delete a server fromt he local favorites list
+*/
+bool deleteEntry (ServerListSpec server)
+{
+ std::vector<ServerListSpec> serverlist = ServerList::getLocal();
+ for(unsigned i = 0; i < serverlist.size(); i++)
+ {
+ if (serverlist[i]["address"] == server["address"]
+ && serverlist[i]["port"] == server["port"])
+ {
+ serverlist.erase(serverlist.begin() + i);
+ }
+ }
+
+ std::string path = ServerList::getFilePath();
+ std::ofstream stream (path.c_str());
+ if (stream.is_open())
+ {
+ stream<<ServerList::serialize(serverlist);
+ return true;
+ }
+ return false;
+}
+
+/*
+ Insert a server to the local favorites list
+*/
+bool insert (ServerListSpec server)
+{
+ // Remove duplicates
+ ServerList::deleteEntry(server);
+
+ std::vector<ServerListSpec> serverlist = ServerList::getLocal();
+
+ // Insert new server at the top of the list
+ serverlist.insert(serverlist.begin(), server);
+
+ std::string path = ServerList::getFilePath();
+ std::ofstream stream (path.c_str());
+ if (stream.is_open())
+ {
+ stream<<ServerList::serialize(serverlist);
+ }
+
+ return false;
+}
+
+std::vector<ServerListSpec> deSerialize(std::string liststring)
+{
+ std::vector<ServerListSpec> serverlist;
+ std::istringstream stream(liststring);
+ std::string line, tmp;
+ while (std::getline(stream, line))
+ {
+ std::transform(line.begin(), line.end(),line.begin(), ::toupper);
+ if (line == "[SERVER]")
+ {
+ ServerListSpec thisserver;
+ std::getline(stream, tmp);
+ thisserver["name"] = tmp;
+ std::getline(stream, tmp);
+ thisserver["address"] = tmp;
+ std::getline(stream, tmp);
+ thisserver["port"] = tmp;
+ std::getline(stream, tmp);
+ thisserver["description"] = tmp;
+ serverlist.push_back(thisserver);
+ }
+ }
+ return serverlist;
+}
+
+std::string serialize(std::vector<ServerListSpec> serverlist)
+{
+ std::string liststring;
+ for(std::vector<ServerListSpec>::iterator i = serverlist.begin(); i != serverlist.end(); i++)
+ {
+ liststring += "[server]\n";
+ liststring += (*i)["name"].asString() + "\n";
+ liststring += (*i)["address"].asString() + "\n";
+ liststring += (*i)["port"].asString() + "\n";
+ liststring += (*i)["description"].asString() + "\n";
+ liststring += "\n";
+ }
+ return liststring;
+}
+
+std::vector<ServerListSpec> deSerializeJson(std::string liststring)
+{
+ std::vector<ServerListSpec> serverlist;
+ Json::Value root;
+ Json::Reader reader;
+ std::istringstream stream(liststring);
+ if (!liststring.size()) {
+ return serverlist;
+ }
+ if (!reader.parse( stream, root ) )
+ {
+ errorstream << "Failed to parse server list " << reader.getFormattedErrorMessages();
+ return serverlist;
+ }
+ if (root["list"].isArray())
+ for (unsigned int i = 0; i < root["list"].size(); i++)
+ {
+ if (root["list"][i].isObject()) {
+ serverlist.push_back(root["list"][i]);
+ }
+ }
+ return serverlist;
+}
+
+std::string serializeJson(std::vector<ServerListSpec> serverlist)
+{
+ Json::Value root;
+ Json::Value list(Json::arrayValue);
+ for(std::vector<ServerListSpec>::iterator i = serverlist.begin(); i != serverlist.end(); i++)
+ {
+ list.append(*i);
+ }
+ root["list"] = list;
+ Json::StyledWriter writer;
+ return writer.write( root );
+}
+
+
+#if USE_CURL
+static size_t ServerAnnounceCallback(void *contents, size_t size, size_t nmemb, void *userp)
+{
+ return 0;
+ //((std::string*)userp)->append((char*)contents, size * nmemb);
+ //return size * nmemb;
+}
+void sendAnnounce(std::string action, u16 clients) {
+ Json::Value server;
+ if (action.size())
+ server["action"] = action;
+ server["port"] = g_settings->get("port");
+ if (action != "del") {
+ server["name"] = g_settings->get("server_name");
+ server["description"] = g_settings->get("server_description");
+ server["address"] = g_settings->get("server_address");
+ server["version"] = VERSION_STRING;
+ server["url"] = g_settings->get("server_url");
+ server["creative"] = g_settings->get("creative_mode");
+ server["damage"] = g_settings->get("enable_damage");
+ server["dedicated"] = g_settings->get("server_dedicated");
+ server["password"] = g_settings->getBool("disallow_empty_password");
+ server["pvp"] = g_settings->getBool("enable_pvp");
+ server["clients"] = clients;
+ server["clients_max"] = g_settings->get("max_users");
+ }
+ if(server["action"] == "start")
+ actionstream << "announcing to " << g_settings->get("serverlist_url") << std::endl;
+ Json::StyledWriter writer;
+ CURL *curl;
+ curl = curl_easy_init();
+ if (curl)
+ {
+ CURLcode res;
+ curl_easy_setopt(curl, CURLOPT_URL, (g_settings->get("serverlist_url")+std::string("/announce?json=")+curl_easy_escape(curl, writer.write( server ).c_str(), 0)).c_str());
+ //curl_easy_setopt(curl, CURLOPT_USERAGENT, "minetest");
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, ServerList::ServerAnnounceCallback);
+ //curl_easy_setopt(curl, CURLOPT_WRITEDATA, &liststring);
+ curl_easy_setopt(curl, CURLOPT_TIMEOUT, 1);
+ curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 1);
+ res = curl_easy_perform(curl);
+ //if (res != CURLE_OK)
+ // errorstream<<"Serverlist at url "<<g_settings->get("serverlist_url")<<" not found (internet connection?)"<<std::endl;
+ curl_easy_cleanup(curl);
+ }
+
+}
+#endif
+
+} //namespace ServerList
diff --git a/src/serverlist.h b/src/serverlist.h
new file mode 100644
index 000000000..52549e97a
--- /dev/null
+++ b/src/serverlist.h
@@ -0,0 +1,46 @@
+/*
+Minetest-c55
+Copyright (C) 2011 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 <iostream>
+#include "config.h"
+#include "json/json.h"
+
+#ifndef SERVERLIST_HEADER
+#define SERVERLIST_HEADER
+
+typedef Json::Value ServerListSpec;
+
+namespace ServerList
+{
+ std::vector<ServerListSpec> getLocal();
+ #if USE_CURL
+ std::vector<ServerListSpec> getOnline();
+ #endif
+ bool deleteEntry(ServerListSpec server);
+ bool insert(ServerListSpec server);
+ std::vector<ServerListSpec> deSerialize(std::string liststring);
+ std::string serialize(std::vector<ServerListSpec>);
+ std::vector<ServerListSpec> deSerializeJson(std::string liststring);
+ std::string serializeJson(std::vector<ServerListSpec>);
+ #if USE_CURL
+ void sendAnnounce(std::string action = "", u16 clients = 0);
+ #endif
+} //ServerList namespace
+
+#endif
diff --git a/src/settings.h b/src/settings.h
index 1ab6fcc6b..addd9980c 100644
--- a/src/settings.h
+++ b/src/settings.h
@@ -32,6 +32,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "debug.h"
#include "log.h"
#include "util/string.h"
+#include "porting.h"
enum ValueType
{
@@ -61,7 +62,7 @@ public:
void writeLines(std::ostream &os)
{
JMutexAutoLock lock(m_mutex);
-
+
for(core::map<std::string, std::string>::Iterator
i = m_settings.getIterator();
i.atEnd() == false; i++)
@@ -71,13 +72,33 @@ public:
os<<name<<" = "<<value<<"\n";
}
}
+
+ // return all keys used
+ std::vector<std::string> getNames(){
+ std::vector<std::string> names;
+ for(core::map<std::string, std::string>::Iterator
+ i = m_settings.getIterator();
+ i.atEnd() == false; i++)
+ {
+ std::string name = i.getNode()->getKey();
+ names.push_back(name);
+ }
+ return names;
+ }
+
+ // remove a setting
+ bool remove(const std::string& name)
+ {
+ return m_settings.remove(name);
+ }
+
bool parseConfigLine(const std::string &line)
{
JMutexAutoLock lock(m_mutex);
-
+
std::string trimmedline = trim(line);
-
+
// Ignore empty lines and comments
if(trimmedline.size() == 0 || trimmedline[0] == '#')
return true;
@@ -91,15 +112,15 @@ public:
if(name == "")
return true;
-
+
std::string value = sf.next("\n");
value = trim(value);
/*infostream<<"Config name=\""<<name<<"\" value=\""
<<value<<"\""<<std::endl;*/
-
+
m_settings[name] = value;
-
+
return true;
}
@@ -124,7 +145,7 @@ public:
{
if(is.eof())
return false;
-
+
/*
NOTE: This function might be expanded to allow multi-line
settings.
@@ -149,16 +170,16 @@ public:
/*infostream<<"Parsing configuration file: \""
<<filename<<"\""<<std::endl;*/
-
+
while(parseConfigObject(is));
-
+
return true;
}
/*
Reads a configuration object from stream (usually a single line)
and adds it to dst.
-
+
Preserves comments and empty lines.
Settings that were added to dst are also added to updated.
@@ -172,10 +193,10 @@ public:
bool &value_changed)
{
JMutexAutoLock lock(m_mutex);
-
+
if(is.eof())
return false;
-
+
// NOTE: This function will be expanded to allow multi-line settings
std::string line;
std::getline(is, line);
@@ -185,7 +206,7 @@ public:
std::string line_end = "";
if(is.eof() == false)
line_end = "\n";
-
+
// Ignore empty lines and comments
if(trimmedline.size() == 0 || trimmedline[0] == '#')
{
@@ -203,14 +224,14 @@ public:
dst.push_back(line+line_end);
return true;
}
-
+
std::string value = sf.next("\n");
value = trim(value);
-
+
if(m_settings.find(name))
{
std::string newvalue = m_settings[name];
-
+
if(newvalue != value)
{
infostream<<"Changing value of \""<<name<<"\" = \""
@@ -223,7 +244,9 @@ public:
updated[name] = true;
}
-
+ else //file contains a setting which is not in m_settings
+ value_changed=true;
+
return true;
}
@@ -236,11 +259,11 @@ public:
{
infostream<<"Updating configuration file: \""
<<filename<<"\""<<std::endl;
-
+
core::list<std::string> objects;
core::map<std::string, bool> updated;
bool something_actually_changed = false;
-
+
// Read and modify stuff
{
std::ifstream is(filename);
@@ -257,9 +280,9 @@ public:
something_actually_changed));
}
}
-
+
JMutexAutoLock lock(m_mutex);
-
+
// If something not yet determined to have been changed, check if
// any new stuff was added
if(!something_actually_changed){
@@ -273,14 +296,14 @@ public:
break;
}
}
-
+
// If nothing was actually changed, skip writing the file
if(!something_actually_changed){
infostream<<"Skipping writing of "<<filename
<<" because content wouldn't be modified"<<std::endl;
return true;
}
-
+
// Write stuff back
{
std::ofstream os(filename);
@@ -291,7 +314,7 @@ public:
<<filename<<"\""<<std::endl;
return false;
}
-
+
/*
Write updated stuff
*/
@@ -318,7 +341,7 @@ public:
os<<name<<" = "<<value<<"\n";
}
}
-
+
return true;
}
@@ -368,7 +391,7 @@ public:
ValueType type = n->getValue().type;
std::string value = "";
-
+
if(type == VALUETYPE_FLAG)
{
value = "true";
@@ -384,7 +407,7 @@ public:
value = argv[i];
i++;
}
-
+
infostream<<"Valid command-line parameter: \""
<<name<<"\" = \""<<value<<"\""
@@ -398,7 +421,7 @@ public:
void set(std::string name, std::string value)
{
JMutexAutoLock lock(m_mutex);
-
+
m_settings[name] = value;
}
@@ -413,21 +436,21 @@ public:
void setDefault(std::string name, std::string value)
{
JMutexAutoLock lock(m_mutex);
-
+
m_defaults[name] = value;
}
bool exists(std::string name)
{
JMutexAutoLock lock(m_mutex);
-
+
return (m_settings.find(name) || m_defaults.find(name));
}
std::string get(std::string name)
{
JMutexAutoLock lock(m_mutex);
-
+
core::map<std::string, std::string>::Node *n;
n = m_settings.find(name);
if(n == NULL)
@@ -446,7 +469,7 @@ public:
{
return is_yes(get(name));
}
-
+
bool getFlag(std::string name)
{
try
@@ -465,7 +488,7 @@ public:
// If it is in settings
if(exists(name))
return getBool(name);
-
+
std::string s;
char templine[10];
std::cout<<question<<" [y/N]: ";
@@ -493,7 +516,7 @@ public:
// If it is in settings
if(exists(name))
return getU16(name);
-
+
std::string s;
char templine[10];
std::cout<<question<<" ["<<def<<"]: ";
@@ -546,6 +569,280 @@ public:
return value;
}
+ u32 getFlagStr(std::string name, FlagDesc *flagdesc)
+ {
+ std::string val = get(name);
+ return (isdigit(val[0])) ? stoi(val) : readFlagString(val, flagdesc);
+ }
+
+ template <class T> T *getStruct(std::string name, std::string format)
+ {
+ size_t len = sizeof(T);
+ std::vector<std::string *> strs_alloced;
+ std::string *str;
+ std::string valstr = get(name);
+ char *s = &valstr[0];
+ T *buf = new T;
+ char *bufpos = (char *)buf;
+ char *f, *snext;
+ size_t pos;
+
+ char *fmtpos, *fmt = &format[0];
+ while ((f = strtok_r(fmt, ",", &fmtpos)) && s) {
+ fmt = NULL;
+
+ bool is_unsigned = false;
+ int width = 0;
+ char valtype = *f;
+
+ width = (int)strtol(f + 1, &f, 10);
+ if (width && valtype == 's')
+ valtype = 'i';
+
+ switch (valtype) {
+ case 'u':
+ is_unsigned = true;
+ /* FALLTHROUGH */
+ case 'i':
+ if (width == 16) {
+ bufpos += PADDING(bufpos, u16);
+ if ((bufpos - (char *)buf) + sizeof(u16) <= len) {
+ if (is_unsigned)
+ *(u16 *)bufpos = (u16)strtoul(s, &s, 10);
+ else
+ *(s16 *)bufpos = (s16)strtol(s, &s, 10);
+ }
+ bufpos += sizeof(u16);
+ } else if (width == 32) {
+ bufpos += PADDING(bufpos, u32);
+ if ((bufpos - (char *)buf) + sizeof(u32) <= len) {
+ if (is_unsigned)
+ *(u32 *)bufpos = (u32)strtoul(s, &s, 10);
+ else
+ *(s32 *)bufpos = (s32)strtol(s, &s, 10);
+ }
+ bufpos += sizeof(u32);
+ } else if (width == 64) {
+ bufpos += PADDING(bufpos, u64);
+ if ((bufpos - (char *)buf) + sizeof(u64) <= len) {
+ if (is_unsigned)
+ *(u64 *)bufpos = (u64)strtoull(s, &s, 10);
+ else
+ *(s64 *)bufpos = (s64)strtoll(s, &s, 10);
+ }
+ bufpos += sizeof(u64);
+ }
+ s = strchr(s, ',');
+ break;
+ case 'b':
+ snext = strchr(s, ',');
+ if (snext)
+ *snext++ = 0;
+
+ bufpos += PADDING(bufpos, bool);
+ if ((bufpos - (char *)buf) + sizeof(bool) <= len)
+ *(bool *)bufpos = is_yes(std::string(s));
+ bufpos += sizeof(bool);
+
+ s = snext;
+ break;
+ case 'f':
+ bufpos += PADDING(bufpos, float);
+ if ((bufpos - (char *)buf) + sizeof(float) <= len)
+ *(float *)bufpos = strtof(s, &s);
+ bufpos += sizeof(float);
+
+ s = strchr(s, ',');
+ break;
+ case 's':
+ while (*s == ' ' || *s == '\t')
+ s++;
+ if (*s++ != '"') //error, expected string
+ goto fail;
+ snext = s;
+
+ while (snext[0] && !(snext[-1] != '\\' && snext[0] == '"'))
+ snext++;
+ *snext++ = 0;
+
+ bufpos += PADDING(bufpos, std::string *);
+
+ str = new std::string(s);
+ pos = 0;
+ while ((pos = str->find("\\\"", pos)) != std::string::npos)
+ str->erase(pos, 1);
+
+ if ((bufpos - (char *)buf) + sizeof(std::string *) <= len)
+ *(std::string **)bufpos = str;
+ bufpos += sizeof(std::string *);
+ strs_alloced.push_back(str);
+
+ s = *snext ? snext + 1 : NULL;
+ break;
+ case 'v':
+ while (*s == ' ' || *s == '\t')
+ s++;
+ if (*s++ != '(') //error, expected vector
+ goto fail;
+
+ if (width == 2) {
+ bufpos += PADDING(bufpos, v2f);
+
+ if ((bufpos - (char *)buf) + sizeof(v2f) <= len) {
+ v2f *v = (v2f *)bufpos;
+ v->X = strtof(s, &s);
+ s++;
+ v->Y = strtof(s, &s);
+ }
+
+ bufpos += sizeof(v2f);
+ } else if (width == 3) {
+ bufpos += PADDING(bufpos, v3f);
+ if ((bufpos - (char *)buf) + sizeof(v3f) <= len) {
+ v3f *v = (v3f *)bufpos;
+ v->X = strtof(s, &s);
+ s++;
+ v->Y = strtof(s, &s);
+ s++;
+ v->Z = strtof(s, &s);
+ }
+
+ bufpos += sizeof(v3f);
+ }
+ s = strchr(s, ',');
+ break;
+ default: //error, invalid format specifier
+ goto fail;
+ }
+
+ if (s && *s == ',')
+ s++;
+
+ if ((size_t)(bufpos - (char *)buf) > len) //error, buffer too small
+ goto fail;
+ }
+
+ if (f && *f) { //error, mismatched number of fields and values
+fail:
+ for (unsigned int i = 0; i != strs_alloced.size(); i++)
+ delete strs_alloced[i];
+ delete buf;
+ //delete[] buf;
+ buf = NULL;
+ }
+
+ return buf;
+ }
+
+ bool setStruct(std::string name, std::string format, void *value)
+ {
+ char sbuf[2048];
+ int sbuflen = sizeof(sbuf) - 1;
+ sbuf[sbuflen] = 0;
+ std::string str;
+ int pos = 0;
+ size_t fpos;
+ char *f;
+
+ char *bufpos = (char *)value;
+ char *fmtpos, *fmt = &format[0];
+ while ((f = strtok_r(fmt, ",", &fmtpos))) {
+ fmt = NULL;
+ bool is_unsigned = false;
+ int width = 0, nprinted = 0;
+ char valtype = *f;
+
+ width = (int)strtol(f + 1, &f, 10);
+ if (width && valtype == 's')
+ valtype = 'i';
+
+ switch (valtype) {
+ case 'u':
+ is_unsigned = true;
+ /* FALLTHROUGH */
+ case 'i':
+ if (width == 16) {
+ bufpos += PADDING(bufpos, u16);
+ nprinted = snprintf(sbuf + pos, sbuflen,
+ is_unsigned ? "%u, " : "%d, ",
+ *((u16 *)bufpos));
+ bufpos += sizeof(u16);
+ } else if (width == 32) {
+ bufpos += PADDING(bufpos, u32);
+ nprinted = snprintf(sbuf + pos, sbuflen,
+ is_unsigned ? "%u, " : "%d, ",
+ *((u32 *)bufpos));
+ bufpos += sizeof(u32);
+ } else if (width == 64) {
+ bufpos += PADDING(bufpos, u64);
+ nprinted = snprintf(sbuf + pos, sbuflen,
+ is_unsigned ? "%llu, " : "%lli, ",
+ (unsigned long long)*((u64 *)bufpos));
+ bufpos += sizeof(u64);
+ }
+ break;
+ case 'b':
+ bufpos += PADDING(bufpos, bool);
+ nprinted = snprintf(sbuf + pos, sbuflen, "%s, ",
+ *((bool *)bufpos) ? "true" : "false");
+ bufpos += sizeof(bool);
+ break;
+ case 'f':
+ bufpos += PADDING(bufpos, float);
+ nprinted = snprintf(sbuf + pos, sbuflen, "%f, ",
+ *((float *)bufpos));
+ bufpos += sizeof(float);
+ break;
+ case 's':
+ bufpos += PADDING(bufpos, std::string *);
+ str = **((std::string **)bufpos);
+
+ fpos = 0;
+ while ((fpos = str.find('"', fpos)) != std::string::npos) {
+ str.insert(fpos, 1, '\\');
+ fpos += 2;
+ }
+
+ nprinted = snprintf(sbuf + pos, sbuflen, "\"%s\", ",
+ (*((std::string **)bufpos))->c_str());
+ bufpos += sizeof(std::string *);
+ break;
+ case 'v':
+ if (width == 2) {
+ bufpos += PADDING(bufpos, v2f);
+ v2f *v = (v2f *)bufpos;
+ nprinted = snprintf(sbuf + pos, sbuflen,
+ "(%f, %f), ", v->X, v->Y);
+ bufpos += sizeof(v2f);
+ } else {
+ bufpos += PADDING(bufpos, v3f);
+ v3f *v = (v3f *)bufpos;
+ nprinted = snprintf(sbuf + pos, sbuflen,
+ "(%f, %f, %f), ", v->X, v->Y, v->Z);
+ bufpos += sizeof(v3f);
+ }
+ break;
+ default:
+ return false;
+ }
+ if (nprinted < 0) //error, buffer too small
+ return false;
+ pos += nprinted;
+ sbuflen -= nprinted;
+ }
+
+ if (pos >= 2)
+ sbuf[pos - 2] = 0;
+
+ set(name, std::string(sbuf));
+ return true;
+ }
+
+ void setFlagStr(std::string name, u32 flags, FlagDesc *flagdesc)
+ {
+ set(name, writeFlagString(flags, flagdesc));
+ }
+
void setBool(std::string name, bool value)
{
if(value)
@@ -554,11 +851,6 @@ public:
set(name, "false");
}
- void setS32(std::string name, s32 value)
- {
- set(name, itos(value));
- }
-
void setFloat(std::string name, float value)
{
set(name, ftos(value));
@@ -578,6 +870,16 @@ public:
set(name, os.str());
}
+ void setS16(std::string name, s16 value)
+ {
+ set(name, itos(value));
+ }
+
+ void setS32(std::string name, s32 value)
+ {
+ set(name, itos(value));
+ }
+
void setU64(std::string name, u64 value)
{
std::ostringstream os;
@@ -588,7 +890,7 @@ public:
void clear()
{
JMutexAutoLock lock(m_mutex);
-
+
m_settings.clear();
m_defaults.clear();
}
@@ -596,7 +898,7 @@ public:
void updateValue(Settings &other, const std::string &name)
{
JMutexAutoLock lock(m_mutex);
-
+
if(&other == this)
return;
@@ -613,7 +915,7 @@ public:
{
JMutexAutoLock lock(m_mutex);
JMutexAutoLock lock2(other.m_mutex);
-
+
if(&other == this)
return;
@@ -623,7 +925,7 @@ public:
{
m_settings[i.getNode()->getKey()] = i.getNode()->getValue();
}
-
+
for(core::map<std::string, std::string>::Iterator
i = other.m_defaults.getIterator();
i.atEnd() == false; i++)
@@ -638,7 +940,7 @@ public:
{
JMutexAutoLock lock(m_mutex);
JMutexAutoLock lock2(other.m_mutex);
-
+
if(&other == this)
return *this;
@@ -649,7 +951,7 @@ public:
m_settings.insert(i.getNode()->getKey(),
i.getNode()->getValue());
}
-
+
for(core::map<std::string, std::string>::Iterator
i = other.m_defaults.getIterator();
i.atEnd() == false; i++)
@@ -666,13 +968,13 @@ public:
{
JMutexAutoLock lock(m_mutex);
JMutexAutoLock lock2(other.m_mutex);
-
+
if(&other == this)
return *this;
clear();
(*this) += other;
-
+
return *this;
}
diff --git a/src/subgame.cpp b/src/subgame.cpp
index c36189933..2f5340373 100644
--- a/src/subgame.cpp
+++ b/src/subgame.cpp
@@ -74,9 +74,9 @@ SubgameSpec findSubgame(const std::string &id)
}
if(game_path == "")
return SubgameSpec();
+ std::string gamemod_path = game_path + DIR_DELIM + "mods";
// Find mod directories
std::set<std::string> mods_paths;
- mods_paths.insert(game_path + DIR_DELIM + "mods");
if(!user_game)
mods_paths.insert(share + DIR_DELIM + "mods" + DIR_DELIM + id);
if(user != share || user_game)
@@ -84,7 +84,7 @@ SubgameSpec findSubgame(const std::string &id)
std::string game_name = getGameName(game_path);
if(game_name == "")
game_name = id;
- return SubgameSpec(id, game_path, mods_paths, game_name);
+ return SubgameSpec(id, game_path, gamemod_path, mods_paths, game_name);
}
SubgameSpec findWorldSubgame(const std::string &world_path)
@@ -96,7 +96,7 @@ SubgameSpec findWorldSubgame(const std::string &world_path)
SubgameSpec gamespec;
gamespec.id = world_gameid;
gamespec.path = world_gamepath;
- gamespec.mods_paths.insert(world_gamepath + DIR_DELIM + "mods");
+ gamespec.gamemods_path= world_gamepath + DIR_DELIM + "mods";
gamespec.name = getGameName(world_gamepath);
if(gamespec.name == "")
gamespec.name = "unknown";
diff --git a/src/subgame.h b/src/subgame.h
index bffa86e28..dd725caf7 100644
--- a/src/subgame.h
+++ b/src/subgame.h
@@ -29,17 +29,20 @@ with this program; if not, write to the Free Software Foundation, Inc.,
struct SubgameSpec
{
std::string id; // "" = game does not exist
- std::string path;
- std::set<std::string> mods_paths;
+ std::string path; // path to game
+ std::string gamemods_path; //path to mods of the game
+ std::set<std::string> addon_mods_paths; //paths to addon mods for this game
std::string name;
SubgameSpec(const std::string &id_="",
- const std::string &path_="",
- const std::set<std::string> &mods_paths_=std::set<std::string>(),
+ const std::string &path_="",
+ const std::string &gamemods_path_="",
+ const std::set<std::string> &addon_mods_paths_=std::set<std::string>(),
const std::string &name_=""):
id(id_),
path(path_),
- mods_paths(mods_paths_),
+ gamemods_path(gamemods_path_),
+ addon_mods_paths(addon_mods_paths_),
name(name_)
{}
diff --git a/src/treegen.cpp b/src/treegen.cpp
index 49f0666bc..7530843d3 100644
--- a/src/treegen.cpp
+++ b/src/treegen.cpp
@@ -1,7 +1,7 @@
/*
Minetest-c55
Copyright (C) 2010-2012 celeron55, Perttu Ahola <celeron55@gmail.com>,
- 2012 RealBadAngel, Maciej Kasatkin <mk@realbadangel.pl>
+ 2012-2013 RealBadAngel, Maciej Kasatkin <mk@realbadangel.pl>
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
@@ -118,7 +118,7 @@ void spawn_ltree (ServerEnvironment *env, v3s16 p0, INodeDefManager *ndef, TreeD
core::map<v3s16, MapBlock*> modified_blocks;
ManualMapVoxelManipulator vmanip(map);
v3s16 tree_blockp = getNodeBlockPos(p0);
- vmanip.initialEmerge(tree_blockp - v3s16(1,1,1), tree_blockp + v3s16(1,2,1));
+ vmanip.initialEmerge(tree_blockp - v3s16(1,1,1), tree_blockp + v3s16(1,3,1));
make_ltree (vmanip, p0, ndef, tree_definition);
vmanip.blitBackAll(&modified_blocks);
@@ -169,8 +169,11 @@ void make_ltree(ManualMapVoxelManipulator &vmanip, v3s16 p0, INodeDefManager *nd
//initialize rotation matrix, position and stacks for branches
core::matrix4 rotation;
- rotation=setRotationAxisRadians(rotation, M_PI/2,v3f(0,0,1));
- v3f position = v3f(0,0,0);
+ rotation = setRotationAxisRadians(rotation, M_PI/2,v3f(0,0,1));
+ v3f position;
+ position.X = p0.X;
+ position.Y = p0.Y;
+ position.Z = p0.Z;
std::stack <core::matrix4> stack_orientation;
std::stack <v3f> stack_position;
@@ -223,16 +226,16 @@ void make_ltree(ManualMapVoxelManipulator &vmanip, v3s16 p0, INodeDefManager *nd
//make sure tree is not floating in the air
if (tree_definition.trunk_type == "double")
{
- make_tree_node_placement(vmanip,v3f(p0.X+position.X+1,p0.Y+position.Y-1,p0.Z+position.Z),dirtnode);
- make_tree_node_placement(vmanip,v3f(p0.X+position.X,p0.Y+position.Y-1,p0.Z+position.Z+1),dirtnode);
- make_tree_node_placement(vmanip,v3f(p0.X+position.X+1,p0.Y+position.Y-1,p0.Z+position.Z+1),dirtnode);
+ tree_node_placement(vmanip,v3f(position.X+1,position.Y-1,position.Z),dirtnode);
+ tree_node_placement(vmanip,v3f(position.X,position.Y-1,position.Z+1),dirtnode);
+ tree_node_placement(vmanip,v3f(position.X+1,position.Y-1,position.Z+1),dirtnode);
}
if (tree_definition.trunk_type == "crossed")
{
- make_tree_node_placement(vmanip,v3f(p0.X+position.X+1,p0.Y+position.Y-1,p0.Z+position.Z),dirtnode);
- make_tree_node_placement(vmanip,v3f(p0.X+position.X-1,p0.Y+position.Y-1,p0.Z+position.Z),dirtnode);
- make_tree_node_placement(vmanip,v3f(p0.X+position.X,p0.Y+position.Y-1,p0.Z+position.Z+1),dirtnode);
- make_tree_node_placement(vmanip,v3f(p0.X+position.X,p0.Y+position.Y-1,p0.Z+position.Z-1),dirtnode);
+ tree_node_placement(vmanip,v3f(position.X+1,position.Y-1,position.Z),dirtnode);
+ tree_node_placement(vmanip,v3f(position.X-1,position.Y-1,position.Z),dirtnode);
+ tree_node_placement(vmanip,v3f(position.X,position.Y-1,position.Z+1),dirtnode);
+ tree_node_placement(vmanip,v3f(position.X,position.Y-1,position.Z-1),dirtnode);
}
/* build tree out of generated axiom
@@ -241,7 +244,9 @@ void make_ltree(ManualMapVoxelManipulator &vmanip, v3s16 p0, INodeDefManager *nd
G - move forward one unit with the pen up
F - move forward one unit with the pen down drawing trunks and branches
- f - move forward one unit with the pen down drawing leaves
+ f - move forward one unit with the pen down drawing leaves (100% chance)
+ T - move forward one unit with the pen down drawing trunks only
+ R - move forward one unit with the pen down placing fruit
A - replace with rules set A
B - replace with rules set B
C - replace with rules set C
@@ -275,35 +280,54 @@ void make_ltree(ManualMapVoxelManipulator &vmanip, v3s16 p0, INodeDefManager *nd
dir = transposeMatrix(rotation,dir);
position+=dir;
break;
+ case 'T':
+ tree_trunk_placement(vmanip,v3f(position.X,position.Y,position.Z),tree_definition);
+ if (tree_definition.trunk_type == "double" && !tree_definition.thin_branches)
+ {
+ tree_trunk_placement(vmanip,v3f(position.X+1,position.Y,position.Z),tree_definition);
+ tree_trunk_placement(vmanip,v3f(position.X,position.Y,position.Z+1),tree_definition);
+ tree_trunk_placement(vmanip,v3f(position.X+1,position.Y,position.Z+1),tree_definition);
+ }
+ if (tree_definition.trunk_type == "crossed" && !tree_definition.thin_branches)
+ {
+ tree_trunk_placement(vmanip,v3f(position.X+1,position.Y,position.Z),tree_definition);
+ tree_trunk_placement(vmanip,v3f(position.X-1,position.Y,position.Z),tree_definition);
+ tree_trunk_placement(vmanip,v3f(position.X,position.Y,position.Z+1),tree_definition);
+ tree_trunk_placement(vmanip,v3f(position.X,position.Y,position.Z-1),tree_definition);
+ }
+ dir = v3f(1,0,0);
+ dir = transposeMatrix(rotation,dir);
+ position+=dir;
+ break;
case 'F':
- make_tree_trunk_placement(vmanip,v3f(p0.X+position.X,p0.Y+position.Y,p0.Z+position.Z),tree_definition);
+ tree_trunk_placement(vmanip,v3f(position.X,position.Y,position.Z),tree_definition);
if ((stack_orientation.empty() && tree_definition.trunk_type == "double") ||
(!stack_orientation.empty() && tree_definition.trunk_type == "double" && !tree_definition.thin_branches))
{
- make_tree_trunk_placement(vmanip,v3f(p0.X+position.X+1,p0.Y+position.Y,p0.Z+position.Z),tree_definition);
- make_tree_trunk_placement(vmanip,v3f(p0.X+position.X,p0.Y+position.Y,p0.Z+position.Z+1),tree_definition);
- make_tree_trunk_placement(vmanip,v3f(p0.X+position.X+1,p0.Y+position.Y,p0.Z+position.Z+1),tree_definition);
+ tree_trunk_placement(vmanip,v3f(position.X+1,position.Y,position.Z),tree_definition);
+ tree_trunk_placement(vmanip,v3f(position.X,position.Y,position.Z+1),tree_definition);
+ tree_trunk_placement(vmanip,v3f(position.X+1,position.Y,position.Z+1),tree_definition);
}
if ((stack_orientation.empty() && tree_definition.trunk_type == "crossed") ||
(!stack_orientation.empty() && tree_definition.trunk_type == "crossed" && !tree_definition.thin_branches))
{
- make_tree_trunk_placement(vmanip,v3f(p0.X+position.X+1,p0.Y+position.Y,p0.Z+position.Z),tree_definition);
- make_tree_trunk_placement(vmanip,v3f(p0.X+position.X-1,p0.Y+position.Y,p0.Z+position.Z),tree_definition);
- make_tree_trunk_placement(vmanip,v3f(p0.X+position.X,p0.Y+position.Y,p0.Z+position.Z+1),tree_definition);
- make_tree_trunk_placement(vmanip,v3f(p0.X+position.X,p0.Y+position.Y,p0.Z+position.Z-1),tree_definition);
+ tree_trunk_placement(vmanip,v3f(position.X+1,position.Y,position.Z),tree_definition);
+ tree_trunk_placement(vmanip,v3f(position.X-1,position.Y,position.Z),tree_definition);
+ tree_trunk_placement(vmanip,v3f(position.X,position.Y,position.Z+1),tree_definition);
+ tree_trunk_placement(vmanip,v3f(position.X,position.Y,position.Z-1),tree_definition);
}
if (stack_orientation.empty() == false)
{
s16 size = 1;
- for(x=-size; x<size+1; x++)
- for(y=-size; y<size+1; y++)
- for(z=-size; z<size+1; z++)
+ for(x=-size; x<=size; x++)
+ for(y=-size; y<=size; y++)
+ for(z=-size; z<=size; z++)
if (abs(x) == size && abs(y) == size && abs(z) == size)
{
- make_tree_leaves_placement(vmanip,v3f(p0.X+position.X+x+1,p0.Y+position.Y+y,p0.Z+position.Z+z),tree_definition);
- make_tree_leaves_placement(vmanip,v3f(p0.X+position.X+x-1,p0.Y+position.Y+y,p0.Z+position.Z+z),tree_definition);
- make_tree_leaves_placement(vmanip,v3f(p0.X+position.X+x,p0.Y+position.Y+y,p0.Z+position.Z+z+1),tree_definition);
- make_tree_leaves_placement(vmanip,v3f(p0.X+position.X+x,p0.Y+position.Y+y,p0.Z+position.Z+z-1),tree_definition);
+ tree_leaves_placement(vmanip,v3f(position.X+x+1,position.Y+y,position.Z+z),tree_definition);
+ tree_leaves_placement(vmanip,v3f(position.X+x-1,position.Y+y,position.Z+z),tree_definition);
+ tree_leaves_placement(vmanip,v3f(position.X+x,position.Y+y,position.Z+z+1),tree_definition);
+ tree_leaves_placement(vmanip,v3f(position.X+x,position.Y+y,position.Z+z-1),tree_definition);
}
}
dir = v3f(1,0,0);
@@ -311,12 +335,19 @@ void make_ltree(ManualMapVoxelManipulator &vmanip, v3s16 p0, INodeDefManager *nd
position+=dir;
break;
case 'f':
- make_tree_leaves_placement(vmanip,v3f(p0.X+position.X,p0.Y+position.Y,p0.Z+position.Z),tree_definition);
+ tree_single_leaves_placement(vmanip,v3f(position.X,position.Y,position.Z),tree_definition);
dir = v3f(1,0,0);
dir = transposeMatrix(rotation,dir);
position+=dir;
break;
- // turtle commands
+ case 'R':
+ tree_fruit_placement(vmanip,v3f(position.X,position.Y,position.Z),tree_definition);
+ dir = v3f(1,0,0);
+ dir = transposeMatrix(rotation,dir);
+ position+=dir;
+ break;
+
+ // turtle orientation commands
case '[':
stack_orientation.push(rotation);
stack_position.push(position);
@@ -363,7 +394,7 @@ void make_ltree(ManualMapVoxelManipulator &vmanip, v3s16 p0, INodeDefManager *nd
}
}
-void make_tree_node_placement(ManualMapVoxelManipulator &vmanip, v3f p0,
+void tree_node_placement(ManualMapVoxelManipulator &vmanip, v3f p0,
MapNode node)
{
v3s16 p1 = v3s16(myround(p0.X),myround(p0.Y),myround(p0.Z));
@@ -376,7 +407,7 @@ void make_tree_node_placement(ManualMapVoxelManipulator &vmanip, v3f p0,
vmanip.m_data[vmanip.m_area.index(p1)] = node;
}
-void make_tree_trunk_placement(ManualMapVoxelManipulator &vmanip, v3f p0,
+void tree_trunk_placement(ManualMapVoxelManipulator &vmanip, v3f p0,
TreeDef &tree_definition)
{
v3s16 p1 = v3s16(myround(p0.X),myround(p0.Y),myround(p0.Z));
@@ -389,7 +420,7 @@ void make_tree_trunk_placement(ManualMapVoxelManipulator &vmanip, v3f p0,
vmanip.m_data[vmanip.m_area.index(p1)] = tree_definition.trunknode;
}
-void make_tree_leaves_placement(ManualMapVoxelManipulator &vmanip, v3f p0,
+void tree_leaves_placement(ManualMapVoxelManipulator &vmanip, v3f p0,
TreeDef &tree_definition)
{
MapNode leavesnode=tree_definition.leavesnode;
@@ -401,7 +432,7 @@ void make_tree_leaves_placement(ManualMapVoxelManipulator &vmanip, v3f p0,
u32 vi = vmanip.m_area.index(p1);
if(vmanip.m_data[vi].getContent() != CONTENT_AIR
&& vmanip.m_data[vi].getContent() != CONTENT_IGNORE)
- return;
+ return;
if (tree_definition.fruit_chance>0)
{
if (myrand_range(1,100) > 100-tree_definition.fruit_chance)
@@ -413,6 +444,35 @@ void make_tree_leaves_placement(ManualMapVoxelManipulator &vmanip, v3f p0,
vmanip.m_data[vmanip.m_area.index(p1)] = leavesnode;
}
+void tree_single_leaves_placement(ManualMapVoxelManipulator &vmanip, v3f p0,
+ TreeDef &tree_definition)
+{
+ MapNode leavesnode=tree_definition.leavesnode;
+ if (myrand_range(1,100) > 100-tree_definition.leaves2_chance)
+ leavesnode=tree_definition.leaves2node;
+ v3s16 p1 = v3s16(myround(p0.X),myround(p0.Y),myround(p0.Z));
+ if(vmanip.m_area.contains(p1) == false)
+ return;
+ u32 vi = vmanip.m_area.index(p1);
+ if(vmanip.m_data[vi].getContent() != CONTENT_AIR
+ && vmanip.m_data[vi].getContent() != CONTENT_IGNORE)
+ return;
+ vmanip.m_data[vmanip.m_area.index(p1)] = leavesnode;
+}
+
+void tree_fruit_placement(ManualMapVoxelManipulator &vmanip, v3f p0,
+ TreeDef &tree_definition)
+{
+ v3s16 p1 = v3s16(myround(p0.X),myround(p0.Y),myround(p0.Z));
+ if(vmanip.m_area.contains(p1) == false)
+ return;
+ u32 vi = vmanip.m_area.index(p1);
+ if(vmanip.m_data[vi].getContent() != CONTENT_AIR
+ && vmanip.m_data[vi].getContent() != CONTENT_IGNORE)
+ return;
+ vmanip.m_data[vmanip.m_area.index(p1)] = tree_definition.fruitnode;
+}
+
irr::core::matrix4 setRotationAxisRadians(irr::core::matrix4 M, double angle, v3f axis)
{
double c = cos(angle);
diff --git a/src/treegen.h b/src/treegen.h
index 1435d6bb2..cb365f4be 100644
--- a/src/treegen.h
+++ b/src/treegen.h
@@ -1,7 +1,7 @@
/*
Minetest-c55
Copyright (C) 2010-2012 celeron55, Perttu Ahola <celeron55@gmail.com>,
- 2012 RealBadAngel, Maciej Kasatkin <mk@realbadangel.pl>
+ 2012-2013 RealBadAngel, Maciej Kasatkin <mk@realbadangel.pl>
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
@@ -51,24 +51,29 @@ int fruit_chance;
// Add default tree
void make_tree(ManualMapVoxelManipulator &vmanip, v3s16 p0,
- bool is_apple_tree, INodeDefManager *ndef);
-
+ bool is_apple_tree, INodeDefManager *ndef);
+
// Add L-Systems tree (used by engine)
void make_ltree(ManualMapVoxelManipulator &vmanip, v3s16 p0, INodeDefManager *ndef,
TreeDef tree_definition);
// Spawn L-systems tree from LUA
- void spawn_ltree (ServerEnvironment *env, v3s16 p0, INodeDefManager *ndef, TreeDef tree_definition);
-
+ void spawn_ltree (ServerEnvironment *env, v3s16 p0, INodeDefManager *ndef,
+ TreeDef tree_definition);
+
// L-System tree gen helper functions
- void make_tree_node_placement(ManualMapVoxelManipulator &vmanip, v3f p0,
+ void tree_node_placement(ManualMapVoxelManipulator &vmanip, v3f p0,
MapNode node);
- void make_tree_trunk_placement(ManualMapVoxelManipulator &vmanip, v3f p0,
+ void tree_trunk_placement(ManualMapVoxelManipulator &vmanip, v3f p0,
+ TreeDef &tree_definition);
+ void tree_leaves_placement(ManualMapVoxelManipulator &vmanip, v3f p0,
+ TreeDef &tree_definition);
+ void tree_single_leaves_placement(ManualMapVoxelManipulator &vmanip, v3f p0,
+ TreeDef &tree_definition);
+ void tree_fruit_placement(ManualMapVoxelManipulator &vmanip, v3f p0,
TreeDef &tree_definition);
- void make_tree_leaves_placement(ManualMapVoxelManipulator &vmanip, v3f p0,
- TreeDef &tree_definition);
irr::core::matrix4 setRotationAxisRadians(irr::core::matrix4 M, double angle,v3f axis);
-
+
v3f transposeMatrix(irr::core::matrix4 M ,v3f v);
-
+
}; // namespace treegen
#endif
diff --git a/src/util/numeric.h b/src/util/numeric.h
index 5ed4d0e99..a028f1ff2 100644
--- a/src/util/numeric.h
+++ b/src/util/numeric.h
@@ -120,6 +120,7 @@ inline s16 rangelim(s16 i, s16 max)
}
#define rangelim(d, min, max) ((d) < (min) ? (min) : ((d)>(max)?(max):(d)))
+#define myfloor(x) ((x) > 0.0 ? (int)(x) : (int)(x) - 1)
inline v3s16 arealim(v3s16 p, s16 d)
{
diff --git a/src/util/string.cpp b/src/util/string.cpp
index 215ac299d..61b307c60 100644
--- a/src/util/string.cpp
+++ b/src/util/string.cpp
@@ -21,6 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "../sha1.h"
#include "../base64.h"
+#include "../porting.h"
// Get an sha-1 hash of the player's name combined with
// the password entered. That's what the server uses as
@@ -47,3 +48,67 @@ size_t curl_write_data(char *ptr, size_t size, size_t nmemb, void *userdata) {
stream->write(ptr, count);
return count;
}
+
+u32 readFlagString(std::string str, FlagDesc *flagdesc) {
+ u32 result = 0;
+ char *s = &str[0];
+ char *flagstr, *strpos = NULL;
+
+ while ((flagstr = strtok_r(s, ",", &strpos))) {
+ s = NULL;
+
+ while (*flagstr == ' ' || *flagstr == '\t')
+ flagstr++;
+
+ for (int i = 0; flagdesc[i].name; i++) {
+ if (!strcasecmp(flagstr, flagdesc[i].name)) {
+ result |= flagdesc[i].flag;
+ break;
+ }
+ }
+ }
+
+ return result;
+}
+
+std::string writeFlagString(u32 flags, FlagDesc *flagdesc) {
+ std::string result;
+
+ for (int i = 0; flagdesc[i].name; i++) {
+ if (flags & flagdesc[i].flag) {
+ result += flagdesc[i].name;
+ result += ", ";
+ }
+ }
+
+ size_t len = result.length();
+ if (len >= 2)
+ result.erase(len - 2, 2);
+
+ return result;
+}
+
+char *mystrtok_r(char *s, const char *sep, char **lasts) {
+ char *t;
+
+ if (!s)
+ s = *lasts;
+
+ while (*s && strchr(sep, *s))
+ s++;
+
+ if (!*s)
+ return NULL;
+
+ t = s;
+ while (*t) {
+ if (strchr(sep, *t)) {
+ *t++ = '\0';
+ break;
+ }
+ t++;
+ }
+
+ *lasts = t;
+ return s;
+}
diff --git a/src/util/string.h b/src/util/string.h
index 58274c677..a469a074a 100644
--- a/src/util/string.h
+++ b/src/util/string.h
@@ -28,6 +28,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <vector>
#include <sstream>
+struct FlagDesc {
+ const char *name;
+ u32 flag;
+};
+
static inline std::string padStringRight(std::string s, size_t len)
{
if(len > s.size())
@@ -283,6 +288,9 @@ inline std::string wrap_rows(const std::string &from, u32 rowlen)
std::string translatePassword(std::string playername, std::wstring password);
size_t curl_write_data(char *ptr, size_t size, size_t nmemb, void *userdata);
+u32 readFlagString(std::string str, FlagDesc *flagdesc);
+std::string writeFlagString(u32 flags, FlagDesc *flagdesc);
+char *mystrtok_r(char *s, const char *sep, char **lasts);
#endif
diff --git a/src/voxel.h b/src/voxel.h
index e7155dfac..c2a5efb4b 100644
--- a/src/voxel.h
+++ b/src/voxel.h
@@ -72,7 +72,7 @@ public:
MaxEdge(p)
{
}
-
+
/*
Modifying methods
*/
@@ -106,14 +106,14 @@ public:
if(p.Y > MaxEdge.Y) MaxEdge.Y = p.Y;
if(p.Z > MaxEdge.Z) MaxEdge.Z = p.Z;
}
-
+
// Pad with d nodes
void pad(v3s16 d)
{
MinEdge -= d;
MaxEdge += d;
}
-
+
/*void operator+=(v3s16 off)
{
MinEdge += off;
@@ -202,7 +202,7 @@ public:
}
assert(contains(a));
-
+
// Take back area, XY inclusive
{
v3s16 min(MinEdge.X, MinEdge.Y, a.MaxEdge.Z+1);
@@ -258,7 +258,7 @@ public:
}
}
-
+
/*
Translates position from virtual coordinates to array index
*/
@@ -274,7 +274,7 @@ public:
{
return index(p.X, p.Y, p.Z);
}
-
+
// Translate index in the X coordinate
void add_x(const v3s16 &extent, u32 &i, s16 a)
{
@@ -343,7 +343,7 @@ class VoxelManipulator /*: public NodeContainer*/
public:
VoxelManipulator();
virtual ~VoxelManipulator();
-
+
/*
Virtuals from NodeContainer
*/
@@ -430,7 +430,7 @@ public:
void setNode(v3s16 p, const MapNode &n)
{
emerge(p);
-
+
m_data[m_area.index(p)] = n;
m_flags[m_area.index(p)] &= ~VOXELFLAG_INEXISTENT;
m_flags[m_area.index(p)] &= ~VOXELFLAG_NOT_LOADED;
@@ -457,10 +457,10 @@ public:
//dstream<<"operator[] p=("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;
if(isValidPosition(p) == false)
emerge(VoxelArea(p));
-
+
return m_data[m_area.index(p)];
}*/
-
+
/*
Set stuff if available without an emerge.
Return false if failed.
@@ -496,7 +496,7 @@ public:
void print(std::ostream &o, INodeDefManager *nodemgr,
VoxelPrintMode mode=VOXELPRINT_MATERIAL);
-
+
void addArea(VoxelArea area);
/*
@@ -505,7 +505,7 @@ public:
*/
void copyFrom(MapNode *src, VoxelArea src_area,
v3s16 from_pos, v3s16 to_pos, v3s16 size);
-
+
// Copy data
void copyTo(MapNode *dst, VoxelArea dst_area,
v3s16 dst_pos, v3s16 from_pos, v3s16 size);
@@ -523,15 +523,15 @@ public:
void unspreadLight(enum LightBank bank,
core::map<v3s16, u8> & from_nodes,
core::map<v3s16, bool> & light_sources, INodeDefManager *nodemgr);
-
+
void spreadLight(enum LightBank bank, v3s16 p, INodeDefManager *nodemgr);
void spreadLight(enum LightBank bank,
core::map<v3s16, bool> & from_nodes, INodeDefManager *nodemgr);
-
+
/*
Virtual functions
*/
-
+
/*
Get the contents of the requested area from somewhere.
Shall touch only nodes that have VOXELFLAG_NOT_LOADED
@@ -565,7 +565,7 @@ public:
MaxEdge is 1 higher than maximum allowed position
*/
VoxelArea m_area;
-
+
/*
NULL if data size is 0 (extent (0,0,0))
Data is stored as [z*h*w + y*h + x]
@@ -576,7 +576,7 @@ public:
Flags of all nodes
*/
u8 *m_flags;
-
+
//TODO: Use these or remove them
//TODO: Would these make any speed improvement?
//bool m_pressure_route_valid;
diff --git a/src/voxelalgorithms.cpp b/src/voxelalgorithms.cpp
index 1af934f80..795530d40 100644
--- a/src/voxelalgorithms.cpp
+++ b/src/voxelalgorithms.cpp
@@ -86,10 +86,10 @@ SunlightPropagateResult propagateSunlight(VoxelManipulator &v, VoxelArea a,
required_a.pad(v3s16(0,1,0));
// Make sure we have access to it
v.emerge(a);
-
+
s16 max_y = a.MaxEdge.Y;
s16 min_y = a.MinEdge.Y;
-
+
for(s32 x=a.MinEdge.X; x<=a.MaxEdge.X; x++)
for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
{
@@ -125,11 +125,11 @@ SunlightPropagateResult propagateSunlight(VoxelManipulator &v, VoxelArea a,
if(incoming_light > old_light)
n.setLight(LIGHTBANK_DAY, incoming_light, ndef);
-
+
if(diminish_light(incoming_light) != 0)
light_sources.insert(p, true);
}
-
+
// Check validity of sunlight at top of block below if it
// hasn't already been proven invalid
if(bottom_sunlight_valid)