summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTiger Wang <ziwei.tiger@hotmail.co.uk>2014-06-10 21:51:22 +0200
committerTiger Wang <ziwei.tiger@hotmail.co.uk>2014-06-10 21:51:22 +0200
commitbfa8aaf41bd2e982e81ca1385efc9ec25ebd246c (patch)
tree706bb3680c60cbc1f78e31f0ca2ac6d1e9422a6a
parentPortal improvements and suggestions (diff)
parentFixed clang warnings about abs() in Noise.cpp. (diff)
downloadcuberite-bfa8aaf41bd2e982e81ca1385efc9ec25ebd246c.tar
cuberite-bfa8aaf41bd2e982e81ca1385efc9ec25ebd246c.tar.gz
cuberite-bfa8aaf41bd2e982e81ca1385efc9ec25ebd246c.tar.bz2
cuberite-bfa8aaf41bd2e982e81ca1385efc9ec25ebd246c.tar.lz
cuberite-bfa8aaf41bd2e982e81ca1385efc9ec25ebd246c.tar.xz
cuberite-bfa8aaf41bd2e982e81ca1385efc9ec25ebd246c.tar.zst
cuberite-bfa8aaf41bd2e982e81ca1385efc9ec25ebd246c.zip
-rw-r--r--MCServer/Plugins/APIDump/APIDesc.lua2
-rw-r--r--MCServer/furnace.txt42
-rw-r--r--Tools/MCADefrag/Globals.h1
-rw-r--r--docs/Generator.html210
-rw-r--r--docs/img/biomalheights.jpgbin0 -> 77747 bytes
-rw-r--r--docs/img/biomeheights.jpgbin0 -> 16432 bytes
-rw-r--r--docs/img/biomeheightsavg.jpgbin0 -> 14946 bytes
-rw-r--r--docs/img/gaussprobability.jpgbin0 -> 12994 bytes
-rw-r--r--docs/img/perlincompositor1.jpgbin0 -> 15457 bytes
-rw-r--r--docs/img/perlincompositor2.jpgbin0 -> 29005 bytes
-rw-r--r--docs/img/perlincompositor3.jpgbin0 -> 21119 bytes
-rw-r--r--docs/img/roofprobability.jpgbin0 -> 16679 bytes
-rw-r--r--docs/img/smallfoliageclumps.jpgbin0 -> 15867 bytes
-rw-r--r--lib/tolua++/src/bin/lua/_driver.lua93
-rw-r--r--lib/tolua++/src/bin/lua/basic.lua2
-rw-r--r--lib/tolua++/src/bin/lua/compat-5.1.lua9
-rw-r--r--lib/tolua++/src/bin/lua/declaration.lua2
-rw-r--r--lib/tolua++/src/bin/lua/feature.lua2
-rw-r--r--src/Bindings/AllToLua_lua.bat.bat27
-rw-r--r--src/BlockArea.cpp8
-rw-r--r--src/BlockID.h1
-rw-r--r--src/Blocks/BlockDoor.cpp2
-rw-r--r--src/Blocks/BlockDoor.h104
-rw-r--r--src/Chunk.cpp1
-rw-r--r--src/Chunk.h9
-rw-r--r--src/ChunkData.cpp2
-rw-r--r--src/ChunkMap.cpp24
-rw-r--r--src/ChunkMap.h4
-rw-r--r--src/ClientHandle.cpp38
-rw-r--r--src/ClientHandle.h5
-rw-r--r--src/Entities/Entity.cpp10
-rw-r--r--src/Entities/Entity.h10
-rw-r--r--src/Entities/ItemFrame.cpp4
-rw-r--r--src/Entities/Player.cpp20
-rw-r--r--src/Entities/Player.h2
-rw-r--r--src/Generating/CompoGen.cpp14
-rw-r--r--src/Generating/ComposableGenerator.cpp14
-rw-r--r--src/Generating/HeiGen.cpp66
-rw-r--r--src/Generating/HeiGen.h21
-rw-r--r--src/Generating/Prefabs/RainbowRoadPrefabs.cpp1406
-rw-r--r--src/Generating/Prefabs/RainbowRoadPrefabs.h15
-rw-r--r--src/Generating/RainbowRoadsGen.cpp115
-rw-r--r--src/Generating/RainbowRoadsGen.h47
-rw-r--r--src/Items/ItemBoat.h2
-rw-r--r--src/Items/ItemBow.h2
-rw-r--r--src/Items/ItemFishingRod.h2
-rw-r--r--src/Items/ItemItemFrame.h2
-rw-r--r--src/Items/ItemMinecart.h2
-rw-r--r--src/Items/ItemPainting.h2
-rw-r--r--src/Mobs/Bat.cpp3
-rw-r--r--src/Mobs/Blaze.cpp5
-rw-r--r--src/Mobs/CaveSpider.cpp1
-rw-r--r--src/Mobs/Ghast.cpp2
-rw-r--r--src/Mobs/Skeleton.cpp2
-rw-r--r--src/Mobs/Wither.cpp2
-rw-r--r--src/Mobs/Wither.h2
-rw-r--r--src/Noise.cpp170
-rw-r--r--src/Noise.h64
-rw-r--r--src/OSSupport/IsThread.cpp21
-rw-r--r--src/OSSupport/IsThread.h4
-rw-r--r--src/Protocol/Protocol125.cpp11
-rw-r--r--src/Protocol/Protocol125.h4
-rw-r--r--src/Protocol/Protocol132.cpp2
-rw-r--r--src/Protocol/Protocol17x.cpp11
-rw-r--r--src/Protocol/Protocol17x.h4
-rw-r--r--src/Server.cpp8
-rw-r--r--src/Simulator/IncrementalRedstoneSimulator.cpp326
-rw-r--r--src/Simulator/IncrementalRedstoneSimulator.h6
-rw-r--r--src/Simulator/SandSimulator.cpp2
-rw-r--r--src/UI/Window.cpp1
-rw-r--r--src/World.cpp136
-rw-r--r--src/World.h30
72 files changed, 2868 insertions, 293 deletions
diff --git a/MCServer/Plugins/APIDump/APIDesc.lua b/MCServer/Plugins/APIDump/APIDesc.lua
index 1423d64bc..40bfe79ac 100644
--- a/MCServer/Plugins/APIDump/APIDesc.lua
+++ b/MCServer/Plugins/APIDump/APIDesc.lua
@@ -1969,7 +1969,7 @@ cPluginManager.AddHook(cPluginManager.HOOK_CHAT, OnChatMessage);
BroadcastChatSuccess = { Params = "Message", Return = "", Notes = "Prepends Green [INFO] / colours entire text (depending on ShouldUseChatPrefixes()) and broadcasts message. For success messages." },
BroadcastChatWarning = { Params = "Message", Return = "", Notes = "Prepends Rose [WARN] / colours entire text (depending on ShouldUseChatPrefixes()) and broadcasts message. For concerning events, such as plugin reload etc." },
CreateAndInitializeWorld = { Params = "WorldName", Return = "{{cWorld|cWorld}}", Notes = "Creates a new world and initializes it. If there is a world whith the same name it returns nil." },
- FindAndDoWithPlayer = { Params = "PlayerName, CallbackFunction", Return = "", Notes = "Calls the given callback function for the given player." },
+ FindAndDoWithPlayer = { Params = "PlayerName, CallbackFunction", Return = "", Notes = "Calls the given callback function for all players with names partially (or fully) matching the name string provided." },
ForEachPlayer = { Params = "CallbackFunction", Return = "", Notes = "Calls the given callback function for each player. The callback function has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cPlayer|cPlayer}})</pre>" },
ForEachWorld = { Params = "CallbackFunction", Return = "", Notes = "Calls the given callback function for each world. The callback function has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cWorld|cWorld}})</pre>" },
GetCraftingRecipes = { Params = "", Return = "{{cCraftingRecipe|cCraftingRecipe}}", Notes = "Returns the CraftingRecipes object" },
diff --git a/MCServer/furnace.txt b/MCServer/furnace.txt
index 31d265ce7..1e98583ba 100644
--- a/MCServer/furnace.txt
+++ b/MCServer/furnace.txt
@@ -61,18 +61,30 @@
#--------------------------
# Fuels
-! 263:1 = 1600 # 1 Coal -> 80 sec
-! 263:1:1 = 1600 # 1 Charcoal -> 80 sec
-! 42:126:1 = 150 # 1 Halfslab -> 7.5 seconds
-! 5:1 = 300 # 1 Planks -> 15 sec
-! 280:1 = 100 # 1 Stick -> 5 sec
-! 85:1 = 300 # 1 Fence -> 15 sec
-! 53:1 = 300 # 1 Wooden Stairs -> 15 sec
-! 58:1 = 300 # 1 Crafting Table -> 15 sec
-! 47:1 = 300 # 1 Bookshelf -> 15 sec
-! 54:1 = 300 # 1 Chest -> 15 sec
-! 84:1 = 300 # 1 Jukebox -> 15 sec
-! 327:1 = 200000 # 1 Lava Bucket -> 1000 sec
-! 17:1 = 300 # 1 Wood -> 15 sec
-! 6:1 = 100 # 1 Sapling -> 5 sec
-! 173:1 = 7400 # 1 Coal Block -> 370 sec, based on https://github.com/minetest/common/commit/e0f5a6fd6936052756e27a05a2bfdd6aa86b38e1 which is a clone of MC \ No newline at end of file
+! 263:1 = 1600 # 1 Coal -> 80 sec
+! 263:1:1 = 1600 # 1 Charcoal -> 80 sec
+! 126:1 = 15 # 1 Halfslab -> 7.5 sec
+! 5:1 = 300 # 1 Planks -> 15 sec
+! 280:1 = 100 # 1 Stick -> 5 sec
+! 85:1 = 300 # 1 Fence -> 15 sec
+! 53:1 = 300 # 1 Wooden Stairs -> 15 sec
+! 58:1 = 300 # 1 Crafting Table -> 15 sec
+! 47:1 = 300 # 1 Bookshelf -> 15 sec
+! 54:1 = 300 # 1 Chest -> 15 sec
+! 84:1 = 300 # 1 Jukebox -> 15 sec
+! 327:1 = 20000 # 1 Lava Bucket -> 1000 sec
+! 17:1 = 300 # 1 Wood -> 15 sec
+! 6:1 = 100 # 1 Sapling -> 5 sec
+! 173:1 = 16000 # 1 Coal Block -> 800 sec
+! 369:1 = 2400 # 1 Blaze Rod -> 120 sec
+! 25:1 = 300 # 1 Note Block -> 15 sec
+! 151:1 = 300 # 1 Daylight Sensor -> 15 sec
+! 107:1 = 300 # 1 Fence Gate -> 15 sec
+! 167:1 = 300 # 1 Trapdoor -> 15 sec
+! 146:1 = 300 # 1 Trapped Chest -> 15 sec
+! 72:1 = 300 # 1 Pressure Plate -> 15 sec
+! 270:1 = 200 # 1 Wooden Pickaxe -> 10 sec
+! 271:1 = 200 # 1 Wooden Axe -> 10 sec
+! 269:1 = 200 # 1 Wooden Shovel -> 10 sec
+! 290:1 = 200 # 1 Wooden Hoe -> 10 sec
+! 268:1 = 200 # 1 Wooden Sword -> 10 sec
diff --git a/Tools/MCADefrag/Globals.h b/Tools/MCADefrag/Globals.h
index f70a90d17..6593187e6 100644
--- a/Tools/MCADefrag/Globals.h
+++ b/Tools/MCADefrag/Globals.h
@@ -240,6 +240,7 @@ template <typename Type> class cItemCallback
public:
/// Called for each item in the internal list; return true to stop the loop, or false to continue enumerating
virtual bool Item(Type * a_Type) = 0;
+ virtual ~cItemCallback() {};
} ;
diff --git a/docs/Generator.html b/docs/Generator.html
index f6e7f1cd9..90a92c553 100644
--- a/docs/Generator.html
+++ b/docs/Generator.html
@@ -19,6 +19,8 @@ with specific implementation notes regarding MCServer.</p>
<li><a href="#heightgen">Terrain height</a></li>
<li><a href="#compositiongen">Terrain composition</a></li>
<li><a href="#finishgen">Finishers</a></li>
+<li><a href="#makefaster">Making it all faster</a></li>
+<li><a href="#GPU">Executing on a GPU</a></li>
</ul>
</p>
@@ -304,16 +306,224 @@ using the same approach as in MultiStepMap - by using a thresholded 2D Perlin no
<hr />
<a name="heightgen"><h2>Terrain height</h2></a>
+<p>As with biomes, the easiest way to generate terrain height is not generating at all - assigning a constant
+height value to all columns. This is again useful either for internal tests, and for worlds like MineCraft's
+Flat world.</p>
+
+<p>For a somewhat more realistic landscape, we will employ the good old 2D Perlin noise. We can use it
+directly as a heightmap - each value we get from the noise is stretched into the desired range (usually from
+40 to 120 blocks for regular MineCraft worlds) and used as the height value. However, this doesn't play too
+well with the biomes we've just generated. If the biome says "ocean" and the Perlin noise says "mountain",
+the end result will be unpleasant.</p>
+
+<p>So we want a height generator that is biome-aware. The easiest way of doing this is to have a separate
+generator for each biome. Simply use the biome map to select which generator to use, then ask the appropriate
+generator for the height value. Again, this doesn't work too well - imagine an ExtremeHills biome right next
+to an Ocean biome. If no extra care is taken, the border between these two will be a high wall. The following
+image shows a 2D representation (for simplification purposes) of the problem:</p>
+<img src="img/biomeheights.jpg" />
+
+<p>This requires some further processing. What we need is for the terrain height to be dependent not only on
+the immediate biome for that column, but also on the close surroundings of the column. This is exactly the
+kind of task that averaging is designed for. If we take the area of 9x9 biomes centered around the queried
+column, generate height for each of the biomes therein, sum them up and divide by 81 (the number of biomes
+summed), we will be effectively making a 9-long running average over the terrain, and all the borders will
+suddenly become smooth. The following image shows the situation from the previous paragraph after applying
+the averaging process: </p>
+<img src="img/biomeheightsavg.jpg" />
+
+<p>The approach used in MCServer's Biomal generator is based on this idea, with two slight modifications.
+Instead of using a separate generator for each biome, one generator is used with a different set of input
+parameters for each biomes. These input parameters modify the overall amplitude and frequency of the Perlin
+noise that the generator produces, thus modifying the final terrain with regards to biomes. Additionally, the
+averaging process is weighted - columns closer to the queried column get a more powerful weight in the sum
+than the columns further away. The following image shows the output of MCServer's Biomal terrain height
+generator (each block type represents a different biome - ocean in the front (stone), plains and ice plains
+behind it (lapis, whitewool), extreme hills back right (soulsand), desert hills back left (mossy
+cobble)):</p>
+<a name="biomalheights"><img src="img/biomalheights.jpg" /></a>
+
+<p>One key observation about this whole approach is that in order for it to work, the biomes must be
+available for columns outside the currently generated chunk, otherwise the columns at the chunk's edge would
+not be able to properly average their height. This requirement can be fulfilled only by biome generators that
+adhere to the second <a href="#expectedproperties">Expected property</a> - that re-generating will produce
+the same data. If the biome generator returned different data for the same chunk each time it was invoked, it
+would become impossible to apply the averaging.</p>
+
+<p>(TODO: height with variations (N/A in MCS yet)</p>
<hr />
<a name="compositiongen"><h2>Terrain composition</h2></a>
+<p>As with the other generators, the composition generator category has its easy and debugging items, too.
+There's the "special" composition of "all the blocks are the same type", which fills the entire column, from
+the bottom to the height, with a single blocktype. This generator is useful when testing the generators in
+the other categories, to speed up the generation by leaving out unnecessary calculations. Another special
+compositor is a similar one, that fills all blocks with the same type, but the type varies for each biome.
+This way it's easy to see the generated biomes and possibly the heights for those biomes, as shown in the
+previous section on the <a href="#biomalheights">height averaging screenshot</a>.</p>
+
+<p>For a natural look, we need to put together a more complicated algorithm. The standard set forth in
+MineCraft is that the top of the world is covered in grass, then there are a few blocks of dirt and finally
+stone. This basic layout is then varied for different biomes - deserts have sand and sandstone instead of the
+grass and dirt layer. Mushroom biomes have mycelium in place of the grass. This per-biome dependency is
+trivial to implement - when compositing, simply use the appropriate layout for the column's biome.</p>
+
+<p>The next change concerns oceans. The generated heightmap doesn't include any waterlevel indication
+whatsoever. So it's up to the terrain compositor to actually decide where to place water. We do this by
+configuration - simply have a value in the config file specifying the sealevel height. The compositor then
+has to add water above any column which has a height lower than that. Additionally, the water needs to
+override per-biome layout selection - we don't want grass blocks to generate under water when the terrain
+height in the plains biome drops below the sealevel accidentally.</p>
+
+<p>The final feature in the compositor is the decision between multiple composition layouts within a single
+biome. A megataiga biome contains patches of non-grass dirt and podzol blocks, and the ocean floor can be
+made of dirt, gravel, sand or clay. A simple 2D Perlin noise can be used to select the layout to use for a
+specific column - simply threshold the noise's value by as many thresholds as there are layout variations,
+and use the layout corresponding to the threshold:</p>
+<img src="img/perlincompositor1.jpg" />
+<img src="img/perlincompositor2.jpg" />
+<img src="img/perlincompositor3.jpg" />
+
+<h3>Nether composition</h3>
+<p>So far we've been discussing only the Overworld generator. But MineCraft contains more than that. The
+Nether has a completely different look and feel, and quite different processes are required to generate that.
+Recall that MineCraft's Nether is 128 blocks high, with bedrock both at the top and the bottom. Between these
+two, the terrain looks more like a cavern than a surface. Not surprisingly, the Nether doesn't need a
+complicated height generator, it can use the flat height. However, the terrain composition must take an
+altogether different approach.</p>
+
+<p>The very first idea is to use the Perlin noise, but generate it in 3D, rather than 2D. Then, for each
+block, evaluate the noise value, if below 0, make it air, if not, make it netherrack.
+
+<p>To make it so that the bedrock at the top and at the bottom is never revealed, we can add a value
+increasing the more the Y coord gets towards the bottom or the top. This way the thresholding then guarantees
+that there will be no air anywhere near the bedrock.</p>
+
+<p>(TODO)</p>
<hr />
<a name="finishgen"><h2>Finishers</h2></a>
+<p>Finishers are a vast category of various additions to the terrain generator. They range from very easy
+ones, such as generating snow on top of the terrain in cold biomes, through medium ones, such as growing
+patches of flowers, complicated ones, such as placing trees and generating caves, all the way to very
+complicated ones such as villages and nether fortresses. There is no formal distinction between all these
+"categories", the only thing they have in common is that they take a chunk of blocks and modify it in some
+way.</p>
+
+<h3>Snow</h3>
+<p>Snow is probably the easiest of the finishers. It generates a block of snow on top of each block that is
+on top of the terrain and is not marked as non-snowable. It checks the chunk's heightmap to determine the top
+block, then checks whether the block supports snow on its top. Rails, levers and tall grass don't support
+snow, for example.</p>
+
+<h3>Ice</h3>
+<p>Another example of an easy finisher. This scans through the world and turn each water block on the surface
+into an ice block if the biome is cold. This means that any water block that is under any kind of other
+block, such as under a tree's leaves, will still stay water. Thus an additional improvement could be made by
+scanning down from the surface block through blocks that we deem as non-surface, such as leaves, torches,
+ladders, fences etc. Note that MCServer currently implements only the easy solution.</p>
+
+<h3>Bottom lava</h3>
+<p>Most worlds in MineCraft have lava lakes at their bottom. Generating these is pretty straightforward: Use
+the user-configured depth and replace all the air blocks below this depth with lava blocks. Note however,
+that this makes this generator dependent on the order in which the finishers are applied. If the mineshafts
+generate before bottom lava, the mineshafts that are below the lava level will get filled with lava. On the
+other hand, if bottom lava is generated before the mineshafts, it is possible for a mineshaft to "drill
+through" a lake of lava. MCServer doesn't try to solve this and instead lets the admin choose whichever they
+prefer.</p>
+
+<h3>Specific foliage</h3>
+<p>There are generators for specific kinds of foliage. The dead bushes in the desert biome and lilypads in
+the swamp biome both share the same generating pattern. They are both specific to a single biome and they
+both require a specific block underneath them in order to generate. Their implementation is simple: pick
+several random columns in the chunk. If the column is of the correct biome and has the correct top block,
+add the foliage block on top.</p>
+
+<p>In order to generate the same set of coordinates when the chunk is re-generated, we use the Perlin noise's
+basis functions (the ones providing the random values for Perlin cell vertices). These basically work as a
+hash function for the coorinates - the same input coordinates generate the same output value. We use the
+chunk's coordinates as two of the coords, and the iteration number as the third coordinate, to generate a
+random number. We then check the biome and the top block at those coordinates, if they allow, we generate the
+foliage block on top.</p>
+
+<p>Another example of specific foliage is the tall grass in the plains biome. There are quite a lot of these
+tall grass blocks, it would be inefficient to generate them using the random-coords approach described above.
+Instead, we will use a 2D Perlin noise again, with a threshold defining where to put the grass and where
+not.</p>
+
+<h3>Small foliage</h3>
+<p>For the flowers, grass, mushrooms in caves etc. we want to use a slightly different algorithm. These
+foliage blocks are customarily generated in small "clumps" - there are several blocks of the same type near
+together. To generate these, we first select random coords, using the coord hash functions, for a center of a
+clump. Then we select the type of block to generate. Finally, we loop over adding a random (coord hash)
+number to the clump center coords to get the block where to generate the foliage block:</p>
+<img src="img/smallfoliageclumps.jpg" />
+
+<p>In order to make the clump more "round" and "centered", we want the offsets to be closer to the clump
+center more often. This is done using a thing called Gaussian function distribution. Instead of having each
+random number generate with the same probability, we want higher probability for the numbers around zero,
+like this:</p>
+<img src="img/gaussprobability.jpg" />
+
+<p>Instead of doing complicated calculations to match this shape exactly, we will use a much easier shape.
+By adding together two random numbers in the same range, we get the probability distribution that has a
+"roof" shape, enough for our needs:</p>
+<img src="img/roofprobability.jpg" />
+
+<p>(For the curious, there is a proof that adding together infinitely many uniform-distributed random numbers
+produces random numbers with the Gaussian distribution.)</p>
+
+<p>This scheme can be used to produce clumps of flowers, when we select the 2D coords of the clump center on
+the top surface of the terrain. We simply generate the 2D coords of the foliage blocks and use the terrain
+height to find the third coord. If we want to generate clumps of mushrooms in the caves, however, we need to
+generate the clump center coords in 3D and either use 3 offsets for the mushrooms, or use 2 offsets plus
+searching for the closest opening Y-wise in the terrain.</p>
+
+<p>Note that the clumps generated by this scheme may overlap several chunks. Therefore it's crucial to
+actually check the surrounding chunks if their clumps overlap into the currently generated chunk, and apply
+those as well, otherwise there will be visible cuts in the foliage along the chunks borders.</p>
+
+<h3>Springs</h3>
+<p>Water and lava springs are essential for making the underground quite a lot more interesting. They are
+rather easy to generate, but a bit more difficult to get right. Generating simply means that a few random
+locations (obtained by our familiar coord hashing) are checked and if the block type in there is stone. Then
+we see all the horizontal neighbors of the block, plus the block underneath. If all of them except one are
+stone, and the one left is air, our block is suitable for turning into a spring. If there were more air
+neighbors, the spring would look somewhat unnatural; if there were no air neighbors, the spring won't flow
+anywhere, so it would be rather useless.</p>
+
+<p>The difficult part about springs is the amount of them to generate. There should be a few springs on the
+surface, perhaps a bit more in the mountaineous biomes. There should be quite a few more springs underground,
+but there should definitely be more water springs than lava springs in the upper levels of the terrain, while
+there should be more lava springs and almost no water springs near the bottom. To accomodate this, the
+MCServer team has made a tool that scanned through MineCraft's terrain and counted the amount of both types
+of springs in relation to their height. Two curves have been found for the distribution of each type of the
+spring:</p>
+<img src="http://mc-server.xoft.cz/img/vanilla_springs_huge.png" />
+
+<p>MCServer uses an approximation of the above curves to choose the height at which to generate the
+spring.</p>
+
+<!--
+<h3>Caves</h3>
+<p>Caves are definitely one of the main things people notice about MineCraft terrain. There are quite a lot
+of different algorithms available to generate terrain with caves.
+-->
+<hr />
+
+<a name="makefaster"><h2>Making it all faster</h2></a>
+<p>(TODO)</p>
+
+<a name="GPU"><h2>Executing on a GPU</h2></a>
+<p>Much of the terain generation consists of doing the same thing for every single column or block in a chunk. This
+sort of computation is much faster on a GPU as GPUs are massively parallel. High end GPUs can execute up to 30,000
+threads simultaneously, which would allow them to generate every block in half a chunk in parallel or every column
+in over 100 chunks in parallel. A naive comparison suggests that a 800MHz GPU with 15,000 threads can execute parallel
+code 250 times faster than a 3GHz CPU with 128 bit SIMD. Obviously we want to harness that power.</p>
</body>
</html>
diff --git a/docs/img/biomalheights.jpg b/docs/img/biomalheights.jpg
new file mode 100644
index 000000000..a01faef87
--- /dev/null
+++ b/docs/img/biomalheights.jpg
Binary files differ
diff --git a/docs/img/biomeheights.jpg b/docs/img/biomeheights.jpg
new file mode 100644
index 000000000..9dda27b0e
--- /dev/null
+++ b/docs/img/biomeheights.jpg
Binary files differ
diff --git a/docs/img/biomeheightsavg.jpg b/docs/img/biomeheightsavg.jpg
new file mode 100644
index 000000000..c8217cafc
--- /dev/null
+++ b/docs/img/biomeheightsavg.jpg
Binary files differ
diff --git a/docs/img/gaussprobability.jpg b/docs/img/gaussprobability.jpg
new file mode 100644
index 000000000..77da24748
--- /dev/null
+++ b/docs/img/gaussprobability.jpg
Binary files differ
diff --git a/docs/img/perlincompositor1.jpg b/docs/img/perlincompositor1.jpg
new file mode 100644
index 000000000..0d8f93cd9
--- /dev/null
+++ b/docs/img/perlincompositor1.jpg
Binary files differ
diff --git a/docs/img/perlincompositor2.jpg b/docs/img/perlincompositor2.jpg
new file mode 100644
index 000000000..11fc5b51d
--- /dev/null
+++ b/docs/img/perlincompositor2.jpg
Binary files differ
diff --git a/docs/img/perlincompositor3.jpg b/docs/img/perlincompositor3.jpg
new file mode 100644
index 000000000..46a2583ba
--- /dev/null
+++ b/docs/img/perlincompositor3.jpg
Binary files differ
diff --git a/docs/img/roofprobability.jpg b/docs/img/roofprobability.jpg
new file mode 100644
index 000000000..e7a155113
--- /dev/null
+++ b/docs/img/roofprobability.jpg
Binary files differ
diff --git a/docs/img/smallfoliageclumps.jpg b/docs/img/smallfoliageclumps.jpg
new file mode 100644
index 000000000..4cc6cbc00
--- /dev/null
+++ b/docs/img/smallfoliageclumps.jpg
Binary files differ
diff --git a/lib/tolua++/src/bin/lua/_driver.lua b/lib/tolua++/src/bin/lua/_driver.lua
new file mode 100644
index 000000000..21db96098
--- /dev/null
+++ b/lib/tolua++/src/bin/lua/_driver.lua
@@ -0,0 +1,93 @@
+
+-- Allow debugging by ZBS, if run under the IDE:
+local mobdebugfound, mobdebug = pcall(require, "mobdebug")
+if mobdebugfound then mobdebug.start() end
+
+-- The list of valid arguments that the ToLua scripts can process:
+local KnownArgs = {
+ ['v'] = true,
+ ['h'] = true,
+ ['p'] = true,
+ ['P'] = true,
+ ['o'] = true,
+ ['n'] = true,
+ ['H'] = true,
+ ['S'] = true,
+ ['1'] = true,
+ ['L'] = true,
+ ['D'] = true,
+ ['W'] = true,
+ ['C'] = true,
+ ['E'] = true,
+ ['t'] = true,
+ ['q'] = true,
+}
+
+
+
+
+
+-- The flags table used by ToLua scripts, to be filled from the cmdline params:
+flags = {}
+
+-- Te extra parameters used by ToLua scripts:
+_extra_parameters = {}
+
+-- ToLua version required by the scripts:
+TOLUA_VERSION = "tolua++-1.0.92"
+
+-- Lua version used by ToLua, required by the scripts:
+TOLUA_LUA_VERSION = "Lua 5.1"
+
+
+
+
+
+
+-- Process the cmdline params into the flags table:
+local args = arg or {}
+local argc = #args
+local i = 1
+while (i <= argc) do
+ local argv = args[i]
+ if (argv:sub(1, 1) == "-") then
+ if (KnownArgs[argv:sub(2)]) then
+ print("Setting flag \"" .. argv:sub(2) .. "\" to \"" .. args[i + 1] .. "\".")
+ flags[argv:sub(2)] = args[i + 1]
+ i = i + 1
+ else
+ print("Unknown option (" .. i .. "): " .. argv)
+ print("Aborting.")
+ os.exit(1)
+ end
+ else
+ print("Setting flag \"f\" to \"" .. argv .. "\".")
+ flags['f'] = argv
+ break
+ end
+ i = i + 1
+end
+
+-- Get the path where the scripts are located:
+path = args[0] or ""
+local index = path:find("/[^/]*$")
+if (index == nil) then
+ index = path:find("\\[^\\]*$")
+end
+if (index ~= nil) then
+ path = path:sub(1, index)
+end
+
+print("path is set to \"" .. path .. "\".")
+
+
+
+
+
+-- Call the ToLua processor:
+dofile(path .. "all.lua")
+
+
+
+
+
diff --git a/lib/tolua++/src/bin/lua/basic.lua b/lib/tolua++/src/bin/lua/basic.lua
index 4bff5276b..7b401d638 100644
--- a/lib/tolua++/src/bin/lua/basic.lua
+++ b/lib/tolua++/src/bin/lua/basic.lua
@@ -383,7 +383,7 @@ end
-- called to output an error message
function output_error_hook(...)
- return string.format(...)
+ return string.format(...) -- Note that this line must not end in the triple-dot-parenthesis due to pre-parsing
end
-- custom pushers
diff --git a/lib/tolua++/src/bin/lua/compat-5.1.lua b/lib/tolua++/src/bin/lua/compat-5.1.lua
index 7a2c60b69..c591592a6 100644
--- a/lib/tolua++/src/bin/lua/compat-5.1.lua
+++ b/lib/tolua++/src/bin/lua/compat-5.1.lua
@@ -18,17 +18,16 @@ local function pp_dofile(path)
local ret = file:read("*a")
file:close()
- ret = string.gsub(ret, "%.%.%.%s*%)", "...) local arg = {n=select('#', ...), ...};")
-
+ ret = string.gsub(ret, "%.%.%.%s*%)$", "...) local arg = {n=select('#', ...), ...};")
+
loaded = true
return ret
end
end
- local f = load(getfile, path)
+ local f, err = load(getfile, path)
if not f then
-
- error("error loading file "..path)
+ error("error loading file " .. path .. ": " .. err)
end
return f()
end
diff --git a/lib/tolua++/src/bin/lua/declaration.lua b/lib/tolua++/src/bin/lua/declaration.lua
index 26ceeba22..3ec6e144f 100644
--- a/lib/tolua++/src/bin/lua/declaration.lua
+++ b/lib/tolua++/src/bin/lua/declaration.lua
@@ -524,7 +524,7 @@ function Declaration (s,kind,is_parameter)
end
-- check the form: mod type* name
- local s1 = gsub(s,"(%b\[\])",function (n) return gsub(n,'%*','\1') end)
+ local s1 = gsub(s,"(%b%[%])",function (n) return gsub(n,'%*','\1') end)
t = split_c_tokens(s1,'%*')
if t.n == 2 then
t[2] = gsub(t[2],'\1','%*') -- restore * in dimension expression
diff --git a/lib/tolua++/src/bin/lua/feature.lua b/lib/tolua++/src/bin/lua/feature.lua
index 042b5d28e..14f01d325 100644
--- a/lib/tolua++/src/bin/lua/feature.lua
+++ b/lib/tolua++/src/bin/lua/feature.lua
@@ -132,7 +132,7 @@ function classFeature:cfuncname (n)
if not fname or fname == '' then
fname = self.name
end
- n = string.gsub(n..'_'.. (fname), "[<>:, \.%*&]", "_")
+ n = string.gsub(n..'_'.. (fname), "[<>:, %.%*&]", "_")
return n
end
diff --git a/src/Bindings/AllToLua_lua.bat.bat b/src/Bindings/AllToLua_lua.bat.bat
new file mode 100644
index 000000000..81c738f32
--- /dev/null
+++ b/src/Bindings/AllToLua_lua.bat.bat
@@ -0,0 +1,27 @@
+
+:: AllToLua_Lua.bat
+:: This scripts updates the automatically-generates Lua bindings in Bindings.cpp / Bindings.h
+:: When called without any parameters, it will pause for a keypress at the end
+:: Call with any parameter to disable the wait (for buildserver use)
+:: This script assumes "lua" executable to be in PATH, it uses a pure-lua implementation of the ToLua processor
+
+@echo off
+
+
+
+
+
+:: Regenerate the files:
+echo Regenerating LUA bindings . . .
+lua ..\..\lib\tolua++\src\bin\lua\_driver.lua -L virtual_method_hooks.lua -o Bindings.cpp -H Bindings.h AllToLua.pkg
+
+
+
+
+: Wait for keypress, if no param given:
+echo.
+if %ALLTOLUA_WAIT%N == N pause
+
+
+
+
diff --git a/src/BlockArea.cpp b/src/BlockArea.cpp
index e3a859e4f..4fe6cd51e 100644
--- a/src/BlockArea.cpp
+++ b/src/BlockArea.cpp
@@ -309,6 +309,14 @@ void cBlockArea::Clear(void)
void cBlockArea::Create(int a_SizeX, int a_SizeY, int a_SizeZ, int a_DataTypes)
{
+ if ((a_SizeX < 0) || (a_SizeY < 0) || (a_SizeZ < 0))
+ {
+ LOGWARNING("Creating a cBlockArea with a negative size! Call to Create ignored. (%d, %d, %d)",
+ a_SizeX, a_SizeY, a_SizeZ
+ );
+ return;
+ }
+
Clear();
int BlockCount = a_SizeX * a_SizeY * a_SizeZ;
if ((a_DataTypes & baTypes) != 0)
diff --git a/src/BlockID.h b/src/BlockID.h
index 97c1aae86..24b69e41b 100644
--- a/src/BlockID.h
+++ b/src/BlockID.h
@@ -790,6 +790,7 @@ enum eDimension
dimNether = -1,
dimOverworld = 0,
dimEnd = 1,
+ dimNotSet = 255, // For things that need an "indeterminate" state, such as cProtocol's LastSentDimension
} ;
diff --git a/src/Blocks/BlockDoor.cpp b/src/Blocks/BlockDoor.cpp
index 479c68153..fb2d6f2dc 100644
--- a/src/Blocks/BlockDoor.cpp
+++ b/src/Blocks/BlockDoor.cpp
@@ -62,7 +62,7 @@ void cBlockDoorHandler::OnCancelRightClick(cChunkInterface & a_ChunkInterface, c
a_WorldInterface.SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, a_Player);
NIBBLETYPE Meta = a_ChunkInterface.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ);
- if (Meta & 8)
+ if (Meta & 0x8)
{
// Current block is top of the door
a_WorldInterface.SendBlockTo(a_BlockX, a_BlockY - 1, a_BlockZ, a_Player);
diff --git a/src/Blocks/BlockDoor.h b/src/Blocks/BlockDoor.h
index 797fe484c..bc59051c3 100644
--- a/src/Blocks/BlockDoor.h
+++ b/src/Blocks/BlockDoor.h
@@ -20,12 +20,12 @@ public:
virtual void OnUse(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override;
virtual void OnCancelRightClick(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace) override;
virtual const char * GetStepSound(void) override;
-
+
virtual NIBBLETYPE MetaRotateCCW(NIBBLETYPE a_Meta) override;
virtual NIBBLETYPE MetaRotateCW(NIBBLETYPE a_Meta) override;
virtual NIBBLETYPE MetaMirrorXY(NIBBLETYPE a_Meta) override;
virtual NIBBLETYPE MetaMirrorYZ(NIBBLETYPE a_Meta) override;
-
+
virtual bool GetPlacementBlockTypeMeta(
cChunkInterface & a_ChunkInterface, cPlayer * a_Player,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace,
@@ -52,7 +52,7 @@ public:
return true;
}
-
+
virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
{
a_Pickups.push_back(cItem((m_BlockType == E_BLOCK_WOODEN_DOOR) ? E_ITEM_WOODEN_DOOR : E_ITEM_IRON_DOOR, 1, 0));
@@ -77,8 +77,8 @@ public:
{
return ((a_RelY > 0) && (a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ) != E_BLOCK_AIR));
}
-
-
+
+
bool CanReplaceBlock(BLOCKTYPE a_BlockType)
{
switch (a_BlockType)
@@ -99,7 +99,7 @@ public:
}
- /// Converts the player's yaw to placed door's blockmeta
+ /** Converts the player's yaw to placed door's blockmeta */
inline static NIBBLETYPE PlayerYawToMetaData(double a_Yaw)
{
ASSERT((a_Yaw >= -180) && (a_Yaw < 180));
@@ -111,67 +111,109 @@ public:
}
if ((a_Yaw >= 0) && (a_Yaw < 90))
{
- return 0x0;
+ return 0x00;
}
else if ((a_Yaw >= 180) && (a_Yaw < 270))
{
- return 0x2;
+ return 0x02;
}
else if ((a_Yaw >= 90) && (a_Yaw < 180))
{
- return 0x1;
+ return 0x01;
}
else
{
- return 0x3;
+ return 0x03;
}
}
- /// Returns true if the specified blocktype is any kind of door
+ /** Returns true if the specified blocktype is any kind of door */
inline static bool IsDoor(BLOCKTYPE a_Block)
{
return (a_Block == E_BLOCK_WOODEN_DOOR) || (a_Block == E_BLOCK_IRON_DOOR);
}
- /// Returns the metadata for the opposite door state (open vs closed)
- static NIBBLETYPE ChangeStateMetaData(NIBBLETYPE a_MetaData)
+ static NIBBLETYPE IsOpen(cChunkInterface & a_ChunkInterface, int a_BlockX, int a_BlockY, int a_BlockZ)
{
- return a_MetaData ^ 4;
+ NIBBLETYPE Meta = GetCompleteDoorMeta(a_ChunkInterface, a_BlockX, a_BlockY, a_BlockZ);
+ return ((Meta & 0x04) != 0);
}
- /// Changes the door at the specified coords from open to close or vice versa
- static void ChangeDoor(cChunkInterface & a_ChunkInterface, int a_X, int a_Y, int a_Z)
+ /** Returns the complete meta composed from the both parts of the door as (TopMeta << 4) | BottomMeta
+ The coords may point to either part of the door.
+ The returned value has bit 3 (0x08) set iff the coords point to the top part of the door.
+ Fails gracefully for (invalid) doors on the world's top and bottom. */
+ static NIBBLETYPE GetCompleteDoorMeta(cChunkInterface & a_ChunkInterface, int a_BlockX, int a_BlockY, int a_BlockZ)
{
- NIBBLETYPE OldMetaData = a_ChunkInterface.GetBlockMeta(a_X, a_Y, a_Z);
-
- a_ChunkInterface.SetBlockMeta(a_X, a_Y, a_Z, ChangeStateMetaData(OldMetaData));
+ NIBBLETYPE Meta = a_ChunkInterface.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ);
- if (OldMetaData & 8)
+ if ((Meta & 0x08) != 0)
{
- // Current block is top of the door
- BLOCKTYPE BottomBlock = a_ChunkInterface.GetBlock(a_X, a_Y - 1, a_Z);
- NIBBLETYPE BottomMeta = a_ChunkInterface.GetBlockMeta(a_X, a_Y - 1, a_Z);
-
- if (IsDoor(BottomBlock) && !(BottomMeta & 8))
+ // The coords are pointing at the top part of the door
+ if (a_BlockX > 0)
{
- a_ChunkInterface.SetBlockMeta(a_X, a_Y - 1, a_Z, ChangeStateMetaData(BottomMeta));
+ NIBBLETYPE DownMeta = a_ChunkInterface.GetBlockMeta(a_BlockX, a_BlockY - 1, a_BlockZ);
+ return (DownMeta & 0x07) | 0x08 | (Meta << 4);
}
+ // This is the top part of the door at the bottommost layer of the world, there's no bottom:
+ return 0x08 | (Meta << 4);
}
else
{
- // Current block is bottom of the door
- BLOCKTYPE TopBlock = a_ChunkInterface.GetBlock(a_X, a_Y + 1, a_Z);
- NIBBLETYPE TopMeta = a_ChunkInterface.GetBlockMeta(a_X, a_Y + 1, a_Z);
+ // The coords are pointing at the bottom part of the door
+ if (a_BlockY < cChunkDef::Height - 1)
+ {
+ NIBBLETYPE UpMeta = a_ChunkInterface.GetBlockMeta(a_BlockX, a_BlockY + 1, a_BlockZ);
+ return Meta | (UpMeta << 4);
+ }
+ // This is the bottom part of the door at the topmost layer of the world, there's no top:
+ return Meta;
+ }
+ }
- if (IsDoor(TopBlock) && (TopMeta & 8))
+
+ /** Sets the door to the specified state. If the door is already in that state, does nothing. */
+ static void SetOpen(cChunkInterface & a_ChunkInterface, int a_BlockX, int a_BlockY, int a_BlockZ, bool a_Open)
+ {
+ BLOCKTYPE Block = a_ChunkInterface.GetBlock(a_BlockX, a_BlockY, a_BlockZ);
+ if (!IsDoor(Block))
+ {
+ return;
+ }
+
+ NIBBLETYPE Meta = GetCompleteDoorMeta(a_ChunkInterface, a_BlockX, a_BlockY, a_BlockZ);
+ bool IsOpened = ((Meta & 0x04) != 0);
+ if (IsOpened == a_Open)
+ {
+ return;
+ }
+
+ // Change the door
+ NIBBLETYPE NewMeta = (Meta & 0x07) ^ 0x04; // Flip the "IsOpen" bit (0x04)
+ if ((Meta & 0x08) == 0)
+ {
+ // The block is the bottom part of the door
+ a_ChunkInterface.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, NewMeta);
+ }
+ else
+ {
+ // The block is the top part of the door, set the meta to the corresponding top part
+ if (a_BlockY > 0)
{
- a_ChunkInterface.SetBlockMeta(a_X, a_Y + 1, a_Z, ChangeStateMetaData(TopMeta));
+ a_ChunkInterface.SetBlockMeta(a_BlockX, a_BlockY - 1, a_BlockZ, NewMeta);
}
}
}
+
+
+ /** Changes the door at the specified coords from open to close or vice versa */
+ static void ChangeDoor(cChunkInterface & a_ChunkInterface, int a_BlockX, int a_BlockY, int a_BlockZ)
+ {
+ SetOpen(a_ChunkInterface, a_BlockX, a_BlockY, a_BlockZ, !IsOpen(a_ChunkInterface, a_BlockX, a_BlockY, a_BlockZ));
+ }
} ;
diff --git a/src/Chunk.cpp b/src/Chunk.cpp
index 02857ba5a..399c8b9c7 100644
--- a/src/Chunk.cpp
+++ b/src/Chunk.cpp
@@ -1518,6 +1518,7 @@ void cChunk::FastSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockT
}
MarkDirty();
+ m_IsRedstoneDirty = true;
m_ChunkData.SetBlock(a_RelX, a_RelY, a_RelZ, a_BlockType);
diff --git a/src/Chunk.h b/src/Chunk.h
index 92350243c..8123ac062 100644
--- a/src/Chunk.h
+++ b/src/Chunk.h
@@ -332,6 +332,7 @@ public:
if (hasChanged)
{
MarkDirty();
+ m_IsRedstoneDirty = true;
m_PendingSendBlocks.push_back(sSetBlock(m_PosX, m_PosZ, a_RelX, a_RelY, a_RelZ, GetBlock(a_RelX, a_RelY, a_RelZ), a_Meta));
}
@@ -378,10 +379,13 @@ public:
cSandSimulatorChunkData & GetSandSimulatorData (void) { return m_SandSimulatorData; }
cRedstoneSimulatorChunkData * GetRedstoneSimulatorData(void) { return &m_RedstoneSimulatorData; }
+ cRedstoneSimulatorChunkData * GetRedstoneSimulatorQueuedData(void) { return &m_RedstoneSimulatorQueuedData; }
cIncrementalRedstoneSimulator::PoweredBlocksList * GetRedstoneSimulatorPoweredBlocksList(void) { return &m_RedstoneSimulatorPoweredBlocksList; }
cIncrementalRedstoneSimulator::LinkedBlocksList * GetRedstoneSimulatorLinkedBlocksList(void) { return &m_RedstoneSimulatorLinkedBlocksList; };
cIncrementalRedstoneSimulator::SimulatedPlayerToggleableList * GetRedstoneSimulatorSimulatedPlayerToggleableList(void) { return &m_RedstoneSimulatorSimulatedPlayerToggleableList; };
cIncrementalRedstoneSimulator::RepeatersDelayList * GetRedstoneSimulatorRepeatersDelayList(void) { return &m_RedstoneSimulatorRepeatersDelayList; };
+ bool IsRedstoneDirty(void) const { return m_IsRedstoneDirty; }
+ void SetIsRedstoneDirty(bool a_Flag) { m_IsRedstoneDirty = a_Flag; }
cBlockEntity * GetBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ);
cBlockEntity * GetBlockEntity(const Vector3i & a_BlockPos) { return GetBlockEntity(a_BlockPos.x, a_BlockPos.y, a_BlockPos.z); }
@@ -450,13 +454,16 @@ private:
cSandSimulatorChunkData m_SandSimulatorData;
cRedstoneSimulatorChunkData m_RedstoneSimulatorData;
+ cRedstoneSimulatorChunkData m_RedstoneSimulatorQueuedData;
cIncrementalRedstoneSimulator::PoweredBlocksList m_RedstoneSimulatorPoweredBlocksList;
cIncrementalRedstoneSimulator::LinkedBlocksList m_RedstoneSimulatorLinkedBlocksList;
cIncrementalRedstoneSimulator::SimulatedPlayerToggleableList m_RedstoneSimulatorSimulatedPlayerToggleableList;
cIncrementalRedstoneSimulator::RepeatersDelayList m_RedstoneSimulatorRepeatersDelayList;
+ /** Indicates if simulate-once blocks should be updated by the redstone simulator */
+ bool m_IsRedstoneDirty;
- // pick up a random block of this chunk
+ // Pick up a random block of this chunk
void getRandomBlockCoords(int& a_X, int& a_Y, int& a_Z);
void getThreeRandomNumber(int& a_X, int& a_Y, int& a_Z,int a_MaxX, int a_MaxY, int a_MaxZ);
diff --git a/src/ChunkData.cpp b/src/ChunkData.cpp
index 1fa2fd212..f2d220bd2 100644
--- a/src/ChunkData.cpp
+++ b/src/ChunkData.cpp
@@ -258,7 +258,7 @@ bool cChunkData::SetMeta(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_Nibble
(m_Sections[Section]->m_BlockMetas[Index / 2] & (0xf0 >> ((Index & 1) * 4))) | // The untouched nibble
((a_Nibble & 0x0f) << ((Index & 1) * 4)) // The nibble being set
);
- return oldval == a_Nibble;
+ return oldval != a_Nibble;
}
diff --git a/src/ChunkMap.cpp b/src/ChunkMap.cpp
index 0a8164b47..dba6f3f41 100644
--- a/src/ChunkMap.cpp
+++ b/src/ChunkMap.cpp
@@ -1665,6 +1665,30 @@ void cChunkMap::AddEntity(cEntity * a_Entity)
+void cChunkMap::AddEntityIfNotPresent(cEntity * a_Entity)
+{
+ cCSLock Lock(m_CSLayers);
+ cChunkPtr Chunk = GetChunkNoGen(a_Entity->GetChunkX(), ZERO_CHUNK_Y, a_Entity->GetChunkZ());
+ if (
+ (Chunk == NULL) || // Chunk not present at all
+ (!Chunk->IsValid() && !a_Entity->IsPlayer()) // Chunk present, but no valid data; players need to spawn in such chunks (#953)
+ )
+ {
+ LOGWARNING("Entity at %p (%s, ID %d) spawning in a non-existent chunk, the entity is lost.",
+ a_Entity, a_Entity->GetClass(), a_Entity->GetUniqueID()
+ );
+ return;
+ }
+ if (!Chunk->HasEntity(a_Entity->GetUniqueID()))
+ {
+ Chunk->AddEntity(a_Entity);
+ }
+}
+
+
+
+
+
bool cChunkMap::HasEntity(int a_UniqueID)
{
cCSLock Lock(m_CSLayers);
diff --git a/src/ChunkMap.h b/src/ChunkMap.h
index 8786d7016..7e85bb6f1 100644
--- a/src/ChunkMap.h
+++ b/src/ChunkMap.h
@@ -199,6 +199,10 @@ public:
/** Adds the entity to its appropriate chunk, takes ownership of the entity pointer */
void AddEntity(cEntity * a_Entity);
+ /** Adds the entity to its appropriate chunk, if the entity is not already added.
+ Takes ownership of the entity pointer */
+ void AddEntityIfNotPresent(cEntity * a_Entity);
+
/** Returns true if the entity with specified ID is present in the chunks */
bool HasEntity(int a_EntityID);
diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp
index 1e2c148b8..256dad7da 100644
--- a/src/ClientHandle.cpp
+++ b/src/ClientHandle.cpp
@@ -326,7 +326,7 @@ void cClientHandle::Authenticate(const AString & a_Name, const AString & a_UUID)
// Send experience
m_Player->SendExperience();
- m_Player->Initialize(World);
+ m_Player->Initialize(*World);
m_State = csAuthenticated;
// Query player team
@@ -355,7 +355,7 @@ void cClientHandle::StreamChunks(void)
}
ASSERT(m_Player != NULL);
-
+
int ChunkPosX = FAST_FLOOR_DIV((int)m_Player->GetPosX(), cChunkDef::Width);
int ChunkPosZ = FAST_FLOOR_DIV((int)m_Player->GetPosZ(), cChunkDef::Width);
if ((ChunkPosX == m_LastStreamedChunkX) && (ChunkPosZ == m_LastStreamedChunkZ))
@@ -1088,18 +1088,25 @@ void cClientHandle::HandleRightClick(int a_BlockX, int a_BlockY, int a_BlockZ, e
cWorld * World = m_Player->GetWorld();
if (
- (Diff(m_Player->GetPosX(), (double)a_BlockX) > 6) ||
- (Diff(m_Player->GetPosY(), (double)a_BlockY) > 6) ||
- (Diff(m_Player->GetPosZ(), (double)a_BlockZ) > 6)
+ (a_BlockFace != BLOCK_FACE_NONE) && // The client is interacting with a specific block
+ (
+ (Diff(m_Player->GetPosX(), (double)a_BlockX) > 6) || // The block is too far away
+ (Diff(m_Player->GetPosY(), (double)a_BlockY) > 6) ||
+ (Diff(m_Player->GetPosZ(), (double)a_BlockZ) > 6)
+ )
)
{
- if (a_BlockFace != BLOCK_FACE_NONE)
+ AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace);
+ World->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, m_Player);
+ if (a_BlockY < cChunkDef::Height - 1)
{
- AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace);
- World->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, m_Player);
World->SendBlockTo(a_BlockX, a_BlockY + 1, a_BlockZ, m_Player); // 2 block high things
- m_Player->GetInventory().SendEquippedSlot();
}
+ if (a_BlockY > 0)
+ {
+ World->SendBlockTo(a_BlockX, a_BlockY - 1, a_BlockZ, m_Player); // 2 block high things
+ }
+ m_Player->GetInventory().SendEquippedSlot();
return;
}
@@ -1745,18 +1752,8 @@ void cClientHandle::SendData(const char * a_Data, size_t a_Size)
-void cClientHandle::MoveToWorld(cWorld & a_World, bool a_SendRespawnPacket)
+void cClientHandle::RemoveFromWorld(void)
{
- UNUSED(a_World);
- ASSERT(m_Player != NULL);
-
- if (a_SendRespawnPacket)
- {
- SendRespawn(a_World);
- }
-
- cWorld * World = m_Player->GetWorld();
-
// Remove all associated chunks:
cChunkCoordsList Chunks;
{
@@ -1766,7 +1763,6 @@ void cClientHandle::MoveToWorld(cWorld & a_World, bool a_SendRespawnPacket)
}
for (cChunkCoordsList::iterator itr = Chunks.begin(), end = Chunks.end(); itr != end; ++itr)
{
- World->RemoveChunkClient(itr->m_ChunkX, itr->m_ChunkZ, this);
m_Protocol->SendUnloadChunk(itr->m_ChunkX, itr->m_ChunkZ);
} // for itr - Chunks[]
diff --git a/src/ClientHandle.h b/src/ClientHandle.h
index e86287735..0d883f3af 100644
--- a/src/ClientHandle.h
+++ b/src/ClientHandle.h
@@ -250,8 +250,9 @@ public:
void SendData(const char * a_Data, size_t a_Size);
- /** Called when the player moves into a different world; queues sreaming the new chunks */
- void MoveToWorld(cWorld & a_World, bool a_SendRespawnPacket);
+ /** Called when the player moves into a different world.
+ Sends an UnloadChunk packet for each loaded chunk and resets the streamed chunks. */
+ void RemoveFromWorld(void);
/** Called when the player will enchant a Item */
void HandleEnchantItem(Byte & WindowID, Byte & Enchantment);
diff --git a/src/Entities/Entity.cpp b/src/Entities/Entity.cpp
index 4b376a1fe..334cf5aa7 100644
--- a/src/Entities/Entity.cpp
+++ b/src/Entities/Entity.cpp
@@ -130,9 +130,9 @@ const char * cEntity::GetParentClass(void) const
-bool cEntity::Initialize(cWorld * a_World)
+bool cEntity::Initialize(cWorld & a_World)
{
- if (cPluginManager::Get()->CallHookSpawningEntity(*a_World, *this))
+ if (cPluginManager::Get()->CallHookSpawningEntity(a_World, *this))
{
return false;
}
@@ -145,13 +145,13 @@ bool cEntity::Initialize(cWorld * a_World)
*/
m_IsInitialized = true;
- m_World = a_World;
+ m_World = &a_World;
m_World->AddEntity(this);
- cPluginManager::Get()->CallHookSpawnedEntity(*a_World, *this);
+ cPluginManager::Get()->CallHookSpawnedEntity(a_World, *this);
// Spawn the entity on the clients:
- a_World->BroadcastSpawnEntity(*this);
+ a_World.BroadcastSpawnEntity(*this);
return true;
}
diff --git a/src/Entities/Entity.h b/src/Entities/Entity.h
index b2317aaac..934e0302b 100644
--- a/src/Entities/Entity.h
+++ b/src/Entities/Entity.h
@@ -146,8 +146,9 @@ public:
cEntity(eEntityType a_EntityType, double a_X, double a_Y, double a_Z, double a_Width, double a_Height);
virtual ~cEntity();
- /// Spawns the entity in the world; returns true if spawned, false if not (plugin disallowed)
- virtual bool Initialize(cWorld * a_World);
+ /** Spawns the entity in the world; returns true if spawned, false if not (plugin disallowed).
+ Adds the entity to the world. */
+ virtual bool Initialize(cWorld & a_World);
// tolua_begin
@@ -430,6 +431,9 @@ public:
UNUSED(a_Killer);
}
+ /** Sets the internal world pointer to a new cWorld, doesn't update anything else. */
+ void SetWorld(cWorld * a_World) { m_World = a_World; }
+
protected:
static cCriticalSection m_CSCount;
static int m_EntityCount;
@@ -494,8 +498,6 @@ protected:
virtual void Destroyed(void) {} // Called after the entity has been destroyed
- void SetWorld(cWorld * a_World) { m_World = a_World; }
-
/** Called in each tick to handle air-related processing i.e. drowning */
virtual void HandleAir();
diff --git a/src/Entities/ItemFrame.cpp b/src/Entities/ItemFrame.cpp
index 9dd909880..7bc7bda8d 100644
--- a/src/Entities/ItemFrame.cpp
+++ b/src/Entities/ItemFrame.cpp
@@ -55,6 +55,7 @@ void cItemFrame::KilledBy(cEntity * a_Killer)
{
if (m_Item.IsEmpty())
{
+ SetHealth(0);
super::KilledBy(a_Killer);
Destroy();
return;
@@ -69,8 +70,9 @@ void cItemFrame::KilledBy(cEntity * a_Killer)
}
SetHealth(GetMaxHealth());
- m_Item.Clear();
+ m_Item.Empty();
m_Rotation = 0;
+ SetInvulnerableTicks(0);
GetWorld()->BroadcastEntityMetadata(*this);
}
diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp
index af4299e4b..4d6688694 100644
--- a/src/Entities/Player.cpp
+++ b/src/Entities/Player.cpp
@@ -934,6 +934,8 @@ void cPlayer::Killed(cEntity * a_Victim)
void cPlayer::Respawn(void)
{
+ ASSERT(m_World != NULL);
+
m_Health = GetMaxHealth();
SetInvulnerableTicks(20);
@@ -1590,24 +1592,22 @@ bool cPlayer::MoveToWorld(const AString & a_WorldName, cWorld * a_World)
return false;
}
- eDimension OldDimension = m_World->GetDimension();
-
+ // Send the respawn packet:
+ if (m_ClientHandle != NULL)
+ {
+ m_ClientHandle->SendRespawn(*World);
+ }
+
// Remove all links to the old world
m_World->RemovePlayer(this);
- m_ClientHandle->RemoveFromAllChunks();
// If the dimension is different, we can send the respawn packet
// http://wiki.vg/Protocol#0x09 says "don't send if dimension is the same" as of 2013_07_02
- bool SendRespawn = OldDimension != World->GetDimension();
- m_ClientHandle->MoveToWorld(*World, SendRespawn);
- // Add player to all the necessary parts of the new world
- SetWorld(World);
- m_ClientHandle->StreamChunks();
- World->AddEntity(this);
+ // Queue adding player to the new world, including all the necessary adjustments to the object
World->AddPlayer(this);
- if (SendRespawn)
+ if (GetWorld()->GetDimension() != World->GetDimension())
{
GetClientHandle()->SendPlayerMoveLook();
GetClientHandle()->SendHealth();
diff --git a/src/Entities/Player.h b/src/Entities/Player.h
index 39031fbfb..99a0e601c 100644
--- a/src/Entities/Player.h
+++ b/src/Entities/Player.h
@@ -328,6 +328,8 @@ public:
void SetVisible( bool a_bVisible ); // tolua_export
bool IsVisible(void) const { return m_bVisible; } // tolua_export
+ /** Moves the player to the specified world.
+ Returns true if successful, false on failure (world not found). */
virtual bool MoveToWorld(const AString & a_WorldName, cWorld * a_World = NULL) override; // tolua_export
/** Saves all player data, such as inventory, to JSON */
diff --git a/src/Generating/CompoGen.cpp b/src/Generating/CompoGen.cpp
index 578bb2481..688d19c40 100644
--- a/src/Generating/CompoGen.cpp
+++ b/src/Generating/CompoGen.cpp
@@ -561,10 +561,16 @@ void cCompoGenNether::ComposeTerrain(cChunkDesc & a_ChunkDesc)
// Interpolate the lowest floor:
for (int z = 0; z <= 16 / INTERPOL_Z; z++) for (int x = 0; x <= 16 / INTERPOL_X; x++)
{
- FloorLo[INTERPOL_X * x + 17 * INTERPOL_Z * z] =
+ //*
+ FloorLo[INTERPOL_X * x + 17 * INTERPOL_Z * z] =
m_Noise1.IntNoise3DInt(BaseX + INTERPOL_X * x, 0, BaseZ + INTERPOL_Z * z) *
m_Noise2.IntNoise3DInt(BaseX + INTERPOL_X * x, 0, BaseZ + INTERPOL_Z * z) /
256;
+ //*/
+ /*
+ FloorLo[INTERPOL_X * x + 17 * INTERPOL_Z * z] =
+ m_Noise1.IntNoise3DInt(BaseX + INTERPOL_X * x, 0, BaseZ + INTERPOL_Z * z) / 256;
+ //*/
} // for x, z - FloorLo[]
LinearUpscale2DArrayInPlace<17, 17, INTERPOL_X, INTERPOL_Z>(FloorLo);
@@ -574,10 +580,16 @@ void cCompoGenNether::ComposeTerrain(cChunkDesc & a_ChunkDesc)
// First update the high floor:
for (int z = 0; z <= 16 / INTERPOL_Z; z++) for (int x = 0; x <= 16 / INTERPOL_X; x++)
{
+ //*
FloorHi[INTERPOL_X * x + 17 * INTERPOL_Z * z] =
m_Noise1.IntNoise3DInt(BaseX + INTERPOL_X * x, Segment + SEGMENT_HEIGHT, BaseZ + INTERPOL_Z * z) *
m_Noise2.IntNoise3DInt(BaseX + INTERPOL_Z * x, Segment + SEGMENT_HEIGHT, BaseZ + INTERPOL_Z * z) /
256;
+ //*/
+ /*
+ FloorHi[INTERPOL_X * x + 17 * INTERPOL_Z * z] =
+ m_Noise1.IntNoise3DInt(BaseX + INTERPOL_X * x, Segment + SEGMENT_HEIGHT, BaseZ + INTERPOL_Z * z) / 256;
+ //*/
} // for x, z - FloorLo[]
LinearUpscale2DArrayInPlace<17, 17, INTERPOL_X, INTERPOL_Z>(FloorHi);
diff --git a/src/Generating/ComposableGenerator.cpp b/src/Generating/ComposableGenerator.cpp
index 37a6b829a..7f57343ad 100644
--- a/src/Generating/ComposableGenerator.cpp
+++ b/src/Generating/ComposableGenerator.cpp
@@ -24,6 +24,7 @@
#include "NetherFortGen.h"
#include "Noise3DGenerator.h"
#include "POCPieceGenerator.h"
+#include "RainbowRoadsGen.h"
#include "Ravines.h"
#include "UnderwaterBaseGen.h"
#include "VillageGen.h"
@@ -377,6 +378,13 @@ void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile)
{
m_FinishGens.push_back(new cFinishGenPreSimulator);
}
+ else if (NoCaseCompare(*itr, "RainbowRoads") == 0)
+ {
+ int GridSize = a_IniFile.GetValueSetI("Generator", "RainbowRoadsGridSize", 512);
+ int MaxDepth = a_IniFile.GetValueSetI("Generator", "RainbowRoadsMaxDepth", 30);
+ int MaxSize = a_IniFile.GetValueSetI("Generator", "RainbowRoadsMaxSize", 260);
+ m_FinishGens.push_back(new cRainbowRoadsGen(Seed, GridSize, MaxDepth, MaxSize));
+ }
else if (NoCaseCompare(*itr, "Ravines") == 0)
{
m_FinishGens.push_back(new cStructGenRavines(Seed, 128));
@@ -395,9 +403,9 @@ void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile)
}
else if (NoCaseCompare(*itr, "UnderwaterBases") == 0)
{
- int GridSize = a_IniFile.GetValueSetI("Generator", "UnderwaterBaseGridSize", 1024);
- int MaxDepth = a_IniFile.GetValueSetI("Generator", "UnderwaterBaseMaxDepth", 7);
- int MaxSize = a_IniFile.GetValueSetI("Generator", "UnderwaterBaseMaxSize", 128);
+ int GridSize = a_IniFile.GetValueSetI("Generator", "UnderwaterBaseGridSize", 1024);
+ int MaxDepth = a_IniFile.GetValueSetI("Generator", "UnderwaterBaseMaxDepth", 7);
+ int MaxSize = a_IniFile.GetValueSetI("Generator", "UnderwaterBaseMaxSize", 128);
m_FinishGens.push_back(new cUnderwaterBaseGen(Seed, GridSize, MaxDepth, MaxSize, *m_BiomeGen));
}
else if (NoCaseCompare(*itr, "Villages") == 0)
diff --git a/src/Generating/HeiGen.cpp b/src/Generating/HeiGen.cpp
index 3621421c2..25ac912fd 100644
--- a/src/Generating/HeiGen.cpp
+++ b/src/Generating/HeiGen.cpp
@@ -47,6 +47,10 @@ cTerrainHeightGen * cTerrainHeightGen::CreateHeightGen(cIniFile &a_IniFile, cBio
{
res = new cEndGen(a_Seed);
}
+ else if (NoCaseCompare(HeightGenName, "Mountains") == 0)
+ {
+ res = new cHeiGenMountains(a_Seed);
+ }
else if (NoCaseCompare(HeightGenName, "Noise3D") == 0)
{
res = new cNoise3DComposable(a_Seed);
@@ -301,6 +305,68 @@ void cHeiGenClassic::InitializeHeightGen(cIniFile & a_IniFile)
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cHeiGenMountains:
+
+cHeiGenMountains::cHeiGenMountains(int a_Seed) :
+ m_Seed(a_Seed),
+ m_Noise(a_Seed)
+{
+}
+
+
+
+
+
+void cHeiGenMountains::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap)
+{
+ NOISE_DATATYPE StartX = (NOISE_DATATYPE)(a_ChunkX * cChunkDef::Width);
+ NOISE_DATATYPE EndX = (NOISE_DATATYPE)(a_ChunkX * cChunkDef::Width + cChunkDef::Width - 1);
+ NOISE_DATATYPE StartZ = (NOISE_DATATYPE)(a_ChunkZ * cChunkDef::Width);
+ NOISE_DATATYPE EndZ = (NOISE_DATATYPE)(a_ChunkZ * cChunkDef::Width + cChunkDef::Width - 1);
+ NOISE_DATATYPE Workspace[16 * 16];
+ NOISE_DATATYPE Noise[16 * 16];
+ NOISE_DATATYPE PerlinNoise[16 * 16];
+ m_Noise.Generate2D(Noise, 16, 16, StartX, EndX, StartZ, EndZ, Workspace);
+ m_Perlin.Generate2D(PerlinNoise, 16, 16, StartX, EndX, StartZ, EndZ, Workspace);
+ for (int z = 0; z < cChunkDef::Width; z++)
+ {
+ int IdxZ = z * cChunkDef::Width;
+ for (int x = 0; x < cChunkDef::Width; x++)
+ {
+ int idx = IdxZ + x;
+ int hei = 100 - (int)((Noise[idx] + PerlinNoise[idx]) * 15);
+ if (hei < 10)
+ {
+ hei = 10;
+ }
+ if (hei > 250)
+ {
+ hei = 250;
+ }
+ cChunkDef::SetHeight(a_HeightMap, x , z, hei);
+ } // for x
+ } // for z
+}
+
+
+
+
+
+void cHeiGenMountains::InitializeHeightGen(cIniFile & a_IniFile)
+{
+ // TODO: Read the params from an INI file
+ m_Noise.AddOctave(0.1f, 0.1f);
+ m_Noise.AddOctave(0.05f, 0.5f);
+ m_Noise.AddOctave(0.02f, 1.5f);
+
+ m_Perlin.AddOctave(0.01f, 1.5f);
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cHeiGenBiomal:
const cHeiGenBiomal::sGenParam cHeiGenBiomal::m_GenParam[256] =
diff --git a/src/Generating/HeiGen.h b/src/Generating/HeiGen.h
index 1376f2a25..5c106c7d9 100644
--- a/src/Generating/HeiGen.h
+++ b/src/Generating/HeiGen.h
@@ -106,6 +106,27 @@ protected:
+class cHeiGenMountains :
+ public cTerrainHeightGen
+{
+public:
+ cHeiGenMountains(int a_Seed);
+
+protected:
+
+ int m_Seed;
+ cRidgedMultiNoise m_Noise;
+ cPerlinNoise m_Perlin;
+
+ // cTerrainHeightGen overrides:
+ virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override;
+ virtual void InitializeHeightGen(cIniFile & a_IniFile) override;
+} ;
+
+
+
+
+
class cHeiGenBiomal :
public cTerrainHeightGen
{
diff --git a/src/Generating/Prefabs/RainbowRoadPrefabs.cpp b/src/Generating/Prefabs/RainbowRoadPrefabs.cpp
new file mode 100644
index 000000000..1a3765c5a
--- /dev/null
+++ b/src/Generating/Prefabs/RainbowRoadPrefabs.cpp
@@ -0,0 +1,1406 @@
+
+// RainbowRoadPrefabs.cpp
+
+// Defines the prefabs in the group RainbowRoad
+
+// NOTE: This file has been generated automatically by GalExport!
+// Any manual changes will be overwritten by the next automatic export!
+
+#include "Globals.h"
+#include "RainbowRoadPrefabs.h"
+
+
+
+
+
+const cPrefab::sDef g_RainbowRoadPrefabs[] =
+{
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // CurveDouble:
+ // The data has been exported from the gallery Cube, area index 89, ID 467, created by Aloe_vera
+ {
+ // Size:
+ 14, 1, 14, // SizeX = 14, SizeY = 1, SizeZ = 14
+
+ // Hitbox (relative to bounding box):
+ 0, -2, 0, // MinX, MinY, MinZ
+ 13, 2, 13, // MaxX, MaxY, MaxZ
+
+ // Block definitions:
+ ".: 0: 0\n" /* air */
+ "a: 35:11\n" /* wool */
+ "b: 35: 3\n" /* wool */
+ "c: 35: 5\n" /* wool */
+ "d: 35: 4\n" /* wool */
+ "e: 35: 1\n" /* wool */
+ "f: 35:14\n" /* wool */
+ "m: 19: 0\n" /* sponge */,
+
+ // Block data:
+ // Level 0
+ /* z\x* 1111 */
+ /* * 01234567890123 */
+ /* 0 */ "aaaaaa........"
+ /* 1 */ "bbbbbba......."
+ /* 2 */ "cccccbbaaa...."
+ /* 3 */ "dddddccbbaa..."
+ /* 4 */ "eeeeeddccbaa.."
+ /* 5 */ "fffffeddccba.."
+ /* 6 */ "ffffffeedcbaa."
+ /* 7 */ "eeeefffeddcba."
+ /* 8 */ "dddeefffedcbba"
+ /* 9 */ "cccddefffedcba"
+ /* 10 */ "bbccdeeffedcba"
+ /* 11 */ "abbccdeffedcba"
+ /* 12 */ ".abbcdeffedcba"
+ /* 13 */ "..abcdeffedcba",
+
+ // Connectors:
+ "2: 2, 1, 13: 3\n" /* Type 2, direction Z+ */
+ "2: 0, 1, 0: 4\n" /* Type 2, direction X- */
+ "-2: 0, 1, 11: 4\n" /* Type -2, direction X- */
+ "-2: 13, 1, 13: 3\n" /* Type -2, direction Z+ */,
+
+ // AllowedRotations:
+ 7, /* 1, 2, 3 CCW rotation allowed */
+
+ // Merge strategy:
+ cBlockArea::msSpongePrint,
+
+ // ShouldExtendFloor:
+ false,
+
+ // DefaultWeight:
+ 100,
+
+ // DepthWeight:
+ "",
+
+ // AddWeightIfSame:
+ 0,
+
+ // MoveToGround:
+ false,
+ }, // CurveDouble
+
+
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // CurveDownFromTopSingle:
+ // The data has been exported from the gallery Cube, area index 100, ID 478, created by Aloe_vera
+ {
+ // Size:
+ 11, 8, 11, // SizeX = 11, SizeY = 8, SizeZ = 11
+
+ // Hitbox (relative to bounding box):
+ 0, -2, 0, // MinX, MinY, MinZ
+ 10, 9, 10, // MaxX, MaxY, MaxZ
+
+ // Block definitions:
+ ".: 0: 0\n" /* air */
+ "a: 35:14\n" /* wool */
+ "b: 35: 5\n" /* wool */
+ "c: 35: 4\n" /* wool */
+ "d: 35: 1\n" /* wool */
+ "e: 35:11\n" /* wool */
+ "f: 35: 3\n" /* wool */
+ "m: 19: 0\n" /* sponge */,
+
+ // Block data:
+ // Level 0
+ /* z\x* 1 */
+ /* * 01234567890 */
+ /* 0 */ "..........."
+ /* 1 */ "..........."
+ /* 2 */ "..........."
+ /* 3 */ "..........."
+ /* 4 */ "..........."
+ /* 5 */ "..........."
+ /* 6 */ "..........."
+ /* 7 */ "..........."
+ /* 8 */ "..........a"
+ /* 9 */ ".......bcda"
+ /* 10 */ ".....efbcda"
+
+ // Level 1
+ /* z\x* 1 */
+ /* * 01234567890 */
+ /* 0 */ "..........."
+ /* 1 */ "..........."
+ /* 2 */ "..........."
+ /* 3 */ "..........."
+ /* 4 */ "..........."
+ /* 5 */ "..........."
+ /* 6 */ "........cda"
+ /* 7 */ ".......bcda"
+ /* 8 */ ".......bcd."
+ /* 9 */ ".....ef...."
+ /* 10 */ "..........."
+
+ // Level 2
+ /* z\x* 1 */
+ /* * 01234567890 */
+ /* 0 */ "..........."
+ /* 1 */ "..........."
+ /* 2 */ "..........."
+ /* 3 */ "..........."
+ /* 4 */ ".........a."
+ /* 5 */ ".......cdda"
+ /* 6 */ "......bc..."
+ /* 7 */ "......b...."
+ /* 8 */ ".....ff...."
+ /* 9 */ "....ee....."
+ /* 10 */ "..........."
+
+ // Level 3
+ /* z\x* 1 */
+ /* * 01234567890 */
+ /* 0 */ "..........."
+ /* 1 */ "..........."
+ /* 2 */ "..........."
+ /* 3 */ "........aa."
+ /* 4 */ "......ccd.."
+ /* 5 */ ".....bb...."
+ /* 6 */ ".....f....."
+ /* 7 */ "....ef....."
+ /* 8 */ "....e......"
+ /* 9 */ "..........."
+ /* 10 */ "..........."
+
+ // Level 4
+ /* z\x* 1 */
+ /* * 01234567890 */
+ /* 0 */ "..........."
+ /* 1 */ "..........."
+ /* 2 */ "......daa.."
+ /* 3 */ ".....ccd..."
+ /* 4 */ "....bb....."
+ /* 5 */ "....f......"
+ /* 6 */ "...ef......"
+ /* 7 */ "...ee......"
+ /* 8 */ "..........."
+ /* 9 */ "..........."
+ /* 10 */ "..........."
+
+ // Level 5
+ /* z\x* 1 */
+ /* * 01234567890 */
+ /* 0 */ "..........."
+ /* 1 */ ".....daa..."
+ /* 2 */ "...ccd....."
+ /* 3 */ "...bc......"
+ /* 4 */ "...b......."
+ /* 5 */ "..ff......."
+ /* 6 */ "..ee......."
+ /* 7 */ "..........."
+ /* 8 */ "..........."
+ /* 9 */ "..........."
+ /* 10 */ "..........."
+
+ // Level 6
+ /* z\x* 1 */
+ /* * 01234567890 */
+ /* 0 */ "...aaa....."
+ /* 1 */ "..ddd......"
+ /* 2 */ ".cc........"
+ /* 3 */ ".bb........"
+ /* 4 */ ".ff........"
+ /* 5 */ ".e........."
+ /* 6 */ ".ee........"
+ /* 7 */ "..........."
+ /* 8 */ "..........."
+ /* 9 */ "..........."
+ /* 10 */ "..........."
+
+ // Level 7
+ /* z\x* 1 */
+ /* * 01234567890 */
+ /* 0 */ "aaa........"
+ /* 1 */ "dd........."
+ /* 2 */ "cc........."
+ /* 3 */ "bb........."
+ /* 4 */ "ff........."
+ /* 5 */ "e.........."
+ /* 6 */ "..........."
+ /* 7 */ "..........."
+ /* 8 */ "..........."
+ /* 9 */ "..........."
+ /* 10 */ "...........",
+
+ // Connectors:
+ "-1: 0, 8, 5: 4\n" /* Type -1, direction X- */
+ "1: 5, 1, 10: 3\n" /* Type 1, direction Z+ */,
+
+ // AllowedRotations:
+ 7, /* 1, 2, 3 CCW rotation allowed */
+
+ // Merge strategy:
+ cBlockArea::msSpongePrint,
+
+ // ShouldExtendFloor:
+ false,
+
+ // DefaultWeight:
+ 100,
+
+ // DepthWeight:
+ "",
+
+ // AddWeightIfSame:
+ 0,
+
+ // MoveToGround:
+ false,
+ }, // CurveDownFromTopSingle
+
+
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // CurveSingle:
+ // The data has been exported from the gallery Cube, area index 84, ID 462, created by Aloe_vera
+ {
+ // Size:
+ 11, 1, 11, // SizeX = 11, SizeY = 1, SizeZ = 11
+
+ // Hitbox (relative to bounding box):
+ 0, -2, 0, // MinX, MinY, MinZ
+ 10, 2, 10, // MaxX, MaxY, MaxZ
+
+ // Block definitions:
+ ".: 0: 0\n" /* air */
+ "a: 35:14\n" /* wool */
+ "b: 35: 1\n" /* wool */
+ "c: 35: 4\n" /* wool */
+ "d: 35: 5\n" /* wool */
+ "e: 35: 3\n" /* wool */
+ "f: 35:11\n" /* wool */
+ "m: 19: 0\n" /* sponge */,
+
+ // Block data:
+ // Level 0
+ /* z\x* 1 */
+ /* * 01234567890 */
+ /* 0 */ "aaaaaa....."
+ /* 1 */ "bbbbbbaa..."
+ /* 2 */ "cccccbbaa.."
+ /* 3 */ "ddddcccbaa."
+ /* 4 */ "eeedddccba."
+ /* 5 */ "ffeeeddcbba"
+ /* 6 */ ".fffeedccba"
+ /* 7 */ "...ffeddcba"
+ /* 8 */ "....feedcba"
+ /* 9 */ "....ffedcba"
+ /* 10 */ ".....fedcba",
+
+ // Connectors:
+ "-1: 0, 1, 5: 4\n" /* Type -1, direction X- */
+ "1: 5, 1, 10: 3\n" /* Type 1, direction Z+ */,
+
+ // AllowedRotations:
+ 7, /* 1, 2, 3 CCW rotation allowed */
+
+ // Merge strategy:
+ cBlockArea::msSpongePrint,
+
+ // ShouldExtendFloor:
+ false,
+
+ // DefaultWeight:
+ 100,
+
+ // DepthWeight:
+ "",
+
+ // AddWeightIfSame:
+ 0,
+
+ // MoveToGround:
+ false,
+ }, // CurveSingle
+
+
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // CurveSingleLeft:
+ // The data has been exported from the gallery Cube, area index 97, ID 475, created by Aloe_vera
+ {
+ // Size:
+ 11, 1, 11, // SizeX = 11, SizeY = 1, SizeZ = 11
+
+ // Hitbox (relative to bounding box):
+ 0, -2, 0, // MinX, MinY, MinZ
+ 10, 2, 10, // MaxX, MaxY, MaxZ
+
+ // Block definitions:
+ ".: 0: 0\n" /* air */
+ "a: 35:14\n" /* wool */
+ "b: 35: 1\n" /* wool */
+ "c: 35: 4\n" /* wool */
+ "d: 35: 5\n" /* wool */
+ "e: 35: 3\n" /* wool */
+ "f: 35:11\n" /* wool */
+ "m: 19: 0\n" /* sponge */,
+
+ // Block data:
+ // Level 0
+ /* z\x* 1 */
+ /* * 01234567890 */
+ /* 0 */ ".....abcdef"
+ /* 1 */ "....aabcdef"
+ /* 2 */ "....abbcdef"
+ /* 3 */ "...aabccdef"
+ /* 4 */ ".aaabbcddef"
+ /* 5 */ "aabbbccdeef"
+ /* 6 */ "bbbcccddef."
+ /* 7 */ "ccccdddeff."
+ /* 8 */ "dddddeeff.."
+ /* 9 */ "eeeeeeff..."
+ /* 10 */ "ffffff.....",
+
+ // Connectors:
+ "-1: 0, 1, 10: 4\n" /* Type -1, direction X- */
+ "1: 10, 1, 0: 2\n" /* Type 1, direction Z- */,
+
+ // AllowedRotations:
+ 7, /* 1, 2, 3 CCW rotation allowed */
+
+ // Merge strategy:
+ cBlockArea::msSpongePrint,
+
+ // ShouldExtendFloor:
+ false,
+
+ // DefaultWeight:
+ 100,
+
+ // DepthWeight:
+ "",
+
+ // AddWeightIfSame:
+ 0,
+
+ // MoveToGround:
+ false,
+ }, // CurveSingleLeft
+
+
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // CurveUpDouble:
+ // The data has been exported from the gallery Cube, area index 92, ID 470, created by Aloe_vera
+ {
+ // Size:
+ 14, 8, 14, // SizeX = 14, SizeY = 8, SizeZ = 14
+
+ // Hitbox (relative to bounding box):
+ 0, -2, 0, // MinX, MinY, MinZ
+ 13, 9, 13, // MaxX, MaxY, MaxZ
+
+ // Block definitions:
+ ".: 0: 0\n" /* air */
+ "a: 35:11\n" /* wool */
+ "b: 35: 3\n" /* wool */
+ "c: 35: 5\n" /* wool */
+ "d: 35: 4\n" /* wool */
+ "e: 35: 1\n" /* wool */
+ "f: 35:14\n" /* wool */
+ "m: 19: 0\n" /* sponge */,
+
+ // Block data:
+ // Level 0
+ /* z\x* 1111 */
+ /* * 01234567890123 */
+ /* 0 */ "a............."
+ /* 1 */ "b............."
+ /* 2 */ "c............."
+ /* 3 */ "d............."
+ /* 4 */ "e............."
+ /* 5 */ "f............."
+ /* 6 */ "f............."
+ /* 7 */ "e............."
+ /* 8 */ "d............."
+ /* 9 */ "c............."
+ /* 10 */ "b............."
+ /* 11 */ "a............."
+ /* 12 */ ".............."
+ /* 13 */ ".............."
+
+ // Level 1
+ /* z\x* 1111 */
+ /* * 01234567890123 */
+ /* 0 */ ".aa..........."
+ /* 1 */ ".bb..........."
+ /* 2 */ ".cc..........."
+ /* 3 */ ".dd..........."
+ /* 4 */ ".ee..........."
+ /* 5 */ ".f............"
+ /* 6 */ ".f............"
+ /* 7 */ ".e............"
+ /* 8 */ ".d............"
+ /* 9 */ ".c............"
+ /* 10 */ ".b............"
+ /* 11 */ ".b............"
+ /* 12 */ ".............."
+ /* 13 */ ".............."
+
+ // Level 2
+ /* z\x* 1111 */
+ /* * 01234567890123 */
+ /* 0 */ "...aaa........"
+ /* 1 */ "...bb........."
+ /* 2 */ "...cc........."
+ /* 3 */ "...dd........."
+ /* 4 */ "...ee........."
+ /* 5 */ "..ff.........."
+ /* 6 */ "..ff.........."
+ /* 7 */ "..ee.........."
+ /* 8 */ "..de.........."
+ /* 9 */ "..c..........."
+ /* 10 */ ".b............"
+ /* 11 */ ".b............"
+ /* 12 */ ".............."
+ /* 13 */ ".............."
+
+ // Level 3
+ /* z\x* 1111 */
+ /* * 01234567890123 */
+ /* 0 */ ".............."
+ /* 1 */ ".....baa......"
+ /* 2 */ ".....bbaaa...."
+ /* 3 */ "....dccbba...."
+ /* 4 */ "....eddcc....."
+ /* 5 */ "....fedd......"
+ /* 6 */ "....ffee......"
+ /* 7 */ "....ff........"
+ /* 8 */ "....e........."
+ /* 9 */ "...dd........."
+ /* 10 */ "..cc.........."
+ /* 11 */ "..b..........."
+ /* 12 */ ".a............"
+ /* 13 */ ".............."
+
+ // Level 4
+ /* z\x* 1111 */
+ /* * 01234567890123 */
+ /* 0 */ ".............."
+ /* 1 */ ".............."
+ /* 2 */ ".............."
+ /* 3 */ "..........a..."
+ /* 4 */ ".........ba..."
+ /* 5 */ "........cc...."
+ /* 6 */ ".......edc...."
+ /* 7 */ "......fedd...."
+ /* 8 */ ".....ff......."
+ /* 9 */ "....de........"
+ /* 10 */ "...cde........"
+ /* 11 */ "..b..........."
+ /* 12 */ ".a............"
+ /* 13 */ ".............."
+
+ // Level 5
+ /* z\x* 1111 */
+ /* * 01234567890123 */
+ /* 0 */ ".............."
+ /* 1 */ ".............."
+ /* 2 */ ".............."
+ /* 3 */ ".............."
+ /* 4 */ "...........a.."
+ /* 5 */ "..........ba.."
+ /* 6 */ "..........baa."
+ /* 7 */ "..........cba."
+ /* 8 */ ".......fedcb.."
+ /* 9 */ "......fffed..."
+ /* 10 */ ".....eef......"
+ /* 11 */ "...ccd........"
+ /* 12 */ "..b..........."
+ /* 13 */ ".............."
+
+ // Level 6
+ /* z\x* 1111 */
+ /* * 01234567890123 */
+ /* 0 */ ".............."
+ /* 1 */ ".............."
+ /* 2 */ ".............."
+ /* 3 */ ".............."
+ /* 4 */ ".............."
+ /* 5 */ ".............."
+ /* 6 */ ".............."
+ /* 7 */ ".............."
+ /* 8 */ "............ba"
+ /* 9 */ "...........cba"
+ /* 10 */ "........fedcba"
+ /* 11 */ "......effedc.."
+ /* 12 */ "..bbcdef......"
+ /* 13 */ "..a..........."
+
+ // Level 7
+ /* z\x* 1111 */
+ /* * 01234567890123 */
+ /* 0 */ ".............."
+ /* 1 */ ".............."
+ /* 2 */ ".............."
+ /* 3 */ ".............."
+ /* 4 */ ".............."
+ /* 5 */ ".............."
+ /* 6 */ ".............."
+ /* 7 */ ".............."
+ /* 8 */ ".............."
+ /* 9 */ ".............."
+ /* 10 */ ".............."
+ /* 11 */ "............ba"
+ /* 12 */ "........fedcba"
+ /* 13 */ "..abcdeffedcba",
+
+ // Connectors:
+ "-2: 0, 1, 11: 4\n" /* Type -2, direction X- */
+ "2: 0, 1, 0: 4\n" /* Type 2, direction X- */
+ "2: 2, 8, 13: 3\n" /* Type 2, direction Z+ */
+ "-2: 13, 8, 13: 3\n" /* Type -2, direction Z+ */,
+
+ // AllowedRotations:
+ 7, /* 1, 2, 3 CCW rotation allowed */
+
+ // Merge strategy:
+ cBlockArea::msSpongePrint,
+
+ // ShouldExtendFloor:
+ false,
+
+ // DefaultWeight:
+ 100,
+
+ // DepthWeight:
+ "",
+
+ // AddWeightIfSame:
+ 0,
+
+ // MoveToGround:
+ false,
+ }, // CurveUpDouble
+
+
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // CurveUpSingle:
+ // The data has been exported from the gallery Cube, area index 87, ID 465, created by Aloe_vera
+ {
+ // Size:
+ 11, 8, 11, // SizeX = 11, SizeY = 8, SizeZ = 11
+
+ // Hitbox (relative to bounding box):
+ 0, -2, 0, // MinX, MinY, MinZ
+ 10, 9, 10, // MaxX, MaxY, MaxZ
+
+ // Block definitions:
+ ".: 0: 0\n" /* air */
+ "a: 35:14\n" /* wool */
+ "b: 35: 1\n" /* wool */
+ "c: 35: 4\n" /* wool */
+ "d: 35: 5\n" /* wool */
+ "e: 35: 3\n" /* wool */
+ "f: 35:11\n" /* wool */
+ "m: 19: 0\n" /* sponge */,
+
+ // Block data:
+ // Level 0
+ /* z\x* 1 */
+ /* * 01234567890 */
+ /* 0 */ "aaa........"
+ /* 1 */ "bb........."
+ /* 2 */ "cc........."
+ /* 3 */ "dd........."
+ /* 4 */ "ee........."
+ /* 5 */ "f.........."
+ /* 6 */ "..........."
+ /* 7 */ "..........."
+ /* 8 */ "..........."
+ /* 9 */ "..........."
+ /* 10 */ "..........."
+
+ // Level 1
+ /* z\x* 1 */
+ /* * 01234567890 */
+ /* 0 */ "...aaa....."
+ /* 1 */ "..bbb......"
+ /* 2 */ ".cc........"
+ /* 3 */ ".dd........"
+ /* 4 */ ".ee........"
+ /* 5 */ ".f........."
+ /* 6 */ ".ff........"
+ /* 7 */ "..........."
+ /* 8 */ "..........."
+ /* 9 */ "..........."
+ /* 10 */ "..........."
+
+ // Level 2
+ /* z\x* 1 */
+ /* * 01234567890 */
+ /* 0 */ "..........."
+ /* 1 */ ".....baa..."
+ /* 2 */ "...ccb....."
+ /* 3 */ "...dc......"
+ /* 4 */ "...d......."
+ /* 5 */ "..ee......."
+ /* 6 */ "..ff......."
+ /* 7 */ "..........."
+ /* 8 */ "..........."
+ /* 9 */ "..........."
+ /* 10 */ "..........."
+
+ // Level 3
+ /* z\x* 1 */
+ /* * 01234567890 */
+ /* 0 */ "..........."
+ /* 1 */ "..........."
+ /* 2 */ "......baa.."
+ /* 3 */ ".....ccb..."
+ /* 4 */ "....dd....."
+ /* 5 */ "....e......"
+ /* 6 */ "...fe......"
+ /* 7 */ "...ff......"
+ /* 8 */ "..........."
+ /* 9 */ "..........."
+ /* 10 */ "..........."
+
+ // Level 4
+ /* z\x* 1 */
+ /* * 01234567890 */
+ /* 0 */ "..........."
+ /* 1 */ "..........."
+ /* 2 */ "..........."
+ /* 3 */ "........aa."
+ /* 4 */ "......ccb.."
+ /* 5 */ ".....dd...."
+ /* 6 */ ".....e....."
+ /* 7 */ "....fe....."
+ /* 8 */ "....f......"
+ /* 9 */ "..........."
+ /* 10 */ "..........."
+
+ // Level 5
+ /* z\x* 1 */
+ /* * 01234567890 */
+ /* 0 */ "..........."
+ /* 1 */ "..........."
+ /* 2 */ "..........."
+ /* 3 */ "..........."
+ /* 4 */ ".........a."
+ /* 5 */ ".......cbba"
+ /* 6 */ "......dc..."
+ /* 7 */ "......d...."
+ /* 8 */ ".....ee...."
+ /* 9 */ "....ff....."
+ /* 10 */ "..........."
+
+ // Level 6
+ /* z\x* 1 */
+ /* * 01234567890 */
+ /* 0 */ "..........."
+ /* 1 */ "..........."
+ /* 2 */ "..........."
+ /* 3 */ "..........."
+ /* 4 */ "..........."
+ /* 5 */ "..........."
+ /* 6 */ "........cba"
+ /* 7 */ ".......dcba"
+ /* 8 */ ".......dcb."
+ /* 9 */ ".....fe...."
+ /* 10 */ ".....f....."
+
+ // Level 7
+ /* z\x* 1 */
+ /* * 01234567890 */
+ /* 0 */ "..........."
+ /* 1 */ "..........."
+ /* 2 */ "..........."
+ /* 3 */ "..........."
+ /* 4 */ "..........."
+ /* 5 */ "..........."
+ /* 6 */ "..........."
+ /* 7 */ "..........."
+ /* 8 */ "..........a"
+ /* 9 */ ".......dcba"
+ /* 10 */ ".....fedcba",
+
+ // Connectors:
+ "-1: 0, 1, 5: 4\n" /* Type -1, direction X- */
+ "1: 5, 8, 10: 3\n" /* Type 1, direction Z+ */,
+
+ // AllowedRotations:
+ 7, /* 1, 2, 3 CCW rotation allowed */
+
+ // Merge strategy:
+ cBlockArea::msSpongePrint,
+
+ // ShouldExtendFloor:
+ false,
+
+ // DefaultWeight:
+ 100,
+
+ // DepthWeight:
+ "",
+
+ // AddWeightIfSame:
+ 0,
+
+ // MoveToGround:
+ false,
+ }, // CurveUpSingle
+
+
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // SlopeDownFromTopSingle:
+ // The data has been exported from the gallery Cube, area index 98, ID 476, created by Aloe_vera
+ {
+ // Size:
+ 16, 8, 6, // SizeX = 16, SizeY = 8, SizeZ = 6
+
+ // Hitbox (relative to bounding box):
+ 0, -2, 0, // MinX, MinY, MinZ
+ 15, 9, 5, // MaxX, MaxY, MaxZ
+
+ // Block definitions:
+ ".: 0: 0\n" /* air */
+ "a: 35:14\n" /* wool */
+ "b: 35: 1\n" /* wool */
+ "c: 35: 4\n" /* wool */
+ "d: 35: 5\n" /* wool */
+ "e: 35: 3\n" /* wool */
+ "f: 35:11\n" /* wool */
+ "m: 19: 0\n" /* sponge */,
+
+ // Block data:
+ // Level 0
+ /* z\x* 111111 */
+ /* * 0123456789012345 */
+ /* 0 */ "..............aa"
+ /* 1 */ "..............bb"
+ /* 2 */ "..............cc"
+ /* 3 */ "..............dd"
+ /* 4 */ "..............ee"
+ /* 5 */ "..............ff"
+
+ // Level 1
+ /* z\x* 111111 */
+ /* * 0123456789012345 */
+ /* 0 */ "............aa.."
+ /* 1 */ "............bb.."
+ /* 2 */ "............cc.."
+ /* 3 */ "............dd.."
+ /* 4 */ "............ee.."
+ /* 5 */ "............ff.."
+
+ // Level 2
+ /* z\x* 111111 */
+ /* * 0123456789012345 */
+ /* 0 */ "..........aa...."
+ /* 1 */ "..........bb...."
+ /* 2 */ "..........cc...."
+ /* 3 */ "..........dd...."
+ /* 4 */ "..........ee...."
+ /* 5 */ "..........ff...."
+
+ // Level 3
+ /* z\x* 111111 */
+ /* * 0123456789012345 */
+ /* 0 */ "........aa......"
+ /* 1 */ "........bb......"
+ /* 2 */ "........cc......"
+ /* 3 */ "........dd......"
+ /* 4 */ "........ee......"
+ /* 5 */ "........ff......"
+
+ // Level 4
+ /* z\x* 111111 */
+ /* * 0123456789012345 */
+ /* 0 */ "......aa........"
+ /* 1 */ "......bb........"
+ /* 2 */ "......cc........"
+ /* 3 */ "......dd........"
+ /* 4 */ "......ee........"
+ /* 5 */ "......ff........"
+
+ // Level 5
+ /* z\x* 111111 */
+ /* * 0123456789012345 */
+ /* 0 */ "....aa.........."
+ /* 1 */ "....bb.........."
+ /* 2 */ "....cc.........."
+ /* 3 */ "....dd.........."
+ /* 4 */ "....ee.........."
+ /* 5 */ "....ff.........."
+
+ // Level 6
+ /* z\x* 111111 */
+ /* * 0123456789012345 */
+ /* 0 */ "..aa............"
+ /* 1 */ "..bb............"
+ /* 2 */ "..cc............"
+ /* 3 */ "..dd............"
+ /* 4 */ "..ee............"
+ /* 5 */ "..ff............"
+
+ // Level 7
+ /* z\x* 111111 */
+ /* * 0123456789012345 */
+ /* 0 */ "aa.............."
+ /* 1 */ "bb.............."
+ /* 2 */ "cc.............."
+ /* 3 */ "dd.............."
+ /* 4 */ "ee.............."
+ /* 5 */ "ff..............",
+
+ // Connectors:
+ "-1: 0, 8, 5: 4\n" /* Type -1, direction X- */
+ "1: 15, 1, 5: 5\n" /* Type 1, direction X+ */,
+
+ // AllowedRotations:
+ 7, /* 1, 2, 3 CCW rotation allowed */
+
+ // Merge strategy:
+ cBlockArea::msSpongePrint,
+
+ // ShouldExtendFloor:
+ false,
+
+ // DefaultWeight:
+ 100,
+
+ // DepthWeight:
+ "",
+
+ // AddWeightIfSame:
+ 0,
+
+ // MoveToGround:
+ false,
+ }, // SlopeDownFromTopSingle
+
+
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // SlopeUpDouble:
+ // The data has been exported from the gallery Cube, area index 90, ID 468, created by Aloe_vera
+ {
+ // Size:
+ 16, 8, 12, // SizeX = 16, SizeY = 8, SizeZ = 12
+
+ // Hitbox (relative to bounding box):
+ 0, -2, 0, // MinX, MinY, MinZ
+ 15, 9, 11, // MaxX, MaxY, MaxZ
+
+ // Block definitions:
+ ".: 0: 0\n" /* air */
+ "a: 35:11\n" /* wool */
+ "b: 35: 3\n" /* wool */
+ "c: 35: 5\n" /* wool */
+ "d: 35: 4\n" /* wool */
+ "e: 35: 1\n" /* wool */
+ "f: 35:14\n" /* wool */
+ "m: 19: 0\n" /* sponge */,
+
+ // Block data:
+ // Level 0
+ /* z\x* 111111 */
+ /* * 0123456789012345 */
+ /* 0 */ "aa.............."
+ /* 1 */ "bb.............."
+ /* 2 */ "cc.............."
+ /* 3 */ "dd.............."
+ /* 4 */ "ee.............."
+ /* 5 */ "ff.............."
+ /* 6 */ "ff.............."
+ /* 7 */ "ee.............."
+ /* 8 */ "dd.............."
+ /* 9 */ "cc.............."
+ /* 10 */ "bb.............."
+ /* 11 */ "aa.............."
+
+ // Level 1
+ /* z\x* 111111 */
+ /* * 0123456789012345 */
+ /* 0 */ "..aa............"
+ /* 1 */ "..bb............"
+ /* 2 */ "..cc............"
+ /* 3 */ "..dd............"
+ /* 4 */ "..ee............"
+ /* 5 */ "..ff............"
+ /* 6 */ "..ff............"
+ /* 7 */ "..ee............"
+ /* 8 */ "..dd............"
+ /* 9 */ "..cc............"
+ /* 10 */ "..bb............"
+ /* 11 */ "..aa............"
+
+ // Level 2
+ /* z\x* 111111 */
+ /* * 0123456789012345 */
+ /* 0 */ "....aa.........."
+ /* 1 */ "....bb.........."
+ /* 2 */ "....cc.........."
+ /* 3 */ "....dd.........."
+ /* 4 */ "....ee.........."
+ /* 5 */ "....ff.........."
+ /* 6 */ "....ff.........."
+ /* 7 */ "....ee.........."
+ /* 8 */ "....dd.........."
+ /* 9 */ "....cc.........."
+ /* 10 */ "....bb.........."
+ /* 11 */ "....aa.........."
+
+ // Level 3
+ /* z\x* 111111 */
+ /* * 0123456789012345 */
+ /* 0 */ "......aa........"
+ /* 1 */ "......bb........"
+ /* 2 */ "......cc........"
+ /* 3 */ "......dd........"
+ /* 4 */ "......ee........"
+ /* 5 */ "......ff........"
+ /* 6 */ "......ff........"
+ /* 7 */ "......ee........"
+ /* 8 */ "......dd........"
+ /* 9 */ "......cc........"
+ /* 10 */ "......bb........"
+ /* 11 */ "......aa........"
+
+ // Level 4
+ /* z\x* 111111 */
+ /* * 0123456789012345 */
+ /* 0 */ "........aa......"
+ /* 1 */ "........bb......"
+ /* 2 */ "........cc......"
+ /* 3 */ "........dd......"
+ /* 4 */ "........ee......"
+ /* 5 */ "........ff......"
+ /* 6 */ "........ff......"
+ /* 7 */ "........ee......"
+ /* 8 */ "........dd......"
+ /* 9 */ "........cc......"
+ /* 10 */ "........bb......"
+ /* 11 */ "........aa......"
+
+ // Level 5
+ /* z\x* 111111 */
+ /* * 0123456789012345 */
+ /* 0 */ "..........aa...."
+ /* 1 */ "..........bb...."
+ /* 2 */ "..........cc...."
+ /* 3 */ "..........dd...."
+ /* 4 */ "..........ee...."
+ /* 5 */ "..........ff...."
+ /* 6 */ "..........ff...."
+ /* 7 */ "..........ee...."
+ /* 8 */ "..........dd...."
+ /* 9 */ "..........cc...."
+ /* 10 */ "..........bb...."
+ /* 11 */ "..........aa...."
+
+ // Level 6
+ /* z\x* 111111 */
+ /* * 0123456789012345 */
+ /* 0 */ "............aa.."
+ /* 1 */ "............bb.."
+ /* 2 */ "............cc.."
+ /* 3 */ "............dd.."
+ /* 4 */ "............ee.."
+ /* 5 */ "............ff.."
+ /* 6 */ "............ff.."
+ /* 7 */ "............ee.."
+ /* 8 */ "............dd.."
+ /* 9 */ "............cc.."
+ /* 10 */ "............bb.."
+ /* 11 */ "............aa.."
+
+ // Level 7
+ /* z\x* 111111 */
+ /* * 0123456789012345 */
+ /* 0 */ "..............aa"
+ /* 1 */ "..............bb"
+ /* 2 */ "..............cc"
+ /* 3 */ "..............dd"
+ /* 4 */ "..............ee"
+ /* 5 */ "..............ff"
+ /* 6 */ "..............ff"
+ /* 7 */ "..............ee"
+ /* 8 */ "..............dd"
+ /* 9 */ "..............cc"
+ /* 10 */ "..............bb"
+ /* 11 */ "..............aa",
+
+ // Connectors:
+ "-2: 0, 1, 11: 4\n" /* Type -2, direction X- */
+ "2: 0, 1, 0: 4\n" /* Type 2, direction X- */
+ "-2: 15, 8, 0: 5\n" /* Type -2, direction X+ */
+ "2: 15, 8, 11: 5\n" /* Type 2, direction X+ */,
+
+ // AllowedRotations:
+ 7, /* 1, 2, 3 CCW rotation allowed */
+
+ // Merge strategy:
+ cBlockArea::msSpongePrint,
+
+ // ShouldExtendFloor:
+ false,
+
+ // DefaultWeight:
+ 100,
+
+ // DepthWeight:
+ "",
+
+ // AddWeightIfSame:
+ 0,
+
+ // MoveToGround:
+ false,
+ }, // SlopeUpDouble
+
+
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // SlopeUpSingle:
+ // The data has been exported from the gallery Cube, area index 85, ID 463, created by Aloe_vera
+ {
+ // Size:
+ 16, 8, 6, // SizeX = 16, SizeY = 8, SizeZ = 6
+
+ // Hitbox (relative to bounding box):
+ 0, -2, 0, // MinX, MinY, MinZ
+ 15, 9, 5, // MaxX, MaxY, MaxZ
+
+ // Block definitions:
+ ".: 0: 0\n" /* air */
+ "a: 35:14\n" /* wool */
+ "b: 35: 1\n" /* wool */
+ "c: 35: 4\n" /* wool */
+ "d: 35: 5\n" /* wool */
+ "e: 35: 3\n" /* wool */
+ "f: 35:11\n" /* wool */
+ "m: 19: 0\n" /* sponge */,
+
+ // Block data:
+ // Level 0
+ /* z\x* 111111 */
+ /* * 0123456789012345 */
+ /* 0 */ "aa.............."
+ /* 1 */ "bb.............."
+ /* 2 */ "cc.............."
+ /* 3 */ "dd.............."
+ /* 4 */ "ee.............."
+ /* 5 */ "ff.............."
+
+ // Level 1
+ /* z\x* 111111 */
+ /* * 0123456789012345 */
+ /* 0 */ "..aa............"
+ /* 1 */ "..bb............"
+ /* 2 */ "..cc............"
+ /* 3 */ "..dd............"
+ /* 4 */ "..ee............"
+ /* 5 */ "..ff............"
+
+ // Level 2
+ /* z\x* 111111 */
+ /* * 0123456789012345 */
+ /* 0 */ "....aa.........."
+ /* 1 */ "....bb.........."
+ /* 2 */ "....cc.........."
+ /* 3 */ "....dd.........."
+ /* 4 */ "....ee.........."
+ /* 5 */ "....ff.........."
+
+ // Level 3
+ /* z\x* 111111 */
+ /* * 0123456789012345 */
+ /* 0 */ "......aa........"
+ /* 1 */ "......bb........"
+ /* 2 */ "......cc........"
+ /* 3 */ "......dd........"
+ /* 4 */ "......ee........"
+ /* 5 */ "......ff........"
+
+ // Level 4
+ /* z\x* 111111 */
+ /* * 0123456789012345 */
+ /* 0 */ "........aa......"
+ /* 1 */ "........bb......"
+ /* 2 */ "........cc......"
+ /* 3 */ "........dd......"
+ /* 4 */ "........ee......"
+ /* 5 */ "........ff......"
+
+ // Level 5
+ /* z\x* 111111 */
+ /* * 0123456789012345 */
+ /* 0 */ "..........aa...."
+ /* 1 */ "..........bb...."
+ /* 2 */ "..........cc...."
+ /* 3 */ "..........dd...."
+ /* 4 */ "..........ee...."
+ /* 5 */ "..........ff...."
+
+ // Level 6
+ /* z\x* 111111 */
+ /* * 0123456789012345 */
+ /* 0 */ "............aa.."
+ /* 1 */ "............bb.."
+ /* 2 */ "............cc.."
+ /* 3 */ "............dd.."
+ /* 4 */ "............ee.."
+ /* 5 */ "............ff.."
+
+ // Level 7
+ /* z\x* 111111 */
+ /* * 0123456789012345 */
+ /* 0 */ "..............aa"
+ /* 1 */ "..............bb"
+ /* 2 */ "..............cc"
+ /* 3 */ "..............dd"
+ /* 4 */ "..............ee"
+ /* 5 */ "..............ff",
+
+ // Connectors:
+ "-1: 0, 1, 5: 4\n" /* Type -1, direction X- */
+ "1: 15, 8, 5: 5\n" /* Type 1, direction X+ */,
+
+ // AllowedRotations:
+ 7, /* 1, 2, 3 CCW rotation allowed */
+
+ // Merge strategy:
+ cBlockArea::msSpongePrint,
+
+ // ShouldExtendFloor:
+ false,
+
+ // DefaultWeight:
+ 100,
+
+ // DepthWeight:
+ "",
+
+ // AddWeightIfSame:
+ -1000,
+
+ // MoveToGround:
+ false,
+ }, // SlopeUpSingle
+
+
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // SplitTee:
+ // The data has been exported from the gallery Cube, area index 93, ID 471, created by Aloe_vera
+ {
+ // Size:
+ 16, 1, 14, // SizeX = 16, SizeY = 1, SizeZ = 14
+
+ // Hitbox (relative to bounding box):
+ 0, -2, 0, // MinX, MinY, MinZ
+ 15, 2, 13, // MaxX, MaxY, MaxZ
+
+ // Block definitions:
+ ".: 0: 0\n" /* air */
+ "a: 35:11\n" /* wool */
+ "b: 35: 3\n" /* wool */
+ "c: 35: 5\n" /* wool */
+ "d: 35: 4\n" /* wool */
+ "e: 35: 1\n" /* wool */
+ "f: 35:14\n" /* wool */
+ "m: 19: 0\n" /* sponge */,
+
+ // Block data:
+ // Level 0
+ /* z\x* 111111 */
+ /* * 0123456789012345 */
+ /* 0 */ "aaaaaa.........."
+ /* 1 */ "bbbbbbaaa......."
+ /* 2 */ "ccccccbbbaaa...."
+ /* 3 */ "ddddddcccbbbaaaa"
+ /* 4 */ "eeeeeedddcccbbbb"
+ /* 5 */ "ffffffeeedddcccc"
+ /* 6 */ "fffffffffeeedddd"
+ /* 7 */ "eeeeff...fffeeee"
+ /* 8 */ "dddeeff.....ffff"
+ /* 9 */ "cccddeff........"
+ /* 10 */ "bbbccdeef......."
+ /* 11 */ "aaabbcddef......"
+ /* 12 */ "...aabcddef....."
+ /* 13 */ ".....abcdef.....",
+
+ // Connectors:
+ "-2: 0, 1, 11: 4\n" /* Type -2, direction X- */
+ "2: 0, 1, 0: 4\n" /* Type 2, direction X- */
+ "-1: 15, 1, 3: 5\n" /* Type -1, direction X+ */
+ "1: 5, 1, 13: 3\n" /* Type 1, direction Z+ */,
+
+ // AllowedRotations:
+ 7, /* 1, 2, 3 CCW rotation allowed */
+
+ // Merge strategy:
+ cBlockArea::msSpongePrint,
+
+ // ShouldExtendFloor:
+ false,
+
+ // DefaultWeight:
+ 100,
+
+ // DepthWeight:
+ "",
+
+ // AddWeightIfSame:
+ 0,
+
+ // MoveToGround:
+ false,
+ }, // SplitTee
+
+
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // StraightDouble:
+ // The data has been exported from the gallery Cube, area index 88, ID 466, created by Aloe_vera
+ {
+ // Size:
+ 16, 1, 12, // SizeX = 16, SizeY = 1, SizeZ = 12
+
+ // Hitbox (relative to bounding box):
+ 0, -2, 0, // MinX, MinY, MinZ
+ 15, 2, 11, // MaxX, MaxY, MaxZ
+
+ // Block definitions:
+ ".: 0: 0\n" /* air */
+ "a: 35:11\n" /* wool */
+ "b: 35: 3\n" /* wool */
+ "c: 35: 5\n" /* wool */
+ "d: 35: 4\n" /* wool */
+ "e: 35: 1\n" /* wool */
+ "f: 35:14\n" /* wool */
+ "m: 19: 0\n" /* sponge */,
+
+ // Block data:
+ // Level 0
+ /* z\x* 111111 */
+ /* * 0123456789012345 */
+ /* 0 */ "aaaaaaaaaaaaaaaa"
+ /* 1 */ "bbbbbbbbbbbbbbbb"
+ /* 2 */ "cccccccccccccccc"
+ /* 3 */ "dddddddddddddddd"
+ /* 4 */ "eeeeeeeeeeeeeeee"
+ /* 5 */ "ffffffffffffffff"
+ /* 6 */ "ffffffffffffffff"
+ /* 7 */ "eeeeeeeeeeeeeeee"
+ /* 8 */ "dddddddddddddddd"
+ /* 9 */ "cccccccccccccccc"
+ /* 10 */ "bbbbbbbbbbbbbbbb"
+ /* 11 */ "aaaaaaaaaaaaaaaa",
+
+ // Connectors:
+ "-2: 0, 1, 11: 4\n" /* Type -2, direction X- */
+ "2: 0, 1, 0: 4\n" /* Type 2, direction X- */
+ "-2: 15, 1, 0: 5\n" /* Type -2, direction X+ */
+ "2: 15, 1, 11: 5\n" /* Type 2, direction X+ */,
+
+ // AllowedRotations:
+ 7, /* 1, 2, 3 CCW rotation allowed */
+
+ // Merge strategy:
+ cBlockArea::msSpongePrint,
+
+ // ShouldExtendFloor:
+ false,
+
+ // DefaultWeight:
+ 100,
+
+ // DepthWeight:
+ "",
+
+ // AddWeightIfSame:
+ 0,
+
+ // MoveToGround:
+ false,
+ }, // StraightDouble
+}; // g_RainbowRoadPrefabs
+
+
+
+
+
+
+const cPrefab::sDef g_RainbowRoadStartingPrefabs[] =
+{
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // StraightSingle:
+ // The data has been exported from the gallery Cube, area index 83, ID 461, created by Aloe_vera
+ {
+ // Size:
+ 16, 1, 6, // SizeX = 16, SizeY = 1, SizeZ = 6
+
+ // Hitbox (relative to bounding box):
+ 0, -2, 0, // MinX, MinY, MinZ
+ 15, 2, 5, // MaxX, MaxY, MaxZ
+
+ // Block definitions:
+ ".: 0: 0\n" /* air */
+ "a: 35:14\n" /* wool */
+ "b: 35: 1\n" /* wool */
+ "c: 35: 4\n" /* wool */
+ "d: 35: 5\n" /* wool */
+ "e: 35: 3\n" /* wool */
+ "f: 35:11\n" /* wool */
+ "m: 19: 0\n" /* sponge */,
+
+ // Block data:
+ // Level 0
+ /* z\x* 111111 */
+ /* * 0123456789012345 */
+ /* 0 */ "aaaaaaaaaaaaaaaa"
+ /* 1 */ "bbbbbbbbbbbbbbbb"
+ /* 2 */ "cccccccccccccccc"
+ /* 3 */ "dddddddddddddddd"
+ /* 4 */ "eeeeeeeeeeeeeeee"
+ /* 5 */ "ffffffffffffffff",
+
+ // Connectors:
+ "-1: 0, 1, 5: 4\n" /* Type -1, direction X- */
+ "1: 15, 1, 5: 5\n" /* Type 1, direction X+ */,
+
+ // AllowedRotations:
+ 7, /* 1, 2, 3 CCW rotation allowed */
+
+ // Merge strategy:
+ cBlockArea::msSpongePrint,
+
+ // ShouldExtendFloor:
+ false,
+
+ // DefaultWeight:
+ 100,
+
+ // DepthWeight:
+ "",
+
+ // AddWeightIfSame:
+ 500,
+
+ // MoveToGround:
+ false,
+ }, // StraightSingle
+};
+
+
+
+
+
+// The prefab counts:
+
+const size_t g_RainbowRoadPrefabsCount = ARRAYCOUNT(g_RainbowRoadPrefabs);
+
+const size_t g_RainbowRoadStartingPrefabsCount = ARRAYCOUNT(g_RainbowRoadStartingPrefabs);
+
diff --git a/src/Generating/Prefabs/RainbowRoadPrefabs.h b/src/Generating/Prefabs/RainbowRoadPrefabs.h
new file mode 100644
index 000000000..ab0a0fbb2
--- /dev/null
+++ b/src/Generating/Prefabs/RainbowRoadPrefabs.h
@@ -0,0 +1,15 @@
+
+// RainbowRoadPrefabs.h
+
+// Declares the prefabs in the group RainbowRoad
+
+#include "../Prefab.h"
+
+
+
+
+
+extern const cPrefab::sDef g_RainbowRoadPrefabs[];
+extern const cPrefab::sDef g_RainbowRoadStartingPrefabs[];
+extern const size_t g_RainbowRoadPrefabsCount;
+extern const size_t g_RainbowRoadStartingPrefabsCount;
diff --git a/src/Generating/RainbowRoadsGen.cpp b/src/Generating/RainbowRoadsGen.cpp
new file mode 100644
index 000000000..d1e1f4bda
--- /dev/null
+++ b/src/Generating/RainbowRoadsGen.cpp
@@ -0,0 +1,115 @@
+
+// RainbowRoadsGen.cpp
+
+// Implements the cRainbowRoadsGen class representing the rainbow road generator
+
+#include "Globals.h"
+#include "RainbowRoadsGen.h"
+#include "Prefabs/RainbowRoadPrefabs.h"
+#include "PieceGenerator.h"
+
+
+
+
+
+static cPrefabPiecePool g_RainbowRoads(g_RainbowRoadPrefabs, g_RainbowRoadPrefabsCount, g_RainbowRoadStartingPrefabs, g_RainbowRoadStartingPrefabsCount);
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cRainbowRoadsGen::cRainbowRoads:
+
+class cRainbowRoadsGen::cRainbowRoads :
+ public cGridStructGen::cStructure
+{
+ typedef cGridStructGen::cStructure super;
+
+public:
+ cRainbowRoads(
+ int a_Seed,
+ int a_OriginX, int a_OriginZ,
+ int a_MaxDepth,
+ int a_MaxSize
+ ) :
+ super(a_OriginX, a_OriginZ),
+ m_Seed(a_Seed),
+ m_Noise(a_Seed),
+ m_MaxSize(a_MaxSize),
+ m_Borders(a_OriginX - a_MaxSize, 0, a_OriginZ - a_MaxSize, a_OriginX + a_MaxSize, 255, a_OriginZ + a_MaxSize)
+ {
+ // Generate the pieces for this base:
+ cBFSPieceGenerator pg(g_RainbowRoads, a_Seed);
+ pg.PlacePieces(a_OriginX, 190, a_OriginZ, a_MaxDepth, m_Pieces);
+ if (m_Pieces.empty())
+ {
+ return;
+ }
+ }
+
+ ~cRainbowRoads()
+ {
+ cPieceGenerator::FreePieces(m_Pieces);
+ }
+
+protected:
+ /** Seed for the random functions */
+ int m_Seed;
+
+ /** The noise used as a pseudo-random generator */
+ cNoise m_Noise;
+
+ /** Maximum size, in X/Z blocks, of the village (radius from the origin) */
+ int m_MaxSize;
+
+ /** Borders of the vilalge - no item may reach out of this cuboid. */
+ cCuboid m_Borders;
+
+ /** The village pieces, placed by the generator. */
+ cPlacedPieces m_Pieces;
+
+
+ // cGridStructGen::cStructure overrides:
+ virtual void DrawIntoChunk(cChunkDesc & a_Chunk) override
+ {
+ for (cPlacedPieces::iterator itr = m_Pieces.begin(), end = m_Pieces.end(); itr != end; ++itr)
+ {
+ cPrefab & Prefab = (cPrefab &)((*itr)->GetPiece());
+ Prefab.Draw(a_Chunk, *itr);
+ } // for itr - m_PlacedPieces[]
+ }
+} ;
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cRainbowRoadsGen:
+
+
+
+
+
+cRainbowRoadsGen::cRainbowRoadsGen(int a_Seed, int a_GridSize, int a_MaxDepth, int a_MaxSize) :
+ super(a_Seed, a_GridSize, a_GridSize, a_MaxSize, a_MaxSize, 100),
+ m_Noise(a_Seed + 9000),
+ m_MaxDepth(a_MaxDepth),
+ m_MaxSize(a_MaxSize)
+{
+}
+
+
+
+
+
+cGridStructGen::cStructurePtr cRainbowRoadsGen::CreateStructure(int a_OriginX, int a_OriginZ)
+{
+ // Create a base based on the chosen prefabs:
+ return cStructurePtr(new cRainbowRoads(m_Seed, a_OriginX, a_OriginZ, m_MaxDepth, m_MaxSize));
+}
+
+
+
+
diff --git a/src/Generating/RainbowRoadsGen.h b/src/Generating/RainbowRoadsGen.h
new file mode 100644
index 000000000..acbd5abf9
--- /dev/null
+++ b/src/Generating/RainbowRoadsGen.h
@@ -0,0 +1,47 @@
+
+// RainbowRoadsGen.h
+
+// Declares the cRainbowRoadsGen class representing the underwater base generator
+
+
+
+
+
+#pragma once
+
+#include "GridStructGen.h"
+#include "PrefabPiecePool.h"
+
+
+
+
+
+class cRainbowRoadsGen :
+ public cGridStructGen
+{
+ typedef cGridStructGen super;
+
+public:
+ cRainbowRoadsGen(int a_Seed, int a_GridSize, int a_MaxDepth, int a_MaxSize);
+
+protected:
+ class cRainbowRoads; // fwd: RainbowRoadsGen.cpp
+
+
+ /** The noise used for generating random numbers */
+ cNoise m_Noise;
+
+ /** Maximum depth of the generator tree*/
+ int m_MaxDepth;
+
+ /** Maximum size, in X/Z blocks, of the base (radius from the origin) */
+ int m_MaxSize;
+
+
+ // cGridStructGen overrides:
+ virtual cStructurePtr CreateStructure(int a_OriginX, int a_OriginZ) override;
+} ;
+
+
+
+
diff --git a/src/Items/ItemBoat.h b/src/Items/ItemBoat.h
index 42f4ffc8f..7faac1e32 100644
--- a/src/Items/ItemBoat.h
+++ b/src/Items/ItemBoat.h
@@ -75,7 +75,7 @@ public:
double z = Callbacks.m_Pos.z;
cBoat * Boat = new cBoat(x + 0.5, y + 1, z + 0.5);
- Boat->Initialize(a_World);
+ Boat->Initialize(*a_World);
return true;
}
diff --git a/src/Items/ItemBow.h b/src/Items/ItemBow.h
index 8c0b3a0a3..e0ab339d3 100644
--- a/src/Items/ItemBow.h
+++ b/src/Items/ItemBow.h
@@ -66,7 +66,7 @@ public:
{
return;
}
- if (!Arrow->Initialize(a_Player->GetWorld()))
+ if (!Arrow->Initialize(*a_Player->GetWorld()))
{
delete Arrow;
return;
diff --git a/src/Items/ItemFishingRod.h b/src/Items/ItemFishingRod.h
index 32c151db5..3b1ad1717 100644
--- a/src/Items/ItemFishingRod.h
+++ b/src/Items/ItemFishingRod.h
@@ -231,7 +231,7 @@ public:
else
{
cFloater * Floater = new cFloater(a_Player->GetPosX(), a_Player->GetStance(), a_Player->GetPosZ(), a_Player->GetLookVector() * 15, a_Player->GetUniqueID(), 100 + a_World->GetTickRandomNumber(800) - (a_Player->GetEquippedItem().m_Enchantments.GetLevel(cEnchantments::enchLure) * 100));
- Floater->Initialize(a_World);
+ Floater->Initialize(*a_World);
a_Player->SetIsFishing(true, Floater->GetUniqueID());
}
return true;
diff --git a/src/Items/ItemItemFrame.h b/src/Items/ItemItemFrame.h
index 27e7dba35..097f04d0b 100644
--- a/src/Items/ItemItemFrame.h
+++ b/src/Items/ItemItemFrame.h
@@ -34,7 +34,7 @@ public:
if (Block == E_BLOCK_AIR)
{
cItemFrame * ItemFrame = new cItemFrame(a_Dir, a_BlockX, a_BlockY, a_BlockZ);
- if (!ItemFrame->Initialize(a_World))
+ if (!ItemFrame->Initialize(*a_World))
{
delete ItemFrame;
return false;
diff --git a/src/Items/ItemMinecart.h b/src/Items/ItemMinecart.h
index 4e7d8fcff..63038de51 100644
--- a/src/Items/ItemMinecart.h
+++ b/src/Items/ItemMinecart.h
@@ -70,7 +70,7 @@ public:
return false;
}
} // switch (m_ItemType)
- Minecart->Initialize(a_World);
+ Minecart->Initialize(*a_World);
if (!a_Player->IsGameModeCreative())
{
diff --git a/src/Items/ItemPainting.h b/src/Items/ItemPainting.h
index b85098221..e4bb76ebe 100644
--- a/src/Items/ItemPainting.h
+++ b/src/Items/ItemPainting.h
@@ -79,7 +79,7 @@ public:
};
cPainting * Painting = new cPainting(gPaintingTitlesList[a_World->GetTickRandomNumber(ARRAYCOUNT(gPaintingTitlesList) - 1)].Title, Dir, a_BlockX, a_BlockY, a_BlockZ);
- Painting->Initialize(a_World);
+ Painting->Initialize(*a_World);
if (!a_Player->IsGameModeCreative())
{
diff --git a/src/Mobs/Bat.cpp b/src/Mobs/Bat.cpp
index 1417ddd9e..c072d4f48 100644
--- a/src/Mobs/Bat.cpp
+++ b/src/Mobs/Bat.cpp
@@ -7,8 +7,7 @@
cBat::cBat(void) :
- // TODO: The size is only a guesstimate, measure in vanilla and fix the size values here
- super("Bat", mtBat, "mob.bat.hurt", "mob.bat.death", 0.7, 0.7)
+ super("Bat", mtBat, "mob.bat.hurt", "mob.bat.death", 0.5, 0.9)
{
}
diff --git a/src/Mobs/Blaze.cpp b/src/Mobs/Blaze.cpp
index 326b42f07..19bdf8737 100644
--- a/src/Mobs/Blaze.cpp
+++ b/src/Mobs/Blaze.cpp
@@ -9,8 +9,7 @@
cBlaze::cBlaze(void) :
- // TODO: The size is only a guesstimate, measure in vanilla and fix the size values here
- super("Blaze", mtBlaze, "mob.blaze.hit", "mob.blaze.death", 0.7, 1.8)
+ super("Blaze", mtBlaze, "mob.blaze.hit", "mob.blaze.death", 0.6, 1.8)
{
}
@@ -45,7 +44,7 @@ void cBlaze::Attack(float a_Dt)
{
return;
}
- if (!FireCharge->Initialize(m_World))
+ if (!FireCharge->Initialize(*m_World))
{
delete FireCharge;
return;
diff --git a/src/Mobs/CaveSpider.cpp b/src/Mobs/CaveSpider.cpp
index 56ecd2d28..1157b81f9 100644
--- a/src/Mobs/CaveSpider.cpp
+++ b/src/Mobs/CaveSpider.cpp
@@ -20,7 +20,6 @@ void cCaveSpider::Tick(float a_Dt, cChunk & a_Chunk)
{
super::Tick(a_Dt, a_Chunk);
- // TODO: Check vanilla if cavespiders really get passive during the day / in daylight
m_EMPersonality = (GetWorld()->GetTimeOfDay() < (12000 + 1000)) ? PASSIVE : AGGRESSIVE;
}
diff --git a/src/Mobs/Ghast.cpp b/src/Mobs/Ghast.cpp
index d8a7663f8..4df8e165c 100644
--- a/src/Mobs/Ghast.cpp
+++ b/src/Mobs/Ghast.cpp
@@ -46,7 +46,7 @@ void cGhast::Attack(float a_Dt)
{
return;
}
- if (!GhastBall->Initialize(m_World))
+ if (!GhastBall->Initialize(*m_World))
{
delete GhastBall;
return;
diff --git a/src/Mobs/Skeleton.cpp b/src/Mobs/Skeleton.cpp
index 1e62d7987..e7f3971cc 100644
--- a/src/Mobs/Skeleton.cpp
+++ b/src/Mobs/Skeleton.cpp
@@ -81,7 +81,7 @@ void cSkeleton::Attack(float a_Dt)
{
return;
}
- if (!Arrow->Initialize(m_World))
+ if (!Arrow->Initialize(*m_World))
{
delete Arrow;
return;
diff --git a/src/Mobs/Wither.cpp b/src/Mobs/Wither.cpp
index 170f4fdc0..da4cc7765 100644
--- a/src/Mobs/Wither.cpp
+++ b/src/Mobs/Wither.cpp
@@ -30,7 +30,7 @@ bool cWither::IsArmored(void) const
-bool cWither::Initialize(cWorld * a_World)
+bool cWither::Initialize(cWorld & a_World)
{
// Set health before BroadcastSpawnEntity()
SetHealth(GetMaxHealth() / 3);
diff --git a/src/Mobs/Wither.h b/src/Mobs/Wither.h
index 93b4f8bfc..03a320788 100644
--- a/src/Mobs/Wither.h
+++ b/src/Mobs/Wither.h
@@ -25,7 +25,7 @@ public:
bool IsArmored(void) const;
// cEntity overrides
- virtual bool Initialize(cWorld * a_World) override;
+ virtual bool Initialize(cWorld & a_World) override;
virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override;
virtual bool DoTakeDamage(TakeDamageInfo & a_TDI) override;
virtual void Tick(float a_Dt, cChunk & a_Chunk) override;
diff --git a/src/Noise.cpp b/src/Noise.cpp
index 89115d992..040421106 100644
--- a/src/Noise.cpp
+++ b/src/Noise.cpp
@@ -854,7 +854,7 @@ void cPerlinNoise::Generate2D(
NOISE_DATATYPE Amplitude = FirstOctave.m_Amplitude;
for (int i = 0; i < ArrayCount; i++)
{
- a_Array[i] *= Amplitude;
+ a_Array[i] = a_Workspace[i] * Amplitude;
}
// Add each octave:
@@ -949,3 +949,171 @@ void cPerlinNoise::Generate3D(
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cRidgedMultiNoise:
+
+cRidgedMultiNoise::cRidgedMultiNoise(void) :
+ m_Seed(0)
+{
+}
+
+
+
+
+
+cRidgedMultiNoise::cRidgedMultiNoise(int a_Seed) :
+ m_Seed(a_Seed)
+{
+}
+
+
+
+
+
+void cRidgedMultiNoise::SetSeed(int a_Seed)
+{
+ m_Seed = a_Seed;
+}
+
+
+
+
+
+void cRidgedMultiNoise::AddOctave(float a_Frequency, float a_Amplitude)
+{
+ m_Octaves.push_back(cOctave(m_Seed * ((int)m_Octaves.size() + 4) * 4 + 1024, a_Frequency, a_Amplitude));
+}
+
+
+
+
+
+void cRidgedMultiNoise::Generate2D(
+ NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y]
+ int a_SizeX, int a_SizeY, ///< Count of the array, in each direction
+ NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array in the X direction
+ NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY, ///< Noise-space coords of the array in the Y direction
+ NOISE_DATATYPE * a_Workspace ///< Workspace that this function can use and trash
+) const
+{
+ if (m_Octaves.empty())
+ {
+ // No work to be done
+ ASSERT(!"RidgedMulti: No octaves to generate!");
+ return;
+ }
+
+ bool ShouldFreeWorkspace = (a_Workspace == NULL);
+ int ArrayCount = a_SizeX * a_SizeY;
+ if (ShouldFreeWorkspace)
+ {
+ a_Workspace = new NOISE_DATATYPE[ArrayCount];
+ }
+
+ // Generate the first octave directly into array:
+ const cOctave & FirstOctave = m_Octaves.front();
+
+ FirstOctave.m_Noise.Generate2D(
+ a_Workspace, a_SizeX, a_SizeY,
+ a_StartX * FirstOctave.m_Frequency, a_EndX * FirstOctave.m_Frequency,
+ a_StartY * FirstOctave.m_Frequency, a_EndY * FirstOctave.m_Frequency
+ );
+ NOISE_DATATYPE Amplitude = FirstOctave.m_Amplitude;
+ for (int i = 0; i < ArrayCount; i++)
+ {
+ a_Array[i] = fabs(a_Workspace[i] * Amplitude);
+ }
+
+ // Add each octave:
+ for (cOctaves::const_iterator itr = m_Octaves.begin() + 1, end = m_Octaves.end(); itr != end; ++itr)
+ {
+ // Generate cubic noise for the octave:
+ itr->m_Noise.Generate2D(
+ a_Workspace, a_SizeX, a_SizeY,
+ a_StartX * itr->m_Frequency, a_EndX * itr->m_Frequency,
+ a_StartY * itr->m_Frequency, a_EndY * itr->m_Frequency
+ );
+ // Add the cubic noise into the output:
+ NOISE_DATATYPE Amplitude = itr->m_Amplitude;
+ for (int i = 0; i < ArrayCount; i++)
+ {
+ a_Array[i] += fabs(a_Workspace[i] * Amplitude);
+ }
+ }
+
+ if (ShouldFreeWorkspace)
+ {
+ delete[] a_Workspace;
+ }
+}
+
+
+
+
+
+void cRidgedMultiNoise::Generate3D(
+ NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y + a_SizeX * a_SizeY * z]
+ int a_SizeX, int a_SizeY, int a_SizeZ, ///< Count of the array, in each direction
+ NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array in the X direction
+ NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY, ///< Noise-space coords of the array in the Y direction
+ NOISE_DATATYPE a_StartZ, NOISE_DATATYPE a_EndZ, ///< Noise-space coords of the array in the Z direction
+ NOISE_DATATYPE * a_Workspace ///< Workspace that this function can use and trash
+) const
+{
+ if (m_Octaves.empty())
+ {
+ // No work to be done
+ ASSERT(!"RidgedMulti: No octaves to generate!");
+ return;
+ }
+
+ bool ShouldFreeWorkspace = (a_Workspace == NULL);
+ int ArrayCount = a_SizeX * a_SizeY * a_SizeZ;
+ if (ShouldFreeWorkspace)
+ {
+ a_Workspace = new NOISE_DATATYPE[ArrayCount];
+ }
+
+ // Generate the first octave directly into array:
+ const cOctave & FirstOctave = m_Octaves.front();
+
+ FirstOctave.m_Noise.Generate3D(
+ a_Workspace, a_SizeX, a_SizeY, a_SizeZ,
+ a_StartX * FirstOctave.m_Frequency, a_EndX * FirstOctave.m_Frequency,
+ a_StartY * FirstOctave.m_Frequency, a_EndY * FirstOctave.m_Frequency,
+ a_StartZ * FirstOctave.m_Frequency, a_EndZ * FirstOctave.m_Frequency
+ );
+ NOISE_DATATYPE Amplitude = FirstOctave.m_Amplitude;
+ for (int i = 0; i < ArrayCount; i++)
+ {
+ a_Array[i] = a_Workspace[i] * Amplitude;
+ }
+
+ // Add each octave:
+ for (cOctaves::const_iterator itr = m_Octaves.begin() + 1, end = m_Octaves.end(); itr != end; ++itr)
+ {
+ // Generate cubic noise for the octave:
+ itr->m_Noise.Generate3D(
+ a_Workspace, a_SizeX, a_SizeY, a_SizeZ,
+ a_StartX * itr->m_Frequency, a_EndX * itr->m_Frequency,
+ a_StartY * itr->m_Frequency, a_EndY * itr->m_Frequency,
+ a_StartZ * itr->m_Frequency, a_EndZ * itr->m_Frequency
+ );
+ // Add the cubic noise into the output:
+ NOISE_DATATYPE Amplitude = itr->m_Amplitude;
+ for (int i = 0; i < ArrayCount; i++)
+ {
+ a_Array[i] += a_Workspace[i] * Amplitude;
+ }
+ }
+
+ if (ShouldFreeWorkspace)
+ {
+ delete[] a_Workspace;
+ }
+}
+
+
+
+
diff --git a/src/Noise.h b/src/Noise.h
index e605051b5..83af0cf08 100644
--- a/src/Noise.h
+++ b/src/Noise.h
@@ -192,6 +192,70 @@ protected:
+class cRidgedMultiNoise
+{
+public:
+ cRidgedMultiNoise(void);
+ cRidgedMultiNoise(int a_Seed);
+
+
+ void SetSeed(int a_Seed);
+
+ void AddOctave(NOISE_DATATYPE a_Frequency, NOISE_DATATYPE a_Amplitude);
+
+ void Generate1D(
+ NOISE_DATATYPE * a_Array, ///< Array to generate into
+ int a_SizeX, ///< Count of the array
+ NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array
+ NOISE_DATATYPE * a_Workspace = NULL ///< Workspace that this function can use and trash
+ ) const;
+
+
+ void Generate2D(
+ NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y]
+ int a_SizeX, int a_SizeY, ///< Count of the array, in each direction
+ NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array in the X direction
+ NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY, ///< Noise-space coords of the array in the Y direction
+ NOISE_DATATYPE * a_Workspace = NULL ///< Workspace that this function can use and trash
+ ) const;
+
+
+ void Generate3D(
+ NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y + a_SizeX * a_SizeY * z]
+ int a_SizeX, int a_SizeY, int a_SizeZ, ///< Count of the array, in each direction
+ NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array in the X direction
+ NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY, ///< Noise-space coords of the array in the Y direction
+ NOISE_DATATYPE a_StartZ, NOISE_DATATYPE a_EndZ, ///< Noise-space coords of the array in the Z direction
+ NOISE_DATATYPE * a_Workspace = NULL ///< Workspace that this function can use and trash
+ ) const;
+
+protected:
+ class cOctave
+ {
+ public:
+ cCubicNoise m_Noise;
+
+ NOISE_DATATYPE m_Frequency; // Coord multiplier
+ NOISE_DATATYPE m_Amplitude; // Value multiplier
+
+ cOctave(int a_Seed, NOISE_DATATYPE a_Frequency, NOISE_DATATYPE a_Amplitude) :
+ m_Noise(a_Seed),
+ m_Frequency(a_Frequency),
+ m_Amplitude(a_Amplitude)
+ {
+ }
+ } ;
+
+ typedef std::vector<cOctave> cOctaves;
+
+ int m_Seed;
+ cOctaves m_Octaves;
+} ;
+
+
+
+
+
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Inline function definitions:
// These need to be in the header, otherwise linker error occur in MSVC
diff --git a/src/OSSupport/IsThread.cpp b/src/OSSupport/IsThread.cpp
index 04fc818e4..67f336c97 100644
--- a/src/OSSupport/IsThread.cpp
+++ b/src/OSSupport/IsThread.cpp
@@ -60,6 +60,9 @@ static void SetThreadName(DWORD dwThreadID, const char * threadName)
cIsThread::cIsThread(const AString & iThreadName) :
m_ShouldTerminate(false),
m_ThreadName(iThreadName),
+ #ifdef _WIN32
+ m_ThreadID(0),
+ #endif
m_Handle(NULL_HANDLE)
{
}
@@ -83,8 +86,8 @@ bool cIsThread::Start(void)
ASSERT(m_Handle == NULL_HANDLE); // Has already started one thread?
#ifdef _WIN32
// Create the thread suspended, so that the mHandle variable is valid in the thread procedure
- DWORD ThreadID = 0;
- m_Handle = CreateThread(NULL, 0, thrExecute, this, CREATE_SUSPENDED, &ThreadID);
+ m_ThreadID = 0;
+ m_Handle = CreateThread(NULL, 0, thrExecute, this, CREATE_SUSPENDED, &m_ThreadID);
if (m_Handle == NULL)
{
LOGERROR("ERROR: Could not create thread \"%s\", GLE = %d!", m_ThreadName.c_str(), GetLastError());
@@ -96,7 +99,7 @@ bool cIsThread::Start(void)
// Thread naming is available only in MSVC
if (!m_ThreadName.empty())
{
- SetThreadName(ThreadID, m_ThreadName.c_str());
+ SetThreadName(m_ThreadID, m_ThreadName.c_str());
}
#endif // _DEBUG and _MSC_VER
@@ -177,3 +180,15 @@ unsigned long cIsThread::GetCurrentID(void)
+bool cIsThread::IsCurrentThread(void) const
+{
+ #ifdef _WIN32
+ return (GetCurrentThreadId() == m_ThreadID);
+ #else
+ return (m_Handle == pthread_self());
+ #endif
+}
+
+
+
+
diff --git a/src/OSSupport/IsThread.h b/src/OSSupport/IsThread.h
index 57651a490..c20fc3e7e 100644
--- a/src/OSSupport/IsThread.h
+++ b/src/OSSupport/IsThread.h
@@ -48,6 +48,9 @@ public:
/// Returns the OS-dependent thread ID for the caller's thread
static unsigned long GetCurrentID(void);
+ /** Returns true if the thread calling this function is the thread contained within this object. */
+ bool IsCurrentThread(void) const;
+
protected:
AString m_ThreadName;
@@ -60,6 +63,7 @@ protected:
#ifdef _WIN32
+ DWORD m_ThreadID;
HANDLE m_Handle;
static DWORD __stdcall thrExecute(LPVOID a_Param)
diff --git a/src/Protocol/Protocol125.cpp b/src/Protocol/Protocol125.cpp
index 873295f62..5796ba271 100644
--- a/src/Protocol/Protocol125.cpp
+++ b/src/Protocol/Protocol125.cpp
@@ -133,7 +133,8 @@ typedef unsigned char Byte;
cProtocol125::cProtocol125(cClientHandle * a_Client) :
super(a_Client),
- m_ReceivedData(32 KiB)
+ m_ReceivedData(32 KiB),
+ m_LastSentDimension(dimNotSet)
{
}
@@ -591,6 +592,7 @@ void cProtocol125::SendLogin(const cPlayer & a_Player, const cWorld & a_World)
WriteByte (0); // Unused
WriteByte (60); // Client list width or something
Flush();
+ m_LastSentDimension = a_World.GetDimension();
}
@@ -834,6 +836,11 @@ void cProtocol125::SendRemoveEntityEffect(const cEntity & a_Entity, int a_Effect
void cProtocol125::SendRespawn(const cWorld & a_World)
{
cCSLock Lock(m_CSPacket);
+ if (m_LastSentDimension == a_World.GetDimension())
+ {
+ // Must not send a respawn for the world with the same dimension, the client goes cuckoo if we do
+ return;
+ }
cPlayer * Player = m_Client->GetPlayer();
WriteByte (PACKET_RESPAWN);
WriteInt ((int)(a_World.GetDimension()));
@@ -841,6 +848,8 @@ void cProtocol125::SendRespawn(const cWorld & a_World)
WriteChar ((char)Player->GetGameMode());
WriteShort (256); // Current world height
WriteString("default");
+ Flush();
+ m_LastSentDimension = a_World.GetDimension();
}
diff --git a/src/Protocol/Protocol125.h b/src/Protocol/Protocol125.h
index 3f39c8965..85418f71f 100644
--- a/src/Protocol/Protocol125.h
+++ b/src/Protocol/Protocol125.h
@@ -113,6 +113,10 @@ protected:
cByteBuffer m_ReceivedData; ///< Buffer for the received data
AString m_Username; ///< Stored in ParseHandshake(), compared to Login username
+
+ /** The dimension that was last sent to a player in a Respawn or Login packet.
+ Used to avoid Respawning into the same dimension, which confuses the client. */
+ eDimension m_LastSentDimension;
virtual void SendData(const char * a_Data, size_t a_Size) override;
diff --git a/src/Protocol/Protocol132.cpp b/src/Protocol/Protocol132.cpp
index f4717f592..1e3fc8de8 100644
--- a/src/Protocol/Protocol132.cpp
+++ b/src/Protocol/Protocol132.cpp
@@ -253,7 +253,7 @@ void cProtocol132::SendLogin(const cPlayer & a_Player, const cWorld & a_World)
WriteByte (0); // Unused, used to be world height
WriteByte (8); // Client list width or something
Flush();
-
+ m_LastSentDimension = a_World.GetDimension();
SendCompass(a_World);
}
diff --git a/src/Protocol/Protocol17x.cpp b/src/Protocol/Protocol17x.cpp
index 9c5f5eaba..f5b176e54 100644
--- a/src/Protocol/Protocol17x.cpp
+++ b/src/Protocol/Protocol17x.cpp
@@ -92,7 +92,8 @@ cProtocol172::cProtocol172(cClientHandle * a_Client, const AString & a_ServerAdd
m_ReceivedData(32 KiB),
m_OutPacketBuffer(64 KiB),
m_OutPacketLenBuffer(20), // 20 bytes is more than enough for one VarInt
- m_IsEncrypted(false)
+ m_IsEncrypted(false),
+ m_LastSentDimension(dimNotSet)
{
// Create the comm log file, if so requested:
if (g_ShouldLogCommIn || g_ShouldLogCommOut)
@@ -656,6 +657,7 @@ void cProtocol172::SendLogin(const cPlayer & a_Player, const cWorld & a_World)
Pkt.WriteByte(std::min(Server->GetMaxPlayers(), 60));
Pkt.WriteString("default"); // Level type - wtf?
}
+ m_LastSentDimension = a_World.GetDimension();
// Send the spawn position:
{
@@ -986,12 +988,19 @@ void cProtocol172::SendRemoveEntityEffect(const cEntity & a_Entity, int a_Effect
void cProtocol172::SendRespawn(const cWorld & a_World)
{
+ if (m_LastSentDimension == a_World.GetDimension())
+ {
+ // Must not send a respawn for the world with the same dimension, the client goes cuckoo if we do
+ return;
+ }
+
cPacketizer Pkt(*this, 0x07); // Respawn packet
cPlayer * Player = m_Client->GetPlayer();
Pkt.WriteInt((int)a_World.GetDimension());
Pkt.WriteByte(2); // TODO: Difficulty (set to Normal)
Pkt.WriteByte((Byte)Player->GetEffectiveGameMode());
Pkt.WriteString("default");
+ m_LastSentDimension = a_World.GetDimension();
}
diff --git a/src/Protocol/Protocol17x.h b/src/Protocol/Protocol17x.h
index cafdb50e4..8be1d9211 100644
--- a/src/Protocol/Protocol17x.h
+++ b/src/Protocol/Protocol17x.h
@@ -244,6 +244,10 @@ protected:
/** The logfile where the comm is logged, when g_ShouldLogComm is true */
cFile m_CommLogFile;
+ /** The dimension that was last sent to a player in a Respawn or Login packet.
+ Used to avoid Respawning into the same dimension, which confuses the client. */
+ eDimension m_LastSentDimension;
+
/** Adds the received (unencrypted) data to m_ReceivedData, parses complete packets */
void AddReceivedData(const char * a_Data, size_t a_Size);
diff --git a/src/Server.cpp b/src/Server.cpp
index aa731cdd2..66bccd680 100644
--- a/src/Server.cpp
+++ b/src/Server.cpp
@@ -107,10 +107,16 @@ void cServer::cTickThread::Execute(void)
cServer::cServer(void) :
m_ListenThreadIPv4(*this, cSocket::IPv4, "Client IPv4"),
m_ListenThreadIPv6(*this, cSocket::IPv6, "Client IPv6"),
+ m_PlayerCount(0),
+ m_PlayerCountDiff(0),
+ m_ClientViewDistance(0),
m_bIsConnected(false),
m_bRestarting(false),
m_RCONServer(*this),
- m_TickThread(*this)
+ m_MaxPlayers(0),
+ m_bIsHardcore(false),
+ m_TickThread(*this),
+ m_ShouldAuthenticate(false)
{
}
diff --git a/src/Simulator/IncrementalRedstoneSimulator.cpp b/src/Simulator/IncrementalRedstoneSimulator.cpp
index 51a7c2886..b32a57165 100644
--- a/src/Simulator/IncrementalRedstoneSimulator.cpp
+++ b/src/Simulator/IncrementalRedstoneSimulator.cpp
@@ -17,8 +17,14 @@
-cIncrementalRedstoneSimulator::cIncrementalRedstoneSimulator(cWorld & a_World)
- : super(a_World)
+cIncrementalRedstoneSimulator::cIncrementalRedstoneSimulator(cWorld & a_World) :
+ super(a_World),
+ m_RedstoneSimulatorChunkData(),
+ m_PoweredBlocks(),
+ m_LinkedPoweredBlocks(),
+ m_SimulatedPlayerToggleableBlocks(),
+ m_RepeatersDelayList(),
+ m_Chunk()
{
}
@@ -83,6 +89,7 @@ void cIncrementalRedstoneSimulator::RedstoneAddBlock(int a_BlockX, int a_BlockY,
{
LOGD("cIncrementalRedstoneSimulator: Erased block @ {%i, %i, %i} from powered blocks list as it no longer connected to a source", itr->a_BlockPos.x, itr->a_BlockPos.y, itr->a_BlockPos.z);
itr = PoweredBlocks->erase(itr);
+ a_Chunk->SetIsRedstoneDirty(true);
continue;
}
else if (
@@ -97,6 +104,7 @@ void cIncrementalRedstoneSimulator::RedstoneAddBlock(int a_BlockX, int a_BlockY,
{
LOGD("cIncrementalRedstoneSimulator: Erased block @ {%i, %i, %i} from powered blocks list due to present/past metadata mismatch", itr->a_BlockPos.x, itr->a_BlockPos.y, itr->a_BlockPos.z);
itr = PoweredBlocks->erase(itr);
+ a_Chunk->SetIsRedstoneDirty(true);
continue;
}
++itr;
@@ -112,6 +120,7 @@ void cIncrementalRedstoneSimulator::RedstoneAddBlock(int a_BlockX, int a_BlockY,
{
LOGD("cIncrementalRedstoneSimulator: Erased block @ {%i, %i, %i} from linked powered blocks list as it is no longer connected to a source", itr->a_BlockPos.x, itr->a_BlockPos.y, itr->a_BlockPos.z);
itr = LinkedPoweredBlocks->erase(itr);
+ a_Chunk->SetIsRedstoneDirty(true);
continue;
}
else if (
@@ -125,6 +134,7 @@ void cIncrementalRedstoneSimulator::RedstoneAddBlock(int a_BlockX, int a_BlockY,
{
LOGD("cIncrementalRedstoneSimulator: Erased block @ {%i, %i, %i} from linked powered blocks list due to present/past metadata mismatch", itr->a_BlockPos.x, itr->a_BlockPos.y, itr->a_BlockPos.z);
itr = LinkedPoweredBlocks->erase(itr);
+ a_Chunk->SetIsRedstoneDirty(true);
continue;
}
}
@@ -134,6 +144,7 @@ void cIncrementalRedstoneSimulator::RedstoneAddBlock(int a_BlockX, int a_BlockY,
{
LOGD("cIncrementalRedstoneSimulator: Erased block @ {%i, %i, %i} from linked powered blocks list as it is no longer powered through a valid middle block", itr->a_BlockPos.x, itr->a_BlockPos.y, itr->a_BlockPos.z);
itr = LinkedPoweredBlocks->erase(itr);
+ a_Chunk->SetIsRedstoneDirty(true);
continue;
}
}
@@ -198,8 +209,16 @@ void cIncrementalRedstoneSimulator::RedstoneAddBlock(int a_BlockX, int a_BlockY,
{
return;
}
-
- RedstoneSimulatorChunkData->push_back(cCoordWithBlockAndBool(RelX, a_BlockY, RelZ, Block, false));
+
+ for (cRedstoneSimulatorChunkData::iterator itr = a_Chunk->GetRedstoneSimulatorQueuedData()->begin(); itr != a_Chunk->GetRedstoneSimulatorQueuedData()->end(); ++itr)
+ {
+ if ((itr->x == RelX) && (itr->y == a_BlockY) && (itr->z == RelZ))
+ {
+ // Can't have duplicates in here either, in case something adds the block again before the structure can written to the main chunk data
+ return;
+ }
+ }
+ a_Chunk->GetRedstoneSimulatorQueuedData()->push_back(cCoordWithBlockAndBool(RelX, a_BlockY, RelZ, Block, false));
}
@@ -208,22 +227,29 @@ void cIncrementalRedstoneSimulator::RedstoneAddBlock(int a_BlockX, int a_BlockY,
void cIncrementalRedstoneSimulator::SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk)
{
- // We still attempt to simulate all blocks in the chunk every tick, because of outside influence that needs to be taken into account
- // For example, repeaters need to be ticked, pressure plates checked for entities, daylight sensor checked for light changes, etc.
- // The easiest way to make this more efficient is probably just to reduce code within the handlers that put too much strain on server, like getting or setting blocks
- // A marking dirty system might be a TODO for later on, perhaps
-
m_RedstoneSimulatorChunkData = a_Chunk->GetRedstoneSimulatorData();
- if (m_RedstoneSimulatorChunkData->empty())
+ if (m_RedstoneSimulatorChunkData->empty() && a_Chunk->GetRedstoneSimulatorQueuedData()->empty())
{
return;
}
+ m_RedstoneSimulatorChunkData->insert(m_RedstoneSimulatorChunkData->end(), a_Chunk->GetRedstoneSimulatorQueuedData()->begin(), a_Chunk->GetRedstoneSimulatorQueuedData()->end());
+ a_Chunk->GetRedstoneSimulatorQueuedData()->clear();
+
m_PoweredBlocks = a_Chunk->GetRedstoneSimulatorPoweredBlocksList();
m_RepeatersDelayList = a_Chunk->GetRedstoneSimulatorRepeatersDelayList();
m_SimulatedPlayerToggleableBlocks = a_Chunk->GetRedstoneSimulatorSimulatedPlayerToggleableList();
m_LinkedPoweredBlocks = a_Chunk->GetRedstoneSimulatorLinkedBlocksList();
m_Chunk = a_Chunk;
+ bool ShouldUpdateSimulateOnceBlocks = false;
+
+ if (a_Chunk->IsRedstoneDirty())
+ {
+ // Simulate the majority of devices only if something (blockwise or power-wise) has changed
+ // Make sure to allow the chunk to resimulate after the initial run if there was a power change (ShouldUpdateSimulateOnceBlocks helps to do this)
+ a_Chunk->SetIsRedstoneDirty(false);
+ ShouldUpdateSimulateOnceBlocks = true;
+ }
for (cRedstoneSimulatorChunkData::iterator dataitr = m_RedstoneSimulatorChunkData->begin(); dataitr != m_RedstoneSimulatorChunkData->end();)
{
@@ -235,63 +261,25 @@ void cIncrementalRedstoneSimulator::SimulateChunk(float a_Dt, int a_ChunkX, int
switch (dataitr->Data)
{
- case E_BLOCK_BLOCK_OF_REDSTONE: HandleRedstoneBlock(dataitr->x, dataitr->y, dataitr->z); break;
- case E_BLOCK_LEVER: HandleRedstoneLever(dataitr->x, dataitr->y, dataitr->z); break;
- case E_BLOCK_FENCE_GATE: HandleFenceGate(dataitr->x, dataitr->y, dataitr->z); break;
- case E_BLOCK_TNT: HandleTNT(dataitr->x, dataitr->y, dataitr->z); break;
- case E_BLOCK_TRAPDOOR: HandleTrapdoor(dataitr->x, dataitr->y, dataitr->z); break;
- case E_BLOCK_REDSTONE_WIRE: HandleRedstoneWire(dataitr->x, dataitr->y, dataitr->z); break;
- case E_BLOCK_NOTE_BLOCK: HandleNoteBlock(dataitr->x, dataitr->y, dataitr->z); break;
- case E_BLOCK_DAYLIGHT_SENSOR: HandleDaylightSensor(dataitr->x, dataitr->y, dataitr->z); break;
- case E_BLOCK_COMMAND_BLOCK: HandleCommandBlock(dataitr->x, dataitr->y, dataitr->z); break;
+ case E_BLOCK_DAYLIGHT_SENSOR: HandleDaylightSensor(dataitr->x, dataitr->y, dataitr->z); break;
- case E_BLOCK_REDSTONE_TORCH_OFF:
- case E_BLOCK_REDSTONE_TORCH_ON:
- {
- HandleRedstoneTorch(dataitr->x, dataitr->y, dataitr->z, dataitr->Data);
- break;
- }
- case E_BLOCK_STONE_BUTTON:
- case E_BLOCK_WOODEN_BUTTON:
- {
- HandleRedstoneButton(dataitr->x, dataitr->y, dataitr->z);
- break;
- }
case E_BLOCK_REDSTONE_REPEATER_OFF:
case E_BLOCK_REDSTONE_REPEATER_ON:
{
- HandleRedstoneRepeater(dataitr->x, dataitr->y, dataitr->z, dataitr->Data);
- break;
- }
- case E_BLOCK_PISTON:
- case E_BLOCK_STICKY_PISTON:
- {
- HandlePiston(dataitr->x, dataitr->y, dataitr->z);
- break;
- }
- case E_BLOCK_REDSTONE_LAMP_OFF:
- case E_BLOCK_REDSTONE_LAMP_ON:
- {
- HandleRedstoneLamp(dataitr->x, dataitr->y, dataitr->z, dataitr->Data);
- break;
- }
- case E_BLOCK_DISPENSER:
- case E_BLOCK_DROPPER:
- {
- HandleDropSpenser(dataitr->x, dataitr->y, dataitr->z);
- break;
- }
- case E_BLOCK_WOODEN_DOOR:
- case E_BLOCK_IRON_DOOR:
- {
- HandleDoor(dataitr->x, dataitr->y, dataitr->z);
- break;
- }
- case E_BLOCK_ACTIVATOR_RAIL:
- case E_BLOCK_DETECTOR_RAIL:
- case E_BLOCK_POWERED_RAIL:
- {
- HandleRail(dataitr->x, dataitr->y, dataitr->z, dataitr->Data);
+ bool FoundItem = false;
+ for (RepeatersDelayList::iterator repeateritr = m_RepeatersDelayList->begin(); repeateritr != m_RepeatersDelayList->end(); ++repeateritr)
+ {
+ if (repeateritr->a_RelBlockPos == Vector3i(dataitr->x, dataitr->y, dataitr->z))
+ {
+ HandleRedstoneRepeater(dataitr->x, dataitr->y, dataitr->z, dataitr->Data, repeateritr);
+ FoundItem = true;
+ break;
+ }
+ }
+ if (!FoundItem && ShouldUpdateSimulateOnceBlocks)
+ {
+ HandleRedstoneRepeater(dataitr->x, dataitr->y, dataitr->z, dataitr->Data, m_RepeatersDelayList->end());
+ }
break;
}
case E_BLOCK_WOODEN_PRESSURE_PLATE:
@@ -302,7 +290,67 @@ void cIncrementalRedstoneSimulator::SimulateChunk(float a_Dt, int a_ChunkX, int
HandlePressurePlate(dataitr->x, dataitr->y, dataitr->z, dataitr->Data);
break;
}
- default: LOGD("Unhandled block (!) or unimplemented redstone block: %s", ItemToString(dataitr->Data).c_str()); break;
+ default: break;
+ }
+
+ if (ShouldUpdateSimulateOnceBlocks)
+ {
+ switch (dataitr->Data)
+ {
+ case E_BLOCK_REDSTONE_WIRE: HandleRedstoneWire(dataitr->x, dataitr->y, dataitr->z); break;
+ case E_BLOCK_COMMAND_BLOCK: HandleCommandBlock(dataitr->x, dataitr->y, dataitr->z); break;
+ case E_BLOCK_NOTE_BLOCK: HandleNoteBlock(dataitr->x, dataitr->y, dataitr->z); break;
+ case E_BLOCK_BLOCK_OF_REDSTONE: HandleRedstoneBlock(dataitr->x, dataitr->y, dataitr->z); break;
+ case E_BLOCK_LEVER: HandleRedstoneLever(dataitr->x, dataitr->y, dataitr->z); break;
+ case E_BLOCK_FENCE_GATE: HandleFenceGate(dataitr->x, dataitr->y, dataitr->z); break;
+ case E_BLOCK_TNT: HandleTNT(dataitr->x, dataitr->y, dataitr->z); break;
+ case E_BLOCK_TRAPDOOR: HandleTrapdoor(dataitr->x, dataitr->y, dataitr->z); break;
+
+ case E_BLOCK_ACTIVATOR_RAIL:
+ case E_BLOCK_DETECTOR_RAIL:
+ case E_BLOCK_POWERED_RAIL:
+ {
+ HandleRail(dataitr->x, dataitr->y, dataitr->z, dataitr->Data);
+ break;
+ }
+ case E_BLOCK_WOODEN_DOOR:
+ case E_BLOCK_IRON_DOOR:
+ {
+ HandleDoor(dataitr->x, dataitr->y, dataitr->z);
+ break;
+ }
+ case E_BLOCK_REDSTONE_LAMP_OFF:
+ case E_BLOCK_REDSTONE_LAMP_ON:
+ {
+ HandleRedstoneLamp(dataitr->x, dataitr->y, dataitr->z, dataitr->Data);
+ break;
+ }
+ case E_BLOCK_DISPENSER:
+ case E_BLOCK_DROPPER:
+ {
+ HandleDropSpenser(dataitr->x, dataitr->y, dataitr->z);
+ break;
+ }
+ case E_BLOCK_PISTON:
+ case E_BLOCK_STICKY_PISTON:
+ {
+ HandlePiston(dataitr->x, dataitr->y, dataitr->z);
+ break;
+ }
+ case E_BLOCK_REDSTONE_TORCH_OFF:
+ case E_BLOCK_REDSTONE_TORCH_ON:
+ {
+ HandleRedstoneTorch(dataitr->x, dataitr->y, dataitr->z, dataitr->Data);
+ break;
+ }
+ case E_BLOCK_STONE_BUTTON:
+ case E_BLOCK_WOODEN_BUTTON:
+ {
+ HandleRedstoneButton(dataitr->x, dataitr->y, dataitr->z);
+ break;
+ }
+ default: break;
+ }
}
++dataitr;
}
@@ -699,7 +747,7 @@ void cIncrementalRedstoneSimulator::HandleRedstoneWire(int a_RelBlockX, int a_Re
-void cIncrementalRedstoneSimulator::HandleRedstoneRepeater(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, BLOCKTYPE a_MyState)
+void cIncrementalRedstoneSimulator::HandleRedstoneRepeater(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, BLOCKTYPE a_MyState, RepeatersDelayList::iterator a_Itr)
{
/* Repeater Orientation Mini Guide:
===================================
@@ -725,87 +773,99 @@ void cIncrementalRedstoneSimulator::HandleRedstoneRepeater(int a_RelBlockX, int
// Create a variable holding my meta to avoid multiple lookups.
NIBBLETYPE a_Meta = m_Chunk->GetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ);
bool IsOn = (a_MyState == E_BLOCK_REDSTONE_REPEATER_ON);
-
+
+ bool WereItrsChanged = false;
if (!IsRepeaterLocked(a_RelBlockX, a_RelBlockY, a_RelBlockZ, a_Meta)) // If we're locked, change nothing. Otherwise:
{
bool IsSelfPowered = IsRepeaterPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, a_Meta);
if (IsSelfPowered && !IsOn) // Queue a power change if powered, but not on and not locked.
{
- QueueRepeaterPowerChange(a_RelBlockX, a_RelBlockY, a_RelBlockZ, a_Meta, true);
+ WereItrsChanged = QueueRepeaterPowerChange(a_RelBlockX, a_RelBlockY, a_RelBlockZ, a_Meta, true);
}
else if (!IsSelfPowered && IsOn) // Queue a power change if unpowered, on, and not locked.
{
- QueueRepeaterPowerChange(a_RelBlockX, a_RelBlockY, a_RelBlockZ, a_Meta, false);
+ WereItrsChanged = QueueRepeaterPowerChange(a_RelBlockX, a_RelBlockY, a_RelBlockZ, a_Meta, false);
+ }
+ else
+ {
+ return;
}
}
+ else
+ {
+ return;
+ }
- for (RepeatersDelayList::iterator itr = m_RepeatersDelayList->begin(); itr != m_RepeatersDelayList->end(); ++itr)
+ if (WereItrsChanged)
{
- if (!itr->a_RelBlockPos.Equals(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ)))
+ for (a_Itr = m_RepeatersDelayList->begin(); a_Itr != m_RepeatersDelayList->end(); ++a_Itr)
{
- continue;
+ if (a_Itr->a_RelBlockPos == Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ))
+ {
+ break;
+ }
}
+ }
- if (itr->a_ElapsedTicks >= itr->a_DelayTicks) // Has the elapsed ticks reached the target ticks?
+ if (a_Itr->a_ElapsedTicks >= a_Itr->a_DelayTicks) // Has the elapsed ticks reached the target ticks?
+ {
+ if (a_Itr->ShouldPowerOn)
{
- if (itr->ShouldPowerOn)
+ if (!IsOn)
{
- if (!IsOn)
+ m_Chunk->SetBlock(a_RelBlockX, a_RelBlockY, a_RelBlockZ, E_BLOCK_REDSTONE_REPEATER_ON, a_Meta); // For performance
+ }
+
+ switch (a_Meta & 0x3) // We only want the direction (bottom) bits
+ {
+ case 0x0:
{
- m_Chunk->SetBlock(a_RelBlockX, a_RelBlockY, a_RelBlockZ, E_BLOCK_REDSTONE_REPEATER_ON, a_Meta); // For performance
+ SetBlockPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ - 1, a_RelBlockX, a_RelBlockY, a_RelBlockZ);
+ SetDirectionLinkedPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, BLOCK_FACE_ZM);
+ break;
}
-
- switch (a_Meta & 0x3) // We only want the direction (bottom) bits
+ case 0x1:
{
- case 0x0:
- {
- SetBlockPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ - 1, a_RelBlockX, a_RelBlockY, a_RelBlockZ);
- SetDirectionLinkedPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, BLOCK_FACE_ZM);
- break;
- }
- case 0x1:
- {
- SetBlockPowered(a_RelBlockX + 1, a_RelBlockY, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ);
- SetDirectionLinkedPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, BLOCK_FACE_XP);
- break;
- }
- case 0x2:
- {
- SetBlockPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ + 1, a_RelBlockX, a_RelBlockY, a_RelBlockZ);
- SetDirectionLinkedPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, BLOCK_FACE_ZP);
- break;
- }
- case 0x3:
- {
- SetBlockPowered(a_RelBlockX - 1, a_RelBlockY, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ);
- SetDirectionLinkedPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, BLOCK_FACE_XM);
- break;
- }
+ SetBlockPowered(a_RelBlockX + 1, a_RelBlockY, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ);
+ SetDirectionLinkedPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, BLOCK_FACE_XP);
+ break;
}
-
- // Removal of the data entry will be handled in SimChunk - we still want to continue trying to power blocks, even if our delay time has reached
- // Otherwise, the power state of blocks in front won't update after we have powered on
- return;
- }
- else
- {
- if (IsOn)
+ case 0x2:
{
- m_Chunk->SetBlock(a_RelBlockX, a_RelBlockY, a_RelBlockZ, E_BLOCK_REDSTONE_REPEATER_OFF, a_Meta);
+ SetBlockPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ + 1, a_RelBlockX, a_RelBlockY, a_RelBlockZ);
+ SetDirectionLinkedPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, BLOCK_FACE_ZP);
+ break;
+ }
+ case 0x3:
+ {
+ SetBlockPowered(a_RelBlockX - 1, a_RelBlockY, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ);
+ SetDirectionLinkedPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, BLOCK_FACE_XM);
+ break;
}
- m_RepeatersDelayList->erase(itr); // We can remove off repeaters which don't need further updating
- return;
}
+
+ // Removal of the data entry will be handled in SimChunk - we still want to continue trying to power blocks, even if our delay time has reached
+ // Otherwise, the power state of blocks in front won't update after we have powered on
+ return;
}
else
{
- // Apparently, incrementing ticks only works reliably here, and not in SimChunk;
- // With a world with lots of redstone, the repeaters simply do not delay
- // I am confounded to say why. Perhaps optimisation failure.
- LOGD("Incremented a repeater @ {%i %i %i} | Elapsed ticks: %i | Target delay: %i", itr->a_RelBlockPos.x, itr->a_RelBlockPos.y, itr->a_RelBlockPos.z, itr->a_ElapsedTicks, itr->a_DelayTicks);
- itr->a_ElapsedTicks++;
+ if (IsOn)
+ {
+ m_Chunk->SetBlock(a_RelBlockX, a_RelBlockY, a_RelBlockZ, E_BLOCK_REDSTONE_REPEATER_OFF, a_Meta);
+ }
+ m_RepeatersDelayList->erase(a_Itr); // We can remove off repeaters which don't need further updating
+ return;
}
}
+ else
+ {
+ // Apparently, incrementing ticks only works reliably here, and not in SimChunk;
+ // With a world with lots of redstone, the repeaters simply do not delay
+ // I am confounded to say why. Perhaps optimisation failure.
+ LOGD("Incremented a repeater @ {%i %i %i} | Elapsed ticks: %i | Target delay: %i", a_Itr->a_RelBlockPos.x, a_Itr->a_RelBlockPos.y, a_Itr->a_RelBlockPos.z, a_Itr->a_ElapsedTicks, a_Itr->a_DelayTicks);
+ a_Itr->a_ElapsedTicks++;
+ }
}
@@ -905,8 +965,11 @@ void cIncrementalRedstoneSimulator::HandleDoor(int a_RelBlockX, int a_RelBlockY,
if (!AreCoordsSimulated(a_RelBlockX, a_RelBlockY, a_RelBlockZ, true))
{
cChunkInterface ChunkInterface(m_World.GetChunkMap());
- cBlockDoorHandler::ChangeDoor(ChunkInterface, a_RelBlockX, a_RelBlockY, a_RelBlockZ);
- m_Chunk->BroadcastSoundParticleEffect(1003, BlockX, a_RelBlockY, BlockZ, 0);
+ if (!cBlockDoorHandler::IsOpen(ChunkInterface, BlockX, a_RelBlockY, BlockZ))
+ {
+ cBlockDoorHandler::SetOpen(ChunkInterface, BlockX, a_RelBlockY, BlockZ, true);
+ m_Chunk->BroadcastSoundParticleEffect(1003, BlockX, a_RelBlockY, BlockZ, 0);
+ }
SetPlayerToggleableBlockAsSimulated(a_RelBlockX, a_RelBlockY, a_RelBlockZ, true);
}
}
@@ -915,8 +978,11 @@ void cIncrementalRedstoneSimulator::HandleDoor(int a_RelBlockX, int a_RelBlockY,
if (!AreCoordsSimulated(a_RelBlockX, a_RelBlockY, a_RelBlockZ, false))
{
cChunkInterface ChunkInterface(m_World.GetChunkMap());
- cBlockDoorHandler::ChangeDoor(ChunkInterface, a_RelBlockX, a_RelBlockY, a_RelBlockZ);
- m_Chunk->BroadcastSoundParticleEffect(1003, BlockX, a_RelBlockY, BlockZ, 0);
+ if (cBlockDoorHandler::IsOpen(ChunkInterface, BlockX, a_RelBlockY, BlockZ))
+ {
+ cBlockDoorHandler::SetOpen(ChunkInterface, BlockX, a_RelBlockY, BlockZ, false);
+ m_Chunk->BroadcastSoundParticleEffect(1003, BlockX, a_RelBlockY, BlockZ, 0);
+ }
SetPlayerToggleableBlockAsSimulated(a_RelBlockX, a_RelBlockY, a_RelBlockZ, false);
}
}
@@ -1730,7 +1796,8 @@ void cIncrementalRedstoneSimulator::SetBlockPowered(int a_RelBlockX, int a_RelBl
return;
}
- PoweredBlocksList * Powered = m_Chunk->GetNeighborChunk(BlockX, BlockZ)->GetRedstoneSimulatorPoweredBlocksList();
+ cChunk * Neighbour = m_Chunk->GetNeighborChunk(BlockX, BlockZ);
+ PoweredBlocksList * Powered = Neighbour->GetRedstoneSimulatorPoweredBlocksList();
for (PoweredBlocksList::iterator itr = Powered->begin(); itr != Powered->end(); ++itr) // Check powered list
{
if (
@@ -1762,6 +1829,8 @@ void cIncrementalRedstoneSimulator::SetBlockPowered(int a_RelBlockX, int a_RelBl
RC.a_SourcePos = Vector3i(SourceX, a_RelSourceY, SourceZ);
RC.a_PowerLevel = a_PowerLevel;
Powered->push_back(RC);
+ Neighbour->SetIsRedstoneDirty(true);
+ m_Chunk->SetIsRedstoneDirty(true);
}
@@ -1801,7 +1870,8 @@ void cIncrementalRedstoneSimulator::SetBlockLinkedPowered(
return;
}
- LinkedBlocksList * Linked = m_Chunk->GetNeighborChunk(BlockX, BlockZ)->GetRedstoneSimulatorLinkedBlocksList();
+ cChunk * Neighbour = m_Chunk->GetNeighborChunk(BlockX, BlockZ);
+ LinkedBlocksList * Linked = Neighbour->GetRedstoneSimulatorLinkedBlocksList();
for (LinkedBlocksList::iterator itr = Linked->begin(); itr != Linked->end(); ++itr) // Check linked powered list
{
if (
@@ -1822,6 +1892,8 @@ void cIncrementalRedstoneSimulator::SetBlockLinkedPowered(
RC.a_SourcePos = Vector3i(SourceX, a_RelSourceY, SourceZ);
RC.a_PowerLevel = a_PowerLevel;
Linked->push_back(RC);
+ Neighbour->SetIsRedstoneDirty(true);
+ m_Chunk->SetIsRedstoneDirty(true);
}
@@ -1861,7 +1933,7 @@ void cIncrementalRedstoneSimulator::SetPlayerToggleableBlockAsSimulated(int a_Re
-void cIncrementalRedstoneSimulator::QueueRepeaterPowerChange(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, NIBBLETYPE a_Meta, bool ShouldPowerOn)
+bool cIncrementalRedstoneSimulator::QueueRepeaterPowerChange(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, NIBBLETYPE a_Meta, bool ShouldPowerOn)
{
for (RepeatersDelayList::iterator itr = m_RepeatersDelayList->begin(); itr != m_RepeatersDelayList->end(); ++itr)
{
@@ -1869,14 +1941,14 @@ void cIncrementalRedstoneSimulator::QueueRepeaterPowerChange(int a_RelBlockX, in
{
if (ShouldPowerOn == itr->ShouldPowerOn) // We are queued already for the same thing, don't replace entry
{
- return;
+ return false;
}
// Already in here (normal to allow repeater to continue on powering and updating blocks in front) - just update info and quit
itr->a_DelayTicks = (((a_Meta & 0xC) >> 0x2) + 1) * 2; // See below for description
itr->a_ElapsedTicks = 0;
itr->ShouldPowerOn = ShouldPowerOn;
- return;
+ return false;
}
}
@@ -1891,7 +1963,7 @@ void cIncrementalRedstoneSimulator::QueueRepeaterPowerChange(int a_RelBlockX, in
RC.a_ElapsedTicks = 0;
RC.ShouldPowerOn = ShouldPowerOn;
m_RepeatersDelayList->push_back(RC);
- return;
+ return true;
}
diff --git a/src/Simulator/IncrementalRedstoneSimulator.h b/src/Simulator/IncrementalRedstoneSimulator.h
index 233a3d408..83076311a 100644
--- a/src/Simulator/IncrementalRedstoneSimulator.h
+++ b/src/Simulator/IncrementalRedstoneSimulator.h
@@ -108,7 +108,7 @@ private:
/** Handles redstone wire */
void HandleRedstoneWire(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ);
/** Handles repeaters */
- void HandleRedstoneRepeater(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, BLOCKTYPE a_MyState);
+ void HandleRedstoneRepeater(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, BLOCKTYPE a_MyState, RepeatersDelayList::iterator a_Itr);
/* ====================== */
/* ====== DEVICES ====== */
@@ -145,8 +145,8 @@ private:
void SetDirectionLinkedPowered(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, char a_Direction, unsigned char a_PowerLevel = MAX_POWER_LEVEL);
/** Marks all blocks immediately surrounding a coordinate as powered */
void SetAllDirsAsPowered(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, unsigned char a_PowerLevel = MAX_POWER_LEVEL);
- /** Queues a repeater to be powered or unpowered */
- void QueueRepeaterPowerChange(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, NIBBLETYPE a_Meta, bool ShouldPowerOn);
+ /** Queues a repeater to be powered or unpowered and returns if the m_RepeatersDelayList iterators were invalidated */
+ bool QueueRepeaterPowerChange(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, NIBBLETYPE a_Meta, bool ShouldPowerOn);
/** Returns if a coordinate is powered or linked powered */
bool AreCoordsPowered(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ) { return AreCoordsDirectlyPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ) || AreCoordsLinkedPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ); }
diff --git a/src/Simulator/SandSimulator.cpp b/src/Simulator/SandSimulator.cpp
index b8f34559f..1380f8841 100644
--- a/src/Simulator/SandSimulator.cpp
+++ b/src/Simulator/SandSimulator.cpp
@@ -60,7 +60,7 @@ void cSandSimulator::SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, cChun
);
*/
cFallingBlock * FallingBlock = new cFallingBlock(Pos, BlockType, a_Chunk->GetMeta(itr->x, itr->y, itr->z));
- FallingBlock->Initialize(&m_World);
+ FallingBlock->Initialize(m_World);
a_Chunk->SetBlock(itr->x, itr->y, itr->z, E_BLOCK_AIR, 0);
}
}
diff --git a/src/UI/Window.cpp b/src/UI/Window.cpp
index c0e2cef64..3922ac489 100644
--- a/src/UI/Window.cpp
+++ b/src/UI/Window.cpp
@@ -854,6 +854,7 @@ void cAnvilWindow::GetBlockPos(int & a_PosX, int & a_PosY, int & a_PosZ)
cEnchantingWindow::cEnchantingWindow(int a_BlockX, int a_BlockY, int a_BlockZ) :
cWindow(wtEnchantment, "Enchant"),
+ m_SlotArea(),
m_BlockX(a_BlockX),
m_BlockY(a_BlockY),
m_BlockZ(a_BlockZ)
diff --git a/src/World.cpp b/src/World.cpp
index 1f4a88fa0..6904000ea 100644
--- a/src/World.cpp
+++ b/src/World.cpp
@@ -813,6 +813,20 @@ void cWorld::Tick(float a_Dt, int a_LastTickDurationMSec)
m_LastTimeUpdate = m_WorldAge;
}
+ // Add entities waiting in the queue to be added:
+ {
+ cCSLock Lock(m_CSEntitiesToAdd);
+ for (cEntityList::iterator itr = m_EntitiesToAdd.begin(), end = m_EntitiesToAdd.end(); itr != end; ++itr)
+ {
+ (*itr)->SetWorld(this);
+ m_ChunkMap->AddEntity(*itr);
+ }
+ m_EntitiesToAdd.clear();
+ }
+
+ // Add players waiting in the queue to be added:
+ AddQueuedPlayers();
+
m_ChunkMap->Tick(a_Dt);
TickClients(a_Dt);
@@ -1701,7 +1715,7 @@ void cWorld::SpawnItemPickups(const cItems & a_Pickups, double a_BlockX, double
a_BlockX, a_BlockY, a_BlockZ,
*itr, IsPlayerCreated, SpeedX, SpeedY, SpeedZ
);
- Pickup->Initialize(this);
+ Pickup->Initialize(*this);
}
}
@@ -1722,7 +1736,7 @@ void cWorld::SpawnItemPickups(const cItems & a_Pickups, double a_BlockX, double
a_BlockX, a_BlockY, a_BlockZ,
*itr, IsPlayerCreated, (float)a_SpeedX, (float)a_SpeedY, (float)a_SpeedZ
);
- Pickup->Initialize(this);
+ Pickup->Initialize(*this);
}
}
@@ -1733,7 +1747,7 @@ void cWorld::SpawnItemPickups(const cItems & a_Pickups, double a_BlockX, double
int cWorld::SpawnFallingBlock(int a_X, int a_Y, int a_Z, BLOCKTYPE BlockType, NIBBLETYPE BlockMeta)
{
cFallingBlock * FallingBlock = new cFallingBlock(Vector3i(a_X, a_Y, a_Z), BlockType, BlockMeta);
- FallingBlock->Initialize(this);
+ FallingBlock->Initialize(*this);
return FallingBlock->GetUniqueID();
}
@@ -1749,7 +1763,7 @@ int cWorld::SpawnExperienceOrb(double a_X, double a_Y, double a_Z, int a_Reward)
}
cExpOrb * ExpOrb = new cExpOrb(a_X, a_Y, a_Z, a_Reward);
- ExpOrb->Initialize(this);
+ ExpOrb->Initialize(*this);
return ExpOrb->GetUniqueID();
}
@@ -1772,7 +1786,7 @@ int cWorld::SpawnMinecart(double a_X, double a_Y, double a_Z, int a_MinecartType
return -1;
}
} // switch (a_MinecartType)
- Minecart->Initialize(this);
+ Minecart->Initialize(*this);
return Minecart->GetUniqueID();
}
@@ -1783,7 +1797,7 @@ int cWorld::SpawnMinecart(double a_X, double a_Y, double a_Z, int a_MinecartType
void cWorld::SpawnPrimedTNT(double a_X, double a_Y, double a_Z, int a_FuseTicks, double a_InitialVelocityCoeff)
{
cTNTEntity * TNT = new cTNTEntity(a_X, a_Y, a_Z, a_FuseTicks);
- TNT->Initialize(this);
+ TNT->Initialize(*this);
TNT->SetSpeed(
a_InitialVelocityCoeff * (GetTickRandomNumber(2) - 1), /** -1, 0, 1 */
a_InitialVelocityCoeff * 2,
@@ -2299,7 +2313,7 @@ void cWorld::SetChunkData(
// Initialize the entities (outside the m_ChunkMap's CS, to fix FS #347):
for (cEntityList::iterator itr = a_Entities.begin(), end = a_Entities.end(); itr != end; ++itr)
{
- (*itr)->Initialize(this);
+ (*itr)->Initialize(*this);
}
// If a client is requesting this chunk, send it to them:
@@ -2392,23 +2406,8 @@ void cWorld::CollectPickupsByPlayer(cPlayer * a_Player)
void cWorld::AddPlayer(cPlayer * a_Player)
{
- {
- cCSLock Lock(m_CSPlayers);
-
- ASSERT(std::find(m_Players.begin(), m_Players.end(), a_Player) == m_Players.end()); // Is it already in the list? HOW?
-
- m_Players.remove(a_Player); // Make sure the player is registered only once
- m_Players.push_back(a_Player);
- }
-
- // Add the player's client to the list of clients to be ticked:
- if (a_Player->GetClientHandle() != NULL)
- {
- cCSLock Lock(m_CSClients);
- m_ClientsToAdd.push_back(a_Player->GetClientHandle());
- }
-
- // The player has already been added to the chunkmap as the entity, do NOT add again!
+ cCSLock Lock(m_CSPlayersToAdd);
+ m_PlayersToAdd.push_back(a_Player);
}
@@ -2417,17 +2416,26 @@ void cWorld::AddPlayer(cPlayer * a_Player)
void cWorld::RemovePlayer(cPlayer * a_Player)
{
+
m_ChunkMap->RemoveEntity(a_Player);
{
+ cCSLock Lock(m_CSPlayersToAdd);
+ m_PlayersToAdd.remove(a_Player);
+ }
+ {
cCSLock Lock(m_CSPlayers);
+ LOGD("Removing player \"%s\" from world \"%s\".", a_Player->GetName().c_str(), m_WorldName.c_str());
m_Players.remove(a_Player);
}
// Remove the player's client from the list of clients to be ticked:
- if (a_Player->GetClientHandle() != NULL)
+ cClientHandle * Client = a_Player->GetClientHandle();
+ if (Client != NULL)
{
+ Client->RemoveFromWorld();
+ m_ChunkMap->RemoveClientFromChunks(Client);
cCSLock Lock(m_CSClients);
- m_ClientsToRemove.push_back(a_Player->GetClientHandle());
+ m_ClientsToRemove.push_back(Client);
}
}
@@ -2878,9 +2886,11 @@ void cWorld::ScheduleTask(int a_DelayTicks, cTask * a_Task)
+
void cWorld::AddEntity(cEntity * a_Entity)
{
- m_ChunkMap->AddEntity(a_Entity);
+ cCSLock Lock(m_CSEntitiesToAdd);
+ m_EntitiesToAdd.push_back(a_Entity);
}
@@ -2889,6 +2899,19 @@ void cWorld::AddEntity(cEntity * a_Entity)
bool cWorld::HasEntity(int a_UniqueID)
{
+ // Check if the entity is in the queue to be added to the world:
+ {
+ cCSLock Lock(m_CSEntitiesToAdd);
+ for (cEntityList::const_iterator itr = m_EntitiesToAdd.begin(), end = m_EntitiesToAdd.end(); itr != end; ++itr)
+ {
+ if ((*itr)->GetUniqueID() == a_UniqueID)
+ {
+ return true;
+ }
+ } // for itr - m_EntitiesToAdd[]
+ }
+
+ // Check if the entity is in the chunkmap:
return m_ChunkMap->HasEntity(a_UniqueID);
}
@@ -3021,7 +3044,7 @@ int cWorld::SpawnMobFinalize(cMonster * a_Monster)
delete a_Monster;
return -1;
}
- if (!a_Monster->Initialize(this))
+ if (!a_Monster->Initialize(*this))
{
delete a_Monster;
return -1;
@@ -3043,7 +3066,7 @@ int cWorld::CreateProjectile(double a_PosX, double a_PosY, double a_PosZ, cProje
{
return -1;
}
- if (!Projectile->Initialize(this))
+ if (!Projectile->Initialize(*this))
{
delete Projectile;
return -1;
@@ -3173,6 +3196,60 @@ cFluidSimulator * cWorld::InitializeFluidSimulator(cIniFile & a_IniFile, const c
+void cWorld::AddQueuedPlayers(void)
+{
+ ASSERT(m_TickThread.IsCurrentThread());
+
+ // Grab the list of players to add, it has to be locked to access it:
+ cPlayerList PlayersToAdd;
+ {
+ cCSLock Lock(m_CSPlayersToAdd);
+ std::swap(PlayersToAdd, m_PlayersToAdd);
+ }
+
+ // Add all the players in the grabbed list:
+ {
+ cCSLock Lock(m_CSPlayers);
+ for (cPlayerList::iterator itr = PlayersToAdd.begin(), end = PlayersToAdd.end(); itr != end; ++itr)
+ {
+ ASSERT(std::find(m_Players.begin(), m_Players.end(), *itr) == m_Players.end()); // Is it already in the list? HOW?
+
+ m_Players.push_back(*itr);
+ (*itr)->SetWorld(this);
+
+ // Add to chunkmap, if not already there (Spawn vs MoveToWorld):
+ m_ChunkMap->AddEntityIfNotPresent(*itr);
+ } // for itr - PlayersToAdd[]
+ } // Lock(m_CSPlayers)
+
+ // Add all the players' clienthandles:
+ {
+ cCSLock Lock(m_CSClients);
+ for (cPlayerList::iterator itr = PlayersToAdd.begin(), end = PlayersToAdd.end(); itr != end; ++itr)
+ {
+ cClientHandle * Client = (*itr)->GetClientHandle();
+ if (Client != NULL)
+ {
+ m_Clients.push_back(Client);
+ }
+ } // for itr - PlayersToAdd[]
+ } // Lock(m_CSClients)
+
+ // Stream chunks to all eligible clients:
+ for (cPlayerList::iterator itr = PlayersToAdd.begin(), end = PlayersToAdd.end(); itr != end; ++itr)
+ {
+ cClientHandle * Client = (*itr)->GetClientHandle();
+ if (Client != NULL)
+ {
+ Client->StreamChunks();
+ }
+ } // for itr - PlayersToAdd[]
+}
+
+
+
+
+
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cWorld::cTaskSaveAllChunks:
@@ -3313,4 +3390,3 @@ void cWorld::cChunkGeneratorCallbacks::CallHookChunkGenerated (cChunkDesc & a_Ch
-
diff --git a/src/World.h b/src/World.h
index 676c5d69a..6ac58b09e 100644
--- a/src/World.h
+++ b/src/World.h
@@ -284,8 +284,15 @@ public:
void CollectPickupsByPlayer(cPlayer * a_Player);
- void AddPlayer( cPlayer* a_Player );
- void RemovePlayer( cPlayer* a_Player );
+ /** Adds the player to the world.
+ Uses a queue to store the player object until the Tick thread processes the addition event.
+ Also adds the player as an entity in the chunkmap, and the player's ClientHandle, if any, for ticking. */
+ void AddPlayer(cPlayer * a_Player);
+
+ /** Removes the player from the world.
+ Removes the player from the addition queue, too, if appropriate.
+ If the player has a ClientHandle, the ClientHandle is removed from all chunks in the world and will not be ticked by this world anymore. */
+ void RemovePlayer(cPlayer * a_Player);
/** Calls the callback for each player in the list; returns true if all players processed, false if the callback aborted by returning true */
virtual bool ForEachPlayer(cPlayerListCallback & a_Callback) override; // >> EXPORTED IN MANUALBINDINGS <<
@@ -301,7 +308,8 @@ public:
void SendPlayerList(cPlayer * a_DestPlayer); // Sends playerlist to the player
- /** Adds the entity into its appropriate chunk; takes ownership of the entity ptr */
+ /** Adds the entity into its appropriate chunk; takes ownership of the entity ptr.
+ The entity is added lazily - this function only puts it in a queue that is then processed by the Tick thread. */
void AddEntity(cEntity * a_Entity);
bool HasEntity(int a_UniqueID);
@@ -961,6 +969,18 @@ private:
/** Clients that are scheduled for adding, waiting for TickClients to add them */
cClientHandleList m_ClientsToAdd;
+ /** Guards m_EntitiesToAdd */
+ cCriticalSection m_CSEntitiesToAdd;
+
+ /** List of entities that are scheduled for adding, waiting for the Tick thread to add them. */
+ cEntityList m_EntitiesToAdd;
+
+ /** Guards m_PlayersToAdd */
+ cCriticalSection m_CSPlayersToAdd;
+
+ /** List of players that are scheduled for adding, waiting for the Tick thread to add them. */
+ cPlayerList m_PlayersToAdd;
+
cWorld(const AString & a_WorldName, eDimension a_Dimension = dimOverworld, const AString & a_OverworldName = "");
virtual ~cWorld();
@@ -998,6 +1018,10 @@ private:
/** Creates a new redstone simulator.*/
cRedstoneSimulator * InitializeRedstoneSimulator(cIniFile & a_IniFile);
+
+ /** Adds the players queued in the m_PlayersToAdd queue into the m_Players list.
+ Assumes it is called from the Tick thread. */
+ void AddQueuedPlayers(void);
}; // tolua_export