summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.travis.yml6
-rwxr-xr-xCIbuild.sh2
-rw-r--r--LICENSE2
-rw-r--r--MCServer/Plugins/APIDump/APIDesc.lua34
-rw-r--r--MCServer/Plugins/APIDump/Classes/Network.lua1
-rw-r--r--MCServer/Plugins/APIDump/Hooks/OnEntityTeleport.lua29
-rw-r--r--MCServer/Plugins/APIDump/main_APIDump.lua110
m---------MCServer/Plugins/Core0
-rw-r--r--MCServer/Plugins/Debuggers/Debuggers.lua130
-rw-r--r--MCServer/Plugins/Debuggers/Info.lua12
-rw-r--r--MCServer/Plugins/HookNotify/HookNotify.lua17
-rw-r--r--MCServer/Plugins/NetworkTest/Info.lua6
-rw-r--r--MCServer/Plugins/NetworkTest/NetworkTest.lua13
-rw-r--r--src/Bindings/AllToLua.pkg81
-rw-r--r--src/Bindings/LuaWindow.cpp18
-rw-r--r--src/Bindings/LuaWindow.h7
-rw-r--r--src/Bindings/ManualBindings.cpp89
-rw-r--r--src/Bindings/ManualBindings_Network.cpp35
-rw-r--r--src/Bindings/Plugin.h5
-rw-r--r--src/Bindings/PluginLua.cpp112
-rw-r--r--src/Bindings/PluginLua.h88
-rw-r--r--src/Bindings/PluginManager.cpp24
-rw-r--r--src/Bindings/PluginManager.h4
-rw-r--r--src/BlockEntities/BeaconEntity.cpp3
-rw-r--r--src/BlockEntities/BlockEntityWithItems.h2
-rw-r--r--src/BlockEntities/ChestEntity.cpp2
-rw-r--r--src/BlockEntities/DropSpenserEntity.cpp1
-rw-r--r--src/BlockEntities/EnderChestEntity.cpp2
-rw-r--r--src/BlockEntities/FurnaceEntity.cpp2
-rw-r--r--src/BlockEntities/HopperEntity.cpp1
-rw-r--r--src/Blocks/BlockAnvil.h1
-rw-r--r--src/Blocks/BlockBed.cpp24
-rw-r--r--src/Blocks/BlockBed.h25
-rw-r--r--src/Blocks/BlockDirt.h20
-rw-r--r--src/Blocks/BlockEnchantmentTable.h2
-rw-r--r--src/Blocks/BlockPiston.cpp16
-rw-r--r--src/Blocks/BlockPiston.h2
-rw-r--r--src/Blocks/BlockWorkbench.h2
-rw-r--r--src/CMakeLists.txt4
-rw-r--r--src/Chunk.cpp18
-rw-r--r--src/ChunkData.cpp2
-rw-r--r--src/ChunkMap.cpp5
-rw-r--r--src/ClientHandle.cpp32
-rw-r--r--src/Endianness.h18
-rw-r--r--src/Entities/ArrowEntity.cpp13
-rw-r--r--src/Entities/Entity.cpp18
-rw-r--r--src/Entities/HangingEntity.cpp55
-rw-r--r--src/Entities/HangingEntity.h86
-rw-r--r--src/Entities/ItemFrame.cpp11
-rw-r--r--src/Entities/ItemFrame.h1
-rw-r--r--src/Entities/Minecart.cpp1
-rw-r--r--src/Entities/Painting.cpp18
-rw-r--r--src/Entities/Painting.h18
-rw-r--r--src/Entities/Player.cpp27
-rw-r--r--src/Entities/Player.h14
-rw-r--r--src/Entities/ProjectileEntity.cpp7
-rw-r--r--src/FastRandom.cpp34
-rw-r--r--src/FastRandom.h6
-rw-r--r--src/FurnaceRecipe.cpp16
-rw-r--r--src/FurnaceRecipe.h3
-rw-r--r--src/Generating/BioGen.cpp2
-rw-r--r--src/Generating/CompoGenBiomal.cpp14
-rw-r--r--src/Generating/ComposableGenerator.cpp5
-rw-r--r--src/Generating/FinishGen.cpp94
-rw-r--r--src/Generating/FinishGen.h23
-rw-r--r--src/Generating/HeiGen.cpp64
-rw-r--r--src/Generating/ProtIntGen.h344
-rw-r--r--src/Generating/Trees.cpp16
-rw-r--r--src/Inventory.h2
-rw-r--r--src/Items/ItemPainting.h19
-rw-r--r--src/Map.cpp4
-rw-r--r--src/Map.h5
-rw-r--r--src/MobSpawner.cpp9
-rw-r--r--src/Mobs/AggressiveMonster.cpp5
-rw-r--r--src/Mobs/Blaze.cpp2
-rw-r--r--src/Mobs/Ghast.cpp2
-rw-r--r--src/Mobs/Skeleton.cpp3
-rw-r--r--src/OSSupport/CMakeLists.txt1
-rw-r--r--src/OSSupport/Network.h3
-rw-r--r--src/OSSupport/NetworkInterfaceEnum.cpp185
-rw-r--r--src/OSSupport/UDPEndpointImpl.cpp9
-rw-r--r--src/OSSupport/UDPEndpointImpl.h2
-rw-r--r--src/Protocol/Protocol17x.cpp2
-rw-r--r--src/Protocol/Protocol18x.cpp14
-rw-r--r--src/Server.cpp2
-rw-r--r--src/Simulator/FloodyFluidSimulator.cpp3
-rw-r--r--src/StringUtils.cpp48
-rw-r--r--src/StringUtils.h5
-rw-r--r--src/UI/AnvilWindow.cpp83
-rw-r--r--src/UI/AnvilWindow.h45
-rw-r--r--src/UI/BeaconWindow.cpp76
-rw-r--r--src/UI/BeaconWindow.h40
-rw-r--r--src/UI/CMakeLists.txt23
-rw-r--r--src/UI/ChestWindow.cpp141
-rw-r--r--src/UI/ChestWindow.h45
-rw-r--r--src/UI/CraftingWindow.cpp61
-rw-r--r--src/UI/CraftingWindow.h31
-rw-r--r--src/UI/DropSpenserWindow.cpp46
-rw-r--r--src/UI/DropSpenserWindow.h32
-rw-r--r--src/UI/EnchantingWindow.cpp100
-rw-r--r--src/UI/EnchantingWindow.h44
-rw-r--r--src/UI/EnderChestWindow.cpp71
-rw-r--r--src/UI/EnderChestWindow.h38
-rw-r--r--src/UI/FurnaceWindow.cpp74
-rw-r--r--src/UI/FurnaceWindow.h32
-rw-r--r--src/UI/HopperWindow.cpp48
-rw-r--r--src/UI/HopperWindow.h32
-rw-r--r--src/UI/InventoryWindow.cpp73
-rw-r--r--src/UI/InventoryWindow.h34
-rw-r--r--src/UI/MinecartWithChestWindow.h67
-rw-r--r--src/UI/SlotArea.cpp130
-rw-r--r--src/UI/SlotArea.h22
-rw-r--r--src/UI/Window.cpp437
-rw-r--r--src/UI/Window.h204
-rw-r--r--src/WebAdmin.h2
-rw-r--r--src/World.cpp111
-rw-r--r--src/World.h54
-rw-r--r--src/WorldStorage/NBTChunkSerializer.cpp52
-rw-r--r--src/WorldStorage/NBTChunkSerializer.h2
-rwxr-xr-xsrc/WorldStorage/WSSAnvil.cpp72
-rwxr-xr-xsrc/WorldStorage/WSSAnvil.h1
-rw-r--r--src/main.cpp4
122 files changed, 3276 insertions, 1275 deletions
diff --git a/.travis.yml b/.travis.yml
index 84b963a89..2fd3bf991 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,7 +1,5 @@
language: cpp
-compiler:
- - gcc
- - clang
+compiler: clang
before_install:
- if [ "$TRAVIS_MCSERVER_BUILD_TYPE" == "COVERAGE" ]; then sudo pip install cpp_coveralls; fi
@@ -29,8 +27,6 @@ after_success:
env:
- TRAVIS_MCSERVER_BUILD_TYPE=RELEASE MCSERVER_PATH=./MCServer
- TRAVIS_MCSERVER_BUILD_TYPE=DEBUG MCSERVER_PATH=./MCServer_debug
- - TRAVIS_MCSERVER_BUILD_TYPE=RELEASE TRAVIS_MCSERVER_FORCE32=1 MCSERVER_PATH=./MCServer
- - TRAVIS_MCSERVER_BUILD_TYPE=DEBUG TRAVIS_MCSERVER_FORCE32=1 MCSERVER_PATH=./MCServer_debug
matrix:
include:
diff --git a/CIbuild.sh b/CIbuild.sh
index 755dc7daa..f5a9005da 100755
--- a/CIbuild.sh
+++ b/CIbuild.sh
@@ -11,7 +11,7 @@ cd src
lua CheckBasicStyle.lua
cd ..
make -j 2;
-make -j 2 test;
+make -j 2 test ARGS="-V";
cd MCServer/;
if [ "$TRAVIS_MCSERVER_BUILD_TYPE" != "COVERAGE" ]
then echo stop | $MCSERVER_PATH;
diff --git a/LICENSE b/LICENSE
index 69b3c7390..d8a8ec1e4 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,7 +1,7 @@
MCServer: A performant C++ Minecraft Server
www: http://mc-server.org/
-Copyright 2014 MCServer Team
+Copyright 2011-2015 MCServer Team
------
diff --git a/MCServer/Plugins/APIDump/APIDesc.lua b/MCServer/Plugins/APIDump/APIDesc.lua
index f2ec2546a..92cdb7415 100644
--- a/MCServer/Plugins/APIDump/APIDesc.lua
+++ b/MCServer/Plugins/APIDump/APIDesc.lua
@@ -925,8 +925,8 @@ local Hash = cCryptoHash.sha1HexString("DataToHash")
{ Params = "DamageType, AttackerEntity, RawDamage, KnockbackAmount", Return = "", Notes = "Causes this entity to take damage of the specified type, from the specified attacker (may be nil). The final damage is calculated from RawDamage using the currently equipped armor." },
{ Params = "DamageType, ArrackerEntity, RawDamage, FinalDamage, KnockbackAmount", Return = "", Notes = "Causes this entity to take damage of the specified type, from the specified attacker (may be nil). The values are wrapped into a {{TakeDamageInfo}} structure and applied directly." },
},
- TeleportToCoords = { Params = "PosX, PosY, PosZ", Return = "", Notes = "Teleports the entity to the specified coords." },
- TeleportToEntity = { Params = "DestEntity", Return = "", Notes = "Teleports this entity to the specified destination entity." },
+ TeleportToCoords = { Params = "PosX, PosY, PosZ", Return = "", Notes = "Teleports the entity to the specified coords. Asks plugins if the teleport is allowed." },
+ TeleportToEntity = { Params = "DestEntity", Return = "", Notes = "Teleports this entity to the specified destination entity. Asks plugins if the teleport is allowed." },
},
Constants =
{
@@ -2871,41 +2871,18 @@ end
This class represents the tolua bridge between the Lua API and MCServer. It supports some low
level operations and queries on the objects. See also the tolua++'s documentation at
{{http://www.codenix.com/~tolua/tolua++.html#utilities}}. Normally you shouldn't use any of these
- functions except for cast() and type()
+ functions except for type()
]],
Functions =
{
- cast = { Params = "Object, TypeStr", Return = "Object", Notes = "Casts the object to the specified type through the inheritance hierarchy." },
+ cast = { Params = "Object, TypeStr", Return = "Object", Notes = "Casts the object to the specified type.<br/><b>Note:</b> This is a potentially unsafe operation and it could crash the server. There is normally no need to use this function at all, so don't use it unless you know exactly what you're doing." },
getpeer = { Params = "", Return = "", Notes = "" },
inherit = { Params = "", Return = "", Notes = "" },
releaseownership = { Params = "", Return = "", Notes = "" },
setpeer = { Params = "", Return = "", Notes = "" },
takeownership = { Params = "", Return = "", Notes = "" },
- type = { Params = "Object", Return = "TypeStr", Notes = "Returns a string representing the type of the object. This works similar to Lua's built-in type() function, but recognizes the underlying C++ types, too." },
+ type = { Params = "Object", Return = "TypeStr", Notes = "Returns a string representing the type of the object. This works similar to Lua's built-in type() function, but recognizes the underlying C++ classes, too." },
},
- AdditionalInfo =
- {
- {
- Header = "Usage example",
- Contents =
- [[
- The tolua.cast() function is normally used to cast between related types. For example in the
- hook callbacks you often receive a generic {{cEntity}} object, when in fact you know that the
- object is a {{cMonster}}. You can cast the object to access its cMonster functions:
-<pre class="prettyprint lang-lua">
-function OnTakeDamage(a_ReceiverEntity, TDI)
- if (a_ReceiverEntity.IsMob()) then
- local Mob = tolua.cast(a_ReceiverEntity, "cMonster"); -- Cast a_ReceiverEntity into a {{cMonster}} instance
- if (Mob:GetMonsterType() == cMonster.mtSheep) then
- local Sheep = tolua.cast(Mob, "cSheep"); -- Cast Mob into a {{cSheep}} instance
- -- Do something sheep-specific
- end
- end
-end
-</pre>
- ]],
- }
- } -- AdditionalInfo
}, -- tolua
Globals =
@@ -2965,6 +2942,7 @@ end
RotateBlockFaceCW = { Params = "{{Globals#BlockFaces|eBlockFace}}", Return = "{{Globals#BlockFaces|eBlockFace}}", Notes = "Returns the {{Globals#BlockFaces|eBlockFace}} that corresponds to the given {{Globals#BlockFaces|eBlockFace}} after rotating it around the Y axis 90 degrees clockwise." },
StringSplit = {Params = "string, SeperatorsString", Return = "array table of strings", Notes = "Seperates string into multiple by splitting every time any of the characters in SeperatorsString is encountered."},
StringSplitAndTrim = {Params = "string, SeperatorsString", Return = "array table of strings", Notes = "Seperates string into multiple by splitting every time any of the characters in SeperatorsString is encountered. Each of the separate strings is trimmed (whitespace removed from the beginning and end of the string)"},
+ StringSplitWithQuotes = {Params = "string, SeperatorsString", Return = "array table of strings", Notes = "Seperates string into multiple by splitting every time any of the characters in SeperatorsString is encountered. Whitespace wrapped with single or double quotes will be ignored"},
StringToBiome = {Params = "string", Return = "{{Globals#BiomeTypes|BiomeType}}", Notes = "Converts a string representation to a {{Globals#BiomeTypes|BiomeType}} enumerated value"},
StringToDamageType = {Params = "string", Return = "{{Globals#DamageType|DamageType}}", Notes = "Converts a string representation to a {{Globals#DamageType|DamageType}} enumerated value."},
StringToDimension = {Params = "string", Return = "{{Globals#WorldDimension|Dimension}}", Notes = "Converts a string representation to a {{Globals#WorldDimension|Dimension}} enumerated value"},
diff --git a/MCServer/Plugins/APIDump/Classes/Network.lua b/MCServer/Plugins/APIDump/Classes/Network.lua
index 797788661..c7626562d 100644
--- a/MCServer/Plugins/APIDump/Classes/Network.lua
+++ b/MCServer/Plugins/APIDump/Classes/Network.lua
@@ -289,6 +289,7 @@ g_Server = nil
{
Connect = { Params = "Host, Port, LinkCallbacks", Return = "bool", Notes = "(STATIC) Begins establishing a (client) TCP connection to the specified host. Uses the LinkCallbacks table to report progress, success, errors and incoming data. Returns false if it fails immediately (bad port value, bad hostname format), true otherwise. Host can be either an IP address or a hostname." },
CreateUDPEndpoint = { Params = "Port, UDPCallbacks", Return = "{{cUDPEndpoint|UDPEndpoint}}", Notes = "(STATIC) Creates a UDP endpoint that listens for incoming datagrams on the specified port, and can be used to send or broadcast datagrams. Uses the UDPCallbacks to report incoming datagrams or errors. If the endpoint cannot be created, the OnError callback is called with the error details and the returned endpoint will report IsOpen() == false. The plugin needs to store the returned endpoint object for as long as it needs the UDP port open; if the endpoint is garbage-collected by Lua, the socket will be closed and no more incoming data will be reported.<br>If the Port is zero, the OS chooses an available UDP port for the endpoint; use {{cUDPEndpoint}}:GetPort() to query the port number in such case." },
+ EnumLocalIPAddresses = { Params = "", Return = "array-table of strings", Notes = "(STATIC) Returns all local IP addresses for network interfaces currently available on the machine." },
HostnameToIP = { Params = "Host, LookupCallbacks", Return = "bool", Notes = "(STATIC) Begins a DNS lookup to find the IP address(es) for the specified host. Uses the LookupCallbacks table to report progress, success or errors. Returns false if it fails immediately (bad hostname format), true if the lookup started successfully. Host can be either a hostname or an IP address." },
IPToHostname = { Params = "Address, LookupCallbacks", Return = "bool", Notes = "(STATIC) Begins a reverse-DNS lookup to find out the hostname for the specified IP address. Uses the LookupCallbacks table to report progress, success or errors. Returns false if it fails immediately (bad address format), true if the lookup started successfully." },
Listen = { Params = "Port, ListenCallbacks", Return = "{{cServerHandle|ServerHandle}}", Notes = "(STATIC) Starts listening on the specified port. Uses the ListenCallbacks to report incoming connections or errors. Returns a {{cServerHandle}} object representing the server. If the listen operation failed, the OnError callback is called with the error details and the returned server handle will report IsListening() == false. The plugin needs to store the server handle object for as long as it needs the server running, if the server handle is garbage-collected by Lua, the listening socket will be closed and all current connections dropped." },
diff --git a/MCServer/Plugins/APIDump/Hooks/OnEntityTeleport.lua b/MCServer/Plugins/APIDump/Hooks/OnEntityTeleport.lua
new file mode 100644
index 000000000..cdeb0947f
--- /dev/null
+++ b/MCServer/Plugins/APIDump/Hooks/OnEntityTeleport.lua
@@ -0,0 +1,29 @@
+return
+{
+ HOOK_ENTITY_TELEPORT =
+ {
+ CalledWhen = "Any entity teleports. Plugin may refuse teleport.",
+ DefaultFnName = "OnEntityTeleport", -- also used as pagename
+ Desc = [[
+ This function is called in each server tick for each {{cEntity|Entity}} that has
+ teleported. Plugins may refuse the teleport.
+ ]],
+ Params =
+ {
+ { Name = "Entity", Type = "{{cEntity}}", Notes = "The entity who has teleported. New position is set in the object after successfull teleport" },
+ { Name = "OldPosition", Type = "{{Vector3d}}", Notes = "The old position." },
+ { Name = "NewPosition", Type = "{{Vector3d}}", Notes = "The new position." },
+ },
+ Returns = [[
+ If the function returns true, teleport is prohibited.</p>
+ <p>
+ If the function returns false or no value, other plugins' callbacks are called and finally the new
+ position is permanently stored in the cEntity object.</p>
+ ]],
+ }, -- HOOK_ENTITY_TELEPORT
+}
+
+
+
+
+
diff --git a/MCServer/Plugins/APIDump/main_APIDump.lua b/MCServer/Plugins/APIDump/main_APIDump.lua
index a25bab9cf..239bec69c 100644
--- a/MCServer/Plugins/APIDump/main_APIDump.lua
+++ b/MCServer/Plugins/APIDump/main_APIDump.lua
@@ -62,7 +62,7 @@ local function CreateAPITables()
Variables = {
},
Descendants = {}, -- Will be filled by ReadDescriptions(), array of class APIs (references to other member in the tree)
- }},
+ },
{
Name = "cBlockArea",
Functions = {
@@ -78,7 +78,9 @@ local function CreateAPITables()
Variables = {
},
...
- }}
+ },
+
+ cCuboid = {} -- Each array item also has the map item by its name
};
local Globals = {
Functions = {
@@ -135,7 +137,9 @@ local function CreateAPITables()
(v ~= g_APIDesc)
) then
if (type(v) == "table") then
- table.insert(API, ParseClass(i, v));
+ local cls = ParseClass(i, v)
+ table.insert(API, cls);
+ API[cls.Name] = cls
else
Add(Globals, i, v);
end
@@ -1449,6 +1453,103 @@ end
+--- Returns true if a_Descendant is declared to be a (possibly indirect) descendant of a_Base
+local function IsDeclaredDescendant(a_DescendantName, a_BaseName, a_API)
+ -- Check params:
+ assert(type(a_DescendantName) == "string")
+ assert(type(a_BaseName) == "string")
+ assert(type(a_API) == "table")
+ if not(a_API[a_BaseName]) then
+ return false
+ end
+ assert(type(a_API[a_BaseName]) == "table", "Not a class name: " .. a_BaseName)
+ assert(type(a_API[a_BaseName].Descendants) == "table")
+
+ -- Check direct inheritance:
+ for _, desc in ipairs(a_API[a_BaseName].Descendants) do
+ if (desc.Name == a_DescendantName) then
+ return true
+ end
+ end -- for desc - a_BaseName's descendants
+
+ -- Check indirect inheritance:
+ for _, desc in ipairs(a_API[a_BaseName].Descendants) do
+ if (IsDeclaredDescendant(a_DescendantName, desc.Name, a_API)) then
+ return true
+ end
+ end -- for desc - a_BaseName's descendants
+
+ return false
+end
+
+
+
+
+
+--- Checks the specified class' inheritance
+-- Reports any problems as new items in the a_Report table
+local function CheckClassInheritance(a_Class, a_API, a_Report)
+ -- Check params:
+ assert(type(a_Class) == "table")
+ assert(type(a_API) == "table")
+ assert(type(a_Report) == "table")
+
+ -- Check that the declared descendants are really descendants:
+ local registry = debug.getregistry()
+ for _, desc in ipairs(a_Class.Descendants or {}) do
+ local isParent = false
+ local parents = registry["tolua_super"][_G[desc.Name]]
+ if not(parents[a_Class.Name]) then
+ table.insert(a_Report, desc.Name .. " is not a descendant of " .. a_Class.Name)
+ end
+ end -- for desc - a_Class.Descendants[]
+
+ -- Check that all inheritance is listed for the class:
+ local parents = registry["tolua_super"][_G[a_Class.Name]] -- map of "classname" -> true for each class that a_Class inherits
+ for clsName, isParent in pairs(parents or {}) do
+ if ((clsName ~= "") and not(clsName:match("const .*"))) then
+ if not(IsDeclaredDescendant(a_Class.Name, clsName, a_API)) then
+ table.insert(a_Report, a_Class.Name .. " inherits from " .. clsName .. " but this isn't documented")
+ end
+ end
+ end
+end
+
+
+
+
+
+--- Checks each class's declared inheritance versus the actual inheritance
+local function CheckAPIDescendants(a_API)
+ -- Check each class:
+ local report = {}
+ for _, cls in ipairs(a_API) do
+ if (cls.Name ~= "Globals") then
+ CheckClassInheritance(cls, a_API, report)
+ end
+ end
+
+ -- If there's anything to report, output it to a file:
+ if (report[1] ~= nil) then
+ LOG("There are inheritance errors in the API description:")
+ for _, msg in ipairs(report) do
+ LOG(" " .. msg)
+ end
+
+ local f, err = io.open("API/_inheritance_errors.txt", "w")
+ if (f == nil) then
+ LOG("Cannot report inheritance problems to a file: " .. tostring(err))
+ return
+ end
+ f:write(table.concat(report, "\n"))
+ f:close()
+ end
+end
+
+
+
+
+
local function DumpApi()
LOG("Dumping the API...")
@@ -1501,6 +1602,9 @@ local function DumpApi()
LOG("Reading descriptions...");
ReadDescriptions(API);
+ -- Check that the API lists the inheritance properly, report any problems to a file:
+ CheckAPIDescendants(API)
+
-- Dump all available API objects in HTML format into a subfolder:
DumpAPIHtml(API);
diff --git a/MCServer/Plugins/Core b/MCServer/Plugins/Core
-Subproject 6e9d612b9eb548d888d2bf986488aad762a99be
+Subproject ee3cd9ba917baa94d6b9bfe7c9205609e0722fa
diff --git a/MCServer/Plugins/Debuggers/Debuggers.lua b/MCServer/Plugins/Debuggers/Debuggers.lua
index c8069a411..d0c362ab4 100644
--- a/MCServer/Plugins/Debuggers/Debuggers.lua
+++ b/MCServer/Plugins/Debuggers/Debuggers.lua
@@ -1643,6 +1643,81 @@ end
+--- Monitors the state of the "inh" entity-spawning hook
+-- if false, the hook is installed before the "inh" command processing
+local isInhHookInstalled = false
+
+function HandleConsoleInh(a_Split, a_FullCmd)
+ -- Check the param:
+ local kindStr = a_Split[2] or "pkArrow"
+ local kind = cProjectileEntity[kindStr]
+ if (kind == nil) then
+ return true, "There's no projectile kind '" .. kindStr .. "'."
+ end
+
+ -- Get the world to test in:
+ local world = cRoot:Get():GetDefaultWorld()
+ if (world == nil) then
+ return true, "Cannot test inheritance, no default world"
+ end
+
+ -- Install the hook, if needed:
+ if not(isInhHookInstalled) then
+ cPluginManager:AddHook(cPluginManager.HOOK_SPAWNING_ENTITY,
+ function (a_CBWorld, a_CBEntity)
+ LOG("New entity is spawning:")
+ LOG(" Lua type: '" .. type(a_CBEntity) .. "'")
+ LOG(" ToLua type: '" .. tolua.type(a_CBEntity) .. "'")
+ LOG(" GetEntityType(): '" .. a_CBEntity:GetEntityType() .. "'")
+ LOG(" GetClass(): '" .. a_CBEntity:GetClass() .. "'")
+ end
+ )
+ isInhHookInstalled = true
+ end
+
+ -- Create the projectile:
+ LOG("Creating a " .. kindStr .. " projectile in world " .. world:GetName() .. "...")
+ local msg
+ world:ChunkStay({{0, 0}},
+ nil,
+ function ()
+ -- Create a projectile at {8, 100, 8}:
+ local entityID = world:CreateProjectile(8, 100, 8, kind, nil, nil)
+ if (entityID < 0) then
+ msg = "Cannot test inheritance, projectile creation failed."
+ return
+ end
+ LOG("Entity created, ID #" .. entityID)
+
+ -- Call a function on the newly created entity:
+ local hasExecutedCallback = false
+ world:DoWithEntityByID(
+ entityID,
+ function (a_CBEntity)
+ LOG("Projectile created and found using the DoWithEntityByID() callback")
+ LOG("Lua type: '" .. type(a_CBEntity) .. "'")
+ LOG("ToLua type: '" .. tolua.type(a_CBEntity) .. "'")
+ LOG("GetEntityType(): '" .. a_CBEntity:GetEntityType() .. "'")
+ LOG("GetClass(): '" .. a_CBEntity:GetClass() .. "'")
+ hasExecutedCallback = true
+ end
+ )
+ if not(hasExecutedCallback) then
+ msg = "The callback failed to execute"
+ return
+ end
+
+ msg = "Inheritance test finished"
+ end
+ )
+
+ return true, msg
+end
+
+
+
+
+
function HandleConsoleLoadChunk(a_Split)
-- Check params:
local numParams = #a_Split
@@ -1741,3 +1816,58 @@ end
+
+function HandleConsoleBBox(a_Split)
+ local bbox = cBoundingBox(0, 10, 0, 10, 0, 10)
+ local v1 = Vector3d(1, 1, 1)
+ local v2 = Vector3d(5, 5, 5)
+ local v3 = Vector3d(11, 11, 11)
+
+ if (bbox:IsInside(v1)) then
+ LOG("v1 is inside bbox")
+ else
+ LOG("v1 is not inside bbox")
+ end
+
+ if (bbox:IsInside(v2)) then
+ LOG("v2 is inside bbox")
+ else
+ LOG("v2 is not inside bbox")
+ end
+
+ if (bbox:IsInside(v3)) then
+ LOG("v3 is inside bbox")
+ else
+ LOG("v3 is not inside bbox")
+ end
+
+ if (bbox:IsInside(v1, v2)) then
+ LOG("v1*v2 is inside bbox")
+ else
+ LOG("v1*v2 is not inside bbox")
+ end
+
+ if (bbox:IsInside(v2, v1)) then
+ LOG("v2*v1 is inside bbox")
+ else
+ LOG("v2*v1 is not inside bbox")
+ end
+
+ if (bbox:IsInside(v1, v3)) then
+ LOG("v1*v3 is inside bbox")
+ else
+ LOG("v1*v3 is not inside bbox")
+ end
+
+ if (bbox:IsInside(v2, v3)) then
+ LOG("v2*v3 is inside bbox")
+ else
+ LOG("v2*v3 is not inside bbox")
+ end
+
+ return true
+end
+
+
+
+
diff --git a/MCServer/Plugins/Debuggers/Info.lua b/MCServer/Plugins/Debuggers/Info.lua
index 63a4b9177..0370145df 100644
--- a/MCServer/Plugins/Debuggers/Info.lua
+++ b/MCServer/Plugins/Debuggers/Info.lua
@@ -200,12 +200,24 @@ g_PluginInfo =
ConsoleCommands =
{
+ ["bbox"] =
+ {
+ Handler = HandleConsoleBBox,
+ HelpString = "Performs cBoundingBox API tests",
+ },
+
["hash"] =
{
Handler = HandleConsoleHash,
HelpString = "Tests the crypto hashing functions",
},
+ ["inh"] =
+ {
+ Handler = HandleConsoleInh,
+ HelpString = "Tests the bindings of the cEntity inheritance",
+ },
+
["loadchunk"] =
{
Handler = HandleConsoleLoadChunk,
diff --git a/MCServer/Plugins/HookNotify/HookNotify.lua b/MCServer/Plugins/HookNotify/HookNotify.lua
index ed791090e..1d3d5088e 100644
--- a/MCServer/Plugins/HookNotify/HookNotify.lua
+++ b/MCServer/Plugins/HookNotify/HookNotify.lua
@@ -23,6 +23,7 @@ function Initialize(Plugin)
cPluginManager.AddHook(cPluginManager.HOOK_COLLECTING_PICKUP, OnCollectingPickup);
cPluginManager.AddHook(cPluginManager.HOOK_CRAFTING_NO_RECIPE, OnCraftingNoRecipe);
cPluginManager.AddHook(cPluginManager.HOOK_DISCONNECT, OnDisconnect);
+ cPluginManager.AddHook(cPluginManager.HOOK_ENTITY_TELEPORT, OnEntityTeleport);
cPluginManager.AddHook(cPluginManager.HOOK_EXECUTE_COMMAND, OnExecuteCommand);
cPluginManager.AddHook(cPluginManager.HOOK_HANDSHAKE, OnHandshake);
cPluginManager.AddHook(cPluginManager.HOOK_KILLING, OnKilling);
@@ -179,6 +180,22 @@ end
+function OnEntityTeleport(arg1,arg2,arg3)
+ if arg1.IsPlayer() then
+ -- if it's a player, get his name
+ LOG("OnEntityTeleport: Player: " .. arg1.GetName());
+ else
+ -- if it's a entity, get its type
+ LOG("OnEntityTeleport: EntityType: " .. arg1.GetEntityType());
+ end
+ LOG("OldPos: " .. arg2.x .. " / " .. arg2.y .. " / " .. arg2.z);
+ LOG("NewPos: " .. arg3.x .. " / " .. arg3.y .. " / " .. arg3.z);
+end
+
+
+
+
+
function OnExecuteCommand(...)
LogHook("OnExecuteCommand", unpack(arg));
diff --git a/MCServer/Plugins/NetworkTest/Info.lua b/MCServer/Plugins/NetworkTest/Info.lua
index 52422d427..d8c3095fe 100644
--- a/MCServer/Plugins/NetworkTest/Info.lua
+++ b/MCServer/Plugins/NetworkTest/Info.lua
@@ -50,6 +50,12 @@ g_PluginInfo =
}, -- ParameterCombinations
}, -- close
+ ips =
+ {
+ HelpString = "Prints all locally available IP addresses",
+ Handler = HandleConsoleNetIps,
+ }, -- ips
+
listen =
{
HelpString = "Creates a new listening socket on the specified port with the specified service attached to it",
diff --git a/MCServer/Plugins/NetworkTest/NetworkTest.lua b/MCServer/Plugins/NetworkTest/NetworkTest.lua
index daab0a4bf..22056d4e9 100644
--- a/MCServer/Plugins/NetworkTest/NetworkTest.lua
+++ b/MCServer/Plugins/NetworkTest/NetworkTest.lua
@@ -288,6 +288,19 @@ end
+function HandleConsoleNetIps(a_Split)
+ local Addresses = cNetwork:EnumLocalIPAddresses()
+ LOG("IP addresses enumerated, " .. #Addresses .. " found")
+ for idx, addr in ipairs(Addresses) do
+ LOG(" IP #" .. idx .. ": " .. addr)
+ end
+ return true
+end
+
+
+
+
+
function HandleConsoleNetLookup(a_Split)
-- Get the name to look up:
local Addr = a_Split[3] or "google.com"
diff --git a/src/Bindings/AllToLua.pkg b/src/Bindings/AllToLua.pkg
index 7e174e770..8f55eba07 100644
--- a/src/Bindings/AllToLua.pkg
+++ b/src/Bindings/AllToLua.pkg
@@ -1,4 +1,19 @@
+// AllToLua.pkg
+
+// Defines the bindings that are exported to Lua by the ToLua processor
+
+/*
+ Each file referenced in the $cfile is parsed by ToLua, and bindings are generated for classes and functions
+marked with "// tolua_export", or between the "// tolua_begin" and "// tolua_end" markers.
+
+ Note that if class D inherits from class B, then class B needs to be parsed before class D, otherwise the
+inheritance doesn't work properly (#1789).
+*/
+
+
+
+
$#include "../Globals.h"
// Typedefs from Globals.h, so that we don't have to process that file:
@@ -27,14 +42,38 @@ $cfile "WebPlugin.h"
$cfile "LuaWindow.h"
$cfile "../BlockID.h"
-$cfile "../Mobs/MonsterTypes.h"
$cfile "../BlockInfo.h"
$cfile "../StringUtils.h"
$cfile "../Defines.h"
$cfile "../ChatColor.h"
$cfile "../ClientHandle.h"
-$cfile "../Entities/ArrowEntity.h"
+$cfile "../Server.h"
+$cfile "../World.h"
+$cfile "../Inventory.h"
+$cfile "../Enchantments.h"
+$cfile "../Item.h"
+$cfile "../ItemGrid.h"
+$cfile "../WebAdmin.h"
+$cfile "../Root.h"
+$cfile "../Cuboid.h"
+$cfile "../BoundingBox.h"
+$cfile "../Tracer.h"
+$cfile "../BlockArea.h"
+$cfile "../Generating/ChunkDesc.h"
+$cfile "../CraftingRecipes.h"
+$cfile "../UI/Window.h"
+$cfile "../CompositeChat.h"
+$cfile "../Map.h"
+$cfile "../MapManager.h"
+$cfile "../Scoreboard.h"
+$cfile "../Statistics.h"
+$cfile "../Protocol/MojangAPI.h"
+
+// Entities:
$cfile "../Entities/Entity.h"
+$cfile "../Entities/Pawn.h"
+$cfile "../Entities/ProjectileEntity.h"
+$cfile "../Entities/ArrowEntity.h"
$cfile "../Entities/EntityEffect.h"
$cfile "../Entities/ExpBottleEntity.h"
$cfile "../Entities/FireChargeEntity.h"
@@ -43,25 +82,21 @@ $cfile "../Entities/Floater.h"
$cfile "../Entities/GhastFireballEntity.h"
$cfile "../Entities/HangingEntity.h"
$cfile "../Entities/ItemFrame.h"
-$cfile "../Entities/Pawn.h"
$cfile "../Entities/Player.h"
$cfile "../Entities/Painting.h"
$cfile "../Entities/Pickup.h"
-$cfile "../Entities/ProjectileEntity.h"
$cfile "../Entities/SplashPotionEntity.h"
$cfile "../Entities/ThrownEggEntity.h"
$cfile "../Entities/ThrownEnderPearlEntity.h"
$cfile "../Entities/ThrownSnowballEntity.h"
$cfile "../Entities/TNTEntity.h"
$cfile "../Entities/WitherSkullEntity.h"
-$cfile "../Server.h"
-$cfile "../World.h"
-$cfile "../Inventory.h"
-$cfile "../Enchantments.h"
-$cfile "../Item.h"
-$cfile "../ItemGrid.h"
-$cfile "../BlockEntities/BeaconEntity.h"
+$cfile "../Mobs/MonsterTypes.h"
+$cfile "../Mobs/Monster.h"
+
+// Block entities:
$cfile "../BlockEntities/BlockEntity.h"
+$cfile "../BlockEntities/BeaconEntity.h"
$cfile "../BlockEntities/BlockEntityWithItems.h"
$cfile "../BlockEntities/ChestEntity.h"
$cfile "../BlockEntities/CommandBlockEntity.h"
@@ -76,30 +111,6 @@ $cfile "../BlockEntities/SignEntity.h"
$cfile "../BlockEntities/MobHeadEntity.h"
$cfile "../BlockEntities/MobSpawnerEntity.h"
$cfile "../BlockEntities/FlowerPotEntity.h"
-$cfile "../WebAdmin.h"
-$cfile "../Root.h"
-$cfile "../Cuboid.h"
-$cfile "../BoundingBox.h"
-$cfile "../Tracer.h"
-$cfile "../BlockArea.h"
-$cfile "../Generating/ChunkDesc.h"
-$cfile "../CraftingRecipes.h"
-$cfile "../UI/Window.h"
-$cfile "../Mobs/Monster.h"
-$cfile "../CompositeChat.h"
-$cfile "../Map.h"
-$cfile "../MapManager.h"
-$cfile "../Scoreboard.h"
-$cfile "../Statistics.h"
-$cfile "../Protocol/MojangAPI.h"
-
-
-
-
-
-// Need to declare this class so that the usertype is properly registered in Bindings.cpp -
-// it seems impossible to register a usertype in ManualBindings.cpp
-class cLineBlockTracer;
diff --git a/src/Bindings/LuaWindow.cpp b/src/Bindings/LuaWindow.cpp
index 35730878d..d4014059b 100644
--- a/src/Bindings/LuaWindow.cpp
+++ b/src/Bindings/LuaWindow.cpp
@@ -167,6 +167,24 @@ void cLuaWindow::Destroy(void)
+void cLuaWindow::DistributeStack(cItem & a_ItemStack, int a_Slot, cPlayer& a_Player, cSlotArea * a_ClickedArea, bool a_ShouldApply)
+{
+ cSlotAreas Areas;
+ for (auto Area : m_SlotAreas)
+ {
+ if (Area != a_ClickedArea)
+ {
+ Areas.push_back(Area);
+ }
+ }
+
+ super::DistributeStackToAreas(a_ItemStack, a_Player, Areas, a_ShouldApply, false);
+}
+
+
+
+
+
void cLuaWindow::OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum)
{
if (a_ItemGrid != &m_Contents)
diff --git a/src/Bindings/LuaWindow.h b/src/Bindings/LuaWindow.h
index dab99a2e2..d4fc58660 100644
--- a/src/Bindings/LuaWindow.h
+++ b/src/Bindings/LuaWindow.h
@@ -35,8 +35,10 @@ This reference needs to be unreferenced in the Destroy() function.
*/
// tolua_begin
class cLuaWindow :
- public cWindow,
- public cItemGrid::cListener
+ public cWindow
+ // tolua_end
+ , public cItemGrid::cListener
+ // tolua_begin
{
typedef cWindow super;
@@ -84,6 +86,7 @@ protected:
// cWindow overrides:
virtual bool ClosedByPlayer(cPlayer & a_Player, bool a_CanRefuse) override;
virtual void Destroy(void) override;
+ virtual void DistributeStack(cItem & a_ItemStack, int a_Slot, cPlayer & a_Player, cSlotArea * a_ClickedArea, bool a_ShouldApply) override;
// cItemGrid::cListener overrides:
virtual void OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) override;
diff --git a/src/Bindings/ManualBindings.cpp b/src/Bindings/ManualBindings.cpp
index a6ae4869b..cdc01ae09 100644
--- a/src/Bindings/ManualBindings.cpp
+++ b/src/Bindings/ManualBindings.cpp
@@ -266,6 +266,24 @@ static int tolua_StringSplit(lua_State * tolua_S)
+static int tolua_StringSplitWithQuotes(lua_State * tolua_S)
+{
+ cLuaState S(tolua_S);
+
+ AString str;
+ AString delim;
+
+ S.GetStackValues(1, str, delim);
+
+ AStringVector Split = StringSplitWithQuotes(str, delim);
+ S.Push(Split);
+ return 1;
+}
+
+
+
+
+
static int tolua_StringSplitAndTrim(lua_State * tolua_S)
{
cLuaState LuaState(tolua_S);
@@ -620,11 +638,11 @@ static int tolua_DoWithID(lua_State* tolua_S)
private:
virtual bool Item(Ty2 * a_Item) override
{
- lua_rawgeti(LuaState, LUA_REGISTRYINDEX, FuncRef); // Push function to call
- tolua_pushusertype(LuaState, a_Item, Ty2::GetClassStatic()); // Push the item
+ lua_rawgeti(LuaState, LUA_REGISTRYINDEX, FuncRef); // Push function to call
+ tolua_pushusertype(LuaState, a_Item, a_Item->GetClass()); // Push the item
if (TableRef != LUA_REFNIL)
{
- lua_rawgeti(LuaState, LUA_REGISTRYINDEX, TableRef); // Push the optional callbackdata param
+ lua_rawgeti(LuaState, LUA_REGISTRYINDEX, TableRef); // Push the optional callbackdata param
}
int s = lua_pcall(LuaState, (TableRef == LUA_REFNIL ? 1 : 2), 1, 0);
@@ -1281,23 +1299,27 @@ tolua_lerror:
class cLuaWorldTask :
- public cWorld::cTask
+ public cWorld::cTask,
+ public cPluginLua::cResettable
{
public:
cLuaWorldTask(cPluginLua & a_Plugin, int a_FnRef) :
- m_Plugin(a_Plugin),
+ cPluginLua::cResettable(a_Plugin),
m_FnRef(a_FnRef)
{
}
protected:
- cPluginLua & m_Plugin;
int m_FnRef;
// cWorld::cTask overrides:
virtual void Run(cWorld & a_World) override
{
- m_Plugin.Call(m_FnRef, &a_World);
+ cCSLock Lock(m_CSPlugin);
+ if (m_Plugin != nullptr)
+ {
+ m_Plugin->Call(m_FnRef, &a_World);
+ }
}
} ;
@@ -1336,7 +1358,9 @@ static int tolua_cWorld_QueueTask(lua_State * tolua_S)
return lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get function reference of parameter #1");
}
- self->QueueTask(make_unique<cLuaWorldTask>(*Plugin, FnRef));
+ auto task = std::make_shared<cLuaWorldTask>(*Plugin, FnRef);
+ Plugin->AddResettable(task);
+ self->QueueTask(task);
return 0;
}
@@ -1345,23 +1369,27 @@ static int tolua_cWorld_QueueTask(lua_State * tolua_S)
class cLuaScheduledWorldTask :
- public cWorld::cTask
+ public cWorld::cTask,
+ public cPluginLua::cResettable
{
public:
cLuaScheduledWorldTask(cPluginLua & a_Plugin, int a_FnRef) :
- m_Plugin(a_Plugin),
+ cPluginLua::cResettable(a_Plugin),
m_FnRef(a_FnRef)
{
}
protected:
- cPluginLua & m_Plugin;
int m_FnRef;
// cWorld::cTask overrides:
virtual void Run(cWorld & a_World) override
{
- m_Plugin.Call(m_FnRef, &a_World);
+ cCSLock Lock(m_CSPlugin);
+ if (m_Plugin != nullptr)
+ {
+ m_Plugin->Call(m_FnRef, &a_World);
+ }
}
};
@@ -1407,7 +1435,9 @@ static int tolua_cWorld_ScheduleTask(lua_State * tolua_S)
int DelayTicks = (int)tolua_tonumber(tolua_S, 2, 0);
- World->ScheduleTask(DelayTicks, new cLuaScheduledWorldTask(*Plugin, FnRef));
+ auto task = std::make_shared<cLuaScheduledWorldTask>(*Plugin, FnRef);
+ Plugin->AddResettable(task);
+ World->ScheduleTask(DelayTicks, task);
return 0;
}
@@ -2392,10 +2422,10 @@ static int tolua_md5HexString(lua_State * tolua_S)
// Convert the md5 checksum to hex string:
std::stringstream Output;
- Output << std::hex << std::setw(2) << std::setfill('0');
+ Output << std::hex << std::setfill('0');
for (size_t i = 0; i < ARRAYCOUNT(md5Output); i++)
{
- Output << static_cast<unsigned short>(md5Output[i]); // Need to cast to a number, otherwise a char is output
+ Output << std::setw(2) << static_cast<unsigned short>(md5Output[i]); // Need to cast to a number, otherwise a char is output
}
lua_pushlstring(tolua_S, Output.str().c_str(), Output.str().size());
return 1;
@@ -2438,10 +2468,10 @@ static int tolua_sha1HexString(lua_State * tolua_S)
// Convert the sha1 checksum to hex string:
std::stringstream Output;
- Output << std::hex << std::setw(2) << std::setfill('0');
+ Output << std::hex << std::setfill('0');
for (size_t i = 0; i < ARRAYCOUNT(sha1Output); i++)
{
- Output << static_cast<unsigned short>(sha1Output[i]); // Need to cast to a number, otherwise a char is output
+ Output << std::setw(2) << static_cast<unsigned short>(sha1Output[i]); // Need to cast to a number, otherwise a char is output
}
lua_pushlstring(tolua_S, Output.str().c_str(), Output.str().size());
return 1;
@@ -3659,19 +3689,22 @@ void ManualBindings::Bind(lua_State * tolua_S)
tolua_cclass(tolua_S, "cCryptoHash", "cCryptoHash", "", nullptr);
tolua_usertype(tolua_S, "cStringCompression");
tolua_cclass(tolua_S, "cStringCompression", "cStringCompression", "", nullptr);
+ tolua_usertype(tolua_S, "cLineBlockTracer");
+ tolua_cclass(tolua_S, "cLineBlockTracer", "cLineBlockTracer", "", nullptr);
// Globals:
- tolua_function(tolua_S, "Clamp", tolua_Clamp);
- tolua_function(tolua_S, "StringSplit", tolua_StringSplit);
- tolua_function(tolua_S, "StringSplitAndTrim", tolua_StringSplitAndTrim);
- tolua_function(tolua_S, "LOG", tolua_LOG);
- tolua_function(tolua_S, "LOGINFO", tolua_LOGINFO);
- tolua_function(tolua_S, "LOGWARN", tolua_LOGWARN);
- tolua_function(tolua_S, "LOGWARNING", tolua_LOGWARN);
- tolua_function(tolua_S, "LOGERROR", tolua_LOGERROR);
- tolua_function(tolua_S, "Base64Encode", tolua_Base64Encode);
- tolua_function(tolua_S, "Base64Decode", tolua_Base64Decode);
- tolua_function(tolua_S, "md5", tolua_md5_obsolete); // OBSOLETE, use cCryptoHash.md5() instead
+ tolua_function(tolua_S, "Clamp", tolua_Clamp);
+ tolua_function(tolua_S, "StringSplit", tolua_StringSplit);
+ tolua_function(tolua_S, "StringSplitWithQuotes", tolua_StringSplitWithQuotes);
+ tolua_function(tolua_S, "StringSplitAndTrim", tolua_StringSplitAndTrim);
+ tolua_function(tolua_S, "LOG", tolua_LOG);
+ tolua_function(tolua_S, "LOGINFO", tolua_LOGINFO);
+ tolua_function(tolua_S, "LOGWARN", tolua_LOGWARN);
+ tolua_function(tolua_S, "LOGWARNING", tolua_LOGWARN);
+ tolua_function(tolua_S, "LOGERROR", tolua_LOGERROR);
+ tolua_function(tolua_S, "Base64Encode", tolua_Base64Encode);
+ tolua_function(tolua_S, "Base64Decode", tolua_Base64Decode);
+ tolua_function(tolua_S, "md5", tolua_md5_obsolete); // OBSOLETE, use cCryptoHash.md5() instead
tolua_beginmodule(tolua_S, "cFile");
tolua_function(tolua_S, "GetFolderContents", tolua_cFile_GetFolderContents);
diff --git a/src/Bindings/ManualBindings_Network.cpp b/src/Bindings/ManualBindings_Network.cpp
index a628eb9ca..628cda7f0 100644
--- a/src/Bindings/ManualBindings_Network.cpp
+++ b/src/Bindings/ManualBindings_Network.cpp
@@ -129,6 +129,30 @@ static int tolua_cNetwork_CreateUDPEndpoint(lua_State * L)
+/** Binds cNetwork::EnumLocalIPAddresses */
+static int tolua_cNetwork_EnumLocalIPAddresses(lua_State * L)
+{
+ // Function signature:
+ // cNetwork:EnumLocalIPAddresses() -> {string, ...}
+
+ cLuaState S(L);
+ if (
+ !S.CheckParamUserTable(1, "cNetwork") ||
+ !S.CheckParamEnd(2)
+ )
+ {
+ return 0;
+ }
+
+ // Push the enumerated addresses:
+ S.Push(cNetwork::EnumLocalIPAddresses());
+ return 1;
+}
+
+
+
+
+
/** Binds cNetwork::HostnameToIP */
static int tolua_cNetwork_HostnameToIP(lua_State * L)
{
@@ -903,11 +927,12 @@ void ManualBindings::BindNetwork(lua_State * tolua_S)
// Fill in the functions (alpha-sorted):
tolua_beginmodule(tolua_S, "cNetwork");
- tolua_function(tolua_S, "Connect", tolua_cNetwork_Connect);
- tolua_function(tolua_S, "CreateUDPEndpoint", tolua_cNetwork_CreateUDPEndpoint);
- tolua_function(tolua_S, "HostnameToIP", tolua_cNetwork_HostnameToIP);
- tolua_function(tolua_S, "IPToHostname", tolua_cNetwork_IPToHostname);
- tolua_function(tolua_S, "Listen", tolua_cNetwork_Listen);
+ tolua_function(tolua_S, "Connect", tolua_cNetwork_Connect);
+ tolua_function(tolua_S, "CreateUDPEndpoint", tolua_cNetwork_CreateUDPEndpoint);
+ tolua_function(tolua_S, "EnumLocalIPAddresses", tolua_cNetwork_EnumLocalIPAddresses);
+ tolua_function(tolua_S, "HostnameToIP", tolua_cNetwork_HostnameToIP);
+ tolua_function(tolua_S, "IPToHostname", tolua_cNetwork_IPToHostname);
+ tolua_function(tolua_S, "Listen", tolua_cNetwork_Listen);
tolua_endmodule(tolua_S);
tolua_beginmodule(tolua_S, "cServerHandle");
diff --git a/src/Bindings/Plugin.h b/src/Bindings/Plugin.h
index 6210dbed4..3f9fa7655 100644
--- a/src/Bindings/Plugin.h
+++ b/src/Bindings/Plugin.h
@@ -57,6 +57,7 @@ public:
virtual bool OnCraftingNoRecipe (cPlayer & a_Player, cCraftingGrid & a_Grid, cCraftingRecipe & a_Recipe) = 0;
virtual bool OnDisconnect (cClientHandle & a_Client, const AString & a_Reason) = 0;
virtual bool OnEntityAddEffect (cEntity & a_Entity, int a_EffectType, int a_EffectDurationTicks, int a_EffectIntensity, double a_DistanceModifier) = 0;
+ virtual bool OnEntityTeleport (cEntity & a_Entity, const Vector3d & a_OldPosition, const Vector3d & a_NewPosition) = 0;
virtual bool OnExecuteCommand (cPlayer * a_Player, const AStringVector & a_Split) = 0;
virtual bool OnExploded (cWorld & a_World, double a_ExplosionSize, bool a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData) = 0;
virtual bool OnExploding (cWorld & a_World, double & a_ExplosionSize, bool & a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData) = 0;
@@ -110,12 +111,12 @@ public:
Command permissions have already been checked.
Returns true if command handled successfully
*/
- virtual bool HandleCommand(const AStringVector & a_Split, cPlayer & a_Player) = 0;
+ virtual bool HandleCommand(const AStringVector & a_Split, cPlayer & a_Player, const AString & a_FullCommand) = 0;
/** Handles the console command split into a_Split.
Returns true if command handled successfully. Output is to be sent to the a_Output callback.
*/
- virtual bool HandleConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output) = 0;
+ virtual bool HandleConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output, const AString & a_FullCommand) = 0;
/// All bound commands are to be removed, do any language-dependent cleanup here
virtual void ClearCommands(void) {}
diff --git a/src/Bindings/PluginLua.cpp b/src/Bindings/PluginLua.cpp
index 500913e76..0a2a8411d 100644
--- a/src/Bindings/PluginLua.cpp
+++ b/src/Bindings/PluginLua.cpp
@@ -6,10 +6,11 @@
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
#ifdef __APPLE__
-#define LUA_USE_MACOSX
+ #define LUA_USE_MACOSX
#else
-#define LUA_USE_POSIX
+ #define LUA_USE_POSIX
#endif
+
#include "PluginLua.h"
#include "../CommandOutput.h"
#include "PluginManager.h"
@@ -52,24 +53,40 @@ cPluginLua::~cPluginLua()
void cPluginLua::Close(void)
{
- if (m_LuaState.IsValid())
- {
- // Release all the references in the hook map:
- for (cHookMap::iterator itrH = m_HookMap.begin(), endH = m_HookMap.end(); itrH != endH; ++itrH)
- {
- for (cLuaRefs::iterator itrR = itrH->second.begin(), endR = itrH->second.end(); itrR != endR; ++itrR)
- {
- delete *itrR;
- } // for itrR - itrH->second[]
- } // for itrH - m_HookMap[]
- m_HookMap.clear();
-
- m_LuaState.Close();
- }
- else
+ cCSLock Lock(m_CriticalSection);
+
+ // If already closed, bail out:
+ if (!m_LuaState.IsValid())
{
+ ASSERT(m_Resettables.empty());
ASSERT(m_HookMap.empty());
+ return;
}
+
+ // Notify and remove all m_Resettables (unlock the m_CriticalSection while resetting them):
+ cResettablePtrs resettables;
+ std::swap(m_Resettables, resettables);
+ {
+ cCSUnlock Unlock(Lock);
+ for (auto resettable: resettables)
+ {
+ resettable->Reset();
+ }
+ m_Resettables.clear();
+ } // cCSUnlock (m_CriticalSection)
+
+ // Release all the references in the hook map:
+ for (cHookMap::iterator itrH = m_HookMap.begin(), endH = m_HookMap.end(); itrH != endH; ++itrH)
+ {
+ for (cLuaRefs::iterator itrR = itrH->second.begin(), endR = itrH->second.end(); itrR != endR; ++itrR)
+ {
+ delete *itrR;
+ } // for itrR - itrH->second[]
+ } // for itrH - m_HookMap[]
+ m_HookMap.clear();
+
+ // Close the Lua engine:
+ m_LuaState.Close();
}
@@ -857,6 +874,26 @@ bool cPluginLua::OnPlayerMoving(cPlayer & a_Player, const Vector3d & a_OldPositi
+bool cPluginLua::OnEntityTeleport(cEntity & a_Entity, const Vector3d & a_OldPosition, const Vector3d & a_NewPosition)
+{
+ cCSLock Lock(m_CriticalSection);
+ bool res = false;
+ cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_ENTITY_TELEPORT];
+ for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
+ {
+ m_LuaState.Call((int)(**itr), &a_Entity, a_OldPosition, a_NewPosition, cLuaState::Return, res);
+ if (res)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
bool cPluginLua::OnPlayerPlacedBlock(cPlayer & a_Player, const sSetBlock & a_BlockChange)
{
cCSLock Lock(m_CriticalSection);
@@ -1445,7 +1482,7 @@ bool cPluginLua::OnWorldTick(cWorld & a_World, std::chrono::milliseconds a_Dt, s
-bool cPluginLua::HandleCommand(const AStringVector & a_Split, cPlayer & a_Player)
+bool cPluginLua::HandleCommand(const AStringVector & a_Split, cPlayer & a_Player, const AString & a_FullCommand)
{
ASSERT(!a_Split.empty());
CommandMap::iterator cmd = m_Commands.find(a_Split[0]);
@@ -1457,7 +1494,7 @@ bool cPluginLua::HandleCommand(const AStringVector & a_Split, cPlayer & a_Player
cCSLock Lock(m_CriticalSection);
bool res = false;
- m_LuaState.Call(cmd->second, a_Split, &a_Player, cLuaState::Return, res);
+ m_LuaState.Call(cmd->second, a_Split, &a_Player, a_FullCommand, cLuaState::Return, res);
return res;
}
@@ -1465,7 +1502,7 @@ bool cPluginLua::HandleCommand(const AStringVector & a_Split, cPlayer & a_Player
-bool cPluginLua::HandleConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output)
+bool cPluginLua::HandleConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output, const AString & a_FullCommand)
{
ASSERT(!a_Split.empty());
CommandMap::iterator cmd = m_ConsoleCommands.find(a_Split[0]);
@@ -1480,7 +1517,7 @@ bool cPluginLua::HandleConsoleCommand(const AStringVector & a_Split, cCommandOut
cCSLock Lock(m_CriticalSection);
bool res = false;
AString str;
- m_LuaState.Call(cmd->second, a_Split, cLuaState::Return, res, str);
+ m_LuaState.Call(cmd->second, a_Split, a_FullCommand, cLuaState::Return, res, str);
if (res && !str.empty())
{
a_Output.Out(str);
@@ -1577,6 +1614,7 @@ const char * cPluginLua::GetHookFnName(int a_HookType)
case cPluginManager::HOOK_DISCONNECT: return "OnDisconnect";
case cPluginManager::HOOK_PLAYER_ANIMATION: return "OnPlayerAnimation";
case cPluginManager::HOOK_ENTITY_ADD_EFFECT: return "OnEntityAddEffect";
+ case cPluginManager::HOOK_ENTITY_TELEPORT: return "OnEntityTeleport";
case cPluginManager::HOOK_EXECUTE_COMMAND: return "OnExecuteCommand";
case cPluginManager::HOOK_HANDSHAKE: return "OnHandshake";
case cPluginManager::HOOK_KILLING: return "OnKilling";
@@ -1688,6 +1726,16 @@ int cPluginLua::CallFunctionFromForeignState(
+void cPluginLua::AddResettable(cPluginLua::cResettablePtr a_Resettable)
+{
+ cCSLock Lock(m_CriticalSection);
+ m_Resettables.push_back(a_Resettable);
+}
+
+
+
+
+
AString cPluginLua::HandleWebRequest(const HTTPRequest * a_Request)
{
cCSLock Lock(m_CriticalSection);
@@ -1805,3 +1853,25 @@ void cPluginLua::CallbackWindowSlotChanged(int a_FnRef, cWindow & a_Window, int
+
+////////////////////////////////////////////////////////////////////////////////
+// cPluginLua::cResettable:
+
+cPluginLua::cResettable::cResettable(cPluginLua & a_Plugin):
+ m_Plugin(&a_Plugin)
+{
+}
+
+
+
+
+
+void cPluginLua::cResettable::Reset(void)
+{
+ cCSLock Lock(m_CSPlugin);
+ m_Plugin = nullptr;
+}
+
+
+
+
diff --git a/src/Bindings/PluginLua.h b/src/Bindings/PluginLua.h
index f443f5fc0..c14b02687 100644
--- a/src/Bindings/PluginLua.h
+++ b/src/Bindings/PluginLua.h
@@ -59,6 +59,37 @@ public:
/** RAII lock for m_Plugin.m_CriticalSection */
cCSLock m_Lock;
} ;
+
+
+
+ /** A base class that represents something related to a plugin
+ The plugin can reset this class so that the instance can continue to exist but will not engage the (possibly non-existent) plugin anymore.
+ This is used for scheduled tasks etc., so that they can be queued and reset when the plugin is terminated, without removing them from the queue. */
+ class cResettable
+ {
+ public:
+ /** Creates a new instance bound to the specified plugin. */
+ cResettable(cPluginLua & a_Plugin);
+
+ // Force a virtual destructor in descendants:
+ virtual ~cResettable() {}
+
+ /** Resets the plugin instance stored within.
+ The instance will continue to exist, but should not call into the plugin anymore. */
+ virtual void Reset(void);
+
+ protected:
+ /** The plugin that this instance references.
+ If nullptr, the plugin has already unloaded and the instance should bail out any processing.
+ Protected against multithreaded access by m_CSPlugin. */
+ cPluginLua * m_Plugin;
+
+ /** The mutex protecting m_Plugin against multithreaded access. */
+ cCriticalSection m_CSPlugin;
+ };
+
+ typedef SharedPtr<cResettable> cResettablePtr;
+ typedef std::vector<cResettablePtr> cResettablePtrs;
cPluginLua(const AString & a_PluginDirectory);
@@ -106,6 +137,7 @@ public:
virtual bool OnPlayerRightClickingEntity(cPlayer & a_Player, cEntity & a_Entity) override;
virtual bool OnPlayerShooting (cPlayer & a_Player) override;
virtual bool OnPlayerSpawned (cPlayer & a_Player) override;
+ virtual bool OnEntityTeleport (cEntity & a_Entity, const Vector3d & a_OldPosition, const Vector3d & a_NewPosition) override;
virtual bool OnPlayerTossingItem (cPlayer & a_Player) override;
virtual bool OnPlayerUsedBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override;
virtual bool OnPlayerUsedItem (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override;
@@ -130,9 +162,9 @@ public:
virtual bool OnWorldStarted (cWorld & a_World) override;
virtual bool OnWorldTick (cWorld & a_World, std::chrono::milliseconds a_Dt, std::chrono::milliseconds a_LastTickDurationMSec) override;
- virtual bool HandleCommand(const AStringVector & a_Split, cPlayer & a_Player) override;
+ virtual bool HandleCommand(const AStringVector & a_Split, cPlayer & a_Player, const AString & a_FullCommand) override;
- virtual bool HandleConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output) override;
+ virtual bool HandleConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output, const AString & a_FullCommand) override;
virtual void ClearCommands(void) override;
@@ -186,42 +218,16 @@ public:
int a_ParamEnd
);
- // The following templates allow calls to arbitrary Lua functions residing in the plugin:
-
- /** Call a Lua function with 0 args */
- template <typename FnT> bool Call(FnT a_Fn)
- {
- cCSLock Lock(m_CriticalSection);
- return m_LuaState.Call(a_Fn);
- }
-
- /** Call a Lua function with 1 arg */
- template <typename FnT, typename ArgT0> bool Call(FnT a_Fn, ArgT0 a_Arg0)
+ /** Call a Lua function residing in the plugin. */
+ template <typename FnT, typename... Args>
+ bool Call(FnT a_Fn, Args && ... a_Args)
{
cCSLock Lock(m_CriticalSection);
- return m_LuaState.Call(a_Fn, a_Arg0);
+ return m_LuaState.Call(a_Fn, a_Args...);
}
- /** Call a Lua function with 2 args */
- template <typename FnT, typename ArgT0, typename ArgT1> bool Call(FnT a_Fn, ArgT0 a_Arg0, ArgT1 a_Arg1)
- {
- cCSLock Lock(m_CriticalSection);
- return m_LuaState.Call(a_Fn, a_Arg0, a_Arg1);
- }
-
- /** Call a Lua function with 3 args */
- template <typename FnT, typename ArgT0, typename ArgT1, typename ArgT2> bool Call(FnT a_Fn, ArgT0 a_Arg0, ArgT1 a_Arg1, ArgT2 a_Arg2)
- {
- cCSLock Lock(m_CriticalSection);
- return m_LuaState.Call(a_Fn, a_Arg0, a_Arg1, a_Arg2);
- }
-
- /** Call a Lua function with 4 args */
- template <typename FnT, typename ArgT0, typename ArgT1, typename ArgT2, typename ArgT3> bool Call(FnT a_Fn, ArgT0 a_Arg0, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3)
- {
- cCSLock Lock(m_CriticalSection);
- return m_LuaState.Call(a_Fn, a_Arg0, a_Arg1, a_Arg2, a_Arg3);
- }
+ /** Adds the specified cResettable instance to m_Resettables, so that it is notified when the plugin is being closed. */
+ void AddResettable(cResettablePtr a_Resettable);
protected:
/** Maps command name into Lua function reference */
@@ -233,15 +239,27 @@ protected:
/** Maps hook types into arrays of Lua function references to call for each hook type */
typedef std::map<int, cLuaRefs> cHookMap;
+
+ /** The mutex protecting m_LuaState and each of the m_Resettables[] against multithreaded use. */
cCriticalSection m_CriticalSection;
+
+ /** The plugin's Lua state. */
cLuaState m_LuaState;
+ /** Objects that need notification when the plugin is about to be unloaded. */
+ cResettablePtrs m_Resettables;
+
+ /** In-game commands that the plugin has registered. */
CommandMap m_Commands;
+
+ /** Console commands that the plugin has registered. */
CommandMap m_ConsoleCommands;
+ /** Hooks that the plugin has registered. */
cHookMap m_HookMap;
- /** Releases all Lua references and closes the LuaState */
+
+ /** Releases all Lua references, notifies and removes all m_Resettables[] and closes the m_LuaState. */
void Close(void);
} ; // tolua_export
diff --git a/src/Bindings/PluginManager.cpp b/src/Bindings/PluginManager.cpp
index 9d86c64a2..8935f7dd3 100644
--- a/src/Bindings/PluginManager.cpp
+++ b/src/Bindings/PluginManager.cpp
@@ -505,6 +505,24 @@ bool cPluginManager::CallHookEntityAddEffect(cEntity & a_Entity, int a_EffectTyp
+bool cPluginManager::CallHookEntityTeleport(cEntity & a_Entity, const Vector3d & a_OldPosition, const Vector3d & a_NewPosition)
+{
+ FIND_HOOK(HOOK_ENTITY_TELEPORT);
+ VERIFY_HOOK;
+
+ for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
+ {
+ if ((*itr)->OnEntityTeleport(a_Entity, a_OldPosition, a_NewPosition))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
bool cPluginManager::CallHookExecuteCommand(cPlayer * a_Player, const AStringVector & a_Split)
{
FIND_HOOK(HOOK_EXECUTE_COMMAND);
@@ -1447,7 +1465,7 @@ cPluginManager::CommandResult cPluginManager::HandleCommand(cPlayer & a_Player,
ASSERT(cmd->second.m_Plugin != nullptr);
- if (!cmd->second.m_Plugin->HandleCommand(Split, a_Player))
+ if (!cmd->second.m_Plugin->HandleCommand(Split, a_Player, a_Command))
{
return crError;
}
@@ -1750,7 +1768,7 @@ bool cPluginManager::IsConsoleCommandBound(const AString & a_Command)
-bool cPluginManager::ExecuteConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output)
+bool cPluginManager::ExecuteConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output, const AString & a_Command)
{
if (a_Split.empty())
{
@@ -1777,7 +1795,7 @@ bool cPluginManager::ExecuteConsoleCommand(const AStringVector & a_Split, cComma
return false;
}
- return cmd->second.m_Plugin->HandleConsoleCommand(a_Split, a_Output);
+ return cmd->second.m_Plugin->HandleConsoleCommand(a_Split, a_Output, a_Command);
}
diff --git a/src/Bindings/PluginManager.h b/src/Bindings/PluginManager.h
index 97e91c1df..4efcbb6f3 100644
--- a/src/Bindings/PluginManager.h
+++ b/src/Bindings/PluginManager.h
@@ -109,6 +109,7 @@ public:
HOOK_PLAYER_RIGHT_CLICKING_ENTITY,
HOOK_PLAYER_SHOOTING,
HOOK_PLAYER_SPAWNED,
+ HOOK_ENTITY_TELEPORT,
HOOK_PLAYER_TOSSING_ITEM,
HOOK_PLAYER_USED_BLOCK,
HOOK_PLAYER_USED_ITEM,
@@ -190,6 +191,7 @@ public:
bool CallHookCraftingNoRecipe (cPlayer & a_Player, cCraftingGrid & a_Grid, cCraftingRecipe & a_Recipe);
bool CallHookDisconnect (cClientHandle & a_Client, const AString & a_Reason);
bool CallHookEntityAddEffect (cEntity & a_Entity, int a_EffectType, int a_EffectDurationTicks, int a_EffectIntensity, double a_DistanceModifier);
+ bool CallHookEntityTeleport (cEntity & a_Entity, const Vector3d & a_OldPosition, const Vector3d & a_NewPosition);
bool CallHookExecuteCommand (cPlayer * a_Player, const AStringVector & a_Split); // If a_Player == nullptr, it is a console cmd
bool CallHookExploded (cWorld & a_World, double a_ExplosionSize, bool a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData);
bool CallHookExploding (cWorld & a_World, double & a_ExplosionSize, bool & a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData);
@@ -282,7 +284,7 @@ public:
bool IsConsoleCommandBound(const AString & a_Command); // tolua_export
/** Executes the command split into a_Split, as if it was given on the console. Returns true if executed. Output is sent to the a_Output callback */
- bool ExecuteConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output);
+ bool ExecuteConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output, const AString & a_Command);
/** Appends all commands beginning with a_Text (case-insensitive) into a_Results.
If a_Player is not nullptr, only commands for which the player has permissions are added.
diff --git a/src/BlockEntities/BeaconEntity.cpp b/src/BlockEntities/BeaconEntity.cpp
index 37ce7a8ab..fb3940ce9 100644
--- a/src/BlockEntities/BeaconEntity.cpp
+++ b/src/BlockEntities/BeaconEntity.cpp
@@ -4,6 +4,7 @@
#include "BeaconEntity.h"
#include "../BlockArea.h"
#include "../Entities/Player.h"
+#include "../UI/BeaconWindow.h"
@@ -289,7 +290,7 @@ void cBeaconEntity::UsedBy(cPlayer * a_Player)
OpenWindow(new cBeaconWindow(m_PosX, m_PosY, m_PosZ, this));
Window = GetWindow();
}
-
+
if (Window != nullptr)
{
// if (a_Player->GetWindow() != Window)
diff --git a/src/BlockEntities/BlockEntityWithItems.h b/src/BlockEntities/BlockEntityWithItems.h
index 2c2ced1cb..740dbca51 100644
--- a/src/BlockEntities/BlockEntityWithItems.h
+++ b/src/BlockEntities/BlockEntityWithItems.h
@@ -21,7 +21,9 @@
// tolua_begin
class cBlockEntityWithItems :
public cBlockEntity,
+ // tolua_end
public cItemGrid::cListener,
+ // tolua_begin
public cBlockEntityWindowOwner
{
typedef cBlockEntity super;
diff --git a/src/BlockEntities/ChestEntity.cpp b/src/BlockEntities/ChestEntity.cpp
index 0cd9c66e0..3821f9aab 100644
--- a/src/BlockEntities/ChestEntity.cpp
+++ b/src/BlockEntities/ChestEntity.cpp
@@ -4,7 +4,7 @@
#include "ChestEntity.h"
#include "../Item.h"
#include "../Entities/Player.h"
-#include "../UI/Window.h"
+#include "../UI/ChestWindow.h"
diff --git a/src/BlockEntities/DropSpenserEntity.cpp b/src/BlockEntities/DropSpenserEntity.cpp
index 5e98506f1..039f5d360 100644
--- a/src/BlockEntities/DropSpenserEntity.cpp
+++ b/src/BlockEntities/DropSpenserEntity.cpp
@@ -8,6 +8,7 @@
#include "DropSpenserEntity.h"
#include "../Entities/Player.h"
#include "../Chunk.h"
+#include "../UI/DropSpenserWindow.h"
diff --git a/src/BlockEntities/EnderChestEntity.cpp b/src/BlockEntities/EnderChestEntity.cpp
index e18490a1e..ab5c5a2de 100644
--- a/src/BlockEntities/EnderChestEntity.cpp
+++ b/src/BlockEntities/EnderChestEntity.cpp
@@ -4,7 +4,7 @@
#include "EnderChestEntity.h"
#include "../Item.h"
#include "../Entities/Player.h"
-#include "../UI/Window.h"
+#include "../UI/EnderChestWindow.h"
diff --git a/src/BlockEntities/FurnaceEntity.cpp b/src/BlockEntities/FurnaceEntity.cpp
index cc5a00af7..2621b560b 100644
--- a/src/BlockEntities/FurnaceEntity.cpp
+++ b/src/BlockEntities/FurnaceEntity.cpp
@@ -2,7 +2,7 @@
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
#include "FurnaceEntity.h"
-#include "../UI/Window.h"
+#include "../UI/FurnaceWindow.h"
#include "../Entities/Player.h"
#include "../Root.h"
#include "../Chunk.h"
diff --git a/src/BlockEntities/HopperEntity.cpp b/src/BlockEntities/HopperEntity.cpp
index c2d6cabbb..bfd4b8322 100644
--- a/src/BlockEntities/HopperEntity.cpp
+++ b/src/BlockEntities/HopperEntity.cpp
@@ -9,6 +9,7 @@
#include "../Entities/Player.h"
#include "../Entities/Pickup.h"
#include "../Bindings/PluginManager.h"
+#include "../UI/HopperWindow.h"
#include "ChestEntity.h"
#include "FurnaceEntity.h"
diff --git a/src/Blocks/BlockAnvil.h b/src/Blocks/BlockAnvil.h
index 20514580e..abfa0f782 100644
--- a/src/Blocks/BlockAnvil.h
+++ b/src/Blocks/BlockAnvil.h
@@ -4,6 +4,7 @@
#include "BlockHandler.h"
#include "../World.h"
#include "../Entities/Player.h"
+#include "../UI/AnvilWindow.h"
diff --git a/src/Blocks/BlockBed.cpp b/src/Blocks/BlockBed.cpp
index 57b9855d0..e56f4bfe0 100644
--- a/src/Blocks/BlockBed.cpp
+++ b/src/Blocks/BlockBed.cpp
@@ -1,14 +1,9 @@
#include "Globals.h"
#include "BlockBed.h"
-
-
-
#include "BroadcastInterface.h"
-#include "ChunkInterface.h"
#include "Entities/../World.h"
#include "Entities/Player.h"
-#include "WorldInterface.h"
@@ -64,21 +59,22 @@ class cPlayerBedStateUnsetter :
public cPlayerListCallback
{
public:
- cPlayerBedStateUnsetter(Vector3i a_Position, cWorldInterface & a_WorldInterface) :
- m_Position(a_Position), m_WorldInterface(a_WorldInterface)
+ cPlayerBedStateUnsetter(Vector3i a_Position, cChunkInterface & a_ChunkInterface) :
+ m_Position(a_Position),
+ m_ChunkInterface(a_ChunkInterface)
{
}
virtual bool Item(cPlayer * a_Player) override
{
+ cBlockBedHandler::SetBedOccupationState(m_ChunkInterface, a_Player->GetLastBedPos(), false);
a_Player->SetIsInBed(false);
- m_WorldInterface.GetBroadcastManager().BroadcastEntityAnimation(*a_Player, 2);
return false;
}
private:
Vector3i m_Position;
- cWorldInterface & m_WorldInterface;
+ cChunkInterface & m_ChunkInterface;
};
@@ -97,7 +93,7 @@ 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 & 0x4)
+ if ((Meta & 0x4) == 0x4)
{
a_Player->SendMessageFailure("This bed is occupied");
}
@@ -105,7 +101,7 @@ void cBlockBedHandler::OnUse(cChunkInterface & a_ChunkInterface, cWorldInterface
{
Vector3i PillowDirection(0, 0, 0);
- if (Meta & 0x8)
+ if ((Meta & 0x8) == 0x8)
{
// Is pillow
a_WorldInterface.GetBroadcastManager().BroadcastUseBed(*a_Player, a_BlockX, a_BlockY, a_BlockZ);
@@ -122,15 +118,15 @@ void cBlockBedHandler::OnUse(cChunkInterface & a_ChunkInterface, cWorldInterface
}
}
- a_ChunkInterface.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, Meta | 0x4); // Where 0x4 = occupied bit
- a_Player->SetIsInBed(true);
a_Player->SetBedPos(Vector3i(a_BlockX, a_BlockY, a_BlockZ));
+ SetBedOccupationState(a_ChunkInterface, a_Player->GetLastBedPos(), true);
+ a_Player->SetIsInBed(true);
a_Player->SendMessageSuccess("Home position set successfully");
cTimeFastForwardTester Tester;
if (a_WorldInterface.ForEachPlayer(Tester))
{
- cPlayerBedStateUnsetter Unsetter(Vector3i(a_BlockX + PillowDirection.x, a_BlockY, a_BlockZ + PillowDirection.z), a_WorldInterface);
+ cPlayerBedStateUnsetter Unsetter(Vector3i(a_BlockX + PillowDirection.x, a_BlockY, a_BlockZ + PillowDirection.z), a_ChunkInterface);
a_WorldInterface.ForEachPlayer(Unsetter);
a_WorldInterface.SetTimeOfDay(0);
a_ChunkInterface.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, Meta & 0x0b); // Clear the "occupied" bit of the bed's block
diff --git a/src/Blocks/BlockBed.h b/src/Blocks/BlockBed.h
index 5b746110a..46f361686 100644
--- a/src/Blocks/BlockBed.h
+++ b/src/Blocks/BlockBed.h
@@ -4,9 +4,9 @@
#include "BlockHandler.h"
#include "MetaRotator.h"
#include "Item.h"
+#include "ChunkInterface.h"
-class cChunkInterface;
class cPlayer;
class cWorldInterface;
@@ -21,17 +21,14 @@ public:
: cMetaRotator<cBlockHandler, 0x3, 0x02, 0x03, 0x00, 0x01, true>(a_BlockType)
{
}
-
-
+
virtual void OnDestroyed(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, int a_BlockX, int a_BlockY, int a_BlockZ) override;
virtual void OnUse(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override;
-
virtual bool IsUseable(void) override
{
return true;
}
-
virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
{
@@ -39,14 +36,12 @@ public:
a_Pickups.push_back(cItem(E_ITEM_BED, 1, 0));
}
-
virtual bool CanDirtGrowGrass(NIBBLETYPE a_Meta) override
{
return true;
}
-
// Bed specific helper functions
static NIBBLETYPE RotationToMetaData(double a_Rotation)
{
@@ -61,7 +56,6 @@ public:
return ((char)a_Rotation + 2) % 4;
}
-
static Vector3i MetaDataToDirection(NIBBLETYPE a_MetaData)
{
switch (a_MetaData)
@@ -73,6 +67,21 @@ public:
}
return Vector3i();
}
+
+ static void SetBedOccupationState(cChunkInterface & a_ChunkInterface, const Vector3i & a_BedPosition, bool a_IsOccupied)
+ {
+ auto Meta = a_ChunkInterface.GetBlockMeta(a_BedPosition.x, a_BedPosition.y, a_BedPosition.z);
+ if (a_IsOccupied)
+ {
+ Meta |= 0x04; // Where 0x4 = occupied bit
+ }
+ else
+ {
+ Meta &= 0x0b; // Clear the "occupied" bit of the bed's block
+ }
+
+ a_ChunkInterface.SetBlockMeta(a_BedPosition.x, a_BedPosition.y, a_BedPosition.z, Meta);
+ }
} ;
diff --git a/src/Blocks/BlockDirt.h b/src/Blocks/BlockDirt.h
index aae6719e2..cc0d845e4 100644
--- a/src/Blocks/BlockDirt.h
+++ b/src/Blocks/BlockDirt.h
@@ -52,14 +52,26 @@ public:
return;
}
}
-
+
+ // Make sure that there is enough light at the source block to spread
+ if (!a_Chunk.GetWorld()->IsChunkLighted(a_Chunk.GetPosX(), a_Chunk.GetPosZ()))
+ {
+ a_Chunk.GetWorld()->QueueLightChunk(a_Chunk.GetPosX(), a_Chunk.GetPosZ());
+ return;
+ }
+ else if (std::max(a_Chunk.GetBlockLight(a_RelX, a_RelY + 1, a_RelZ), a_Chunk.GetTimeAlteredLight(a_Chunk.GetSkyLight(a_RelX, a_RelY + 1, a_RelZ))) < 9)
+ {
+ // Source block is not bright enough to spread
+ return;
+ }
+
// Grass spreads to adjacent dirt blocks:
cFastRandom rand;
for (int i = 0; i < 2; i++) // Pick two blocks to grow to
{
- int OfsX = rand.NextInt(3, a_RelX) - 1; // [-1 .. 1]
- int OfsY = rand.NextInt(5, a_RelY) - 3; // [-3 .. 1]
- int OfsZ = rand.NextInt(3, a_RelZ) - 1; // [-1 .. 1]
+ int OfsX = rand.NextInt(3) - 1; // [-1 .. 1]
+ int OfsY = rand.NextInt(5) - 3; // [-3 .. 1]
+ int OfsZ = rand.NextInt(3) - 1; // [-1 .. 1]
BLOCKTYPE DestBlock;
NIBBLETYPE DestMeta;
diff --git a/src/Blocks/BlockEnchantmentTable.h b/src/Blocks/BlockEnchantmentTable.h
index 81d2cb9a0..40001f356 100644
--- a/src/Blocks/BlockEnchantmentTable.h
+++ b/src/Blocks/BlockEnchantmentTable.h
@@ -2,7 +2,7 @@
#pragma once
#include "BlockHandler.h"
-#include "../UI/Window.h"
+#include "../UI/EnchantingWindow.h"
#include "../Entities/Player.h"
diff --git a/src/Blocks/BlockPiston.cpp b/src/Blocks/BlockPiston.cpp
index 8d245cabe..d9f4913d8 100644
--- a/src/Blocks/BlockPiston.cpp
+++ b/src/Blocks/BlockPiston.cpp
@@ -61,6 +61,16 @@ void cBlockPistonHandler::OnDestroyed(cChunkInterface & a_ChunkInterface, cWorld
+void cBlockPistonHandler::ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta)
+{
+ // Returning Piston Item without Direction-Metavalue
+ a_Pickups.push_back(cItem(m_BlockType, 1));
+}
+
+
+
+
+
bool cBlockPistonHandler::GetPlacementBlockTypeMeta(
cChunkInterface & a_ChunkInterface, cPlayer * a_Player,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace,
@@ -169,7 +179,7 @@ void cBlockPistonHandler::ExtendPiston(int a_BlockX, int a_BlockY, int a_BlockZ,
a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, pistonBlock, pistonMeta | 0x8);
a_World->SetBlock(extx, exty, extz, E_BLOCK_PISTON_EXTENSION, pistonMeta | (IsSticky(pistonBlock) ? 8 : 0), false);
- a_World->ScheduleTask(PISTON_TICK_DELAY, new cWorld::cTaskSendBlockToAllPlayers(ScheduledBlocks));
+ a_World->ScheduleTask(PISTON_TICK_DELAY, std::make_shared<cWorld::cTaskSendBlockToAllPlayers>(ScheduledBlocks));
}
@@ -219,7 +229,7 @@ void cBlockPistonHandler::RetractPiston(int a_BlockX, int a_BlockY, int a_BlockZ
std::vector<Vector3i> ScheduledBlocks;
ScheduledBlocks.push_back(Vector3i(a_BlockX, a_BlockY, a_BlockZ));
ScheduledBlocks.push_back(Vector3i(tempx, tempy, tempz));
- a_World->ScheduleTask(PISTON_TICK_DELAY + 1, new cWorld::cTaskSendBlockToAllPlayers(ScheduledBlocks));
+ a_World->ScheduleTask(PISTON_TICK_DELAY + 1, std::make_shared<cWorld::cTaskSendBlockToAllPlayers>(ScheduledBlocks));
return;
}
}
@@ -229,7 +239,7 @@ void cBlockPistonHandler::RetractPiston(int a_BlockX, int a_BlockY, int a_BlockZ
std::vector<Vector3i> ScheduledBlocks;
ScheduledBlocks.push_back(Vector3i(a_BlockX, a_BlockY, a_BlockZ));
- a_World->ScheduleTask(PISTON_TICK_DELAY + 1, new cWorld::cTaskSendBlockToAllPlayers(ScheduledBlocks));
+ a_World->ScheduleTask(PISTON_TICK_DELAY + 1, std::make_shared<cWorld::cTaskSendBlockToAllPlayers>(ScheduledBlocks));
}
diff --git a/src/Blocks/BlockPiston.h b/src/Blocks/BlockPiston.h
index f868f4d8e..9a891025a 100644
--- a/src/Blocks/BlockPiston.h
+++ b/src/Blocks/BlockPiston.h
@@ -15,6 +15,8 @@ public:
virtual void OnDestroyed(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, int a_BlockX, int a_BlockY, int a_BlockZ) override;
+ virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override;
+
virtual bool GetPlacementBlockTypeMeta(
cChunkInterface & a_ChunkInterface, cPlayer * a_Player,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace,
diff --git a/src/Blocks/BlockWorkbench.h b/src/Blocks/BlockWorkbench.h
index 699badaf2..e40e15606 100644
--- a/src/Blocks/BlockWorkbench.h
+++ b/src/Blocks/BlockWorkbench.h
@@ -2,7 +2,7 @@
#pragma once
#include "BlockHandler.h"
-#include "../UI/Window.h"
+#include "../UI/CraftingWindow.h"
#include "../Entities/Player.h"
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index c39f5f6e6..b91c4f65a 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -9,7 +9,7 @@ include_directories (SYSTEM "${CMAKE_CURRENT_SOURCE_DIR}/../lib/libevent/include
set(FOLDERS
OSSupport HTTPServer Items Blocks Protocol Generating PolarSSL++ Bindings
- WorldStorage Mobs Entities Simulator UI BlockEntities Generating/Prefabs
+ WorldStorage Mobs Entities Simulator BlockEntities UI Generating/Prefabs
Noise
)
@@ -318,7 +318,7 @@ if (NOT MSVC)
target_link_libraries(${EXECUTABLE}
OSSupport HTTPServer Bindings Items Blocks Noise
Protocol Generating Generating_Prefabs WorldStorage
- Mobs Entities Simulator UI BlockEntities PolarSSL++
+ Mobs Entities Simulator BlockEntities UI PolarSSL++
)
endif ()
if (WIN32)
diff --git a/src/Chunk.cpp b/src/Chunk.cpp
index e05fa4a99..00ac1fdb1 100644
--- a/src/Chunk.cpp
+++ b/src/Chunk.cpp
@@ -776,10 +776,22 @@ void cChunk::BroadcastPendingBlockChanges(void)
{
return;
}
-
- for (cClientHandleList::iterator itr = m_LoadedByClient.begin(), end = m_LoadedByClient.end(); itr != end; ++itr)
+
+ if (m_PendingSendBlocks.size() >= 10240)
+ {
+ // Resend the full chunk
+ for (cClientHandleList::iterator itr = m_LoadedByClient.begin(), end = m_LoadedByClient.end(); itr != end; ++itr)
+ {
+ m_World->ForceSendChunkTo(m_PosX, m_PosZ, cChunkSender::E_CHUNK_PRIORITY_MEDIUM, (*itr));
+ }
+ }
+ else
{
- (*itr)->SendBlockChanges(m_PosX, m_PosZ, m_PendingSendBlocks);
+ // Only send block changes
+ for (cClientHandleList::iterator itr = m_LoadedByClient.begin(), end = m_LoadedByClient.end(); itr != end; ++itr)
+ {
+ (*itr)->SendBlockChanges(m_PosX, m_PosZ, m_PendingSendBlocks);
+ }
}
m_PendingSendBlocks.clear();
}
diff --git a/src/ChunkData.cpp b/src/ChunkData.cpp
index 57b27c5e0..80f40d39e 100644
--- a/src/ChunkData.cpp
+++ b/src/ChunkData.cpp
@@ -361,7 +361,7 @@ void cChunkData::CopyBlockTypes(BLOCKTYPE * a_Dest, size_t a_Idx, size_t a_Lengt
}
else
{
- memset(&a_Dest[(i * SectionBlockCount) - a_Idx], 0, sizeof(BLOCKTYPE) * ToCopy);
+ memset(&a_Dest[(i * SectionBlockCount) + StartPos - a_Idx], 0, sizeof(BLOCKTYPE) * ToCopy);
}
}
}
diff --git a/src/ChunkMap.cpp b/src/ChunkMap.cpp
index 2dd04f801..b9d4ed55b 100644
--- a/src/ChunkMap.cpp
+++ b/src/ChunkMap.cpp
@@ -1322,10 +1322,7 @@ void cChunkMap::SetBlockMeta(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYP
void cChunkMap::SetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, bool a_SendToClients)
{
cChunkInterface ChunkInterface(this);
- if (a_BlockType == E_BLOCK_AIR)
- {
- BlockHandler(GetBlock(a_BlockX, a_BlockY, a_BlockZ))->OnDestroyed(ChunkInterface, *m_World, a_BlockX, a_BlockY, a_BlockZ);
- }
+ BlockHandler(GetBlock(a_BlockX, a_BlockY, a_BlockZ))->OnDestroyed(ChunkInterface, *m_World, a_BlockX, a_BlockY, a_BlockZ);
int ChunkX, ChunkZ, X = a_BlockX, Y = a_BlockY, Z = a_BlockZ;
cChunkDef::AbsoluteToRelative( X, Y, Z, ChunkX, ChunkZ);
diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp
index 2e0e86653..ec10ac521 100644
--- a/src/ClientHandle.cpp
+++ b/src/ClientHandle.cpp
@@ -12,12 +12,16 @@
#include "BlockEntities/CommandBlockEntity.h"
#include "BlockEntities/SignEntity.h"
#include "UI/Window.h"
+#include "UI/AnvilWindow.h"
+#include "UI/BeaconWindow.h"
+#include "UI/EnchantingWindow.h"
#include "Item.h"
#include "Mobs/Monster.h"
#include "ChatColor.h"
#include "Items/ItemHandler.h"
#include "Blocks/BlockHandler.h"
#include "Blocks/BlockSlab.h"
+#include "Blocks/BlockBed.h"
#include "Blocks/ChunkInterface.h"
#include "Root.h"
@@ -1498,30 +1502,6 @@ void cClientHandle::HandleAnimation(int a_Animation)
return;
}
- // Because the animation ID sent to servers by clients are different to those sent back, we need this
- switch (a_Animation)
- {
- case 0: // No animation - wiki.vg doesn't say that client has something specific for it, so I suppose it will just become -1
- case 1:
- case 2:
- case 3:
- {
- a_Animation--; // Offset by -1
- break;
- }
- case 5:
- case 6:
- case 7:
- {
- a_Animation -= 2; // Offset by -2
- break;
- }
- default: // Anything else is the same
- {
- break;
- }
- }
-
m_Player->GetWorld()->BroadcastEntityAnimation(*m_Player, a_Animation, this);
}
@@ -1763,7 +1743,9 @@ void cClientHandle::HandleEntityLeaveBed(int a_EntityID)
return;
}
- m_Player->GetWorld()->BroadcastEntityAnimation(*m_Player, 2);
+ cChunkInterface Interface(GetPlayer()->GetWorld()->GetChunkMap());
+ cBlockBedHandler::SetBedOccupationState(Interface, GetPlayer()->GetLastBedPos(), false);
+ GetPlayer()->SetIsInBed(false);
}
diff --git a/src/Endianness.h b/src/Endianness.h
index 9aeb44986..5692b3811 100644
--- a/src/Endianness.h
+++ b/src/Endianness.h
@@ -2,7 +2,7 @@
#pragma once
#undef ntohll
-#define ntohll(x) ((((UInt64)ntohl((u_long)x)) << 32) + ntohl(x >> 32))
+#define ntohll(x) ((((UInt64)ntohl((UInt32)x)) << 32) + ntohl(x >> 32))
@@ -11,10 +11,10 @@
// Changes endianness
inline UInt64 HostToNetwork8(const void * a_Value)
{
- unsigned long long __HostToNetwork8;
- memcpy( &__HostToNetwork8, a_Value, sizeof( __HostToNetwork8));
- __HostToNetwork8 = (( ( (unsigned long long)htonl((u_long)__HostToNetwork8)) << 32) + htonl(__HostToNetwork8 >> 32));
- return __HostToNetwork8;
+ UInt64 buf;
+ memcpy( &buf, a_Value, sizeof( buf));
+ buf = (( ( (UInt64)htonl((UInt32)buf)) << 32) + htonl(buf >> 32));
+ return buf;
}
@@ -23,10 +23,10 @@ inline UInt64 HostToNetwork8(const void * a_Value)
inline UInt32 HostToNetwork4(const void* a_Value)
{
- unsigned int __HostToNetwork4;
- memcpy( &__HostToNetwork4, a_Value, sizeof( __HostToNetwork4));
- __HostToNetwork4 = ntohl( __HostToNetwork4);
- return __HostToNetwork4;
+ UInt32 buf;
+ memcpy( &buf, a_Value, sizeof( buf));
+ buf = ntohl( buf);
+ return buf;
}
diff --git a/src/Entities/ArrowEntity.cpp b/src/Entities/ArrowEntity.cpp
index 0fbbfb681..3c1fabb1b 100644
--- a/src/Entities/ArrowEntity.cpp
+++ b/src/Entities/ArrowEntity.cpp
@@ -72,14 +72,8 @@ bool cArrowEntity::CanPickup(const cPlayer & a_Player) const
void cArrowEntity::OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace)
{
- if (GetSpeed().EqualsEps(Vector3d(0, 0, 0), 0.0000001))
- {
- SetSpeed(GetLookVector().NormalizeCopy() * 0.1); // Ensure that no division by zero happens later
- }
-
Vector3d Hit = a_HitPos;
- Vector3d SinkMovement = (GetSpeed() / 1000);
- Hit += SinkMovement * (0.0005 / SinkMovement.Length()); // Make arrow sink into block a centimetre so it lodges (but not to far so it goes black clientside)
+ Hit += GetSpeed().NormalizeCopy() / 100000; // Make arrow sink into block a bit so it lodges (TODO: investigate how to stop them going so far so that they become black clientside)
super::OnHitSolidBlock(Hit, a_HitFace);
Vector3i BlockHit = Hit.Floor();
@@ -195,11 +189,6 @@ void cArrowEntity::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
if (m_IsInGround)
{
- // When an arrow hits, the client doesn't think its in the ground and keeps on moving, IF BroadcastMovementUpdate() and TeleportEntity was called during flight, AT ALL
- // Fix is to simply not sync with the client and send a teleport to confirm pos after arrow has stabilised (around 1 sec after landing)
- // We can afford to do this because xoft's algorithm for trajectory is near perfect, so things are pretty close anyway without sync
- // Besides, this seems to be what the vanilla server does, note how arrows teleport half a second after they hit to the server position
-
if (!m_HasTeleported) // Sent a teleport already, don't do again
{
if (m_HitGroundTimer > std::chrono::milliseconds(500))
diff --git a/src/Entities/Entity.cpp b/src/Entities/Entity.cpp
index 07cfb97b2..039c58ee4 100644
--- a/src/Entities/Entity.cpp
+++ b/src/Entities/Entity.cpp
@@ -1263,7 +1263,7 @@ bool cEntity::DetectPortal()
{
if (GetWorld()->GetDimension() == dimOverworld)
{
- if (GetWorld()->GetNetherWorldName().empty() && GetWorld()->GetEndWorldName().empty())
+ if (GetWorld()->GetLinkedNetherWorldName().empty() && GetWorld()->GetLinkedEndWorldName().empty())
{
// Teleportation to either dimension not enabled, don't bother proceeding
return false;
@@ -1314,7 +1314,7 @@ bool cEntity::DetectPortal()
}
else
{
- if (GetWorld()->GetNetherWorldName().empty())
+ if (GetWorld()->GetLinkedNetherWorldName().empty())
{
return false;
}
@@ -1327,7 +1327,7 @@ bool cEntity::DetectPortal()
((cPlayer *)this)->GetClientHandle()->SendRespawn(dimNether);
}
- return MoveToWorld(cRoot::Get()->CreateAndInitializeWorld(GetWorld()->GetNetherWorldName(), dimNether, GetWorld()->GetName()), false);
+ return MoveToWorld(cRoot::Get()->CreateAndInitializeWorld(GetWorld()->GetLinkedNetherWorldName(), dimNether, GetWorld()->GetName()), false);
}
}
case E_BLOCK_END_PORTAL:
@@ -1358,7 +1358,7 @@ bool cEntity::DetectPortal()
}
else
{
- if (GetWorld()->GetEndWorldName().empty())
+ if (GetWorld()->GetLinkedEndWorldName().empty())
{
return false;
}
@@ -1371,7 +1371,7 @@ bool cEntity::DetectPortal()
((cPlayer *)this)->GetClientHandle()->SendRespawn(dimEnd);
}
- return MoveToWorld(cRoot::Get()->CreateAndInitializeWorld(GetWorld()->GetEndWorldName(), dimEnd, GetWorld()->GetName()), false);
+ return MoveToWorld(cRoot::Get()->CreateAndInitializeWorld(GetWorld()->GetLinkedEndWorldName(), dimEnd, GetWorld()->GetName()), false);
}
}
@@ -1632,8 +1632,12 @@ void cEntity::TeleportToEntity(cEntity & a_Entity)
void cEntity::TeleportToCoords(double a_PosX, double a_PosY, double a_PosZ)
{
- SetPosition(a_PosX, a_PosY, a_PosZ);
- m_World->BroadcastTeleportEntity(*this);
+ // ask the plugins to allow teleport to the new position.
+ if (!cRoot::Get()->GetPluginManager()->CallHookEntityTeleport(*this, m_LastPos, Vector3d(a_PosX, a_PosY, a_PosZ)))
+ {
+ SetPosition(a_PosX, a_PosY, a_PosZ);
+ m_World->BroadcastTeleportEntity(*this);
+ }
}
diff --git a/src/Entities/HangingEntity.cpp b/src/Entities/HangingEntity.cpp
index a6b9c40c8..a37d8702e 100644
--- a/src/Entities/HangingEntity.cpp
+++ b/src/Entities/HangingEntity.cpp
@@ -11,7 +11,7 @@
cHangingEntity::cHangingEntity(eEntityType a_EntityType, eBlockFace a_Facing, double a_X, double a_Y, double a_Z) :
cEntity(a_EntityType, a_X, a_Y, a_Z, 0.8, 0.8),
- m_Facing(a_Facing)
+ m_Facing(cHangingEntity::BlockFaceToProtocolFace(a_Facing))
{
SetMaxHealth(1);
SetHealth(1);
@@ -21,60 +21,9 @@ cHangingEntity::cHangingEntity(eEntityType a_EntityType, eBlockFace a_Facing, do
-void cHangingEntity::SetFacing(eBlockFace a_Facing)
-{
- // Y-based faces are not allowed:
- switch (a_Facing)
- {
- case BLOCK_FACE_NONE:
- case BLOCK_FACE_YM:
- case BLOCK_FACE_YP:
- {
- LOGWARNING("%s: Invalid facing: %d. Ignoring.", __FUNCTION__, a_Facing);
- ASSERT(!"Tried to set a bad facing!");
- return;
- }
- default: break;
- }
-
- m_Facing = a_Facing;
-}
-
-
-
-
-
void cHangingEntity::SpawnOn(cClientHandle & a_ClientHandle)
{
- int Dir = 0;
-
- // The client uses different values for item frame directions and block faces. Our constants are for the block faces, so we convert them here to item frame faces
- switch (m_Facing)
- {
- case BLOCK_FACE_ZP: Dir = 0; break;
- case BLOCK_FACE_ZM: Dir = 2; break;
- case BLOCK_FACE_XM: Dir = 1; break;
- case BLOCK_FACE_XP: Dir = 3; break;
- default:
- {
- LOGINFO("Invalid facing (%d) in a cHangingEntity at {%d, %d, %d}, adjusting to BLOCK_FACE_XP.",
- m_Facing, (int)GetPosX(), (int)GetPosY(), (int)GetPosZ()
- );
- Dir = 3;
- }
- }
-
- if ((Dir == 0) || (Dir == 2)) // Probably a client bug, but two directions are flipped and contrary to the norm, so we do -180
- {
- SetYaw((Dir * 90) - 180);
- }
- else
- {
- SetYaw(Dir * 90);
- }
-
- a_ClientHandle.SendSpawnObject(*this, 71, Dir, (Byte)GetYaw(), (Byte)GetPitch());
- a_ClientHandle.SendEntityMetadata(*this);
+ SetYaw(GetProtocolFacing() * 90);
}
diff --git a/src/Entities/HangingEntity.h b/src/Entities/HangingEntity.h
index d1ef79a68..507502ac6 100644
--- a/src/Entities/HangingEntity.h
+++ b/src/Entities/HangingEntity.h
@@ -24,28 +24,82 @@ public:
// tolua_begin
/** Returns the direction in which the entity is facing. */
- eBlockFace GetFacing() const { return m_Facing; }
-
+ eBlockFace GetFacing() const { return cHangingEntity::ProtocolFaceToBlockFace(m_Facing); }
+
/** Set the direction in which the entity is facing. */
- void SetFacing(eBlockFace a_Facing);
-
- /** Returns the X coord of the block in which the entity resides. */
- int GetBlockX() const { return POSX_TOINT; }
-
- /** Returns the Y coord of the block in which the entity resides. */
- int GetBlockY() const { return POSY_TOINT; }
-
- /** Returns the Z coord of the block in which the entity resides. */
- int GetBlockZ() const { return POSZ_TOINT; }
+ void SetFacing(eBlockFace a_Facing) { m_Facing = cHangingEntity::BlockFaceToProtocolFace(a_Facing); }
// tolua_end
-private:
+ /** Returns the direction in which the entity is facing. */
+ Byte GetProtocolFacing() const { return m_Facing; }
- virtual void SpawnOn(cClientHandle & a_ClientHandle) override;
- virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override {}
+ /** Set the direction in which the entity is facing. */
+ void SetProtocolFacing(Byte a_Facing)
+ {
+ ASSERT((a_Facing <= 3) && (a_Facing >= 0));
+ m_Facing = a_Facing;
+ }
- eBlockFace m_Facing;
+protected:
+
+ virtual void SpawnOn(cClientHandle & a_ClientHandle) override;
+ virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override
+ {
+ UNUSED(a_Dt);
+ UNUSED(a_Chunk);
+ }
+
+ /** Converts protocol hanging item facing to eBlockFace values */
+ inline static eBlockFace ProtocolFaceToBlockFace(Byte a_ProtocolFace)
+ {
+ eBlockFace Dir;
+
+ // The client uses different values for item frame directions and block faces. Our constants are for the block faces, so we convert them here to item frame faces
+ switch (a_ProtocolFace)
+ {
+ case 0: Dir = BLOCK_FACE_ZP; break;
+ case 2: Dir = BLOCK_FACE_ZM; break;
+ case 1: Dir = BLOCK_FACE_XM; break;
+ case 3: Dir = BLOCK_FACE_XP; break;
+ default:
+ {
+ LOGINFO("Invalid facing (%d) in a cHangingEntity, adjusting to BLOCK_FACE_XP.", a_ProtocolFace);
+ ASSERT(!"Tried to convert a bad facing!");
+
+ Dir = cHangingEntity::ProtocolFaceToBlockFace(3);
+ }
+ }
+
+ return Dir;
+ }
+
+ /** Converts eBlockFace values to protocol hanging item faces */
+ inline static Byte BlockFaceToProtocolFace(eBlockFace a_BlockFace)
+ {
+ Byte Dir;
+
+ // The client uses different values for item frame directions and block faces. Our constants are for the block faces, so we convert them here to item frame faces
+ switch (a_BlockFace)
+ {
+ case BLOCK_FACE_ZP: Dir = 0; break;
+ case BLOCK_FACE_ZM: Dir = 2; break;
+ case BLOCK_FACE_XM: Dir = 1; break;
+ case BLOCK_FACE_XP: Dir = 3; break;
+ default:
+ {
+ // Uncomment when entities are initialised with their real data, instead of dummy values:
+ // LOGINFO("Invalid facing (%d) in a cHangingEntity, adjusting to BLOCK_FACE_XP.", a_BlockFace);
+ // ASSERT(!"Tried to convert a bad facing!");
+
+ Dir = cHangingEntity::BlockFaceToProtocolFace(BLOCK_FACE_XP);
+ }
+ }
+
+ return Dir;
+ }
+
+ Byte m_Facing;
}; // tolua_export
diff --git a/src/Entities/ItemFrame.cpp b/src/Entities/ItemFrame.cpp
index dfffcd3ed..4e6e38f1f 100644
--- a/src/Entities/ItemFrame.cpp
+++ b/src/Entities/ItemFrame.cpp
@@ -92,3 +92,14 @@ void cItemFrame::GetDrops(cItems & a_Items, cEntity * a_Killer)
+
+void cItemFrame::SpawnOn(cClientHandle & a_ClientHandle)
+{
+ super::SpawnOn(a_ClientHandle);
+ a_ClientHandle.SendSpawnObject(*this, 71, GetProtocolFacing(), (Byte)GetYaw(), (Byte)GetPitch());
+ a_ClientHandle.SendEntityMetadata(*this);
+}
+
+
+
+
diff --git a/src/Entities/ItemFrame.h b/src/Entities/ItemFrame.h
index ced8c37af..2444b26a3 100644
--- a/src/Entities/ItemFrame.h
+++ b/src/Entities/ItemFrame.h
@@ -42,6 +42,7 @@ private:
virtual void OnRightClicked(cPlayer & a_Player) override;
virtual void KilledBy(TakeDamageInfo & a_TDI) override;
virtual void GetDrops(cItems & a_Items, cEntity * a_Killer) override;
+ virtual void SpawnOn(cClientHandle & a_ClientHandle) override;
cItem m_Item;
Byte m_ItemRotation;
diff --git a/src/Entities/Minecart.cpp b/src/Entities/Minecart.cpp
index 776f957f4..a32926838 100644
--- a/src/Entities/Minecart.cpp
+++ b/src/Entities/Minecart.cpp
@@ -11,6 +11,7 @@
#include "../Chunk.h"
#include "Player.h"
#include "../BoundingBox.h"
+#include "../UI/MinecartWithChestWindow.h"
#define NO_SPEED 0.0
#define MAX_SPEED 8
diff --git a/src/Entities/Painting.cpp b/src/Entities/Painting.cpp
index 6f6277f28..02a8f6ed0 100644
--- a/src/Entities/Painting.cpp
+++ b/src/Entities/Painting.cpp
@@ -10,10 +10,9 @@
-cPainting::cPainting(const AString & a_Name, int a_Direction, double a_X, double a_Y, double a_Z)
- : cEntity(etPainting, a_X, a_Y, a_Z, 1, 1),
- m_Name(a_Name),
- m_Direction(a_Direction)
+cPainting::cPainting(const AString & a_Name, eBlockFace a_Direction, double a_X, double a_Y, double a_Z)
+ : cHangingEntity(etPainting, a_Direction, a_X, a_Y, a_Z),
+ m_Name(a_Name)
{
}
@@ -24,6 +23,7 @@ cPainting::cPainting(const AString & a_Name, int a_Direction, double a_X, double
void cPainting::SpawnOn(cClientHandle & a_Client)
{
+ super::SpawnOn(a_Client);
a_Client.SendPaintingSpawn(*this);
}
@@ -31,16 +31,6 @@ void cPainting::SpawnOn(cClientHandle & a_Client)
-void cPainting::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
-{
- UNUSED(a_Dt);
- UNUSED(a_Chunk);
-}
-
-
-
-
-
void cPainting::GetDrops(cItems & a_Items, cEntity * a_Killer)
{
if ((a_Killer != nullptr) && a_Killer->IsPlayer() && !((cPlayer *)a_Killer)->IsGameModeCreative())
diff --git a/src/Entities/Painting.h b/src/Entities/Painting.h
index 6e8a382fc..20968d4f0 100644
--- a/src/Entities/Painting.h
+++ b/src/Entities/Painting.h
@@ -1,7 +1,7 @@
#pragma once
-#include "Entity.h"
+#include "HangingEntity.h"
@@ -9,9 +9,9 @@
// tolua_begin
class cPainting :
- public cEntity
+ public cHangingEntity
{
- typedef cEntity super;
+ typedef cHangingEntity super;
public:
@@ -19,19 +19,14 @@ public:
CLASS_PROTODEF(cPainting)
- cPainting(const AString & a_Name, int a_Direction, double a_X, double a_Y, double a_Z);
+ cPainting(const AString & a_Name, eBlockFace a_Direction, double a_X, double a_Y, double a_Z);
- // tolua_begin
-
- const AString & GetName(void) const { return m_Name; }
- int GetDirection(void) const { return m_Direction; }
-
- // tolua_end
+ /** Returns the protocol name of the painting */
+ const AString & GetName(void) const { return m_Name; } // tolua_export
private:
virtual void SpawnOn(cClientHandle & a_Client) override;
- virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
virtual void GetDrops(cItems & a_Items, cEntity * a_Killer) override;
virtual void KilledBy(TakeDamageInfo & a_TDI) override
{
@@ -40,7 +35,6 @@ private:
}
AString m_Name;
- int m_Direction;
}; // tolua_export
diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp
index e1d9f4550..c89e7b87c 100644
--- a/src/Entities/Player.cpp
+++ b/src/Entities/Player.cpp
@@ -5,7 +5,7 @@
#include <unordered_map>
#include "../ChatColor.h"
#include "../Server.h"
-#include "../UI/Window.h"
+#include "../UI/InventoryWindow.h"
#include "../UI/WindowOwner.h"
#include "../World.h"
#include "../Bindings/PluginManager.h"
@@ -232,7 +232,7 @@ void cPlayer::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
}
bool CanMove = true;
- if (!GetPosition().EqualsEps(m_LastPos, 0.01)) // Non negligible change in position from last tick?
+ if (!GetPosition().EqualsEps(m_LastPos, 0.02)) // Non negligible change in position from last tick? 0.02 tp prevent continous calling while floating sometimes.
{
// Apply food exhaustion from movement:
ApplyFoodExhaustionFromMovement();
@@ -396,6 +396,7 @@ int cPlayer::DeltaExperience(int a_Xp_delta)
// Make sure they didn't subtract too much
m_CurrentXp = std::max(m_CurrentXp, 0);
+
// Update total for score calculation
if (a_Xp_delta > 0)
{
@@ -1274,13 +1275,17 @@ unsigned int cPlayer::AwardAchievement(const eStatistic a_Ach)
void cPlayer::TeleportToCoords(double a_PosX, double a_PosY, double a_PosZ)
{
- SetPosition(a_PosX, a_PosY, a_PosZ);
- m_LastGroundHeight = static_cast<float>(a_PosY);
- m_LastJumpHeight = static_cast<float>(a_PosY);
- m_bIsTeleporting = true;
+ // ask plugins to allow teleport to the new position.
+ if (!cRoot::Get()->GetPluginManager()->CallHookEntityTeleport(*this, m_LastPos, Vector3d(a_PosX, a_PosY, a_PosZ)))
+ {
+ SetPosition(a_PosX, a_PosY, a_PosZ);
+ m_LastGroundHeight = static_cast<float>(a_PosY);
+ m_LastJumpHeight = static_cast<float>(a_PosY);
+ m_bIsTeleporting = true;
- m_World->BroadcastTeleportEntity(*this, GetClientHandle());
- m_ClientHandle->SendPlayerMoveLook();
+ m_World->BroadcastTeleportEntity(*this, GetClientHandle());
+ m_ClientHandle->SendPlayerMoveLook();
+ }
}
@@ -1725,9 +1730,9 @@ bool cPlayer::LoadFromFile(const AString & a_FileName, cWorldPtr & a_World)
m_FoodSaturationLevel = root.get("foodSaturation", MAX_FOOD_LEVEL).asDouble();
m_FoodTickTimer = root.get("foodTickTimer", 0).asInt();
m_FoodExhaustionLevel = root.get("foodExhaustion", 0).asDouble();
- m_LifetimeTotalXp = root.get("xpTotal", 0).asInt();
- m_CurrentXp = root.get("xpCurrent", 0).asInt();
- m_IsFlying = root.get("isflying", 0).asBool();
+ m_LifetimeTotalXp = root.get("xpTotal", 0).asInt();
+ m_CurrentXp = root.get("xpCurrent", 0).asInt();
+ m_IsFlying = root.get("isflying", 0).asBool();
m_GameMode = (eGameMode) root.get("gamemode", eGameMode_NotSet).asInt();
diff --git a/src/Entities/Player.h b/src/Entities/Player.h
index e02c66bd3..3dae58dc1 100644
--- a/src/Entities/Player.h
+++ b/src/Entities/Player.h
@@ -314,8 +314,18 @@ public:
// tolua_end
- /** Sets a player's in-bed state; we can't be sure plugins will keep this value updated, so no exporting */
- void SetIsInBed(bool a_Flag) { m_bIsInBed = a_Flag; }
+ /** Sets a player's in-bed state
+ We can't be sure plugins will keep this value updated, so no exporting
+ If value is false (not in bed), will update players of the fact that they have been ejected from the bed
+ */
+ void SetIsInBed(bool a_Flag)
+ {
+ m_bIsInBed = a_Flag;
+ if (!a_Flag)
+ {
+ GetWorld()->BroadcastEntityAnimation(*this, 2);
+ }
+ }
/** Starts eating the currently equipped item. Resets the eating timer and sends the proper animation packet */
void StartEating(void);
diff --git a/src/Entities/ProjectileEntity.cpp b/src/Entities/ProjectileEntity.cpp
index 4f20bfae6..21ec4e63a 100644
--- a/src/Entities/ProjectileEntity.cpp
+++ b/src/Entities/ProjectileEntity.cpp
@@ -334,12 +334,7 @@ AString cProjectileEntity::GetMCAClassName(void) const
void cProjectileEntity::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
super::Tick(a_Dt, a_Chunk);
-
- // TODO: see BroadcastMovementUpdate; RelativeMove packet jerkiness affects projectiles too (cause of sympton described in cArrowEntity::Tick())
- if (GetProjectileKind() != pkArrow)
- {
- BroadcastMovementUpdate();
- }
+ BroadcastMovementUpdate();
}
diff --git a/src/FastRandom.cpp b/src/FastRandom.cpp
index 515dc25ea..737b13535 100644
--- a/src/FastRandom.cpp
+++ b/src/FastRandom.cpp
@@ -6,6 +6,12 @@
#include "Globals.h"
#include "FastRandom.h"
+#ifdef _WIN32
+ #define thread_local __declspec(thread)
+#endif
+
+thread_local unsigned int m_Counter = 0;
+
@@ -86,7 +92,7 @@ public:
cFastRandom::cFastRandom(void) :
- m_LinearRand(static_cast<unsigned>(std::chrono::system_clock::now().time_since_epoch().count()))
+ m_LinearRand(m_Counter++)
{
}
@@ -105,18 +111,6 @@ int cFastRandom::NextInt(int a_Range)
-int cFastRandom::NextInt(int a_Range, int a_Salt)
-{
- m_LinearRand.seed(a_Salt);
- std::uniform_int_distribution<> distribution(0, a_Range - 1);
- return distribution(m_LinearRand);
-}
-
-
-
-
-
-
float cFastRandom::NextFloat(float a_Range)
{
std::uniform_real_distribution<float> distribution(0, a_Range);
@@ -128,18 +122,6 @@ float cFastRandom::NextFloat(float a_Range)
-float cFastRandom::NextFloat(float a_Range, int a_Salt)
-{
- m_LinearRand.seed(a_Salt);
- std::uniform_real_distribution<float> distribution(0, a_Range);
- return distribution(m_LinearRand);
-}
-
-
-
-
-
-
int cFastRandom::GenerateRandomInteger(int a_Begin, int a_End)
{
std::uniform_int_distribution<> distribution(a_Begin, a_End);
@@ -154,7 +136,7 @@ int cFastRandom::GenerateRandomInteger(int a_Begin, int a_End)
// MTRand:
MTRand::MTRand() :
- m_MersenneRand(static_cast<unsigned>(std::chrono::system_clock::now().time_since_epoch().count()))
+ m_MersenneRand(m_Counter++)
{
}
diff --git a/src/FastRandom.h b/src/FastRandom.h
index 64a087c97..30395a293 100644
--- a/src/FastRandom.h
+++ b/src/FastRandom.h
@@ -36,16 +36,10 @@ public:
/** Returns a random int in the range [0 .. a_Range - 1]; a_Range must be less than 1M */
int NextInt(int a_Range);
-
- /** Returns a random int in the range [0 .. a_Range - 1]; a_Range must be less than 1M; a_Salt is additional source of randomness */
- int NextInt(int a_Range, int a_Salt);
/** Returns a random float in the range [0 .. a_Range]; a_Range must be less than 1M */
float NextFloat(float a_Range);
- /** Returns a random float in the range [0 .. a_Range]; a_Range must be less than 1M; a_Salt is additional source of randomness */
- float NextFloat(float a_Range, int a_Salt);
-
/** Returns a random float between 0 and 1. */
float NextFloat(void) { return NextFloat(1); }
diff --git a/src/FurnaceRecipe.cpp b/src/FurnaceRecipe.cpp
index 112aa8146..ea952a852 100644
--- a/src/FurnaceRecipe.cpp
+++ b/src/FurnaceRecipe.cpp
@@ -291,6 +291,22 @@ const cFurnaceRecipe::cRecipe * cFurnaceRecipe::GetRecipeFrom(const cItem & a_In
+bool cFurnaceRecipe::IsFuel(const cItem & a_Item) const
+{
+ for (auto & Fuel : m_pState->Fuel)
+ {
+ if ((Fuel.In->m_ItemType == a_Item.m_ItemType) && (Fuel.In->m_ItemCount <= a_Item.m_ItemCount))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
int cFurnaceRecipe::GetBurnTime(const cItem & a_Fuel) const
{
int BestFuel = 0;
diff --git a/src/FurnaceRecipe.h b/src/FurnaceRecipe.h
index 936ef706d..912b6aba2 100644
--- a/src/FurnaceRecipe.h
+++ b/src/FurnaceRecipe.h
@@ -34,6 +34,9 @@ public:
/** Returns a recipe for the specified input, nullptr if no recipe found */
const cRecipe * GetRecipeFrom(const cItem & a_Ingredient) const;
+
+ /** Returns true if the item is a fuel, false if not. */
+ bool IsFuel(const cItem & a_Item) const;
/** Returns the amount of time that the specified fuel burns, in ticks */
int GetBurnTime(const cItem & a_Fuel) const;
diff --git a/src/Generating/BioGen.cpp b/src/Generating/BioGen.cpp
index 9a7ba4d05..867155ad2 100644
--- a/src/Generating/BioGen.cpp
+++ b/src/Generating/BioGen.cpp
@@ -1036,7 +1036,7 @@ protected:
////////////////////////////////////////////////////////////////////////////////
-// cBioGenGrown:
+// cBioGenProtGrown:
class cBioGenProtGrown:
public cBiomeGen
diff --git a/src/Generating/CompoGenBiomal.cpp b/src/Generating/CompoGenBiomal.cpp
index 030c2baa5..3140bd754 100644
--- a/src/Generating/CompoGenBiomal.cpp
+++ b/src/Generating/CompoGenBiomal.cpp
@@ -542,6 +542,20 @@ protected:
HasHadWater = true;
} // for y
a_ChunkDesc.SetBlockType(a_RelX, 0, a_RelZ, E_BLOCK_BEDROCK);
+
+ EMCSBiome MesaVersion = a_ChunkDesc.GetBiome(a_RelX, a_RelZ);
+ if ((MesaVersion == biMesaPlateauF) || (MesaVersion == biMesaPlateauFM))
+ {
+ if (Top < 95 + static_cast<int>(m_MesaFloor.CubicNoise2D(NoiseY * 2, NoiseX * 2) * 6))
+ {
+ return;
+ }
+
+ BLOCKTYPE Block = (m_MesaFloor.CubicNoise2D(NoiseX * 4, NoiseY * 4) < 0) ? E_BLOCK_DIRT : E_BLOCK_GRASS;
+ NIBBLETYPE Meta = (Block == E_BLOCK_GRASS) ? 0 : 1;
+
+ a_ChunkDesc.SetBlockTypeMeta(a_RelX, Top, a_RelZ, Block, Meta);
+ }
}
diff --git a/src/Generating/ComposableGenerator.cpp b/src/Generating/ComposableGenerator.cpp
index bda45ad92..4a670b064 100644
--- a/src/Generating/ComposableGenerator.cpp
+++ b/src/Generating/ComposableGenerator.cpp
@@ -616,6 +616,11 @@ void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile)
int MaxDensity = a_IniFile.GetValueSetI("Generator", "VillageMaxDensity", 80);
m_FinishGens.push_back(std::make_shared<cVillageGen>(Seed, GridSize, MaxOffset, MaxDepth, MaxSize, MinDensity, MaxDensity, m_BiomeGen, m_CompositedHeightCache));
}
+ else if (NoCaseCompare(*itr, "Vines") == 0)
+ {
+ int Level = a_IniFile.GetValueSetI("Generator", "VinesLevel", 40);
+ m_FinishGens.push_back(std::make_shared<cFinishGenVines>(Seed, Level));
+ }
else if (NoCaseCompare(*itr, "WaterLakes") == 0)
{
int Probability = a_IniFile.GetValueSetI("Generator", "WaterLakesProbability", 25);
diff --git a/src/Generating/FinishGen.cpp b/src/Generating/FinishGen.cpp
index d8fb9c8c0..260253d62 100644
--- a/src/Generating/FinishGen.cpp
+++ b/src/Generating/FinishGen.cpp
@@ -244,6 +244,100 @@ void cFinishGenTallGrass::GenFinish(cChunkDesc & a_ChunkDesc)
////////////////////////////////////////////////////////////////////////////////
+// cFinishGenVines
+
+bool cFinishGenVines::IsJungleVariant(EMCSBiome a_Biome)
+{
+ switch (a_Biome)
+ {
+ case biJungle:
+ case biJungleEdge:
+ case biJungleEdgeM:
+ case biJungleHills:
+ case biJungleM:
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+
+
+
+void cFinishGenVines::GenFinish(cChunkDesc & a_ChunkDesc)
+{
+ for (int x = 0; x < cChunkDef::Width; x++)
+ {
+ int xx = x + a_ChunkDesc.GetChunkX() * cChunkDef::Width;
+ for (int z = 0; z < cChunkDef::Width; z++)
+ {
+ int zz = z + a_ChunkDesc.GetChunkZ() * cChunkDef::Width;
+ if (!IsJungleVariant(a_ChunkDesc.GetBiome(x, z)))
+ {
+ // Current biome isn't a jungle
+ continue;
+ }
+
+ if ((m_Noise.IntNoise2DInt(xx, zz) % 101) < 50)
+ {
+ continue;
+ }
+
+ int Height = a_ChunkDesc.GetHeight(x, z);
+ for (int y = Height; y > m_Level; y--)
+ {
+ if (a_ChunkDesc.GetBlockType(x, y, z) != E_BLOCK_AIR)
+ {
+ // Can't place vines in non-air blocks
+ continue;
+ }
+
+ if ((m_Noise.IntNoise3DInt(xx, y, zz) % 101) < 50)
+ {
+ continue;
+ }
+
+ std::vector<NIBBLETYPE> Places;
+ if ((x + 1 < cChunkDef::Width) && cBlockInfo::FullyOccupiesVoxel(a_ChunkDesc.GetBlockType(x + 1, y, z)))
+ {
+ Places.push_back(8);
+ }
+
+ if ((x - 1 > 0) && cBlockInfo::FullyOccupiesVoxel(a_ChunkDesc.GetBlockType(x - 1, y, z)))
+ {
+ Places.push_back(2);
+ }
+
+ if ((z + 1 < cChunkDef::Width) && cBlockInfo::FullyOccupiesVoxel(a_ChunkDesc.GetBlockType(x, y, z + 1)))
+ {
+ Places.push_back(1);
+ }
+
+ if ((z - 1 > 0) && cBlockInfo::FullyOccupiesVoxel(a_ChunkDesc.GetBlockType(x, y, z - 1)))
+ {
+ Places.push_back(4);
+ }
+
+ if (Places.size() == 0)
+ {
+ continue;
+ }
+
+ NIBBLETYPE Meta = Places[m_Noise.IntNoise3DInt(xx, y, zz) % Places.size()];
+ a_ChunkDesc.SetBlockTypeMeta(x, y, z, E_BLOCK_VINES, Meta);
+ }
+ }
+ }
+}
+
+
+
+
+
+////////////////////////////////////////////////////////////////////////////////
// cFinishGenSprinkleFoliage:
bool cFinishGenSprinkleFoliage::TryAddSugarcane(cChunkDesc & a_ChunkDesc, int a_RelX, int a_RelY, int a_RelZ)
diff --git a/src/Generating/FinishGen.h b/src/Generating/FinishGen.h
index ae6dee590..70696c4f8 100644
--- a/src/Generating/FinishGen.h
+++ b/src/Generating/FinishGen.h
@@ -118,6 +118,29 @@ protected:
+class cFinishGenVines :
+ public cFinishGen
+{
+public:
+ cFinishGenVines(int a_Seed, int a_Level) :
+ m_Noise(a_Seed),
+ m_Level(a_Level)
+ {
+ }
+
+ bool IsJungleVariant(EMCSBiome a_Biome);
+
+protected:
+ cNoise m_Noise;
+ int m_Level;
+
+ virtual void GenFinish(cChunkDesc & a_ChunkDesc) override;
+};
+
+
+
+
+
class cFinishGenSoulsandRims :
public cFinishGen
{
diff --git a/src/Generating/HeiGen.cpp b/src/Generating/HeiGen.cpp
index 61d087c17..54567cb4d 100644
--- a/src/Generating/HeiGen.cpp
+++ b/src/Generating/HeiGen.cpp
@@ -10,6 +10,66 @@
#include "DistortedHeightmap.h"
#include "EndGen.h"
#include "Noise3DGenerator.h"
+#include "ProtIntGen.h"
+
+
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+// cHeiGenSteppy:
+
+class cHeiGenSteppy:
+ public cTerrainHeightGen
+{
+public:
+ cHeiGenSteppy(int a_Seed) :
+ m_Seed(a_Seed)
+ {
+ m_Gen =
+ std::make_shared<cProtIntGenWeightAvg<16, 1, 0>>(
+ std::make_shared<cProtIntGenSmooth> (a_Seed + 1,
+ std::make_shared<cProtIntGenZoom> (a_Seed + 2,
+ std::make_shared<cProtIntGenSmooth> (a_Seed + 3,
+ std::make_shared<cProtIntGenZoom> (a_Seed + 4,
+ std::make_shared<cProtIntGenAddRnd> (a_Seed + 5, 1,
+ std::make_shared<cProtIntGenSmooth> (a_Seed + 6,
+ std::make_shared<cProtIntGenZoom> (a_Seed + 7,
+ std::make_shared<cProtIntGenRndBetween> (a_Seed + 8, 60,
+ std::make_shared<cProtIntGenAddRnd> (a_Seed + 9, 1,
+ std::make_shared<cProtIntGenSmooth> (a_Seed + 1,
+ std::make_shared<cProtIntGenZoom> (a_Seed + 2,
+ std::make_shared<cProtIntGenRndBetween> (a_Seed + 3, 60,
+ std::make_shared<cProtIntGenSmooth> (a_Seed + 4,
+ std::make_shared<cProtIntGenZoom> (a_Seed + 5,
+ std::make_shared<cProtIntGenRndBetween> (a_Seed + 6, 60,
+ std::make_shared<cProtIntGenRndChoice> (a_Seed + 7, 10, 50, 50,
+ std::make_shared<cProtIntGenSmooth> (a_Seed + 8,
+ std::make_shared<cProtIntGenZoom> (a_Seed + 9,
+ std::make_shared<cProtIntGenRndChoice> (a_Seed + 1, 10, 50, 50,
+ std::make_shared<cProtIntGenAddRnd> (a_Seed + 2, 2,
+ std::make_shared<cProtIntGenZoom> (a_Seed + 3,
+ std::make_shared<cProtIntGenZoom> (a_Seed + 4,
+ std::make_shared<cProtIntGenChoice> (a_Seed + 5, 10)
+ )))))))))))))))))))))));
+ }
+
+ // cTerrainHeightGen overrides:
+ virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override
+ {
+ int heights[cChunkDef::Width * cChunkDef::Width];
+ m_Gen->GetInts(a_ChunkX * cChunkDef::Width, a_ChunkZ * cChunkDef::Width, cChunkDef::Width, cChunkDef::Width, heights);
+ for (size_t i = 0; i < ARRAYCOUNT(heights); i++)
+ {
+ a_HeightMap[i] = static_cast<HEIGHTTYPE>(std::max(std::min(60 + heights[i], cChunkDef::Height - 60), 40));
+ }
+ }
+
+protected:
+ int m_Seed;
+
+ SharedPtr<cProtIntGen> m_Gen;
+};
@@ -821,6 +881,10 @@ cTerrainHeightGenPtr cTerrainHeightGen::CreateHeightGen(cIniFile & a_IniFile, cB
// Return an empty pointer, the caller will create the proper generator:
return cTerrainHeightGenPtr();
}
+ else if (NoCaseCompare(HeightGenName, "Steppy") == 0)
+ {
+ res = std::make_shared<cHeiGenSteppy>(a_Seed);
+ }
else if (NoCaseCompare(HeightGenName, "Noise3D") == 0)
{
// Not a heightmap-based generator, but it used to be accessible via HeightGen, so we need to skip making the default out of it
diff --git a/src/Generating/ProtIntGen.h b/src/Generating/ProtIntGen.h
index 73ed27096..e709222fe 100644
--- a/src/Generating/ProtIntGen.h
+++ b/src/Generating/ProtIntGen.h
@@ -318,6 +318,350 @@ protected:
+/** Averages the values of the underlying 2 * 2 neighbors. */
+class cProtIntGenAvgValues :
+ public cProtIntGen
+{
+ typedef cProtIntGen super;
+
+public:
+ cProtIntGenAvgValues(Underlying a_Underlying) :
+ m_Underlying(a_Underlying)
+ {
+ }
+
+
+ virtual void GetInts(int a_MinX, int a_MinZ, int a_SizeX, int a_SizeZ, int * a_Values) override
+ {
+ // Generate the underlying values:
+ int lowerSizeX = a_SizeX + 1;
+ int lowerSizeZ = a_SizeZ + 1;
+ ASSERT(lowerSizeX * lowerSizeZ <= m_BufferSize);
+ int lowerData[m_BufferSize];
+ m_Underlying->GetInts(a_MinX, a_MinZ, lowerSizeX, lowerSizeZ, lowerData);
+
+ // Average - add all 4 "neighbors" and divide by 4:
+ for (int z = 0; z < a_SizeZ; z++)
+ {
+ for (int x = 0; x < a_SizeX; x++)
+ {
+ int idxLower = x + lowerSizeX * z;
+ a_Values[x + a_SizeX * z] = (
+ lowerData[idxLower] + lowerData[idxLower + 1] +
+ lowerData[idxLower + lowerSizeX] + lowerData[idxLower + lowerSizeX + 1]
+ ) / 4;
+ }
+ }
+ }
+
+protected:
+ Underlying m_Underlying;
+};
+
+
+
+
+
+/** Averages the values of the underlying 4 * 4 neighbors. */
+class cProtIntGenAvg4Values :
+ public cProtIntGen
+{
+ typedef cProtIntGen super;
+
+public:
+ cProtIntGenAvg4Values(Underlying a_Underlying) :
+ m_Underlying(a_Underlying)
+ {
+ }
+
+
+ virtual void GetInts(int a_MinX, int a_MinZ, int a_SizeX, int a_SizeZ, int * a_Values) override
+ {
+ // Generate the underlying values:
+ int lowerSizeX = a_SizeX + 4;
+ int lowerSizeZ = a_SizeZ + 4;
+ ASSERT(lowerSizeX * lowerSizeZ <= m_BufferSize);
+ int lowerData[m_BufferSize];
+ m_Underlying->GetInts(a_MinX - 1, a_MinZ - 1, lowerSizeX, lowerSizeZ, lowerData);
+
+ // Calculate the weighted average of all 16 "neighbors":
+ for (int z = 0; z < a_SizeZ; z++)
+ {
+ for (int x = 0; x < a_SizeX; x++)
+ {
+ int idxLower1 = x + lowerSizeX * z;
+ int idxLower2 = idxLower1 + lowerSizeX;
+ int idxLower3 = idxLower1 + 2 * lowerSizeX;
+ int idxLower4 = idxLower1 + 3 * lowerSizeX;
+ a_Values[x + a_SizeX * z] = (
+ 1 * lowerData[idxLower1] + 2 * lowerData[idxLower1 + 1] + 2 * lowerData[idxLower1 + 2] + 1 * lowerData[idxLower1 + 3] +
+ 2 * lowerData[idxLower2] + 32 * lowerData[idxLower2 + 1] + 32 * lowerData[idxLower2 + 2] + 2 * lowerData[idxLower2 + 3] +
+ 2 * lowerData[idxLower3] + 32 * lowerData[idxLower3 + 1] + 32 * lowerData[idxLower3 + 2] + 2 * lowerData[idxLower3 + 3] +
+ 1 * lowerData[idxLower4] + 2 * lowerData[idxLower4 + 1] + 2 * lowerData[idxLower4 + 2] + 1 * lowerData[idxLower4 + 3]
+ ) / 148;
+ }
+ }
+ }
+
+protected:
+ Underlying m_Underlying;
+};
+
+
+
+
+
+/** Averages the values of the underlying 3 * 3 neighbors with custom weight. */
+template <int WeightCenter, int WeightCardinal, int WeightDiagonal>
+class cProtIntGenWeightAvg :
+ public cProtIntGen
+{
+ typedef cProtIntGen super;
+
+public:
+ cProtIntGenWeightAvg(Underlying a_Underlying) :
+ m_Underlying(a_Underlying)
+ {
+ }
+
+
+ virtual void GetInts(int a_MinX, int a_MinZ, int a_SizeX, int a_SizeZ, int * a_Values) override
+ {
+ // Generate the underlying values:
+ int lowerSizeX = a_SizeX + 3;
+ int lowerSizeZ = a_SizeZ + 3;
+ ASSERT(lowerSizeX * lowerSizeZ <= m_BufferSize);
+ int lowerData[m_BufferSize];
+ m_Underlying->GetInts(a_MinX, a_MinZ, lowerSizeX, lowerSizeZ, lowerData);
+
+ // Calculate the weighted average the neighbors:
+ for (int z = 0; z < a_SizeZ; z++)
+ {
+ for (int x = 0; x < a_SizeX; x++)
+ {
+ int idxLower1 = x + lowerSizeX * z;
+ int idxLower2 = idxLower1 + lowerSizeX;
+ int idxLower3 = idxLower1 + 2 * lowerSizeX;
+ a_Values[x + a_SizeX * z] = (
+ WeightDiagonal * lowerData[idxLower1] + WeightCardinal * lowerData[idxLower1 + 1] + WeightDiagonal * lowerData[idxLower1 + 2] +
+ WeightCardinal * lowerData[idxLower2] + WeightCenter * lowerData[idxLower2 + 1] + WeightCardinal * lowerData[idxLower2 + 2] +
+ WeightDiagonal * lowerData[idxLower3] + WeightCardinal * lowerData[idxLower3 + 1] + WeightDiagonal * lowerData[idxLower3 + 2]
+ ) / (4 * WeightDiagonal + 4 * WeightCardinal + WeightCenter);
+ }
+ }
+ }
+
+protected:
+ Underlying m_Underlying;
+};
+
+
+
+
+
+/** Replaces random values of the underlying data with random integers in the specified range [Min .. Min + Range). */
+class cProtIntGenRndChoice :
+ public cProtIntGenWithNoise
+{
+ typedef cProtIntGenWithNoise super;
+
+public:
+ cProtIntGenRndChoice(int a_Seed, int a_ChancePct, int a_Min, int a_Range, Underlying a_Underlying) :
+ super(a_Seed),
+ m_ChancePct(a_ChancePct),
+ m_Min(a_Min),
+ m_Range(a_Range),
+ m_Underlying(a_Underlying)
+ {
+ }
+
+
+ virtual void GetInts(int a_MinX, int a_MinZ, int a_SizeX, int a_SizeZ, int * a_Values) override
+ {
+ // Generate the underlying values:
+ m_Underlying->GetInts(a_MinX, a_MinZ, a_SizeX, a_SizeZ, a_Values);
+
+ // Replace random values:
+ for (int z = 0; z < a_SizeZ; z++)
+ {
+ int BaseZ = a_MinZ + z;
+ for (int x = 0; x < a_SizeX; x++)
+ {
+ if (((super::m_Noise.IntNoise2DInt(BaseZ, a_MinX + x) / 13) % 101) < m_ChancePct)
+ {
+ a_Values[x + a_SizeX * z] = m_Min + (super::m_Noise.IntNoise2DInt(a_MinX + x, BaseZ) / 7) % m_Range;
+ }
+ } // for x
+ } // for z
+ }
+
+protected:
+ int m_ChancePct;
+ int m_Min;
+ int m_Range;
+ Underlying m_Underlying;
+};
+
+
+
+
+
+/** Adds a random value in range [-a_HalfRange, +a_HalfRange] to each of the underlying values. */
+class cProtIntGenAddRnd :
+ public cProtIntGenWithNoise
+{
+ typedef cProtIntGenWithNoise super;
+
+public:
+ cProtIntGenAddRnd(int a_Seed, int a_HalfRange, Underlying a_Underlying) :
+ super(a_Seed),
+ m_Range(a_HalfRange * 2 + 1),
+ m_HalfRange(a_HalfRange),
+ m_Underlying(a_Underlying)
+ {
+ }
+
+
+ virtual void GetInts(int a_MinX, int a_MinZ, int a_SizeX, int a_SizeZ, int * a_Values) override
+ {
+ // Generate the underlying values:
+ m_Underlying->GetInts(a_MinX, a_MinZ, a_SizeX, a_SizeZ, a_Values);
+
+ // Add the random values:
+ for (int z = 0; z < a_SizeZ; z++)
+ {
+ int NoiseZ = a_MinZ + z;
+ for (int x = 0; x < a_SizeX; x++)
+ {
+ int noiseVal = ((super::m_Noise.IntNoise2DInt(a_MinX + x, NoiseZ) / 7) % m_Range) - m_HalfRange;
+ a_Values[x + z * a_SizeX] += noiseVal;
+ }
+ }
+ }
+
+protected:
+ int m_Range;
+ int m_HalfRange;
+ Underlying m_Underlying;
+};
+
+
+
+
+
+/** Replaces random underlying values with the average of the neighbors. */
+class cProtIntGenRndAvg :
+ public cProtIntGenWithNoise
+{
+ typedef cProtIntGenWithNoise super;
+
+public:
+ cProtIntGenRndAvg(int a_Seed, int a_AvgChancePct, Underlying a_Underlying) :
+ super(a_Seed),
+ m_AvgChancePct(a_AvgChancePct),
+ m_Underlying(a_Underlying)
+ {
+ }
+
+
+ virtual void GetInts(int a_MinX, int a_MinZ, int a_SizeX, int a_SizeZ, int * a_Values) override
+ {
+ // Generate the underlying values:
+ int lowerSizeX = a_SizeX + 2;
+ int lowerSizeZ = a_SizeZ + 2;
+ ASSERT(lowerSizeX * lowerSizeZ <= m_BufferSize);
+ int lowerData[m_BufferSize];
+ m_Underlying->GetInts(a_MinX - 1, a_MinZ - 1, lowerSizeX, lowerSizeZ, lowerData);
+
+ // Average random values:
+ for (int z = 0; z < a_SizeZ; z++)
+ {
+ int NoiseZ = a_MinZ + z;
+ for (int x = 0; x < a_SizeX; x++)
+ {
+ int idxLower = x + 1 + lowerSizeX * (z + 1);
+ if (((super::m_Noise.IntNoise2DInt(a_MinX + x, NoiseZ) / 7) % 100) > m_AvgChancePct)
+ {
+ // Average the 4 neighbors:
+ a_Values[x + z * a_SizeX] = (
+ lowerData[idxLower - 1] + lowerData[idxLower + 1] +
+ lowerData[idxLower - lowerSizeX] + lowerData[idxLower + lowerSizeX]
+ ) / 4;
+ }
+ else
+ {
+ // Keep the underlying value:
+ a_Values[x + z * a_SizeX] = lowerData[idxLower];
+ }
+ }
+ }
+ }
+
+protected:
+ int m_AvgChancePct;
+ Underlying m_Underlying;
+};
+
+
+
+
+
+/** Replaces random underlying values with a random value in between the max and min of the neighbors. */
+class cProtIntGenRndBetween :
+ public cProtIntGenWithNoise
+{
+ typedef cProtIntGenWithNoise super;
+
+public:
+ cProtIntGenRndBetween(int a_Seed, int a_AvgChancePct, Underlying a_Underlying) :
+ super(a_Seed),
+ m_AvgChancePct(a_AvgChancePct),
+ m_Underlying(a_Underlying)
+ {
+ }
+
+
+ virtual void GetInts(int a_MinX, int a_MinZ, int a_SizeX, int a_SizeZ, int * a_Values) override
+ {
+ // Generate the underlying values:
+ int lowerSizeX = a_SizeX + 2;
+ int lowerSizeZ = a_SizeZ + 2;
+ ASSERT(lowerSizeX * lowerSizeZ <= m_BufferSize);
+ int lowerData[m_BufferSize];
+ m_Underlying->GetInts(a_MinX - 1, a_MinZ - 1, lowerSizeX, lowerSizeZ, lowerData);
+
+ // Average random values:
+ for (int z = 0; z < a_SizeZ; z++)
+ {
+ int NoiseZ = a_MinZ + z;
+ for (int x = 0; x < a_SizeX; x++)
+ {
+ int idxLower = x + 1 + lowerSizeX * (z + 1);
+ if (((super::m_Noise.IntNoise2DInt(a_MinX + x, NoiseZ) / 7) % 100) > m_AvgChancePct)
+ {
+ // Chose a value in between the min and max neighbor:
+ int min = std::min(std::min(lowerData[idxLower - 1], lowerData[idxLower + 1]), std::min(lowerData[idxLower - lowerSizeX], lowerData[idxLower + lowerSizeX]));
+ int max = std::max(std::max(lowerData[idxLower - 1], lowerData[idxLower + 1]), std::max(lowerData[idxLower - lowerSizeX], lowerData[idxLower + lowerSizeX]));
+ a_Values[x + z * a_SizeX] = min + ((super::m_Noise.IntNoise2DInt(a_MinX + x, NoiseZ + 10) / 7) % (max - min + 1));
+ }
+ else
+ {
+ // Keep the underlying value:
+ a_Values[x + z * a_SizeX] = lowerData[idxLower];
+ }
+ }
+ }
+ }
+
+protected:
+ int m_AvgChancePct;
+ Underlying m_Underlying;
+};
+
+
+
+
+
/** Converts land biomes at the edge of an ocean into the respective beach biome. */
class cProtIntGenBeaches :
public cProtIntGen
diff --git a/src/Generating/Trees.cpp b/src/Generating/Trees.cpp
index a10e0f4f1..9e72a688f 100644
--- a/src/Generating/Trees.cpp
+++ b/src/Generating/Trees.cpp
@@ -224,9 +224,6 @@ void GetTreeImageByBiome(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_No
case biMegaTaiga:
case biMegaTaigaHills:
case biExtremeHillsPlus:
- case biMesa:
- case biMesaPlateauF:
- case biMesaPlateau:
case biSunflowerPlains:
case biDesertM:
case biExtremeHillsM:
@@ -239,9 +236,6 @@ void GetTreeImageByBiome(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_No
case biMegaSpruceTaiga:
case biMegaSpruceTaigaHills:
case biExtremeHillsPlusM:
- case biMesaBryce:
- case biMesaPlateauFM:
- case biMesaPlateauM:
{
// TODO: These need their special trees
GetBirchTreeImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_LogBlocks, a_OtherBlocks);
@@ -264,6 +258,16 @@ void GetTreeImageByBiome(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_No
return;
}
+ case biMesa:
+ case biMesaPlateauF:
+ case biMesaPlateau:
+ case biMesaBryce:
+ case biMesaPlateauFM:
+ case biMesaPlateauM:
+ {
+ GetSmallAppleTreeImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_LogBlocks, a_OtherBlocks);
+ }
+
case biDesert:
case biDesertHills:
case biRiver:
diff --git a/src/Inventory.h b/src/Inventory.h
index 4e76bc0d3..311f64562 100644
--- a/src/Inventory.h
+++ b/src/Inventory.h
@@ -31,7 +31,9 @@ You can use the invArmorOffset, invInventoryOffset and invHotbarOffset constants
*/
class cInventory :
+ // tolua_end
public cItemGrid::cListener
+ // tolua_begin
{
public:
diff --git a/src/Items/ItemPainting.h b/src/Items/ItemPainting.h
index a2a77ce21..d6f2e24b4 100644
--- a/src/Items/ItemPainting.h
+++ b/src/Items/ItemPainting.h
@@ -21,30 +21,17 @@ public:
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
{
- if (a_Dir == BLOCK_FACE_NONE)
+ if ((a_Dir == BLOCK_FACE_NONE) || (a_Dir == BLOCK_FACE_YM) || (a_Dir == BLOCK_FACE_YP))
{
- // Client sends this if clicked on top or bottom face
+ // Paintings can't be flatly placed
return false;
}
AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_Dir); // Make sure block that will be occupied is free
BLOCKTYPE Block = a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ);
- AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_Dir, true); // We want the clicked block, so go back again
if (Block == E_BLOCK_AIR)
{
- int Dir = 0;
-
- // The client uses different values for painting directions and block faces. Our constants are for the block faces, so we convert them here to painting faces
- switch (a_Dir)
- {
- case BLOCK_FACE_ZP: break; // Initialised to zero
- case BLOCK_FACE_ZM: Dir = 2; break;
- case BLOCK_FACE_XM: Dir = 1; break;
- case BLOCK_FACE_XP: Dir = 3; break;
- default: ASSERT(!"Unhandled block face when trying spawn painting!"); return false;
- }
-
static const struct // Define all the possible painting titles
{
AString Title;
@@ -78,7 +65,7 @@ public:
{ "BurningSkull" }
};
- cPainting * Painting = new cPainting(gPaintingTitlesList[a_World->GetTickRandomNumber(ARRAYCOUNT(gPaintingTitlesList) - 1)].Title, Dir, a_BlockX, a_BlockY, a_BlockZ);
+ cPainting * Painting = new cPainting(gPaintingTitlesList[a_World->GetTickRandomNumber(ARRAYCOUNT(gPaintingTitlesList) - 1)].Title, a_Dir, a_BlockX, a_BlockY, a_BlockZ);
Painting->Initialize(*a_World);
if (!a_Player->IsGameModeCreative())
diff --git a/src/Map.cpp b/src/Map.cpp
index fbde00cf7..5e57cc8ec 100644
--- a/src/Map.cpp
+++ b/src/Map.cpp
@@ -66,10 +66,8 @@ void cMapDecorator::Update(void)
{
cFastRandom Random;
- Int64 WorldAge = m_Player->GetWorld()->GetWorldAge();
-
// TODO 2014-02-19 xdot: Refine
- m_Rot = Random.NextInt(16, (int) WorldAge);
+ m_Rot = Random.NextInt(16);
}
else
{
diff --git a/src/Map.h b/src/Map.h
index fe324a5e7..3e775231a 100644
--- a/src/Map.h
+++ b/src/Map.h
@@ -189,6 +189,11 @@ public:
return "cMap";
}
+ const char * GetClass(void) // Needed for ManualBindings' DoWith templates
+ {
+ return "cMap";
+ }
+
protected:
diff --git a/src/MobSpawner.cpp b/src/MobSpawner.cpp
index 541135996..7a5238fd8 100644
--- a/src/MobSpawner.cpp
+++ b/src/MobSpawner.cpp
@@ -110,8 +110,7 @@ eMonsterType cMobSpawner::ChooseMobType(EMCSBiome a_Biome)
if (allowedMobsSize > 0)
{
std::set<eMonsterType>::iterator itr = allowedMobs.begin();
- static int Counter = 0;
- int iRandom = m_Random.NextInt((int)allowedMobsSize, Counter++);
+ int iRandom = m_Random.NextInt((int)allowedMobsSize);
for (int i = 0; i < iRandom; i++)
{
@@ -187,7 +186,7 @@ bool cMobSpawner::CanSpawnHere(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_R
(BlockBelow == E_BLOCK_GRASS) || (BlockBelow == E_BLOCK_LEAVES) || (BlockBelow == E_BLOCK_NEW_LEAVES)
) &&
(a_RelY >= 62) &&
- (Random.NextInt(3, a_Biome) != 0)
+ (Random.NextInt(3) != 0)
);
}
@@ -248,7 +247,7 @@ bool cMobSpawner::CanSpawnHere(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_R
(!cBlockInfo::IsTransparent(BlockBelow)) &&
(SkyLight <= 7) &&
(BlockLight <= 7) &&
- (Random.NextInt(2, a_Biome) == 0)
+ (Random.NextInt(2) == 0)
);
}
@@ -272,7 +271,7 @@ bool cMobSpawner::CanSpawnHere(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_R
(TargetBlock == E_BLOCK_AIR) &&
(BlockAbove == E_BLOCK_AIR) &&
(!cBlockInfo::IsTransparent(BlockBelow)) &&
- (Random.NextInt(20, a_Biome) == 0)
+ (Random.NextInt(20) == 0)
);
}
diff --git a/src/Mobs/AggressiveMonster.cpp b/src/Mobs/AggressiveMonster.cpp
index 72317d66b..526b39e39 100644
--- a/src/Mobs/AggressiveMonster.cpp
+++ b/src/Mobs/AggressiveMonster.cpp
@@ -85,7 +85,7 @@ void cAggressiveMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
if (ReachedFinalDestination() && !LineOfSight.Trace(GetPosition(), AttackDirection, (int)AttackDirection.Length()))
{
// Attack if reached destination, target isn't null, and have a clear line of sight to target (so won't attack through walls)
- Attack(a_Dt / 1000);
+ Attack(a_Dt);
}
}
@@ -95,8 +95,7 @@ void cAggressiveMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
void cAggressiveMonster::Attack(std::chrono::milliseconds a_Dt)
{
- m_AttackInterval += a_Dt.count() * m_AttackRate;
-
+ m_AttackInterval += (static_cast<float>(a_Dt.count()) / 1000) * m_AttackRate;
if ((m_Target == nullptr) || (m_AttackInterval < 3.0))
{
return;
diff --git a/src/Mobs/Blaze.cpp b/src/Mobs/Blaze.cpp
index 172ccd071..89eeb3709 100644
--- a/src/Mobs/Blaze.cpp
+++ b/src/Mobs/Blaze.cpp
@@ -32,7 +32,7 @@ void cBlaze::GetDrops(cItems & a_Drops, cEntity * a_Killer)
void cBlaze::Attack(std::chrono::milliseconds a_Dt)
{
- m_AttackInterval += a_Dt.count() * m_AttackRate;
+ m_AttackInterval += (static_cast<float>(a_Dt.count()) / 1000) * m_AttackRate;
if ((m_Target != nullptr) && (m_AttackInterval > 3.0))
{
diff --git a/src/Mobs/Ghast.cpp b/src/Mobs/Ghast.cpp
index ea0295102..d17047ab7 100644
--- a/src/Mobs/Ghast.cpp
+++ b/src/Mobs/Ghast.cpp
@@ -34,7 +34,7 @@ void cGhast::GetDrops(cItems & a_Drops, cEntity * a_Killer)
void cGhast::Attack(std::chrono::milliseconds a_Dt)
{
- m_AttackInterval += a_Dt.count() * m_AttackRate;
+ m_AttackInterval += (static_cast<float>(a_Dt.count()) / 1000) * m_AttackRate;
if ((m_Target != nullptr) && (m_AttackInterval > 3.0))
{
diff --git a/src/Mobs/Skeleton.cpp b/src/Mobs/Skeleton.cpp
index dd59d6454..331c8e8ad 100644
--- a/src/Mobs/Skeleton.cpp
+++ b/src/Mobs/Skeleton.cpp
@@ -69,8 +69,7 @@ void cSkeleton::MoveToPosition(const Vector3d & a_Position)
void cSkeleton::Attack(std::chrono::milliseconds a_Dt)
{
- m_AttackInterval += a_Dt.count() * m_AttackRate;
-
+ m_AttackInterval += (static_cast<float>(a_Dt.count()) / 1000) * m_AttackRate;
if ((m_Target != nullptr) && (m_AttackInterval > 3.0))
{
// Setting this higher gives us more wiggle room for attackrate
diff --git a/src/OSSupport/CMakeLists.txt b/src/OSSupport/CMakeLists.txt
index 81b37ef0e..0d3c9a63e 100644
--- a/src/OSSupport/CMakeLists.txt
+++ b/src/OSSupport/CMakeLists.txt
@@ -13,6 +13,7 @@ SET (SRCS
HostnameLookup.cpp
IPLookup.cpp
IsThread.cpp
+ NetworkInterfaceEnum.cpp
NetworkSingleton.cpp
Semaphore.cpp
ServerHandleImpl.cpp
diff --git a/src/OSSupport/Network.h b/src/OSSupport/Network.h
index 5dd596223..95a935bbe 100644
--- a/src/OSSupport/Network.h
+++ b/src/OSSupport/Network.h
@@ -318,6 +318,9 @@ public:
If a_Port is 0, the OS is free to assign any port number it likes to the endpoint.
Returns the endpoint object that can be interacted with. */
static cUDPEndpointPtr CreateUDPEndpoint(UInt16 a_Port, cUDPEndpoint::cCallbacks & a_Callbacks);
+
+ /** Returns all local IP addresses for network interfaces currently available. */
+ static AStringVector EnumLocalIPAddresses(void);
};
diff --git a/src/OSSupport/NetworkInterfaceEnum.cpp b/src/OSSupport/NetworkInterfaceEnum.cpp
new file mode 100644
index 000000000..c4af1e93c
--- /dev/null
+++ b/src/OSSupport/NetworkInterfaceEnum.cpp
@@ -0,0 +1,185 @@
+
+// NetworkInterfaceEnum.cpp
+
+// Implements the cNetwork::EnumLocalIPAddresses() interface enumeration function
+
+#include "Globals.h"
+#include "Network.h"
+#include "event2/util.h"
+#ifdef _WIN32
+ #include <IPHlpApi.h>
+ #pragma comment(lib, "IPHLPAPI.lib")
+#else // _WIN32
+ #include <sys/types.h>
+ #include <ifaddrs.h>
+ #include <netinet/in.h>
+ #include <arpa/inet.h>
+#endif // else _WIN32
+
+
+
+
+
+#ifdef SELF_TEST
+
+static class cEnumIPAddressTest
+{
+public:
+ cEnumIPAddressTest(void)
+ {
+ printf("Enumerating all IP addresses...\n");
+ auto IPs = cNetwork::EnumLocalIPAddresses();
+ for (auto & ip: IPs)
+ {
+ printf(" %s\n", ip.c_str());
+ }
+ printf("Done.\n");
+ }
+} g_EnumIPAddressTest;
+
+#endif // SELF_TEST
+
+
+
+
+
+#ifdef _WIN32
+
+/** Converts the SOCKET_ADDRESS structure received from the OS into an IP address string. */
+static AString PrintAddress(SOCKET_ADDRESS & a_Addr)
+{
+ char IP[128];
+ switch (a_Addr.lpSockaddr->sa_family)
+ {
+ case AF_INET:
+ {
+ auto sin = reinterpret_cast<const sockaddr_in *>(a_Addr.lpSockaddr);
+ evutil_inet_ntop(a_Addr.lpSockaddr->sa_family, &(sin->sin_addr), IP, sizeof(IP));
+ break;
+ }
+ case AF_INET6:
+ {
+ auto sin = reinterpret_cast<const sockaddr_in6 *>(a_Addr.lpSockaddr);
+ evutil_inet_ntop(a_Addr.lpSockaddr->sa_family, &(sin->sin6_addr), IP, sizeof(IP));
+ break;
+ }
+ default:
+ {
+ IP[0] = 0;
+ break;
+ }
+ }
+ return IP;
+}
+
+#else // _WIN32
+
+static AString PrintAddress(ifaddrs * InterfaceAddress)
+{
+ switch (InterfaceAddress->ifa_addr->sa_family)
+ {
+ case AF_INET:
+ { // IPv4
+ char AddressBuffer[INET_ADDRSTRLEN];
+ sockaddr_in InternetSocket;
+
+ std::memcpy(&InternetSocket, InterfaceAddress->ifa_addr, sizeof(InternetSocket));
+ inet_ntop(AF_INET, &InternetSocket.sin_addr, AddressBuffer, INET_ADDRSTRLEN);
+ return AddressBuffer;
+ }
+ case AF_INET6:
+ { // IPv6
+ char AddressBuffer[INET6_ADDRSTRLEN];
+ sockaddr_in6 InternetSocket;
+
+ std::memcpy(&InternetSocket, InterfaceAddress->ifa_addr, sizeof(InternetSocket));
+ inet_ntop(AF_INET6, &InternetSocket.sin6_addr, AddressBuffer, INET6_ADDRSTRLEN);
+ return AddressBuffer;
+ }
+ default:
+ {
+ LOG("Unknown address family: %i", InterfaceAddress->ifa_addr->sa_family);
+ return "";
+ }
+ }
+}
+
+#endif // else _WIN32
+
+
+
+
+
+AStringVector cNetwork::EnumLocalIPAddresses(void)
+{
+ AStringVector res;
+
+ #ifdef _WIN32
+
+ // Query the OS for all adapters' addresses:
+ char buffer[64 KiB]; // A buffer backing the address list
+ PIP_ADAPTER_ADDRESSES pAddresses = reinterpret_cast<PIP_ADAPTER_ADDRESSES>(&buffer);
+ ULONG outBufLen = sizeof(buffer);
+ DWORD dwRetVal = GetAdaptersAddresses(
+ AF_UNSPEC,
+ GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_FRIENDLY_NAME, nullptr,
+ pAddresses, &outBufLen
+ );
+ if (dwRetVal != ERROR_SUCCESS)
+ {
+ LOG("GetAdaptersAddresses() failed: %u", dwRetVal);
+ return res;
+ }
+
+ // Enumerate all active adapters
+ for (auto pCurrAddresses = pAddresses; pCurrAddresses != nullptr; pCurrAddresses = pCurrAddresses->Next)
+ {
+ if (pCurrAddresses->OperStatus != IfOperStatusUp)
+ {
+ // Adapter not active, skip it:
+ continue;
+ }
+
+ // Collect all IP addresses on this adapter:
+ for (auto pUnicast = pCurrAddresses->FirstUnicastAddress; pUnicast != nullptr; pUnicast = pUnicast->Next)
+ {
+ auto Address = PrintAddress(pUnicast->Address);
+ if (!Address.empty())
+ {
+ res.push_back(Address);
+ }
+ } // for pUnicast
+ } // for pCurrAddresses
+
+ #else // _WIN32
+
+ struct ifaddrs * ifAddrStruct = nullptr;
+ getifaddrs(&ifAddrStruct);
+
+ for (auto ifa = ifAddrStruct; ifa != nullptr; ifa = ifa->ifa_next)
+ {
+ if (ifa->ifa_addr == nullptr)
+ {
+ continue;
+ }
+
+ auto Address = PrintAddress(ifa);
+ if (!Address.empty())
+ {
+ res.emplace_back(Address);
+ }
+ }
+
+ if (ifAddrStruct != nullptr)
+ {
+ freeifaddrs(ifAddrStruct);
+ }
+
+ #endif // else _WIN32
+
+ return res;
+}
+
+
+
+
diff --git a/src/OSSupport/UDPEndpointImpl.cpp b/src/OSSupport/UDPEndpointImpl.cpp
index ece521ab8..31ca107ce 100644
--- a/src/OSSupport/UDPEndpointImpl.cpp
+++ b/src/OSSupport/UDPEndpointImpl.cpp
@@ -197,6 +197,15 @@ cUDPEndpointImpl::cUDPEndpointImpl(UInt16 a_Port, cUDPEndpoint::cCallbacks & a_C
+cUDPEndpointImpl::~cUDPEndpointImpl()
+{
+ Close();
+}
+
+
+
+
+
void cUDPEndpointImpl::Close(void)
{
if (m_Port == 0)
diff --git a/src/OSSupport/UDPEndpointImpl.h b/src/OSSupport/UDPEndpointImpl.h
index 75942b0cf..0e28d0b13 100644
--- a/src/OSSupport/UDPEndpointImpl.h
+++ b/src/OSSupport/UDPEndpointImpl.h
@@ -35,6 +35,8 @@ public:
If a_Port is 0, the OS is free to assign any port number it likes to the endpoint. */
cUDPEndpointImpl(UInt16 a_Port, cUDPEndpoint::cCallbacks & a_Callbacks);
+ ~cUDPEndpointImpl();
+
// cUDPEndpoint overrides:
virtual void Close(void) override;
virtual bool IsOpen(void) const override;
diff --git a/src/Protocol/Protocol17x.cpp b/src/Protocol/Protocol17x.cpp
index f78c2e54b..9abe81238 100644
--- a/src/Protocol/Protocol17x.cpp
+++ b/src/Protocol/Protocol17x.cpp
@@ -637,7 +637,7 @@ void cProtocol172::SendPaintingSpawn(const cPainting & a_Painting)
Pkt.WriteInt(static_cast<int>(a_Painting.GetPosX()));
Pkt.WriteInt(static_cast<int>(a_Painting.GetPosY()));
Pkt.WriteInt(static_cast<int>(a_Painting.GetPosZ()));
- Pkt.WriteInt(a_Painting.GetDirection());
+ Pkt.WriteInt(a_Painting.GetProtocolFacing());
}
diff --git a/src/Protocol/Protocol18x.cpp b/src/Protocol/Protocol18x.cpp
index 22280f800..2d1a473d1 100644
--- a/src/Protocol/Protocol18x.cpp
+++ b/src/Protocol/Protocol18x.cpp
@@ -632,19 +632,11 @@ void cProtocol180::SendPaintingSpawn(const cPainting & a_Painting)
double PosY = a_Painting.GetPosY();
double PosZ = a_Painting.GetPosZ();
- switch (a_Painting.GetDirection())
- {
- case 0: PosZ += 1; break;
- case 1: PosX -= 1; break;
- case 2: PosZ -= 1; break;
- case 3: PosX += 1; break;
- }
-
cPacketizer Pkt(*this, 0x10); // Spawn Painting packet
Pkt.WriteVarInt(a_Painting.GetUniqueID());
Pkt.WriteString(a_Painting.GetName().c_str());
Pkt.WritePosition((int)PosX, (int)PosY, (int)PosZ);
- Pkt.WriteChar(a_Painting.GetDirection());
+ Pkt.WriteChar(a_Painting.GetProtocolFacing());
}
@@ -2105,9 +2097,7 @@ void cProtocol180::HandlePacketLoginStart(cByteBuffer & a_ByteBuffer)
void cProtocol180::HandlePacketAnimation(cByteBuffer & a_ByteBuffer)
{
- HANDLE_READ(a_ByteBuffer, ReadBEInt, int, EntityID);
- HANDLE_READ(a_ByteBuffer, ReadByte, Byte, Animation);
- m_Client->HandleAnimation(Animation);
+ m_Client->HandleAnimation(1); // Packet exists solely for arm-swing notification
}
diff --git a/src/Server.cpp b/src/Server.cpp
index 3f61be378..df2c7deef 100644
--- a/src/Server.cpp
+++ b/src/Server.cpp
@@ -549,7 +549,7 @@ void cServer::ExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallbac
}
#endif
- else if (cPluginManager::Get()->ExecuteConsoleCommand(split, a_Output))
+ else if (cPluginManager::Get()->ExecuteConsoleCommand(split, a_Output, a_Cmd))
{
a_Output.Finished();
return;
diff --git a/src/Simulator/FloodyFluidSimulator.cpp b/src/Simulator/FloodyFluidSimulator.cpp
index bcd083294..a9481edb0 100644
--- a/src/Simulator/FloodyFluidSimulator.cpp
+++ b/src/Simulator/FloodyFluidSimulator.cpp
@@ -108,8 +108,9 @@ void cFloodyFluidSimulator::SimulateBlock(cChunk * a_Chunk, int a_RelX, int a_Re
{
SpreadXZ(a_Chunk, a_RelX, a_RelY, a_RelZ, NewMeta);
}
+
// If source creation is on, check for it here:
- else if (
+ if (
(m_NumNeighborsForSource > 0) && // Source creation is on
(MyMeta == m_Falloff) && // Only exactly one block away from a source (fast bail-out)
!IsPassableForFluid(Below) && // Only exactly 1 block deep
diff --git a/src/StringUtils.cpp b/src/StringUtils.cpp
index 4eb2d48b6..4adc6a0a0 100644
--- a/src/StringUtils.cpp
+++ b/src/StringUtils.cpp
@@ -140,6 +140,54 @@ AStringVector StringSplit(const AString & str, const AString & delim)
+AStringVector StringSplitWithQuotes(const AString & str, const AString & delim)
+{
+ AStringVector results;
+
+ size_t cutAt = 0;
+ size_t Prev = 0;
+ size_t cutAtQuote = 0;
+
+ while ((cutAt = str.find_first_of(delim, Prev)) != str.npos)
+ {
+ AString current = str.substr(Prev, cutAt - Prev);
+ if ((current.front() == '"') || (current.front() == '\''))
+ {
+ Prev += 1;
+ cutAtQuote = str.find_first_of(current.front(), Prev);
+ if (cutAtQuote != str.npos)
+ {
+ current = str.substr(Prev, cutAtQuote - Prev);
+ cutAt = cutAtQuote + 1;
+ }
+ }
+
+ results.push_back(std::move(current));
+ Prev = cutAt + 1;
+ }
+
+ if (Prev < str.length())
+ {
+ AString current = str.substr(Prev);
+
+ // If the remant is wrapped in matching quotes, remove them:
+ if (
+ (current.length() >= 2) &&
+ ((current.front() == '"') || (current.front() == '\'')) &&
+ (current.front() == current.back())
+ )
+ {
+ current = current.substr(1, current.length() - 2);
+ }
+
+ results.push_back(current);
+ }
+
+ return results;
+}
+
+
+
AStringVector StringSplitAndTrim(const AString & str, const AString & delim)
{
diff --git a/src/StringUtils.h b/src/StringUtils.h
index bc3bb7a2c..785197763 100644
--- a/src/StringUtils.h
+++ b/src/StringUtils.h
@@ -41,6 +41,11 @@ extern AString & AppendPrintf (AString & a_Dst, const char * format, ...) FORMAT
Return the splitted strings as a stringvector. */
extern AStringVector StringSplit(const AString & str, const AString & delim);
+/** Split the string at any of the listed delimiters. Keeps quoted content together
+Resolves issue #490
+Return the splitted strings as a stringvector. */
+extern AStringVector StringSplitWithQuotes(const AString & str, const AString & delim);
+
/** Split the string at any of the listed delimiters and trim each value.
Returns the splitted strings as a stringvector. */
extern AStringVector StringSplitAndTrim(const AString & str, const AString & delim);
diff --git a/src/UI/AnvilWindow.cpp b/src/UI/AnvilWindow.cpp
new file mode 100644
index 000000000..daa35cf47
--- /dev/null
+++ b/src/UI/AnvilWindow.cpp
@@ -0,0 +1,83 @@
+
+// AnvilWindow.cpp
+
+// Representing the UI window for the anvil block
+
+#include "Globals.h"
+#include "AnvilWindow.h"
+#include "SlotArea.h"
+
+
+
+
+cAnvilWindow::cAnvilWindow(int a_BlockX, int a_BlockY, int a_BlockZ) :
+ cWindow(wtAnvil, "Repair"),
+ m_RepairedItemName(""),
+ m_BlockX(a_BlockX),
+ m_BlockY(a_BlockY),
+ m_BlockZ(a_BlockZ)
+{
+ m_AnvilSlotArea = new cSlotAreaAnvil(*this);
+ m_SlotAreas.push_back(m_AnvilSlotArea);
+ m_SlotAreas.push_back(new cSlotAreaInventory(*this));
+ m_SlotAreas.push_back(new cSlotAreaHotBar(*this));
+}
+
+
+
+
+
+AString cAnvilWindow::GetRepairedItemName(void) const
+{
+ return m_RepairedItemName;
+}
+
+
+
+
+
+void cAnvilWindow::SetRepairedItemName(const AString & a_Name, cPlayer * a_Player)
+{
+ m_RepairedItemName = a_Name;
+ if (a_Player != nullptr)
+ {
+ m_AnvilSlotArea->UpdateResult(*a_Player);
+ }
+}
+
+
+
+
+
+void cAnvilWindow::GetBlockPos(int & a_PosX, int & a_PosY, int & a_PosZ)
+{
+ a_PosX = m_BlockX;
+ a_PosY = m_BlockY;
+ a_PosZ = m_BlockZ;
+}
+
+
+
+
+
+void cAnvilWindow::DistributeStack(cItem & a_ItemStack, int a_Slot, cPlayer & a_Player, cSlotArea * a_ClickedArea, bool a_ShouldApply)
+{
+ cSlotAreas AreasInOrder;
+
+ if (a_ClickedArea == m_SlotAreas[0])
+ {
+ // Anvil Slot
+ AreasInOrder.push_back(m_SlotAreas[1]); /* Inventory */
+ AreasInOrder.push_back(m_SlotAreas[2]); /* Hotbar */
+ }
+ else
+ {
+ // Inventory or Hotbar
+ AreasInOrder.push_back(m_SlotAreas[0]); /* Anvil */
+ }
+ super::DistributeStackToAreas(a_ItemStack, a_Player, AreasInOrder, a_ShouldApply, false);
+}
+
+
+
+
diff --git a/src/UI/AnvilWindow.h b/src/UI/AnvilWindow.h
new file mode 100644
index 000000000..e23c744fe
--- /dev/null
+++ b/src/UI/AnvilWindow.h
@@ -0,0 +1,45 @@
+
+// AnvilWindow.h
+
+// Representing the UI window for the anvil block
+
+
+
+
+
+#pragma once
+
+#include "Window.h"
+
+
+
+
+
+class cAnvilWindow :
+ public cWindow
+{
+ typedef cWindow super;
+
+public:
+ cAnvilWindow(int a_BlockX, int a_BlockY, int a_BlockZ);
+
+ /** Gets the repaired item name. */
+ AString GetRepairedItemName(void) const;
+
+ /** Set the repaired item name. */
+ void SetRepairedItemName(const AString & a_Name, cPlayer * a_Player);
+
+ /** Gets the Position from the Anvil */
+ void GetBlockPos(int & a_PosX, int & a_PosY, int & a_PosZ);
+
+ virtual void DistributeStack(cItem & a_ItemStack, int a_Slot, cPlayer & a_Player, cSlotArea * a_ClickedArea, bool a_ShouldApply) override;
+
+protected:
+ cSlotAreaAnvil * m_AnvilSlotArea;
+ AString m_RepairedItemName;
+ int m_BlockX, m_BlockY, m_BlockZ;
+};
+
+
+
+
diff --git a/src/UI/BeaconWindow.cpp b/src/UI/BeaconWindow.cpp
new file mode 100644
index 000000000..c1efa78ad
--- /dev/null
+++ b/src/UI/BeaconWindow.cpp
@@ -0,0 +1,76 @@
+
+// BeaconWindow.cpp
+
+// Representing the UI window for the beacon block
+
+#include "Globals.h"
+#include "BeaconWindow.h"
+#include "SlotArea.h"
+#include "../BlockEntities/BeaconEntity.h"
+#include "../Entities/Player.h"
+
+
+
+
+
+cBeaconWindow::cBeaconWindow(int a_BlockX, int a_BlockY, int a_BlockZ, cBeaconEntity * a_Beacon) :
+ cWindow(wtBeacon, "Beacon"),
+ m_Beacon(a_Beacon)
+{
+ m_SlotAreas.push_back(new cSlotAreaBeacon(m_Beacon, *this));
+ m_SlotAreas.push_back(new cSlotAreaInventory(*this));
+ m_SlotAreas.push_back(new cSlotAreaHotBar(*this));
+}
+
+
+
+
+
+void cBeaconWindow::DistributeStack(cItem & a_ItemStack, int a_Slot, cPlayer & a_Player, cSlotArea * a_ClickedArea, bool a_ShouldApply)
+{
+ cSlotAreas AreasInOrder;
+
+ if (a_ClickedArea == m_SlotAreas[0])
+ {
+ // Beacon Area
+ AreasInOrder.push_back(m_SlotAreas[2]); /* Hotbar */
+ AreasInOrder.push_back(m_SlotAreas[1]); /* Inventory */
+ super::DistributeStackToAreas(a_ItemStack, a_Player, AreasInOrder, a_ShouldApply, true);
+ }
+ else
+ {
+ if (cSlotAreaBeacon::IsPlaceableItem(a_ItemStack.m_ItemType) && (a_ItemStack.m_ItemCount == 1))
+ {
+ AreasInOrder.push_back(m_SlotAreas[0]); /* Beacon */
+ }
+
+ if (a_ClickedArea == m_SlotAreas[1])
+ {
+ // Inventory Area
+ AreasInOrder.push_back(m_SlotAreas[2]); /* Hotbar */
+ }
+ else
+ {
+ // Hotbar Area
+ AreasInOrder.push_back(m_SlotAreas[1]); /* Inventory */
+ }
+ super::DistributeStackToAreas(a_ItemStack, a_Player, AreasInOrder, a_ShouldApply, false);
+ }
+}
+
+
+
+
+
+void cBeaconWindow::OpenedByPlayer(cPlayer & a_Player)
+{
+ super::OpenedByPlayer(a_Player);
+
+ a_Player.GetClientHandle()->SendWindowProperty(*this, 0, m_Beacon->GetBeaconLevel());
+ a_Player.GetClientHandle()->SendWindowProperty(*this, 1, m_Beacon->GetPrimaryEffect());
+ a_Player.GetClientHandle()->SendWindowProperty(*this, 2, m_Beacon->GetSecondaryEffect());
+}
+
+
+
+
diff --git a/src/UI/BeaconWindow.h b/src/UI/BeaconWindow.h
new file mode 100644
index 000000000..fa28b41ba
--- /dev/null
+++ b/src/UI/BeaconWindow.h
@@ -0,0 +1,40 @@
+
+// BeaconWindow.h
+
+// Representing the UI window for the beacon block
+
+
+
+
+
+#pragma once
+
+#include "Window.h"
+#include "../Entities/Player.h"
+
+
+
+
+
+class cBeaconWindow :
+ public cWindow
+{
+ typedef cWindow super;
+
+public:
+ cBeaconWindow(int a_BlockX, int a_BlockY, int a_BlockZ, cBeaconEntity * a_Beacon);
+
+ cBeaconEntity * GetBeaconEntity(void) const { return m_Beacon; }
+
+ virtual void DistributeStack(cItem & a_ItemStack, int a_Slot, cPlayer & a_Player, cSlotArea * a_ClickedArea, bool a_ShouldApply) override;
+
+ // cWindow Overrides:
+ virtual void OpenedByPlayer(cPlayer & a_Player) override;
+
+protected:
+ cBeaconEntity * m_Beacon;
+};
+
+
+
+
diff --git a/src/UI/CMakeLists.txt b/src/UI/CMakeLists.txt
index 2b094ef1d..ef4afc40a 100644
--- a/src/UI/CMakeLists.txt
+++ b/src/UI/CMakeLists.txt
@@ -6,11 +6,32 @@ include_directories ("${PROJECT_SOURCE_DIR}/../")
SET (SRCS
SlotArea.cpp
- Window.cpp)
+ Window.cpp
+ AnvilWindow.cpp
+ BeaconWindow.cpp
+ ChestWindow.cpp
+ CraftingWindow.cpp
+ DropSpenserWindow.cpp
+ EnchantingWindow.cpp
+ EnderChestWindow.cpp
+ FurnaceWindow.cpp
+ HopperWindow.cpp
+ InventoryWindow.cpp)
SET (HDRS
SlotArea.h
Window.h
+ AnvilWindow.h
+ BeaconWindow.h
+ ChestWindow.h
+ CraftingWindow.h
+ DropSpenserWindow.h
+ EnchantingWindow.h
+ EnderChestWindow.h
+ FurnaceWindow.h
+ HopperWindow.h
+ InventoryWindow.h
+ MinecartWithChestWindow.h
WindowOwner.h)
if(NOT MSVC)
diff --git a/src/UI/ChestWindow.cpp b/src/UI/ChestWindow.cpp
new file mode 100644
index 000000000..3766b132d
--- /dev/null
+++ b/src/UI/ChestWindow.cpp
@@ -0,0 +1,141 @@
+
+// ChestWindow.cpp
+
+// Representing the UI window for the chest block
+
+#include "Globals.h"
+#include "ChestWindow.h"
+#include "../BlockEntities/ChestEntity.h"
+#include "../Entities/Player.h"
+
+
+
+
+
+cChestWindow::cChestWindow(cChestEntity * a_Chest) :
+ cWindow(wtChest, (a_Chest->GetBlockType() == E_BLOCK_CHEST) ? "Chest" : "Trapped Chest"),
+ m_World(a_Chest->GetWorld()),
+ m_BlockX(a_Chest->GetPosX()),
+ m_BlockY(a_Chest->GetPosY()),
+ m_BlockZ(a_Chest->GetPosZ()),
+ m_PrimaryChest(a_Chest),
+ m_SecondaryChest(nullptr)
+{
+ m_SlotAreas.push_back(new cSlotAreaChest(a_Chest, *this));
+ m_SlotAreas.push_back(new cSlotAreaInventory(*this));
+ m_SlotAreas.push_back(new cSlotAreaHotBar(*this));
+
+ // Play the opening sound:
+ m_World->BroadcastSoundEffect("random.chestopen", (double)m_BlockX, (double)m_BlockY, (double)m_BlockZ, 1, 1);
+
+ // Send out the chest-open packet:
+ m_World->BroadcastBlockAction(m_BlockX, m_BlockY, m_BlockZ, 1, 1, a_Chest->GetBlockType());
+}
+
+
+
+
+
+cChestWindow::cChestWindow(cChestEntity * a_PrimaryChest, cChestEntity * a_SecondaryChest) :
+ cWindow(wtChest, (a_PrimaryChest->GetBlockType() == E_BLOCK_CHEST) ? "Double Chest" : "Double Trapped Chest"),
+ m_World(a_PrimaryChest->GetWorld()),
+ m_BlockX(a_PrimaryChest->GetPosX()),
+ m_BlockY(a_PrimaryChest->GetPosY()),
+ m_BlockZ(a_PrimaryChest->GetPosZ()),
+ m_PrimaryChest(a_PrimaryChest),
+ m_SecondaryChest(a_SecondaryChest)
+{
+ m_SlotAreas.push_back(new cSlotAreaDoubleChest(a_PrimaryChest, a_SecondaryChest, *this));
+ m_SlotAreas.push_back(new cSlotAreaInventory(*this));
+ m_SlotAreas.push_back(new cSlotAreaHotBar(*this));
+
+ // Play the opening sound:
+ m_World->BroadcastSoundEffect("random.chestopen", (double)m_BlockX, (double)m_BlockY, (double)m_BlockZ, 1, 1);
+
+ // Send out the chest-open packet:
+ m_World->BroadcastBlockAction(m_BlockX, m_BlockY, m_BlockZ, 1, 1, a_PrimaryChest->GetBlockType());
+}
+
+
+
+
+
+cChestWindow::~cChestWindow()
+{
+ // Send out the chest-close packet:
+ m_World->BroadcastBlockAction(m_BlockX, m_BlockY, m_BlockZ, 1, 0, m_PrimaryChest->GetBlockType());
+
+ m_World->BroadcastSoundEffect("random.chestclosed", (double)m_BlockX, (double)m_BlockY, (double)m_BlockZ, 1, 1);
+}
+
+
+
+
+
+bool cChestWindow::ClosedByPlayer(cPlayer & a_Player, bool a_CanRefuse)
+{
+ int ChunkX, ChunkZ;
+
+ m_PrimaryChest->SetNumberOfPlayers(m_PrimaryChest->GetNumberOfPlayers() - 1);
+ cChunkDef::BlockToChunk(m_PrimaryChest->GetPosX(), m_PrimaryChest->GetPosZ(), ChunkX, ChunkZ);
+ m_PrimaryChest->GetWorld()->MarkRedstoneDirty(ChunkX, ChunkZ);
+
+ if (m_SecondaryChest != nullptr)
+ {
+ m_SecondaryChest->SetNumberOfPlayers(m_SecondaryChest->GetNumberOfPlayers() - 1);
+ cChunkDef::BlockToChunk(m_SecondaryChest->GetPosX(), m_SecondaryChest->GetPosZ(), ChunkX, ChunkZ);
+ m_SecondaryChest->GetWorld()->MarkRedstoneDirty(ChunkX, ChunkZ);
+ }
+
+ cWindow::ClosedByPlayer(a_Player, a_CanRefuse);
+ return true;
+}
+
+
+
+
+
+void cChestWindow::OpenedByPlayer(cPlayer & a_Player)
+{
+ int ChunkX, ChunkZ;
+
+ m_PrimaryChest->SetNumberOfPlayers(m_PrimaryChest->GetNumberOfPlayers() + 1);
+ cChunkDef::BlockToChunk(m_PrimaryChest->GetPosX(), m_PrimaryChest->GetPosZ(), ChunkX, ChunkZ);
+ m_PrimaryChest->GetWorld()->MarkRedstoneDirty(ChunkX, ChunkZ);
+
+ if (m_SecondaryChest != nullptr)
+ {
+ m_SecondaryChest->SetNumberOfPlayers(m_SecondaryChest->GetNumberOfPlayers() + 1);
+ cChunkDef::BlockToChunk(m_SecondaryChest->GetPosX(), m_SecondaryChest->GetPosZ(), ChunkX, ChunkZ);
+ m_SecondaryChest->GetWorld()->MarkRedstoneDirty(ChunkX, ChunkZ);
+ }
+
+ cWindow::OpenedByPlayer(a_Player);
+}
+
+
+
+
+
+void cChestWindow::DistributeStack(cItem & a_ItemStack, int a_Slot, cPlayer & a_Player, cSlotArea * a_ClickedArea, bool a_ShouldApply)
+{
+ cSlotAreas AreasInOrder;
+
+ if (a_ClickedArea == m_SlotAreas[0])
+ {
+ // Chest Area
+ AreasInOrder.push_back(m_SlotAreas[2]); /* Hotbar */
+ AreasInOrder.push_back(m_SlotAreas[1]); /* Inventory */
+ super::DistributeStackToAreas(a_ItemStack, a_Player, AreasInOrder, a_ShouldApply, true);
+ }
+ else
+ {
+ // Hotbar or Inventory
+ AreasInOrder.push_back(m_SlotAreas[0]); /* Chest */
+ super::DistributeStackToAreas(a_ItemStack, a_Player, AreasInOrder, a_ShouldApply, false);
+ }
+}
+
+
+
+
diff --git a/src/UI/ChestWindow.h b/src/UI/ChestWindow.h
new file mode 100644
index 000000000..a3b20cdd9
--- /dev/null
+++ b/src/UI/ChestWindow.h
@@ -0,0 +1,45 @@
+
+// ChestWindow.h
+
+// Representing the UI window for the chest block
+
+
+
+
+
+#pragma once
+
+#include "Window.h"
+
+
+
+
+
+class cChestWindow :
+ public cWindow
+{
+ typedef cWindow super;
+
+public:
+ cChestWindow(cChestEntity * a_Chest);
+
+ cChestWindow(cChestEntity * a_PrimaryChest, cChestEntity * a_SecondaryChest);
+
+ ~cChestWindow();
+
+ virtual bool ClosedByPlayer(cPlayer & a_Player, bool a_CanRefuse) override;
+
+ virtual void OpenedByPlayer(cPlayer & a_Player) override;
+
+ virtual void DistributeStack(cItem & a_ItemStack, int a_Slot, cPlayer & a_Player, cSlotArea * a_ClickedArea, bool a_ShouldApply) override;
+
+protected:
+ cWorld * m_World;
+ int m_BlockX, m_BlockY, m_BlockZ; // Position of the chest, for the window-close packet
+ cChestEntity * m_PrimaryChest;
+ cChestEntity * m_SecondaryChest;
+};
+
+
+
+
diff --git a/src/UI/CraftingWindow.cpp b/src/UI/CraftingWindow.cpp
new file mode 100644
index 000000000..ca44056f9
--- /dev/null
+++ b/src/UI/CraftingWindow.cpp
@@ -0,0 +1,61 @@
+
+// CraftingWindow.cpp
+
+// Representing the UI window for the crafting block
+
+#include "Globals.h"
+#include "CraftingWindow.h"
+#include "SlotArea.h"
+
+
+
+
+cCraftingWindow::cCraftingWindow(int a_BlockX, int a_BlockY, int a_BlockZ) :
+ cWindow(wtWorkbench, "Crafting Table")
+{
+ m_SlotAreas.push_back(new cSlotAreaCrafting(3, *this));
+ m_SlotAreas.push_back(new cSlotAreaInventory(*this));
+ m_SlotAreas.push_back(new cSlotAreaHotBar(*this));
+}
+
+
+
+
+
+void cCraftingWindow::DistributeStack(cItem & a_ItemStack, int a_Slot, cPlayer & a_Player, cSlotArea * a_ClickedArea, bool a_ShouldApply)
+{
+ cSlotAreas AreasInOrder;
+
+ if (a_ClickedArea == m_SlotAreas[0])
+ {
+ // Crafting Area
+ if (a_Slot == 0)
+ {
+ // Result Slot
+ AreasInOrder.push_back(m_SlotAreas[2]); /* Hotbar */
+ AreasInOrder.push_back(m_SlotAreas[1]); /* Inventory */
+ }
+ else
+ {
+ AreasInOrder.push_back(m_SlotAreas[1]); /* Inventory */
+ AreasInOrder.push_back(m_SlotAreas[2]); /* Hotbar */
+ }
+ super::DistributeStackToAreas(a_ItemStack, a_Player, AreasInOrder, a_ShouldApply, (a_Slot == 0));
+ }
+ else if (a_ClickedArea == m_SlotAreas[1])
+ {
+ // Inventory Area
+ AreasInOrder.push_back(m_SlotAreas[2]); /* Hotbar */
+ super::DistributeStackToAreas(a_ItemStack, a_Player, AreasInOrder, a_ShouldApply, false);
+ }
+ else
+ {
+ // Hotbar
+ AreasInOrder.push_back(m_SlotAreas[1]); /* Inventory */
+ super::DistributeStackToAreas(a_ItemStack, a_Player, AreasInOrder, a_ShouldApply, false);
+ }
+}
+
+
+
+
diff --git a/src/UI/CraftingWindow.h b/src/UI/CraftingWindow.h
new file mode 100644
index 000000000..01b2da73a
--- /dev/null
+++ b/src/UI/CraftingWindow.h
@@ -0,0 +1,31 @@
+
+// CraftingWindow.h
+
+// Representing the UI window for the crafting block
+
+
+
+
+
+#pragma once
+
+#include "Window.h"
+
+
+
+
+
+class cCraftingWindow :
+ public cWindow
+{
+ typedef cWindow super;
+
+public:
+ cCraftingWindow(int a_BlockX, int a_BlockY, int a_BlockZ);
+
+ virtual void DistributeStack(cItem & a_ItemStack, int a_Slot, cPlayer & a_Player, cSlotArea * a_ClickedArea, bool a_ShouldApply) override;
+};
+
+
+
+
diff --git a/src/UI/DropSpenserWindow.cpp b/src/UI/DropSpenserWindow.cpp
new file mode 100644
index 000000000..aeb7c64b7
--- /dev/null
+++ b/src/UI/DropSpenserWindow.cpp
@@ -0,0 +1,46 @@
+
+// DropSpenserWindow.cpp
+
+// Representing the UI window for the dropper/dispenser block
+
+#include "Globals.h"
+#include "DropSpenserWindow.h"
+
+
+
+
+
+cDropSpenserWindow::cDropSpenserWindow(int a_BlockX, int a_BlockY, int a_BlockZ, cDropSpenserEntity * a_DropSpenser) :
+ cWindow(wtDropSpenser, (a_DropSpenser->GetBlockType() == E_BLOCK_DISPENSER) ? "Dispenser" : "Dropper")
+{
+ m_SlotAreas.push_back(new cSlotAreaItemGrid(a_DropSpenser->GetContents(), *this));
+ m_SlotAreas.push_back(new cSlotAreaInventory(*this));
+ m_SlotAreas.push_back(new cSlotAreaHotBar(*this));
+}
+
+
+
+
+
+void cDropSpenserWindow::DistributeStack(cItem & a_ItemStack, int a_Slot, cPlayer & a_Player, cSlotArea * a_ClickedArea, bool a_ShouldApply)
+{
+ cSlotAreas AreasInOrder;
+
+ if (a_ClickedArea == m_SlotAreas[0])
+ {
+ // DropSpenser Area
+ AreasInOrder.push_back(m_SlotAreas[2]); /* Hotbar */
+ AreasInOrder.push_back(m_SlotAreas[1]); /* Inventory */
+ super::DistributeStackToAreas(a_ItemStack, a_Player, AreasInOrder, a_ShouldApply, true);
+ }
+ else
+ {
+ // Inventory or Hotbar
+ AreasInOrder.push_back(m_SlotAreas[0]); /* DropSpenser */
+ super::DistributeStackToAreas(a_ItemStack, a_Player, AreasInOrder, a_ShouldApply, false);
+ }
+}
+
+
+
+
diff --git a/src/UI/DropSpenserWindow.h b/src/UI/DropSpenserWindow.h
new file mode 100644
index 000000000..edff936e5
--- /dev/null
+++ b/src/UI/DropSpenserWindow.h
@@ -0,0 +1,32 @@
+
+// DropSpenserWindow.h
+
+// Representing the UI window for the dropper/dispenser block
+
+
+
+
+
+#pragma once
+
+#include "Window.h"
+#include "../BlockEntities/DropSpenserEntity.h"
+
+
+
+
+
+class cDropSpenserWindow :
+ public cWindow
+{
+ typedef cWindow super;
+
+public:
+ cDropSpenserWindow(int a_BlockX, int a_BlockY, int a_BlockZ, cDropSpenserEntity * a_DropSpenser);
+
+ virtual void DistributeStack(cItem & a_ItemStack, int a_Slot, cPlayer & a_Player, cSlotArea * a_ClickedArea, bool a_ShouldApply) override;
+};
+
+
+
+
diff --git a/src/UI/EnchantingWindow.cpp b/src/UI/EnchantingWindow.cpp
new file mode 100644
index 000000000..fe21ee83d
--- /dev/null
+++ b/src/UI/EnchantingWindow.cpp
@@ -0,0 +1,100 @@
+
+// EnchantingWindow.cpp
+
+// Representing the UI window for the enchanting block
+
+#include "Globals.h"
+#include "EnchantingWindow.h"
+#include "SlotArea.h"
+
+
+
+
+
+cEnchantingWindow::cEnchantingWindow(int a_BlockX, int a_BlockY, int a_BlockZ) :
+ cWindow(wtEnchantment, "Enchant"),
+ m_SlotArea(),
+ m_BlockX(a_BlockX),
+ m_BlockY(a_BlockY),
+ m_BlockZ(a_BlockZ)
+{
+ m_SlotArea = new cSlotAreaEnchanting(*this, m_BlockX, m_BlockY, m_BlockZ);
+ m_SlotAreas.push_back(m_SlotArea);
+ m_SlotAreas.push_back(new cSlotAreaInventory(*this));
+ m_SlotAreas.push_back(new cSlotAreaHotBar(*this));
+}
+
+
+
+
+
+void cEnchantingWindow::SetProperty(short a_Property, short a_Value, cPlayer & a_Player)
+{
+ if ((a_Property < 0) || ((size_t)a_Property >= ARRAYCOUNT(m_PropertyValue)))
+ {
+ ASSERT(!"a_Property is invalid");
+ return;
+ }
+
+ m_PropertyValue[a_Property] = a_Value;
+ super::SetProperty(a_Property, a_Value, a_Player);
+}
+
+
+
+
+
+
+void cEnchantingWindow::SetProperty(short a_Property, short a_Value)
+{
+ if ((a_Property < 0) || ((size_t)a_Property >= ARRAYCOUNT(m_PropertyValue)))
+ {
+ ASSERT(!"a_Property is invalid");
+ return;
+ }
+
+ m_PropertyValue[a_Property] = a_Value;
+ super::SetProperty(a_Property, a_Value);
+}
+
+
+
+
+
+short cEnchantingWindow::GetPropertyValue(short a_Property)
+{
+ if ((a_Property < 0) || ((size_t)a_Property >= ARRAYCOUNT(m_PropertyValue)))
+ {
+ ASSERT(!"a_Property is invalid");
+ return 0;
+ }
+
+ return m_PropertyValue[a_Property];
+}
+
+
+
+
+
+void cEnchantingWindow::DistributeStack(cItem & a_ItemStack, int a_Slot, cPlayer & a_Player, cSlotArea * a_ClickedArea, bool a_ShouldApply)
+{
+ cSlotAreas AreasInOrder;
+
+ if (a_ClickedArea == m_SlotAreas[0])
+ {
+ // Enchanting Area
+ AreasInOrder.push_back(m_SlotAreas[2]); /* Hotbar */
+ AreasInOrder.push_back(m_SlotAreas[1]); /* Inventory */
+ super::DistributeStackToAreas(a_ItemStack, a_Player, AreasInOrder, a_ShouldApply, true);
+ }
+ else
+ {
+ // Inventory or Hotbar
+ AreasInOrder.push_back(m_SlotAreas[0]); /* Enchanting */
+ super::DistributeStackToAreas(a_ItemStack, a_Player, AreasInOrder, a_ShouldApply, false);
+ }
+}
+
+
+
+
diff --git a/src/UI/EnchantingWindow.h b/src/UI/EnchantingWindow.h
new file mode 100644
index 000000000..bf805c6c8
--- /dev/null
+++ b/src/UI/EnchantingWindow.h
@@ -0,0 +1,44 @@
+
+// EnchantingWindow.h
+
+// Representing the UI window for the enchanting block
+
+
+
+
+
+#pragma once
+
+#include "Window.h"
+
+
+
+
+
+class cEnchantingWindow :
+ public cWindow
+{
+ typedef cWindow super;
+
+public:
+ cEnchantingWindow(int a_BlockX, int a_BlockY, int a_BlockZ);
+
+ virtual void SetProperty(short a_Property, short a_Value, cPlayer & a_Player) override;
+
+ virtual void SetProperty(short a_Property, short a_Value) override;
+
+ /** Return the value of a property */
+ short GetPropertyValue(short a_Property);
+
+ virtual void DistributeStack(cItem & a_ItemStack, int a_Slot, cPlayer & a_Player, cSlotArea * a_ClickedArea, bool a_ShouldApply) override;
+
+ cSlotArea * m_SlotArea;
+
+protected:
+ short m_PropertyValue[3];
+ int m_BlockX, m_BlockY, m_BlockZ;
+};
+
+
+
+
diff --git a/src/UI/EnderChestWindow.cpp b/src/UI/EnderChestWindow.cpp
new file mode 100644
index 000000000..a5484468f
--- /dev/null
+++ b/src/UI/EnderChestWindow.cpp
@@ -0,0 +1,71 @@
+
+// EnderChestWindow.cpp
+
+// Representing the UI window for the enderchest block
+
+#include "Globals.h"
+#include "../World.h"
+#include "EnderChestWindow.h"
+#include "SlotArea.h"
+
+
+
+
+
+cEnderChestWindow::cEnderChestWindow(cEnderChestEntity * a_EnderChest) :
+ cWindow(wtChest, "Ender Chest"),
+ m_World(a_EnderChest->GetWorld()),
+ m_BlockX(a_EnderChest->GetPosX()),
+ m_BlockY(a_EnderChest->GetPosY()),
+ m_BlockZ(a_EnderChest->GetPosZ())
+{
+ m_SlotAreas.push_back(new cSlotAreaEnderChest(a_EnderChest, *this));
+ m_SlotAreas.push_back(new cSlotAreaInventory(*this));
+ m_SlotAreas.push_back(new cSlotAreaHotBar(*this));
+
+ // Play the opening sound:
+ m_World->BroadcastSoundEffect("random.chestopen", (double)m_BlockX, (double)m_BlockY, (double)m_BlockZ, 1, 1);
+
+ // Send out the chest-open packet:
+ m_World->BroadcastBlockAction(m_BlockX, m_BlockY, m_BlockZ, 1, 1, E_BLOCK_ENDER_CHEST);
+}
+
+
+
+
+
+cEnderChestWindow::~cEnderChestWindow()
+{
+ // Send out the chest-close packet:
+ m_World->BroadcastBlockAction(m_BlockX, m_BlockY, m_BlockZ, 1, 0, E_BLOCK_ENDER_CHEST);
+
+ // Play the closing sound
+ m_World->BroadcastSoundEffect("random.chestclosed", (double)m_BlockX, (double)m_BlockY, (double)m_BlockZ, 1, 1);
+}
+
+
+
+
+
+void cEnderChestWindow::DistributeStack(cItem & a_ItemStack, int a_Slot, cPlayer & a_Player, cSlotArea * a_ClickedArea, bool a_ShouldApply)
+{
+ cSlotAreas AreasInOrder;
+
+ if (a_ClickedArea == m_SlotAreas[0])
+ {
+ // Chest Area
+ AreasInOrder.push_back(m_SlotAreas[2]); /* Hotbar */
+ AreasInOrder.push_back(m_SlotAreas[1]); /* Inventory */
+ super::DistributeStackToAreas(a_ItemStack, a_Player, AreasInOrder, a_ShouldApply, true);
+ }
+ else
+ {
+ // Hotbar or Inventory
+ AreasInOrder.push_back(m_SlotAreas[0]); /* Chest */
+ super::DistributeStackToAreas(a_ItemStack, a_Player, AreasInOrder, a_ShouldApply, false);
+ }
+}
+
+
+
+
diff --git a/src/UI/EnderChestWindow.h b/src/UI/EnderChestWindow.h
new file mode 100644
index 000000000..006a490bf
--- /dev/null
+++ b/src/UI/EnderChestWindow.h
@@ -0,0 +1,38 @@
+
+// EnderChestWindow.h
+
+// Representing the UI window for the enderchest block
+
+
+
+
+
+#pragma once
+
+#include "Window.h"
+#include "../BlockEntities/EnderChestEntity.h"
+
+
+
+
+
+class cEnderChestWindow :
+ public cWindow
+{
+ typedef cWindow super;
+
+public:
+ cEnderChestWindow(cEnderChestEntity * a_EnderChest);
+
+ ~cEnderChestWindow();
+
+ virtual void DistributeStack(cItem & a_ItemStack, int a_Slot, cPlayer & a_Player, cSlotArea * a_ClickedArea, bool a_ShouldApply) override;
+
+protected:
+ cWorld * m_World;
+ int m_BlockX, m_BlockY, m_BlockZ; // Position of the enderchest, for the window-close packet
+};
+
+
+
+
diff --git a/src/UI/FurnaceWindow.cpp b/src/UI/FurnaceWindow.cpp
new file mode 100644
index 000000000..132439ff3
--- /dev/null
+++ b/src/UI/FurnaceWindow.cpp
@@ -0,0 +1,74 @@
+
+// FurnaceWindow.cpp
+
+// Representing the UI window for the furnace block
+
+#include "Globals.h"
+#include "FurnaceWindow.h"
+#include "SlotArea.h"
+#include "../FurnaceRecipe.h"
+#include "../Root.h"
+
+
+
+
+
+cFurnaceWindow::cFurnaceWindow(int a_BlockX, int a_BlockY, int a_BlockZ, cFurnaceEntity * a_Furnace) :
+ cWindow(wtFurnace, "Furnace")
+{
+ m_SlotAreas.push_back(new cSlotAreaFurnace(a_Furnace, *this));
+ m_SlotAreas.push_back(new cSlotAreaInventory(*this));
+ m_SlotAreas.push_back(new cSlotAreaHotBar(*this));
+}
+
+
+
+
+
+void cFurnaceWindow::DistributeStack(cItem & a_ItemStack, int a_Slot, cPlayer & a_Player, cSlotArea * a_ClickedArea, bool a_ShouldApply)
+{
+ cSlotAreas AreasInOrder;
+
+ if (a_ClickedArea == m_SlotAreas[0])
+ {
+ // Furnace Area
+ if (a_Slot == 2)
+ {
+ // Result Slot
+ AreasInOrder.push_back(m_SlotAreas[2]); /* Hotbar */
+ AreasInOrder.push_back(m_SlotAreas[1]); /* Inventory */
+ super::DistributeStackToAreas(a_ItemStack, a_Player, AreasInOrder, a_ShouldApply, true);
+ }
+ else
+ {
+ // Furnace Input/Fuel Slot
+ AreasInOrder.push_back(m_SlotAreas[1]); /* Inventory */
+ AreasInOrder.push_back(m_SlotAreas[2]); /* Hotbar */
+ super::DistributeStackToAreas(a_ItemStack, a_Player, AreasInOrder, a_ShouldApply, false);
+ }
+ }
+ else
+ {
+ cFurnaceRecipe * FurnaceRecipes = cRoot::Get()->GetFurnaceRecipe();
+ if ((FurnaceRecipes->GetRecipeFrom(a_ItemStack) != nullptr) || (FurnaceRecipes->IsFuel(a_ItemStack)))
+ {
+ // The item is a valid input item or fuel
+ AreasInOrder.push_back(m_SlotAreas[0]); /* Furnace Area */
+ }
+ else if (a_ClickedArea == m_SlotAreas[1])
+ {
+ // Inventory Area
+ AreasInOrder.push_back(m_SlotAreas[2]); /* Hotbar */
+ }
+ else
+ {
+ // Hotbar Area
+ AreasInOrder.push_back(m_SlotAreas[1]); /* Inventory */
+ }
+ super::DistributeStackToAreas(a_ItemStack, a_Player, AreasInOrder, a_ShouldApply, false);
+ }
+}
+
+
+
+
diff --git a/src/UI/FurnaceWindow.h b/src/UI/FurnaceWindow.h
new file mode 100644
index 000000000..845505f8e
--- /dev/null
+++ b/src/UI/FurnaceWindow.h
@@ -0,0 +1,32 @@
+
+// FurnaceWindow.h
+
+// Representing the UI window for the furnace block
+
+
+
+
+
+#pragma once
+
+#include "Window.h"
+
+
+
+
+
+class cFurnaceWindow :
+ public cWindow
+{
+ typedef cWindow super;
+
+public:
+ cFurnaceWindow(int a_BlockX, int a_BlockY, int a_BlockZ, cFurnaceEntity * a_Furnace);
+
+ virtual void DistributeStack(cItem & a_ItemStack, int a_Slot, cPlayer & a_Player, cSlotArea * a_ClickedArea, bool a_ShouldApply) override;
+
+};
+
+
+
+
diff --git a/src/UI/HopperWindow.cpp b/src/UI/HopperWindow.cpp
new file mode 100644
index 000000000..79f0767e8
--- /dev/null
+++ b/src/UI/HopperWindow.cpp
@@ -0,0 +1,48 @@
+
+// HopperWindow.cpp
+
+// Representing the UI window for the hopper block
+
+#include "Globals.h"
+#include "../BlockEntities/HopperEntity.h"
+#include "HopperWindow.h"
+#include "../BlockEntities/DropperEntity.h"
+
+
+
+
+
+cHopperWindow::cHopperWindow(int a_BlockX, int a_BlockY, int a_BlockZ, cHopperEntity * a_Hopper) :
+ super(wtHopper, "Hopper")
+{
+ m_SlotAreas.push_back(new cSlotAreaItemGrid(a_Hopper->GetContents(), *this));
+ m_SlotAreas.push_back(new cSlotAreaInventory(*this));
+ m_SlotAreas.push_back(new cSlotAreaHotBar(*this));
+}
+
+
+
+
+
+void cHopperWindow::DistributeStack(cItem & a_ItemStack, int a_Slot, cPlayer & a_Player, cSlotArea * a_ClickedArea, bool a_ShouldApply)
+{
+ cSlotAreas AreasInOrder;
+
+ if (a_ClickedArea == m_SlotAreas[0])
+ {
+ // Hopper Area
+ AreasInOrder.push_back(m_SlotAreas[2]); /* Hotbar */
+ AreasInOrder.push_back(m_SlotAreas[1]); /* Inventory */
+ super::DistributeStackToAreas(a_ItemStack, a_Player, AreasInOrder, a_ShouldApply, true);
+ }
+ else
+ {
+ // Inventory or Hotbar
+ AreasInOrder.push_back(m_SlotAreas[0]); /* Hopper */
+ super::DistributeStackToAreas(a_ItemStack, a_Player, AreasInOrder, a_ShouldApply, false);
+ }
+}
+
+
+
+
diff --git a/src/UI/HopperWindow.h b/src/UI/HopperWindow.h
new file mode 100644
index 000000000..2dec08666
--- /dev/null
+++ b/src/UI/HopperWindow.h
@@ -0,0 +1,32 @@
+
+// HopperWindow.h
+
+// Representing the UI window for the hopper block
+
+
+
+
+
+#pragma once
+
+#include "Window.h"
+
+
+
+
+
+class cHopperWindow :
+ public cWindow
+{
+ typedef cWindow super;
+
+public:
+ cHopperWindow(int a_BlockX, int a_BlockY, int a_BlockZ, cHopperEntity * a_Hopper);
+
+ virtual void DistributeStack(cItem & a_ItemStack, int a_Slot, cPlayer & a_Player, cSlotArea * a_ClickedArea, bool a_ShouldApply) override;
+
+};
+
+
+
+
diff --git a/src/UI/InventoryWindow.cpp b/src/UI/InventoryWindow.cpp
new file mode 100644
index 000000000..0f876e559
--- /dev/null
+++ b/src/UI/InventoryWindow.cpp
@@ -0,0 +1,73 @@
+
+// InventoryWindow.cpp
+
+// Representing the UI window for the player inventory
+
+#include "Globals.h"
+#include "InventoryWindow.h"
+#include "SlotArea.h"
+
+
+
+
+
+cInventoryWindow::cInventoryWindow(cPlayer & a_Player) :
+ cWindow(wtInventory, "Inventory"),
+ m_Player(a_Player)
+{
+ m_SlotAreas.push_back(new cSlotAreaCrafting(2, *this)); // The creative inventory doesn't display it, but it's still counted into slot numbers
+ m_SlotAreas.push_back(new cSlotAreaArmor(*this));
+ m_SlotAreas.push_back(new cSlotAreaInventory(*this));
+ m_SlotAreas.push_back(new cSlotAreaHotBar(*this));
+}
+
+
+
+
+
+void cInventoryWindow::DistributeStack(cItem & a_ItemStack, int a_Slot, cPlayer & a_Player, cSlotArea * a_ClickedArea, bool a_ShouldApply)
+{
+ cSlotAreas AreasInOrder;
+
+ if (a_ClickedArea == m_SlotAreas[0])
+ {
+ // Crafting Area
+ if (a_Slot == 0)
+ {
+ // Result Slot
+ AreasInOrder.push_back(m_SlotAreas[3]); /* Hotbar */
+ AreasInOrder.push_back(m_SlotAreas[2]); /* Inventory */
+ }
+ else
+ {
+ AreasInOrder.push_back(m_SlotAreas[2]); /* Inventory */
+ AreasInOrder.push_back(m_SlotAreas[3]); /* Hotbar */
+ }
+ super::DistributeStackToAreas(a_ItemStack, a_Player, AreasInOrder, a_ShouldApply, (a_Slot == 0));
+ }
+ else if (a_ClickedArea == m_SlotAreas[1])
+ {
+ // Armor Area
+ AreasInOrder.push_back(m_SlotAreas[2]); /* Inventory */
+ AreasInOrder.push_back(m_SlotAreas[3]); /* Hotbar */
+ super::DistributeStackToAreas(a_ItemStack, a_Player, AreasInOrder, a_ShouldApply, false);
+ }
+ else if (a_ClickedArea == m_SlotAreas[2])
+ {
+ // Inventory Area
+ AreasInOrder.push_back(m_SlotAreas[1]); /* Armor */
+ AreasInOrder.push_back(m_SlotAreas[3]); /* Hotbar */
+ super::DistributeStackToAreas(a_ItemStack, a_Player, AreasInOrder, a_ShouldApply, false);
+ }
+ else
+ {
+ // Hotbar
+ AreasInOrder.push_back(m_SlotAreas[1]); /* Armor */
+ AreasInOrder.push_back(m_SlotAreas[2]); /* Inventory */
+ super::DistributeStackToAreas(a_ItemStack, a_Player, AreasInOrder, a_ShouldApply, false);
+ }
+}
+
+
+
+
diff --git a/src/UI/InventoryWindow.h b/src/UI/InventoryWindow.h
new file mode 100644
index 000000000..10952d37f
--- /dev/null
+++ b/src/UI/InventoryWindow.h
@@ -0,0 +1,34 @@
+
+// InventoryWindow.h
+
+// Representing the UI window for the player inventory
+
+
+
+
+
+#pragma once
+
+#include "Window.h"
+
+
+
+
+
+class cInventoryWindow :
+ public cWindow
+{
+ typedef cWindow super;
+
+public:
+ cInventoryWindow(cPlayer & a_Player);
+
+ virtual void DistributeStack(cItem & a_ItemStack, int a_Slot, cPlayer & a_Player, cSlotArea * a_ClickedArea, bool a_ShouldApply) override;
+
+protected:
+ cPlayer & m_Player;
+};
+
+
+
+
diff --git a/src/UI/MinecartWithChestWindow.h b/src/UI/MinecartWithChestWindow.h
new file mode 100644
index 000000000..a2b5283a6
--- /dev/null
+++ b/src/UI/MinecartWithChestWindow.h
@@ -0,0 +1,67 @@
+
+// MinecartWithChestWindow.h
+
+// Representing the UI window for the minecart chest entity
+
+
+
+
+
+#pragma once
+
+#include "Window.h"
+#include "../Entities/Minecart.h"
+
+
+
+
+
+class cMinecartWithChestWindow :
+ public cWindow
+{
+ typedef cWindow super;
+
+public:
+ cMinecartWithChestWindow(cMinecartWithChest * a_ChestCart) :
+ cWindow(wtChest, "Minecart with Chest"),
+ m_ChestCart(a_ChestCart)
+ {
+ m_SlotAreas.push_back(new cSlotAreaMinecartWithChest(a_ChestCart, *this));
+ m_SlotAreas.push_back(new cSlotAreaInventory(*this));
+ m_SlotAreas.push_back(new cSlotAreaHotBar(*this));
+
+ a_ChestCart->GetWorld()->BroadcastSoundEffect("random.chestopen", a_ChestCart->GetPosX(), a_ChestCart->GetPosY(), a_ChestCart->GetPosZ(), 1, 1);
+ }
+
+ virtual void DistributeStack(cItem & a_ItemStack, int a_Slot, cPlayer & a_Player, cSlotArea* a_ClickedArea, bool a_ShouldApply) override
+ {
+ cSlotAreas AreasInOrder;
+
+ if (a_ClickedArea == m_SlotAreas[0])
+ {
+ // Chest Area
+ AreasInOrder.push_back(m_SlotAreas[2]); /* Hotbar */
+ AreasInOrder.push_back(m_SlotAreas[1]); /* Inventory */
+ super::DistributeStackToAreas(a_ItemStack, a_Player, AreasInOrder, a_ShouldApply, true);
+ }
+ else
+ {
+ // Hotbar or Inventory
+ AreasInOrder.push_back(m_SlotAreas[0]); /* Chest */
+ super::DistributeStackToAreas(a_ItemStack, a_Player, AreasInOrder, a_ShouldApply, false);
+ }
+ }
+
+
+ ~cMinecartWithChestWindow()
+ {
+ m_ChestCart->GetWorld()->BroadcastSoundEffect("random.chestclosed", m_ChestCart->GetPosX(), m_ChestCart->GetPosY(), m_ChestCart->GetPosZ(), 1, 1);
+ }
+
+private:
+ cMinecartWithChest * m_ChestCart;
+};
+
+
+
+
diff --git a/src/UI/SlotArea.cpp b/src/UI/SlotArea.cpp
index e784569d9..37683a8e5 100644
--- a/src/UI/SlotArea.cpp
+++ b/src/UI/SlotArea.cpp
@@ -1,3 +1,4 @@
+
// SlotArea.cpp
// Implements the cSlotArea class and its descendants
@@ -12,6 +13,7 @@
#include "../BlockEntities/FurnaceEntity.h"
#include "../Entities/Minecart.h"
#include "../Items/ItemHandler.h"
+#include "AnvilWindow.h"
#include "Window.h"
#include "../CraftingRecipes.h"
#include "../Root.h"
@@ -205,7 +207,7 @@ void cSlotArea::ShiftClicked(cPlayer & a_Player, int a_SlotNum, const cItem & a_
{
// Make a copy of the slot, distribute it among the other areas, then update the slot to contain the leftover:
cItem Slot(*GetSlot(a_SlotNum, a_Player));
- m_ParentWindow.DistributeStack(Slot, a_Player, this, true);
+ m_ParentWindow.DistributeStack(Slot, a_SlotNum, a_Player, this, true);
if (Slot.IsEmpty())
{
// Empty the slot completely, the client doesn't like left-over ItemType with zero count
@@ -340,31 +342,31 @@ void cSlotArea::OnPlayerRemoved(cPlayer & a_Player)
-void cSlotArea::DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots)
+void cSlotArea::DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots, bool a_BackFill)
{
for (int i = 0; i < m_NumSlots; i++)
{
- const cItem * Slot = GetSlot(i, a_Player);
+ int SlotNum = (a_BackFill) ? (m_NumSlots - 1 - i) : i;
+
+ const cItem * Slot = GetSlot(SlotNum, a_Player);
if (!Slot->IsEqual(a_ItemStack) && (!Slot->IsEmpty() || a_KeepEmptySlots))
{
// Different items
continue;
}
- int NumFit = ItemHandler(Slot->m_ItemType)->GetMaxStackSize() - Slot->m_ItemCount;
+ char NumFit = ItemHandler(Slot->m_ItemType)->GetMaxStackSize() - Slot->m_ItemCount;
if (NumFit <= 0)
{
// Full stack already
continue;
}
- if (NumFit > a_ItemStack.m_ItemCount)
- {
- NumFit = a_ItemStack.m_ItemCount;
- }
+ NumFit = std::min(NumFit, a_ItemStack.m_ItemCount);
+
if (a_ShouldApply)
{
cItem NewSlot(a_ItemStack);
NewSlot.m_ItemCount = Slot->m_ItemCount + NumFit;
- SetSlot(i, a_Player, NewSlot);
+ SetSlot(SlotNum, a_Player, NewSlot);
}
a_ItemStack.m_ItemCount -= NumFit;
if (a_ItemStack.IsEmpty())
@@ -589,12 +591,13 @@ void cSlotAreaCrafting::SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem &
-void cSlotAreaCrafting::DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots)
+void cSlotAreaCrafting::DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots, bool a_BackFill)
{
UNUSED(a_ItemStack);
UNUSED(a_Player);
UNUSED(a_ShouldApply);
UNUSED(a_KeepEmptySlots);
+ UNUSED(a_BackFill);
}
@@ -656,7 +659,7 @@ void cSlotAreaCrafting::ShiftClickedResult(cPlayer & a_Player)
{
// Try distributing the result. If it fails, bail out:
cItem ResultCopy(Result);
- m_ParentWindow.DistributeStack(ResultCopy, a_Player, this, false);
+ m_ParentWindow.DistributeStack(ResultCopy, 0, a_Player, this, false);
if (!ResultCopy.IsEmpty())
{
// Couldn't distribute all of it. Bail out
@@ -665,7 +668,7 @@ void cSlotAreaCrafting::ShiftClickedResult(cPlayer & a_Player)
// Distribute the result, this time for real:
ResultCopy = Result;
- m_ParentWindow.DistributeStack(ResultCopy, a_Player, this, true);
+ m_ParentWindow.DistributeStack(ResultCopy, 0, a_Player, this, true);
// Remove the ingredients from the crafting grid and update the recipe:
cCraftingRecipe & Recipe = GetRecipeForPlayer(a_Player);
@@ -769,7 +772,7 @@ void cSlotAreaCrafting::HandleCraftItem(const cItem & a_Result, cPlayer & a_Play
////////////////////////////////////////////////////////////////////////////////
// cSlotAreaAnvil:
-cSlotAreaAnvil::cSlotAreaAnvil(cAnvilWindow & a_ParentWindow) :
+cSlotAreaAnvil::cSlotAreaAnvil(cWindow & a_ParentWindow) :
cSlotAreaTemporary(3, a_ParentWindow),
m_MaximumCost(0),
m_StackSizeToBeUsedInRepair(0)
@@ -894,7 +897,7 @@ void cSlotAreaAnvil::ShiftClicked(cPlayer & a_Player, int a_SlotNum, const cItem
return;
}
- m_ParentWindow.DistributeStack(Slot, a_Player, this, true);
+ m_ParentWindow.DistributeStack(Slot, a_SlotNum, a_Player, this, true);
if (Slot.IsEmpty())
{
Slot.Empty();
@@ -910,31 +913,31 @@ void cSlotAreaAnvil::ShiftClicked(cPlayer & a_Player, int a_SlotNum, const cItem
-void cSlotAreaAnvil::DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots)
+void cSlotAreaAnvil::DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots, bool a_BackFill)
{
for (int i = 0; i < 2; i++)
{
- const cItem * Slot = GetSlot(i, a_Player);
+ int SlotNum = (a_BackFill) ? (2 - 1 - i) : i;
+
+ const cItem * Slot = GetSlot(SlotNum, a_Player);
if (!Slot->IsEqual(a_ItemStack) && (!Slot->IsEmpty() || a_KeepEmptySlots))
{
// Different items
continue;
}
- int NumFit = ItemHandler(Slot->m_ItemType)->GetMaxStackSize() - Slot->m_ItemCount;
+ char NumFit = ItemHandler(Slot->m_ItemType)->GetMaxStackSize() - Slot->m_ItemCount;
if (NumFit <= 0)
{
// Full stack already
continue;
}
- if (NumFit > a_ItemStack.m_ItemCount)
- {
- NumFit = a_ItemStack.m_ItemCount;
- }
+ NumFit = std::min(NumFit, a_ItemStack.m_ItemCount);
+
if (a_ShouldApply)
{
cItem NewSlot(a_ItemStack);
NewSlot.m_ItemCount = Slot->m_ItemCount + NumFit;
- SetSlot(i, a_Player, NewSlot);
+ SetSlot(SlotNum, a_Player, NewSlot);
}
a_ItemStack.m_ItemCount -= NumFit;
if (a_ItemStack.IsEmpty())
@@ -1051,7 +1054,7 @@ void cSlotAreaAnvil::UpdateResult(cPlayer & a_Player)
cItem SecondInput(*GetSlot(1, a_Player));
cItem Output(*GetSlot(2, a_Player));
- if (Input.IsEmpty() && !Output.IsEmpty())
+ if (Input.IsEmpty())
{
Output.Empty();
SetSlot(2, a_Player, Output);
@@ -1335,7 +1338,7 @@ void cSlotAreaBeacon::Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_
-void cSlotAreaBeacon::DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots)
+void cSlotAreaBeacon::DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots, bool a_BackFill)
{
const cItem * Slot = GetSlot(0, a_Player);
if (!Slot->IsEmpty() || !IsPlaceableItem(a_ItemStack.m_ItemType) || (a_ItemStack.m_ItemCount != 1))
@@ -1390,13 +1393,12 @@ void cSlotAreaBeacon::OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum)
////////////////////////////////////////////////////////////////////////////////
// cSlotAreaEnchanting:
-cSlotAreaEnchanting::cSlotAreaEnchanting(cEnchantingWindow & a_ParentWindow, int a_BlockX, int a_BlockY, int a_BlockZ) :
+cSlotAreaEnchanting::cSlotAreaEnchanting(cWindow & a_ParentWindow, int a_BlockX, int a_BlockY, int a_BlockZ) :
cSlotAreaTemporary(1, a_ParentWindow),
m_BlockX(a_BlockX),
m_BlockY(a_BlockY),
m_BlockZ(a_BlockZ)
{
- a_ParentWindow.m_SlotArea = this;
}
@@ -1503,7 +1505,7 @@ void cSlotAreaEnchanting::Clicked(cPlayer & a_Player, int a_SlotNum, eClickActio
-void cSlotAreaEnchanting::DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_Apply, bool a_KeepEmptySlots)
+void cSlotAreaEnchanting::DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_Apply, bool a_KeepEmptySlots, bool a_BackFill)
{
const cItem * Slot = GetSlot(0, a_Player);
if (!Slot->IsEmpty())
@@ -1833,38 +1835,50 @@ void cSlotAreaFurnace::Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a
-void cSlotAreaFurnace::DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots)
+void cSlotAreaFurnace::DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots, bool a_BackFill)
{
- for (int i = 0; i < 2; i++)
+ int SlotNum;
+ cFurnaceRecipe * FurnaceRecipes = cRoot::Get()->GetFurnaceRecipe();
+
+ if (FurnaceRecipes->GetRecipeFrom(a_ItemStack) != nullptr)
{
- const cItem * Slot = GetSlot(i, a_Player);
- if (!Slot->IsEqual(a_ItemStack) && (!Slot->IsEmpty() || a_KeepEmptySlots))
- {
- // Different items
- continue;
- }
- int NumFit = ItemHandler(Slot->m_ItemType)->GetMaxStackSize() - Slot->m_ItemCount;
- if (NumFit <= 0)
- {
- // Full stack already
- continue;
- }
- if (NumFit > a_ItemStack.m_ItemCount)
- {
- NumFit = a_ItemStack.m_ItemCount;
- }
- if (a_ShouldApply)
- {
- cItem NewSlot(a_ItemStack);
- NewSlot.m_ItemCount = Slot->m_ItemCount + NumFit;
- SetSlot(i, a_Player, NewSlot);
- }
- a_ItemStack.m_ItemCount -= NumFit;
- if (a_ItemStack.IsEmpty())
- {
- return;
- }
- } // for i - Slots
+ SlotNum = 0;
+ }
+ else if (FurnaceRecipes->IsFuel(a_ItemStack))
+ {
+ SlotNum = 1;
+ }
+ else
+ {
+ return;
+ }
+
+ const cItem * Slot = GetSlot(SlotNum, a_Player);
+ if (!Slot->IsEqual(a_ItemStack) && (!Slot->IsEmpty() || a_KeepEmptySlots))
+ {
+ // Different items
+ return;
+ }
+
+ char NumFit = ItemHandler(Slot->m_ItemType)->GetMaxStackSize() - Slot->m_ItemCount;
+ if (NumFit <= 0)
+ {
+ // Full stack already
+ return;
+ }
+ NumFit = std::min(NumFit, a_ItemStack.m_ItemCount);
+
+ if (a_ShouldApply)
+ {
+ cItem NewSlot(a_ItemStack);
+ NewSlot.m_ItemCount = Slot->m_ItemCount + NumFit;
+ SetSlot(SlotNum, a_Player, NewSlot);
+ }
+ a_ItemStack.m_ItemCount -= NumFit;
+ if (a_ItemStack.IsEmpty())
+ {
+ return;
+ }
}
@@ -2013,7 +2027,7 @@ void cSlotAreaInventoryBase::SetSlot(int a_SlotNum, cPlayer & a_Player, const cI
////////////////////////////////////////////////////////////////////////////////
// cSlotAreaArmor:
-void cSlotAreaArmor::DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots)
+void cSlotAreaArmor::DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots, bool a_BackFill)
{
if (ItemCategory::IsHelmet(a_ItemStack.m_ItemType) && GetSlot(0, a_Player)->IsEmpty())
{
diff --git a/src/UI/SlotArea.h b/src/UI/SlotArea.h
index 1eeeb9836..e39d372c9 100644
--- a/src/UI/SlotArea.h
+++ b/src/UI/SlotArea.h
@@ -17,12 +17,10 @@ class cWindow;
class cPlayer;
class cBeaconEntity;
class cChestEntity;
-class cDropSpenserEntity;
class cEnderChestEntity;
class cFurnaceEntity;
class cMinecartWithChest;
class cCraftingRecipe;
-class cEnchantingWindow;
class cWorld;
@@ -73,7 +71,7 @@ public:
if a_ShouldApply is false, only a_ItemStack is modified to reflect the number of fits (for fit-testing purposes)
If a_KeepEmptySlots is true, empty slots will be skipped and won't be filled
*/
- virtual void DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots);
+ virtual void DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots, bool a_BackFill);
/// Called on DblClicking to collect all stackable items into hand.
/// The items are accumulated in a_Dragging and removed from the slots immediately.
@@ -158,7 +156,7 @@ public:
}
/** Distributing the stack is allowed only for compatible items (helmets into helmet slot etc.) */
- virtual void DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots) override;
+ virtual void DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots, bool a_BackFill) override;
/** Called when a player clicks in the window. Parameters taken from the click packet. */
virtual void Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem) override;
@@ -246,7 +244,7 @@ public:
virtual void SetSlot (int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) override;
// Distributing items into this area is completely disabled
- virtual void DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots) override;
+ virtual void DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots, bool a_BackFill) override;
protected:
@@ -285,12 +283,12 @@ class cSlotAreaAnvil :
typedef cSlotAreaTemporary super;
public:
- cSlotAreaAnvil(cAnvilWindow & a_ParentWindow);
+ cSlotAreaAnvil(cWindow & a_ParentWindow);
// cSlotArea overrides:
virtual void Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem) override;
virtual void ShiftClicked(cPlayer & a_Player, int a_SlotNum, const cItem & a_ClickedItem) override;
- virtual void DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots) override;
+ virtual void DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots, bool a_BackFill) override;
// cSlotAreaTemporary overrides:
virtual void OnPlayerRemoved(cPlayer & a_Player) override;
@@ -326,10 +324,10 @@ public:
cSlotAreaBeacon(cBeaconEntity * a_Beacon, cWindow & a_ParentWindow);
virtual ~cSlotAreaBeacon();
- bool IsPlaceableItem(short a_ItemType);
+ static bool IsPlaceableItem(short a_ItemType);
virtual void Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem) override;
- virtual void DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots) override;
+ virtual void DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots, bool a_BackFill) override;
virtual const cItem * GetSlot(int a_SlotNum, cPlayer & a_Player) const override;
virtual void SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) override;
@@ -350,11 +348,11 @@ class cSlotAreaEnchanting :
typedef cSlotAreaTemporary super;
public:
- cSlotAreaEnchanting(cEnchantingWindow & a_ParentWindow, int a_BlockX, int a_BlockY, int a_BlockZ);
+ cSlotAreaEnchanting(cWindow & a_ParentWindow, int a_BlockX, int a_BlockY, int a_BlockZ);
// cSlotArea overrides:
virtual void Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem) override;
- virtual void DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots) override;
+ virtual void DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots, bool a_BackFill) override;
virtual void SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) override;
// cSlotAreaTemporary overrides:
@@ -439,7 +437,7 @@ public:
virtual ~cSlotAreaFurnace();
virtual void Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem) override;
- virtual void DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots) override;
+ virtual void DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots, bool a_BackFill) override;
virtual const cItem * GetSlot(int a_SlotNum, cPlayer & a_Player) const override;
virtual void SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) override;
diff --git a/src/UI/Window.cpp b/src/UI/Window.cpp
index 1598dd3e7..bb2e2a807 100644
--- a/src/UI/Window.cpp
+++ b/src/UI/Window.cpp
@@ -32,7 +32,6 @@ cWindow::cWindow(WindowType a_WindowType, const AString & a_WindowTitle) :
m_WindowType(a_WindowType),
m_WindowTitle(a_WindowTitle),
m_IsDestroyed(false),
- m_ShouldDistributeToHotbarFirst(true),
m_Owner(nullptr)
{
if (a_WindowType == wtInventory)
@@ -392,43 +391,23 @@ bool cWindow::ForEachClient(cItemCallback<cClientHandle> & a_Callback)
-void cWindow::DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, cSlotArea * a_ExcludeArea, bool a_ShouldApply)
+void cWindow::DistributeStackToAreas(cItem & a_ItemStack, cPlayer & a_Player, cSlotAreas & a_AreasInOrder, bool a_ShouldApply, bool a_BackFill)
{
- // Ask each slot area to take as much of the stack as it can.
- // First ask only slots that already have the same kind of item
- // Then ask any remaining slots
- for (int Pass = 0; Pass < 2; ++Pass)
+ /* Ask each slot area to take as much of the stack as it can.
+ First ask only slots that already have the same kind of item
+ Then ask any remaining slots */
+ for (size_t Pass = 0; Pass < 2; Pass++)
{
- if (m_ShouldDistributeToHotbarFirst)
+ for (auto SlotArea : a_AreasInOrder)
{
- // First distribute into the hotbar:
- if (a_ExcludeArea != m_SlotAreas.back())
- {
- m_SlotAreas.back()->DistributeStack(a_ItemStack, a_Player, a_ShouldApply, (Pass == 0));
- if (a_ItemStack.IsEmpty())
- {
- // Distributed it all
- return;
- }
- }
- }
-
- // The distribute to all other areas:
- cSlotAreas::iterator end = m_ShouldDistributeToHotbarFirst ? (m_SlotAreas.end() - 1) : m_SlotAreas.end();
- for (cSlotAreas::iterator itr = m_SlotAreas.begin(); itr != end; ++itr)
- {
- if (*itr == a_ExcludeArea)
- {
- continue;
- }
- (*itr)->DistributeStack(a_ItemStack, a_Player, a_ShouldApply, (Pass == 0));
+ SlotArea->DistributeStack(a_ItemStack, a_Player, a_ShouldApply, (Pass == 0), a_BackFill);
if (a_ItemStack.IsEmpty())
{
// Distributed it all
return;
}
- } // for itr - m_SlotAreas[]
- } // for Pass - repeat twice
+ }
+ }
}
@@ -779,401 +758,3 @@ void cWindow::SetProperty(short a_Property, short a_Value, cPlayer & a_Player)
-
-////////////////////////////////////////////////////////////////////////////////
-// cInventoryWindow:
-
-cInventoryWindow::cInventoryWindow(cPlayer & a_Player) :
- cWindow(wtInventory, "Inventory"),
- m_Player(a_Player)
-{
- m_SlotAreas.push_back(new cSlotAreaCrafting(2, *this)); // The creative inventory doesn't display it, but it's still counted into slot numbers
- m_SlotAreas.push_back(new cSlotAreaArmor(*this));
- m_SlotAreas.push_back(new cSlotAreaInventory(*this));
- m_SlotAreas.push_back(new cSlotAreaHotBar(*this));
-}
-
-
-
-
-
-////////////////////////////////////////////////////////////////////////////////
-// cCraftingWindow:
-
-cCraftingWindow::cCraftingWindow(int a_BlockX, int a_BlockY, int a_BlockZ) :
- cWindow(wtWorkbench, "Crafting Table")
-{
- m_SlotAreas.push_back(new cSlotAreaCrafting(3, *this));
- m_SlotAreas.push_back(new cSlotAreaInventory(*this));
- m_SlotAreas.push_back(new cSlotAreaHotBar(*this));
-}
-
-
-
-
-
-////////////////////////////////////////////////////////////////////////////////
-// cAnvilWindow:
-
-cAnvilWindow::cAnvilWindow(int a_BlockX, int a_BlockY, int a_BlockZ) :
- cWindow(wtAnvil, "Repair"),
- m_RepairedItemName(""),
- m_BlockX(a_BlockX),
- m_BlockY(a_BlockY),
- m_BlockZ(a_BlockZ)
-{
- m_AnvilSlotArea = new cSlotAreaAnvil(*this);
- m_SlotAreas.push_back(m_AnvilSlotArea);
- m_SlotAreas.push_back(new cSlotAreaInventory(*this));
- m_SlotAreas.push_back(new cSlotAreaHotBar(*this));
-}
-
-
-
-
-
-void cAnvilWindow::SetRepairedItemName(const AString & a_Name, cPlayer * a_Player)
-{
- m_RepairedItemName = a_Name;
-
- if (a_Player != nullptr)
- {
- m_AnvilSlotArea->UpdateResult(*a_Player);
- }
-}
-
-
-
-
-
-void cAnvilWindow::GetBlockPos(int & a_PosX, int & a_PosY, int & a_PosZ)
-{
- a_PosX = m_BlockX;
- a_PosY = m_BlockY;
- a_PosZ = m_BlockZ;
-}
-
-
-
-
-
-////////////////////////////////////////////////////////////////////////////////
-// cBeaconWindow:
-
-cBeaconWindow::cBeaconWindow(int a_BlockX, int a_BlockY, int a_BlockZ, cBeaconEntity * a_Beacon) :
- cWindow(wtBeacon, "Beacon"),
- m_Beacon(a_Beacon)
-{
- m_ShouldDistributeToHotbarFirst = true;
- m_SlotAreas.push_back(new cSlotAreaBeacon(m_Beacon, *this));
- m_SlotAreas.push_back(new cSlotAreaInventory(*this));
- m_SlotAreas.push_back(new cSlotAreaHotBar(*this));
-}
-
-
-
-
-
-void cBeaconWindow::OpenedByPlayer(cPlayer & a_Player)
-{
- super::OpenedByPlayer(a_Player);
-
- a_Player.GetClientHandle()->SendWindowProperty(*this, 0, m_Beacon->GetBeaconLevel());
- a_Player.GetClientHandle()->SendWindowProperty(*this, 1, m_Beacon->GetPrimaryEffect());
- a_Player.GetClientHandle()->SendWindowProperty(*this, 2, m_Beacon->GetSecondaryEffect());
-}
-
-
-
-
-
-////////////////////////////////////////////////////////////////////////////////
-// cEnchantingWindow:
-
-cEnchantingWindow::cEnchantingWindow(int a_BlockX, int a_BlockY, int a_BlockZ) :
- cWindow(wtEnchantment, "Enchant"),
- m_SlotArea(),
- m_BlockX(a_BlockX),
- m_BlockY(a_BlockY),
- m_BlockZ(a_BlockZ)
-{
- m_SlotAreas.push_back(new cSlotAreaEnchanting(*this, m_BlockX, m_BlockY, m_BlockZ));
- m_SlotAreas.push_back(new cSlotAreaInventory(*this));
- m_SlotAreas.push_back(new cSlotAreaHotBar(*this));
-}
-
-
-
-
-
-void cEnchantingWindow::SetProperty(short a_Property, short a_Value)
-{
- if ((a_Property < 0) || ((size_t)a_Property >= ARRAYCOUNT(m_PropertyValue)))
- {
- ASSERT(!"a_Property is invalid");
- return;
- }
-
- m_PropertyValue[a_Property] = a_Value;
- super::SetProperty(a_Property, a_Value);
-}
-
-
-
-
-
-void cEnchantingWindow::SetProperty(short a_Property, short a_Value, cPlayer & a_Player)
-{
- if ((a_Property < 0) || ((size_t)a_Property >= ARRAYCOUNT(m_PropertyValue)))
- {
- ASSERT(!"a_Property is invalid");
- return;
- }
-
- m_PropertyValue[a_Property] = a_Value;
- super::SetProperty(a_Property, a_Value, a_Player);
-}
-
-
-
-
-
-short cEnchantingWindow::GetPropertyValue(short a_Property)
-{
- if ((a_Property < 0) || ((size_t)a_Property >= ARRAYCOUNT(m_PropertyValue)))
- {
- ASSERT(!"a_Property is invalid");
- return 0;
- }
-
- return m_PropertyValue[a_Property];
-}
-
-
-
-
-
-////////////////////////////////////////////////////////////////////////////////
-// cChestWindow:
-
-cChestWindow::cChestWindow(cChestEntity * a_Chest) :
- cWindow(wtChest, (a_Chest->GetBlockType() == E_BLOCK_CHEST) ? "Chest" : "Trapped Chest"),
- m_World(a_Chest->GetWorld()),
- m_BlockX(a_Chest->GetPosX()),
- m_BlockY(a_Chest->GetPosY()),
- m_BlockZ(a_Chest->GetPosZ()),
- m_PrimaryChest(a_Chest),
- m_SecondaryChest(nullptr)
-{
- m_SlotAreas.push_back(new cSlotAreaChest(a_Chest, *this));
- m_SlotAreas.push_back(new cSlotAreaInventory(*this));
- m_SlotAreas.push_back(new cSlotAreaHotBar(*this));
-
- // Play the opening sound:
- m_World->BroadcastSoundEffect("random.chestopen", (double)m_BlockX, (double)m_BlockY, (double)m_BlockZ, 1, 1);
-
- // Send out the chest-open packet:
- m_World->BroadcastBlockAction(m_BlockX, m_BlockY, m_BlockZ, 1, 1, a_Chest->GetBlockType());
-}
-
-
-
-
-
-cChestWindow::cChestWindow(cChestEntity * a_PrimaryChest, cChestEntity * a_SecondaryChest) :
- cWindow(wtChest, (a_PrimaryChest->GetBlockType() == E_BLOCK_CHEST) ? "Double Chest" : "Double Trapped Chest"),
- m_World(a_PrimaryChest->GetWorld()),
- m_BlockX(a_PrimaryChest->GetPosX()),
- m_BlockY(a_PrimaryChest->GetPosY()),
- m_BlockZ(a_PrimaryChest->GetPosZ()),
- m_PrimaryChest(a_PrimaryChest),
- m_SecondaryChest(a_SecondaryChest)
-{
- m_SlotAreas.push_back(new cSlotAreaDoubleChest(a_PrimaryChest, a_SecondaryChest, *this));
- m_SlotAreas.push_back(new cSlotAreaInventory(*this));
- m_SlotAreas.push_back(new cSlotAreaHotBar(*this));
-
- m_ShouldDistributeToHotbarFirst = false;
-
- // Play the opening sound:
- m_World->BroadcastSoundEffect("random.chestopen", (double)m_BlockX, (double)m_BlockY, (double)m_BlockZ, 1, 1);
-
- // Send out the chest-open packet:
- m_World->BroadcastBlockAction(m_BlockX, m_BlockY, m_BlockZ, 1, 1, a_PrimaryChest->GetBlockType());
-}
-
-
-
-
-
-void cChestWindow::OpenedByPlayer(cPlayer & a_Player)
-{
- int ChunkX, ChunkZ;
-
- m_PrimaryChest->SetNumberOfPlayers(m_PrimaryChest->GetNumberOfPlayers() + 1);
- cChunkDef::BlockToChunk(m_PrimaryChest->GetPosX(), m_PrimaryChest->GetPosZ(), ChunkX, ChunkZ);
- m_PrimaryChest->GetWorld()->MarkRedstoneDirty(ChunkX, ChunkZ);
-
- if (m_SecondaryChest != nullptr)
- {
- m_SecondaryChest->SetNumberOfPlayers(m_SecondaryChest->GetNumberOfPlayers() + 1);
- cChunkDef::BlockToChunk(m_SecondaryChest->GetPosX(), m_SecondaryChest->GetPosZ(), ChunkX, ChunkZ);
- m_SecondaryChest->GetWorld()->MarkRedstoneDirty(ChunkX, ChunkZ);
- }
-
- cWindow::OpenedByPlayer(a_Player);
-}
-
-
-
-
-
-bool cChestWindow::ClosedByPlayer(cPlayer & a_Player, bool a_CanRefuse)
-{
- int ChunkX, ChunkZ;
-
- m_PrimaryChest->SetNumberOfPlayers(m_PrimaryChest->GetNumberOfPlayers() - 1);
- cChunkDef::BlockToChunk(m_PrimaryChest->GetPosX(), m_PrimaryChest->GetPosZ(), ChunkX, ChunkZ);
- m_PrimaryChest->GetWorld()->MarkRedstoneDirty(ChunkX, ChunkZ);
-
- if (m_SecondaryChest != nullptr)
- {
- m_SecondaryChest->SetNumberOfPlayers(m_SecondaryChest->GetNumberOfPlayers() - 1);
- cChunkDef::BlockToChunk(m_SecondaryChest->GetPosX(), m_SecondaryChest->GetPosZ(), ChunkX, ChunkZ);
- m_SecondaryChest->GetWorld()->MarkRedstoneDirty(ChunkX, ChunkZ);
- }
-
- cWindow::ClosedByPlayer(a_Player, a_CanRefuse);
- return true;
-}
-
-
-
-
-
-cChestWindow::~cChestWindow()
-{
- // Send out the chest-close packet:
- m_World->BroadcastBlockAction(m_BlockX, m_BlockY, m_BlockZ, 1, 0, m_PrimaryChest->GetBlockType());
-
- m_World->BroadcastSoundEffect("random.chestclosed", (double)m_BlockX, (double)m_BlockY, (double)m_BlockZ, 1, 1);
-}
-
-
-
-
-
-////////////////////////////////////////////////////////////////////////////////
-// cMinecartWithChestWindow:
-
-cMinecartWithChestWindow::cMinecartWithChestWindow(cMinecartWithChest * a_ChestCart) :
- cWindow(wtChest, "Minecart with Chest"),
- m_ChestCart(a_ChestCart)
-{
- m_ShouldDistributeToHotbarFirst = false;
- m_SlotAreas.push_back(new cSlotAreaMinecartWithChest(a_ChestCart, *this));
- m_SlotAreas.push_back(new cSlotAreaInventory(*this));
- m_SlotAreas.push_back(new cSlotAreaHotBar(*this));
-
- a_ChestCart->GetWorld()->BroadcastSoundEffect("random.chestopen", a_ChestCart->GetPosX(), a_ChestCart->GetPosY(), a_ChestCart->GetPosZ(), 1, 1);
-}
-
-
-
-
-
-cMinecartWithChestWindow::~cMinecartWithChestWindow()
-{
- m_ChestCart->GetWorld()->BroadcastSoundEffect("random.chestclosed", m_ChestCart->GetPosX(), m_ChestCart->GetPosY(), m_ChestCart->GetPosZ(), 1, 1);
-}
-
-
-
-
-
-////////////////////////////////////////////////////////////////////////////////
-// cDropSpenserWindow:
-
-cDropSpenserWindow::cDropSpenserWindow(int a_BlockX, int a_BlockY, int a_BlockZ, cDropSpenserEntity * a_DropSpenser) :
- cWindow(wtDropSpenser, (a_DropSpenser->GetBlockType() == E_BLOCK_DISPENSER) ? "Dispenser" : "Dropper")
-{
- m_ShouldDistributeToHotbarFirst = false;
- m_SlotAreas.push_back(new cSlotAreaItemGrid(a_DropSpenser->GetContents(), *this));
- m_SlotAreas.push_back(new cSlotAreaInventory(*this));
- m_SlotAreas.push_back(new cSlotAreaHotBar(*this));
-}
-
-
-
-
-
-////////////////////////////////////////////////////////////////////////////////
-// cEnderChestWindow:
-
-cEnderChestWindow::cEnderChestWindow(cEnderChestEntity * a_EnderChest) :
- cWindow(wtChest, "Ender Chest"),
- m_World(a_EnderChest->GetWorld()),
- m_BlockX(a_EnderChest->GetPosX()),
- m_BlockY(a_EnderChest->GetPosY()),
- m_BlockZ(a_EnderChest->GetPosZ())
-{
- m_ShouldDistributeToHotbarFirst = false;
- m_SlotAreas.push_back(new cSlotAreaEnderChest(a_EnderChest, *this));
- m_SlotAreas.push_back(new cSlotAreaInventory(*this));
- m_SlotAreas.push_back(new cSlotAreaHotBar(*this));
-
- // Play the opening sound:
- m_World->BroadcastSoundEffect("random.chestopen", (double)m_BlockX, (double)m_BlockY, (double)m_BlockZ, 1, 1);
-
- // Send out the chest-open packet:
- m_World->BroadcastBlockAction(m_BlockX, m_BlockY, m_BlockZ, 1, 1, E_BLOCK_ENDER_CHEST);
-}
-
-
-
-
-
-cEnderChestWindow::~cEnderChestWindow()
-{
- // Send out the chest-close packet:
- m_World->BroadcastBlockAction(m_BlockX, m_BlockY, m_BlockZ, 1, 0, E_BLOCK_ENDER_CHEST);
-
- // Play the closing sound
- m_World->BroadcastSoundEffect("random.chestclosed", (double)m_BlockX, (double)m_BlockY, (double)m_BlockZ, 1, 1);
-}
-
-
-
-
-
-////////////////////////////////////////////////////////////////////////////////
-// cHopperWindow:
-
-cHopperWindow::cHopperWindow(int a_BlockX, int a_BlockY, int a_BlockZ, cHopperEntity * a_Hopper) :
- super(wtHopper, "Hopper")
-{
- m_ShouldDistributeToHotbarFirst = false;
- m_SlotAreas.push_back(new cSlotAreaItemGrid(a_Hopper->GetContents(), *this));
- m_SlotAreas.push_back(new cSlotAreaInventory(*this));
- m_SlotAreas.push_back(new cSlotAreaHotBar(*this));
-}
-
-
-
-
-
-////////////////////////////////////////////////////////////////////////////////
-// cFurnaceWindow:
-
-cFurnaceWindow::cFurnaceWindow(int a_BlockX, int a_BlockY, int a_BlockZ, cFurnaceEntity * a_Furnace) :
- cWindow(wtFurnace, "Furnace")
-{
- m_ShouldDistributeToHotbarFirst = false;
- m_SlotAreas.push_back(new cSlotAreaFurnace(a_Furnace, *this));
- m_SlotAreas.push_back(new cSlotAreaInventory(*this));
- m_SlotAreas.push_back(new cSlotAreaHotBar(*this));
-}
-
-
-
-
diff --git a/src/UI/Window.h b/src/UI/Window.h
index e62176d50..9821aade1 100644
--- a/src/UI/Window.h
+++ b/src/UI/Window.h
@@ -19,7 +19,6 @@ class cPlayer;
class cWindowOwner;
class cClientHandle;
class cChestEntity;
-class cDropSpenserEntity;
class cEnderChestEntity;
class cFurnaceEntity;
class cHopperEntity;
@@ -154,14 +153,19 @@ public:
/** Called on shift-clicking to distribute the stack into other areas; Modifies a_ItemStack as it is distributed!
if a_ShouldApply is true, the changes are written into the slots;
+ if a_ShouldApply is false, only a_ItemStack is modified to reflect the number of fits (for fit-testing purposes) */
+ virtual void DistributeStack(cItem & a_ItemStack, int a_Slot, cPlayer & a_Player, cSlotArea * a_ClickedArea, bool a_ShouldApply) = 0;
+
+ /** Called from DistributeStack() to distribute the stack into a_AreasInOrder; Modifies a_ItemStack as it is distributed!
+ If a_ShouldApply is true, the changes are written into the slots;
if a_ShouldApply is false, only a_ItemStack is modified to reflect the number of fits (for fit-testing purposes)
- */
- void DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, cSlotArea * a_ExcludeArea, bool a_ShouldApply);
+ If a_BackFill is true, the areas will be filled from the back (right side). (Example: Empty Hotbar -> Item get in slot 8, not slot 0) */
+ void DistributeStackToAreas(cItem & a_ItemStack, cPlayer & a_Player, cSlotAreas & a_AreasInOrder, bool a_ShouldApply, bool a_BackFill);
- /// Called on DblClicking to collect all stackable items from all areas into hand, starting with the specified area.
- /// The items are accumulated in a_Dragging and removed from the SlotAreas immediately.
- /// If a_CollectFullStacks is false, slots with full stacks in the area are skipped while collecting.
- /// Returns true if full stack has been collected, false if there's space remaining to fill.
+ /** Called on DblClicking to collect all stackable items from all areas into hand, starting with the specified area.
+ The items are accumulated in a_Dragging and removed from the SlotAreas immediately.
+ If a_CollectFullStacks is false, slots with full stacks in the area are skipped while collecting.
+ Returns true if full stack has been collected, false if there's space remaining to fill. */
bool CollectItemsToHand(cItem & a_Dragging, cSlotArea & a_Area, cPlayer & a_Player, bool a_CollectFullStacks);
/// Used by cSlotAreas to send individual slots to clients, a_RelativeSlotNum is the slot number relative to a_SlotArea
@@ -178,7 +182,6 @@ protected:
cPlayerList m_OpenedBy;
bool m_IsDestroyed;
- bool m_ShouldDistributeToHotbarFirst; ///< If set (default), shift+click tries to distribute to hotbar first, then other areas. False for doublechests
cWindowOwner * m_Owner;
@@ -219,188 +222,3 @@ protected:
-
-class cCraftingWindow :
- public cWindow
-{
- typedef cWindow super;
-public:
- cCraftingWindow(int a_BlockX, int a_BlockY, int a_BlockZ);
-} ;
-
-
-
-
-
-class cAnvilWindow :
- public cWindow
-{
- typedef cWindow super;
-public:
- cAnvilWindow(int a_BlockX, int a_BlockY, int a_BlockZ);
-
- /** Gets the repaired item name. */
- AString GetRepairedItemName(void) const { return m_RepairedItemName; }
-
- /** Set the repaired item name. */
- void SetRepairedItemName(const AString & a_Name, cPlayer * a_Player);
-
- /** Gets the Position from the Anvil */
- void GetBlockPos(int & a_PosX, int & a_PosY, int & a_PosZ);
-
-protected:
- cSlotAreaAnvil * m_AnvilSlotArea;
- AString m_RepairedItemName;
- int m_BlockX, m_BlockY, m_BlockZ;
-} ;
-
-
-
-
-
-class cBeaconWindow :
- public cWindow
-{
- typedef cWindow super;
-public:
- cBeaconWindow(int a_BlockX, int a_BlockY, int a_BlockZ, cBeaconEntity * a_Beacon);
-
- cBeaconEntity * GetBeaconEntity(void) const { return m_Beacon; }
-
- // cWindow Overrides:
- virtual void OpenedByPlayer(cPlayer & a_Player) override;
-
-protected:
- cBeaconEntity * m_Beacon;
-} ;
-
-
-
-
-
-class cEnchantingWindow :
- public cWindow
-{
- typedef cWindow super;
-public:
- cEnchantingWindow(int a_BlockX, int a_BlockY, int a_BlockZ);
- virtual void SetProperty(short a_Property, short a_Value, cPlayer & a_Player) override;
- virtual void SetProperty(short a_Property, short a_Value) override;
-
- /** Return the Value of a Property */
- short GetPropertyValue(short a_Property);
-
- cSlotArea * m_SlotArea;
-
-protected:
- short m_PropertyValue[3];
- int m_BlockX, m_BlockY, m_BlockZ;
-};
-
-
-
-
-
-class cFurnaceWindow :
- public cWindow
-{
- typedef cWindow super;
-public:
- cFurnaceWindow(int a_BlockX, int a_BlockY, int a_BlockZ, cFurnaceEntity * a_Furnace);
-} ;
-
-
-
-
-
-class cDropSpenserWindow :
- public cWindow
-{
- typedef cWindow super;
-public:
- cDropSpenserWindow(int a_BlockX, int a_BlockY, int a_BlockZ, cDropSpenserEntity * a_Dispenser);
-} ;
-
-
-
-
-
-class cHopperWindow :
- public cWindow
-{
- typedef cWindow super;
-public:
- cHopperWindow(int a_BlockX, int a_BlockY, int a_BlockZ, cHopperEntity * a_Hopper);
-} ;
-
-
-
-
-
-class cChestWindow :
- public cWindow
-{
-public:
- cChestWindow(cChestEntity * a_Chest);
- cChestWindow(cChestEntity * a_PrimaryChest, cChestEntity * a_SecondaryChest);
- ~cChestWindow();
-
- virtual bool ClosedByPlayer(cPlayer & a_Player, bool a_CanRefuse) override;
- virtual void OpenedByPlayer(cPlayer & a_Player) override;
-
-protected:
- cWorld * m_World;
- int m_BlockX, m_BlockY, m_BlockZ; // Position of the chest, for the window-close packet
- cChestEntity * m_PrimaryChest;
- cChestEntity * m_SecondaryChest;
-} ;
-
-
-
-
-
-class cMinecartWithChestWindow :
- public cWindow
-{
-public:
- cMinecartWithChestWindow(cMinecartWithChest * a_ChestCart);
- ~cMinecartWithChestWindow();
-private:
- cMinecartWithChest * m_ChestCart;
-};
-
-
-
-
-
-class cEnderChestWindow :
- public cWindow
-{
-public:
- cEnderChestWindow(cEnderChestEntity * a_EnderChest);
- ~cEnderChestWindow();
-
-protected:
- cWorld * m_World;
- int m_BlockX, m_BlockY, m_BlockZ; // Position of the enderchest, for the window-close packet
-};
-
-
-
-
-
-class cInventoryWindow :
- public cWindow
-{
-public:
- cInventoryWindow(cPlayer & a_Player);
-
-protected:
- cPlayer & m_Player;
-
-} ;
-
-
-
-
-
diff --git a/src/WebAdmin.h b/src/WebAdmin.h
index 86a8a9a4b..1e1a9bfa9 100644
--- a/src/WebAdmin.h
+++ b/src/WebAdmin.h
@@ -95,7 +95,9 @@ struct sWebAdminPage
// tolua_begin
class cWebAdmin :
+ // tolua_end
public cHTTPServer::cCallbacks
+ // tolua_begin
{
public:
// tolua_end
diff --git a/src/World.cpp b/src/World.cpp
index 474f77b81..7d63a8a52 100644
--- a/src/World.cpp
+++ b/src/World.cpp
@@ -259,9 +259,9 @@ void cWorld::cTickThread::Execute(void)
////////////////////////////////////////////////////////////////////////////////
// cWorld:
-cWorld::cWorld(const AString & a_WorldName, eDimension a_Dimension, const AString & a_OverworldName) :
+cWorld::cWorld(const AString & a_WorldName, eDimension a_Dimension, const AString & a_LinkedOverworldName) :
m_WorldName(a_WorldName),
- m_OverworldName(a_OverworldName),
+ m_LinkedOverworldName(a_LinkedOverworldName),
m_IniFileName(m_WorldName + "/world.ini"),
m_StorageSchema("Default"),
#ifdef __arm__
@@ -604,12 +604,12 @@ void cWorld::Start(void)
if (GetDimension() == dimOverworld)
{
- m_NetherWorldName = IniFile.GetValueSet("LinkedWorlds", "NetherWorldName", GetName() + "_nether");
- m_EndWorldName = IniFile.GetValueSet("LinkedWorlds", "EndWorldName", GetName() + "_end");
+ m_LinkedNetherWorldName = IniFile.GetValueSet("LinkedWorlds", "NetherWorldName", GetName() + "_nether");
+ m_LinkedEndWorldName = IniFile.GetValueSet("LinkedWorlds", "EndWorldName", GetName() + "_end");
}
else
{
- m_OverworldName = IniFile.GetValueSet("LinkedWorlds", "OverworldName", GetLinkedOverworldName());
+ m_LinkedOverworldName = IniFile.GetValueSet("LinkedWorlds", "OverworldName", GetLinkedOverworldName());
}
// Adjust the enum-backed variables into their respective bounds:
@@ -667,18 +667,23 @@ void cWorld::Start(void)
void cWorld::GenerateRandomSpawn(void)
{
LOGD("Generating random spawnpoint...");
-
+ bool foundSpawnPoint = false;
// Look for a spawn point at most 100 chunks away from map center:
for (int i = 0; i < 100; i++)
{
EMCSBiome biome = GetBiomeAt((int)m_SpawnX, (int)m_SpawnZ);
+
if (
(biome != biOcean) && (biome != biFrozenOcean) && // The biome is acceptable (don't want a small ocean island)
!IsBlockWaterOrIce(GetBlock((int)m_SpawnX, GetHeight((int)m_SpawnX, (int)m_SpawnZ), (int)m_SpawnZ)) // The terrain is acceptable (don't want to spawn inside a lake / river)
)
{
- // A good spawnpoint was found
- break;
+ if (CheckPlayerSpawnPoint((int)m_SpawnX, GetHeight((int)m_SpawnX, (int)m_SpawnZ), (int)m_SpawnZ))
+ {
+ // A good spawnpoint was found
+ foundSpawnPoint = true;
+ break;
+ }
}
// Try a neighboring chunk:
if ((GetTickRandomNumber(4) % 2) == 0) // Randomise whether to increment X or Z coords
@@ -692,8 +697,60 @@ void cWorld::GenerateRandomSpawn(void)
} // for i - 100*
m_SpawnY = (double)GetHeight((int)m_SpawnX, (int)m_SpawnZ) + 1.6f; // 1.6f to accomodate player height
+ if (foundSpawnPoint)
+ {
+ LOGINFO("Generated random spawnpoint position at {%i, %i, %i}", (int)m_SpawnX, (int)m_SpawnY, (int)m_SpawnZ);
+ }
+ else
+ {
+ LOGINFO("Did not find an acceptable spawnpoint. Generated a random spawnpoint position at {%i, %i, %i}", (int)m_SpawnX, (int)m_SpawnY, (int)m_SpawnZ);
+ } // Maybe widen the search instead?
+
+}
+
+
+
+
+
+bool cWorld::CheckPlayerSpawnPoint(int a_PosX, int a_PosY, int a_PosZ)
+{
+ // Check that spawnblock and surrounding blocks are neither solid nor water / lava
+ static const struct
+ {
+ int x, z;
+ } Coords[] =
+ {
+ { 0, 0 },
+ { -1, 0 },
+ { 1, 0 },
+ { 0, -1 },
+ { 0, 1 },
+ };
+ for (size_t i = 0; i < ARRAYCOUNT(Coords); i++)
+ {
+ BLOCKTYPE BlockType = GetBlock(a_PosX + Coords[i].x, a_PosY, a_PosZ + Coords[i].x);
+ if (cBlockInfo::IsSolid(BlockType) || IsBlockLiquid(BlockType))
+ {
+ return false;
+ }
+ } // for i - Coords[]
- LOGINFO("Generated random spawnpoint position {%i, %i, %i}", (int)m_SpawnX, (int)m_SpawnY, (int)m_SpawnZ);
+ // Check that the block below is solid:
+ if (!cBlockInfo::IsSolid(GetBlock(a_PosX, a_PosY - 1, a_PosZ)))
+ {
+ return false;
+ }
+
+ // Check that all the blocks above the spawnpoint are not solid:
+ for (int i = a_PosY; i < cChunkDef::Height; i++)
+ {
+ BLOCKTYPE BlockType = GetBlock(a_PosX, i, a_PosZ);
+ if (cBlockInfo::IsSolid(BlockType))
+ {
+ return false;
+ }
+ }
+ return true;
}
@@ -827,18 +884,18 @@ void cWorld::Stop(void)
IniFile.ReadFile(m_IniFileName);
if (GetDimension() == dimOverworld)
{
- IniFile.SetValue("LinkedWorlds", "NetherWorldName", m_NetherWorldName);
- IniFile.SetValue("LinkedWorlds", "EndWorldName", m_EndWorldName);
+ IniFile.SetValue("LinkedWorlds", "NetherWorldName", m_LinkedNetherWorldName);
+ IniFile.SetValue("LinkedWorlds", "EndWorldName", m_LinkedEndWorldName);
}
else
{
- IniFile.SetValue("LinkedWorlds", "OverworldName", m_OverworldName);
+ IniFile.SetValue("LinkedWorlds", "OverworldName", m_LinkedOverworldName);
}
- IniFile.SetValueI("Physics", "TNTShrapnelLevel", (int)m_TNTShrapnelLevel);
+ IniFile.SetValueI("Physics", "TNTShrapnelLevel", static_cast<int>(m_TNTShrapnelLevel));
IniFile.SetValueB("Mechanics", "CommandBlocksEnabled", m_bCommandBlocksEnabled);
IniFile.SetValueB("Mechanics", "UseChatPrefixes", m_bUseChatPrefixes);
IniFile.SetValueB("General", "IsDaylightCycleEnabled", m_IsDaylightCycleEnabled);
- IniFile.SetValueI("General", "Weather", (int)m_Weather);
+ IniFile.SetValueI("General", "Weather", static_cast<int>(m_Weather));
IniFile.SetValueI("General", "TimeInTicks", GetTimeOfDay());
IniFile.WriteFile(m_IniFileName);
@@ -2851,6 +2908,20 @@ bool cWorld::ForEachEntityInBox(const cBoundingBox & a_Box, cEntityCallback & a_
bool cWorld::DoWithEntityByID(int a_UniqueID, cEntityCallback & a_Callback)
{
+ // First check the entities-to-add:
+ {
+ cCSLock Lock(m_CSEntitiesToAdd);
+ for (auto & ent: m_EntitiesToAdd)
+ {
+ if (ent->GetUniqueID() == a_UniqueID)
+ {
+ a_Callback.Item(ent);
+ return true;
+ }
+ } // for ent - m_EntitiesToAdd[]
+ }
+
+ // Then check the chunkmap:
return m_ChunkMap->DoWithEntityByID(a_UniqueID, a_Callback);
}
@@ -3096,14 +3167,14 @@ void cWorld::SaveAllChunks(void)
void cWorld::QueueSaveAllChunks(void)
{
- QueueTask(make_unique<cWorld::cTaskSaveAllChunks>());
+ QueueTask(std::make_shared<cWorld::cTaskSaveAllChunks>());
}
-void cWorld::QueueTask(std::unique_ptr<cTask> a_Task)
+void cWorld::QueueTask(cTaskPtr a_Task)
{
cCSLock Lock(m_CSTasks);
m_Tasks.push_back(std::move(a_Task));
@@ -3113,7 +3184,7 @@ void cWorld::QueueTask(std::unique_ptr<cTask> a_Task)
-void cWorld::ScheduleTask(int a_DelayTicks, cTask * a_Task)
+void cWorld::ScheduleTask(int a_DelayTicks, cTaskPtr a_Task)
{
Int64 TargetTick = a_DelayTicks + std::chrono::duration_cast<cTickTimeLong>(m_WorldAge).count();
@@ -3123,11 +3194,11 @@ void cWorld::ScheduleTask(int a_DelayTicks, cTask * a_Task)
{
if ((*itr)->m_TargetTick >= TargetTick)
{
- m_ScheduledTasks.insert(itr, make_unique<cScheduledTask>(TargetTick, a_Task));
+ m_ScheduledTasks.insert(itr, cScheduledTaskPtr(new cScheduledTask(TargetTick, a_Task)));
return;
}
}
- m_ScheduledTasks.push_back(make_unique<cScheduledTask>(TargetTick, a_Task));
+ m_ScheduledTasks.push_back(cScheduledTaskPtr(new cScheduledTask(TargetTick, a_Task)));
}
@@ -3578,7 +3649,7 @@ void cWorld::cTaskUnloadUnusedChunks::Run(cWorld & a_World)
////////////////////////////////////////////////////////////////////////////////
-// cWorld::cTaskSendBlockTo
+// cWorld::cTaskSendBlockToAllPlayers
cWorld::cTaskSendBlockToAllPlayers::cTaskSendBlockToAllPlayers(std::vector<Vector3i> & a_SendQueue) :
m_SendQueue(a_SendQueue)
diff --git a/src/World.h b/src/World.h
index 3cac71a36..ffc10a9e4 100644
--- a/src/World.h
+++ b/src/World.h
@@ -106,7 +106,8 @@ public:
virtual void Run(cWorld & a_World) = 0;
} ;
- typedef std::vector<std::unique_ptr<cTask>> cTasks;
+ typedef SharedPtr<cTask> cTaskPtr;
+ typedef std::vector<cTaskPtr> cTasks;
class cTaskSaveAllChunks :
@@ -673,14 +674,14 @@ public:
bool ShouldBroadcastAchievementMessages(void) const { return m_BroadcastAchievementMessages; }
- AString GetNetherWorldName(void) const { return m_NetherWorldName; }
- void SetNetherWorldName(const AString & a_Name) { m_NetherWorldName = a_Name; }
+ AString GetLinkedNetherWorldName(void) const { return m_LinkedNetherWorldName; }
+ void SetLinkedNetherWorldName(const AString & a_Name) { m_LinkedNetherWorldName = a_Name; }
- AString GetEndWorldName(void) const { return m_EndWorldName; }
- void SetEndWorldName(const AString & a_Name) { m_EndWorldName = a_Name; }
+ AString GetLinkedEndWorldName(void) const { return m_LinkedEndWorldName; }
+ void SetLinkedEndWorldName(const AString & a_Name) { m_LinkedEndWorldName = a_Name; }
- AString GetLinkedOverworldName(void) const { return m_OverworldName; }
- void SetLinkedOverworldName(const AString & a_Name) { m_OverworldName = a_Name; }
+ AString GetLinkedOverworldName(void) const { return m_LinkedOverworldName; }
+ void SetLinkedOverworldName(const AString & a_Name) { m_LinkedOverworldName = a_Name; }
// tolua_end
@@ -691,11 +692,10 @@ public:
void QueueSaveAllChunks(void); // tolua_export
/** Queues a task onto the tick thread. The task object will be deleted once the task is finished */
- void QueueTask(std::unique_ptr<cTask> a_Task); // Exported in ManualBindings.cpp
+ void QueueTask(cTaskPtr a_Task); // Exported in ManualBindings.cpp
- /** Queues a task onto the tick thread, with the specified delay.
- The task object will be deleted once the task is finished */
- void ScheduleTask(int a_DelayTicks, cTask * a_Task);
+ /** Queues a task onto the tick thread, with the specified delay. */
+ void ScheduleTask(int a_DelayTicks, cTaskPtr a_Task);
/** Returns the number of chunks loaded */
int GetNumChunks() const; // tolua_export
@@ -867,20 +867,16 @@ private:
{
public:
Int64 m_TargetTick;
- cTask * m_Task;
+ cTaskPtr m_Task;
/** Creates a new scheduled task; takes ownership of the task object passed to it. */
- cScheduledTask(Int64 a_TargetTick, cTask * a_Task) :
+ cScheduledTask(Int64 a_TargetTick, cTaskPtr a_Task) :
m_TargetTick(a_TargetTick),
m_Task(a_Task)
{
}
- virtual ~cScheduledTask()
- {
- delete m_Task;
- m_Task = nullptr;
- }
+ virtual ~cScheduledTask() {}
};
typedef std::unique_ptr<cScheduledTask> cScheduledTaskPtr;
@@ -889,10 +885,9 @@ private:
AString m_WorldName;
- /** The name of the world that a portal in this world should link to
- Only has effect if this world is a nether or end world, as it is used by entities to see which world to teleport to when in a portal
- */
- AString m_OverworldName;
+ /** The name of the overworld that portals in this world should link to.
+ Only has effect if this world is a Nether or End world. */
+ AString m_LinkedOverworldName;
AString m_IniFileName;
@@ -985,11 +980,13 @@ private:
/** The maximum view distance that a player can have in this world. */
int m_MaxViewDistance;
- /** Name of the nether world */
- AString m_NetherWorldName;
+ /** Name of the nether world - where Nether portals should teleport.
+ Only used when this world is an Overworld. */
+ AString m_LinkedNetherWorldName;
- /** Name of the end world */
- AString m_EndWorldName;
+ /** Name of the End world - where End portals should teleport.
+ Only used when this world is an Overworld. */
+ AString m_LinkedEndWorldName;
cChunkGenerator m_Generator;
@@ -1049,7 +1046,7 @@ private:
cSetChunkDataPtrs m_SetChunkDataQueue;
- cWorld(const AString & a_WorldName, eDimension a_Dimension = dimOverworld, const AString & a_OverworldName = "");
+ cWorld(const AString & a_WorldName, eDimension a_Dimension = dimOverworld, const AString & a_LinkedOverworldName = "");
virtual ~cWorld();
void Tick(std::chrono::milliseconds a_Dt, std::chrono::milliseconds a_LastTickDurationMSec);
@@ -1077,6 +1074,9 @@ private:
/** <summary>Generates a random spawnpoint on solid land by walking chunks and finding their biomes</summary> */
void GenerateRandomSpawn(void);
+ /** Check if player starting point is acceptable **/
+ bool CheckPlayerSpawnPoint(int a_PosX, int a_PosY, int a_PosZ);
+
/** Chooses a reasonable transition from the current weather to a new weather **/
eWeather ChooseNewWeather(void);
diff --git a/src/WorldStorage/NBTChunkSerializer.cpp b/src/WorldStorage/NBTChunkSerializer.cpp
index c87397542..10231ae3b 100644
--- a/src/WorldStorage/NBTChunkSerializer.cpp
+++ b/src/WorldStorage/NBTChunkSerializer.cpp
@@ -36,20 +36,9 @@
#include "../Entities/ExpOrb.h"
#include "../Entities/HangingEntity.h"
#include "../Entities/ItemFrame.h"
+#include "../Entities/Painting.h"
-#include "../Mobs/Monster.h"
-#include "../Mobs/Bat.h"
-#include "../Mobs/Creeper.h"
-#include "../Mobs/Enderman.h"
-#include "../Mobs/Horse.h"
-#include "../Mobs/MagmaCube.h"
-#include "../Mobs/Sheep.h"
-#include "../Mobs/Slime.h"
-#include "../Mobs/Skeleton.h"
-#include "../Mobs/Villager.h"
-#include "../Mobs/Wither.h"
-#include "../Mobs/Wolf.h"
-#include "../Mobs/Zombie.h"
+#include "../Mobs/IncludeAllMonsters.h"
@@ -726,24 +715,10 @@ void cNBTChunkSerializer::AddProjectileEntity(cProjectileEntity * a_Projectile)
void cNBTChunkSerializer::AddHangingEntity(cHangingEntity * a_Hanging)
{
- m_Writer.AddInt("TileX", a_Hanging->GetBlockX());
- m_Writer.AddInt("TileY", a_Hanging->GetBlockY());
- m_Writer.AddInt("TileZ", a_Hanging->GetBlockZ());
- switch (a_Hanging->GetFacing())
- {
- case BLOCK_FACE_XM: m_Writer.AddByte("Facing", 1); break;
- case BLOCK_FACE_XP: m_Writer.AddByte("Facing", 3); break;
- case BLOCK_FACE_ZM: m_Writer.AddByte("Facing", 2); break;
- case BLOCK_FACE_ZP: m_Writer.AddByte("Facing", 0); break;
-
- case BLOCK_FACE_YM:
- case BLOCK_FACE_YP:
- case BLOCK_FACE_NONE:
- {
- // These directions are invalid, but they may have been previously loaded, so keep them.
- break;
- }
- }
+ m_Writer.AddInt("TileX", FloorC(a_Hanging->GetPosX()));
+ m_Writer.AddInt("TileY", FloorC(a_Hanging->GetPosY()));
+ m_Writer.AddInt("TileZ", FloorC(a_Hanging->GetPosZ()));
+ m_Writer.AddByte("Facing", a_Hanging->GetProtocolFacing());
}
@@ -790,6 +765,19 @@ void cNBTChunkSerializer::AddItemFrameEntity(cItemFrame * a_ItemFrame)
+void cNBTChunkSerializer::AddPaintingEntity(cPainting * a_Painting)
+{
+ m_Writer.BeginCompound("");
+ AddBasicEntity(a_Painting, "Painting");
+ AddHangingEntity(a_Painting);
+ m_Writer.AddString("Motive", a_Painting->GetName());
+ m_Writer.EndCompound();
+}
+
+
+
+
+
void cNBTChunkSerializer::AddMinecartChestContents(cMinecartWithChest * a_Minecart)
{
m_Writer.BeginList("Items", TAG_Compound);
@@ -888,7 +876,7 @@ void cNBTChunkSerializer::Entity(cEntity * a_Entity)
case cEntity::etTNT: AddTNTEntity ((cTNTEntity *) a_Entity); break;
case cEntity::etExpOrb: AddExpOrbEntity ((cExpOrb *) a_Entity); break;
case cEntity::etItemFrame: AddItemFrameEntity ((cItemFrame *) a_Entity); break;
- case cEntity::etPainting: /* TODO */ break;
+ case cEntity::etPainting: AddPaintingEntity (reinterpret_cast<cPainting *>(a_Entity)); break;
case cEntity::etPlayer: return; // Players aren't saved into the world
default:
{
diff --git a/src/WorldStorage/NBTChunkSerializer.h b/src/WorldStorage/NBTChunkSerializer.h
index 4c066b9af..f30cd59d5 100644
--- a/src/WorldStorage/NBTChunkSerializer.h
+++ b/src/WorldStorage/NBTChunkSerializer.h
@@ -48,6 +48,7 @@ class cTNTEntity;
class cExpOrb;
class cHangingEntity;
class cItemFrame;
+class cPainting;
class cEntityEffect;
@@ -123,6 +124,7 @@ protected:
void AddTNTEntity (cTNTEntity * a_TNT);
void AddExpOrbEntity (cExpOrb * a_ExpOrb);
void AddItemFrameEntity (cItemFrame * a_ItemFrame);
+ void AddPaintingEntity (cPainting * a_Painting);
void AddMinecartChestContents(cMinecartWithChest * a_Minecart);
diff --git a/src/WorldStorage/WSSAnvil.cpp b/src/WorldStorage/WSSAnvil.cpp
index cc8b8d3f5..7244bcb73 100755
--- a/src/WorldStorage/WSSAnvil.cpp
+++ b/src/WorldStorage/WSSAnvil.cpp
@@ -50,6 +50,7 @@
#include "../Entities/ExpOrb.h"
#include "../Entities/HangingEntity.h"
#include "../Entities/ItemFrame.h"
+#include "../Entities/Painting.h"
#include "../Protocol/MojangAPI.h"
#include "Server.h"
@@ -1337,6 +1338,10 @@ void cWSSAnvil::LoadEntityFromNBT(cEntityList & a_Entities, const cParsedNBT & a
{
LoadPickupFromNBT(a_Entities, a_NBT, a_EntityTagIdx);
}
+ else if (strncmp(a_IDTag, "Painting", a_IDTagLength) == 0)
+ {
+ LoadPaintingFromNBT(a_Entities, a_NBT, a_EntityTagIdx);
+ }
else if (strncmp(a_IDTag, "PrimedTnt", a_IDTagLength) == 0)
{
LoadTNTFromNBT(a_Entities, a_NBT, a_EntityTagIdx);
@@ -1747,52 +1752,22 @@ void cWSSAnvil::LoadHangingFromNBT(cHangingEntity & a_Hanging, const cParsedNBT
{
// "Facing" tag is the prime source of the Facing; if not available, translate from older "Direction" or "Dir"
int Facing = a_NBT.FindChildByName(a_TagIdx, "Facing");
- if (Facing > 0)
+ if (Facing < 0)
{
- Facing = (int)a_NBT.GetByte(Facing);
- if ((Facing >= 2) && (Facing <= 5))
- {
- a_Hanging.SetFacing(static_cast<eBlockFace>(Facing));
- }
- }
- else
- {
- Facing = a_NBT.FindChildByName(a_TagIdx, "Direction");
- if (Facing > 0)
- {
- switch ((int)a_NBT.GetByte(Facing))
- {
- case 0: a_Hanging.SetFacing(BLOCK_FACE_ZM); break;
- case 1: a_Hanging.SetFacing(BLOCK_FACE_XM); break;
- case 2: a_Hanging.SetFacing(BLOCK_FACE_ZP); break;
- case 3: a_Hanging.SetFacing(BLOCK_FACE_XP); break;
- }
- }
- else
- {
- Facing = a_NBT.FindChildByName(a_TagIdx, "Dir"); // Has values 0 and 2 swapped
- if (Facing > 0)
- {
- switch ((int)a_NBT.GetByte(Facing))
- {
- case 0: a_Hanging.SetFacing(BLOCK_FACE_ZP); break;
- case 1: a_Hanging.SetFacing(BLOCK_FACE_XM); break;
- case 2: a_Hanging.SetFacing(BLOCK_FACE_ZM); break;
- case 3: a_Hanging.SetFacing(BLOCK_FACE_XP); break;
- }
- }
- }
+ return;
}
+ a_Hanging.SetProtocolFacing(a_NBT.GetByte(Facing));
+
int TileX = a_NBT.FindChildByName(a_TagIdx, "TileX");
int TileY = a_NBT.FindChildByName(a_TagIdx, "TileY");
int TileZ = a_NBT.FindChildByName(a_TagIdx, "TileZ");
if ((TileX > 0) && (TileY > 0) && (TileZ > 0))
{
a_Hanging.SetPosition(
- (double)a_NBT.GetInt(TileX),
- (double)a_NBT.GetInt(TileY),
- (double)a_NBT.GetInt(TileZ)
+ static_cast<double>(a_NBT.GetInt(TileX)),
+ static_cast<double>(a_NBT.GetInt(TileY)),
+ static_cast<double>(a_NBT.GetInt(TileZ))
);
}
}
@@ -1838,6 +1813,29 @@ void cWSSAnvil::LoadItemFrameFromNBT(cEntityList & a_Entities, const cParsedNBT
+void cWSSAnvil::LoadPaintingFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
+{
+ // Load painting name:
+ int MotiveTag = a_NBT.FindChildByName(a_TagIdx, "Motive");
+ if ((MotiveTag < 0) || (a_NBT.GetType(MotiveTag) != TAG_String))
+ {
+ return;
+ }
+
+ std::unique_ptr<cPainting> Painting(new cPainting(a_NBT.GetString(MotiveTag), BLOCK_FACE_NONE, 0.0, 0.0, 0.0));
+ if (!LoadEntityBaseFromNBT(*Painting.get(), a_NBT, a_TagIdx))
+ {
+ return;
+ }
+
+ LoadHangingFromNBT(*Painting.get(), a_NBT, a_TagIdx);
+ a_Entities.push_back(Painting.release());
+}
+
+
+
+
+
void cWSSAnvil::LoadArrowFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
std::unique_ptr<cArrowEntity> Arrow(new cArrowEntity(nullptr, 0, 0, 0, Vector3d(0, 0, 0)));
diff --git a/src/WorldStorage/WSSAnvil.h b/src/WorldStorage/WSSAnvil.h
index 362796614..892645785 100755
--- a/src/WorldStorage/WSSAnvil.h
+++ b/src/WorldStorage/WSSAnvil.h
@@ -166,6 +166,7 @@ protected:
void LoadExpOrbFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx);
void LoadHangingFromNBT (cHangingEntity & a_Hanging, const cParsedNBT & a_NBT, int a_TagIdx);
void LoadItemFrameFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx);
+ void LoadPaintingFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx);
void LoadMinecartRFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx);
void LoadMinecartCFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx);
diff --git a/src/main.cpp b/src/main.cpp
index 20609a2f8..428e89e93 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -282,8 +282,12 @@ int main( int argc, char **argv)
}
} // for i - argv[]
+ // Initialize logging subsystem:
cLogger::InitiateMultithreading();
+ // Initialize LibEvent:
+ cNetworkSingleton::Get();
+
#if !defined(ANDROID_NDK)
try
#endif