summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt4
-rw-r--r--src/biome.cpp233
-rw-r--r--src/biome.h98
-rw-r--r--src/clouds.cpp3
-rw-r--r--src/defaultsettings.cpp27
-rw-r--r--src/environment.cpp12
-rw-r--r--src/environment.h3
-rw-r--r--src/farmesh.cpp6
-rw-r--r--src/guiConfigureWorld.cpp686
-rw-r--r--src/guiConfigureWorld.h110
-rw-r--r--src/guiMainMenu.cpp197
-rw-r--r--src/guiMainMenu.h16
-rw-r--r--src/guiPauseMenu.cpp506
-rw-r--r--src/guiPauseMenu.h120
-rw-r--r--src/irrlichttypes.h3
-rw-r--r--src/main.cpp12
-rw-r--r--src/map.cpp353
-rw-r--r--src/map.h102
-rw-r--r--src/mapgen.cpp338
-rw-r--r--src/mapgen.h224
-rw-r--r--src/mapgen_v6.cpp1406
-rw-r--r--src/mods.cpp277
-rw-r--r--src/mods.h90
-rw-r--r--src/nodedef.cpp10
-rw-r--r--src/noise.cpp736
-rw-r--r--src/noise.h171
-rw-r--r--src/porting.cpp90
-rw-r--r--src/porting.h15
-rw-r--r--src/scriptapi.cpp313
-rw-r--r--src/server.cpp512
-rw-r--r--src/server.h108
-rw-r--r--src/serverlist.cpp185
-rw-r--r--src/serverlist.h46
-rw-r--r--src/settings.h385
-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/voxel.h34
-rw-r--r--src/voxelalgorithms.cpp8
41 files changed, 5959 insertions, 1653 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 24f682f3f..8d0363548 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -188,6 +188,7 @@ set(common_SRCS
log.cpp
content_sao.cpp
mapgen.cpp
+ mapgen_v6.cpp
treegen.cpp
content_nodemeta.cpp
content_mapnode.cpp
@@ -218,6 +219,7 @@ set(common_SRCS
sha1.cpp
base64.cpp
ban.cpp
+ biome.cpp
clientserver.cpp
staticobject.cpp
util/serialize.cpp
@@ -275,11 +277,13 @@ set(minetest_SRCS
guiDeathScreen.cpp
guiChatConsole.cpp
guiCreateWorld.cpp
+ guiConfigureWorld.cpp
guiConfirmMenu.cpp
client.cpp
filecache.cpp
tile.cpp
shader.cpp
+ serverlist.cpp
game.cpp
main.cpp
)
diff --git a/src/biome.cpp b/src/biome.cpp
new file mode 100644
index 000000000..180a9c4a5
--- /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 " << 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/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/defaultsettings.cpp b/src/defaultsettings.cpp
index ca5f33609..e6526d3d7 100644
--- a/src/defaultsettings.cpp
+++ b/src/defaultsettings.cpp
@@ -107,6 +107,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 +129,9 @@ void set_default_settings(Settings *settings)
settings->setDefault("media_fetch_threads", "8");
+ settings->setDefault("serverlist_url", "servers.minetest.ru/server.list");
+ settings->setDefault("serverlist_file", "favoriteservers.txt");
+
// Server stuff
// "map-dir" doesn't exist by default.
settings->setDefault("default_game", "minetest");
@@ -168,5 +172,28 @@ void set_default_settings(Settings *settings)
settings->setDefault("congestion_control_max_rate", "400");
settings->setDefault("congestion_control_min_rate", "10");
settings->setDefault("remote_media", "");
+
+ //mapgen related things
+ settings->setDefault("mg_name", "v6");
+ settings->setDefault("water_level", "1");
+ settings->setDefault("chunksize", "5");
+ settings->setDefault("mg_flags", "19");
+ 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..51255b918 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)
{
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/guiConfigureWorld.cpp b/src/guiConfigureWorld.cpp
new file mode 100644
index 000000000..38f1a554d
--- /dev/null
+++ b/src/guiConfigureWorld.cpp
@@ -0,0 +1,686 @@
+/*
+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());
+ ModSpec mod = m_addonmods[modname];
+ enableAllMods(mod.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());
+ ModSpec mod = m_addonmods[modname];
+ enableAllMods(mod.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);
+
+ // if no mods installed, 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);
+ }
+ else
+ {
+ // if modpack, show enable/disable all buttons. otherwise, show
+ // enabled checkbox
+ if(node->getParent() == m_treeview->getRoot() ||
+ m_addonmods[modname].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());
+ }
+ else
+ {
+ 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);
+
+ // dependencies of this mod:
+ m_dependencies_listbox->clear();
+ ModSpec mspec = m_addonmods[modname];
+ 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:
+ m_rdependencies_listbox->clear();
+ 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)
+{
+ 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
+ ModSpec mspec = m_addonmods[modname];
+ 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)
+{
+ 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/guiMainMenu.cpp b/src/guiMainMenu.cpp
index bac9052b9..9291bb4ec 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"
@@ -114,6 +115,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
@@ -361,14 +365,14 @@ void GUIMainMenu::regenerateGui(v2u32 screensize)
// 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 +380,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 +389,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 +420,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");
}
@@ -868,6 +909,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;
+ dst->serverdescription = server.description;
+ }
}
void GUIMainMenu::acceptInput()
@@ -912,6 +959,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 +971,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 +1034,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 +1112,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 +1164,55 @@ 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->name != "" && i->description != "")
+ text = i->name + " (" + i->description + ")";
+ else if (i->name !="")
+ text = i->name;
+ else
+ text = i->address + ":" + i->port;
+
+ 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;
+ ((gui::IGUIEditBox*)getElementFromId(GUI_ID_ADDRESS_INPUT))
+ ->setText(narrow_to_wide(m_data->servers[id].address).c_str());
+ ((gui::IGUIEditBox*)getElementFromId(GUI_ID_PORT_INPUT))
+ ->setText(narrow_to_wide(m_data->servers[id].port).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..2c657cb23 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;
@@ -58,8 +62,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
@@ -73,7 +80,9 @@ struct MainMenuData
selected_world(0),
simple_singleplayer_mode(false),
// Actions
- only_refresh(false)
+ only_refresh(false),
+
+ serverlist_show_available(false)
{}
};
@@ -110,12 +119,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/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/main.cpp b/src/main.cpp
index 0af9d113c..6f4095148 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -71,6 +71,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "util/string.h"
#include "subgame.h"
#include "quicktune.h"
+#include "serverlist.h"
/*
Settings.
@@ -1581,7 +1582,7 @@ int main(int argc, char *argv[])
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 +1599,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..6a058bb51 100644
--- a/src/map.cpp
+++ b/src/map.cpp
@@ -39,13 +39,13 @@ with this program; if not, write to the Free Software Foundation, Inc.,
/*
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 +108,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 +133,7 @@ MapSector * Map::getSectorNoGenerate(v2s16 p)
MapSector *sector = getSectorNoGenerateNoEx(p);
if(sector == NULL)
throw InvalidPositionException();
-
+
return sector;
}
@@ -148,7 +148,7 @@ MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
}
MapBlock * Map::getBlockNoCreate(v3s16 p3d)
-{
+{
MapBlock *block = getBlockNoCreateNoEx(p3d);
if(block == NULL)
throw InvalidPositionException();
@@ -248,10 +248,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 +265,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 +693,7 @@ void Map::updateLighting(enum LightBank bank,
core::map<v3s16, u8> unlight_from;
int num_bottom_invalid = 0;
-
+
{
//TimeTaker t("first stuff");
@@ -847,7 +847,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 +938,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 +951,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 +1440,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 +1461,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 +1503,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 +1560,7 @@ void Map::unloadUnusedData(float timeout,
i != blocks.end(); i++)
{
MapBlock *block = (*i);
-
+
if(block->getUsageTimer() > timeout)
{
// Save if modified
@@ -1629,7 +1629,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 +1714,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 +1824,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,10 +1993,10 @@ void Map::removeNodeTimer(v3s16 p)
/*
ServerMap
*/
-
ServerMap::ServerMap(std::string savedir, IGameDef *gamedef):
Map(dout_server, gamedef),
m_seed(0),
+ m_emerge(NULL),
m_map_metadata_changed(true),
m_database(NULL),
m_database_read(NULL),
@@ -2006,16 +2006,19 @@ ServerMap::ServerMap(std::string savedir, IGameDef *gamedef):
//m_chunksize = 8; // Takes a few seconds
+ m_mgparams = MapgenParams::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");
+ + ((u64)(myrand()&0xffff)<<48));
+ m_mgparams->seed = m_seed;
}
/*
@@ -2050,6 +2053,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 +2143,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 +2151,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 +2174,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 +2187,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 +2215,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 +2226,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 +2250,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 +2321,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 +2338,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 +2392,7 @@ MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data,
assert(block);
block->setGenerated(true);
}
-
+
/*
Save changed parts of map
NOTE: Will be saved later.
@@ -2429,14 +2436,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 +2476,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 +2486,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 +2500,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 +2526,7 @@ MapBlock * ServerMap::generateBlock(
/*
Create block make data
*/
- mapgen::BlockMakeData data;
+ BlockMakeData data;
initBlockMake(&data, p);
/*
@@ -2526,7 +2534,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 +2604,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 +2621,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 +2666,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 +2688,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 +2714,10 @@ MapBlock * ServerMap::emergeBlock(v3s16 p, bool allow_generate)
// Queue event
dispatchEvent(&event);
-
+
return block;
}
- }
+ }*/
return NULL;
}
@@ -2741,8 +2758,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 +2784,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 +2930,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 +2942,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 +2955,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 +2964,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 +3044,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 +3061,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 +3076,48 @@ void ServerMap::saveMapMeta()
<<"could not open"<<fullpath<<std::endl;
throw FileNotGoodException("Cannot open chunk metadata");
}
-
+
Settings params;
- params.setU64("seed", m_seed);
+
+ params.set("mg_name", m_emerge->params->mg_name);
+
+ params.setU64("seed", m_emerge->params->seed);
+ params.setS16("water_level", m_emerge->params->water_level);
+ params.setS16("chunksize", m_emerge->params->chunksize);
+ params.setS32("mg_flags", m_emerge->params->flags);
+/* switch (m_emerge->params->mg_version) {
+ case 6:
+ {*/
+ MapgenV6Params *v6params = (MapgenV6Params *)m_emerge->params;
+
+ params.setFloat("mgv6_freq_desert", v6params->freq_desert);
+ params.setFloat("mgv6_freq_beach", v6params->freq_beach);
+ params.setNoiseParams("mgv6_np_terrain_base", v6params->np_terrain_base);
+ params.setNoiseParams("mgv6_np_terrain_higher", v6params->np_terrain_higher);
+ params.setNoiseParams("mgv6_np_steepness", v6params->np_steepness);
+ params.setNoiseParams("mgv6_np_height_select", v6params->np_height_select);
+ params.setNoiseParams("mgv6_np_trees", v6params->np_trees);
+ params.setNoiseParams("mgv6_np_mud", v6params->np_mud);
+ params.setNoiseParams("mgv6_np_beach", v6params->np_beach);
+ params.setNoiseParams("mgv6_np_biome", v6params->np_biome);
+ params.setNoiseParams("mgv6_np_cave", v6params->np_cave);
+ /* break;
+ }
+ default:
+ ; //complain here
+ }*/
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 +3145,18 @@ void ServerMap::loadMapMeta()
params.parseConfigLine(line);
}
- m_seed = params.getU64("seed");
+ MapgenParams *mgparams = MapgenParams::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 +3170,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 +3216,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 +3261,7 @@ bool ServerMap::loadSectorMeta(v2s16 p2d)
{
return false;
}
-
+
return true;
}
@@ -3250,7 +3305,7 @@ bool ServerMap::loadSectorFull(v2s16 p2d)
{
return false;
}
-
+
/*
Load blocks
*/
@@ -3312,8 +3367,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 +3384,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 +3432,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 +3460,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 +3475,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 +3504,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 +3527,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 +3543,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 +3553,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 +3575,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 +3584,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 +3602,7 @@ MapBlock* ServerMap::loadBlock(v3s16 blockpos)
return getBlockNoCreateNoEx(blockpos);
}
sqlite3_reset(m_database_read);
-
+
// Not found in database, try the files
}
@@ -3560,7 +3623,7 @@ MapBlock* ServerMap::loadBlock(v3s16 blockpos)
loadlayout = 2;
sectordir = getSectorDir(p2d, 2);
}
-
+
/*
Make sure sector is loaded
*/
@@ -3583,7 +3646,7 @@ MapBlock* ServerMap::loadBlock(v3s16 blockpos)
return NULL;
}
}
-
+
/*
Make sure file exists
*/
@@ -3641,7 +3704,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 +3715,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 +3755,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 +3779,9 @@ void MapVoxelManipulator::blitBack
continue;
MapNode &n = m_data[m_area.index(p)];
-
+
v3s16 blockpos = getNodeBlockPos(p);
-
+
try
{
// Get block
@@ -3727,7 +3790,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 +3800,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 +3843,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 +3864,7 @@ void ManualMapVoxelManipulator::initialEmerge(
n = m_loaded_blocks.find(p);
if(n != NULL)
continue;
-
+
bool block_data_inexistent = false;
try
{
@@ -3842,7 +3905,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..760b23de5 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;
@@ -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,21 @@ public:
u64 getSeed(){ return m_seed; }
+ MapgenParams *getMapgenParams(){ return m_mgparams; }
+
+ void setEmerge(EmergeManager *emerge){ m_emerge = emerge; }
+
+ // 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 +509,7 @@ private:
This is reset to false when written on disk.
*/
bool m_map_metadata_changed;
-
+
/*
SQLite database and statements
*/
@@ -514,7 +524,7 @@ class MapVoxelManipulator : public VoxelManipulator
public:
MapVoxelManipulator(Map *map);
virtual ~MapVoxelManipulator();
-
+
virtual void clear()
{
VoxelManipulator::clear();
@@ -542,11 +552,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..c86743459 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,192 @@ 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"
+/////////////////// 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};
+
+
+///////////////////////////////////////////////////////////////////////////////
+/////////////////////////////// Emerge Manager ////////////////////////////////
+
+
+
+EmergeManager::EmergeManager(IGameDef *gamedef, BiomeDefManager *bdef,
+ MapgenParams *mgparams) {
+ //the order of these assignments is pretty important
+ this->biomedef = bdef ? bdef : new BiomeDefManager(gamedef);
+ this->params = mgparams;
+ this->mapgen = NULL;
+ this->mapgen = getMapgen();
+}
+
+
+EmergeManager::~EmergeManager() {
+ delete biomedef;
+ delete params;
+}
+
+
+Mapgen *EmergeManager::getMapgen() {
+ if (!mapgen) {
+ /*switch (params->mg_version) {
+ case 6:*/
+ mapgen = new MapgenV6(0, (MapgenV6Params *)params);
+ /* break;
+ default:
+ errorstream << "EmergeManager: Unsupported mapgen version "
+ << params->mg_version << ", falling back to V6" << std::endl;
+ params->mg_version = 6;
+ mapgen = new MapgenV6(0, (MapgenV6Params *)params);
+ }*/
+ }
+ 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;
+}
+
+
+MapgenParams *MapgenParams::createMapgenParams(std::string &mgstr) {
+ return new MapgenV6Params(); // this will be fixed later
+ /*switch (mgver) {
+ case 6:
+ return new MapgenV6Params();
+ default: //instead of complaining, default to 6
+ return new MapgenV6Params();
+ }*/
+}
+
+
+MapgenParams *MapgenParams::getParamsFromSettings(Settings *settings) {
+ std::string mg_name = settings->get("mg_name");
+ MapgenParams *mgparams = MapgenParams::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->getS32("mg_flags");
+
+/* switch (mg_version) {
+ case 6:
+ {*/
+ MapgenV6Params *v6params = (MapgenV6Params *)mgparams;
+
+ v6params->freq_desert = settings->getFloat("mgv6_freq_desert");
+ v6params->freq_beach = settings->getFloat("mgv6_freq_beach");
+ v6params->np_terrain_base = settings->getNoiseParams("mgv6_np_terrain_base");
+ v6params->np_terrain_higher = settings->getNoiseParams("mgv6_np_terrain_higher");
+ v6params->np_steepness = settings->getNoiseParams("mgv6_np_steepness");
+ v6params->np_height_select = settings->getNoiseParams("mgv6_np_height_select");
+ v6params->np_trees = settings->getNoiseParams("mgv6_np_trees");
+ v6params->np_mud = settings->getNoiseParams("mgv6_np_mud");
+ v6params->np_beach = settings->getNoiseParams("mgv6_np_beach");
+ v6params->np_biome = settings->getNoiseParams("mgv6_np_biome");
+ v6params->np_cave = settings->getNoiseParams("mgv6_np_cave");
+
+ if (!v6params->np_terrain_base || !v6params->np_terrain_higher ||
+ !v6params->np_steepness || !v6params->np_height_select ||
+ !v6params->np_trees || !v6params->np_mud ||
+ !v6params->np_beach || !v6params->np_biome || !v6params->np_cave) {
+ delete mgparams;
+ return NULL;
+ }
+/*
+ break;
+ }
+ default:
+ delete mgparams;
+ return NULL;
+ }*/
+
+ return mgparams;
+
+}
+
+
+/////////////////////////////////// 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 +306,7 @@ static s16 find_stone_level(VoxelManipulator &vmanip, v2s16 p2d,
}
#endif
+
#if 0
static void make_papyrus(VoxelManipulator &vmanip, v3s16 p0,
@@ -190,7 +376,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 +400,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 +424,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 +587,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 +629,7 @@ public:
{
m_dir = dir;
}
-
+
bool findPlaceForDoor(v3s16 &result_place, v3s16 &result_dir)
{
for(u32 i=0; i<100; i++)
@@ -540,7 +726,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 +773,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 +813,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 +835,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 +853,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 +861,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 +889,7 @@ static void make_dungeon1(VoxelManipulator &vmanip, PseudoRandom &random,
else
// Don't actually make a door
roomplace -= doordir;
-
+
}
}
#endif
@@ -926,7 +1112,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 +1210,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 +1318,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 +1355,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 +1363,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 +1379,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 +1393,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 +1439,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 +1460,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 +1470,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 +1502,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 +1561,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 +1577,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 +1620,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 +1637,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 +1653,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 +1681,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 +1697,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 +1759,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 +1788,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 +1801,12 @@ void make_block(BlockMakeData *data)
orp = rp;
}
-
+
}
}//timer1
#endif
-
+
#if 1
{
// 15ms @cs=8
@@ -1629,13 +1815,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 +1846,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 +1877,7 @@ void make_block(BlockMakeData *data)
{
if(mudcount >= mud_add_amount)
break;
-
+
MapNode &n = vmanip.m_data[i];
n = addnode;
mudcount++;
@@ -1756,7 +1942,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 +1959,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 +1980,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 +1999,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 +2037,7 @@ void make_block(BlockMakeData *data)
}
// Drop mud on side
-
+
for(u32 di=0; di<4; di++)
{
v3s16 dirp = dirs4[di];
@@ -1894,7 +2080,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 +2098,7 @@ void make_block(BlockMakeData *data)
}
}
}
-
+
}
}//timer1
@@ -1940,7 +2126,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 +2168,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 +2194,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 +2299,7 @@ void make_block(BlockMakeData *data)
else
vmanip.m_data[i] = n_stone;
}
-
+
vmanip->m_area.add_y(em, i, 1);
}
}
@@ -2168,7 +2354,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 +2368,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 +2390,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 +2443,7 @@ void make_block(BlockMakeData *data)
make_nc(vmanip, ncrandom, ndef);
}
}
-
+
/*
Add top and bottom side of water to transforming_liquid queue
*/
@@ -2346,7 +2532,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 +2570,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 +2707,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 +2741,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 +2798,7 @@ void make_block(BlockMakeData *data)
if(mineralrandom.next()%8 == 0)
vmanip.m_data[vi] = MapNode(c_mese);
}
-
+
}
}
/*
@@ -2742,7 +2928,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 +2942,8 @@ void make_block(BlockMakeData *data)
}
}
+#endif ///BIG COMMENT
+
BlockMakeData::BlockMakeData():
no_op(false),
vmanip(NULL),
@@ -2768,6 +2956,6 @@ BlockMakeData::~BlockMakeData()
delete vmanip;
}
-}; // namespace mapgen
+//}; // namespace mapgen
diff --git a/src/mapgen.h b/src/mapgen.h
index 8986ddab0..d508d37a8 100644
--- a/src/mapgen.h
+++ b/src/mapgen.h
@@ -22,48 +22,210 @@ 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"
-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 AVERAGE_MUD_AMOUNT 4
+
+class BiomeDefManager;
+class Biome;
+
+//struct BlockMakeData;
class MapBlock;
class ManualMapVoxelManipulator;
+class VoxelManipulator;
class INodeDefManager;
-namespace mapgen
+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;
+
+extern NoiseParams nparams_v7_def_terrain;
+extern NoiseParams nparams_v7_def_bgroup;
+extern NoiseParams nparams_v7_def_heat;
+extern NoiseParams nparams_v7_def_humidity;
+
+enum BiomeType
{
- // Finds precise ground level at any position
- s16 find_ground_level_from_noise(u64 seed, v2s16 p2d, s16 precision);
+ BT_NORMAL,
+ BT_DESERT
+};
- // Find out if block is completely underground
- bool block_is_underground(u64 seed, v3s16 blockpos);
- // Get a pseudorandom seed for a position on the map
- u32 get_blockseed(u64 seed, v3s16 p);
+struct BlockMakeData {
+ bool no_op;
+ ManualMapVoxelManipulator *vmanip;
+ u64 seed;
+ v3s16 blockpos_min;
+ v3s16 blockpos_max;
+ v3s16 blockpos_requested;
+ UniqueQueue<v3s16> transforming_liquid;
+ INodeDefManager *nodedef;
- // Main map generation routine
- void make_block(BlockMakeData *data);
-
- /*
- These are used by FarMesh
- */
- bool get_have_beach(u64 seed, v2s16 p2d);
+ 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;
+ }
+
+ static MapgenParams *createMapgenParams(std::string &mgname);
+ static MapgenParams *getParamsFromSettings(Settings *settings);
+
+};
+
+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;
+ }
+};
+
+
+class Mapgen {
+public:
+ int seed;
+ int water_level;
+ bool generating;
+ int id;
+
+ virtual void makeChunk(BlockMakeData *data) {};
+ virtual int getGroundLevelAtPoint(v2s16 p) = 0;
+
+ //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);
+};
+
+
+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);
+};
+
+
+class EmergeManager {
+public:
+ //settings
+ MapgenParams *params;
+
+ //mapgen objects here
+ Mapgen *mapgen;
+
+ //biome manager
+ BiomeDefManager *biomedef;
+
+ EmergeManager(IGameDef *gamedef, BiomeDefManager *bdef, MapgenParams *mgparams);
+ ~EmergeManager();
+
+ Mapgen *getMapgen();
+ void addBlockToQueue();
- 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..a86edcca9
--- /dev/null
+++ b/src/mapgen_v6.cpp
@@ -0,0 +1,1406 @@
+/*
+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
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+
+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)
+{
+ 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) {
+ 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)
+{
+ /*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...
+ 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);
+
+ 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/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..560c6090d 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
*/
@@ -354,7 +354,7 @@ public:
ContentFeatures &f = m_content_features[i];
f.reset(); // Reset to defaults
}
-
+
// Set CONTENT_AIR
{
ContentFeatures f;
@@ -541,11 +541,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++)
diff --git a/src/noise.cpp b/src/noise.cpp
index e75fbf4bd..de9d48808 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,323 @@ 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.
+ */
+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
+ noisey = 0;
+ for (j = 0; j != sy; j++) {
+ v00 = noisebuf[noisey * nlx];
+ v10 = noisebuf[noisey * nlx + 1];
+ v01 = noisebuf[(noisey + 1) * nlx];
+ v11 = noisebuf[(noisey + 1) * nlx + 1];
+
+ u = orig_u;
+ noisex = 0;
+ for (i = 0; i != sx; i++) {
+ buf[j * sx + i] = biLinearInterpolation(v00, v10, v01, v11, u, v);
+ u += step_x;
+ if (u >= 1.0) {
+ u -= 1.0;
+ noisex++;
+ v00 = v10;
+ v01 = v11;
+ v10 = noisebuf[noisey * nlx + noisex + 1];
+ v11 = noisebuf[(noisey + 1) * nlx + noisex + 1];
+ }
+ }
+
+ v += step_y;
+ if (v >= 1.0) {
+ v -= 1.0;
+ noisey++;
+ }
+ }
}
-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);
+
+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_w;
+ 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_w = w;
+
+ //calculate noise point lattice
+ nlx = (int)(u + sx * step_x) + 2;
+ nly = (int)(v + sy * step_y) + 2;
+ nlz = (int)(v + sy * 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);
+
+#define index(x, y, z) ((z) * nly * nlx + (y) * nlx + (x))
+
+ //calculate interpolations
+ noisey = 0;
+ noisez = 0;
+ for (k = 0; k != sz; k++) {
+ v000 = noisebuf[index(0, noisey, noisez)];
+ v100 = noisebuf[index(1, noisey, noisez)];
+ v010 = noisebuf[index(0, noisey + 1, noisez)];
+ v110 = noisebuf[index(1, noisey + 1, noisez)];
+ v001 = noisebuf[index(0, noisey, noisez + 1)];
+ v101 = noisebuf[index(1, noisey, noisez + 1)];
+ v011 = noisebuf[index(0, noisey + 1, noisez + 1)];
+ v111 = noisebuf[index(1, noisey + 1, noisez + 1)];
+
+ w = orig_w;
+ noisey = 0;
+ for (j = 0; j != sy; j++) {
+ v000 = noisebuf[index(0, noisey, noisez)];
+ v100 = noisebuf[index(1, noisey, noisez)];
+ v010 = noisebuf[index(0, noisey + 1, noisez)];
+ v110 = noisebuf[index(1, noisey + 1, noisez)];
+ v001 = noisebuf[index(0, noisey, noisez + 1)];
+ v101 = noisebuf[index(1, noisey, noisez + 1)];
+ v011 = noisebuf[index(0, noisey + 1, noisez + 1)];
+ v111 = noisebuf[index(1, noisey + 1, noisez + 1)];
+
+ u = orig_u;
+ noisex = 0;
+ for (i = 0; i != sx; i++) {
+ buf[j * sx + i] = 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[index(noisex + 1, noisey, noisez)];
+ v110 = noisebuf[index(noisex + 1, noisey + 1, noisez)];
+ v001 = v101;
+ v011 = v111;
+ v101 = noisebuf[index(noisex + 1, noisey, noisez + 1)];
+ v111 = noisebuf[index(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++;
+ }
+ }
}
-/*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::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;
+}
+
+
+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/porting.cpp b/src/porting.cpp
index 945aea6eb..de15de9ce 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) != 0);
+
+ 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..184e1ab54 100644
--- a/src/porting.h
+++ b/src/porting.h
@@ -35,14 +35,29 @@ 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 ALIGNOF(x) __alignof(x)
#define sleep_ms(x) Sleep(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)
#else
#include <unistd.h>
+ #include <stdint.h> //for uintptr_t
+
+ #define ALIGNOF(x) __alignof__(x)
#define sleep_ms(x) usleep(x*1000)
#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 35e708a04..6e2a0a314 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
*/
@@ -988,7 +999,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);
@@ -1032,7 +1043,7 @@ static ItemDefinition read_item_definition(lua_State *L, int index,
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 +1078,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 +1124,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 +1149,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 +1184,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 +1215,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 +1239,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 +1275,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 +1290,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 +1379,7 @@ private:
static const luaL_reg methods[];
// Exported functions
-
+
// garbage collector
static int gc_object(lua_State *L)
{
@@ -1621,7 +1632,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 +1821,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 +1841,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 +2196,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 +2223,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 +2338,7 @@ private:
InvRef::createNodeMeta(L, ref->m_p);
return 1;
}
-
+
// to_table(self)
static int l_to_table(lua_State *L)
{
@@ -2373,7 +2384,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 +2508,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 +2524,7 @@ private:
return NULL;
return (LuaEntitySAO*)obj;
}
-
+
static PlayerSAO* getplayersao(ObjectRef *ref)
{
ServerActiveObject *obj = getobject(ref);
@@ -2523,7 +2534,7 @@ private:
return NULL;
return (PlayerSAO*)obj;
}
-
+
static Player* getplayer(ObjectRef *ref)
{
PlayerSAO *playersao = getplayersao(ref);
@@ -2531,9 +2542,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 +2563,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 +2581,7 @@ private:
lua_setfield(L, -2, "z");
return 1;
}
-
+
// setpos(self, pos)
static int l_setpos(lua_State *L)
{
@@ -2584,7 +2595,7 @@ private:
co->setPos(pos);
return 0;
}
-
+
// moveto(self, pos, continuous=false)
static int l_moveto(lua_State *L)
{
@@ -2856,7 +2867,7 @@ private:
co->setVelocity(pos);
return 0;
}
-
+
// getvelocity(self)
static int l_getvelocity(lua_State *L)
{
@@ -2868,7 +2879,7 @@ private:
pushFloatPos(L, v);
return 1;
}
-
+
// setacceleration(self, {x=num, y=num, z=num})
static int l_setacceleration(lua_State *L)
{
@@ -2881,7 +2892,7 @@ private:
co->setAcceleration(pos);
return 0;
}
-
+
// getacceleration(self)
static int l_getacceleration(lua_State *L)
{
@@ -2893,7 +2904,7 @@ private:
pushFloatPos(L, v);
return 1;
}
-
+
// setyaw(self, radians)
static int l_setyaw(lua_State *L)
{
@@ -2905,7 +2916,7 @@ private:
co->setYaw(yaw);
return 0;
}
-
+
// getyaw(self)
static int l_getyaw(lua_State *L)
{
@@ -2917,7 +2928,7 @@ private:
lua_pushnumber(L, yaw);
return 1;
}
-
+
// settexturemod(self, mod)
static int l_settexturemod(lua_State *L)
{
@@ -2929,7 +2940,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 +2977,7 @@ private:
lua_pushstring(L, name.c_str());
return 1;
}
-
+
// get_luaentity(self)
static int l_get_luaentity(lua_State *L)
{
@@ -2977,7 +2988,7 @@ private:
luaentity_get(L, co->getId());
return 1;
}
-
+
/* Player-only */
// is_player(self)
@@ -2988,7 +2999,7 @@ private:
lua_pushboolean(L, (player != NULL));
return 1;
}
-
+
// get_player_name(self)
static int l_get_player_name(lua_State *L)
{
@@ -3002,7 +3013,7 @@ private:
lua_pushstring(L, player->getName());
return 1;
}
-
+
// get_look_dir(self)
static int l_get_look_dir(lua_State *L)
{
@@ -3144,7 +3155,7 @@ public:
ObjectRef *o = checkobject(L, -1);
o->m_object = NULL;
}
-
+
static void Register(lua_State *L)
{
lua_newtable(L);
@@ -3240,8 +3251,8 @@ 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 +3284,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 +3303,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);
@@ -3370,7 +3381,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 +3392,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 +3402,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 +3411,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 +3422,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 +3433,7 @@ private:
lua_pushnumber(L,t.timeout);
return 1;
}
-
+
static int l_get_elapsed(lua_State *L)
{
NodeTimerRef *o = checkobject(L, 1);
@@ -3460,7 +3471,7 @@ public:
NodeTimerRef *o = checkobject(L, -1);
o->m_env = NULL;
}
-
+
static void Register(lua_State *L)
{
lua_newtable(L);
@@ -3525,7 +3536,7 @@ private:
if(!ud) luaL_typerror(L, narg, className);
return *(EnvRef**)ud; // unbox pointer
}
-
+
// Exported functions
// EnvRef:set_node(pos, node)
@@ -3969,7 +3980,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 +4010,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;
@@ -4094,7 +4105,7 @@ public:
EnvRef *o = checkobject(L, -1);
o->m_env = NULL;
}
-
+
static void Register(lua_State *L)
{
lua_newtable(L);
@@ -4147,6 +4158,8 @@ 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_2d},
+ //method{EnvRef, get_perlin_map_3d},
method(EnvRef, clear_objects),
method(EnvRef, spawn_tree),
{0,0}
@@ -4166,7 +4179,7 @@ private:
static const luaL_reg methods[];
// Exported functions
-
+
// garbage collector
static int gc_object(lua_State *L)
{
@@ -4217,7 +4230,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 +4334,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 +4350,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");
@@ -4442,6 +4455,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)
{
@@ -4481,7 +4572,7 @@ static int l_register_item_raw(lua_State *L)
else
def.node_placement_prediction = "";
}
-
+
// Register item definition
idef->registerItem(def);
@@ -4504,9 +4595,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 +4706,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 +4881,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 +4997,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 +5214,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 +5249,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 +5352,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 +5405,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");
@@ -5337,7 +5438,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 +5482,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 +5533,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 +5577,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 +5596,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 +5692,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 +5712,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 +5860,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 +5868,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 +5888,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 +5898,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 +5915,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 +5931,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 +6196,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 +6212,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 +6780,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 +6795,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 +6803,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 +6819,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 +6830,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 +6861,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 +6880,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 +6913,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 +6930,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..d227474aa 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"
@@ -80,7 +81,7 @@ public:
*m_flag = false;
}
}
-
+
private:
bool *m_flag;
};
@@ -105,7 +106,7 @@ public:
*m_ignorevariable = VoxelArea();
}
}
-
+
private:
VoxelArea *m_ignorevariable;
};
@@ -129,7 +130,7 @@ void * ServerThread::Thread()
//TimeTaker timer("AsyncRunStep()");
m_server->AsyncRunStep();
}
-
+
//infostream<<"Running m_server->Receive()"<<std::endl;
m_server->Receive();
}
@@ -149,7 +150,7 @@ void * ServerThread::Thread()
m_server->setAsyncFatalError(e.what());
}
}
-
+
END_DEBUG_EXCEPTION_HANDLER(errorstream)
return NULL;
@@ -168,7 +169,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 +185,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 +198,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 +229,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 +248,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 +269,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 +292,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 +312,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 +325,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 +338,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 +354,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 +369,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 +440,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 +465,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 +477,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 +490,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 +499,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 +520,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 +530,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 +547,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 +587,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 +600,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 +614,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 +629,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 +661,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 +703,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 +717,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 +781,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 +796,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 +823,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 +878,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 +888,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 +944,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()),
@@ -955,7 +964,7 @@ Server::Server(
m_objectdata_timer = 0.0;
m_emergethread_trigger_timer = 0.0;
m_savemap_timer = 0.0;
-
+
m_env_mutex.Init();
m_con_mutex.Init();
m_step_dtime_mutex.Init();
@@ -963,10 +972,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 +985,72 @@ 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();
+ // complain about mods with unsatisfied dependencies
+ if(!modconf.isConsistent())
+ {
+ errorstream << "The following mods have unsatisfied dependencies: ";
+ std::list<ModSpec> modlist = modconf.getUnsatisfiedMods();
+ for(std::list<ModSpec>::iterator it = modlist.begin();
+ it != modlist.end(); ++it)
+ {
+ errorstream << (*it).name << " ";
+ }
+ 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);
+ 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 +1065,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 +1087,31 @@ 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();
+
// Initialize Environment
-
- m_env = new ServerEnvironment(new ServerMap(path_world, this), m_lua,
- this, this);
-
+ ServerMap *servermap = new ServerMap(path_world, this);
+ m_env = new ServerEnvironment(servermap, m_lua, this, this);
+
+ // Create emerge manager
+ m_emerge = new EmergeManager(this, m_biomedef, servermap->getMapgenParams());
+
+ // Give map pointer to the emerge manager
+ servermap->setEmerge(m_emerge);
+
// 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 +1139,7 @@ Server::~Server()
*/
{
JMutexAutoLock conlock(m_con_mutex);
-
+
std::wstring line = L"*** Server shutting down";
/*
@@ -1138,12 +1188,12 @@ Server::~Server()
infostream<<"Server: Saving environment metadata"<<std::endl;
m_env->saveMeta(m_path_world);
}
-
+
/*
Stop threads
*/
stop();
-
+
/*
Delete clients
*/
@@ -1154,19 +1204,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 +1240,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 +1248,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 +1265,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 +1273,7 @@ void Server::stop()
m_emergethread.setRun(false);
m_thread.stop();
m_emergethread.stop();
-
+
infostream<<"Server: Threads stopped"<<std::endl;
}
@@ -1245,28 +1297,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 +1330,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 +1384,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 +1394,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 +1441,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 +1474,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 +1482,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 +1500,7 @@ void Server::AsyncRunStep()
counter = 0.0;
JMutexAutoLock lock2(m_con_mutex);
-
+
if(m_clients.size() != 0)
infostream<<"Players:"<<std::endl;
for(core::map<u16, RemoteClient*>::Iterator
@@ -1511,18 +1563,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 +1589,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 +1607,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 +1621,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 +1671,7 @@ void Server::AsyncRunStep()
all_known_objects[id] = true;
}
}
-
+
m_env->setKnownActiveObjects(whatever);
#endif
@@ -1644,7 +1696,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 +1711,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 +1818,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 +1870,7 @@ void Server::AsyncRunStep()
infostream<<"WARNING: Server: Unknown MapEditEvent "
<<((u32)event->type)<<std::endl;
}
-
+
/*
Set blocks not sent to far players
*/
@@ -1862,7 +1914,7 @@ void Server::AsyncRunStep()
verbosestream<<"Server: MapEditEvents:"<<std::endl;
prof.print(verbosestream);
}
-
+
}
/*
@@ -1875,7 +1927,7 @@ void Server::AsyncRunStep()
if(counter >= 2.0)
{
counter = 0.0;
-
+
m_emergethread.trigger();
// Update m_enable_rollback_recording here too
@@ -1898,13 +1950,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 +1990,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 +2010,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 +2036,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 +2048,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 +2074,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 +2089,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
);
return;
}
-
+
/*
Read and check network protocol version
*/
@@ -2093,7 +2145,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 +2170,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 +2178,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 +2222,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 +2246,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 +2271,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 +2309,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);
}
@@ -2295,16 +2347,16 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 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 +2367,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 +2381,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 +2402,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 +2445,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 +2466,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 +2494,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 +2503,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
{
if(datasize < 2+1)
return;
-
+
/*
[0] u16 command
[2] u8 count
@@ -2477,7 +2529,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
{
if(datasize < 2+1)
return;
-
+
/*
[0] u16 command
[2] u8 count
@@ -2658,7 +2710,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 +2726,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 +2744,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 +2783,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
send_to_sender = true;
}
}
-
+
if(line != L"")
{
if(send_to_others)
@@ -2858,9 +2910,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 +2922,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 +3216,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
}
}
} // action == 2
-
+
/*
3: place block or right-click object
*/
@@ -3200,7 +3252,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 +3313,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 +3346,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 +3363,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 +3449,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 +3468,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 +3510,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 +3523,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;
@@ -3592,7 +3644,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 +3658,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 +3670,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 +3689,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 +3752,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 +3780,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 +3942,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 +4030,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 +4055,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 +4071,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 +4090,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 +4107,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 +4126,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 +4154,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 +4167,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 +4287,7 @@ void Server::sendMediaAnnouncement(u16 peer_id)
string sha1_digest
}
*/
-
+
writeU16(os, TOCLIENT_ANNOUNCE_MEDIA);
writeU16(os, file_announcements.size());
@@ -4436,7 +4488,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 +4528,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 +4724,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 +4733,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 +4756,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 +4822,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 +4832,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 +4847,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 +4867,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 +4903,7 @@ v3f findSpawnPos(ServerMap &map)
}
}
#endif
-
+
return intToFloat(nodepos, BS);
}
@@ -4922,7 +4976,7 @@ void Server::handlePeerChange(PeerChange &c)
{
JMutexAutoLock envlock(m_env_mutex);
JMutexAutoLock conlock(m_con_mutex);
-
+
if(c.type == PEER_ADDED)
{
/*
@@ -4952,7 +5006,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 +5019,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 +5054,7 @@ void Server::handlePeerChange(PeerChange &c)
message += L" (timed out)";
}
}
-
+
/* Run scripts and remove from environment */
{
if(player != NULL)
@@ -5043,18 +5097,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 +5133,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;
diff --git a/src/server.h b/src/server.h
index 43beb167a..86d5513d8 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,11 @@ 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 SendHP(con::Connection &con, u16 peer_id, u8 hp);
static void SendAccessDenied(con::Connection &con, u16 peer_id,
const std::wstring &reason);
@@ -605,7 +611,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 +639,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 +658,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 +693,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 +702,7 @@ private:
/*
Variables
*/
-
+
// World directory
std::string m_path_world;
// Path to user's configuration file ("" = no configuration file)
@@ -709,7 +715,7 @@ 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;
@@ -717,14 +723,14 @@ private:
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;
@@ -739,29 +745,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 +785,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 +794,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 +816,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..88a213db1
--- /dev/null
+++ b/src/serverlist.cpp
@@ -0,0 +1,185 @@
+/*
+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"
+#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").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::deSerialize(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;
+ while (std::getline(stream, line))
+ {
+ std::transform(line.begin(), line.end(),line.begin(), ::toupper);
+ if (line == "[SERVER]")
+ {
+ ServerListSpec thisserver;
+ std::getline(stream, thisserver.name);
+ std::getline(stream, thisserver.address);
+ std::getline(stream, thisserver.port);
+ std::getline(stream, thisserver.description);
+ 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 + "\n";
+ liststring += i->address + "\n";
+ liststring += i->port + "\n";
+ liststring += i->description + "\n";
+ liststring += "\n";
+ }
+ return liststring;
+}
+
+} //namespace ServerList
diff --git a/src/serverlist.h b/src/serverlist.h
new file mode 100644
index 000000000..a040d53e3
--- /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"
+
+#ifndef SERVERLIST_HEADER
+#define SERVERLIST_HEADER
+
+struct ServerListSpec
+{
+ std::string name;
+ std::string address;
+ std::string port;
+ std::string description;
+};
+
+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>);
+} //ServerList namespace
+
+#endif
diff --git a/src/settings.h b/src/settings.h
index 1ab6fcc6b..2b46676c6 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,269 @@ public:
return value;
}
+ 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 setBool(std::string name, bool value)
{
if(value)
@@ -554,11 +840,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 +859,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 +879,7 @@ public:
void clear()
{
JMutexAutoLock lock(m_mutex);
-
+
m_settings.clear();
m_defaults.clear();
}
@@ -596,7 +887,7 @@ public:
void updateValue(Settings &other, const std::string &name)
{
JMutexAutoLock lock(m_mutex);
-
+
if(&other == this)
return;
@@ -613,7 +904,7 @@ public:
{
JMutexAutoLock lock(m_mutex);
JMutexAutoLock lock2(other.m_mutex);
-
+
if(&other == this)
return;
@@ -623,7 +914,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 +929,7 @@ public:
{
JMutexAutoLock lock(m_mutex);
JMutexAutoLock lock2(other.m_mutex);
-
+
if(&other == this)
return *this;
@@ -649,7 +940,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 +957,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/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)