Merge branch 'master' into portals
Conflicts: src/ClientHandle.cpp src/Entities/Player.cpp src/Entities/Player.h src/Protocol/Protocol125.cpp src/Protocol/Protocol17x.cpp
@ -1969,7 +1969,7 @@ cPluginManager.AddHook(cPluginManager.HOOK_CHAT, OnChatMessage);
|
||||
BroadcastChatSuccess = { Params = "Message", Return = "", Notes = "Prepends Green [INFO] / colours entire text (depending on ShouldUseChatPrefixes()) and broadcasts message. For success messages." },
|
||||
BroadcastChatWarning = { Params = "Message", Return = "", Notes = "Prepends Rose [WARN] / colours entire text (depending on ShouldUseChatPrefixes()) and broadcasts message. For concerning events, such as plugin reload etc." },
|
||||
CreateAndInitializeWorld = { Params = "WorldName", Return = "{{cWorld|cWorld}}", Notes = "Creates a new world and initializes it. If there is a world whith the same name it returns nil." },
|
||||
FindAndDoWithPlayer = { Params = "PlayerName, CallbackFunction", Return = "", Notes = "Calls the given callback function for the given player." },
|
||||
FindAndDoWithPlayer = { Params = "PlayerName, CallbackFunction", Return = "", Notes = "Calls the given callback function for all players with names partially (or fully) matching the name string provided." },
|
||||
ForEachPlayer = { Params = "CallbackFunction", Return = "", Notes = "Calls the given callback function for each player. The callback function has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cPlayer|cPlayer}})</pre>" },
|
||||
ForEachWorld = { Params = "CallbackFunction", Return = "", Notes = "Calls the given callback function for each world. The callback function has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cWorld|cWorld}})</pre>" },
|
||||
GetCraftingRecipes = { Params = "", Return = "{{cCraftingRecipe|cCraftingRecipe}}", Notes = "Returns the CraftingRecipes object" },
|
||||
|
@ -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
|
||||
! 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
|
||||
|
@ -240,6 +240,7 @@ template <typename Type> class cItemCallback
|
||||
public:
|
||||
/// Called for each item in the internal list; return true to stop the loop, or false to continue enumerating
|
||||
virtual bool Item(Type * a_Type) = 0;
|
||||
virtual ~cItemCallback() {};
|
||||
} ;
|
||||
|
||||
|
||||
|
@ -19,6 +19,8 @@ with specific implementation notes regarding MCServer.</p>
|
||||
<li><a href="#heightgen">Terrain height</a></li>
|
||||
<li><a href="#compositiongen">Terrain composition</a></li>
|
||||
<li><a href="#finishgen">Finishers</a></li>
|
||||
<li><a href="#makefaster">Making it all faster</a></li>
|
||||
<li><a href="#GPU">Executing on a GPU</a></li>
|
||||
</ul>
|
||||
</p>
|
||||
|
||||
@ -304,16 +306,224 @@ using the same approach as in MultiStepMap - by using a thresholded 2D Perlin no
|
||||
<hr />
|
||||
|
||||
<a name="heightgen"><h2>Terrain height</h2></a>
|
||||
<p>As with biomes, the easiest way to generate terrain height is not generating at all - assigning a constant
|
||||
height value to all columns. This is again useful either for internal tests, and for worlds like MineCraft's
|
||||
Flat world.</p>
|
||||
|
||||
<p>For a somewhat more realistic landscape, we will employ the good old 2D Perlin noise. We can use it
|
||||
directly as a heightmap - each value we get from the noise is stretched into the desired range (usually from
|
||||
40 to 120 blocks for regular MineCraft worlds) and used as the height value. However, this doesn't play too
|
||||
well with the biomes we've just generated. If the biome says "ocean" and the Perlin noise says "mountain",
|
||||
the end result will be unpleasant.</p>
|
||||
|
||||
<p>So we want a height generator that is biome-aware. The easiest way of doing this is to have a separate
|
||||
generator for each biome. Simply use the biome map to select which generator to use, then ask the appropriate
|
||||
generator for the height value. Again, this doesn't work too well - imagine an ExtremeHills biome right next
|
||||
to an Ocean biome. If no extra care is taken, the border between these two will be a high wall. The following
|
||||
image shows a 2D representation (for simplification purposes) of the problem:</p>
|
||||
<img src="img/biomeheights.jpg" />
|
||||
|
||||
<p>This requires some further processing. What we need is for the terrain height to be dependent not only on
|
||||
the immediate biome for that column, but also on the close surroundings of the column. This is exactly the
|
||||
kind of task that averaging is designed for. If we take the area of 9x9 biomes centered around the queried
|
||||
column, generate height for each of the biomes therein, sum them up and divide by 81 (the number of biomes
|
||||
summed), we will be effectively making a 9-long running average over the terrain, and all the borders will
|
||||
suddenly become smooth. The following image shows the situation from the previous paragraph after applying
|
||||
the averaging process: </p>
|
||||
<img src="img/biomeheightsavg.jpg" />
|
||||
|
||||
<p>The approach used in MCServer's Biomal generator is based on this idea, with two slight modifications.
|
||||
Instead of using a separate generator for each biome, one generator is used with a different set of input
|
||||
parameters for each biomes. These input parameters modify the overall amplitude and frequency of the Perlin
|
||||
noise that the generator produces, thus modifying the final terrain with regards to biomes. Additionally, the
|
||||
averaging process is weighted - columns closer to the queried column get a more powerful weight in the sum
|
||||
than the columns further away. The following image shows the output of MCServer's Biomal terrain height
|
||||
generator (each block type represents a different biome - ocean in the front (stone), plains and ice plains
|
||||
behind it (lapis, whitewool), extreme hills back right (soulsand), desert hills back left (mossy
|
||||
cobble)):</p>
|
||||
<a name="biomalheights"><img src="img/biomalheights.jpg" /></a>
|
||||
|
||||
<p>One key observation about this whole approach is that in order for it to work, the biomes must be
|
||||
available for columns outside the currently generated chunk, otherwise the columns at the chunk's edge would
|
||||
not be able to properly average their height. This requirement can be fulfilled only by biome generators that
|
||||
adhere to the second <a href="#expectedproperties">Expected property</a> - that re-generating will produce
|
||||
the same data. If the biome generator returned different data for the same chunk each time it was invoked, it
|
||||
would become impossible to apply the averaging.</p>
|
||||
|
||||
<p>(TODO: height with variations (N/A in MCS yet)</p>
|
||||
|
||||
|
||||
<hr />
|
||||
|
||||
<a name="compositiongen"><h2>Terrain composition</h2></a>
|
||||
<p>As with the other generators, the composition generator category has its easy and debugging items, too.
|
||||
There's the "special" composition of "all the blocks are the same type", which fills the entire column, from
|
||||
the bottom to the height, with a single blocktype. This generator is useful when testing the generators in
|
||||
the other categories, to speed up the generation by leaving out unnecessary calculations. Another special
|
||||
compositor is a similar one, that fills all blocks with the same type, but the type varies for each biome.
|
||||
This way it's easy to see the generated biomes and possibly the heights for those biomes, as shown in the
|
||||
previous section on the <a href="#biomalheights">height averaging screenshot</a>.</p>
|
||||
|
||||
<p>For a natural look, we need to put together a more complicated algorithm. The standard set forth in
|
||||
MineCraft is that the top of the world is covered in grass, then there are a few blocks of dirt and finally
|
||||
stone. This basic layout is then varied for different biomes - deserts have sand and sandstone instead of the
|
||||
grass and dirt layer. Mushroom biomes have mycelium in place of the grass. This per-biome dependency is
|
||||
trivial to implement - when compositing, simply use the appropriate layout for the column's biome.</p>
|
||||
|
||||
<p>The next change concerns oceans. The generated heightmap doesn't include any waterlevel indication
|
||||
whatsoever. So it's up to the terrain compositor to actually decide where to place water. We do this by
|
||||
configuration - simply have a value in the config file specifying the sealevel height. The compositor then
|
||||
has to add water above any column which has a height lower than that. Additionally, the water needs to
|
||||
override per-biome layout selection - we don't want grass blocks to generate under water when the terrain
|
||||
height in the plains biome drops below the sealevel accidentally.</p>
|
||||
|
||||
<p>The final feature in the compositor is the decision between multiple composition layouts within a single
|
||||
biome. A megataiga biome contains patches of non-grass dirt and podzol blocks, and the ocean floor can be
|
||||
made of dirt, gravel, sand or clay. A simple 2D Perlin noise can be used to select the layout to use for a
|
||||
specific column - simply threshold the noise's value by as many thresholds as there are layout variations,
|
||||
and use the layout corresponding to the threshold:</p>
|
||||
<img src="img/perlincompositor1.jpg" />
|
||||
<img src="img/perlincompositor2.jpg" />
|
||||
<img src="img/perlincompositor3.jpg" />
|
||||
|
||||
<h3>Nether composition</h3>
|
||||
<p>So far we've been discussing only the Overworld generator. But MineCraft contains more than that. The
|
||||
Nether has a completely different look and feel, and quite different processes are required to generate that.
|
||||
Recall that MineCraft's Nether is 128 blocks high, with bedrock both at the top and the bottom. Between these
|
||||
two, the terrain looks more like a cavern than a surface. Not surprisingly, the Nether doesn't need a
|
||||
complicated height generator, it can use the flat height. However, the terrain composition must take an
|
||||
altogether different approach.</p>
|
||||
|
||||
<p>The very first idea is to use the Perlin noise, but generate it in 3D, rather than 2D. Then, for each
|
||||
block, evaluate the noise value, if below 0, make it air, if not, make it netherrack.
|
||||
|
||||
<p>To make it so that the bedrock at the top and at the bottom is never revealed, we can add a value
|
||||
increasing the more the Y coord gets towards the bottom or the top. This way the thresholding then guarantees
|
||||
that there will be no air anywhere near the bedrock.</p>
|
||||
|
||||
<p>(TODO)</p>
|
||||
|
||||
|
||||
<hr />
|
||||
|
||||
<a name="finishgen"><h2>Finishers</h2></a>
|
||||
<p>Finishers are a vast category of various additions to the terrain generator. They range from very easy
|
||||
ones, such as generating snow on top of the terrain in cold biomes, through medium ones, such as growing
|
||||
patches of flowers, complicated ones, such as placing trees and generating caves, all the way to very
|
||||
complicated ones such as villages and nether fortresses. There is no formal distinction between all these
|
||||
"categories", the only thing they have in common is that they take a chunk of blocks and modify it in some
|
||||
way.</p>
|
||||
|
||||
<h3>Snow</h3>
|
||||
<p>Snow is probably the easiest of the finishers. It generates a block of snow on top of each block that is
|
||||
on top of the terrain and is not marked as non-snowable. It checks the chunk's heightmap to determine the top
|
||||
block, then checks whether the block supports snow on its top. Rails, levers and tall grass don't support
|
||||
snow, for example.</p>
|
||||
|
||||
<h3>Ice</h3>
|
||||
<p>Another example of an easy finisher. This scans through the world and turn each water block on the surface
|
||||
into an ice block if the biome is cold. This means that any water block that is under any kind of other
|
||||
block, such as under a tree's leaves, will still stay water. Thus an additional improvement could be made by
|
||||
scanning down from the surface block through blocks that we deem as non-surface, such as leaves, torches,
|
||||
ladders, fences etc. Note that MCServer currently implements only the easy solution.</p>
|
||||
|
||||
<h3>Bottom lava</h3>
|
||||
<p>Most worlds in MineCraft have lava lakes at their bottom. Generating these is pretty straightforward: Use
|
||||
the user-configured depth and replace all the air blocks below this depth with lava blocks. Note however,
|
||||
that this makes this generator dependent on the order in which the finishers are applied. If the mineshafts
|
||||
generate before bottom lava, the mineshafts that are below the lava level will get filled with lava. On the
|
||||
other hand, if bottom lava is generated before the mineshafts, it is possible for a mineshaft to "drill
|
||||
through" a lake of lava. MCServer doesn't try to solve this and instead lets the admin choose whichever they
|
||||
prefer.</p>
|
||||
|
||||
<h3>Specific foliage</h3>
|
||||
<p>There are generators for specific kinds of foliage. The dead bushes in the desert biome and lilypads in
|
||||
the swamp biome both share the same generating pattern. They are both specific to a single biome and they
|
||||
both require a specific block underneath them in order to generate. Their implementation is simple: pick
|
||||
several random columns in the chunk. If the column is of the correct biome and has the correct top block,
|
||||
add the foliage block on top.</p>
|
||||
|
||||
<p>In order to generate the same set of coordinates when the chunk is re-generated, we use the Perlin noise's
|
||||
basis functions (the ones providing the random values for Perlin cell vertices). These basically work as a
|
||||
hash function for the coorinates - the same input coordinates generate the same output value. We use the
|
||||
chunk's coordinates as two of the coords, and the iteration number as the third coordinate, to generate a
|
||||
random number. We then check the biome and the top block at those coordinates, if they allow, we generate the
|
||||
foliage block on top.</p>
|
||||
|
||||
<p>Another example of specific foliage is the tall grass in the plains biome. There are quite a lot of these
|
||||
tall grass blocks, it would be inefficient to generate them using the random-coords approach described above.
|
||||
Instead, we will use a 2D Perlin noise again, with a threshold defining where to put the grass and where
|
||||
not.</p>
|
||||
|
||||
<h3>Small foliage</h3>
|
||||
<p>For the flowers, grass, mushrooms in caves etc. we want to use a slightly different algorithm. These
|
||||
foliage blocks are customarily generated in small "clumps" - there are several blocks of the same type near
|
||||
together. To generate these, we first select random coords, using the coord hash functions, for a center of a
|
||||
clump. Then we select the type of block to generate. Finally, we loop over adding a random (coord hash)
|
||||
number to the clump center coords to get the block where to generate the foliage block:</p>
|
||||
<img src="img/smallfoliageclumps.jpg" />
|
||||
|
||||
<p>In order to make the clump more "round" and "centered", we want the offsets to be closer to the clump
|
||||
center more often. This is done using a thing called Gaussian function distribution. Instead of having each
|
||||
random number generate with the same probability, we want higher probability for the numbers around zero,
|
||||
like this:</p>
|
||||
<img src="img/gaussprobability.jpg" />
|
||||
|
||||
<p>Instead of doing complicated calculations to match this shape exactly, we will use a much easier shape.
|
||||
By adding together two random numbers in the same range, we get the probability distribution that has a
|
||||
"roof" shape, enough for our needs:</p>
|
||||
<img src="img/roofprobability.jpg" />
|
||||
|
||||
<p>(For the curious, there is a proof that adding together infinitely many uniform-distributed random numbers
|
||||
produces random numbers with the Gaussian distribution.)</p>
|
||||
|
||||
<p>This scheme can be used to produce clumps of flowers, when we select the 2D coords of the clump center on
|
||||
the top surface of the terrain. We simply generate the 2D coords of the foliage blocks and use the terrain
|
||||
height to find the third coord. If we want to generate clumps of mushrooms in the caves, however, we need to
|
||||
generate the clump center coords in 3D and either use 3 offsets for the mushrooms, or use 2 offsets plus
|
||||
searching for the closest opening Y-wise in the terrain.</p>
|
||||
|
||||
<p>Note that the clumps generated by this scheme may overlap several chunks. Therefore it's crucial to
|
||||
actually check the surrounding chunks if their clumps overlap into the currently generated chunk, and apply
|
||||
those as well, otherwise there will be visible cuts in the foliage along the chunks borders.</p>
|
||||
|
||||
<h3>Springs</h3>
|
||||
<p>Water and lava springs are essential for making the underground quite a lot more interesting. They are
|
||||
rather easy to generate, but a bit more difficult to get right. Generating simply means that a few random
|
||||
locations (obtained by our familiar coord hashing) are checked and if the block type in there is stone. Then
|
||||
we see all the horizontal neighbors of the block, plus the block underneath. If all of them except one are
|
||||
stone, and the one left is air, our block is suitable for turning into a spring. If there were more air
|
||||
neighbors, the spring would look somewhat unnatural; if there were no air neighbors, the spring won't flow
|
||||
anywhere, so it would be rather useless.</p>
|
||||
|
||||
<p>The difficult part about springs is the amount of them to generate. There should be a few springs on the
|
||||
surface, perhaps a bit more in the mountaineous biomes. There should be quite a few more springs underground,
|
||||
but there should definitely be more water springs than lava springs in the upper levels of the terrain, while
|
||||
there should be more lava springs and almost no water springs near the bottom. To accomodate this, the
|
||||
MCServer team has made a tool that scanned through MineCraft's terrain and counted the amount of both types
|
||||
of springs in relation to their height. Two curves have been found for the distribution of each type of the
|
||||
spring:</p>
|
||||
<img src="http://mc-server.xoft.cz/img/vanilla_springs_huge.png" />
|
||||
|
||||
<p>MCServer uses an approximation of the above curves to choose the height at which to generate the
|
||||
spring.</p>
|
||||
|
||||
<!--
|
||||
<h3>Caves</h3>
|
||||
<p>Caves are definitely one of the main things people notice about MineCraft terrain. There are quite a lot
|
||||
of different algorithms available to generate terrain with caves.
|
||||
-->
|
||||
|
||||
<hr />
|
||||
|
||||
<a name="makefaster"><h2>Making it all faster</h2></a>
|
||||
<p>(TODO)</p>
|
||||
|
||||
<a name="GPU"><h2>Executing on a GPU</h2></a>
|
||||
<p>Much of the terain generation consists of doing the same thing for every single column or block in a chunk. This
|
||||
sort of computation is much faster on a GPU as GPUs are massively parallel. High end GPUs can execute up to 30,000
|
||||
threads simultaneously, which would allow them to generate every block in half a chunk in parallel or every column
|
||||
in over 100 chunks in parallel. A naive comparison suggests that a 800MHz GPU with 15,000 threads can execute parallel
|
||||
code 250 times faster than a 3GHz CPU with 128 bit SIMD. Obviously we want to harness that power.</p>
|
||||
</body>
|
||||
</html>
|
||||
|
BIN
docs/img/biomalheights.jpg
Normal file
After Width: | Height: | Size: 76 KiB |
BIN
docs/img/biomeheights.jpg
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
docs/img/biomeheightsavg.jpg
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
docs/img/gaussprobability.jpg
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
docs/img/perlincompositor1.jpg
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
docs/img/perlincompositor2.jpg
Normal file
After Width: | Height: | Size: 28 KiB |
BIN
docs/img/perlincompositor3.jpg
Normal file
After Width: | Height: | Size: 21 KiB |
BIN
docs/img/roofprobability.jpg
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
docs/img/smallfoliageclumps.jpg
Normal file
After Width: | Height: | Size: 16 KiB |
93
lib/tolua++/src/bin/lua/_driver.lua
Normal file
@ -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")
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
27
src/Bindings/AllToLua_lua.bat.bat
Normal file
@ -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
|
||||
|
||||
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
} ;
|
||||
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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));
|
||||
}
|
||||
} ;
|
||||
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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[]
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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 */
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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:
|
||||
|
||||
|
@ -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
|
||||
{
|
||||
|
1406
src/Generating/Prefabs/RainbowRoadPrefabs.cpp
Normal file
15
src/Generating/Prefabs/RainbowRoadPrefabs.h
Normal file
@ -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;
|
115
src/Generating/RainbowRoadsGen.cpp
Normal file
@ -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));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
47
src/Generating/RainbowRoadsGen.h
Normal file
@ -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;
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -66,7 +66,7 @@ public:
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!Arrow->Initialize(a_Player->GetWorld()))
|
||||
if (!Arrow->Initialize(*a_Player->GetWorld()))
|
||||
{
|
||||
delete Arrow;
|
||||
return;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -70,7 +70,7 @@ public:
|
||||
return false;
|
||||
}
|
||||
} // switch (m_ItemType)
|
||||
Minecart->Initialize(a_World);
|
||||
Minecart->Initialize(*a_World);
|
||||
|
||||
if (!a_Player->IsGameModeCreative())
|
||||
{
|
||||
|
@ -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())
|
||||
{
|
||||
|
@ -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)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -46,7 +46,7 @@ void cGhast::Attack(float a_Dt)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!GhastBall->Initialize(m_World))
|
||||
if (!GhastBall->Initialize(*m_World))
|
||||
{
|
||||
delete GhastBall;
|
||||
return;
|
||||
|
@ -81,7 +81,7 @@ void cSkeleton::Attack(float a_Dt)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!Arrow->Initialize(m_World))
|
||||
if (!Arrow->Initialize(*m_World))
|
||||
{
|
||||
delete Arrow;
|
||||
return;
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
170
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
64
src/Noise.h
@ -192,6 +192,70 @@ protected:
|
||||
|
||||
|
||||
|
||||
class cRidgedMultiNoise
|
||||
{
|
||||
public:
|
||||
cRidgedMultiNoise(void);
|
||||
cRidgedMultiNoise(int a_Seed);
|
||||
|
||||
|
||||
void SetSeed(int a_Seed);
|
||||
|
||||
void AddOctave(NOISE_DATATYPE a_Frequency, NOISE_DATATYPE a_Amplitude);
|
||||
|
||||
void Generate1D(
|
||||
NOISE_DATATYPE * a_Array, ///< Array to generate into
|
||||
int a_SizeX, ///< Count of the array
|
||||
NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array
|
||||
NOISE_DATATYPE * a_Workspace = NULL ///< Workspace that this function can use and trash
|
||||
) const;
|
||||
|
||||
|
||||
void Generate2D(
|
||||
NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y]
|
||||
int a_SizeX, int a_SizeY, ///< Count of the array, in each direction
|
||||
NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array in the X direction
|
||||
NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY, ///< Noise-space coords of the array in the Y direction
|
||||
NOISE_DATATYPE * a_Workspace = NULL ///< Workspace that this function can use and trash
|
||||
) const;
|
||||
|
||||
|
||||
void Generate3D(
|
||||
NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y + a_SizeX * a_SizeY * z]
|
||||
int a_SizeX, int a_SizeY, int a_SizeZ, ///< Count of the array, in each direction
|
||||
NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array in the X direction
|
||||
NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY, ///< Noise-space coords of the array in the Y direction
|
||||
NOISE_DATATYPE a_StartZ, NOISE_DATATYPE a_EndZ, ///< Noise-space coords of the array in the Z direction
|
||||
NOISE_DATATYPE * a_Workspace = NULL ///< Workspace that this function can use and trash
|
||||
) const;
|
||||
|
||||
protected:
|
||||
class cOctave
|
||||
{
|
||||
public:
|
||||
cCubicNoise m_Noise;
|
||||
|
||||
NOISE_DATATYPE m_Frequency; // Coord multiplier
|
||||
NOISE_DATATYPE m_Amplitude; // Value multiplier
|
||||
|
||||
cOctave(int a_Seed, NOISE_DATATYPE a_Frequency, NOISE_DATATYPE a_Amplitude) :
|
||||
m_Noise(a_Seed),
|
||||
m_Frequency(a_Frequency),
|
||||
m_Amplitude(a_Amplitude)
|
||||
{
|
||||
}
|
||||
} ;
|
||||
|
||||
typedef std::vector<cOctave> cOctaves;
|
||||
|
||||
int m_Seed;
|
||||
cOctaves m_Octaves;
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Inline function definitions:
|
||||
// These need to be in the header, otherwise linker error occur in MSVC
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
@ -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); }
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
136
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
|
||||
|
||||
|
||||
|
||||
|
||||
|
30
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
|
||||
|
||||
|
||||
|