summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CONTRIBUTING.md38
-rw-r--r--MCServer/Plugins/@EnableMobDebug.lua29
-rw-r--r--MCServer/Plugins/APIDump/APIDesc.lua407
-rw-r--r--MCServer/Plugins/APIDump/APIDump.deproj3
-rw-r--r--MCServer/Plugins/APIDump/Classes/Geometry.lua325
-rw-r--r--MCServer/Plugins/InfoReg.lua16
-rw-r--r--MCServer/crafting.txt2
-rw-r--r--MCServer/lua5.1.dllbin0 -> 6722 bytes
-rw-r--r--lib/lua/CMakeLists.txt6
-rw-r--r--src/Bindings/AllToLua.pkg10
-rw-r--r--src/Bindings/LuaState.cpp7
-rw-r--r--src/Bindings/ManualBindings.cpp77
-rw-r--r--src/BlockArea.cpp107
-rw-r--r--src/BlockArea.h95
-rw-r--r--src/BlockEntities/CommandBlockEntity.cpp3
-rw-r--r--src/BlockEntities/DropSpenserEntity.cpp1
-rw-r--r--src/BlockEntities/FurnaceEntity.cpp6
-rw-r--r--src/BlockEntities/FurnaceEntity.h2
-rw-r--r--src/BlockEntities/HopperEntity.cpp3
-rw-r--r--src/BlockEntities/NoteEntity.cpp1
-rw-r--r--src/Blocks/BlockBed.cpp25
-rw-r--r--src/Chunk.cpp7
-rw-r--r--src/Chunk.h1
-rw-r--r--src/ChunkDef.h1
-rw-r--r--src/ChunkMap.cpp4
-rw-r--r--src/ClientHandle.cpp124
-rw-r--r--src/ClientHandle.h76
-rw-r--r--src/Cuboid.cpp82
-rw-r--r--src/Cuboid.h27
-rw-r--r--src/Enchantments.h1
-rw-r--r--src/Entities/Boat.cpp10
-rw-r--r--src/Entities/Player.cpp3
-rw-r--r--src/Generating/StructGen.cpp9
-rw-r--r--src/Globals.h10
-rw-r--r--src/Inventory.cpp25
-rw-r--r--src/Inventory.h3
-rw-r--r--src/Items/ItemEmptyMap.h62
-rw-r--r--src/Items/ItemHandler.cpp4
-rw-r--r--src/Items/ItemHandler.h8
-rw-r--r--src/Items/ItemMap.h43
-rw-r--r--src/LightingThread.h6
-rw-r--r--src/Log.cpp4
-rw-r--r--src/Map.cpp631
-rw-r--r--src/Map.h264
-rw-r--r--src/MapManager.cpp178
-rw-r--r--src/MapManager.h78
-rw-r--r--src/MersenneTwister.h2
-rw-r--r--src/Mobs/Blaze.cpp6
-rw-r--r--src/Mobs/Cavespider.cpp12
-rw-r--r--src/Mobs/Chicken.cpp9
-rw-r--r--src/Mobs/Cow.cpp9
-rw-r--r--src/Mobs/Creeper.cpp7
-rw-r--r--src/Mobs/Enderman.cpp7
-rw-r--r--src/Mobs/Ghast.cpp9
-rw-r--r--src/Mobs/Horse.cpp7
-rw-r--r--src/Mobs/IronGolem.cpp2
-rw-r--r--src/Mobs/Magmacube.cpp6
-rw-r--r--src/Mobs/Monster.cpp76
-rw-r--r--src/Mobs/Monster.h35
-rw-r--r--src/Mobs/Mooshroom.cpp50
-rw-r--r--src/Mobs/Mooshroom.h1
-rw-r--r--src/Mobs/Pig.cpp7
-rw-r--r--src/Mobs/Skeleton.cpp22
-rw-r--r--src/Mobs/Slime.cpp11
-rw-r--r--src/Mobs/SnowGolem.cpp3
-rw-r--r--src/Mobs/Spider.cpp12
-rw-r--r--src/Mobs/Squid.cpp7
-rw-r--r--src/Mobs/Witch.cpp29
-rw-r--r--src/Mobs/Witch.h1
-rw-r--r--src/Mobs/Zombie.cpp16
-rw-r--r--src/Mobs/Zombiepigman.cpp15
-rw-r--r--src/OSSupport/Queue.h6
-rw-r--r--src/Protocol/Protocol.h4
-rw-r--r--src/Protocol/Protocol125.cpp52
-rw-r--r--src/Protocol/Protocol125.h3
-rw-r--r--src/Protocol/Protocol17x.cpp55
-rw-r--r--src/Protocol/Protocol17x.h3
-rw-r--r--src/Protocol/ProtocolRecognizer.cpp30
-rw-r--r--src/Protocol/ProtocolRecognizer.h3
-rw-r--r--src/Server.cpp4
-rw-r--r--src/Simulator/IncrementalRedstoneSimulator.cpp4
-rw-r--r--src/World.cpp8
-rw-r--r--src/World.h7
-rw-r--r--src/WorldStorage/FastNBT.h14
-rw-r--r--src/WorldStorage/MapSerializer.cpp276
-rw-r--r--src/WorldStorage/MapSerializer.h86
-rw-r--r--src/WorldStorage/NBTChunkSerializer.cpp8
-rw-r--r--src/WorldStorage/WSSAnvil.cpp183
-rw-r--r--src/WorldStorage/WSSAnvil.h7
-rw-r--r--src/main.cpp31
90 files changed, 3474 insertions, 495 deletions
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 83173a6af..a0a332f30 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -1,35 +1,39 @@
Code Stuff
----------
- * Because some devs use MSVC2008, we use C++03 - no C++11 magic for now at least :(
+ * We use C++03
* Use the provided wrappers for OS stuff:
- - Threading is done by inheriting from cIsThread, thread synchronization through cCriticalSection, cSemaphore and cEvent, file access and filesystem operations through the cFile class, high-precision timers through cTimer, high-precision sleep through cSleep
+ - Threading is done by inheriting from `cIsThread`, thread synchronization through `cCriticalSection`, `cSemaphore` and `cEvent`, file access and filesystem operations through the `cFile` class, high-precision timers through `cTimer`, high-precision sleep through `cSleep`
* No magic numbers, use named constants:
- - E_ITEM_XXX, E_BLOCK_XXX and E_META_XXX for items and blocks
- - E_ENTITY_TYPE_XXX for mob types
- - dimNether, dimOverworld and dimEnd for world dimension
- - gmSurvival, gmCreative, gmAdventure for game modes
- - wSunny, wRain, wThunderstorm for weather
- - cChunkDef::Width, cChunkDef::Height for chunk dimensions (C++)
+ - `E_ITEM_XXX`, `E_BLOCK_XXX` and `E_META_XXX` for items and blocks
+ - `cEntity::etXXX` for entity types, `cMonster::mtXXX` for mob types
+ - `dimNether`, `dimOverworld` and `dimEnd` for world dimension
+ - `gmSurvival`, `gmCreative`, `gmAdventure` for game modes
+ - `wSunny`, `wRain`, `wThunderstorm` for weather
+ - `cChunkDef::Width`, `cChunkDef::Height` for chunk dimensions (C++)
- etc.
- * Instead of checking for specific value, use Is functions, if available:
- - cPlayer:IsGameModeCreative() instead of (cPlayer:GetGameMode() == gmCreative)
- * Please use tabs for indentation and spaces for alignment. This means that if it's at line start, it's a tab; if it's in the middle of a line, it's a space
+ * Instead of checking for a specific value, use an `IsXXX` function, if available:
+ - `cPlayer:IsGameModeCreative()` instead of` (cPlayer:GetGameMode() == gmCreative)` (the player can also inherit the gamemode from the world, which the value-d condition doesn't catch)
+ * Please use **tabs for indentation and spaces for alignment**. This means that if it's at line start, it's a tab; if it's in the middle of a line, it's a space
* Alpha-sort stuff that makes sense alpha-sorting - long lists of similar items etc.
* Keep individual functions spaced out by 5 empty lines, this enhances readability and makes navigation in the source file easier.
* Add those extra parentheses to conditions, especially in C++
- - "if ((a == 1) && ((b == 2) || (c == 3)))" instead of ambiguous "if (a == 1 && b == 2 || c == 3)"
- - This helps prevent mistakes such as "if (a & 1 == 0)"
+ - `if ((a == 1) && ((b == 2) || (c == 3)))` instead of ambiguous `if (a == 1 && b == 2 || c == 3)`
+ - This helps prevent mistakes such as `if (a & 1 == 0)`
* White space is free, so use it freely
- "freely" as in "plentifully", not "arbitrarily"
* Each and every control statement deserves its braces. This helps maintainability later on when the file is edited, lines added or removed - the control logic doesn't break so easily.
- * Please leave the first line of all source files blank, to get around an IDE bug.
- * Also leave the last line of all source files blank (GCC and GIT can complain otherwise)
+ - The only exception: a `switch` statement with all `case` statements being a single short statement is allowed to use the short brace-less form.
+ * Add an empty last line in all source files (GCC and GIT can complain otherwise)
+ * Use doxy-comments for functions in the header file, format as `/** Description */`
+ * Use spaces after the comment markers: `// Comment` instead of `//Comment`
Copyright
---------
-Your work should be licensed under the Apache license, and you should add yourself to the CONTRIBUTORS file.
+Your work must be licensed at least under the Apache license.
-If your work is not licensed under the Apache license, then it must be compatible and marked as such. Note that only plugins may choose a different license; MC-server's internals need to be single-license.
+You can add yourself to the CONTRIBUTORS file if you wish.
+
+**PLUGINS ONLY**: If your plugin is not licensed under the Apache license, then it must be compatible and marked as such. This is only valid for the plugins included within the MCServer source; plugins developed on separate repositories can use whatever license they want.
diff --git a/MCServer/Plugins/@EnableMobDebug.lua b/MCServer/Plugins/@EnableMobDebug.lua
new file mode 100644
index 000000000..48d4c36b7
--- /dev/null
+++ b/MCServer/Plugins/@EnableMobDebug.lua
@@ -0,0 +1,29 @@
+
+-- @EnableMobDebug.lua
+
+-- Enables the MobDebug debugger, used by ZeroBrane Studio, for a plugin
+-- Needs to be named with a @ at the start so that it's loaded as the first file of the plugin
+
+--[[
+Usage:
+Copy this file to your plugin's folder when you want to debug that plugin
+You should neither check this file into the plugin's version control system,
+nor distribute it in the final release.
+--]]
+
+
+
+
+
+-- Try to load the debugger, be silent about failures:
+local IsSuccess, MobDebug = pcall(require, "mobdebug")
+if (IsSuccess) then
+ MobDebug.start()
+
+ -- The debugger will automatically put a breakpoint on this line, use this opportunity to set more breakpoints in your code
+ LOG(cPluginManager:GetCurrentPlugin():GetName() .. ": MobDebug enabled")
+end
+
+
+
+
diff --git a/MCServer/Plugins/APIDump/APIDesc.lua b/MCServer/Plugins/APIDump/APIDesc.lua
index 73bb5c7fb..c6221f30d 100644
--- a/MCServer/Plugins/APIDump/APIDesc.lua
+++ b/MCServer/Plugins/APIDump/APIDesc.lua
@@ -104,13 +104,18 @@ g_APIDesc =
DumpToRawFile = { Params = "FileName", Return = "", Notes = "Dumps the raw data into a file. For debugging purposes only." },
Expand = { Params = "SubMinX, AddMaxX, SubMinY, AddMaxY, SubMinZ, AddMaxZ", Return = "", Notes = "Expands the specified number of blocks from each border. Modifies the size of this blockarea object. New blocks created with this operation are filled with zeroes." },
Fill = { Params = "DataTypes, BlockType, [BlockMeta], [BlockLight], [BlockSkyLight]", Return = "", Notes = "Fills the entire block area with the same values, specified. Uses the DataTypes param to determine which content types are modified." },
- FillRelCuboid = { Params = "MinRelX, MaxRelX, MinRelY, MaxRelY, MinRelZ, MaxRelZ, DataTypes, BlockType, [BlockMeta], [BlockLight], [BlockSkyLight]", Return = "", Notes = "Fills the specified cuboid with the same values (like Fill() )." },
+ FillRelCuboid =
+ {
+ { Params = "{{cCuboid|RelCuboid}}, DataTypes, BlockType, [BlockMeta], [BlockLight], [BlockSkyLight]", Return = "", Notes = "Fills the specified cuboid (in relative coords) with the same values (like Fill() )." },
+ { Params = "MinRelX, MaxRelX, MinRelY, MaxRelY, MinRelZ, MaxRelZ, DataTypes, BlockType, [BlockMeta], [BlockLight], [BlockSkyLight]", Return = "", Notes = "Fills the specified cuboid with the same values (like Fill() )." },
+ },
GetBlockLight = { Params = "BlockX, BlockY, BlockZ", Return = "NIBBLETYPE", Notes = "Returns the blocklight at the specified absolute coords" },
GetBlockMeta = { Params = "BlockX, BlockY, BlockZ", Return = "NIBBLETYPE", Notes = "Returns the block meta at the specified absolute coords" },
GetBlockSkyLight = { Params = "BlockX, BlockY, BlockZ", Return = "NIBBLETYPE", Notes = "Returns the skylight at the specified absolute coords" },
GetBlockType = { Params = "BlockX, BlockY, BlockZ", Return = "BLOCKTYPE", Notes = "Returns the block type at the specified absolute coords" },
GetBlockTypeMeta = { Params = "BlockX, BlockY, BlockZ", Return = "BLOCKTYPE, NIBBLETYPE", Notes = "Returns the block type and meta at the specified absolute coords" },
GetDataTypes = { Params = "", Return = "number", Notes = "Returns the mask of datatypes that the objectis currently holding" },
+ GetOrigin = { Params = "", Return = "OriginX, OriginY, OriginZ", Notes = "Returns the origin coords of where the area was read from." },
GetOriginX = { Params = "", Return = "number", Notes = "Returns the origin x-coord" },
GetOriginY = { Params = "", Return = "number", Notes = "Returns the origin y-coord" },
GetOriginZ = { Params = "", Return = "number", Notes = "Returns the origin z-coord" },
@@ -119,23 +124,38 @@ g_APIDesc =
GetRelBlockSkyLight = { Params = "RelBlockX, RelBlockY, RelBlockZ", Return = "NIBBLETYPE", Notes = "Returns the skylight at the specified relative coords" },
GetRelBlockType = { Params = "RelBlockX, RelBlockY, RelBlockZ", Return = "BLOCKTYPE", Notes = "Returns the block type at the specified relative coords" },
GetRelBlockTypeMeta = { Params = "RelBlockX, RelBlockY, RelBlockZ", Return = "BLOCKTYPE, NIBBLETYPE", Notes = "Returns the block type and meta at the specified relative coords" },
+ GetSize = { Params = "", Return = "SizeX, SizeY, SizeZ", Notes = "Returns the size of the area in all 3 axes." },
GetSizeX = { Params = "", Return = "number", Notes = "Returns the size of the held data in the x-axis" },
GetSizeY = { Params = "", Return = "number", Notes = "Returns the size of the held data in the y-axis" },
GetSizeZ = { Params = "", Return = "number", Notes = "Returns the size of the held data in the z-axis" },
+ GetVolume = { Params = "", Return = "number", Notes = "Returns the volume of the area - the total number of blocks stored within." },
HasBlockLights = { Params = "", Return = "bool", Notes = "Returns true if current datatypes include blocklight" },
HasBlockMetas = { Params = "", Return = "bool", Notes = "Returns true if current datatypes include block metas" },
HasBlockSkyLights = { Params = "", Return = "bool", Notes = "Returns true if current datatypes include skylight" },
HasBlockTypes = { Params = "", Return = "bool", Notes = "Returns true if current datatypes include block types" },
LoadFromSchematicFile = { Params = "FileName", Return = "", Notes = "Clears current content and loads new content from the specified schematic file. Returns true if successful. Returns false and logs error if unsuccessful, old content is preserved in such a case." },
- Merge = { Params = "BlockAreaSrc, RelX, RelY, RelZ, Strategy", Return = "", Notes = "Merges BlockAreaSrc into this object at the specified relative coords, using the specified strategy" },
+ Merge =
+ {
+ { Params = "BlockAreaSrc, {{Vector3i|RelMinCoords}}, Strategy", Return = "", Notes = "Merges BlockAreaSrc into this object at the specified relative coords, using the specified strategy" },
+ { Params = "BlockAreaSrc, RelX, RelY, RelZ, Strategy", Return = "", Notes = "Merges BlockAreaSrc into this object at the specified relative coords, using the specified strategy" },
+ },
MirrorXY = { Params = "", Return = "", Notes = "Mirrors this block area around the XY plane. Modifies blocks' metas (if present) to match (i. e. furnaces facing the opposite direction)." },
MirrorXYNoMeta = { Params = "", Return = "", Notes = "Mirrors this block area around the XY plane. Doesn't modify blocks' metas." },
MirrorXZ = { Params = "", Return = "", Notes = "Mirrors this block area around the XZ plane. Modifies blocks' metas (if present)" },
MirrorXZNoMeta = { Params = "", Return = "", Notes = "Mirrors this block area around the XZ plane. Doesn't modify blocks' metas." },
MirrorYZ = { Params = "", Return = "", Notes = "Mirrors this block area around the YZ plane. Modifies blocks' metas (if present)" },
MirrorYZNoMeta = { Params = "", Return = "", Notes = "Mirrors this block area around the YZ plane. Doesn't modify blocks' metas." },
- Read = { Params = "World, MinX, MaxX, MinY, MaxY, MinZ, MaxZ, DataTypes", Return = "bool", Notes = "Reads the area from World, returns true if successful" },
- RelLine = { Params = "RelX1, RelY1, RelZ1, RelX2, RelY2, RelZ2, DataTypes, BlockType, [BlockMeta], [BlockLight], [BlockSkyLight]", Return = "", Notes = "Draws a line between the two specified points. Sets only datatypes specified by DataTypes." },
+ Read =
+ {
+ { Params = "World, {{cCuboid|Cuboid}}, DataTypes", Return = "bool", Notes = "Reads the area from World, returns true if successful" },
+ { Params = "World, {{Vector3i|Point1}}, {{Vector3i|Point2}}, DataTypes", Return = "bool", Notes = "Reads the area from World, returns true if successful" },
+ { Params = "World, X1, X2, Y1, Y2, Z1, Z2, DataTypes", Return = "bool", Notes = "Reads the area from World, returns true if successful" },
+ },
+ RelLine =
+ {
+ { Params = "{{Vector3i|RelPoint1}}, {{Vector3i|RelPoint2}}, DataTypes, BlockType, [BlockMeta], [BlockLight], [BlockSkyLight]", Return = "", Notes = "Draws a line between the two specified points. Sets only datatypes specified by DataTypes (baXXX constants)." },
+ { Params = "RelX1, RelY1, RelZ1, RelX2, RelY2, RelZ2, DataTypes, BlockType, [BlockMeta], [BlockLight], [BlockSkyLight]", Return = "", Notes = "Draws a line between the two specified points. Sets only datatypes specified by DataTypes (baXXX constants)." },
+ },
RotateCCW = { Params = "", Return = "", Notes = "Rotates the block area around the Y axis, counter-clockwise (east -> north). Modifies blocks' metas (if present) to match." },
RotateCCWNoMeta = { Params = "", Return = "", Notes = "Rotates the block area around the Y axis, counter-clockwise (east -> north). Doesn't modify blocks' metas." },
RotateCW = { Params = "", Return = "", Notes = "Rotates the block area around the Y axis, clockwise (north -> east). Modifies blocks' metas (if present) to match." },
@@ -146,13 +166,21 @@ g_APIDesc =
SetBlockSkyLight = { Params = "BlockX, BlockY, BlockZ, SkyLight", Return = "", Notes = "Sets the skylight at the specified absolute coords" },
SetBlockType = { Params = "BlockX, BlockY, BlockZ, BlockType", Return = "", Notes = "Sets the block type at the specified absolute coords" },
SetBlockTypeMeta = { Params = "BlockX, BlockY, BlockZ, BlockType, BlockMeta", Return = "", Notes = "Sets the block type and meta at the specified absolute coords" },
- SetOrigin = { Params = "OriginX, OriginY, OriginZ", Return = "", Notes = "Resets the origin for the absolute coords. Only affects how absolute coords are translated into relative coords." },
+ SetOrigin =
+ {
+ { Params = "{{Vector3i|Origin}}", Return = "", Notes = "Resets the origin for the absolute coords. Only affects how absolute coords are translated into relative coords." },
+ { Params = "OriginX, OriginY, OriginZ", Return = "", Notes = "Resets the origin for the absolute coords. Only affects how absolute coords are translated into relative coords." },
+ },
SetRelBlockLight = { Params = "RelBlockX, RelBlockY, RelBlockZ, BlockLight", Return = "", Notes = "Sets the blocklight at the specified relative coords" },
SetRelBlockMeta = { Params = "RelBlockX, RelBlockY, RelBlockZ, BlockMeta", Return = "", Notes = "Sets the block meta at the specified relative coords" },
SetRelBlockSkyLight = { Params = "RelBlockX, RelBlockY, RelBlockZ, SkyLight", Return = "", Notes = "Sets the skylight at the specified relative coords" },
SetRelBlockType = { Params = "RelBlockX, RelBlockY, RelBlockZ, BlockType", Return = "", Notes = "Sets the block type at the specified relative coords" },
SetRelBlockTypeMeta = { Params = "RelBlockX, RelBlockY, RelBlockZ, BlockType, BlockMeta", Return = "", Notes = "Sets the block type and meta at the specified relative coords" },
- Write = { Params = "World, MinX, MinY, MinZ, DataTypes", Return = "bool", Notes = "Writes the area into World at the specified coords, returns true if successful" },
+ Write =
+ {
+ { Params = "World, {{Vector3i|MinPoint}}, DataTypes", Return = "bool", Notes = "Writes the area into World at the specified coords, returns true if successful" },
+ { Params = "World, MinX, MinY, MinZ, DataTypes", Return = "bool", Notes = "Writes the area into World at the specified coords, returns true if successful" },
+ },
},
Constants =
{
@@ -262,45 +290,6 @@ g_APIDesc =
}, -- AdditionalInfo
}, -- cBlockArea
- cBoundingBox =
- {
- Desc = [[
- Represents two sets of coordinates, minimum and maximum for each direction; thus defining an
- axis-aligned cuboid with floating-point boundaries. It supports operations changing the size and
- position of the box, as well as querying whether a point or another BoundingBox is inside the box.</p>
- <p>
- All the points within the coordinate limits (inclusive the edges) are considered "inside" the box.
- However, for intersection purposes, if the intersection is "sharp" in any coord (min1 == max2, i. e.
- zero volume), the boxes are considered non-intersecting.</p>
- ]],
- Functions =
- {
- constructor =
- {
- { Params = "MinX, MaxX, MinY, MaxY, MinZ, MaxZ", Return = "cBoundingBox", Notes = "Creates a new bounding box with the specified edges" },
- { Params = "{{Vector3d|Min}}, {{Vector3d|Max}}", Return = "cBoundingBox", Notes = "Creates a new bounding box with the coords specified as two vectors" },
- { Params = "{{Vector3d|Pos}}, Radius, Height", Return = "cBoundingBox", Notes = "Creates a new bounding box from the position given and radius (X/Z) and height. Radius is added from X/Z to calculate the maximum coords and subtracted from X/Z to get the minimum; minimum Y is set to Pos.y and maxumim Y to Pos.y plus Height. This corresponds with how {{cEntity|entities}} are represented in Minecraft." },
- { Params = "OtherBoundingBox", Return = "cBoundingBox", Notes = "Creates a new copy of the given bounding box. Same result can be achieved by using a simple assignment." },
- },
- CalcLineIntersection = { Params = "{{Vector3d|LineStart}}, {{Vector3d|LinePt2}}", Return = "DoesIntersect, LineCoeff, Face", Notes = "Calculates the intersection of a ray (half-line), given by two of its points, with the bounding box. Returns false if the line doesn't intersect the bounding box, or true, together with coefficient of the intersection (how much of the difference between the two ray points is needed to reach the intersection), and the face of the box which is intersected.<br /><b>TODO</b>: Lua binding for this function is wrong atm." },
- DoesIntersect = { Params = "OtherBoundingBox", Return = "bool", Notes = "Returns true if the two bounding boxes have an intersection of nonzero volume." },
- Expand = { Params = "ExpandX, ExpandY, ExpandZ", Return = "", Notes = "Expands this bounding box by the specified amount in each direction (so the box becomes larger by 2 * Expand in each axis)." },
- IsInside =
- {
- { Params = "{{Vector3d|Point}}", Return = "bool", Notes = "Returns true if the specified point is inside (including on the edge) of the box." },
- { Params = "PointX, PointY, PointZ", Return = "bool", Notes = "Returns true if the specified point is inside (including on the edge) of the box." },
- { Params = "OtherBoundingBox", Return = "bool", Notes = "Returns true if OtherBoundingBox is inside of this box." },
- { Params = "{{Vector3d|OtherBoxMin}}, {{Vector3d|OtherBoxMax}}", Return = "bool", Notes = "Returns true if the other bounding box, specified by its 2 corners, is inside of this box." },
- },
- Move =
- {
- { Params = "OffsetX, OffsetY, OffsetZ", Return = "", Notes = "Moves the bounding box by the specified offset in each axis" },
- { Params = "{{Vector3d|Offset}}", Return = "", Notes = "Moves the bounding box by the specified offset in each axis" },
- },
- Union = { Params = "OtherBoundingBox", Return = "cBoundingBox", Notes = "Returns the smallest bounding box that contains both OtherBoundingBox and this bounding box. Note that unlike the strict geometrical meaning of \"union\", this operation actually returns a cBoundingBox." },
- },
- },
-
cChatColor =
{
Desc = [[
@@ -448,6 +437,7 @@ end
GetUniqueID = { Params = "", Return = "number", Notes = "Returns the UniqueID of the client used to identify the client in the server" },
GetUsername = { Params = "", Return = "string", Notes = "Returns the username that the client has provided" },
GetViewDistance = { Params = "", Return = "number", Notes = "Returns the viewdistance (number of chunks loaded for the player in each direction)" },
+ HasPluginChannel = { Params = "ChannelName", Return = "bool", Notes = "Returns true if the client has registered to receive messages on the specified plugin channel." },
Kick = { Params = "Reason", Return = "", Notes = "Kicks the user with the specified reason" },
SendPluginMessage = { Params = "Channel, Message", Return = "", Notes = "Sends the plugin message on the specified channel." },
SetUsername = { Params = "Name", Return = "", Notes = "Sets the username" },
@@ -520,48 +510,6 @@ end
},
}, -- cCraftingRecipe
- cCuboid =
- {
- Desc = [[
- cCuboid offers some native support for integral-boundary cuboids. A cuboid internally consists of
- two {{Vector3i}}s. By default the cuboid doesn't make any assumptions about the defining points,
- but for most of the operations in the cCuboid class, the p1 member variable is expected to be the
- minima and the p2 variable the maxima. The Sort() function guarantees this condition.</p>
- <p>
- The Cuboid considers both its edges inclusive.</p>
- ]],
- Functions =
- {
- constructor =
- {
- { Params = "OtheCuboid", Return = "cCuboid", Notes = "Creates a new Cuboid object as a copy of OtherCuboid" },
- { Params = "{{Vector3i|Point1}}, {{Vector3i|Point2}}", Return = "cCuboid", Notes = "Creates a new Cuboid object with the specified points as its corners." },
- { Params = "X, Y, Z", Return = "cCuboid", Notes = "Creates a new Cuboid object with the specified point as both its corners (the cuboid has a size of 1 in each direction)." },
- { Params = "X1, Y1, Z1, X2, Y2, Z2", Return = "cCuboid", Notes = "Creates a new Cuboid object with the specified points as its corners." },
- },
- Assign = { Params = "X1, Y1, Z1, X2, Y2, Z2", Return = "", Notes = "Assigns all the coords stored in the cuboid. Sort-state is ignored." },
- DifX = { Params = "", Return = "number", Notes = "Returns the difference between the two X coords (X-size minus 1). Assumes sorted." },
- DifY = { Params = "", Return = "number", Notes = "Returns the difference between the two Y coords (Y-size minus 1). Assumes sorted." },
- DifZ = { Params = "", Return = "number", Notes = "Returns the difference between the two Z coords (Z-size minus 1). Assumes sorted." },
- DoesIntersect = { Params = "OtherCuboid", Return = "bool", Notes = "Returns true if this cuboid has at least one voxel in common with OtherCuboid. Note that edges are considered inclusive. Assumes both sorted." },
- IsCompletelyInside = { Params = "OuterCuboid", Return = "bool", Notes = "Returns true if this cuboid is completely inside (in all directions) in OuterCuboid. Assumes both sorted." },
- IsInside =
- {
- { Params = "X, Y, Z", Return = "bool", Notes = "Returns true if the specified point (integral coords) is inside this cuboid. Assumes sorted." },
- { Params = "{{Vector3i|Point}}", Return = "bool", Notes = "Returns true if the specified point (integral coords) is inside this cuboid. Assumes sorted." },
- { Params = "{{Vector3d|Point}}", Return = "bool", Notes = "Returns true if the specified point (floating-point coords) is inside this cuboid. Assumes sorted." },
- },
- IsSorted = { Params = "", Return = "bool", Notes = "Returns true if this cuboid is sorted" },
- Move = { Params = "OffsetX, OffsetY, OffsetZ", Return = "", Notes = "Adds the specified offsets to each respective coord, effectively moving the Cuboid. Sort-state is ignored." },
- Sort = { Params = "", Return = "" , Notes = "Sorts the internal representation so that p1 contains the lesser coords and p2 contains the greater coords." },
- },
- Variables =
- {
- p1 = { Type = "{{Vector3i}}", Notes = "The first corner. Usually the lesser of the two coords in each set" },
- p2 = { Type = "{{Vector3i}}", Notes = "The second corner. Usually the larger of the two coords in each set" },
- },
- }, -- cCuboid
-
cEnchantments =
{
Desc = [[
@@ -1312,95 +1260,6 @@ end
},
}, -- cItems
- cLineBlockTracer =
- {
- Desc = [[Objects of this class provide an easy-to-use interface to tracing lines through individual
-blocks in the world. It will call the provided callbacks according to what events it encounters along the
-way.</p>
-<p>
-For the Lua API, there's only one function exported that takes all the parameters necessary to do the
-tracing. The Callbacks parameter is a table containing all the functions that will be called upon the
-various events. See below for further information.
- ]],
- Functions =
- {
- Trace = { Params = "{{cWorld}}, Callbacks, StartX, StartY, StartZ, EndX, EndY, EndZ", Return = "bool", Notes = "(STATIC) Performs the trace on the specified line. Returns true if the entire trace was processed (no callback returned true)" },
- },
-
- AdditionalInfo =
- {
- {
- Header = "Callbacks",
- Contents = [[
-The Callbacks in the Trace() function is a table that contains named functions. MCServer will call
-individual functions from that table for the events that occur on the line - hitting a block, going out of
-valid world data etc. The following table lists all the available callbacks. If the callback function is
-not defined, MCServer skips it. Each function can return a bool value, if it returns true, the tracing is
-aborted and Trace() returns false.</p>
-<p>
-<table><tr><th>Name</th><th>Parameters</th><th>Notes</th></tr>
-<tr><td>OnNextBlock</td><td>BlockX, BlockY, BlockZ, BlockType, BlockMeta, EntryFace</td>
- <td>Called when the ray hits a new valid block. The block type and meta is given. EntryFace is one of the
- BLOCK_FACE_ constants indicating which "side" of the block got hit by the ray.</td></tr>
-<tr><td>OnNextBlockNoData</td><td>BlockX, BlockY, BlockZ, EntryFace</td>
- <td>Called when the ray hits a new block, but the block is in an unloaded chunk - no valid data is
- available. Only the coords and the entry face are given.</td></tr>
-<tr><td>OnOutOfWorld</td><td>X, Y, Z</td>
- <td>Called when the ray goes outside of the world (Y-wise); the coords specify the exact exit point. Note
- that for other paths than lines (considered for future implementations) the path may leave the world and
- go back in again later, in such a case this callback is followed by OnIntoWorld() and further
- OnNextBlock() calls.</td></tr>
-<tr><td>OnIntoWorld</td><td>X, Y, Z</td>
- <td>Called when the ray enters the world (Y-wise); the coords specify the exact entry point.</td></tr>
-<tr><td>OnNoMoreHits</td><td>&nbsp;</td>
- <td>Called when the path is sure not to hit any more blocks. This is the final callback, no more
- callbacks are called after this function. Unlike the other callbacks, this function doesn't have a return
- value.</td></tr>
-<tr><td>OnNoChunk</td><td>&nbsp;</td>
- <td>Called when the ray enters a chunk that is not loaded. This usually means that the tracing is aborted.
- Unlike the other callbacks, this function doesn't have a return value.</td></tr>
-</table>
- ]],
- },
- {
- Header = "Example",
- Contents = [[
-<p>The following example is taken from the Debuggers plugin. It is a command handler function for the
-"/spidey" command that creates a line of cobweb blocks from the player's eyes up to 50 blocks away in
-the direction they're looking, but only through the air.
-<pre class="prettyprint lang-lua">
-function HandleSpideyCmd(a_Split, a_Player)
- local World = a_Player:GetWorld();
-
- local Callbacks = {
- OnNextBlock = function(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta)
- if (a_BlockType ~= E_BLOCK_AIR) then
- -- abort the trace
- return true;
- end
- World:SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_COBWEB, 0);
- end
- };
-
- local EyePos = a_Player:GetEyePosition();
- local LookVector = a_Player:GetLookVector();
- LookVector:Normalize(); -- Make the vector 1 m long
-
- -- Start cca 2 blocks away from the eyes
- local Start = EyePos + LookVector + LookVector;
- local End = EyePos + LookVector * 50;
-
- cLineBlockTracer.Trace(World, Callbacks, Start.x, Start.y, Start.z, End.x, End.y, End.z);
-
- return true;
-end
-</pre>
-</p>
- ]],
- },
- }, -- AdditionalInfo
- }, -- cLineBlockTracer
-
cLuaWindow =
{
Desc = [[This class is used by plugins wishing to display a custom window to the player, unrelated to block entities or entities near the player. The window can be of any type and have any contents that the plugin defines. Callbacks for when the player modifies the window contents and when the player closes the window can be set.
@@ -1485,6 +1344,68 @@ a_Player:OpenWindow(Window);
Inherits = "cWindow",
}, -- cLuaWindow
+ cMap =
+ {
+ Desc = [[
+ This class encapsulates a single in-game colored map.</p>
+ <p>
+ The contents (i.e. pixel data) of a cMap are dynamically updated by each
+ tracked {{cPlayer}} instance. Furthermore, a cMap maintains and periodically
+ updates a list of map decorators, which are objects drawn on the map that
+ can freely move (e.g. Player and item frame pointers).
+ ]],
+ Functions =
+ {
+ EraseData = { Params = "", Return = "", Notes = "Erases all pixel data." },
+ GetCenterX = { Params = "", Return = "number", Notes = "Returns the X coord of the map's center." },
+ GetCenterZ = { Params = "", Return = "number", Notes = "Returns the Y coord of the map's center." },
+ GetDimension = { Params = "", Return = "eDimension", Notes = "Returns the dimension of the associated world." },
+ GetHeight = { Params = "", Return = "number", Notes = "Returns the height of the map." },
+ GetID = { Params = "", Return = "number", Notes = "Returns the numerical ID of the map. (The item damage value)" },
+ GetName = { Params = "", Return = "string", Notes = "Returns the name of the map." },
+ GetNumPixels = { Params = "", Return = "number", Notes = "Returns the number of pixels in this map." },
+ GetPixel = { Params = "PixelX, PixelZ", Return = "ColorID", Notes = "Returns the color of the specified pixel." },
+ GetPixelWidth = { Params = "", Return = "number", Notes = "Returns the width of a single pixel in blocks." },
+ GetScale = { Params = "", Return = "number", Notes = "Returns the scale of the map. Range: [0,4]" },
+ GetWidth = { Params = "", Return = "number", Notes = "Returns the width of the map." },
+ GetWorld = { Params = "", Return = "cWorld", Notes = "Returns the associated world." },
+ Resize = { Params = "Width, Height", Return = "", Notes = "Resizes the map. WARNING: This will erase the pixel data." },
+ SetPixel = { Params = "PixelX, PixelZ, ColorID", Return = "bool", Notes = "Sets the color of the specified pixel. Returns false on error (Out of range)." },
+ SetPosition = { Params = "CenterX, CenterZ", Return = "", Notes = "Relocates the map. The pixel data will not be modified." },
+ SetScale = { Params = "number", Return = "", Notes = "Rescales the map. The pixel data will not be modified." },
+ },
+ Constants =
+ {
+ E_BASE_COLOR_BLUE = { Notes = "" },
+ E_BASE_COLOR_BROWN = { Notes = "" },
+ E_BASE_COLOR_DARK_BROWN = { Notes = "" },
+ E_BASE_COLOR_DARK_GRAY = { Notes = "" },
+ E_BASE_COLOR_DARK_GREEN = { Notes = "" },
+ E_BASE_COLOR_GRAY_1 = { Notes = "" },
+ E_BASE_COLOR_GRAY_2 = { Notes = "" },
+ E_BASE_COLOR_LIGHT_BROWN = { Notes = "" },
+ E_BASE_COLOR_LIGHT_GRAY = { Notes = "" },
+ E_BASE_COLOR_LIGHT_GREEN = { Notes = "" },
+ E_BASE_COLOR_PALE_BLUE = { Notes = "" },
+ E_BASE_COLOR_RED = { Notes = "" },
+ E_BASE_COLOR_TRANSPARENT = { Notes = "" },
+ E_BASE_COLOR_WHITE = { Notes = "" },
+ },
+ }, -- cMap
+
+ cMapManager =
+ {
+ Desc = [[
+ This class is associated with a single {{cWorld}} instance and manages a list of maps.
+ ]],
+ Functions =
+ {
+ DoWithMap = { Params = "ID, CallbackFunction, [CallbackData]", Return = "bool", Notes = "If a map with the specified ID exists, calls the CallbackFunction for that map. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cMap|Map}}, [CallbackData])</pre> Returns true if the map was found and the callback called, false if map not found." },
+ GetNumMaps = { Params = "", Return = "number", Notes = "Returns the number of registered maps." },
+ },
+
+ }, -- cMapManager
+
cMonster =
{
Desc = [[
@@ -1931,21 +1852,6 @@ end
Inherits = "cEntity",
},
- cTracer =
- {
- Desc = [[
- A cTracer object is used to trace lines in the world. One thing you can use the cTracer for, is
- tracing what block a player is looking at, but you can do more with it if you want.</p>
- <p>
- The cTracer is still a work in progress.</p>
- <p>
- See also the {{cLineBlockTracer}} class for an alternative approach using callbacks.
- ]],
- Functions =
- {
- },
- }, -- cTracer
-
cWebAdmin =
{
Desc = "",
@@ -2103,7 +2009,8 @@ end
GetGeneratorQueueLength = { Params = "", Return = "number", Notes = "Returns the number of chunks that are queued in the chunk generator." },
GetHeight = { Params = "BlockX, BlockZ", Return = "number", Notes = "Returns the maximum height of the particula block column in the world. If the chunk is not loaded, it waits for it to load / generate. <b>WARNING</b>: Do not use, Use TryGetHeight() instead for a non-waiting version, otherwise you run the risk of a deadlock!" },
GetIniFileName = { Params = "", Return = "string", Notes = "Returns the name of the world.ini file that the world uses to store the information." },
- GetLightingQueueLength = { Params = "", Return = "number", Notes = "Returns the number of chunks in the lighting thread's queue." },
+ GetLightingQueueLength = { Params = "", Return = "number", Notes = "Returns the number of chunks in the lighting thread's queue." },
+ GetMapManager = { Params = "", Return = "{{cMapManager}}", Notes = "Returns the {{cMapManager|MapManager}} object used by this world." },
GetMaxCactusHeight = { Params = "", Return = "number", Notes = "Returns the configured maximum height to which cacti will grow naturally." },
GetMaxSugarcaneHeight = { Params = "", Return = "number", Notes = "Returns the configured maximum height to which sugarcane will grow naturally." },
GetName = { Params = "", Return = "string", Notes = "Returns the name of the world, as specified in the settings.ini file." },
@@ -2527,122 +2434,6 @@ end
} -- AdditionalInfo
}, -- tolua
- Vector3d =
- {
- Desc = [[
- A Vector3d object uses double precision floating point values to describe a point in 3D space.</p>
- <p>
- See also {{Vector3f}} for single-precision floating point 3D coords and {{Vector3i}} for integer
- 3D coords.
- ]],
- Functions =
- {
- constructor =
- {
- { Params = "{{Vector3f}}", Return = "Vector3d", Notes = "Creates a new Vector3d object by copying the coords from the given Vector3f." },
- { Params = "", Return = "Vector3d", Notes = "Creates a new Vector3d object with all its coords set to 0." },
- { Params = "X, Y, Z", Return = "Vector3d", Notes = "Creates a new Vector3d object with its coords set to the specified values." },
- },
- operator_div = { Params = "number", Return = "Vector3d", Notes = "Returns a new Vector3d with each coord divided by the specified number." },
- operator_mul = { Params = "number", Return = "Vector3d", Notes = "Returns a new Vector3d with each coord multiplied." },
- operator_sub = { Params = "Vector3d", Return = "Vector3d", Notes = "Returns a new Vector3d containing the difference between this object and the specified vector." },
- operator_plus = {Params = "Vector3d", Return = "Vector3d", Notes = "Returns a new Vector3d containing the sum of this vector and the specified vector" },
- Cross = { Params = "Vector3d", Return = "Vector3d", Notes = "Returns a new Vector3d that is a {{http://en.wikipedia.org/wiki/Cross_product|cross product}} of this vector and the specified vector." },
- Dot = { Params = "Vector3d", Return = "number", Notes = "Returns the dot product of this vector and the specified vector." },
- Equals = { Params = "Vector3d", Return = "bool", Notes = "Returns true if this vector is exactly equal to the specified vector." },
- Length = { Params = "", Return = "number", Notes = "Returns the (euclidean) length of the vector." },
- LineCoeffToXYPlane = { Params = "Vector3d, Z", Return = "number", Notes = "Returns the coefficient for the line from the specified vector through this vector to reach the specified Z coord. The result satisfies the following equation: (this + Result * (Param - this)).z = Z. Returns the NO_INTERSECTION constant if there's no intersection." },
- LineCoeffToXZPlane = { Params = "Vector3d, Y", Return = "number", Notes = "Returns the coefficient for the line from the specified vector through this vector to reach the specified Y coord. The result satisfies the following equation: (this + Result * (Param - this)).y = Y. Returns the NO_INTERSECTION constant if there's no intersection." },
- LineCoeffToYZPlane = { Params = "Vector3d, X", Return = "number", Notes = "Returns the coefficient for the line from the specified vector through this vector to reach the specified X coord. The result satisfies the following equation: (this + Result * (Param - this)).x = X. Returns the NO_INTERSECTION constant if there's no intersection." },
- Normalize = { Params = "", Return = "", Notes = "Changes this vector so that it keeps current direction but is exactly 1 unit long. FIXME: Fails for a zero vector." },
- NormalizeCopy = { Params = "", Return = "Vector3d", Notes = "Returns a new vector that has the same directino as this but is exactly 1 unit long. FIXME: Fails for a zero vector." },
- Set = { Params = "X, Y, Z", Return = "", Notes = "Sets all the coords in this object." },
- SqrLength = { Params = "", Return = "number", Notes = "Returns the (euclidean) length of this vector, squared. This operation is slightly less computationally expensive than Length(), while it conserves some properties of Length(), such as comparison. " },
- },
- Constants =
- {
- EPS = { Notes = "The max difference between two coords for which the coords are assumed equal (in LineCoeffToXYPlane() et al)." },
- NO_INTERSECTION = { Notes = "Special return value for the LineCoeffToXYPlane() et al meaning that there's no intersectino with the plane." },
- },
- Variables =
- {
- x = { Type = "number", Notes = "The X coord of the vector." },
- y = { Type = "number", Notes = "The Y coord of the vector." },
- z = { Type = "number", Notes = "The Z coord of the vector." },
- },
- }, -- Vector3d
-
- Vector3f =
- {
- Desc = [[
- A Vector3f object uses floating point values to describe a point in space.</p>
- <p>
- See also {{Vector3d}} for double-precision floating point 3D coords and {{Vector3i}} for integer
- 3D coords.
- ]],
- Functions =
- {
- constructor =
- {
- { Params = "", Return = "Vector3f", Notes = "Creates a new Vector3f object with zero coords" },
- { Params = "x, y, z", Return = "Vector3f", Notes = "Creates a new Vector3f object with the specified coords" },
- { Params = "Vector3f", Return = "Vector3f", Notes = "Creates a new Vector3f object as a copy of the specified vector" },
- { Params = "{{Vector3d}}", Return = "Vector3f", Notes = "Creates a new Vector3f object as a copy of the specified {{Vector3d}}" },
- { Params = "{{Vector3i}}", Return = "Vector3f", Notes = "Creates a new Vector3f object as a copy of the specified {{Vector3i}}" },
- },
- operator_mul =
- {
- { Params = "number", Return = "Vector3f", Notes = "Returns a new Vector3f object that has each of its coords multiplied by the specified number" },
- { Params = "Vector3f", Return = "Vector3f", Notes = "Returns a new Vector3f object that has each of its coords multiplied by the respective coord of the specified vector." },
- },
- operator_plus = { Params = "Vector3f", Return = "Vector3f", Notes = "Returns a new Vector3f object that holds the vector sum of this vector and the specified vector." },
- operator_sub = { Params = "Vector3f", Return = "Vector3f", Notes = "Returns a new Vector3f object that holds the vector differrence between this vector and the specified vector." },
- Cross = { Params = "Vector3f", Return = "Vector3f", Notes = "Returns a new Vector3f object that holds the cross product of this vector and the specified vector." },
- Dot = { Params = "Vector3f", Return = "number", Notes = "Returns the dot product of this vector and the specified vector." },
- Equals = { Params = "Vector3f", Return = "bool", Notes = "Returns true if the specified vector is exactly equal to this vector." },
- Length = { Params = "", Return = "number", Notes = "Returns the (euclidean) length of this vector" },
- Normalize = { Params = "", Return = "", Notes = "Normalizes this vector (makes it 1 unit long while keeping the direction). FIXME: Fails for zero vectors." },
- NormalizeCopy = { Params = "", Return = "Vector3f", Notes = "Returns a copy of this vector that is normalized (1 unit long while keeping the same direction). FIXME: Fails for zero vectors." },
- Set = { Params = "x, y, z", Return = "", Notes = "Sets all the coords of the vector at once." },
- SqrLength = { Params = "", Return = "number", Notes = "Returns the (euclidean) length of this vector, squared. This operation is slightly less computationally expensive than Length(), while it conserves some properties of Length(), such as comparison." },
- },
- Variables =
- {
- x = { Type = "number", Notes = "The X coord of the vector." },
- y = { Type = "number", Notes = "The Y coord of the vector." },
- z = { Type = "number", Notes = "The Z coord of the vector." },
- },
- }, -- Vector3f
-
- Vector3i =
- {
- Desc = [[
- A Vector3i object uses integer values to describe a point in space.</p>
- <p>
- See also {{Vector3d}} for double-precision floating point 3D coords and {{Vector3f}} for
- single-precision floating point 3D coords.
- ]],
- Functions =
- {
- constructor =
- {
- { Params = "", Return = "Vector3i", Notes = "Creates a new Vector3i object with zero coords." },
- { Params = "x, y, z", Return = "Vector3i", Notes = "Creates a new Vector3i object with the specified coords." },
- { Params = "{{Vector3d}}", Return = "Vector3i", Notes = "Creates a new Vector3i object with coords copied and floor()-ed from the specified {{Vector3d}}." },
- },
- Equals = { Params = "Vector3i", Return = "bool", Notes = "Returns true if this vector is exactly the same as the specified vector." },
- Length = { Params = "", Return = "number", Notes = "Returns the (euclidean) length of this vector." },
- Set = { Params = "x, y, z", Return = "", Notes = "Sets all the coords of the vector at once" },
- SqrLength = { Params = "", Return = "number", Notes = "Returns the (euclidean) length of this vector, squared. This operation is slightly less computationally expensive than Length(), while it conserves some properties of Length(), such as comparison." },
- },
- Variables =
- {
- x = { Type = "number", Notes = "The X coord of the vector." },
- y = { Type = "number", Notes = "The Y coord of the vector." },
- z = { Type = "number", Notes = "The Z coord of the vector." },
- },
- }, -- Vector3i
-
Globals =
{
Desc = [[
diff --git a/MCServer/Plugins/APIDump/APIDump.deproj b/MCServer/Plugins/APIDump/APIDump.deproj
index e78974901..0e6fcd325 100644
--- a/MCServer/Plugins/APIDump/APIDump.deproj
+++ b/MCServer/Plugins/APIDump/APIDump.deproj
@@ -7,6 +7,9 @@
<filename>Classes\BlockEntities.lua</filename>
</file>
<file>
+ <filename>Classes\Geometry.lua</filename>
+ </file>
+ <file>
<filename>Hooks\OnBlockToPickups.lua</filename>
</file>
<file>
diff --git a/MCServer/Plugins/APIDump/Classes/Geometry.lua b/MCServer/Plugins/APIDump/Classes/Geometry.lua
new file mode 100644
index 000000000..e83d6e4b1
--- /dev/null
+++ b/MCServer/Plugins/APIDump/Classes/Geometry.lua
@@ -0,0 +1,325 @@
+
+-- Geometry.lua
+
+-- Defines the documentation for geometry-related classes:
+-- cBoundingBox, cCuboid, cLineBlockTracer, cTracer, Vector3X
+
+
+
+
+return
+{
+ cBoundingBox =
+ {
+ Desc = [[
+ Represents two sets of coordinates, minimum and maximum for each direction; thus defining an
+ axis-aligned cuboid with floating-point boundaries. It supports operations changing the size and
+ position of the box, as well as querying whether a point or another BoundingBox is inside the box.</p>
+ <p>
+ All the points within the coordinate limits (inclusive the edges) are considered "inside" the box.
+ However, for intersection purposes, if the intersection is "sharp" in any coord (min1 == max2, i. e.
+ zero volume), the boxes are considered non-intersecting.</p>
+ ]],
+ Functions =
+ {
+ constructor =
+ {
+ { Params = "MinX, MaxX, MinY, MaxY, MinZ, MaxZ", Return = "cBoundingBox", Notes = "Creates a new bounding box with the specified edges" },
+ { Params = "{{Vector3d|Min}}, {{Vector3d|Max}}", Return = "cBoundingBox", Notes = "Creates a new bounding box with the coords specified as two vectors" },
+ { Params = "{{Vector3d|Pos}}, Radius, Height", Return = "cBoundingBox", Notes = "Creates a new bounding box from the position given and radius (X/Z) and height. Radius is added from X/Z to calculate the maximum coords and subtracted from X/Z to get the minimum; minimum Y is set to Pos.y and maxumim Y to Pos.y plus Height. This corresponds with how {{cEntity|entities}} are represented in Minecraft." },
+ { Params = "OtherBoundingBox", Return = "cBoundingBox", Notes = "Creates a new copy of the given bounding box. Same result can be achieved by using a simple assignment." },
+ },
+ CalcLineIntersection = { Params = "{{Vector3d|LineStart}}, {{Vector3d|LinePt2}}", Return = "DoesIntersect, LineCoeff, Face", Notes = "Calculates the intersection of a ray (half-line), given by two of its points, with the bounding box. Returns false if the line doesn't intersect the bounding box, or true, together with coefficient of the intersection (how much of the difference between the two ray points is needed to reach the intersection), and the face of the box which is intersected.<br /><b>TODO</b>: Lua binding for this function is wrong atm." },
+ DoesIntersect = { Params = "OtherBoundingBox", Return = "bool", Notes = "Returns true if the two bounding boxes have an intersection of nonzero volume." },
+ Expand = { Params = "ExpandX, ExpandY, ExpandZ", Return = "", Notes = "Expands this bounding box by the specified amount in each direction (so the box becomes larger by 2 * Expand in each axis)." },
+ IsInside =
+ {
+ { Params = "{{Vector3d|Point}}", Return = "bool", Notes = "Returns true if the specified point is inside (including on the edge) of the box." },
+ { Params = "PointX, PointY, PointZ", Return = "bool", Notes = "Returns true if the specified point is inside (including on the edge) of the box." },
+ { Params = "OtherBoundingBox", Return = "bool", Notes = "Returns true if OtherBoundingBox is inside of this box." },
+ { Params = "{{Vector3d|OtherBoxMin}}, {{Vector3d|OtherBoxMax}}", Return = "bool", Notes = "Returns true if the other bounding box, specified by its 2 corners, is inside of this box." },
+ },
+ Move =
+ {
+ { Params = "OffsetX, OffsetY, OffsetZ", Return = "", Notes = "Moves the bounding box by the specified offset in each axis" },
+ { Params = "{{Vector3d|Offset}}", Return = "", Notes = "Moves the bounding box by the specified offset in each axis" },
+ },
+ Union = { Params = "OtherBoundingBox", Return = "cBoundingBox", Notes = "Returns the smallest bounding box that contains both OtherBoundingBox and this bounding box. Note that unlike the strict geometrical meaning of \"union\", this operation actually returns a cBoundingBox." },
+ },
+ }, -- cBoundingBox
+
+
+ cCuboid =
+ {
+ Desc = [[
+ cCuboid offers some native support for integral-boundary cuboids. A cuboid internally consists of
+ two {{Vector3i}}s. By default the cuboid doesn't make any assumptions about the defining points,
+ but for most of the operations in the cCuboid class, the p1 member variable is expected to be the
+ minima and the p2 variable the maxima. The Sort() function guarantees this condition.</p>
+ <p>
+ The Cuboid considers both its edges inclusive.</p>
+ ]],
+ Functions =
+ {
+ constructor =
+ {
+ { Params = "OtheCuboid", Return = "cCuboid", Notes = "Creates a new Cuboid object as a copy of OtherCuboid" },
+ { Params = "{{Vector3i|Point1}}, {{Vector3i|Point2}}", Return = "cCuboid", Notes = "Creates a new Cuboid object with the specified points as its corners." },
+ { Params = "X, Y, Z", Return = "cCuboid", Notes = "Creates a new Cuboid object with the specified point as both its corners (the cuboid has a size of 1 in each direction)." },
+ { Params = "X1, Y1, Z1, X2, Y2, Z2", Return = "cCuboid", Notes = "Creates a new Cuboid object with the specified points as its corners." },
+ },
+ Assign = { Params = "X1, Y1, Z1, X2, Y2, Z2", Return = "", Notes = "Assigns all the coords stored in the cuboid. Sort-state is ignored." },
+ ClampX = { Params = "MinX, MaxX", Return = "", Notes = "Clamps both X coords into the range provided. Sortedness-agnostic." },
+ ClampY = { Params = "MinY, MaxY", Return = "", Notes = "Clamps both Y coords into the range provided. Sortedness-agnostic." },
+ ClampZ = { Params = "MinZ, MaxZ", Return = "", Notes = "Clamps both Z coords into the range provided. Sortedness-agnostic." },
+ DifX = { Params = "", Return = "number", Notes = "Returns the difference between the two X coords (X-size minus 1). Assumes sorted." },
+ DifY = { Params = "", Return = "number", Notes = "Returns the difference between the two Y coords (Y-size minus 1). Assumes sorted." },
+ DifZ = { Params = "", Return = "number", Notes = "Returns the difference between the two Z coords (Z-size minus 1). Assumes sorted." },
+ DoesIntersect = { Params = "OtherCuboid", Return = "bool", Notes = "Returns true if this cuboid has at least one voxel in common with OtherCuboid. Note that edges are considered inclusive. Assumes both sorted." },
+ Expand = { Params = "SubMinX, AddMaxX, SubMinY, AddMaxY, SubMinZ, AddMaxZ", Return = "", Notes = "Expands the cuboid by the specified amount in each direction. Works on unsorted cuboids as well. NOTE: this function doesn't check for underflows." },
+ GetVolume = { Params = "", Return = "number", Notes = "Returns the volume of the cuboid, in blocks. Note that the volume considers both coords inclusive. Works on unsorted cuboids, too." },
+ IsCompletelyInside = { Params = "OuterCuboid", Return = "bool", Notes = "Returns true if this cuboid is completely inside (in all directions) in OuterCuboid. Assumes both sorted." },
+ IsInside =
+ {
+ { Params = "X, Y, Z", Return = "bool", Notes = "Returns true if the specified point (integral coords) is inside this cuboid. Assumes sorted." },
+ { Params = "{{Vector3i|Point}}", Return = "bool", Notes = "Returns true if the specified point (integral coords) is inside this cuboid. Assumes sorted." },
+ { Params = "{{Vector3d|Point}}", Return = "bool", Notes = "Returns true if the specified point (floating-point coords) is inside this cuboid. Assumes sorted." },
+ },
+ IsSorted = { Params = "", Return = "bool", Notes = "Returns true if this cuboid is sorted" },
+ Move = { Params = "OffsetX, OffsetY, OffsetZ", Return = "", Notes = "Adds the specified offsets to each respective coord, effectively moving the Cuboid. Sort-state is ignored and preserved." },
+ Sort = { Params = "", Return = "" , Notes = "Sorts the internal representation so that p1 contains the lesser coords and p2 contains the greater coords." },
+ },
+ Variables =
+ {
+ p1 = { Type = "{{Vector3i}}", Notes = "The first corner. Usually the lesser of the two coords in each set" },
+ p2 = { Type = "{{Vector3i}}", Notes = "The second corner. Usually the larger of the two coords in each set" },
+ },
+ }, -- cCuboid
+
+
+ cLineBlockTracer =
+ {
+ Desc = [[This class provides an easy-to-use interface for tracing lines through individual
+blocks in the world. It will call the provided callbacks according to what events it encounters along the
+way.</p>
+<p>
+For the Lua API, there's only one static function exported that takes all the parameters necessary to do
+the tracing. The Callbacks parameter is a table containing all the functions that will be called upon the
+various events. See below for further information.
+ ]],
+ Functions =
+ {
+ Trace = { Params = "{{cWorld}}, Callbacks, StartX, StartY, StartZ, EndX, EndY, EndZ", Return = "bool", Notes = "(STATIC) Performs the trace on the specified line. Returns true if the entire trace was processed (no callback returned true)" },
+ },
+
+ AdditionalInfo =
+ {
+ {
+ Header = "Callbacks",
+ Contents = [[
+The Callbacks in the Trace() function is a table that contains named functions. MCServer will call
+individual functions from that table for the events that occur on the line - hitting a block, going out of
+valid world data etc. The following table lists all the available callbacks. If the callback function is
+not defined, MCServer skips it. Each function can return a bool value, if it returns true, the tracing is
+aborted and Trace() returns false.</p>
+<p>
+<table><tr><th>Name</th><th>Parameters</th><th>Notes</th></tr>
+<tr><td>OnNextBlock</td><td>BlockX, BlockY, BlockZ, BlockType, BlockMeta, EntryFace</td>
+ <td>Called when the ray hits a new valid block. The block type and meta is given. EntryFace is one of the
+ BLOCK_FACE_ constants indicating which "side" of the block got hit by the ray.</td></tr>
+<tr><td>OnNextBlockNoData</td><td>BlockX, BlockY, BlockZ, EntryFace</td>
+ <td>Called when the ray hits a new block, but the block is in an unloaded chunk - no valid data is
+ available. Only the coords and the entry face are given.</td></tr>
+<tr><td>OnOutOfWorld</td><td>X, Y, Z</td>
+ <td>Called when the ray goes outside of the world (Y-wise); the coords specify the exact exit point. Note
+ that for other paths than lines (considered for future implementations) the path may leave the world and
+ go back in again later, in such a case this callback is followed by OnIntoWorld() and further
+ OnNextBlock() calls.</td></tr>
+<tr><td>OnIntoWorld</td><td>X, Y, Z</td>
+ <td>Called when the ray enters the world (Y-wise); the coords specify the exact entry point.</td></tr>
+<tr><td>OnNoMoreHits</td><td>&nbsp;</td>
+ <td>Called when the path is sure not to hit any more blocks. This is the final callback, no more
+ callbacks are called after this function. Unlike the other callbacks, this function doesn't have a return
+ value.</td></tr>
+<tr><td>OnNoChunk</td><td>&nbsp;</td>
+ <td>Called when the ray enters a chunk that is not loaded. This usually means that the tracing is aborted.
+ Unlike the other callbacks, this function doesn't have a return value.</td></tr>
+</table>
+ ]],
+ },
+ {
+ Header = "Example",
+ Contents = [[
+<p>The following example is taken from the Debuggers plugin. It is a command handler function for the
+"/spidey" command that creates a line of cobweb blocks from the player's eyes up to 50 blocks away in
+the direction they're looking, but only through the air.
+<pre class="prettyprint lang-lua">
+function HandleSpideyCmd(a_Split, a_Player)
+ local World = a_Player:GetWorld();
+
+ local Callbacks = {
+ OnNextBlock = function(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta)
+ if (a_BlockType ~= E_BLOCK_AIR) then
+ -- abort the trace
+ return true;
+ end
+ World:SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_COBWEB, 0);
+ end
+ };
+
+ local EyePos = a_Player:GetEyePosition();
+ local LookVector = a_Player:GetLookVector();
+ LookVector:Normalize(); -- Make the vector 1 m long
+
+ -- Start cca 2 blocks away from the eyes
+ local Start = EyePos + LookVector + LookVector;
+ local End = EyePos + LookVector * 50;
+
+ cLineBlockTracer.Trace(World, Callbacks, Start.x, Start.y, Start.z, End.x, End.y, End.z);
+
+ return true;
+end
+</pre>
+</p>
+ ]],
+ },
+ }, -- AdditionalInfo
+ }, -- cLineBlockTracer
+
+
+ cTracer =
+ {
+ Desc = [[
+ A cTracer object is used to trace lines in the world. One thing you can use the cTracer for, is
+ tracing what block a player is looking at, but you can do more with it if you want.</p>
+ <p>
+ The cTracer is still a work in progress.</p>
+ <p>
+ See also the {{cLineBlockTracer}} class for an alternative approach using callbacks.
+ ]],
+ Functions =
+ {
+ },
+ }, -- cTracer
+
+
+ Vector3d =
+ {
+ Desc = [[
+ A Vector3d object uses double precision floating point values to describe a point in 3D space.</p>
+ <p>
+ See also {{Vector3f}} for single-precision floating point 3D coords and {{Vector3i}} for integer
+ 3D coords.
+ ]],
+ Functions =
+ {
+ constructor =
+ {
+ { Params = "{{Vector3f}}", Return = "Vector3d", Notes = "Creates a new Vector3d object by copying the coords from the given Vector3f." },
+ { Params = "", Return = "Vector3d", Notes = "Creates a new Vector3d object with all its coords set to 0." },
+ { Params = "X, Y, Z", Return = "Vector3d", Notes = "Creates a new Vector3d object with its coords set to the specified values." },
+ },
+ operator_div = { Params = "number", Return = "Vector3d", Notes = "Returns a new Vector3d with each coord divided by the specified number." },
+ operator_mul = { Params = "number", Return = "Vector3d", Notes = "Returns a new Vector3d with each coord multiplied." },
+ operator_sub = { Params = "Vector3d", Return = "Vector3d", Notes = "Returns a new Vector3d containing the difference between this object and the specified vector." },
+ operator_plus = {Params = "Vector3d", Return = "Vector3d", Notes = "Returns a new Vector3d containing the sum of this vector and the specified vector" },
+ Cross = { Params = "Vector3d", Return = "Vector3d", Notes = "Returns a new Vector3d that is a {{http://en.wikipedia.org/wiki/Cross_product|cross product}} of this vector and the specified vector." },
+ Dot = { Params = "Vector3d", Return = "number", Notes = "Returns the dot product of this vector and the specified vector." },
+ Equals = { Params = "Vector3d", Return = "bool", Notes = "Returns true if this vector is exactly equal to the specified vector." },
+ Length = { Params = "", Return = "number", Notes = "Returns the (euclidean) length of the vector." },
+ LineCoeffToXYPlane = { Params = "Vector3d, Z", Return = "number", Notes = "Returns the coefficient for the line from the specified vector through this vector to reach the specified Z coord. The result satisfies the following equation: (this + Result * (Param - this)).z = Z. Returns the NO_INTERSECTION constant if there's no intersection." },
+ LineCoeffToXZPlane = { Params = "Vector3d, Y", Return = "number", Notes = "Returns the coefficient for the line from the specified vector through this vector to reach the specified Y coord. The result satisfies the following equation: (this + Result * (Param - this)).y = Y. Returns the NO_INTERSECTION constant if there's no intersection." },
+ LineCoeffToYZPlane = { Params = "Vector3d, X", Return = "number", Notes = "Returns the coefficient for the line from the specified vector through this vector to reach the specified X coord. The result satisfies the following equation: (this + Result * (Param - this)).x = X. Returns the NO_INTERSECTION constant if there's no intersection." },
+ Normalize = { Params = "", Return = "", Notes = "Changes this vector so that it keeps current direction but is exactly 1 unit long. FIXME: Fails for a zero vector." },
+ NormalizeCopy = { Params = "", Return = "Vector3d", Notes = "Returns a new vector that has the same directino as this but is exactly 1 unit long. FIXME: Fails for a zero vector." },
+ Set = { Params = "X, Y, Z", Return = "", Notes = "Sets all the coords in this object." },
+ SqrLength = { Params = "", Return = "number", Notes = "Returns the (euclidean) length of this vector, squared. This operation is slightly less computationally expensive than Length(), while it conserves some properties of Length(), such as comparison. " },
+ },
+ Constants =
+ {
+ EPS = { Notes = "The max difference between two coords for which the coords are assumed equal (in LineCoeffToXYPlane() et al)." },
+ NO_INTERSECTION = { Notes = "Special return value for the LineCoeffToXYPlane() et al meaning that there's no intersectino with the plane." },
+ },
+ Variables =
+ {
+ x = { Type = "number", Notes = "The X coord of the vector." },
+ y = { Type = "number", Notes = "The Y coord of the vector." },
+ z = { Type = "number", Notes = "The Z coord of the vector." },
+ },
+ }, -- Vector3d
+
+ Vector3f =
+ {
+ Desc = [[
+ A Vector3f object uses floating point values to describe a point in space.</p>
+ <p>
+ See also {{Vector3d}} for double-precision floating point 3D coords and {{Vector3i}} for integer
+ 3D coords.
+ ]],
+ Functions =
+ {
+ constructor =
+ {
+ { Params = "", Return = "Vector3f", Notes = "Creates a new Vector3f object with zero coords" },
+ { Params = "x, y, z", Return = "Vector3f", Notes = "Creates a new Vector3f object with the specified coords" },
+ { Params = "Vector3f", Return = "Vector3f", Notes = "Creates a new Vector3f object as a copy of the specified vector" },
+ { Params = "{{Vector3d}}", Return = "Vector3f", Notes = "Creates a new Vector3f object as a copy of the specified {{Vector3d}}" },
+ { Params = "{{Vector3i}}", Return = "Vector3f", Notes = "Creates a new Vector3f object as a copy of the specified {{Vector3i}}" },
+ },
+ operator_mul =
+ {
+ { Params = "number", Return = "Vector3f", Notes = "Returns a new Vector3f object that has each of its coords multiplied by the specified number" },
+ { Params = "Vector3f", Return = "Vector3f", Notes = "Returns a new Vector3f object that has each of its coords multiplied by the respective coord of the specified vector." },
+ },
+ operator_plus = { Params = "Vector3f", Return = "Vector3f", Notes = "Returns a new Vector3f object that holds the vector sum of this vector and the specified vector." },
+ operator_sub = { Params = "Vector3f", Return = "Vector3f", Notes = "Returns a new Vector3f object that holds the vector differrence between this vector and the specified vector." },
+ Cross = { Params = "Vector3f", Return = "Vector3f", Notes = "Returns a new Vector3f object that holds the cross product of this vector and the specified vector." },
+ Dot = { Params = "Vector3f", Return = "number", Notes = "Returns the dot product of this vector and the specified vector." },
+ Equals = { Params = "Vector3f", Return = "bool", Notes = "Returns true if the specified vector is exactly equal to this vector." },
+ Length = { Params = "", Return = "number", Notes = "Returns the (euclidean) length of this vector" },
+ Normalize = { Params = "", Return = "", Notes = "Normalizes this vector (makes it 1 unit long while keeping the direction). FIXME: Fails for zero vectors." },
+ NormalizeCopy = { Params = "", Return = "Vector3f", Notes = "Returns a copy of this vector that is normalized (1 unit long while keeping the same direction). FIXME: Fails for zero vectors." },
+ Set = { Params = "x, y, z", Return = "", Notes = "Sets all the coords of the vector at once." },
+ SqrLength = { Params = "", Return = "number", Notes = "Returns the (euclidean) length of this vector, squared. This operation is slightly less computationally expensive than Length(), while it conserves some properties of Length(), such as comparison." },
+ },
+ Variables =
+ {
+ x = { Type = "number", Notes = "The X coord of the vector." },
+ y = { Type = "number", Notes = "The Y coord of the vector." },
+ z = { Type = "number", Notes = "The Z coord of the vector." },
+ },
+ }, -- Vector3f
+
+ Vector3i =
+ {
+ Desc = [[
+ A Vector3i object uses integer values to describe a point in space.</p>
+ <p>
+ See also {{Vector3d}} for double-precision floating point 3D coords and {{Vector3f}} for
+ single-precision floating point 3D coords.
+ ]],
+ Functions =
+ {
+ constructor =
+ {
+ { Params = "", Return = "Vector3i", Notes = "Creates a new Vector3i object with zero coords." },
+ { Params = "x, y, z", Return = "Vector3i", Notes = "Creates a new Vector3i object with the specified coords." },
+ { Params = "{{Vector3d}}", Return = "Vector3i", Notes = "Creates a new Vector3i object with coords copied and floor()-ed from the specified {{Vector3d}}." },
+ },
+ Equals = { Params = "Vector3i", Return = "bool", Notes = "Returns true if this vector is exactly the same as the specified vector." },
+ Length = { Params = "", Return = "number", Notes = "Returns the (euclidean) length of this vector." },
+ Set = { Params = "x, y, z", Return = "", Notes = "Sets all the coords of the vector at once" },
+ SqrLength = { Params = "", Return = "number", Notes = "Returns the (euclidean) length of this vector, squared. This operation is slightly less computationally expensive than Length(), while it conserves some properties of Length(), such as comparison." },
+ },
+ Variables =
+ {
+ x = { Type = "number", Notes = "The X coord of the vector." },
+ y = { Type = "number", Notes = "The Y coord of the vector." },
+ z = { Type = "number", Notes = "The Z coord of the vector." },
+ },
+ }, -- Vector3i
+}
+
+
+
+
diff --git a/MCServer/Plugins/InfoReg.lua b/MCServer/Plugins/InfoReg.lua
index 3afb57488..1cf68dbed 100644
--- a/MCServer/Plugins/InfoReg.lua
+++ b/MCServer/Plugins/InfoReg.lua
@@ -85,6 +85,10 @@ function RegisterPluginInfoCommands()
local function RegisterSubcommands(a_Prefix, a_Subcommands, a_Level)
assert(a_Subcommands ~= nil);
+ -- A table that will hold aliases to subcommands temporarily, during subcommand iteration
+ local AliasTable = {}
+
+ -- Iterate through the subcommands, register them, and accumulate aliases:
for cmd, info in pairs(a_Subcommands) do
local CmdName = a_Prefix .. cmd;
local Handler = info.Handler;
@@ -112,15 +116,25 @@ function RegisterPluginInfoCommands()
end
for idx, alias in ipairs(info.Alias) do
cPluginManager.BindCommand(a_Prefix .. alias, info.Permission or "", Handler, HelpString);
+ -- Also copy the alias's info table as a separate subcommand,
+ -- so that MultiCommandHandler() handles it properly. Need to off-load into a separate table
+ -- than the one we're currently iterating and join after the iterating.
+ AliasTable[alias] = info
end
end
- end
+ end -- else (if Handler == nil)
-- Recursively register any subcommands:
if (info.Subcommands ~= nil) then
RegisterSubcommands(a_Prefix .. cmd .. " ", info.Subcommands, a_Level + 1);
end
+ end -- for cmd, info - a_Subcommands[]
+
+ -- Add the subcommand aliases that were off-loaded during registration:
+ for alias, info in pairs(AliasTable) do
+ a_Subcommands[alias] = info
end
+ AliasTable = {}
end
-- Loop through all commands in the plugin info, register each:
diff --git a/MCServer/crafting.txt b/MCServer/crafting.txt
index fe9a465d0..92abe24cb 100644
--- a/MCServer/crafting.txt
+++ b/MCServer/crafting.txt
@@ -156,7 +156,7 @@ Lighter = IronIngot, 1:1 | Flint, 2:2
Lighter = IronIngot, 2:1 | Flint, 1:2
Bucket = IronIngot, 1:1, 2:2, 3:1
Compass = IronIngot, 2:1, 1:2, 3:2, 2:3 | RedstoneDust, 2:2
-Map = Paper, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | Compass, 2:2
+EmptyMap = Paper, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | Compass, 2:2
Watch = GoldIngot, 2:1, 1:2, 3:2, 2:3 | RedstoneDust, 2:2
FishingRod = Stick, 1:3, 2:2, 3:1 | String, 3:2, 3:3
FishingRod = Stick, 3:3, 2:2, 1:1 | String, 1:2, 1:3
diff --git a/MCServer/lua5.1.dll b/MCServer/lua5.1.dll
new file mode 100644
index 000000000..cca0bcb25
--- /dev/null
+++ b/MCServer/lua5.1.dll
Binary files differ
diff --git a/lib/lua/CMakeLists.txt b/lib/lua/CMakeLists.txt
index 4babae9b2..db112d557 100644
--- a/lib/lua/CMakeLists.txt
+++ b/lib/lua/CMakeLists.txt
@@ -47,8 +47,12 @@ if (WIN32)
)
endif()
+ set_target_properties(lua PROPERTIES OUTPUT_NAME "lua51")
+
# NOTE: The DLL for each configuration is stored at the same place, thus overwriting each other.
- # This is known, however such behavior is needed for LuaRocks - they always load "lua.dll"
+ # This is known, however such behavior is needed for LuaRocks - they always load "lua5.1.dll" or "lua51.dll"
+ # We make it work by compiling to "lua51.dll" and providing a proxy-DLL "lua5.1.dll"
+ # See http://lua-users.org/wiki/LuaProxyDllFour for details
else()
add_library(lua ${SOURCE})
endif()
diff --git a/src/Bindings/AllToLua.pkg b/src/Bindings/AllToLua.pkg
index 1f08c66dc..6537437cd 100644
--- a/src/Bindings/AllToLua.pkg
+++ b/src/Bindings/AllToLua.pkg
@@ -48,6 +48,7 @@ $cfile "../ItemGrid.h"
$cfile "../BlockEntities/BlockEntity.h"
$cfile "../BlockEntities/BlockEntityWithItems.h"
$cfile "../BlockEntities/ChestEntity.h"
+$cfile "../BlockEntities/CommandBlockEntity.h"
$cfile "../BlockEntities/DropSpenserEntity.h"
$cfile "../BlockEntities/DispenserEntity.h"
$cfile "../BlockEntities/DropperEntity.h"
@@ -72,6 +73,8 @@ $cfile "../CraftingRecipes.h"
$cfile "../UI/Window.h"
$cfile "../Mobs/Monster.h"
$cfile "../CompositeChat.h"
+$cfile "../Map.h"
+$cfile "../MapManager.h"
@@ -84,3 +87,10 @@ class cLineBlockTracer;
+
+// To avoid tolua treating Byte as a class, and avoid the need to $cfile entire Globals.h:
+typedef unsigned char Byte;
+
+
+
+
diff --git a/src/Bindings/LuaState.cpp b/src/Bindings/LuaState.cpp
index ca7f6b255..45a066efe 100644
--- a/src/Bindings/LuaState.cpp
+++ b/src/Bindings/LuaState.cpp
@@ -677,6 +677,11 @@ void cLuaState::Push(void * a_Ptr)
{
ASSERT(IsValid());
+ // Investigate the cause of this - what is the callstack?
+ LOGWARNING("Lua engine encountered an error - attempting to push a plain pointer");
+ LogStackTrace();
+ ASSERT(!"A plain pointer should never be pushed on Lua stack");
+
lua_pushnil(m_LuaState);
m_NumCurrentFunctionArgs += 1;
}
@@ -1265,6 +1270,8 @@ void cLuaState::LogStack(const char * a_Header)
void cLuaState::LogStack(lua_State * a_LuaState, const char * a_Header)
{
+ UNUSED(a_Header); // The param seems unused when compiling for release, so the compiler warns
+
LOGD((a_Header != NULL) ? a_Header : "Lua C API Stack contents:");
for (int i = lua_gettop(a_LuaState); i > 0; i--)
{
diff --git a/src/Bindings/ManualBindings.cpp b/src/Bindings/ManualBindings.cpp
index c220e5e0a..461186d3b 100644
--- a/src/Bindings/ManualBindings.cpp
+++ b/src/Bindings/ManualBindings.cpp
@@ -1321,7 +1321,9 @@ static int tolua_cPluginManager_ForEachCommand(lua_State * tolua_S)
private:
virtual bool Command(const AString & a_Command, const cPlugin * a_Plugin, const AString & a_Permission, const AString & a_HelpString) override
{
- lua_rawgeti( LuaState, LUA_REGISTRYINDEX, FuncRef); /* Push function reference */
+ UNUSED(a_Plugin);
+
+ lua_rawgeti(LuaState, LUA_REGISTRYINDEX, FuncRef); /* Push function reference */
tolua_pushcppstring(LuaState, a_Command);
tolua_pushcppstring(LuaState, a_Permission);
tolua_pushcppstring(LuaState, a_HelpString);
@@ -1396,7 +1398,10 @@ static int tolua_cPluginManager_ForEachConsoleCommand(lua_State * tolua_S)
private:
virtual bool Command(const AString & a_Command, const cPlugin * a_Plugin, const AString & a_Permission, const AString & a_HelpString) override
{
- lua_rawgeti( LuaState, LUA_REGISTRYINDEX, FuncRef); /* Push function reference */
+ UNUSED(a_Plugin);
+ UNUSED(a_Permission);
+
+ lua_rawgeti(LuaState, LUA_REGISTRYINDEX, FuncRef); /* Push function reference */
tolua_pushcppstring(LuaState, a_Command);
tolua_pushcppstring(LuaState, a_HelpString);
@@ -2350,6 +2355,37 @@ static int tolua_cBlockArea_GetBlockTypeMeta(lua_State * tolua_S)
+static int tolua_cBlockArea_GetOrigin(lua_State * tolua_S)
+{
+ // function cBlockArea::GetOrigin()
+ // Returns all three coords of the origin point
+ // Exported manually because there's no direct C++ equivalent,
+ // plus tolua would generate extra input params for the outputs
+
+ cLuaState L(tolua_S);
+ if (!L.CheckParamUserType(1, "cBlockArea"))
+ {
+ return 0;
+ }
+
+ cBlockArea * self = (cBlockArea *)tolua_tousertype(tolua_S, 1, NULL);
+ if (self == NULL)
+ {
+ tolua_error(tolua_S, "invalid 'self' in function 'cBlockArea:GetOrigin'", NULL);
+ return 0;
+ }
+
+ // Push the three origin coords:
+ lua_pushnumber(tolua_S, self->GetOriginX());
+ lua_pushnumber(tolua_S, self->GetOriginY());
+ lua_pushnumber(tolua_S, self->GetOriginZ());
+ return 3;
+}
+
+
+
+
+
static int tolua_cBlockArea_GetRelBlockTypeMeta(lua_State * tolua_S)
{
// function cBlockArea::GetRelBlockTypeMeta()
@@ -2385,6 +2421,37 @@ static int tolua_cBlockArea_GetRelBlockTypeMeta(lua_State * tolua_S)
+static int tolua_cBlockArea_GetSize(lua_State * tolua_S)
+{
+ // function cBlockArea::GetSize()
+ // Returns all three sizes of the area
+ // Exported manually because there's no direct C++ equivalent,
+ // plus tolua would generate extra input params for the outputs
+
+ cLuaState L(tolua_S);
+ if (!L.CheckParamUserType(1, "cBlockArea"))
+ {
+ return 0;
+ }
+
+ cBlockArea * self = (cBlockArea *)tolua_tousertype(tolua_S, 1, NULL);
+ if (self == NULL)
+ {
+ tolua_error(tolua_S, "invalid 'self' in function 'cBlockArea:GetSize'", NULL);
+ return 0;
+ }
+
+ // Push the three origin coords:
+ lua_pushnumber(tolua_S, self->GetSizeX());
+ lua_pushnumber(tolua_S, self->GetSizeY());
+ lua_pushnumber(tolua_S, self->GetSizeZ());
+ return 3;
+}
+
+
+
+
+
static int tolua_cBlockArea_LoadFromSchematicFile(lua_State * tolua_S)
{
// function cBlockArea::LoadFromSchematicFile
@@ -2461,7 +2528,9 @@ void ManualBindings::Bind(lua_State * tolua_S)
tolua_beginmodule(tolua_S, "cBlockArea");
tolua_function(tolua_S, "GetBlockTypeMeta", tolua_cBlockArea_GetBlockTypeMeta);
+ tolua_function(tolua_S, "GetOrigin", tolua_cBlockArea_GetOrigin);
tolua_function(tolua_S, "GetRelBlockTypeMeta", tolua_cBlockArea_GetRelBlockTypeMeta);
+ tolua_function(tolua_S, "GetSize", tolua_cBlockArea_GetSize);
tolua_function(tolua_S, "LoadFromSchematicFile", tolua_cBlockArea_LoadFromSchematicFile);
tolua_function(tolua_S, "SaveToSchematicFile", tolua_cBlockArea_SaveToSchematicFile);
tolua_endmodule(tolua_S);
@@ -2511,6 +2580,10 @@ void ManualBindings::Bind(lua_State * tolua_S)
tolua_function(tolua_S, "UpdateSign", tolua_cWorld_SetSignLines);
tolua_endmodule(tolua_S);
+ tolua_beginmodule(tolua_S, "cMapManager");
+ tolua_function(tolua_S, "DoWithMap", tolua_DoWithID<cMapManager, cMap, &cMapManager::DoWithMap>);
+ tolua_endmodule(tolua_S);
+
tolua_beginmodule(tolua_S, "cPlugin");
tolua_function(tolua_S, "Call", tolua_cPlugin_Call);
tolua_endmodule(tolua_S);
diff --git a/src/BlockArea.cpp b/src/BlockArea.cpp
index 194e2d68a..d07ef747a 100644
--- a/src/BlockArea.cpp
+++ b/src/BlockArea.cpp
@@ -8,6 +8,7 @@
#include "BlockArea.h"
#include "OSSupport/GZipFile.h"
#include "Blocks/BlockHandler.h"
+#include "Cuboid.h"
@@ -264,6 +265,15 @@ void cBlockArea::SetOrigin(int a_OriginX, int a_OriginY, int a_OriginZ)
+void cBlockArea::SetOrigin(const Vector3i & a_Origin)
+{
+ SetOrigin(a_Origin.x, a_Origin.y, a_Origin.z);
+}
+
+
+
+
+
bool cBlockArea::Read(cForEachChunkProvider * a_ForEachChunkProvider, int a_MinBlockX, int a_MaxBlockX, int a_MinBlockY, int a_MaxBlockY, int a_MinBlockZ, int a_MaxBlockZ, int a_DataTypes)
{
// Normalize the coords:
@@ -338,6 +348,36 @@ bool cBlockArea::Read(cForEachChunkProvider * a_ForEachChunkProvider, int a_MinB
+bool cBlockArea::Read(cForEachChunkProvider * a_ForEachChunkProvider, const cCuboid & a_Bounds, int a_DataTypes)
+{
+ return Read(
+ a_ForEachChunkProvider,
+ a_Bounds.p1.x, a_Bounds.p2.x,
+ a_Bounds.p1.y, a_Bounds.p2.y,
+ a_Bounds.p1.z, a_Bounds.p2.z,
+ a_DataTypes
+ );
+}
+
+
+
+
+
+bool cBlockArea::Read(cForEachChunkProvider * a_ForEachChunkProvider, const Vector3i & a_Point1, const Vector3i & a_Point2, int a_DataTypes)
+{
+ return Read(
+ a_ForEachChunkProvider,
+ a_Point1.x, a_Point2.x,
+ a_Point1.y, a_Point2.y,
+ a_Point1.z, a_Point2.z,
+ a_DataTypes
+ );
+}
+
+
+
+
+
bool cBlockArea::Write(cForEachChunkProvider * a_ForEachChunkProvider, int a_MinBlockX, int a_MinBlockY, int a_MinBlockZ, int a_DataTypes)
{
ASSERT((a_DataTypes & GetDataTypes()) == a_DataTypes); // Are you requesting only the data that I have?
@@ -362,6 +402,19 @@ bool cBlockArea::Write(cForEachChunkProvider * a_ForEachChunkProvider, int a_Min
+bool cBlockArea::Write(cForEachChunkProvider * a_ForEachChunkProvider, const Vector3i & a_MinCoords, int a_DataTypes)
+{
+ return Write(
+ a_ForEachChunkProvider,
+ a_MinCoords.x, a_MinCoords.y, a_MinCoords.z,
+ a_DataTypes
+ );
+}
+
+
+
+
+
void cBlockArea::CopyTo(cBlockArea & a_Into) const
{
if (&a_Into == this)
@@ -643,6 +696,15 @@ void cBlockArea::Merge(const cBlockArea & a_Src, int a_RelX, int a_RelY, int a_R
+void cBlockArea::Merge(const cBlockArea & a_Src, const Vector3i & a_RelMinCoords, eMergeStrategy a_Strategy)
+{
+ Merge(a_Src, a_RelMinCoords.x, a_RelMinCoords.y, a_RelMinCoords.z, a_Strategy);
+}
+
+
+
+
+
void cBlockArea::Fill(int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, NIBBLETYPE a_BlockLight, NIBBLETYPE a_BlockSkyLight)
{
if ((a_DataTypes & GetDataTypes()) != a_DataTypes)
@@ -735,6 +797,23 @@ void cBlockArea::FillRelCuboid(int a_MinRelX, int a_MaxRelX, int a_MinRelY, int
+void cBlockArea::FillRelCuboid(const cCuboid & a_RelCuboid,
+ int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta,
+ NIBBLETYPE a_BlockLight, NIBBLETYPE a_BlockSkyLight
+)
+{
+ FillRelCuboid(
+ a_RelCuboid.p1.x, a_RelCuboid.p2.x,
+ a_RelCuboid.p1.y, a_RelCuboid.p2.y,
+ a_RelCuboid.p1.z, a_RelCuboid.p2.z,
+ a_DataTypes, a_BlockType, a_BlockMeta, a_BlockLight, a_BlockSkyLight
+ );
+}
+
+
+
+
+
void cBlockArea::RelLine(int a_RelX1, int a_RelY1, int a_RelZ1, int a_RelX2, int a_RelY2, int a_RelZ2,
int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta,
NIBBLETYPE a_BlockLight, NIBBLETYPE a_BlockSkyLight
@@ -852,6 +931,22 @@ void cBlockArea::RelLine(int a_RelX1, int a_RelY1, int a_RelZ1, int a_RelX2, int
+void cBlockArea::RelLine(const Vector3i & a_Point1, const Vector3i & a_Point2,
+ int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta,
+ NIBBLETYPE a_BlockLight, NIBBLETYPE a_BlockSkyLight
+)
+{
+ RelLine(
+ a_Point1.x, a_Point1.y, a_Point1.z,
+ a_Point2.x, a_Point2.y, a_Point2.z,
+ a_DataTypes, a_BlockType, a_BlockMeta, a_BlockLight, a_BlockSkyLight
+ );
+}
+
+
+
+
+
void cBlockArea::RotateCCW(void)
{
if (!HasBlockTypes())
@@ -878,7 +973,7 @@ void cBlockArea::RotateCCW(void)
int NewX = z;
for (int y = 0; y < m_SizeY; y++)
{
- int NewIdx = NewX + NewZ * m_SizeX + y * m_SizeX * m_SizeZ;
+ int NewIdx = NewX + NewZ * m_SizeZ + y * m_SizeX * m_SizeZ;
int OldIdx = MakeIndex(x, y, z);
NewTypes[NewIdx] = m_BlockTypes[OldIdx];
NewMetas[NewIdx] = BlockHandler(m_BlockTypes[OldIdx])->MetaRotateCCW(m_BlockMetas[OldIdx]);
@@ -923,7 +1018,7 @@ void cBlockArea::RotateCW(void)
int NewX = m_SizeZ - z - 1;
for (int y = 0; y < m_SizeY; y++)
{
- int NewIdx = NewX + NewZ * m_SizeX + y * m_SizeX * m_SizeZ;
+ int NewIdx = NewX + NewZ * m_SizeZ + y * m_SizeX * m_SizeZ;
int OldIdx = MakeIndex(x, y, z);
NewTypes[NewIdx] = m_BlockTypes[OldIdx];
NewMetas[NewIdx] = BlockHandler(m_BlockTypes[OldIdx])->MetaRotateCW(m_BlockMetas[OldIdx]);
@@ -1075,7 +1170,7 @@ void cBlockArea::RotateCCWNoMeta(void)
int NewX = z;
for (int y = 0; y < m_SizeY; y++)
{
- NewTypes[NewX + NewZ * m_SizeX + y * m_SizeX * m_SizeZ] = m_BlockTypes[MakeIndex(x, y, z)];
+ NewTypes[NewX + NewZ * m_SizeZ + y * m_SizeX * m_SizeZ] = m_BlockTypes[MakeIndex(x, y, z)];
} // for y
} // for z
} // for x
@@ -1093,7 +1188,7 @@ void cBlockArea::RotateCCWNoMeta(void)
int NewX = z;
for (int y = 0; y < m_SizeY; y++)
{
- NewMetas[NewX + NewZ * m_SizeX + y * m_SizeX * m_SizeZ] = m_BlockMetas[MakeIndex(x, y, z)];
+ NewMetas[NewX + NewZ * m_SizeZ + y * m_SizeX * m_SizeZ] = m_BlockMetas[MakeIndex(x, y, z)];
} // for y
} // for z
} // for x
@@ -1120,7 +1215,7 @@ void cBlockArea::RotateCWNoMeta(void)
int NewZ = x;
for (int y = 0; y < m_SizeY; y++)
{
- NewTypes[NewX + NewZ * m_SizeX + y * m_SizeX * m_SizeZ] = m_BlockTypes[MakeIndex(x, y, z)];
+ NewTypes[NewX + NewZ * m_SizeZ + y * m_SizeX * m_SizeZ] = m_BlockTypes[MakeIndex(x, y, z)];
} // for y
} // for x
} // for z
@@ -1138,7 +1233,7 @@ void cBlockArea::RotateCWNoMeta(void)
int NewZ = x;
for (int y = 0; y < m_SizeY; y++)
{
- NewMetas[NewX + NewZ * m_SizeX + y * m_SizeX * m_SizeZ] = m_BlockMetas[MakeIndex(x, y, z)];
+ NewMetas[NewX + NewZ * m_SizeZ + y * m_SizeX * m_SizeZ] = m_BlockMetas[MakeIndex(x, y, z)];
} // for y
} // for x
} // for z
diff --git a/src/BlockArea.h b/src/BlockArea.h
index b4a161f32..0703f195e 100644
--- a/src/BlockArea.h
+++ b/src/BlockArea.h
@@ -15,6 +15,16 @@
#include "ForEachChunkProvider.h"
+
+
+// fwd:
+class cCuboid;
+class Vector3i;
+
+
+
+
+
// tolua_begin
class cBlockArea
{
@@ -24,7 +34,7 @@ class cBlockArea
public:
- /// What data is to be queried (bit-mask)
+ /** What data is to be queried (bit-mask) */
enum
{
baTypes = 1,
@@ -44,7 +54,7 @@ public:
cBlockArea(void);
~cBlockArea();
- /// Clears the data stored to reclaim memory
+ /** Clears the data stored to reclaim memory */
void Clear(void);
/** Creates a new area of the specified size and contents.
@@ -53,31 +63,43 @@ public:
*/
void Create(int a_SizeX, int a_SizeY, int a_SizeZ, int a_DataTypes = baTypes | baMetas);
- /// Resets the origin. No other changes are made, contents are untouched.
+ /** Resets the origin. No other changes are made, contents are untouched. */
void SetOrigin(int a_OriginX, int a_OriginY, int a_OriginZ);
- /// Reads an area of blocks specified. Returns true if successful. All coords are inclusive.
+ /** Resets the origin. No other changes are made, contents are untouched. */
+ void SetOrigin(const Vector3i & a_Origin);
+
+ /** Reads an area of blocks specified. Returns true if successful. All coords are inclusive. */
bool Read(cForEachChunkProvider * a_ForEachChunkProvider, int a_MinBlockX, int a_MaxBlockX, int a_MinBlockY, int a_MaxBlockY, int a_MinBlockZ, int a_MaxBlockZ, int a_DataTypes = baTypes | baMetas);
+ /** Reads an area of blocks specified. Returns true if successful. The bounds are included in the read area. */
+ bool Read(cForEachChunkProvider * a_ForEachChunkProvider, const cCuboid & a_Bounds, int a_DataTypes = baTypes | baMetas);
+
+ /** Reads an area of blocks specified. Returns true if successful. The bounds are included in the read area. */
+ bool Read(cForEachChunkProvider * a_ForEachChunkProvider, const Vector3i & a_Point1, const Vector3i & a_Point2, int a_DataTypes = baTypes | baMetas);
+
// TODO: Write() is not too good an interface: if it fails, there's no way to repeat only for the parts that didn't write
// A better way may be to return a list of cBlockAreas for each part that didn't succeed writing, so that the caller may try again
- /// Writes the area back into cWorld at the coords specified. Returns true if successful in all chunks, false if only partially / not at all
+ /** Writes the area back into cWorld at the coords specified. Returns true if successful in all chunks, false if only partially / not at all */
bool Write(cForEachChunkProvider * a_ForEachChunkProvider, int a_MinBlockX, int a_MinBlockY, int a_MinBlockZ, int a_DataTypes = baTypes | baMetas);
- /// Copies this object's contents into the specified BlockArea.
+ /** Writes the area back into cWorld at the coords specified. Returns true if successful in all chunks, false if only partially / not at all */
+ bool Write(cForEachChunkProvider * a_ForEachChunkProvider, const Vector3i & a_MinCoords, int a_DataTypes = baTypes | baMetas);
+
+ /** Copies this object's contents into the specified BlockArea. */
void CopyTo(cBlockArea & a_Into) const;
- /// Copies the contents from the specified BlockArea into this object.
+ /** Copies the contents from the specified BlockArea into this object. */
void CopyFrom(const cBlockArea & a_From);
- /// For testing purposes only, dumps the area into a file.
+ /** For testing purposes only, dumps the area into a file. */
void DumpToRawFile(const AString & a_FileName);
- /// Crops the internal contents by the specified amount of blocks from each border.
+ /** Crops the internal contents by the specified amount of blocks from each border. */
void Crop(int a_AddMinX, int a_SubMaxX, int a_AddMinY, int a_SubMaxY, int a_AddMinZ, int a_SubMaxZ);
- /// Expands the internal contents by the specified amount of blocks from each border
+ /** Expands the internal contents by the specified amount of blocks from each border */
void Expand(int a_SubMinX, int a_AddMaxX, int a_SubMinY, int a_AddMaxY, int a_SubMinZ, int a_AddMaxZ);
/** Merges another block area into this one, using the specified block combinating strategy
@@ -117,49 +139,65 @@ public:
*/
void Merge(const cBlockArea & a_Src, int a_RelX, int a_RelY, int a_RelZ, eMergeStrategy a_Strategy);
- /// Fills the entire block area with the specified data
+ /** Merges another block area into this one, using the specified block combinating strategy.
+ See Merge() above for details. */
+ void Merge(const cBlockArea & a_Src, const Vector3i & a_RelMinCoords, eMergeStrategy a_Strategy);
+
+ /** Fills the entire block area with the specified data */
void Fill(int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta = 0, NIBBLETYPE a_BlockLight = 0, NIBBLETYPE a_BlockSkyLight = 0x0f);
- /// Fills a cuboid inside the block area with the specified data
+ /** Fills a cuboid inside the block area with the specified data */
void FillRelCuboid(int a_MinRelX, int a_MaxRelX, int a_MinRelY, int a_MaxRelY, int a_MinRelZ, int a_MaxRelZ,
int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta = 0,
NIBBLETYPE a_BlockLight = 0, NIBBLETYPE a_BlockSkyLight = 0x0f
);
- /// Draws a line from between two points with the specified data
+ /** Fills a cuboid inside the block area with the specified data. a_Cuboid must be sorted. */
+ void FillRelCuboid(const cCuboid & a_RelCuboid,
+ int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta = 0,
+ NIBBLETYPE a_BlockLight = 0, NIBBLETYPE a_BlockSkyLight = 0x0f
+ );
+
+ /** Draws a line from between two points with the specified data */
void RelLine(int a_RelX1, int a_RelY1, int a_RelZ1, int a_RelX2, int a_RelY2, int a_RelZ2,
int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta = 0,
NIBBLETYPE a_BlockLight = 0, NIBBLETYPE a_BlockSkyLight = 0x0f
);
- /// Rotates the entire area counter-clockwise around the Y axis
+ /** Draws a line from between two points with the specified data */
+ void RelLine(const Vector3i & a_Point1, const Vector3i & a_Point2,
+ int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta = 0,
+ NIBBLETYPE a_BlockLight = 0, NIBBLETYPE a_BlockSkyLight = 0x0f
+ );
+
+ /** Rotates the entire area counter-clockwise around the Y axis */
void RotateCCW(void);
- /// Rotates the entire area clockwise around the Y axis
+ /** Rotates the entire area clockwise around the Y axis */
void RotateCW(void);
- /// Mirrors the entire area around the XY plane
+ /** Mirrors the entire area around the XY plane */
void MirrorXY(void);
- /// Mirrors the entire area around the XZ plane
+ /** Mirrors the entire area around the XZ plane */
void MirrorXZ(void);
- /// Mirrors the entire area around the YZ plane
+ /** Mirrors the entire area around the YZ plane */
void MirrorYZ(void);
- /// Rotates the entire area counter-clockwise around the Y axis, doesn't use blockhandlers for block meta
+ /** Rotates the entire area counter-clockwise around the Y axis, doesn't use blockhandlers for block meta */
void RotateCCWNoMeta(void);
- /// Rotates the entire area clockwise around the Y axis, doesn't use blockhandlers for block meta
+ /** Rotates the entire area clockwise around the Y axis, doesn't use blockhandlers for block meta */
void RotateCWNoMeta(void);
- /// Mirrors the entire area around the XY plane, doesn't use blockhandlers for block meta
+ /** Mirrors the entire area around the XY plane, doesn't use blockhandlers for block meta */
void MirrorXYNoMeta(void);
- /// Mirrors the entire area around the XZ plane, doesn't use blockhandlers for block meta
+ /** Mirrors the entire area around the XZ plane, doesn't use blockhandlers for block meta */
void MirrorXZNoMeta(void);
- /// Mirrors the entire area around the YZ plane, doesn't use blockhandlers for block meta
+ /** Mirrors the entire area around the YZ plane, doesn't use blockhandlers for block meta */
void MirrorYZNoMeta(void);
// Setters:
@@ -197,11 +235,14 @@ public:
int GetSizeY(void) const { return m_SizeY; }
int GetSizeZ(void) const { return m_SizeZ; }
+ /** Returns the volume of the area, as number of blocks */
+ int GetVolume(void) const { return m_SizeX * m_SizeY * m_SizeZ; }
+
int GetOriginX(void) const { return m_OriginX; }
int GetOriginY(void) const { return m_OriginY; }
int GetOriginZ(void) const { return m_OriginZ; }
- /// Returns the datatypes that are stored in the object (bitmask of baXXX values)
+ /** Returns the datatypes that are stored in the object (bitmask of baXXX values) */
int GetDataTypes(void) const;
bool HasBlockTypes (void) const { return (m_BlockTypes != NULL); }
@@ -212,7 +253,7 @@ public:
// tolua_end
// Clients can use these for faster access to all blocktypes. Be careful though!
- /// Returns the internal pointer to the block types
+ /** Returns the internal pointer to the block types */
BLOCKTYPE * GetBlockTypes (void) const { return m_BlockTypes; }
NIBBLETYPE * GetBlockMetas (void) const { return m_BlockMetas; } // NOTE: one byte per block!
NIBBLETYPE * GetBlockLight (void) const { return m_BlockLight; } // NOTE: one byte per block!
@@ -263,7 +304,7 @@ protected:
NIBBLETYPE * m_BlockLight; // Each light value is stored as a separate byte for faster access
NIBBLETYPE * m_BlockSkyLight; // Each light value is stored as a separate byte for faster access
- /// Clears the data stored and prepares a fresh new block area with the specified dimensions
+ /** Clears the data stored and prepares a fresh new block area with the specified dimensions */
bool SetSize(int a_SizeX, int a_SizeY, int a_SizeZ, int a_DataTypes);
// Basic Setters:
@@ -282,7 +323,7 @@ protected:
void ExpandBlockTypes(int a_SubMinX, int a_AddMaxX, int a_SubMinY, int a_AddMaxY, int a_SubMinZ, int a_AddMaxZ);
void ExpandNibbles (NIBBLEARRAY & a_Array, int a_SubMinX, int a_AddMaxX, int a_SubMinY, int a_AddMaxY, int a_SubMinZ, int a_AddMaxZ);
- /// Sets the specified datatypes at the specified location.
+ /** Sets the specified datatypes at the specified location. */
void RelSetData(
int a_RelX, int a_RelY, int a_RelZ,
int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta,
diff --git a/src/BlockEntities/CommandBlockEntity.cpp b/src/BlockEntities/CommandBlockEntity.cpp
index 0bc6ca359..d395997a6 100644
--- a/src/BlockEntities/CommandBlockEntity.cpp
+++ b/src/BlockEntities/CommandBlockEntity.cpp
@@ -12,6 +12,7 @@
#include "../CommandOutput.h"
#include "../Root.h"
#include "../Server.h" // ExecuteConsoleCommand()
+#include "../Chunk.h"
@@ -126,6 +127,8 @@ void cCommandBlockEntity::SetRedstonePower(bool a_IsPowered)
bool cCommandBlockEntity::Tick(float a_Dt, cChunk & a_Chunk)
{
+ UNUSED(a_Dt);
+ UNUSED(a_Chunk);
if (!m_ShouldExecute)
{
return false;
diff --git a/src/BlockEntities/DropSpenserEntity.cpp b/src/BlockEntities/DropSpenserEntity.cpp
index 81df0fc8c..0012742fb 100644
--- a/src/BlockEntities/DropSpenserEntity.cpp
+++ b/src/BlockEntities/DropSpenserEntity.cpp
@@ -129,6 +129,7 @@ void cDropSpenserEntity::SetRedstonePower(bool a_IsPowered)
bool cDropSpenserEntity::Tick(float a_Dt, cChunk & a_Chunk)
{
+ UNUSED(a_Dt);
if (!m_ShouldDropSpense)
{
return false;
diff --git a/src/BlockEntities/FurnaceEntity.cpp b/src/BlockEntities/FurnaceEntity.cpp
index c6643bcff..7d6d1f89e 100644
--- a/src/BlockEntities/FurnaceEntity.cpp
+++ b/src/BlockEntities/FurnaceEntity.cpp
@@ -94,6 +94,8 @@ bool cFurnaceEntity::ContinueCooking(void)
bool cFurnaceEntity::Tick(float a_Dt, cChunk & a_Chunk)
{
+ UNUSED(a_Dt);
+ UNUSED(a_Chunk);
if (m_FuelBurnTime <= 0)
{
// No fuel is burning, reset progressbars and bail out
@@ -110,7 +112,7 @@ bool cFurnaceEntity::Tick(float a_Dt, cChunk & a_Chunk)
if (m_TimeCooked >= m_NeedCookTime)
{
// Finished smelting one item
- FinishOne(a_Chunk);
+ FinishOne();
}
}
@@ -208,7 +210,7 @@ void cFurnaceEntity::BroadcastProgress(int a_ProgressbarID, short a_Value)
/// One item finished cooking
-void cFurnaceEntity::FinishOne(cChunk & a_Chunk)
+void cFurnaceEntity::FinishOne()
{
m_TimeCooked = 0;
diff --git a/src/BlockEntities/FurnaceEntity.h b/src/BlockEntities/FurnaceEntity.h
index 5e08ae37a..4f935a74b 100644
--- a/src/BlockEntities/FurnaceEntity.h
+++ b/src/BlockEntities/FurnaceEntity.h
@@ -126,7 +126,7 @@ protected:
void BroadcastProgress(int a_ProgressbarID, short a_Value);
/// One item finished cooking
- void FinishOne(cChunk & a_Chunk);
+ void FinishOne();
/// Starts burning a new fuel, if possible
void BurnNewFuel(void);
diff --git a/src/BlockEntities/HopperEntity.cpp b/src/BlockEntities/HopperEntity.cpp
index 31b23ac99..af7043767 100644
--- a/src/BlockEntities/HopperEntity.cpp
+++ b/src/BlockEntities/HopperEntity.cpp
@@ -13,6 +13,7 @@
#include "DropSpenserEntity.h"
#include "FurnaceEntity.h"
#include "../BoundingBox.h"
+#include "json/json.h"
@@ -58,6 +59,7 @@ bool cHopperEntity::GetOutputBlockPos(NIBBLETYPE a_BlockMeta, int & a_OutputX, i
bool cHopperEntity::Tick(float a_Dt, cChunk & a_Chunk)
{
+ UNUSED(a_Dt);
Int64 CurrentTick = a_Chunk.GetWorld()->GetWorldAge();
bool res = false;
@@ -73,6 +75,7 @@ bool cHopperEntity::Tick(float a_Dt, cChunk & a_Chunk)
void cHopperEntity::SaveToJson(Json::Value & a_Value)
{
+ UNUSED(a_Value);
// TODO
LOGWARNING("%s: Not implemented yet", __FUNCTION__);
}
diff --git a/src/BlockEntities/NoteEntity.cpp b/src/BlockEntities/NoteEntity.cpp
index 9a33ead62..58b05a324 100644
--- a/src/BlockEntities/NoteEntity.cpp
+++ b/src/BlockEntities/NoteEntity.cpp
@@ -21,6 +21,7 @@ cNoteEntity::cNoteEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_Wo
void cNoteEntity::UsedBy(cPlayer * a_Player)
{
+ UNUSED(a_Player);
IncrementPitch();
MakeSound();
}
diff --git a/src/Blocks/BlockBed.cpp b/src/Blocks/BlockBed.cpp
index a6f3c36b6..3dad4feba 100644
--- a/src/Blocks/BlockBed.cpp
+++ b/src/Blocks/BlockBed.cpp
@@ -63,20 +63,29 @@ void cBlockBedHandler::OnUse(cChunkInterface & a_ChunkInterface, cWorldInterface
if (a_WorldInterface.GetTimeOfDay() > 13000)
{
NIBBLETYPE Meta = a_ChunkInterface.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ);
- if (Meta & 0x8)
+ if (Meta & 0x4)
{
- // Is pillow
- a_WorldInterface.GetBroadcastManager().BroadcastUseBed(*a_Player, a_BlockX, a_BlockY, a_BlockZ);
+ a_Player->SendMessageFailure("This bed is occupied.");
}
else
{
- // Is foot end
- Vector3i Direction = MetaDataToDirection( Meta & 0x7 );
- if (a_ChunkInterface.GetBlock(a_BlockX + Direction.x, a_BlockY, a_BlockZ + Direction.z) == E_BLOCK_BED) // Must always use pillow location for sleeping
+ if (Meta & 0x8)
{
- a_WorldInterface.GetBroadcastManager().BroadcastUseBed(*a_Player, a_BlockX + Direction.x, a_BlockY, a_BlockZ + Direction.z);
+ // Is pillow
+ a_WorldInterface.GetBroadcastManager().BroadcastUseBed(*a_Player, a_BlockX, a_BlockY, a_BlockZ);
}
+ else
+ {
+ // Is foot end
+ Vector3i Direction = MetaDataToDirection( Meta & 0x7 );
+ if (a_ChunkInterface.GetBlock(a_BlockX + Direction.x, a_BlockY, a_BlockZ + Direction.z) == E_BLOCK_BED) // Must always use pillow location for sleeping
+ {
+ a_WorldInterface.GetBroadcastManager().BroadcastUseBed(*a_Player, a_BlockX + Direction.x, a_BlockY, a_BlockZ + Direction.z);
+ }
+ }
+ a_ChunkInterface.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, (Meta | (1 << 2)));
}
+
} else {
a_Player->SendMessageFailure("You can only sleep at night");
}
@@ -86,3 +95,5 @@ void cBlockBedHandler::OnUse(cChunkInterface & a_ChunkInterface, cWorldInterface
+
+
diff --git a/src/Chunk.cpp b/src/Chunk.cpp
index 4f301c209..8dfbbeef5 100644
--- a/src/Chunk.cpp
+++ b/src/Chunk.cpp
@@ -562,13 +562,6 @@ void cChunk::Tick(float a_Dt)
{
BroadcastPendingBlockChanges();
- // Unload the chunk from all clients that have queued unloading:
- for (cClientHandleList::iterator itr = m_UnloadQuery.begin(), end = m_UnloadQuery.end(); itr != end; ++itr)
- {
- (*itr)->SendUnloadChunk(m_PosX, m_PosZ);
- }
- m_UnloadQuery.clear();
-
// Set all blocks that have been queued for setting later:
ProcessQueuedSetBlocks();
diff --git a/src/Chunk.h b/src/Chunk.h
index 1b7a6fa07..c9e9697ca 100644
--- a/src/Chunk.h
+++ b/src/Chunk.h
@@ -405,7 +405,6 @@ private:
// A critical section is not needed, because all chunk access is protected by its parent ChunkMap's csLayers
cClientHandleList m_LoadedByClient;
- cClientHandleList m_UnloadQuery;
cEntityList m_Entities;
cBlockEntityList m_BlockEntities;
diff --git a/src/ChunkDef.h b/src/ChunkDef.h
index f48dc4fd5..7be2fa2df 100644
--- a/src/ChunkDef.h
+++ b/src/ChunkDef.h
@@ -92,6 +92,7 @@ public:
/// Converts absolute block coords into relative (chunk + block) coords:
inline static void AbsoluteToRelative(/* in-out */ int & a_X, int & a_Y, int & a_Z, /* out */ int & a_ChunkX, int & a_ChunkZ )
{
+ UNUSED(a_Y);
BlockToChunk(a_X, a_Z, a_ChunkX, a_ChunkZ);
a_X = a_X - a_ChunkX * Width;
diff --git a/src/ChunkMap.cpp b/src/ChunkMap.cpp
index fbb8706e0..b5795fbaf 100644
--- a/src/ChunkMap.cpp
+++ b/src/ChunkMap.cpp
@@ -805,6 +805,10 @@ void cChunkMap::WakeUpSimulators(int a_BlockX, int a_BlockY, int a_BlockZ)
/// Wakes up the simulators for the specified area of blocks
void cChunkMap::WakeUpSimulatorsInArea(int a_MinBlockX, int a_MaxBlockX, int a_MinBlockY, int a_MaxBlockY, int a_MinBlockZ, int a_MaxBlockZ)
{
+ // Limit the Y coords:
+ a_MinBlockY = std::max(a_MinBlockY, 0);
+ a_MaxBlockY = std::min(a_MaxBlockY, cChunkDef::Height - 1);
+
cSimulatorManager * SimMgr = m_World->GetSimulatorManager();
int MinChunkX, MinChunkZ, MaxChunkX, MaxChunkZ;
cChunkDef::BlockToChunk(a_MinBlockX, a_MinBlockZ, MinChunkX, MinChunkZ);
diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp
index 84286fc41..b08ceb5f6 100644
--- a/src/ClientHandle.cpp
+++ b/src/ClientHandle.cpp
@@ -32,6 +32,7 @@
#include "Protocol/ProtocolRecognizer.h"
#include "CompositeChat.h"
+#include "Items/ItemSword.h"
@@ -542,19 +543,23 @@ void cClientHandle::HandlePlayerPos(double a_PosX, double a_PosY, double a_PosZ,
void cClientHandle::HandlePluginMessage(const AString & a_Channel, const AString & a_Message)
{
- if (a_Channel == "MC|AdvCdm") // Command block, set text, Client -> Server
+ if (a_Channel == "MC|AdvCdm")
{
- const char* Data = a_Message.c_str();
- HandleCommandBlockMessage(Data, a_Message.size());
- return;
+ // Command block, set text, Client -> Server
+ HandleCommandBlockMessage(a_Message.c_str(), a_Message.size());
}
- else if (a_Channel == "MC|Brand") // Client <-> Server branding exchange
+ else if (a_Channel == "MC|Brand")
{
- // We are custom,
- // We are awesome,
- // We are MCServer.
+ // Client <-> Server branding exchange
SendPluginMessage("MC|Brand", "MCServer");
- return;
+ }
+ else if (a_Channel == "REGISTER")
+ {
+ RegisterPluginChannels(BreakApartPluginChannels(a_Message));
+ }
+ else if (a_Channel == "UNREGISTER")
+ {
+ UnregisterPluginChannels(BreakApartPluginChannels(a_Message));
}
cPluginManager::Get()->CallHookPluginMessage(*this, a_Channel, a_Message);
@@ -564,7 +569,61 @@ void cClientHandle::HandlePluginMessage(const AString & a_Channel, const AString
-void cClientHandle::HandleCommandBlockMessage(const char* a_Data, unsigned int a_Length)
+AStringVector cClientHandle::BreakApartPluginChannels(const AString & a_PluginChannels)
+{
+ // Break the string on each NUL character.
+ // Note that StringSplit() doesn't work on this because NUL is a special char - string terminator
+ size_t len = a_PluginChannels.size();
+ size_t first = 0;
+ AStringVector res;
+ for (size_t i = 0; i < len; i++)
+ {
+ if (a_PluginChannels[i] != 0)
+ {
+ continue;
+ }
+ if (i > first)
+ {
+ res.push_back(a_PluginChannels.substr(first, i - first));
+ }
+ first = i + 1;
+ } // for i - a_PluginChannels[]
+ if (first < len)
+ {
+ res.push_back(a_PluginChannels.substr(first, len - first));
+ }
+ return res;
+}
+
+
+
+
+
+void cClientHandle::RegisterPluginChannels(const AStringVector & a_ChannelList)
+{
+ for (AStringVector::const_iterator itr = a_ChannelList.begin(), end = a_ChannelList.end(); itr != end; ++itr)
+ {
+ m_PluginChannels.insert(*itr);
+ } // for itr - a_ChannelList[]
+}
+
+
+
+
+
+void cClientHandle::UnregisterPluginChannels(const AStringVector & a_ChannelList)
+{
+ for (AStringVector::const_iterator itr = a_ChannelList.begin(), end = a_ChannelList.end(); itr != end; ++itr)
+ {
+ m_PluginChannels.erase(*itr);
+ } // for itr - a_ChannelList[]
+}
+
+
+
+
+
+void cClientHandle::HandleCommandBlockMessage(const char * a_Data, unsigned int a_Length)
{
if (a_Length < 14)
{
@@ -736,6 +795,15 @@ void cClientHandle::HandleBlockDigStarted(int a_BlockX, int a_BlockY, int a_Bloc
return;
}
+ if (
+ m_Player->IsGameModeCreative() &&
+ ItemCategory::IsSword(m_Player->GetInventory().GetEquippedItem().m_ItemType)
+ )
+ {
+ // Players can't destroy blocks with a Sword in the hand.
+ return;
+ }
+
if (cRoot::Get()->GetPluginManager()->CallHookPlayerBreakingBlock(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_OldBlock, a_OldMeta))
{
// A plugin doesn't agree with the breaking. Bail out. Send the block back to the client, so that it knows:
@@ -2074,6 +2142,33 @@ void cClientHandle::SendInventorySlot(char a_WindowID, short a_SlotNum, const cI
+void cClientHandle::SendMapColumn(int a_ID, int a_X, int a_Y, const Byte * a_Colors, unsigned int a_Length)
+{
+ m_Protocol->SendMapColumn(a_ID, a_X, a_Y, a_Colors, a_Length);
+}
+
+
+
+
+
+void cClientHandle::SendMapDecorators(int a_ID, const cMapDecoratorList & a_Decorators)
+{
+ m_Protocol->SendMapDecorators(a_ID, a_Decorators);
+}
+
+
+
+
+
+void cClientHandle::SendMapInfo(int a_ID, unsigned int a_Scale)
+{
+ m_Protocol->SendMapInfo(a_ID, a_Scale);
+}
+
+
+
+
+
void cClientHandle::SendParticleEffect(const AString & a_ParticleName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmmount)
{
m_Protocol->SendParticleEffect(a_ParticleName, a_SrcX, a_SrcY, a_SrcZ, a_OffsetX, a_OffsetY, a_OffsetZ, a_ParticleData, a_ParticleAmmount);
@@ -2463,6 +2558,15 @@ void cClientHandle::SetViewDistance(int a_ViewDistance)
+bool cClientHandle::HasPluginChannel(const AString & a_PluginChannel)
+{
+ return (m_PluginChannels.find(a_PluginChannel) != m_PluginChannels.end());
+}
+
+
+
+
+
bool cClientHandle::WantsSendChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ)
{
if (m_State >= csDestroying)
diff --git a/src/ClientHandle.h b/src/ClientHandle.h
index aefca7233..194533402 100644
--- a/src/ClientHandle.h
+++ b/src/ClientHandle.h
@@ -17,6 +17,7 @@
#include "ChunkDef.h"
#include "ByteBuffer.h"
#include "Scoreboard.h"
+#include "Map.h"
@@ -72,10 +73,10 @@ public:
inline bool IsLoggedIn(void) const { return (m_State >= csAuthenticating); }
- /// Called while the client is being ticked from the world via its cPlayer object
+ /** Called while the client is being ticked from the world via its cPlayer object */
void Tick(float a_Dt);
- /// Called while the client is being ticked from the cServer object
+ /** Called while the client is being ticked from the cServer object */
void ServerTick(float a_Dt);
void Destroy(void);
@@ -112,6 +113,9 @@ public:
void SendGameMode (eGameMode a_GameMode);
void SendHealth (void);
void SendInventorySlot (char a_WindowID, short a_SlotNum, const cItem & a_Item);
+ void SendMapColumn (int a_ID, int a_X, int a_Y, const Byte * a_Colors, unsigned int a_Length);
+ void SendMapDecorators (int a_ID, const cMapDecoratorList & a_Decorators);
+ void SendMapInfo (int a_ID, unsigned int a_Scale);
void SendPaintingSpawn (const cPainting & a_Painting);
void SendPickupSpawn (const cPickup & a_Pickup);
void SendEntityAnimation (const cEntity & a_Entity, char a_Animation);
@@ -150,23 +154,28 @@ public:
void SendWindowOpen (const cWindow & a_Window);
void SendWindowProperty (const cWindow & a_Window, int a_Property, int a_Value);
- const AString & GetUsername(void) const; // tolua_export
- void SetUsername( const AString & a_Username ); // tolua_export
+ // tolua_begin
+ const AString & GetUsername(void) const;
+ void SetUsername( const AString & a_Username );
- inline short GetPing(void) const { return m_Ping; } // tolua_export
+ inline short GetPing(void) const { return m_Ping; }
- void SetViewDistance(int a_ViewDistance); // tolua_export
- int GetViewDistance(void) const { return m_ViewDistance; } // tolua_export
+ void SetViewDistance(int a_ViewDistance);
+ int GetViewDistance(void) const { return m_ViewDistance; }
- void SetLocale(AString & a_Locale) { m_Locale = a_Locale; } // tolua_export
- AString GetLocale(void) const { return m_Locale; } // tolua_export
+ void SetLocale(AString & a_Locale) { m_Locale = a_Locale; }
+ AString GetLocale(void) const { return m_Locale; }
- int GetUniqueID() const { return m_UniqueID; } // tolua_export
+ int GetUniqueID(void) const { return m_UniqueID; }
- /// Returns true if the client wants the chunk specified to be sent (in m_ChunksToSend)
+ bool HasPluginChannel(const AString & a_PluginChannel);
+
+ // tolua_end
+
+ /** Returns true if the client wants the chunk specified to be sent (in m_ChunksToSend) */
bool WantsSendChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ);
- /// Adds the chunk specified to the list of chunks wanted for sending (m_ChunksToSend)
+ /** Adds the chunk specified to the list of chunks wanted for sending (m_ChunksToSend) */
void AddWantedChunk(int a_ChunkX, int a_ChunkZ);
// Calls that cProtocol descendants use to report state:
@@ -217,14 +226,17 @@ public:
void SendData(const char * a_Data, int a_Size);
- /// Called when the player moves into a different world; queues sreaming the new chunks
+ /** Called when the player moves into a different world; queues sreaming the new chunks */
void MoveToWorld(cWorld & a_World, bool a_SendRespawnPacket);
- /// Handles the block placing packet when it is a real block placement (not block-using, item-using or eating)
+ /** Handles the block placing packet when it is a real block placement (not block-using, item-using or eating) */
void HandlePlaceBlock(int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, cItemHandler & a_ItemHandler);
private:
+ /** The type used for storing the names of registered plugin channels. */
+ typedef std::set<AString> cChannels;
+
int m_ViewDistance; // Number of chunks the player can see in each direction; 4 is the minimum ( http://wiki.vg/Protocol_FAQ#.E2.80.A6all_connecting_clients_spasm_and_jerk_uncontrollably.21 )
static const int GENERATEDISTANCE = 2; // Server generates this many chunks AHEAD of player sight. 2 is the minimum, since foliage is generated 1 step behind chunk terrain generation
@@ -257,7 +269,7 @@ private:
int m_LastStreamedChunkX;
int m_LastStreamedChunkZ;
- /// Seconds since the last packet data was received (updated in Tick(), reset in DataReceived())
+ /** Seconds since the last packet data was received (updated in Tick(), reset in DataReceived()) */
float m_TimeSinceLastPacket;
short m_Ping;
@@ -279,7 +291,7 @@ private:
int m_LastDigBlockY;
int m_LastDigBlockZ;
- /// Used while csDestroyedWaiting for counting the ticks until the connection is closed
+ /** Used while csDestroyedWaiting for counting the ticks until the connection is closed */
int m_TicksSinceDestruction;
enum eState
@@ -299,10 +311,10 @@ private:
eState m_State;
- /// m_State needs to be locked in the Destroy() function so that the destruction code doesn't run twice on two different threads
+ /** m_State needs to be locked in the Destroy() function so that the destruction code doesn't run twice on two different threads */
cCriticalSection m_CSDestroyingState;
- /// If set to true during csDownloadingWorld, the tick thread calls CheckIfWorldDownloaded()
+ /** If set to true during csDownloadingWorld, the tick thread calls CheckIfWorldDownloaded() */
bool m_ShouldCheckDownloaded;
/** Number of explosions sent this tick */
@@ -311,27 +323,39 @@ private:
static int s_ClientCount;
int m_UniqueID;
- /// Set to true when the chunk where the player is is sent to the client. Used for spawning the player
+ /** Set to true when the chunk where the player is is sent to the client. Used for spawning the player */
bool m_HasSentPlayerChunk;
- /// Client Settings
+ /** Client Settings */
AString m_Locale;
+
+ /** The plugin channels that the client has registered. */
+ cChannels m_PluginChannels;
- /// Returns true if the rate block interactions is within a reasonable limit (bot protection)
+ /** Returns true if the rate block interactions is within a reasonable limit (bot protection) */
bool CheckBlockInteractionsRate(void);
- /// Adds a single chunk to be streamed to the client; used by StreamChunks()
+ /** Adds a single chunk to be streamed to the client; used by StreamChunks() */
void StreamChunk(int a_ChunkX, int a_ChunkZ);
- /// Handles the DIG_STARTED dig packet:
+ /** Handles the DIG_STARTED dig packet: */
void HandleBlockDigStarted (int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, BLOCKTYPE a_OldBlock, NIBBLETYPE a_OldMeta);
- /// Handles the DIG_FINISHED dig packet:
+ /** Handles the DIG_FINISHED dig packet: */
void HandleBlockDigFinished(int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, BLOCKTYPE a_OldBlock, NIBBLETYPE a_OldMeta);
- /// Handles the "MC|AdvCdm" plugin message
- void HandleCommandBlockMessage(const char* a_Data, unsigned int a_Length);
+ /** Converts the protocol-formatted channel list (NUL-separated) into a proper string vector. */
+ AStringVector BreakApartPluginChannels(const AString & a_PluginChannels);
+
+ /** Adds all of the channels to the list of current plugin channels. Handles duplicates gracefully. */
+ void RegisterPluginChannels(const AStringVector & a_ChannelList);
+
+ /** Removes all of the channels from the list of current plugin channels. Ignores channels that are not found. */
+ void UnregisterPluginChannels(const AStringVector & a_ChannelList);
+
+ /** Handles the "MC|AdvCdm" plugin message */
+ void HandleCommandBlockMessage(const char * a_Data, unsigned int a_Length);
// cSocketThreads::cCallback overrides:
virtual void DataReceived (const char * a_Data, int a_Size) override; // Data is received from the client
diff --git a/src/Cuboid.cpp b/src/Cuboid.cpp
index ea6f7c453..782837b23 100644
--- a/src/Cuboid.cpp
+++ b/src/Cuboid.cpp
@@ -58,6 +58,18 @@ void cCuboid::Sort(void)
+int cCuboid::GetVolume(void) const
+{
+ int DifX = abs(p2.x - p1.x) + 1;
+ int DifY = abs(p2.y - p1.y) + 1;
+ int DifZ = abs(p2.z - p1.z) + 1;
+ return DifX * DifY * DifZ;
+}
+
+
+
+
+
bool cCuboid::DoesIntersect(const cCuboid & a_Other) const
{
// In order for cuboids to intersect, each of their coord intervals need to intersect
@@ -103,6 +115,76 @@ void cCuboid::Move(int a_OfsX, int a_OfsY, int a_OfsZ)
+void cCuboid::Expand(int a_SubMinX, int a_AddMaxX, int a_SubMinY, int a_AddMaxY, int a_SubMinZ, int a_AddMaxZ)
+{
+ if (p1.x < p2.x)
+ {
+ p1.x -= a_SubMinX;
+ p2.x += a_AddMaxX;
+ }
+ else
+ {
+ p2.x -= a_SubMinX;
+ p1.x += a_AddMaxX;
+ }
+
+ if (p1.y < p2.y)
+ {
+ p1.y -= a_SubMinY;
+ p2.y += a_AddMaxY;
+ }
+ else
+ {
+ p2.y -= a_SubMinY;
+ p1.y += a_AddMaxY;
+ }
+
+ if (p1.z < p2.z)
+ {
+ p1.z -= a_SubMinZ;
+ p2.z += a_AddMaxZ;
+ }
+ else
+ {
+ p2.z -= a_SubMinZ;
+ p1.z += a_AddMaxZ;
+ }
+}
+
+
+
+
+
+void cCuboid::ClampX(int a_MinX, int a_MaxX)
+{
+ p1.x = Clamp(p1.x, a_MinX, a_MaxX);
+ p2.x = Clamp(p2.x, a_MinX, a_MaxX);
+}
+
+
+
+
+
+void cCuboid::ClampY(int a_MinY, int a_MaxY)
+{
+ p1.y = Clamp(p1.y, a_MinY, a_MaxY);
+ p2.y = Clamp(p2.y, a_MinY, a_MaxY);
+}
+
+
+
+
+
+void cCuboid::ClampZ(int a_MinZ, int a_MaxZ)
+{
+ p1.z = Clamp(p1.z, a_MinZ, a_MaxZ);
+ p2.z = Clamp(p2.z, a_MinZ, a_MaxZ);
+}
+
+
+
+
+
bool cCuboid::IsSorted(void) const
{
return (
diff --git a/src/Cuboid.h b/src/Cuboid.h
index 44db7b98e..51ccf799b 100644
--- a/src/Cuboid.h
+++ b/src/Cuboid.h
@@ -29,7 +29,12 @@ public:
int DifY(void) const { return p2.y - p1.y; }
int DifZ(void) const { return p2.z - p1.z; }
- /// Returns true if the cuboids have at least one voxel in common. Both coords are considered inclusive.
+ /** Returns the volume of the cuboid, in blocks.
+ Note that the volume considers both coords inclusive.
+ Works on unsorted cuboids, too. */
+ int GetVolume(void) const;
+
+ /** Returns true if the cuboids have at least one voxel in common. Both coords are considered inclusive. */
bool DoesIntersect(const cCuboid & a_Other) const;
bool IsInside(const Vector3i & v) const
@@ -59,13 +64,27 @@ public:
);
}
- /// Returns true if this cuboid is completely inside the specifie cuboid (in all 6 coords)
+ /** Returns true if this cuboid is completely inside the specifie cuboid (in all 6 coords) */
bool IsCompletelyInside(const cCuboid & a_Outer) const;
- /// Moves the cuboid by the specified offsets in each direction
+ /** Moves the cuboid by the specified offsets in each direction */
void Move(int a_OfsX, int a_OfsY, int a_OfsZ);
+
+ /** Expands the cuboid by the specified amount in each direction.
+ Works on unsorted cuboids as well.
+ Note that this function doesn't check for underflows. */
+ void Expand(int a_SubMinX, int a_AddMaxX, int a_SubMinY, int a_AddMaxY, int a_SubMinZ, int a_AddMaxZ);
+
+ /** Clamps both X coords to the specified range. Works on unsorted cuboids, too. */
+ void ClampX(int a_MinX, int a_MaxX);
+
+ /** Clamps both Y coords to the specified range. Works on unsorted cuboids, too. */
+ void ClampY(int a_MinY, int a_MaxY);
+
+ /** Clamps both Z coords to the specified range. Works on unsorted cuboids, too. */
+ void ClampZ(int a_MinZ, int a_MaxZ);
- /// Returns true if the coords are properly sorted (lesser in p1, greater in p2)
+ /** Returns true if the coords are properly sorted (lesser in p1, greater in p2) */
bool IsSorted(void) const;
} ;
// tolua_end
diff --git a/src/Enchantments.h b/src/Enchantments.h
index e984df92e..f77b535d8 100644
--- a/src/Enchantments.h
+++ b/src/Enchantments.h
@@ -21,7 +21,6 @@ class cParsedNBT;
-
/** Class that stores item enchantments or stored-enchantments
The enchantments may be serialized to a stringspec and read back from such stringspec.
The format for the stringspec is "id=lvl;id=lvl;id=lvl...", with an optional semicolon at the end,
diff --git a/src/Entities/Boat.cpp b/src/Entities/Boat.cpp
index 67df201ce..94b24c5af 100644
--- a/src/Entities/Boat.cpp
+++ b/src/Entities/Boat.cpp
@@ -89,6 +89,7 @@ void cBoat::Tick(float a_Dt, cChunk & a_Chunk)
{
super::Tick(a_Dt, a_Chunk);
BroadcastMovementUpdate();
+
SetSpeed(GetSpeed() * 0.97); // Slowly decrease the speed
if ((POSY_TOINT < 0) || (POSY_TOINT > cChunkDef::Height))
@@ -98,7 +99,10 @@ void cBoat::Tick(float a_Dt, cChunk & a_Chunk)
if (IsBlockWater(m_World->GetBlock(POSX_TOINT, POSY_TOINT, POSZ_TOINT)))
{
- SetSpeedY(1);
+ if (GetSpeedY() < 2)
+ {
+ AddSpeedY(0.2);
+ }
}
}
@@ -108,12 +112,12 @@ void cBoat::Tick(float a_Dt, cChunk & a_Chunk)
void cBoat::HandleSpeedFromAttachee(float a_Forward, float a_Sideways)
{
- if (GetSpeed().Length() > 7)
+ if (GetSpeed().Length() > 7.5)
{
return;
}
- Vector3d ToAddSpeed(m_Attachee->GetLookVector() * (a_Sideways * 1.5));
+ Vector3d ToAddSpeed = m_Attachee->GetLookVector() * (a_Sideways * 0.4) ;
ToAddSpeed.y = 0;
AddSpeed(ToAddSpeed);
diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp
index 19173592e..e0f0b9222 100644
--- a/src/Entities/Player.cpp
+++ b/src/Entities/Player.cpp
@@ -254,6 +254,9 @@ void cPlayer::Tick(float a_Dt, cChunk & a_Chunk)
HandleFloater();
}
+ // Update items (e.g. Maps)
+ m_Inventory.UpdateItems();
+
// Send Player List (Once per m_LastPlayerListTime/1000 ms)
cTimer t1;
if (m_LastPlayerListTime + cPlayer::PLAYER_LIST_TIME_MS <= t1.GetNowTime())
diff --git a/src/Generating/StructGen.cpp b/src/Generating/StructGen.cpp
index 4efcf92f0..47945cc2b 100644
--- a/src/Generating/StructGen.cpp
+++ b/src/Generating/StructGen.cpp
@@ -51,15 +51,6 @@ const int NEST_SIZE_GRAVEL = 32;
-template <typename T> T Clamp(T a_Value, T a_Min, T a_Max)
-{
- return (a_Value < a_Min) ? a_Min : ((a_Value > a_Max) ? a_Max : a_Value);
-}
-
-
-
-
-
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cStructGenTrees:
diff --git a/src/Globals.h b/src/Globals.h
index 1e90d83e9..e4737a98a 100644
--- a/src/Globals.h
+++ b/src/Globals.h
@@ -235,6 +235,16 @@ public:
+/** Clamp X to the specified range. */
+template <typename T>
+T Clamp(T a_Value, T a_Min, T a_Max)
+{
+ return (a_Value < a_Min) ? a_Min : ((a_Value > a_Max) ? a_Max : a_Value);
+}
+
+
+
+
// Common headers (part 2, with macros):
#include "ChunkDef.h"
diff --git a/src/Inventory.cpp b/src/Inventory.cpp
index 0e1cedc85..7f434adfd 100644
--- a/src/Inventory.cpp
+++ b/src/Inventory.cpp
@@ -515,6 +515,31 @@ bool cInventory::AddToBar( cItem & a_Item, const int a_Offset, const int a_Size,
+void cInventory::UpdateItems(void)
+{
+ const cItem & Slot = GetEquippedItem();
+
+ if (Slot.IsEmpty())
+ {
+ return;
+ }
+
+ switch (Slot.m_ItemType)
+ {
+ case E_ITEM_MAP:
+ {
+ ItemHandler(Slot.m_ItemType)->OnUpdate(m_Owner.GetWorld(), &m_Owner, Slot);
+ break;
+ }
+
+ default: break;
+ }
+}
+
+
+
+
+
void cInventory::SaveToJson(Json::Value & a_Value)
{
// The JSON originally included the 4 crafting slots and the result, so we have to put empty items there, too:
diff --git a/src/Inventory.h b/src/Inventory.h
index 3c6a19de8..fd2089a13 100644
--- a/src/Inventory.h
+++ b/src/Inventory.h
@@ -150,6 +150,9 @@ public:
/// Sends the slot contents to the owner
void SendSlot(int a_SlotNum);
+ /// Update items (e.g. Maps)
+ void UpdateItems(void);
+
/// Converts an armor slot number into the ID for the EntityEquipment packet
static int ArmorSlotNumToEntityEquipmentID(short a_ArmorSlotNum);
diff --git a/src/Items/ItemEmptyMap.h b/src/Items/ItemEmptyMap.h
new file mode 100644
index 000000000..f0b1e1424
--- /dev/null
+++ b/src/Items/ItemEmptyMap.h
@@ -0,0 +1,62 @@
+
+// ItemEmptyMap.h
+
+
+
+
+
+#pragma once
+
+#include "../Entities/Entity.h"
+#include "../Item.h"
+
+
+
+
+
+class cItemEmptyMapHandler :
+ public cItemHandler
+{
+ typedef cItemHandler super;
+
+ static const unsigned int DEFAULT_SCALE = 0;
+
+public:
+ cItemEmptyMapHandler() :
+ super(E_ITEM_EMPTY_MAP)
+ {
+ }
+
+ virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_Dir) override
+ {
+ UNUSED(a_Item);
+ UNUSED(a_BlockX);
+ UNUSED(a_BlockZ);
+ UNUSED(a_Dir);
+
+ // The map center is fixed at the central point of the 8x8 block of chunks you are standing in when you right-click it.
+
+ const int RegionWidth = cChunkDef::Width * 8;
+
+ int CenterX = (int)(floor(a_Player->GetPosX() / (float) RegionWidth) * RegionWidth);
+ int CenterZ = (int)(floor(a_Player->GetPosZ() / (float) RegionWidth) * RegionWidth);
+
+ cMap * NewMap = a_World->GetMapManager().CreateMap(CenterX, CenterZ, DEFAULT_SCALE);
+
+ // Remove empty map from inventory
+ if (!a_Player->GetInventory().RemoveOneEquippedItem())
+ {
+ ASSERT(!"Inventory mismatch");
+ return true;
+ }
+
+ if (NewMap == NULL)
+ {
+ return true;
+ }
+
+ a_Player->GetInventory().AddItem(cItem(E_ITEM_MAP, 1, NewMap->GetID()), true, true);
+
+ return true;
+ }
+} ;
diff --git a/src/Items/ItemHandler.cpp b/src/Items/ItemHandler.cpp
index e9bb616a6..c10d13edc 100644
--- a/src/Items/ItemHandler.cpp
+++ b/src/Items/ItemHandler.cpp
@@ -18,6 +18,7 @@
#include "ItemComparator.h"
#include "ItemDoor.h"
#include "ItemDye.h"
+#include "ItemEmptyMap.h"
#include "ItemFishingRod.h"
#include "ItemFlowerPot.h"
#include "ItemFood.h"
@@ -25,6 +26,7 @@
#include "ItemHoe.h"
#include "ItemLeaves.h"
#include "ItemLighter.h"
+#include "ItemMap.h"
#include "ItemMinecart.h"
#include "ItemNetherWart.h"
#include "ItemPainting.h"
@@ -103,11 +105,13 @@ cItemHandler *cItemHandler::CreateItemHandler(int a_ItemType)
case E_ITEM_COMPARATOR: return new cItemComparatorHandler(a_ItemType);
case E_ITEM_DYE: return new cItemDyeHandler(a_ItemType);
case E_ITEM_EGG: return new cItemEggHandler();
+ case E_ITEM_EMPTY_MAP: return new cItemEmptyMapHandler();
case E_ITEM_ENDER_PEARL: return new cItemEnderPearlHandler();
case E_ITEM_FIREWORK_ROCKET: return new cItemFireworkHandler();
case E_ITEM_FISHING_ROD: return new cItemFishingRodHandler(a_ItemType);
case E_ITEM_FLINT_AND_STEEL: return new cItemLighterHandler(a_ItemType);
case E_ITEM_FLOWER_POT: return new cItemFlowerPotHandler(a_ItemType);
+ case E_ITEM_MAP: return new cItemMapHandler();
case E_ITEM_ITEM_FRAME: return new cItemItemFrameHandler(a_ItemType);
case E_ITEM_NETHER_WART: return new cItemNetherWartHandler(a_ItemType);
case E_ITEM_PAINTING: return new cItemPaintingHandler(a_ItemType);
diff --git a/src/Items/ItemHandler.h b/src/Items/ItemHandler.h
index 1a6bb044f..ef3f37a7a 100644
--- a/src/Items/ItemHandler.h
+++ b/src/Items/ItemHandler.h
@@ -32,6 +32,14 @@ public:
UNUSED(a_BlockZ);
UNUSED(a_BlockFace);
}
+
+ /// Called every tick while the item is on the player's inventory (Used by maps) - For now, called only for equipped items
+ virtual void OnUpdate(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item)
+ {
+ UNUSED(a_World);
+ UNUSED(a_Player);
+ UNUSED(a_Item);
+ }
/// Called while the player diggs a block using this item
virtual bool OnDiggingBlock(cWorld * a_World, cPlayer * a_Player, const cItem & a_HeldItem, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace);
diff --git a/src/Items/ItemMap.h b/src/Items/ItemMap.h
new file mode 100644
index 000000000..e8ff9da88
--- /dev/null
+++ b/src/Items/ItemMap.h
@@ -0,0 +1,43 @@
+
+// ItemMap.h
+
+
+
+
+
+#pragma once
+
+#include "../Entities/Entity.h"
+#include "../Item.h"
+
+
+
+
+
+class cItemMapHandler :
+ public cItemHandler
+{
+ typedef cItemHandler super;
+
+ static const unsigned int DEFAULT_RADIUS = 128;
+
+public:
+ cItemMapHandler() :
+ super(E_ITEM_MAP)
+ {
+ }
+
+ virtual void OnUpdate(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item)
+ {
+ cMap * Map = a_World->GetMapManager().GetMapData(a_Item.m_ItemDamage);
+
+ if (Map == NULL)
+ {
+ return;
+ }
+
+ Map->UpdateRadius(*a_Player, DEFAULT_RADIUS);
+
+ Map->UpdateClient(a_Player);
+ }
+} ;
diff --git a/src/LightingThread.h b/src/LightingThread.h
index 81dd9d61f..72d561348 100644
--- a/src/LightingThread.h
+++ b/src/LightingThread.h
@@ -82,7 +82,11 @@ protected:
cLightingChunkStay(cLightingThread & a_LightingThread, int a_ChunkX, int a_ChunkZ, cChunkCoordCallback * a_CallbackAfter);
protected:
- virtual void OnChunkAvailable(int a_ChunkX, int a_ChunkZ) override {}
+ virtual void OnChunkAvailable(int a_ChunkX, int a_ChunkZ) override
+ {
+ UNUSED(a_ChunkX);
+ UNUSED(a_ChunkZ);
+ }
virtual bool OnAllChunksAvailable(void) override;
virtual void OnDisabled(void) override;
} ;
diff --git a/src/Log.cpp b/src/Log.cpp
index 2d6be0f59..1ea327d5d 100644
--- a/src/Log.cpp
+++ b/src/Log.cpp
@@ -42,7 +42,7 @@ cLog::~cLog()
-cLog* cLog::GetInstance()
+cLog * cLog::GetInstance()
{
if (s_Log != NULL)
{
@@ -92,7 +92,7 @@ void cLog::ClearLog()
if( m_File )
fclose (m_File);
#endif
- m_File = 0;
+ m_File = NULL;
}
diff --git a/src/Map.cpp b/src/Map.cpp
new file mode 100644
index 000000000..2d8f57168
--- /dev/null
+++ b/src/Map.cpp
@@ -0,0 +1,631 @@
+
+// Map.cpp
+
+#include "Globals.h"
+
+#include "Map.h"
+
+#include "ClientHandle.h"
+#include "World.h"
+#include "Chunk.h"
+#include "Entities/Player.h"
+#include "FastRandom.h"
+
+
+
+
+
+cMapDecorator::cMapDecorator(cMap * a_Map, eType a_Type, int a_X, int a_Z, int a_Rot)
+ : m_Map(a_Map)
+ , m_Type(a_Type)
+ , m_PixelX(a_X)
+ , m_PixelZ(a_Z)
+ , m_Rot(a_Rot)
+ , m_Player(NULL)
+{
+}
+
+
+
+
+
+cMapDecorator::cMapDecorator(cMap * a_Map, cPlayer * a_Player)
+ : m_Map(a_Map)
+ , m_Type(E_TYPE_PLAYER)
+ , m_Player(a_Player)
+{
+ Update();
+}
+
+
+
+
+
+void cMapDecorator::Update(void)
+{
+ if (m_Player != NULL)
+ {
+ ASSERT(m_Map != NULL);
+ unsigned int PixelWidth = m_Map->GetPixelWidth();
+
+ int InsideWidth = (m_Map->GetWidth() / 2) - 1;
+ int InsideHeight = (m_Map->GetHeight() / 2) - 1;
+
+ int PixelX = (int) (m_Player->GetPosX() - m_Map->GetCenterX()) / PixelWidth;
+ int PixelZ = (int) (m_Player->GetPosZ() - m_Map->GetCenterZ()) / PixelWidth;
+
+ // Center of pixel
+ m_PixelX = (2 * PixelX) + 1;
+ m_PixelZ = (2 * PixelZ) + 1;
+
+ if ((PixelX > -InsideWidth) && (PixelX <= InsideWidth) && (PixelZ > -InsideHeight) && (PixelZ <= InsideHeight))
+ {
+ double Yaw = m_Player->GetYaw();
+
+ if (m_Map->GetDimension() == dimNether)
+ {
+ cFastRandom Random;
+
+ Int64 WorldAge = m_Player->GetWorld()->GetWorldAge();
+
+ // TODO 2014-02-19 xdot: Refine
+ m_Rot = Random.NextInt(16, (int) WorldAge);
+ }
+ else
+ {
+ m_Rot = (int) (Yaw * 16) / 360;
+ }
+
+ m_Type = E_TYPE_PLAYER;
+ }
+ else
+ {
+ if ((abs(PixelX) > 320.0) || (abs(PixelZ) > 320.0))
+ {
+ // TODO 2014-02-18 xdot: Remove decorator
+ }
+
+ m_Rot = 0;
+
+ m_Type = E_TYPE_PLAYER_OUTSIDE;
+
+ // Move to border
+ if (PixelX <= -InsideWidth)
+ {
+ m_PixelX = (2 * -InsideWidth) + 1;
+ }
+ if (PixelZ <= -InsideHeight)
+ {
+ m_PixelZ = (2 * -InsideHeight) + 1;
+ }
+ if (PixelX > InsideWidth)
+ {
+ m_PixelX = (2 * InsideWidth) + 1;
+ }
+ if (PixelZ > InsideHeight)
+ {
+ m_PixelZ = (2 * InsideHeight) + 1;
+ }
+ }
+ }
+}
+
+
+
+
+
+cMap::cMap(unsigned int a_ID, cWorld * a_World)
+ : m_ID(a_ID)
+ , m_Width(cChunkDef::Width * 8)
+ , m_Height(cChunkDef::Width * 8)
+ , m_Scale(3)
+ , m_CenterX(0)
+ , m_CenterZ(0)
+ , m_World(a_World)
+{
+ m_Data.assign(m_Width * m_Height, E_BASE_COLOR_TRANSPARENT);
+
+ Printf(m_Name, "map_%i", m_ID);
+}
+
+
+
+
+
+cMap::cMap(unsigned int a_ID, int a_CenterX, int a_CenterZ, cWorld * a_World, unsigned int a_Scale)
+ : m_ID(a_ID)
+ , m_Width(cChunkDef::Width * 8)
+ , m_Height(cChunkDef::Width * 8)
+ , m_Scale(a_Scale)
+ , m_CenterX(a_CenterX)
+ , m_CenterZ(a_CenterZ)
+ , m_World(a_World)
+{
+ m_Data.assign(m_Width * m_Height, E_BASE_COLOR_TRANSPARENT);
+
+ Printf(m_Name, "map_%i", m_ID);
+}
+
+
+
+
+
+void cMap::UpdateRadius(int a_PixelX, int a_PixelZ, unsigned int a_Radius)
+{
+ int PixelRadius = a_Radius / GetPixelWidth();
+
+ unsigned int StartX = Clamp(a_PixelX - PixelRadius, 0, (int)m_Width);
+ unsigned int StartZ = Clamp(a_PixelZ - PixelRadius, 0, (int)m_Height);
+
+ unsigned int EndX = Clamp(a_PixelX + PixelRadius, 0, (int)m_Width);
+ unsigned int EndZ = Clamp(a_PixelZ + PixelRadius, 0, (int)m_Height);
+
+ for (unsigned int X = StartX; X < EndX; ++X)
+ {
+ for (unsigned int Z = StartZ; Z < EndZ; ++Z)
+ {
+ int dX = X - a_PixelX;
+ int dZ = Z - a_PixelZ;
+
+ if ((dX * dX) + (dZ * dZ) < (PixelRadius * PixelRadius))
+ {
+ UpdatePixel(X, Z);
+ }
+ }
+ }
+}
+
+
+
+
+
+void cMap::UpdateRadius(cPlayer & a_Player, unsigned int a_Radius)
+{
+ unsigned int PixelWidth = GetPixelWidth();
+
+ int PixelX = (int) (a_Player.GetPosX() - m_CenterX) / PixelWidth + (m_Width / 2);
+ int PixelZ = (int) (a_Player.GetPosZ() - m_CenterZ) / PixelWidth + (m_Height / 2);
+
+ UpdateRadius(PixelX, PixelZ, a_Radius);
+}
+
+
+
+
+
+bool cMap::UpdatePixel(unsigned int a_X, unsigned int a_Z)
+{
+ unsigned int PixelWidth = GetPixelWidth();
+
+ int BlockX = m_CenterX + ((a_X - (m_Width / 2)) * PixelWidth);
+ int BlockZ = m_CenterZ + ((a_Z - (m_Height / 2)) * PixelWidth);
+
+ int ChunkX, ChunkY, ChunkZ;
+ m_World->BlockToChunk(BlockX, 0, BlockZ, ChunkX, ChunkY, ChunkZ);
+
+ int RelX = BlockX - (ChunkX * cChunkDef::Width);
+ int RelZ = BlockZ - (ChunkZ * cChunkDef::Width);
+
+ class cCalculatePixelCb :
+ public cChunkCallback
+ {
+ cMap * m_Map;
+
+ int m_RelX, m_RelZ;
+
+ ColorID m_PixelData;
+
+ public:
+ cCalculatePixelCb(cMap * a_Map, int a_RelX, int a_RelZ)
+ : m_Map(a_Map), m_RelX(a_RelX), m_RelZ(a_RelZ), m_PixelData(E_BASE_COLOR_TRANSPARENT) {}
+
+ virtual bool Item(cChunk * a_Chunk) override
+ {
+ if (a_Chunk == NULL)
+ {
+ return false;
+ }
+
+ unsigned int PixelWidth = m_Map->GetPixelWidth();
+
+ if (m_Map->GetDimension() == dimNether)
+ {
+ // TODO 2014-02-22 xdot: Nether maps
+
+ return false;
+ }
+
+ typedef std::map<ColorID, unsigned int> ColorCountMap;
+ ColorCountMap ColorCounts;
+
+ // Count surface blocks
+ for (unsigned int X = m_RelX; X < m_RelX + PixelWidth; ++X)
+ {
+ for (unsigned int Z = m_RelZ; Z < m_RelZ + PixelWidth; ++Z)
+ {
+ unsigned int WaterDepth = 0;
+
+ BLOCKTYPE TargetBlock = E_BLOCK_AIR;
+ NIBBLETYPE TargetMeta = 0;
+
+ int Height = a_Chunk->GetHeight(X, Z);
+
+ while (Height > 0)
+ {
+ a_Chunk->GetBlockTypeMeta(X, Height, Z, TargetBlock, TargetMeta);
+
+ // TODO 2014-02-22 xdot: Check if block color is transparent
+ if (TargetBlock == E_BLOCK_AIR)
+ {
+ --Height;
+ continue;
+ }
+ // TODO 2014-02-22 xdot: Check if block is liquid
+ else if (false)
+ {
+ --Height;
+ ++WaterDepth;
+ continue;
+ }
+
+ break;
+ }
+
+ // TODO 2014-02-22 xdot: Query block color
+ ColorID Color = E_BASE_COLOR_BROWN;
+
+ // Debug - Temporary
+ switch (TargetBlock)
+ {
+ case E_BLOCK_GRASS:
+ {
+ Color = E_BASE_COLOR_LIGHT_GREEN; break;
+ }
+ case E_BLOCK_STATIONARY_WATER:
+ case E_BLOCK_WATER:
+ {
+ Color = E_BASE_COLOR_BLUE; break;
+ }
+ }
+
+ ++ColorCounts[Color];
+ }
+ }
+
+ // Find dominant color
+ ColorID PixelColor = E_BASE_COLOR_TRANSPARENT;
+
+ unsigned int MaxCount = 0;
+
+ for (ColorCountMap::iterator it = ColorCounts.begin(); it != ColorCounts.end(); ++it)
+ {
+ if (it->second > MaxCount)
+ {
+ PixelColor = it->first;
+ MaxCount = it->second;
+ }
+ }
+
+ // TODO 2014-02-22 xdot: Adjust brightness
+ unsigned int dColor = 1;
+
+ m_PixelData = PixelColor + dColor;
+
+ return false;
+ }
+
+ ColorID GetPixelData(void) const
+ {
+ return m_PixelData;
+ }
+ } CalculatePixelCb(this, RelX, RelZ);
+
+ ASSERT(m_World != NULL);
+ m_World->DoWithChunk(ChunkX, ChunkZ, CalculatePixelCb);
+
+ SetPixel(a_X, a_Z, CalculatePixelCb.GetPixelData());
+
+ return true;
+}
+
+
+
+
+
+void cMap::UpdateDecorators(void)
+{
+ for (cMapDecoratorList::iterator it = m_Decorators.begin(); it != m_Decorators.end(); ++it)
+ {
+ it->Update();
+ }
+}
+
+
+
+
+
+void cMap::AddPlayer(cPlayer * a_Player, Int64 a_WorldAge)
+{
+ cClientHandle * Handle = a_Player->GetClientHandle();
+ if (Handle == NULL)
+ {
+ return;
+ }
+
+ cMapClient MapClient;
+
+ MapClient.m_LastUpdate = a_WorldAge;
+ MapClient.m_SendInfo = true;
+ MapClient.m_Handle = Handle;
+ MapClient.m_DataUpdate = 0;
+ MapClient.m_NextDecoratorUpdate = 0;
+
+ m_Clients.push_back(MapClient);
+
+ cMapDecorator PlayerDecorator(this, a_Player);
+
+ m_Decorators.push_back(PlayerDecorator);
+}
+
+
+
+
+
+void cMap::RemoveInactiveClients(Int64 a_WorldAge)
+{
+ for (cMapClientList::iterator it = m_Clients.begin(); it != m_Clients.end();)
+ {
+ if (it->m_LastUpdate < a_WorldAge)
+ {
+ // Remove associated decorators
+ for (cMapDecoratorList::iterator it2 = m_Decorators.begin(); it2 != m_Decorators.end();)
+ {
+ if (it2->GetPlayer()->GetClientHandle() == it->m_Handle)
+ {
+ // Erase decorator
+ cMapDecoratorList::iterator temp = it2;
+ ++it2;
+ m_Decorators.erase(temp);
+ }
+ else
+ {
+ ++it2;
+ }
+ }
+
+ // Erase client
+ cMapClientList::iterator temp = it;
+ ++it;
+ m_Clients.erase(temp);
+ }
+ else
+ {
+ ++it;
+ }
+ }
+}
+
+
+
+
+
+void cMap::StreamNext(cMapClient & a_Client)
+{
+ cClientHandle * Handle = a_Client.m_Handle;
+
+ if (a_Client.m_SendInfo)
+ {
+ Handle->SendMapInfo(m_ID, m_Scale);
+
+ a_Client.m_SendInfo = false;
+
+ return;
+ }
+
+ ++a_Client.m_NextDecoratorUpdate;
+
+ if (a_Client.m_NextDecoratorUpdate >= 4)
+ {
+ // TODO 2014-02-19 xdot
+ // This is dangerous as the player object may have been destroyed before the decorator is erased from the list
+ UpdateDecorators();
+
+ Handle->SendMapDecorators(m_ID, m_Decorators);
+
+ a_Client.m_NextDecoratorUpdate = 0;
+ }
+ else
+ {
+ ++a_Client.m_DataUpdate;
+
+ unsigned int Y = (a_Client.m_DataUpdate * 11) % m_Width;
+
+ const Byte * Colors = &m_Data[Y * m_Height];
+
+ Handle->SendMapColumn(m_ID, Y, 0, Colors, m_Height);
+ }
+}
+
+
+
+
+
+void cMap::UpdateClient(cPlayer * a_Player)
+{
+ ASSERT(a_Player != NULL);
+ cClientHandle * Handle = a_Player->GetClientHandle();
+
+ if (Handle == NULL)
+ {
+ return;
+ }
+
+ Int64 WorldAge = a_Player->GetWorld()->GetWorldAge();
+
+ RemoveInactiveClients(WorldAge - 5);
+
+ // Linear search for client state
+ for (cMapClientList::iterator it = m_Clients.begin(); it != m_Clients.end(); ++it)
+ {
+ if (it->m_Handle == Handle)
+ {
+ it->m_LastUpdate = WorldAge;
+
+ StreamNext(*it);
+
+ return;
+ }
+ }
+
+ // New player, construct a new client state
+ AddPlayer(a_Player, WorldAge);
+}
+
+
+
+
+
+void cMap::EraseData(void)
+{
+ m_Data.assign(m_Width * m_Height, 0);
+}
+
+
+
+
+
+eDimension cMap::GetDimension(void) const
+{
+ ASSERT(m_World != NULL);
+ return m_World->GetDimension();
+}
+
+
+
+
+
+
+void cMap::Resize(unsigned int a_Width, unsigned int a_Height)
+{
+ if ((m_Width == a_Width) && (m_Height == a_Height))
+ {
+ return;
+ }
+
+ m_Width = a_Width;
+ m_Height = a_Height;
+
+ m_Data.assign(m_Width * m_Height, 0);
+}
+
+
+
+
+
+void cMap::SetPosition(int a_CenterX, int a_CenterZ)
+{
+ m_CenterX = a_CenterX;
+ m_CenterZ = a_CenterZ;
+}
+
+
+
+
+
+void cMap::SetScale(unsigned int a_Scale)
+{
+ if (m_Scale == a_Scale)
+ {
+ return;
+ }
+
+ m_Scale = a_Scale;
+
+ for (cMapClientList::iterator it = m_Clients.begin(); it != m_Clients.end(); ++it)
+ {
+ it->m_SendInfo = true;
+ }
+}
+
+
+
+
+
+bool cMap::SetPixel(unsigned int a_X, unsigned int a_Z, cMap::ColorID a_Data)
+{
+ if ((a_X < m_Width) && (a_Z < m_Height))
+ {
+ m_Data[a_Z + (a_X * m_Height)] = a_Data;
+
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+
+
+
+
+cMap::ColorID cMap::GetPixel(unsigned int a_X, unsigned int a_Z)
+{
+ if ((a_X < m_Width) && (a_Z < m_Height))
+ {
+ return m_Data[a_Z + (a_X * m_Height)];
+ }
+ else
+ {
+ return E_BASE_COLOR_TRANSPARENT;
+ }
+}
+
+
+
+
+
+void cMap::SendTo(cClientHandle & a_Client)
+{
+ a_Client.SendMapInfo(m_ID, m_Scale);
+
+ for (unsigned int i = 0; i < m_Width; ++i)
+ {
+ const Byte* Colors = &m_Data[i * m_Height];
+
+ a_Client.SendMapColumn(m_ID, i, 0, Colors, m_Height);
+ }
+
+ a_Client.SendMapDecorators(m_ID, m_Decorators);
+}
+
+
+
+
+
+unsigned int cMap::GetNumPixels(void) const
+{
+ return m_Width * m_Height;
+}
+
+
+
+
+
+unsigned int cMap::GetNumDecorators(void) const
+{
+ return m_Decorators.size();
+}
+
+
+
+
+unsigned int cMap::GetPixelWidth(void) const
+{
+ return (int) pow(2.0, (double) m_Scale);
+}
+
+
+
+
+
diff --git a/src/Map.h b/src/Map.h
new file mode 100644
index 000000000..a313d5431
--- /dev/null
+++ b/src/Map.h
@@ -0,0 +1,264 @@
+
+// Map.h
+
+// Implementation of in-game coloured maps
+
+
+
+
+
+#pragma once
+
+
+
+
+
+#include "BlockID.h"
+
+
+
+
+
+class cClientHandle;
+class cWorld;
+class cPlayer;
+class cMap;
+
+
+
+
+
+/** Encapsulates a map decorator.
+ *
+ * A map decorator represents an object drawn on the map that can move freely.
+ * (e.g. player trackers and item frame pointers)
+ *
+ * Excluding manually placed decorators,
+ * decorators are automatically managed (allocated and freed) by their parent cMap instance.
+ */
+class cMapDecorator
+{
+public:
+
+ enum eType
+ {
+ E_TYPE_PLAYER = 0x00,
+ E_TYPE_ITEM_FRAME = 0x01,
+
+ /** Player outside of the boundaries of the map. */
+ E_TYPE_PLAYER_OUTSIDE = 0x06
+ };
+
+
+public:
+
+ /** Constructs a map decorator fixed at the specified pixel coordinates. (DEBUG) */
+ cMapDecorator(cMap * a_Map, eType a_Type, int a_X, int a_Z, int a_Rot);
+
+ /** Constructs a map decorator that tracks a player. */
+ cMapDecorator(cMap * a_Map, cPlayer * a_Player);
+
+ /** Updates the decorator. */
+ void Update(void);
+
+ unsigned int GetPixelX(void) const { return m_PixelX; }
+ unsigned int GetPixelZ(void) const { return m_PixelZ; }
+
+ int GetRot(void) const { return m_Rot; }
+
+ eType GetType(void) const { return m_Type; }
+
+ cPlayer * GetPlayer(void) { return m_Player; }
+
+
+protected:
+
+ cMap * m_Map;
+
+ eType m_Type;
+
+ unsigned int m_PixelX;
+ unsigned int m_PixelZ;
+
+ unsigned int m_Rot;
+
+ cPlayer * m_Player;
+
+};
+
+typedef std::list<cMapDecorator> cMapDecoratorList;
+
+
+
+
+
+// tolua_begin
+
+/** Encapsulates an in-game world map. */
+class cMap
+{
+public:
+
+ enum eBaseColor
+ {
+ E_BASE_COLOR_TRANSPARENT = 0, /* Air */
+ E_BASE_COLOR_LIGHT_GREEN = 4, /* Grass */
+ E_BASE_COLOR_LIGHT_BROWN = 8, /* Sand */
+ E_BASE_COLOR_GRAY_1 = 12, /* Cloth */
+ E_BASE_COLOR_RED = 16, /* TNT */
+ E_BASE_COLOR_PALE_BLUE = 20, /* Ice */
+ E_BASE_COLOR_GRAY_2 = 24, /* Iron */
+ E_BASE_COLOR_DARK_GREEN = 28, /* Foliage */
+ E_BASE_COLOR_WHITE = 32, /* Snow */
+ E_BASE_COLOR_LIGHT_GRAY = 36, /* Clay */
+ E_BASE_COLOR_BROWN = 40, /* Dirt */
+ E_BASE_COLOR_DARK_GRAY = 44, /* Stone */
+ E_BASE_COLOR_BLUE = 48, /* Water */
+ E_BASE_COLOR_DARK_BROWN = 52 /* Wood */
+ };
+
+ typedef Byte ColorID;
+
+ // tolua_end
+
+ typedef std::vector<ColorID> cColorList;
+
+
+public:
+
+ /** Construct an empty map. */
+ cMap(unsigned int a_ID, cWorld * a_World);
+
+ /** Construct an empty map at the specified coordinates. */
+ cMap(unsigned int a_ID, int a_CenterX, int a_CenterZ, cWorld * a_World, unsigned int a_Scale = 3);
+
+ /** Send this map to the specified client. WARNING: Slow */
+ void SendTo(cClientHandle & a_Client);
+
+ /** Update a circular region with the specified radius and center (in pixels). */
+ void UpdateRadius(int a_PixelX, int a_PixelZ, unsigned int a_Radius);
+
+ /** Update a circular region around the specified player. */
+ void UpdateRadius(cPlayer & a_Player, unsigned int a_Radius);
+
+ /** Send next update packet to the specified player and remove invalid decorators/clients. */
+ void UpdateClient(cPlayer * a_Player);
+
+ // tolua_begin
+
+ /** Erase pixel data */
+ void EraseData(void);
+
+ void Resize(unsigned int a_Width, unsigned int a_Height);
+
+ void SetPosition(int a_CenterX, int a_CenterZ);
+
+ void SetScale(unsigned int a_Scale);
+
+ bool SetPixel(unsigned int a_X, unsigned int a_Z, ColorID a_Data);
+
+ ColorID GetPixel(unsigned int a_X, unsigned int a_Z);
+
+ unsigned int GetWidth (void) const { return m_Width; }
+ unsigned int GetHeight(void) const { return m_Height; }
+
+ unsigned int GetScale(void) const { return m_Scale; }
+
+ int GetCenterX(void) const { return m_CenterX; }
+ int GetCenterZ(void) const { return m_CenterZ; }
+
+ unsigned int GetID(void) const { return m_ID; }
+
+ cWorld * GetWorld(void) { return m_World; }
+
+ AString GetName(void) { return m_Name; }
+
+ eDimension GetDimension(void) const;
+
+ unsigned int GetNumPixels(void) const;
+
+ unsigned int GetPixelWidth(void) const;
+
+ // tolua_end
+
+ unsigned int GetNumDecorators(void) const;
+
+ const cColorList & GetData(void) const { return m_Data; }
+
+ static const char * GetClassStatic(void) // Needed for ManualBindings's DoWith templates
+ {
+ return "cMap";
+ }
+
+
+protected:
+
+ /** Encapsulates the state of a map client.
+ *
+ * In order to enhance performace, maps are streamed column-by-column to each client.
+ * This structure stores the state of the stream.
+ */
+ struct cMapClient
+ {
+ cClientHandle * m_Handle;
+
+ /** Whether the map scale was modified and needs to be resent. */
+ bool m_SendInfo;
+
+ /** Ticks since last decorator update. */
+ unsigned int m_NextDecoratorUpdate;
+
+ /** Number of pixel data updates. */
+ Int64 m_DataUpdate;
+
+ Int64 m_LastUpdate;
+ };
+
+ typedef std::list<cMapClient> cMapClientList;
+
+
+private:
+
+ /** Update the associated decorators. */
+ void UpdateDecorators(void);
+
+ /** Update the specified pixel. */
+ bool UpdatePixel(unsigned int a_X, unsigned int a_Z);
+
+ /** Add a new map client. */
+ void AddPlayer(cPlayer * a_Player, Int64 a_WorldAge);
+
+ /** Remove inactive or invalid clients. */
+ void RemoveInactiveClients(Int64 a_WorldAge);
+
+ /** Send next update packet to the specified client. */
+ void StreamNext(cMapClient & a_Client);
+
+ unsigned int m_ID;
+
+ unsigned int m_Width;
+ unsigned int m_Height;
+
+ /** The zoom level, 2^scale square blocks per pixel */
+ unsigned int m_Scale;
+
+ int m_CenterX;
+ int m_CenterZ;
+
+ /** Column-major array of colours */
+ cColorList m_Data;
+
+ cWorld * m_World;
+
+ cMapDecoratorList m_Decorators;
+
+ cMapClientList m_Clients;
+
+ AString m_Name;
+
+ friend class cMapSerializer;
+
+}; // tolua_export
+
+
+
diff --git a/src/MapManager.cpp b/src/MapManager.cpp
new file mode 100644
index 000000000..9d02eafb4
--- /dev/null
+++ b/src/MapManager.cpp
@@ -0,0 +1,178 @@
+
+// MapManager.cpp
+
+#include "Globals.h"
+
+#include "MapManager.h"
+
+#include "World.h"
+#include "WorldStorage/MapSerializer.h"
+
+
+
+
+
+cMapManager::cMapManager(cWorld * a_World)
+ : m_World(a_World)
+{
+ ASSERT(m_World != NULL);
+}
+
+
+
+
+
+bool cMapManager::DoWithMap(int a_ID, cMapCallback & a_Callback)
+{
+ cCSLock Lock(m_CS);
+ cMap * Map = GetMapData(a_ID);
+
+ if (Map == NULL)
+ {
+ return false;
+ }
+ else
+ {
+ a_Callback.Item(Map);
+ return true;
+ }
+}
+
+
+
+
+
+bool cMapManager::ForEachMap(cMapCallback & a_Callback)
+{
+ cCSLock Lock(m_CS);
+ for (cMapList::iterator itr = m_MapData.begin(); itr != m_MapData.end(); ++itr)
+ {
+ cMap * Map = &(*itr);
+ if (a_Callback.Item(Map))
+ {
+ return false;
+ }
+ } // for itr - m_MapData[]
+ return true;
+}
+
+
+
+
+
+cMap * cMapManager::GetMapData(unsigned int a_ID)
+{
+ if (a_ID < m_MapData.size())
+ {
+ return &m_MapData[a_ID];
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+
+
+
+
+cMap * cMapManager::CreateMap(int a_CenterX, int a_CenterY, int a_Scale)
+{
+ cCSLock Lock(m_CS);
+
+ if (m_MapData.size() >= 65536)
+ {
+ LOGWARN("Could not craft map - Too many maps in use");
+ return NULL;
+ }
+
+ cMap Map(m_MapData.size(), a_CenterX, a_CenterY, m_World, a_Scale);
+
+ m_MapData.push_back(Map);
+
+ return &m_MapData[Map.GetID()];
+}
+
+
+
+
+
+unsigned int cMapManager::GetNumMaps(void) const
+{
+ return m_MapData.size();
+}
+
+
+
+
+
+void cMapManager::LoadMapData(void)
+{
+ cCSLock Lock(m_CS);
+
+ cIDCountSerializer IDSerializer(m_World->GetName());
+
+ if (!IDSerializer.Load())
+ {
+ return;
+ }
+
+ unsigned int MapCount = IDSerializer.GetMapCount();
+
+ m_MapData.clear();
+
+ for (unsigned int i = 0; i < MapCount; ++i)
+ {
+ cMap Map(i, m_World);
+
+ cMapSerializer Serializer(m_World->GetName(), &Map);
+
+ if (!Serializer.Load())
+ {
+ LOGWARN("Could not load map #%i", Map.GetID());
+ }
+
+ m_MapData.push_back(Map);
+ }
+}
+
+
+
+
+
+void cMapManager::SaveMapData(void)
+{
+ cCSLock Lock(m_CS);
+
+ if (m_MapData.empty())
+ {
+ return;
+ }
+
+ cIDCountSerializer IDSerializer(m_World->GetName());
+
+ IDSerializer.SetMapCount(m_MapData.size());
+
+ if (!IDSerializer.Save())
+ {
+ LOGERROR("Could not save idcounts.dat");
+ return;
+ }
+
+ for (cMapList::iterator it = m_MapData.begin(); it != m_MapData.end(); ++it)
+ {
+ cMap & Map = *it;
+
+ cMapSerializer Serializer(m_World->GetName(), &Map);
+
+ if (!Serializer.Save())
+ {
+ LOGWARN("Could not save map #%i", Map.GetID());
+ }
+ }
+}
+
+
+
+
+
diff --git a/src/MapManager.h b/src/MapManager.h
new file mode 100644
index 000000000..80e6d16d1
--- /dev/null
+++ b/src/MapManager.h
@@ -0,0 +1,78 @@
+
+// MapManager.h
+
+
+
+
+
+#pragma once
+
+
+
+
+
+#include "Map.h"
+
+
+
+
+typedef cItemCallback<cMap> cMapCallback;
+
+
+
+
+// tolua_begin
+
+/** Manages the in-game maps of a single world - Thread safe. */
+class cMapManager
+{
+public:
+ // tolua_end
+
+ cMapManager(cWorld * a_World);
+
+ /** Returns the map with the specified ID, NULL if out of range.
+ *
+ * WARNING: The returned map object is not thread safe.
+ */
+ cMap * GetMapData(unsigned int a_ID);
+
+ /** Creates a new map. Returns NULL on error */
+ cMap * CreateMap(int a_CenterX, int a_CenterY, int a_Scale = 3);
+
+ /** Calls the callback for the map with the specified ID.
+ *
+ * Returns true if the map was found and the callback called, false if map not found.
+ * Callback return ignored.
+ */
+ bool DoWithMap(int a_ID, cMapCallback & a_Callback); // Exported in ManualBindings.cpp
+
+ /** Calls the callback for each map.
+ *
+ * Returns true if all maps processed, false if the callback aborted by returning true.
+ */
+ bool ForEachMap(cMapCallback & a_Callback);
+
+ unsigned int GetNumMaps(void) const; // tolua_export
+
+ /** Loads the map data from the disk */
+ void LoadMapData(void);
+
+ /** Saves the map data to the disk */
+ void SaveMapData(void);
+
+
+private:
+
+ typedef std::vector<cMap> cMapList;
+
+ cCriticalSection m_CS;
+
+ cMapList m_MapData;
+
+ cWorld * m_World;
+
+}; // tolua_export
+
+
+
diff --git a/src/MersenneTwister.h b/src/MersenneTwister.h
index 784ac605d..f4c7b0699 100644
--- a/src/MersenneTwister.h
+++ b/src/MersenneTwister.h
@@ -207,7 +207,7 @@ inline void MTRand::seed( uint32 *const bigSeed, const uint32 seedLength )
initialize(19650218UL);
int i = 1;
uint32 j = 0;
- int k = ( N > seedLength ? N : seedLength );
+ int k = ( (uint32)N > seedLength ? (uint32)N : seedLength );
for( ; k; --k )
{
state[i] =
diff --git a/src/Mobs/Blaze.cpp b/src/Mobs/Blaze.cpp
index f9c05b17a..ac42cf40b 100644
--- a/src/Mobs/Blaze.cpp
+++ b/src/Mobs/Blaze.cpp
@@ -19,7 +19,11 @@ cBlaze::cBlaze(void) :
void cBlaze::GetDrops(cItems & a_Drops, cEntity * a_Killer)
{
- AddRandomDropItem(a_Drops, 0, 1, E_ITEM_BLAZE_ROD);
+ if ((a_Killer != NULL) && (a_Killer->IsPlayer() || a_Killer->IsA("cWolf")))
+ {
+ int LootingLevel = a_Killer->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchLooting);
+ AddRandomDropItem(a_Drops, 0, 1 + LootingLevel, E_ITEM_BLAZE_ROD);
+ }
}
diff --git a/src/Mobs/Cavespider.cpp b/src/Mobs/Cavespider.cpp
index aba1ff9f5..94e93283d 100644
--- a/src/Mobs/Cavespider.cpp
+++ b/src/Mobs/Cavespider.cpp
@@ -31,8 +31,16 @@ void cCavespider::Tick(float a_Dt, cChunk & a_Chunk)
void cCavespider::GetDrops(cItems & a_Drops, cEntity * a_Killer)
{
- AddRandomDropItem(a_Drops, 0, 2, E_ITEM_STRING);
- AddRandomDropItem(a_Drops, 0, 1, E_ITEM_SPIDER_EYE);
+ int LootingLevel = 0;
+ if (a_Killer != NULL)
+ {
+ LootingLevel = a_Killer->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchLooting);
+ }
+ AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_STRING);
+ if ((a_Killer != NULL) && (a_Killer->IsPlayer() || a_Killer->IsA("cWolf")))
+ {
+ AddRandomUncommonDropItem(a_Drops, 33.0f, E_ITEM_SPIDER_EYE);
+ }
}
diff --git a/src/Mobs/Chicken.cpp b/src/Mobs/Chicken.cpp
index fab92ce49..f7e44238f 100644
--- a/src/Mobs/Chicken.cpp
+++ b/src/Mobs/Chicken.cpp
@@ -48,8 +48,13 @@ void cChicken::Tick(float a_Dt, cChunk & a_Chunk)
void cChicken::GetDrops(cItems & a_Drops, cEntity * a_Killer)
{
- AddRandomDropItem(a_Drops, 0, 2, E_ITEM_FEATHER);
- a_Drops.push_back(cItem(IsOnFire() ? E_ITEM_COOKED_CHICKEN : E_ITEM_RAW_CHICKEN, 1));
+ int LootingLevel = 0;
+ if (a_Killer != NULL)
+ {
+ LootingLevel = a_Killer->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchLooting);
+ }
+ AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_FEATHER);
+ AddRandomDropItem(a_Drops, 1, 1, IsOnFire() ? E_ITEM_COOKED_CHICKEN : E_ITEM_RAW_CHICKEN);
}
diff --git a/src/Mobs/Cow.cpp b/src/Mobs/Cow.cpp
index d8e905217..9914df6b5 100644
--- a/src/Mobs/Cow.cpp
+++ b/src/Mobs/Cow.cpp
@@ -21,8 +21,13 @@ cCow::cCow(void) :
void cCow::GetDrops(cItems & a_Drops, cEntity * a_Killer)
{
- AddRandomDropItem(a_Drops, 0, 2, E_ITEM_LEATHER);
- AddRandomDropItem(a_Drops, 1, 3, IsOnFire() ? E_ITEM_STEAK : E_ITEM_RAW_BEEF);
+ int LootingLevel = 0;
+ if (a_Killer != NULL)
+ {
+ LootingLevel = a_Killer->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchLooting);
+ }
+ AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_LEATHER);
+ AddRandomDropItem(a_Drops, 1, 3 + LootingLevel, IsOnFire() ? E_ITEM_STEAK : E_ITEM_RAW_BEEF);
}
diff --git a/src/Mobs/Creeper.cpp b/src/Mobs/Creeper.cpp
index ff0abfdca..40ee20e44 100644
--- a/src/Mobs/Creeper.cpp
+++ b/src/Mobs/Creeper.cpp
@@ -39,7 +39,12 @@ void cCreeper::Tick(float a_Dt, cChunk & a_Chunk)
void cCreeper::GetDrops(cItems & a_Drops, cEntity * a_Killer)
{
- AddRandomDropItem(a_Drops, 0, 2, E_ITEM_GUNPOWDER);
+ int LootingLevel = 0;
+ if (a_Killer != NULL)
+ {
+ LootingLevel = a_Killer->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchLooting);
+ }
+ AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_GUNPOWDER);
if ((a_Killer != NULL) && (a_Killer->IsProjectile()))
{
diff --git a/src/Mobs/Enderman.cpp b/src/Mobs/Enderman.cpp
index a784131e4..becc99a86 100644
--- a/src/Mobs/Enderman.cpp
+++ b/src/Mobs/Enderman.cpp
@@ -21,7 +21,12 @@ cEnderman::cEnderman(void) :
void cEnderman::GetDrops(cItems & a_Drops, cEntity * a_Killer)
{
- AddRandomDropItem(a_Drops, 0, 1, E_ITEM_ENDER_PEARL);
+ int LootingLevel = 0;
+ if (a_Killer != NULL)
+ {
+ LootingLevel = a_Killer->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchLooting);
+ }
+ AddRandomDropItem(a_Drops, 0, 1 + LootingLevel, E_ITEM_ENDER_PEARL);
}
diff --git a/src/Mobs/Ghast.cpp b/src/Mobs/Ghast.cpp
index 96a29b2d8..fe18f5e76 100644
--- a/src/Mobs/Ghast.cpp
+++ b/src/Mobs/Ghast.cpp
@@ -18,8 +18,13 @@ cGhast::cGhast(void) :
void cGhast::GetDrops(cItems & a_Drops, cEntity * a_Killer)
{
- AddRandomDropItem(a_Drops, 0, 2, E_ITEM_GUNPOWDER);
- AddRandomDropItem(a_Drops, 0, 1, E_ITEM_GHAST_TEAR);
+ int LootingLevel = 0;
+ if (a_Killer != NULL)
+ {
+ LootingLevel = a_Killer->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchLooting);
+ }
+ AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_GUNPOWDER);
+ AddRandomDropItem(a_Drops, 0, 1 + LootingLevel, E_ITEM_GHAST_TEAR);
}
diff --git a/src/Mobs/Horse.cpp b/src/Mobs/Horse.cpp
index bb9a4e3f6..9d130301f 100644
--- a/src/Mobs/Horse.cpp
+++ b/src/Mobs/Horse.cpp
@@ -140,7 +140,12 @@ void cHorse::OnRightClicked(cPlayer & a_Player)
void cHorse::GetDrops(cItems & a_Drops, cEntity * a_Killer)
{
- AddRandomDropItem(a_Drops, 0, 2, E_ITEM_LEATHER);
+ int LootingLevel = 0;
+ if (a_Killer != NULL)
+ {
+ LootingLevel = a_Killer->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchLooting);
+ }
+ AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_LEATHER);
if (m_bIsSaddled)
{
a_Drops.push_back(cItem(E_ITEM_SADDLE, 1));
diff --git a/src/Mobs/IronGolem.cpp b/src/Mobs/IronGolem.cpp
index 47c961098..dae4615e4 100644
--- a/src/Mobs/IronGolem.cpp
+++ b/src/Mobs/IronGolem.cpp
@@ -18,7 +18,9 @@ cIronGolem::cIronGolem(void) :
void cIronGolem::GetDrops(cItems & a_Drops, cEntity * a_Killer)
{
+ UNUSED(a_Killer);
AddRandomDropItem(a_Drops, 0, 5, E_ITEM_IRON);
+ AddRandomDropItem(a_Drops, 0, 2, E_BLOCK_FLOWER);
}
diff --git a/src/Mobs/Magmacube.cpp b/src/Mobs/Magmacube.cpp
index 86447ff6b..05405f082 100644
--- a/src/Mobs/Magmacube.cpp
+++ b/src/Mobs/Magmacube.cpp
@@ -19,7 +19,11 @@ cMagmaCube::cMagmaCube(int a_Size) :
void cMagmaCube::GetDrops(cItems & a_Drops, cEntity * a_Killer)
{
- AddRandomDropItem(a_Drops, 0, 1, E_ITEM_MAGMA_CREAM);
+ UNUSED(a_Killer);
+ if (GetSize() > 1)
+ {
+ AddRandomUncommonDropItem(a_Drops, 25.0f, E_ITEM_MAGMA_CREAM);
+ }
}
diff --git a/src/Mobs/Monster.cpp b/src/Mobs/Monster.cpp
index b5cf693cb..ac9137ccd 100644
--- a/src/Mobs/Monster.cpp
+++ b/src/Mobs/Monster.cpp
@@ -82,6 +82,12 @@ cMonster::cMonster(const AString & a_ConfigName, eType a_MobType, const AString
, m_AttackRange(2)
, m_AttackInterval(0)
, m_SightDistance(25)
+ , m_DropChanceWeapon(0.085)
+ , m_DropChanceHelmet(0.085)
+ , m_DropChanceChestplate(0.085)
+ , m_DropChanceLeggings(0.085)
+ , m_DropChanceBoots(0.085)
+ , m_CanPickUpLoot(true)
, m_BurnsInDaylight(false)
{
if (!a_ConfigName.empty())
@@ -880,6 +886,76 @@ void cMonster::AddRandomDropItem(cItems & a_Drops, unsigned int a_Min, unsigned
+void cMonster::AddRandomUncommonDropItem(cItems & a_Drops, float a_Chance, short a_Item, short a_ItemHealth)
+{
+ MTRand r1;
+ int Count = r1.randInt() % 1000;
+ if (Count < (a_Chance * 10))
+ {
+ a_Drops.push_back(cItem(a_Item, 1, a_ItemHealth));
+ }
+}
+
+
+
+
+
+void cMonster::AddRandomRareDropItem(cItems & a_Drops, cItems & a_Items, short a_LootingLevel)
+{
+ MTRand r1;
+ int Count = r1.randInt() % 200;
+ if (Count < (5 + a_LootingLevel))
+ {
+ int Rare = r1.randInt() % a_Items.Size();
+ a_Drops.push_back(a_Items.at(Rare));
+ }
+}
+
+
+
+
+
+void cMonster::AddRandomArmorDropItem(cItems & a_Drops, short a_LootingLevel)
+{
+ MTRand r1;
+ if (r1.randInt() % 200 < ((m_DropChanceHelmet * 200) + (a_LootingLevel * 2)))
+ {
+ if (!GetEquippedHelmet().IsEmpty()) a_Drops.push_back(GetEquippedHelmet());
+ }
+
+ if (r1.randInt() % 200 < ((m_DropChanceChestplate * 200) + (a_LootingLevel * 2)))
+ {
+ if (!GetEquippedChestplate().IsEmpty()) a_Drops.push_back(GetEquippedChestplate());
+ }
+
+ if (r1.randInt() % 200 < ((m_DropChanceLeggings * 200) + (a_LootingLevel * 2)))
+ {
+ if (!GetEquippedLeggings().IsEmpty()) a_Drops.push_back(GetEquippedLeggings());
+ }
+
+ if (r1.randInt() % 200 < ((m_DropChanceBoots * 200) + (a_LootingLevel * 2)))
+ {
+ if (!GetEquippedBoots().IsEmpty()) a_Drops.push_back(GetEquippedBoots());
+ }
+}
+
+
+
+
+
+void cMonster::AddRandomWeaponDropItem(cItems & a_Drops, short a_LootingLevel)
+{
+ MTRand r1;
+ if (r1.randInt() % 200 < ((m_DropChanceWeapon * 200) + (a_LootingLevel * 2)))
+ {
+ if (!GetEquippedWeapon().IsEmpty()) a_Drops.push_back(GetEquippedWeapon());
+ }
+}
+
+
+
+
+
void cMonster::HandleDaylightBurning(cChunk & a_Chunk)
{
if (!m_BurnsInDaylight)
diff --git a/src/Mobs/Monster.h b/src/Mobs/Monster.h
index 4d2e099c5..776426a0d 100644
--- a/src/Mobs/Monster.h
+++ b/src/Mobs/Monster.h
@@ -5,6 +5,7 @@
#include "../Defines.h"
#include "../BlockID.h"
#include "../Item.h"
+#include "../Enchantments.h"
@@ -118,6 +119,19 @@ public:
void SetAttackDamage(int a_AttackDamage) { m_AttackDamage = a_AttackDamage; }
void SetSightDistance(int a_SightDistance) { m_SightDistance = a_SightDistance; }
+ float GetDropChanceWeapon() { return m_DropChanceWeapon; }
+ float GetDropChanceHelmet() { return m_DropChanceHelmet; }
+ float GetDropChanceChestplate() { return m_DropChanceChestplate; }
+ float GetDropChanceLeggings() { return m_DropChanceLeggings; }
+ float GetDropChanceBoots() { return m_DropChanceBoots; }
+ bool CanPickUpLoot() { return m_CanPickUpLoot; }
+ void SetDropChanceWeapon(float a_DropChanceWeapon) { m_DropChanceWeapon = a_DropChanceWeapon; }
+ void SetDropChanceHelmet(float a_DropChanceHelmet) { m_DropChanceHelmet = a_DropChanceHelmet; }
+ void SetDropChanceChestplate(float a_DropChanceChestplate) { m_DropChanceChestplate = a_DropChanceChestplate; }
+ void SetDropChanceLeggings(float a_DropChanceLeggings) { m_DropChanceLeggings = a_DropChanceLeggings; }
+ void SetDropChanceBoots(float a_DropChanceBoots) { m_DropChanceBoots = a_DropChanceBoots; }
+ void SetCanPickUpLoot(bool a_CanPickUpLoot) { m_CanPickUpLoot = a_CanPickUpLoot; }
+
/// Sets whether the mob burns in daylight. Only evaluated at next burn-decision tick
void SetBurnsInDaylight(bool a_BurnsInDaylight) { m_BurnsInDaylight = a_BurnsInDaylight; }
@@ -220,10 +234,31 @@ protected:
float m_AttackInterval;
int m_SightDistance;
+ float m_DropChanceWeapon;
+ float m_DropChanceHelmet;
+ float m_DropChanceChestplate;
+ float m_DropChanceLeggings;
+ float m_DropChanceBoots;
+ bool m_CanPickUpLoot;
+
void HandleDaylightBurning(cChunk & a_Chunk);
bool m_BurnsInDaylight;
+ /** Adds a random number of a_Item between a_Min and a_Max to itemdrops a_Drops*/
void AddRandomDropItem(cItems & a_Drops, unsigned int a_Min, unsigned int a_Max, short a_Item, short a_ItemHealth = 0);
+
+ /** Adds a item a_Item with the chance of a_Chance (in percent) to itemdrops a_Drops*/
+ void AddRandomUncommonDropItem(cItems & a_Drops, float a_Chance, short a_Item, short a_ItemHealth = 0);
+
+ /** Adds one rare item out of the list of rare items a_Items modified by the looting level a_LootingLevel(I-III or custom) to the itemdrop a_Drops*/
+ void AddRandomRareDropItem(cItems & a_Drops, cItems & a_Items, short a_LootingLevel);
+
+ /** Adds armor that is equipped with the chance saved in m_DropChance[...] (this will be greter than 1 if piccked up or 0.085 + (0.01 per LootingLevel) if born with) to the drop*/
+ void AddRandomArmorDropItem(cItems & a_Drops, short a_LootingLevel);
+
+ /** Adds weapon that is equipped with the chance saved in m_DropChance[...] (this will be greter than 1 if piccked up or 0.085 + (0.01 per LootingLevel) if born with) to the drop*/
+ void AddRandomWeaponDropItem(cItems & a_Drops, short a_LootingLevel);
+
} ; // tolua_export
diff --git a/src/Mobs/Mooshroom.cpp b/src/Mobs/Mooshroom.cpp
index 88101cd83..81bd3e3b4 100644
--- a/src/Mobs/Mooshroom.cpp
+++ b/src/Mobs/Mooshroom.cpp
@@ -2,11 +2,12 @@
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
#include "Mooshroom.h"
+#include "../Entities/Player.h"
+
-// TODO: Milk Cow
@@ -23,9 +24,52 @@ cMooshroom::cMooshroom(void) :
void cMooshroom::GetDrops(cItems & a_Drops, cEntity * a_Killer)
{
- AddRandomDropItem(a_Drops, 0, 2, E_ITEM_LEATHER);
- AddRandomDropItem(a_Drops, 1, 3, IsOnFire() ? E_ITEM_STEAK : E_ITEM_RAW_BEEF);
+ int LootingLevel = 0;
+ if (a_Killer != NULL)
+ {
+ LootingLevel = a_Killer->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchLooting);
+ }
+ AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_LEATHER);
+ AddRandomDropItem(a_Drops, 1, 3 + LootingLevel, IsOnFire() ? E_ITEM_STEAK : E_ITEM_RAW_BEEF);
}
+
+
+void cMooshroom::OnRightClicked(cPlayer & a_Player)
+{
+ switch (a_Player.GetEquippedItem().m_ItemType)
+ {
+ case E_ITEM_BUCKET:
+ {
+ if (!a_Player.IsGameModeCreative())
+ {
+ a_Player.GetInventory().RemoveOneEquippedItem();
+ a_Player.GetInventory().AddItem(E_ITEM_MILK);
+ }
+ } break;
+ case E_ITEM_BOWL:
+ {
+ if (!a_Player.IsGameModeCreative())
+ {
+ a_Player.GetInventory().RemoveOneEquippedItem();
+ a_Player.GetInventory().AddItem(E_ITEM_MUSHROOM_SOUP);
+ }
+ } break;
+ case E_ITEM_SHEARS:
+ {
+ if (!a_Player.IsGameModeCreative())
+ {
+ a_Player.UseEquippedItem();
+ }
+
+ cItems Drops;
+ Drops.push_back(cItem(E_BLOCK_RED_MUSHROOM, 5, 0));
+ m_World->SpawnItemPickups(Drops, GetPosX(), GetPosY(), GetPosZ(), 10);
+ m_World->SpawnMob(GetPosX(), GetPosY(), GetPosZ(), cMonster::mtCow);
+ Destroy();
+ } break;
+ }
+}
+
diff --git a/src/Mobs/Mooshroom.h b/src/Mobs/Mooshroom.h
index c94301098..16f6c8248 100644
--- a/src/Mobs/Mooshroom.h
+++ b/src/Mobs/Mooshroom.h
@@ -18,6 +18,7 @@ public:
CLASS_PROTODEF(cMooshroom);
virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override;
+ virtual void OnRightClicked(cPlayer & a_Player) override;
virtual const cItem GetFollowedItem(void) const override { return cItem(E_ITEM_WHEAT); }
} ;
diff --git a/src/Mobs/Pig.cpp b/src/Mobs/Pig.cpp
index d8f3dda37..e862f5aaa 100644
--- a/src/Mobs/Pig.cpp
+++ b/src/Mobs/Pig.cpp
@@ -21,7 +21,12 @@ cPig::cPig(void) :
void cPig::GetDrops(cItems & a_Drops, cEntity * a_Killer)
{
- AddRandomDropItem(a_Drops, 1, 3, IsOnFire() ? E_ITEM_COOKED_PORKCHOP : E_ITEM_RAW_PORKCHOP);
+ int LootingLevel = 0;
+ if (a_Killer != NULL)
+ {
+ LootingLevel = a_Killer->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchLooting);
+ }
+ AddRandomDropItem(a_Drops, 1, 3 + LootingLevel, IsOnFire() ? E_ITEM_COOKED_PORKCHOP : E_ITEM_RAW_PORKCHOP);
if (m_bIsSaddled)
{
a_Drops.push_back(cItem(E_ITEM_SADDLE, 1));
diff --git a/src/Mobs/Skeleton.cpp b/src/Mobs/Skeleton.cpp
index 4c8e78988..47fcdbb26 100644
--- a/src/Mobs/Skeleton.cpp
+++ b/src/Mobs/Skeleton.cpp
@@ -20,8 +20,26 @@ cSkeleton::cSkeleton(bool IsWither) :
void cSkeleton::GetDrops(cItems & a_Drops, cEntity * a_Killer)
{
- AddRandomDropItem(a_Drops, 0, 2, E_ITEM_ARROW);
- AddRandomDropItem(a_Drops, 0, 2, E_ITEM_BONE);
+ int LootingLevel = 0;
+ if (a_Killer != NULL)
+ {
+ LootingLevel = a_Killer->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchLooting);
+ }
+ if (IsWither())
+ {
+ AddRandomUncommonDropItem(a_Drops, 33.0f, E_ITEM_COAL);
+ cItems RareDrops;
+ RareDrops.Add(cItem(E_ITEM_HEAD, 1, 1));
+ AddRandomRareDropItem(a_Drops, RareDrops, LootingLevel);
+ }
+ else
+ {
+ AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_ARROW);
+
+ }
+ AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_BONE);
+ AddRandomArmorDropItem(a_Drops, LootingLevel);
+ AddRandomWeaponDropItem(a_Drops, LootingLevel);
}
diff --git a/src/Mobs/Slime.cpp b/src/Mobs/Slime.cpp
index 19f376c21..52a52bb39 100644
--- a/src/Mobs/Slime.cpp
+++ b/src/Mobs/Slime.cpp
@@ -20,8 +20,15 @@ cSlime::cSlime(int a_Size) :
void cSlime::GetDrops(cItems & a_Drops, cEntity * a_Killer)
{
- // TODO: only when tiny
- AddRandomDropItem(a_Drops, 0, 2, E_ITEM_SLIMEBALL);
+ int LootingLevel = 0;
+ if (a_Killer != NULL)
+ {
+ LootingLevel = a_Killer->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchLooting);
+ }
+ if (GetSize() == 1)
+ {
+ AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_SLIMEBALL);
+ }
}
diff --git a/src/Mobs/SnowGolem.cpp b/src/Mobs/SnowGolem.cpp
index c60103055..67e3a3bb8 100644
--- a/src/Mobs/SnowGolem.cpp
+++ b/src/Mobs/SnowGolem.cpp
@@ -19,7 +19,8 @@ cSnowGolem::cSnowGolem(void) :
void cSnowGolem::GetDrops(cItems & a_Drops, cEntity * a_Killer)
{
- AddRandomDropItem(a_Drops, 0, 5, E_ITEM_SNOWBALL);
+ UNUSED(a_Killer);
+ AddRandomDropItem(a_Drops, 0, 15, E_ITEM_SNOWBALL);
}
diff --git a/src/Mobs/Spider.cpp b/src/Mobs/Spider.cpp
index b19a5dcef..8b978ff6b 100644
--- a/src/Mobs/Spider.cpp
+++ b/src/Mobs/Spider.cpp
@@ -18,8 +18,16 @@ cSpider::cSpider(void) :
void cSpider::GetDrops(cItems & a_Drops, cEntity * a_Killer)
{
- AddRandomDropItem(a_Drops, 0, 2, E_ITEM_STRING);
- AddRandomDropItem(a_Drops, 0, 1, E_ITEM_SPIDER_EYE);
+ int LootingLevel = 0;
+ if (a_Killer != NULL)
+ {
+ LootingLevel = a_Killer->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchLooting);
+ }
+ AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_STRING);
+ if ((a_Killer != NULL) && (a_Killer->IsPlayer() || a_Killer->IsA("cWolf")))
+ {
+ AddRandomUncommonDropItem(a_Drops, 33.0f, E_ITEM_SPIDER_EYE);
+ }
}
diff --git a/src/Mobs/Squid.cpp b/src/Mobs/Squid.cpp
index 5a27762ff..ba9171b39 100644
--- a/src/Mobs/Squid.cpp
+++ b/src/Mobs/Squid.cpp
@@ -21,7 +21,12 @@ cSquid::cSquid(void) :
void cSquid::GetDrops(cItems & a_Drops, cEntity * a_Killer)
{
// Drops 0-3 Ink Sacs
- AddRandomDropItem(a_Drops, 0, 3, E_ITEM_DYE, E_META_DYE_BLACK);
+ int LootingLevel = 0;
+ if (a_Killer != NULL)
+ {
+ LootingLevel = a_Killer->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchLooting);
+ }
+ AddRandomDropItem(a_Drops, 0, 3 + LootingLevel, E_ITEM_DYE, E_META_DYE_BLACK);
}
diff --git a/src/Mobs/Witch.cpp b/src/Mobs/Witch.cpp
index 25d27041f..6956f7b7a 100644
--- a/src/Mobs/Witch.cpp
+++ b/src/Mobs/Witch.cpp
@@ -18,13 +18,28 @@ cWitch::cWitch(void) :
void cWitch::GetDrops(cItems & a_Drops, cEntity * a_Killer)
{
- AddRandomDropItem(a_Drops, 0, 6, E_ITEM_GLASS_BOTTLE);
- AddRandomDropItem(a_Drops, 0, 6, E_ITEM_GLOWSTONE_DUST);
- AddRandomDropItem(a_Drops, 0, 6, E_ITEM_GUNPOWDER);
- AddRandomDropItem(a_Drops, 0, 6, E_ITEM_REDSTONE_DUST);
- AddRandomDropItem(a_Drops, 0, 6, E_ITEM_SPIDER_EYE);
- AddRandomDropItem(a_Drops, 0, 6, E_ITEM_STICK);
- AddRandomDropItem(a_Drops, 0, 6, E_ITEM_SUGAR);
+ int LootingLevel = 0;
+ if (a_Killer != NULL)
+ {
+ LootingLevel = a_Killer->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchLooting);
+ }
+ MTRand r1;
+ int DropTypeCount = (r1.randInt() % 3) + 1;
+ for (int i = 0; i < DropTypeCount; i++)
+ {
+ int DropType = r1.randInt() % 7;
+ switch (DropType)
+ {
+ case 0: AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_GLASS_BOTTLE); break;
+ case 1: AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_GLOWSTONE_DUST); break;
+ case 2: AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_GUNPOWDER); break;
+ case 3: AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_REDSTONE_DUST); break;
+ case 4: AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_SPIDER_EYE); break;
+ case 5: AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_STICK); break;
+ case 6: AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_SUGAR); break;
+ }
+ }
+ AddRandomWeaponDropItem(a_Drops, LootingLevel);
}
diff --git a/src/Mobs/Witch.h b/src/Mobs/Witch.h
index 4e637beea..51c63322a 100644
--- a/src/Mobs/Witch.h
+++ b/src/Mobs/Witch.h
@@ -2,6 +2,7 @@
#pragma once
#include "AggressiveMonster.h"
+#include "../MersenneTwister.h"
diff --git a/src/Mobs/Zombie.cpp b/src/Mobs/Zombie.cpp
index 27e8ed5fb..f19e096ee 100644
--- a/src/Mobs/Zombie.cpp
+++ b/src/Mobs/Zombie.cpp
@@ -23,9 +23,19 @@ cZombie::cZombie(bool a_IsVillagerZombie) :
void cZombie::GetDrops(cItems & a_Drops, cEntity * a_Killer)
{
- AddRandomDropItem(a_Drops, 0, 2, E_ITEM_ROTTEN_FLESH);
-
- // TODO: Rare drops
+ int LootingLevel = 0;
+ if (a_Killer != NULL)
+ {
+ LootingLevel = a_Killer->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchLooting);
+ }
+ AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_ROTTEN_FLESH);
+ cItems RareDrops;
+ RareDrops.Add(cItem(E_ITEM_IRON));
+ RareDrops.Add(cItem(E_ITEM_CARROT));
+ RareDrops.Add(cItem(E_ITEM_POTATO));
+ AddRandomRareDropItem(a_Drops, RareDrops, LootingLevel);
+ AddRandomArmorDropItem(a_Drops, LootingLevel);
+ AddRandomWeaponDropItem(a_Drops, LootingLevel);
}
diff --git a/src/Mobs/Zombiepigman.cpp b/src/Mobs/Zombiepigman.cpp
index 6ac89ed4c..a0142b566 100644
--- a/src/Mobs/Zombiepigman.cpp
+++ b/src/Mobs/Zombiepigman.cpp
@@ -19,10 +19,19 @@ cZombiePigman::cZombiePigman(void) :
void cZombiePigman::GetDrops(cItems & a_Drops, cEntity * a_Killer)
{
- AddRandomDropItem(a_Drops, 0, 1, E_ITEM_ROTTEN_FLESH);
- AddRandomDropItem(a_Drops, 0, 1, E_ITEM_GOLD_NUGGET);
+ int LootingLevel = 0;
+ if (a_Killer != NULL)
+ {
+ LootingLevel = a_Killer->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchLooting);
+ }
+ AddRandomDropItem(a_Drops, 0, 1 + LootingLevel, E_ITEM_ROTTEN_FLESH);
+ AddRandomDropItem(a_Drops, 0, 1 + LootingLevel, E_ITEM_GOLD_NUGGET);
- // TODO: Rare drops
+ cItems RareDrops;
+ RareDrops.Add(cItem(E_ITEM_GOLD));
+ AddRandomRareDropItem(a_Drops, RareDrops, LootingLevel);
+ AddRandomArmorDropItem(a_Drops, LootingLevel);
+ AddRandomWeaponDropItem(a_Drops, LootingLevel);
}
diff --git a/src/OSSupport/Queue.h b/src/OSSupport/Queue.h
index 6c3d58295..beb6a63f1 100644
--- a/src/OSSupport/Queue.h
+++ b/src/OSSupport/Queue.h
@@ -29,7 +29,11 @@ public:
static void Delete(T) {};
/// Called when an Item is inserted with EnqueueItemIfNotPresent and there is another equal value already inserted
- static void Combine(T & a_existing, const T & a_new) {};
+ static void Combine(T & a_existing, const T & a_new)
+ {
+ UNUSED(a_existing);
+ UNUSED(a_new);
+ };
};
diff --git a/src/Protocol/Protocol.h b/src/Protocol/Protocol.h
index 46b627254..b5560f7c1 100644
--- a/src/Protocol/Protocol.h
+++ b/src/Protocol/Protocol.h
@@ -13,6 +13,7 @@
#include "../Defines.h"
#include "../Endianness.h"
#include "../Scoreboard.h"
+#include "../Map.h"
@@ -82,6 +83,9 @@ public:
virtual void SendInventorySlot (char a_WindowID, short a_SlotNum, const cItem & a_Item) = 0;
virtual void SendKeepAlive (int a_PingID) = 0;
virtual void SendLogin (const cPlayer & a_Player, const cWorld & a_World) = 0;
+ virtual void SendMapColumn (int a_ID, int a_X, int a_Y, const Byte * a_Colors, unsigned int a_Length) = 0;
+ virtual void SendMapDecorators (int a_ID, const cMapDecoratorList & a_Decorators) = 0;
+ virtual void SendMapInfo (int a_ID, unsigned int a_Scale) = 0;
virtual void SendPaintingSpawn (const cPainting & a_Painting) = 0;
virtual void SendPickupSpawn (const cPickup & a_Pickup) = 0;
virtual void SendPlayerAbilities (void) = 0;
diff --git a/src/Protocol/Protocol125.cpp b/src/Protocol/Protocol125.cpp
index 7020699d1..3980350f5 100644
--- a/src/Protocol/Protocol125.cpp
+++ b/src/Protocol/Protocol125.cpp
@@ -97,6 +97,7 @@ enum
PACKET_WINDOW_PROPERTY = 0x69,
PACKET_CREATIVE_INVENTORY_ACTION = 0x6B,
PACKET_UPDATE_SIGN = 0x82,
+ PACKET_ITEM_DATA = 0x83,
PACKET_PLAYER_LIST_ITEM = 0xC9,
PACKET_PLAYER_ABILITIES = 0xca,
PACKET_PLUGIN_MESSAGE = 0xfa,
@@ -614,6 +615,57 @@ void cProtocol125::SendLogin(const cPlayer & a_Player, const cWorld & a_World)
+void cProtocol125::SendMapColumn(int a_ID, int a_X, int a_Y, const Byte * a_Colors, unsigned int a_Length)
+{
+ cCSLock Lock(m_CSPacket);
+
+ WriteByte (PACKET_ITEM_DATA);
+ WriteShort(E_ITEM_MAP);
+ WriteShort(a_ID);
+ WriteShort(3 + a_Length);
+
+ WriteByte(0);
+ WriteByte(a_X);
+ WriteByte(a_Y);
+
+ for (unsigned int i = 0; i < a_Length; ++i)
+ {
+ WriteByte(a_Colors[i]);
+ }
+
+ Flush();
+}
+
+
+
+
+
+void cProtocol125::SendMapDecorators(int a_ID, const cMapDecoratorList & a_Decorators)
+{
+ cCSLock Lock(m_CSPacket);
+
+ WriteByte (PACKET_ITEM_DATA);
+ WriteShort(E_ITEM_MAP);
+ WriteShort(a_ID);
+ WriteShort(1 + (3 * a_Decorators.size()));
+
+ WriteByte(1);
+
+ for (cMapDecoratorList::const_iterator it = a_Decorators.begin(); it != a_Decorators.end(); ++it)
+ {
+ WriteByte((it->GetType() << 4) | (it->GetRot() & 0xf));
+ WriteByte(it->GetPixelX());
+ WriteByte(it->GetPixelZ());
+ }
+
+ Flush();
+}
+
+
+
+
+
+
void cProtocol125::SendPickupSpawn(const cPickup & a_Pickup)
{
cCSLock Lock(m_CSPacket);
diff --git a/src/Protocol/Protocol125.h b/src/Protocol/Protocol125.h
index 54551ea5f..1d1484a60 100644
--- a/src/Protocol/Protocol125.h
+++ b/src/Protocol/Protocol125.h
@@ -55,6 +55,9 @@ public:
virtual void SendInventorySlot (char a_WindowID, short a_SlotNum, const cItem & a_Item) override;
virtual void SendKeepAlive (int a_PingID) override;
virtual void SendLogin (const cPlayer & a_Player, const cWorld & a_World) override;
+ virtual void SendMapColumn (int a_ID, int a_X, int a_Y, const Byte * a_Colors, unsigned int a_Length) override;
+ virtual void SendMapDecorators (int a_ID, const cMapDecoratorList & a_Decorators) override;
+ virtual void SendMapInfo (int a_ID, unsigned int a_Scale) override {} // This protocol doesn't support such message
virtual void SendParticleEffect (const AString & a_ParticleName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmmount) override;
virtual void SendPaintingSpawn (const cPainting & a_Painting) override {};
virtual void SendPickupSpawn (const cPickup & a_Pickup) override;
diff --git a/src/Protocol/Protocol17x.cpp b/src/Protocol/Protocol17x.cpp
index aaf8830cd..992023464 100644
--- a/src/Protocol/Protocol17x.cpp
+++ b/src/Protocol/Protocol17x.cpp
@@ -587,6 +587,61 @@ void cProtocol172::SendPaintingSpawn(const cPainting & a_Painting)
+void cProtocol172::SendMapColumn(int a_ID, int a_X, int a_Y, const Byte * a_Colors, unsigned int a_Length)
+{
+ cPacketizer Pkt(*this, 0x34);
+ Pkt.WriteVarInt(a_ID);
+ Pkt.WriteShort (3 + a_Length);
+
+ Pkt.WriteByte(0);
+ Pkt.WriteByte(a_X);
+ Pkt.WriteByte(a_Y);
+
+ for (unsigned int i = 0; i < a_Length; ++i)
+ {
+ Pkt.WriteByte(a_Colors[i]);
+ }
+}
+
+
+
+
+
+void cProtocol172::SendMapDecorators(int a_ID, const cMapDecoratorList & a_Decorators)
+{
+ cPacketizer Pkt(*this, 0x34);
+ Pkt.WriteVarInt(a_ID);
+ Pkt.WriteShort (1 + (3 * a_Decorators.size()));
+
+ Pkt.WriteByte(1);
+
+ for (cMapDecoratorList::const_iterator it = a_Decorators.begin(); it != a_Decorators.end(); ++it)
+ {
+ Pkt.WriteByte((it->GetType() << 4) | (it->GetRot() & 0xf));
+ Pkt.WriteByte(it->GetPixelX());
+ Pkt.WriteByte(it->GetPixelZ());
+ }
+}
+
+
+
+
+
+void cProtocol172::SendMapInfo(int a_ID, unsigned int a_Scale)
+{
+ cPacketizer Pkt(*this, 0x34);
+ Pkt.WriteVarInt(a_ID);
+ Pkt.WriteShort (2);
+
+ Pkt.WriteByte(2);
+ Pkt.WriteByte(a_Scale);
+}
+
+
+
+
+
+
void cProtocol172::SendPickupSpawn(const cPickup & a_Pickup)
{
{
diff --git a/src/Protocol/Protocol17x.h b/src/Protocol/Protocol17x.h
index ae3577867..113501568 100644
--- a/src/Protocol/Protocol17x.h
+++ b/src/Protocol/Protocol17x.h
@@ -87,6 +87,9 @@ public:
virtual void SendInventorySlot (char a_WindowID, short a_SlotNum, const cItem & a_Item) override;
virtual void SendKeepAlive (int a_PingID) override;
virtual void SendLogin (const cPlayer & a_Player, const cWorld & a_World) override;
+ virtual void SendMapColumn (int a_ID, int a_X, int a_Y, const Byte * a_Colors, unsigned int a_Length) override;
+ virtual void SendMapDecorators (int a_ID, const cMapDecoratorList & a_Decorators) override;
+ virtual void SendMapInfo (int a_ID, unsigned int a_Scale) override;
virtual void SendPaintingSpawn (const cPainting & a_Painting) override;
virtual void SendPickupSpawn (const cPickup & a_Pickup) override;
virtual void SendPlayerAbilities (void) override;
diff --git a/src/Protocol/ProtocolRecognizer.cpp b/src/Protocol/ProtocolRecognizer.cpp
index b658dc9db..84b052146 100644
--- a/src/Protocol/ProtocolRecognizer.cpp
+++ b/src/Protocol/ProtocolRecognizer.cpp
@@ -396,6 +396,36 @@ void cProtocolRecognizer::SendLogin(const cPlayer & a_Player, const cWorld & a_W
+void cProtocolRecognizer::SendMapColumn(int a_ID, int a_X, int a_Y, const Byte * a_Colors, unsigned int a_Length)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendMapColumn(a_ID, a_X, a_Y, a_Colors, a_Length);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendMapDecorators(int a_ID, const cMapDecoratorList & a_Decorators)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendMapDecorators(a_ID, a_Decorators);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendMapInfo(int a_ID, unsigned int a_Scale)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendMapInfo(a_ID, a_Scale);
+}
+
+
+
+
+
void cProtocolRecognizer::SendParticleEffect(const AString & a_ParticleName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmmount)
{
ASSERT(m_Protocol != NULL);
diff --git a/src/Protocol/ProtocolRecognizer.h b/src/Protocol/ProtocolRecognizer.h
index abbb22827..6aaafedeb 100644
--- a/src/Protocol/ProtocolRecognizer.h
+++ b/src/Protocol/ProtocolRecognizer.h
@@ -90,6 +90,9 @@ public:
virtual void SendInventorySlot (char a_WindowID, short a_SlotNum, const cItem & a_Item) override;
virtual void SendKeepAlive (int a_PingID) override;
virtual void SendLogin (const cPlayer & a_Player, const cWorld & a_World) override;
+ virtual void SendMapColumn (int a_ID, int a_X, int a_Y, const Byte * a_Colors, unsigned int a_Length) override;
+ virtual void SendMapDecorators (int a_ID, const cMapDecoratorList & a_Decorators) override;
+ virtual void SendMapInfo (int a_ID, unsigned int a_Scale) override;
virtual void SendParticleEffect (const AString & a_ParticleName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmmount) override;
virtual void SendPaintingSpawn (const cPainting & a_Painting) override;
virtual void SendPickupSpawn (const cPickup & a_Pickup) override;
diff --git a/src/Server.cpp b/src/Server.cpp
index c80348872..fcbcaa919 100644
--- a/src/Server.cpp
+++ b/src/Server.cpp
@@ -39,7 +39,9 @@ extern "C" {
// For the "dumpmem" server command:
/// Synchronize this with main.cpp - the leak finder needs initialization before it can be used to dump memory
-#define ENABLE_LEAK_FINDER
+// _X 2014_02_20: Disabled for canon repo, it makes the debug version too slow in MSVC2013
+// and we haven't had a memory leak for over a year anyway.
+// #define ENABLE_LEAK_FINDER
#if defined(_MSC_VER) && defined(_DEBUG) && defined(ENABLE_LEAK_FINDER)
#pragma warning(push)
diff --git a/src/Simulator/IncrementalRedstoneSimulator.cpp b/src/Simulator/IncrementalRedstoneSimulator.cpp
index 1637ac02a..91de9e0cc 100644
--- a/src/Simulator/IncrementalRedstoneSimulator.cpp
+++ b/src/Simulator/IncrementalRedstoneSimulator.cpp
@@ -337,13 +337,13 @@ void cIncrementalRedstoneSimulator::WakeUp(int a_BlockX, int a_BlockY, int a_Blo
((a_BlockX % cChunkDef::Width) >= 14) ||
((a_BlockZ % cChunkDef::Width) <= 1) ||
((a_BlockZ % cChunkDef::Width) >= 14)
- ) // Are we on a chunk boundary? ± 2 because of LinkedPowered blocks
+ ) // Are we on a chunk boundary? +- 2 because of LinkedPowered blocks
{
// On a chunk boundary, alert all four sides (i.e. at least one neighbouring chunk)
AddBlock(a_BlockX, a_BlockY, a_BlockZ, a_Chunk);
// Pass the original coordinates, because when adding things to our simulator lists, we get the chunk that they are in, and therefore any updates need to preseve their position
- // RedstoneAddBlock to pass both the neighbouring chunk and the chunk which the coordiantes are in and ± 2 in GetNeighbour() to accomodate for LinkedPowered blocks being 2 away from chunk boundaries
+ // RedstoneAddBlock to pass both the neighbouring chunk and the chunk which the coordiantes are in and +- 2 in GetNeighbour() to accomodate for LinkedPowered blocks being 2 away from chunk boundaries
RedstoneAddBlock(a_BlockX, a_BlockY, a_BlockZ, a_Chunk->GetNeighborChunk(a_BlockX - 2, a_BlockZ), a_Chunk);
RedstoneAddBlock(a_BlockX, a_BlockY, a_BlockZ, a_Chunk->GetNeighborChunk(a_BlockX + 2, a_BlockZ), a_Chunk);
RedstoneAddBlock(a_BlockX, a_BlockY, a_BlockZ, a_Chunk->GetNeighborChunk(a_BlockX, a_BlockZ - 2), a_Chunk);
diff --git a/src/World.cpp b/src/World.cpp
index 42c286c46..ffdae2a37 100644
--- a/src/World.cpp
+++ b/src/World.cpp
@@ -11,6 +11,8 @@
#include "ChunkMap.h"
#include "Generating/ChunkDesc.h"
#include "OSSupport/Timer.h"
+
+// Serializers
#include "WorldStorage/ScoreboardSerializer.h"
// Entities (except mobs):
@@ -251,6 +253,7 @@ cWorld::cWorld(const AString & a_WorldName) :
m_bCommandBlocksEnabled(false),
m_bUseChatPrefixes(true),
m_Scoreboard(this),
+ m_MapManager(this),
m_GeneratorCallbacks(*this),
m_TickThread(*this)
{
@@ -261,6 +264,8 @@ cWorld::cWorld(const AString & a_WorldName) :
// Load the scoreboard
cScoreboardSerializer Serializer(m_WorldName, &m_Scoreboard);
Serializer.Load();
+
+ m_MapManager.LoadMapData();
}
@@ -284,6 +289,8 @@ cWorld::~cWorld()
cScoreboardSerializer Serializer(m_WorldName, &m_Scoreboard);
Serializer.Save();
+ m_MapManager.SaveMapData();
+
delete m_ChunkMap;
}
@@ -3025,6 +3032,7 @@ cFluidSimulator * cWorld::InitializeFluidSimulator(cIniFile & a_IniFile, const c
+
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cWorld::cTaskSaveAllChunks:
diff --git a/src/World.h b/src/World.h
index 5c18c5d23..4b74f7aba 100644
--- a/src/World.h
+++ b/src/World.h
@@ -24,6 +24,7 @@
#include "Entities/ProjectileEntity.h"
#include "ForEachChunkProvider.h"
#include "Scoreboard.h"
+#include "MapManager.h"
#include "Blocks/WorldInterface.h"
#include "Blocks/BroadcastInterface.h"
@@ -580,9 +581,12 @@ public:
/** Returns the name of the world.ini file used by this world */
const AString & GetIniFileName(void) const {return m_IniFileName; }
- /** Returns the associated scoreboard instance */
+ /** Returns the associated scoreboard instance. */
cScoreboard & GetScoreBoard(void) { return m_Scoreboard; }
+ /** Returns the associated map manager instance. */
+ cMapManager & GetMapManager(void) { return m_MapManager; }
+
bool AreCommandBlocksEnabled(void) const { return m_bCommandBlocksEnabled; }
void SetCommandBlocksEnabled(bool a_Flag) { m_bCommandBlocksEnabled = a_Flag; }
@@ -848,6 +852,7 @@ private:
cChunkGenerator m_Generator;
cScoreboard m_Scoreboard;
+ cMapManager m_MapManager;
/** The callbacks that the ChunkGenerator uses to store new chunks and interface to plugins */
cChunkGeneratorCallbacks m_GeneratorCallbacks;
diff --git a/src/WorldStorage/FastNBT.h b/src/WorldStorage/FastNBT.h
index b84eda1a1..49f97c458 100644
--- a/src/WorldStorage/FastNBT.h
+++ b/src/WorldStorage/FastNBT.h
@@ -172,8 +172,18 @@ public:
inline float GetFloat(int a_Tag) const
{
ASSERT(m_Tags[a_Tag].m_Type == TAG_Float);
- Int32 tmp = GetBEInt(m_Data + m_Tags[a_Tag].m_DataStart);
- return *((float *)&tmp);
+
+ // Cause a compile-time error if sizeof(float) != 4
+ // If your platform produces a compiler error here, you'll need to add code that manually decodes 32-bit floats
+ char Check1[5 - sizeof(float)]; // sizeof(float) <= 4
+ char Check2[sizeof(float) - 3]; // sizeof(float) >= 4
+ UNUSED(Check1);
+ UNUSED(Check2);
+
+ Int32 i = GetBEInt(m_Data + m_Tags[a_Tag].m_DataStart);
+ float f;
+ memcpy(&f, &i, sizeof(f));
+ return f;
}
inline double GetDouble(int a_Tag) const
diff --git a/src/WorldStorage/MapSerializer.cpp b/src/WorldStorage/MapSerializer.cpp
new file mode 100644
index 000000000..a4a0aab57
--- /dev/null
+++ b/src/WorldStorage/MapSerializer.cpp
@@ -0,0 +1,276 @@
+
+// MapSerializer.cpp
+
+
+#include "Globals.h"
+#include "MapSerializer.h"
+#include "../StringCompression.h"
+#include "zlib/zlib.h"
+#include "FastNBT.h"
+
+#include "../Map.h"
+#include "../World.h"
+
+
+
+
+
+cMapSerializer::cMapSerializer(const AString& a_WorldName, cMap * a_Map)
+ : m_Map(a_Map)
+{
+ AString DataPath;
+ Printf(DataPath, "%s/data", a_WorldName.c_str());
+
+ Printf(m_Path, "%s/map_%i.dat", DataPath.c_str(), a_Map->GetID());
+
+ cFile::CreateFolder(FILE_IO_PREFIX + DataPath);
+}
+
+
+
+
+
+bool cMapSerializer::Load(void)
+{
+ AString Data = cFile::ReadWholeFile(FILE_IO_PREFIX + m_Path);
+ if (Data.empty())
+ {
+ return false;
+ }
+
+ AString Uncompressed;
+ int res = UncompressStringGZIP(Data.data(), Data.size(), Uncompressed);
+
+ if (res != Z_OK)
+ {
+ return false;
+ }
+
+ // Parse the NBT data:
+ cParsedNBT NBT(Uncompressed.data(), Uncompressed.size());
+ if (!NBT.IsValid())
+ {
+ // NBT Parsing failed
+ return false;
+ }
+
+ return LoadMapFromNBT(NBT);
+}
+
+
+
+
+
+bool cMapSerializer::Save(void)
+{
+ cFastNBTWriter Writer;
+
+ SaveMapToNBT(Writer);
+
+ Writer.Finish();
+
+ #ifdef _DEBUG
+ cParsedNBT TestParse(Writer.GetResult().data(), Writer.GetResult().size());
+ ASSERT(TestParse.IsValid());
+ #endif // _DEBUG
+
+ cFile File;
+ if (!File.Open(FILE_IO_PREFIX + m_Path, cFile::fmWrite))
+ {
+ return false;
+ }
+
+ AString Compressed;
+ int res = CompressStringGZIP(Writer.GetResult().data(), Writer.GetResult().size(), Compressed);
+
+ if (res != Z_OK)
+ {
+ return false;
+ }
+
+ File.Write(Compressed.data(), Compressed.size());
+ File.Close();
+
+ return true;
+}
+
+
+
+
+
+void cMapSerializer::SaveMapToNBT(cFastNBTWriter & a_Writer)
+{
+ a_Writer.BeginCompound("data");
+
+ a_Writer.AddByte("scale", m_Map->GetScale());
+ a_Writer.AddByte("dimension", (int) m_Map->GetDimension());
+
+ a_Writer.AddShort("width", m_Map->GetWidth());
+ a_Writer.AddShort("height", m_Map->GetHeight());
+
+ a_Writer.AddInt("xCenter", m_Map->GetCenterX());
+ a_Writer.AddInt("zCenter", m_Map->GetCenterZ());
+
+ const cMap::cColorList & Data = m_Map->GetData();
+ a_Writer.AddByteArray("colors", (char *) &Data[0], Data.size());
+
+ a_Writer.EndCompound();
+}
+
+
+
+
+
+bool cMapSerializer::LoadMapFromNBT(const cParsedNBT & a_NBT)
+{
+ int Data = a_NBT.FindChildByName(0, "data");
+ if (Data < 0)
+ {
+ return false;
+ }
+
+ int CurrLine = a_NBT.FindChildByName(Data, "scale");
+ if (CurrLine >= 0)
+ {
+ unsigned int Scale = a_NBT.GetByte(CurrLine);
+ m_Map->SetScale(Scale);
+ }
+
+ CurrLine = a_NBT.FindChildByName(Data, "dimension");
+ if (CurrLine >= 0)
+ {
+ eDimension Dimension = (eDimension) a_NBT.GetByte(CurrLine);
+
+ ASSERT(Dimension == m_Map->m_World->GetDimension());
+ }
+
+ CurrLine = a_NBT.FindChildByName(Data, "width");
+ if (CurrLine >= 0)
+ {
+ unsigned int Width = a_NBT.GetShort(CurrLine);
+ m_Map->m_Width = Width;
+ }
+
+ CurrLine = a_NBT.FindChildByName(Data, "height");
+ if (CurrLine >= 0)
+ {
+ unsigned int Height = a_NBT.GetShort(CurrLine);
+ m_Map->m_Height = Height;
+ }
+
+ CurrLine = a_NBT.FindChildByName(Data, "xCenter");
+ if (CurrLine >= 0)
+ {
+ int CenterX = a_NBT.GetInt(CurrLine);
+ m_Map->m_CenterX = CenterX;
+ }
+
+ CurrLine = a_NBT.FindChildByName(Data, "zCenter");
+ if (CurrLine >= 0)
+ {
+ int CenterZ = a_NBT.GetInt(CurrLine);
+ m_Map->m_CenterZ = CenterZ;
+ }
+
+ unsigned int NumPixels = m_Map->GetNumPixels();
+ m_Map->m_Data.resize(NumPixels);
+
+ CurrLine = a_NBT.FindChildByName(Data, "colors");
+ if ((CurrLine >= 0) && (a_NBT.GetType(CurrLine) == TAG_ByteArray))
+ {
+ memcpy(&m_Map->m_Data[0], a_NBT.GetData(CurrLine), NumPixels);
+ }
+
+ return true;
+}
+
+
+
+
+
+cIDCountSerializer::cIDCountSerializer(const AString & a_WorldName) : m_MapCount(0)
+{
+ AString DataPath;
+ Printf(DataPath, "%s/data", a_WorldName.c_str());
+
+ Printf(m_Path, "%s/idcounts.dat", DataPath.c_str());
+
+ cFile::CreateFolder(FILE_IO_PREFIX + DataPath);
+}
+
+
+
+
+
+bool cIDCountSerializer::Load(void)
+{
+ AString Data = cFile::ReadWholeFile(FILE_IO_PREFIX + m_Path);
+ if (Data.empty())
+ {
+ return false;
+ }
+
+ // NOTE: idcounts.dat is not compressed (raw format)
+
+ // Parse the NBT data:
+ cParsedNBT NBT(Data.data(), Data.size());
+ if (!NBT.IsValid())
+ {
+ // NBT Parsing failed
+ return false;
+ }
+
+ int CurrLine = NBT.FindChildByName(0, "map");
+ if (CurrLine >= 0)
+ {
+ m_MapCount = (int)NBT.GetShort(CurrLine) + 1;
+ }
+ else
+ {
+ m_MapCount = 0;
+ }
+
+ return true;
+}
+
+
+
+
+
+bool cIDCountSerializer::Save(void)
+{
+ cFastNBTWriter Writer;
+
+ if (m_MapCount > 0)
+ {
+ Writer.AddShort("map", m_MapCount - 1);
+ }
+
+ Writer.Finish();
+
+ #ifdef _DEBUG
+ cParsedNBT TestParse(Writer.GetResult().data(), Writer.GetResult().size());
+ ASSERT(TestParse.IsValid());
+ #endif // _DEBUG
+
+ cFile File;
+ if (!File.Open(FILE_IO_PREFIX + m_Path, cFile::fmWrite))
+ {
+ return false;
+ }
+
+ // NOTE: idcounts.dat is not compressed (raw format)
+
+ File.Write(Writer.GetResult().data(), Writer.GetResult().size());
+ File.Close();
+
+ return true;
+}
+
+
+
+
+
+
+
+
diff --git a/src/WorldStorage/MapSerializer.h b/src/WorldStorage/MapSerializer.h
new file mode 100644
index 000000000..eb7678a08
--- /dev/null
+++ b/src/WorldStorage/MapSerializer.h
@@ -0,0 +1,86 @@
+
+// MapSerializer.h
+
+// Declares the cMapSerializer class that is used for saving maps into NBT format used by Anvil
+
+
+
+
+
+#pragma once
+
+
+
+
+
+// fwd:
+class cFastNBTWriter;
+class cParsedNBT;
+class cMap;
+
+
+
+
+/** Utility class used to serialize maps. */
+class cMapSerializer
+{
+public:
+
+ cMapSerializer(const AString& a_WorldName, cMap * a_Map);
+
+ /** Try to load the scoreboard */
+ bool Load(void);
+
+ /** Try to save the scoreboard */
+ bool Save(void);
+
+
+private:
+
+ void SaveMapToNBT(cFastNBTWriter & a_Writer);
+
+ bool LoadMapFromNBT(const cParsedNBT & a_NBT);
+
+ cMap * m_Map;
+
+ AString m_Path;
+
+
+} ;
+
+
+
+
+/** Utility class used to serialize item ID counts.
+ *
+ * In order to perform bounds checking (while loading),
+ * the last registered ID of each item is serialized to an NBT file.
+ */
+class cIDCountSerializer
+{
+public:
+
+ cIDCountSerializer(const AString & a_WorldName);
+
+ /** Try to load the ID counts */
+ bool Load(void);
+
+ /** Try to save the ID counts */
+ bool Save(void);
+
+ inline unsigned int GetMapCount(void) const { return m_MapCount; }
+
+ inline void SetMapCount(unsigned int a_MapCount) { m_MapCount = a_MapCount; }
+
+
+private:
+
+ AString m_Path;
+
+ unsigned int m_MapCount;
+
+};
+
+
+
+
diff --git a/src/WorldStorage/NBTChunkSerializer.cpp b/src/WorldStorage/NBTChunkSerializer.cpp
index 2a1eda523..c1c659b36 100644
--- a/src/WorldStorage/NBTChunkSerializer.cpp
+++ b/src/WorldStorage/NBTChunkSerializer.cpp
@@ -409,6 +409,14 @@ void cNBTChunkSerializer::AddMonsterEntity(cMonster * a_Monster)
m_Writer.BeginCompound("");
AddBasicEntity(a_Monster, EntityClass);
+ m_Writer.BeginList("DropChances", TAG_Float);
+ m_Writer.AddFloat("", a_Monster->GetDropChanceWeapon());
+ m_Writer.AddFloat("", a_Monster->GetDropChanceHelmet());
+ m_Writer.AddFloat("", a_Monster->GetDropChanceChestplate());
+ m_Writer.AddFloat("", a_Monster->GetDropChanceLeggings());
+ m_Writer.AddFloat("", a_Monster->GetDropChanceBoots());
+ m_Writer.EndList();
+ m_Writer.AddByte("CanPickUpLoot", (char)a_Monster->CanPickUpLoot());
switch (a_Monster->GetMobType())
{
case cMonster::mtBat:
diff --git a/src/WorldStorage/WSSAnvil.cpp b/src/WorldStorage/WSSAnvil.cpp
index d4490c7fe..05332d23d 100644
--- a/src/WorldStorage/WSSAnvil.cpp
+++ b/src/WorldStorage/WSSAnvil.cpp
@@ -1485,6 +1485,11 @@ void cWSSAnvil::LoadBatFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NB
{
return;
}
+
+ if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
+ {
+ return;
+ }
a_Entities.push_back(Monster.release());
}
@@ -1500,6 +1505,11 @@ void cWSSAnvil::LoadBlazeFromNBT(cEntityList & a_Entities, const cParsedNBT & a_
{
return;
}
+
+ if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
+ {
+ return;
+ }
a_Entities.push_back(Monster.release());
}
@@ -1515,6 +1525,11 @@ void cWSSAnvil::LoadCaveSpiderFromNBT(cEntityList & a_Entities, const cParsedNBT
{
return;
}
+
+ if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
+ {
+ return;
+ }
a_Entities.push_back(Monster.release());
}
@@ -1530,6 +1545,11 @@ void cWSSAnvil::LoadChickenFromNBT(cEntityList & a_Entities, const cParsedNBT &
{
return;
}
+
+ if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
+ {
+ return;
+ }
a_Entities.push_back(Monster.release());
}
@@ -1545,6 +1565,11 @@ void cWSSAnvil::LoadCowFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NB
{
return;
}
+
+ if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
+ {
+ return;
+ }
a_Entities.push_back(Monster.release());
}
@@ -1560,6 +1585,11 @@ void cWSSAnvil::LoadCreeperFromNBT(cEntityList & a_Entities, const cParsedNBT &
{
return;
}
+
+ if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
+ {
+ return;
+ }
a_Entities.push_back(Monster.release());
}
@@ -1575,6 +1605,11 @@ void cWSSAnvil::LoadEnderDragonFromNBT(cEntityList & a_Entities, const cParsedNB
{
return;
}
+
+ if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
+ {
+ return;
+ }
a_Entities.push_back(Monster.release());
}
@@ -1591,6 +1626,11 @@ void cWSSAnvil::LoadEndermanFromNBT(cEntityList & a_Entities, const cParsedNBT &
return;
}
+ if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
+ {
+ return;
+ }
+
a_Entities.push_back(Monster.release());
}
@@ -1606,6 +1646,11 @@ void cWSSAnvil::LoadGhastFromNBT(cEntityList & a_Entities, const cParsedNBT & a_
return;
}
+ if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
+ {
+ return;
+ }
+
a_Entities.push_back(Monster.release());
}
@@ -1620,6 +1665,11 @@ void cWSSAnvil::LoadGiantFromNBT(cEntityList & a_Entities, const cParsedNBT & a_
{
return;
}
+
+ if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
+ {
+ return;
+ }
a_Entities.push_back(Monster.release());
}
@@ -1646,6 +1696,11 @@ void cWSSAnvil::LoadHorseFromNBT(cEntityList & a_Entities, const cParsedNBT & a_
{
return;
}
+
+ if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
+ {
+ return;
+ }
a_Entities.push_back(Monster.release());
}
@@ -1661,6 +1716,11 @@ void cWSSAnvil::LoadIronGolemFromNBT(cEntityList & a_Entities, const cParsedNBT
{
return;
}
+
+ if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
+ {
+ return;
+ }
a_Entities.push_back(Monster.release());
}
@@ -1682,6 +1742,11 @@ void cWSSAnvil::LoadMagmaCubeFromNBT(cEntityList & a_Entities, const cParsedNBT
{
return;
}
+
+ if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
+ {
+ return;
+ }
a_Entities.push_back(Monster.release());
}
@@ -1697,6 +1762,11 @@ void cWSSAnvil::LoadMooshroomFromNBT(cEntityList & a_Entities, const cParsedNBT
{
return;
}
+
+ if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
+ {
+ return;
+ }
a_Entities.push_back(Monster.release());
}
@@ -1712,6 +1782,11 @@ void cWSSAnvil::LoadOcelotFromNBT(cEntityList & a_Entities, const cParsedNBT & a
{
return;
}
+
+ if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
+ {
+ return;
+ }
a_Entities.push_back(Monster.release());
}
@@ -1727,6 +1802,11 @@ void cWSSAnvil::LoadPigFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NB
{
return;
}
+
+ if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
+ {
+ return;
+ }
a_Entities.push_back(Monster.release());
}
@@ -1748,6 +1828,11 @@ void cWSSAnvil::LoadSheepFromNBT(cEntityList & a_Entities, const cParsedNBT & a_
{
return;
}
+
+ if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
+ {
+ return;
+ }
a_Entities.push_back(Monster.release());
}
@@ -1763,6 +1848,11 @@ void cWSSAnvil::LoadSilverfishFromNBT(cEntityList & a_Entities, const cParsedNBT
{
return;
}
+
+ if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
+ {
+ return;
+ }
a_Entities.push_back(Monster.release());
}
@@ -1784,6 +1874,11 @@ void cWSSAnvil::LoadSkeletonFromNBT(cEntityList & a_Entities, const cParsedNBT &
{
return;
}
+
+ if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
+ {
+ return;
+ }
a_Entities.push_back(Monster.release());
}
@@ -1805,6 +1900,11 @@ void cWSSAnvil::LoadSlimeFromNBT(cEntityList & a_Entities, const cParsedNBT & a_
{
return;
}
+
+ if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
+ {
+ return;
+ }
a_Entities.push_back(Monster.release());
}
@@ -1820,6 +1920,11 @@ void cWSSAnvil::LoadSnowGolemFromNBT(cEntityList & a_Entities, const cParsedNBT
{
return;
}
+
+ if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
+ {
+ return;
+ }
a_Entities.push_back(Monster.release());
}
@@ -1835,6 +1940,11 @@ void cWSSAnvil::LoadSpiderFromNBT(cEntityList & a_Entities, const cParsedNBT & a
{
return;
}
+
+ if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
+ {
+ return;
+ }
a_Entities.push_back(Monster.release());
}
@@ -1850,6 +1960,11 @@ void cWSSAnvil::LoadSquidFromNBT(cEntityList & a_Entities, const cParsedNBT & a_
{
return;
}
+
+ if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
+ {
+ return;
+ }
a_Entities.push_back(Monster.release());
}
@@ -1871,6 +1986,11 @@ void cWSSAnvil::LoadVillagerFromNBT(cEntityList & a_Entities, const cParsedNBT &
{
return;
}
+
+ if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
+ {
+ return;
+ }
a_Entities.push_back(Monster.release());
}
@@ -1886,6 +2006,11 @@ void cWSSAnvil::LoadWitchFromNBT(cEntityList & a_Entities, const cParsedNBT & a_
{
return;
}
+
+ if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
+ {
+ return;
+ }
a_Entities.push_back(Monster.release());
}
@@ -1901,6 +2026,11 @@ void cWSSAnvil::LoadWitherFromNBT(cEntityList & a_Entities, const cParsedNBT & a
{
return;
}
+
+ if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
+ {
+ return;
+ }
a_Entities.push_back(Monster.release());
}
@@ -1916,6 +2046,10 @@ void cWSSAnvil::LoadWolfFromNBT(cEntityList & a_Entities, const cParsedNBT & a_N
{
return;
}
+ if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
+ {
+ return;
+ }
int OwnerIdx = a_NBT.FindChildByName(a_TagIdx, "Owner");
if (OwnerIdx > 0)
{
@@ -1964,6 +2098,11 @@ void cWSSAnvil::LoadZombieFromNBT(cEntityList & a_Entities, const cParsedNBT & a
{
return;
}
+
+ if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
+ {
+ return;
+ }
a_Entities.push_back(Monster.release());
}
@@ -1980,6 +2119,11 @@ void cWSSAnvil::LoadPigZombieFromNBT(cEntityList & a_Entities, const cParsedNBT
return;
}
+ if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
+ {
+ return;
+ }
+
a_Entities.push_back(Monster.release());
}
@@ -2023,6 +2167,27 @@ bool cWSSAnvil::LoadEntityBaseFromNBT(cEntity & a_Entity, const cParsedNBT & a_N
+bool cWSSAnvil::LoadMonsterBaseFromNBT(cMonster & a_Monster, const cParsedNBT & a_NBT, int a_TagIdx)
+{
+ float DropChance[5];
+ if (!LoadFloatsListFromNBT(DropChance, 5, a_NBT, a_NBT.FindChildByName(a_TagIdx, "DropChance")))
+ {
+ return false;
+ }
+ a_Monster.SetDropChanceWeapon(DropChance[0]);
+ a_Monster.SetDropChanceHelmet(DropChance[1]);
+ a_Monster.SetDropChanceChestplate(DropChance[2]);
+ a_Monster.SetDropChanceLeggings(DropChance[3]);
+ a_Monster.SetDropChanceBoots(DropChance[4]);
+ bool CanPickUpLoot = (a_NBT.GetByte(a_NBT.FindChildByName(a_TagIdx, "CanPickUpLoot")) == 1);
+ a_Monster.SetCanPickUpLoot(CanPickUpLoot);
+ return true;
+}
+
+
+
+
+
bool cWSSAnvil::LoadProjectileBaseFromNBT(cProjectileEntity & a_Entity, const cParsedNBT & a_NBT, int a_TagIdx)
{
if (!LoadEntityBaseFromNBT(a_Entity, a_NBT, a_TagIdx))
@@ -2065,6 +2230,24 @@ bool cWSSAnvil::LoadDoublesListFromNBT(double * a_Doubles, int a_NumDoubles, con
+bool cWSSAnvil::LoadFloatsListFromNBT(float * a_Floats, int a_NumFloats, const cParsedNBT & a_NBT, int a_TagIdx)
+{
+ if ((a_TagIdx < 0) || (a_NBT.GetType(a_TagIdx) != TAG_List) || (a_NBT.GetChildrenType(a_TagIdx) != TAG_Float))
+ {
+ return false;
+ }
+ int idx = 0;
+ for (int Tag = a_NBT.GetFirstChild(a_TagIdx); (Tag > 0) && (idx < a_NumFloats); Tag = a_NBT.GetNextSibling(Tag), ++idx)
+ {
+ a_Floats[idx] = a_NBT.GetFloat(Tag);
+ } // for Tag - PosTag[]
+ return (idx == a_NumFloats); // Did we read enough doubles?
+}
+
+
+
+
+
bool cWSSAnvil::GetBlockEntityNBTPos(const cParsedNBT & a_NBT, int a_TagIdx, int & a_X, int & a_Y, int & a_Z)
{
int x = a_NBT.FindChildByName(a_TagIdx, "x");
diff --git a/src/WorldStorage/WSSAnvil.h b/src/WorldStorage/WSSAnvil.h
index 541371560..4acf3f2a1 100644
--- a/src/WorldStorage/WSSAnvil.h
+++ b/src/WorldStorage/WSSAnvil.h
@@ -10,6 +10,7 @@
#include "WorldStorage.h"
#include "FastNBT.h"
+#include "../Mobs/Monster.h"
@@ -194,12 +195,18 @@ protected:
/// Loads entity common data from the NBT compound; returns true if successful
bool LoadEntityBaseFromNBT(cEntity & a_Entity, const cParsedNBT & a_NBT, int a_TagIdx);
+ /// Loads monster common data from the NBT compound; returns true if successful
+ bool LoadMonsterBaseFromNBT(cMonster & a_Monster, const cParsedNBT & a_NBT, int a_TagIdx);
+
/// Loads projectile common data from the NBT compound; returns true if successful
bool LoadProjectileBaseFromNBT(cProjectileEntity & a_Entity, const cParsedNBT & a_NBT, int a_TagIx);
/// Loads an array of doubles of the specified length from the specified NBT list tag a_TagIdx; returns true if successful
bool LoadDoublesListFromNBT(double * a_Doubles, int a_NumDoubles, const cParsedNBT & a_NBT, int a_TagIdx);
+ /// Loads an array of floats of the specified length from the specified NBT list tag a_TagIdx; returns true if successful
+ bool LoadFloatsListFromNBT(float * a_Floats, int a_NumFloats, const cParsedNBT & a_NBT, int a_TagIdx);
+
/// Helper function for extracting the X, Y, and Z int subtags of a NBT compound; returns true if successful
bool GetBlockEntityNBTPos(const cParsedNBT & a_NBT, int a_TagIdx, int & a_X, int & a_Y, int & a_Z);
diff --git a/src/main.cpp b/src/main.cpp
index c8cd2d4fe..2ae8a413b 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -30,7 +30,9 @@ bool g_ShouldLogCommOut;
/// If defined, a thorough leak finder will be used (debug MSVC only); leaks will be output to the Output window
-#define ENABLE_LEAK_FINDER
+// _X 2014_02_20: Disabled for canon repo, it makes the debug version too slow in MSVC2013
+// and we haven't had a memory leak for over a year anyway.
+// #define ENABLE_LEAK_FINDER
@@ -241,31 +243,36 @@ int main( int argc, char **argv )
// Check if comm logging is to be enabled:
for (int i = 0; i < argc; i++)
{
+ AString Arg(argv[i]);
if (
- (NoCaseCompare(argv[i], "/commlog") == 0) ||
- (NoCaseCompare(argv[i], "/logcomm") == 0)
+ (NoCaseCompare(Arg, "/commlog") == 0) ||
+ (NoCaseCompare(Arg, "/logcomm") == 0)
)
{
g_ShouldLogCommIn = true;
g_ShouldLogCommOut = true;
}
- if (
- (NoCaseCompare(argv[i], "/commlogin") == 0) ||
- (NoCaseCompare(argv[i], "/comminlog") == 0) ||
- (NoCaseCompare(argv[i], "/logcommin") == 0)
+ else if (
+ (NoCaseCompare(Arg, "/commlogin") == 0) ||
+ (NoCaseCompare(Arg, "/comminlog") == 0) ||
+ (NoCaseCompare(Arg, "/logcommin") == 0)
)
{
g_ShouldLogCommIn = true;
}
- if (
- (NoCaseCompare(argv[i], "/commlogout") == 0) ||
- (NoCaseCompare(argv[i], "/commoutlog") == 0) ||
- (NoCaseCompare(argv[i], "/logcommout") == 0)
+ else if (
+ (NoCaseCompare(Arg, "/commlogout") == 0) ||
+ (NoCaseCompare(Arg, "/commoutlog") == 0) ||
+ (NoCaseCompare(Arg, "/logcommout") == 0)
)
{
g_ShouldLogCommOut = true;
}
- }
+ else if (NoCaseCompare(Arg, "nooutbuf") == 0)
+ {
+ setvbuf(stdout, NULL, _IONBF, 0);
+ }
+ } // for i - argv[]
#if !defined(ANDROID_NDK)
try