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:
function Callback({{cPlayer|cPlayer}})
" }, ForEachWorld = { Params = "CallbackFunction", Return = "", Notes = "Calls the given callback function for each world. The callback function has the following signature:
function Callback({{cWorld|cWorld}})
" }, 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 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.

  • Terrain height
  • Terrain composition
  • Finishers
  • +
  • Making it all faster
  • +
  • Executing on a GPU
  • @@ -304,16 +306,224 @@ using the same approach as in MultiStepMap - by using a thresholded 2D Perlin no

    Terrain height

    +

    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.

    + +

    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.

    + +

    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:

    + + +

    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:

    + + +

    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)):

    + + +

    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 Expected property - 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.

    + +

    (TODO: height with variations (N/A in MCS yet)


    Terrain composition

    +

    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 height averaging screenshot.

    + +

    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.

    + +

    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.

    + +

    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:

    + + + + +

    Nether composition

    +

    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.

    + +

    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. + +

    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.

    + +

    (TODO)


    Finishers

    +

    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.

    +

    Snow

    +

    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.

    + +

    Ice

    +

    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.

    + +

    Bottom lava

    +

    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.

    + +

    Specific foliage

    +

    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.

    + +

    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.

    + +

    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.

    + +

    Small foliage

    +

    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:

    + + +

    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:

    + + +

    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:

    + + +

    (For the curious, there is a proof that adding together infinitely many uniform-distributed random numbers +produces random numbers with the Gaussian distribution.)

    + +

    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.

    + +

    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.

    + +

    Springs

    +

    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.

    + +

    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:

    + + +

    MCServer uses an approximation of the above curves to choose the height at which to generate the +spring.

    + + + +
    + +

    Making it all faster

    +

    (TODO)

    + +

    Executing on a GPU

    +

    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.

    diff --git a/docs/img/biomalheights.jpg b/docs/img/biomalheights.jpg new file mode 100644 index 000000000..a01faef87 Binary files /dev/null and b/docs/img/biomalheights.jpg differ diff --git a/docs/img/biomeheights.jpg b/docs/img/biomeheights.jpg new file mode 100644 index 000000000..9dda27b0e Binary files /dev/null and b/docs/img/biomeheights.jpg differ diff --git a/docs/img/biomeheightsavg.jpg b/docs/img/biomeheightsavg.jpg new file mode 100644 index 000000000..c8217cafc Binary files /dev/null and b/docs/img/biomeheightsavg.jpg differ diff --git a/docs/img/gaussprobability.jpg b/docs/img/gaussprobability.jpg new file mode 100644 index 000000000..77da24748 Binary files /dev/null and b/docs/img/gaussprobability.jpg differ diff --git a/docs/img/perlincompositor1.jpg b/docs/img/perlincompositor1.jpg new file mode 100644 index 000000000..0d8f93cd9 Binary files /dev/null and b/docs/img/perlincompositor1.jpg differ diff --git a/docs/img/perlincompositor2.jpg b/docs/img/perlincompositor2.jpg new file mode 100644 index 000000000..11fc5b51d Binary files /dev/null and b/docs/img/perlincompositor2.jpg differ diff --git a/docs/img/perlincompositor3.jpg b/docs/img/perlincompositor3.jpg new file mode 100644 index 000000000..46a2583ba Binary files /dev/null and b/docs/img/perlincompositor3.jpg differ diff --git a/docs/img/roofprobability.jpg b/docs/img/roofprobability.jpg new file mode 100644 index 000000000..e7a155113 Binary files /dev/null and b/docs/img/roofprobability.jpg differ diff --git a/docs/img/smallfoliageclumps.jpg b/docs/img/smallfoliageclumps.jpg new file mode 100644 index 000000000..4cc6cbc00 Binary files /dev/null and b/docs/img/smallfoliageclumps.jpg 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); + NIBBLETYPE Meta = a_ChunkInterface.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); - a_ChunkInterface.SetBlockMeta(a_X, a_Y, a_Z, ChangeStateMetaData(OldMetaData)); - - 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); - - if (IsDoor(TopBlock) && (TopMeta & 8)) + // The coords are pointing at the bottom part of the door + if (a_BlockY < cChunkDef::Height - 1) { - a_ChunkInterface.SetBlockMeta(a_X, a_Y + 1, a_Z, ChangeStateMetaData(TopMeta)); + 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; + } + } + + + /** 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_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); @@ -300,6 +304,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: 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 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); - } - } - - for (RepeatersDelayList::iterator itr = m_RepeatersDelayList->begin(); itr != m_RepeatersDelayList->end(); ++itr) - { - if (!itr->a_RelBlockPos.Equals(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ))) - { - continue; - } - - if (itr->a_ElapsedTicks >= itr->a_DelayTicks) // Has the elapsed ticks reached the target ticks? - { - if (itr->ShouldPowerOn) - { - 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: - { - 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; - } - } - - // 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) - { - m_Chunk->SetBlock(a_RelBlockX, a_RelBlockY, a_RelBlockZ, E_BLOCK_REDSTONE_REPEATER_OFF, a_Meta); - } - m_RepeatersDelayList->erase(itr); // We can remove off repeaters which don't need further updating - return; - } + WereItrsChanged = QueueRepeaterPowerChange(a_RelBlockX, a_RelBlockY, a_RelBlockZ, a_Meta, false); } 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++; + return; } } + else + { + return; + } + + if (WereItrsChanged) + { + for (a_Itr = m_RepeatersDelayList->begin(); a_Itr != m_RepeatersDelayList->end(); ++a_Itr) + { + if (a_Itr->a_RelBlockPos == Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ)) + { + break; + } + } + } + + if (a_Itr->a_ElapsedTicks >= a_Itr->a_DelayTicks) // Has the elapsed ticks reached the target ticks? + { + if (a_Itr->ShouldPowerOn) + { + 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: + { + 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; + } + } + + // 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) + { + 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