1
0

Merge branch 'master' into awesometnt

This commit is contained in:
Tiger Wang 2014-03-10 18:36:55 +00:00
commit cc2d805238
88 changed files with 2148 additions and 628 deletions

View File

@ -22,8 +22,10 @@ Code Stuff
- This helps prevent mistakes such as `if (a & 1 == 0)` - This helps prevent mistakes such as `if (a & 1 == 0)`
* White space is free, so use it freely * White space is free, so use it freely
- "freely" as in "plentifully", not "arbitrarily" - "freely" as in "plentifully", not "arbitrarily"
* All `case` statements inside a `switch` need an extra indent.
* Each and every control statement deserves its braces. This helps maintainability later on when the file is edited, lines added or removed - the control logic doesn't break so easily. * Each and every control statement deserves its braces. This helps maintainability later on when the file is edited, lines added or removed - the control logic doesn't break so easily.
- The only exception: a `switch` statement with all `case` statements being a single short statement is allowed to use the short brace-less form. - The only exception: a `switch` statement with all `case` statements being a single short statement is allowed to use the short brace-less form.
- These two rules really mean that indent is governed by braces
* Add an empty last line in all source files (GCC and GIT can complain otherwise) * Add an empty last line in all source files (GCC and GIT can complain otherwise)
* Use doxy-comments for functions in the header file, format as `/** Description */` * Use doxy-comments for functions in the header file, format as `/** Description */`
* Use spaces after the comment markers: `// Comment` instead of `//Comment` * Use spaces after the comment markers: `// Comment` instead of `//Comment`

View File

@ -134,6 +134,7 @@ g_APIDesc =
HasBlockSkyLights = { Params = "", Return = "bool", Notes = "Returns true if current datatypes include skylight" }, HasBlockSkyLights = { Params = "", Return = "bool", Notes = "Returns true if current datatypes include skylight" },
HasBlockTypes = { Params = "", Return = "bool", Notes = "Returns true if current datatypes include block types" }, HasBlockTypes = { Params = "", Return = "bool", Notes = "Returns true if current datatypes include block types" },
LoadFromSchematicFile = { Params = "FileName", Return = "", Notes = "Clears current content and loads new content from the specified schematic file. Returns true if successful. Returns false and logs error if unsuccessful, old content is preserved in such a case." }, LoadFromSchematicFile = { Params = "FileName", Return = "", Notes = "Clears current content and loads new content from the specified schematic file. Returns true if successful. Returns false and logs error if unsuccessful, old content is preserved in such a case." },
LoadFromSchematicString = { Params = "SchematicData", Return = "", Notes = "Clears current content and loads new content from the specified string (assumed to contain .schematic data). Returns true if successful. Returns false and logs error if unsuccessful, old content is preserved in such a case." },
Merge = Merge =
{ {
{ Params = "BlockAreaSrc, {{Vector3i|RelMinCoords}}, Strategy", Return = "", Notes = "Merges BlockAreaSrc into this object at the specified relative coords, using the specified strategy" }, { Params = "BlockAreaSrc, {{Vector3i|RelMinCoords}}, Strategy", Return = "", Notes = "Merges BlockAreaSrc into this object at the specified relative coords, using the specified strategy" },
@ -161,6 +162,7 @@ g_APIDesc =
RotateCW = { Params = "", Return = "", Notes = "Rotates the block area around the Y axis, clockwise (north -> east). Modifies blocks' metas (if present) to match." }, RotateCW = { Params = "", Return = "", Notes = "Rotates the block area around the Y axis, clockwise (north -> east). Modifies blocks' metas (if present) to match." },
RotateCWNoMeta = { Params = "", Return = "", Notes = "Rotates the block area around the Y axis, clockwise (north -> east). Doesn't modify blocks' metas." }, RotateCWNoMeta = { Params = "", Return = "", Notes = "Rotates the block area around the Y axis, clockwise (north -> east). Doesn't modify blocks' metas." },
SaveToSchematicFile = { Params = "FileName", Return = "", Notes = "Saves the current contents to a schematic file. Returns true if successful." }, SaveToSchematicFile = { Params = "FileName", Return = "", Notes = "Saves the current contents to a schematic file. Returns true if successful." },
SaveToSchematicString = { Params = "", Return = "string", Notes = "Saves the current contents to a string (in a .schematic file format). Returns the data if successful, nil if failed." },
SetBlockLight = { Params = "BlockX, BlockY, BlockZ, BlockLight", Return = "", Notes = "Sets the blocklight at the specified absolute coords" }, SetBlockLight = { Params = "BlockX, BlockY, BlockZ, BlockLight", Return = "", Notes = "Sets the blocklight at the specified absolute coords" },
SetBlockMeta = { Params = "BlockX, BlockY, BlockZ, BlockMeta", Return = "", Notes = "Sets the block meta at the specified absolute coords" }, SetBlockMeta = { Params = "BlockX, BlockY, BlockZ, BlockMeta", Return = "", Notes = "Sets the block meta at the specified absolute coords" },
SetBlockSkyLight = { Params = "BlockX, BlockY, BlockZ, SkyLight", Return = "", Notes = "Sets the skylight at the specified absolute coords" }, SetBlockSkyLight = { Params = "BlockX, BlockY, BlockZ, SkyLight", Return = "", Notes = "Sets the skylight at the specified absolute coords" },
@ -483,6 +485,58 @@ end
}, },
}, -- cClientHandle }, -- cClientHandle
cCompositeChat =
{
Desc = [[
Encapsulates a chat message that can contain various formatting, URLs, commands executed on click
and commands suggested on click. The chat message can be sent by the regular chat-sending functions,
{{cPlayer}}:SendMessage(), {{cWorld}}:BroadcastChat() and {{cRoot}}:BroadcastChat().</p>
<p>
Note that most of the functions in this class are so-called modifiers - they modify the object and
then return the object itself, so that they can be chained one after another.
]],
Functions =
{
constructor =
{
{ Params = "", Return = "", Notes = "Creates an empty chat message" },
{ Params = "Text", Return = "", Notes = "Creates a chat message containing the specified text, parsed by the ParseText() function. This allows easy migration from old chat messages." },
},
AddRunCommandPart = { Params = "Text, Command, [Style]", Return = "self", Notes = "Adds a text which, when clicked, runs the specified command. Chaining." },
AddSuggestCommandPart = { Params = "Text, Command, [Style]", Return = "self", Notes = "Adds a text which, when clicked, puts the specified command into the player's chat input area. Chaining." },
AddTextPart = { Params = "Text, [Style]", Return = "self", Notes = "Adds a regular text. Chaining." },
AddUrlPart = { Params = "Text, Url, [Style]", Return = "self", Notes = "Adds a text which, when clicked, opens up a browser at the specified URL. Chaining." },
Clear = { Params = "", Return = "", Notes = "Removes all parts from this object" },
GetMessageType = { Params = "", Return = "MessageType", Notes = "Returns the MessageType (mtXXX constant) that is associated with this message. When sent to a player, the message will be formatted according to this message type and the player's settings (adding \"[INFO]\" prefix etc.)" },
ParseText = { Params = "Text", Return = "self", Notes = "Adds text, while recognizing http and https URLs and old-style formatting codes (\"@2\"). Chaining." },
SetMessageType = { Params = "MessageType", Return = "self", Notes = "Sets the MessageType (mtXXX constant) that is associated with this message. When sent to a player, the message will be formatted according to this message type and the player's settings (adding \"[INFO]\" prefix etc.) Chaining." },
UnderlineUrls = { Params = "", Return = "self", Notes = "Makes all URL parts contained in the message underlined. Doesn't affect parts added in the future. Chaining." },
},
AdditionalInfo =
{
{
Header = "Chaining example",
Contents = [[
Sending a chat message that is composed of multiple different parts has been made easy thanks to
chaining. Consider the following example that shows how a message containing all kinds of parts
is sent (adapted from the Debuggers plugin):
<pre class="prettyprint lang-lua">
function OnPlayerJoined(a_Player)
-- Send an example composite chat message to the player:
a_Player:SendMessage(cCompositeChat()
:AddTextPart("Hello, ")
:AddUrlPart(a_Player:GetName(), "www.mc-server.org", "u@2") -- Colored underlined link
:AddSuggestCommandPart(", and welcome.", "/help", "u") -- Underlined suggest-command
:AddRunCommandPart(" SetDay", "/time set 0") -- Regular text that will execute command when clicked
:SetMessageType(mtJoin) -- It is a join-message
)
end</pre>
]],
},
}, -- AdditionalInfo
}, -- cCompositeChat
cCraftingGrid = cCraftingGrid =
{ {
Desc = [[ Desc = [[
@ -2098,7 +2152,9 @@ end
DoWithDropSpenserAt = { Params = "X, Y, Z, CallbackFunction, [CallbackData]", Return = "bool", Notes = "If there is a dropper or a dispenser at the specified coords, calls the CallbackFunction with the {{cDropSpenserEntity}} parameter representing the dropper or dispenser. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cDropSpenserEntity|DropSpenserEntity}}, [CallbackData])</pre> Note that this can be used to access both dispensers and droppers in a similar way. The function returns false if there is neither dispenser nor dropper, or if there is, it returns the bool value that the callback has returned." }, DoWithDropSpenserAt = { Params = "X, Y, Z, CallbackFunction, [CallbackData]", Return = "bool", Notes = "If there is a dropper or a dispenser at the specified coords, calls the CallbackFunction with the {{cDropSpenserEntity}} parameter representing the dropper or dispenser. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cDropSpenserEntity|DropSpenserEntity}}, [CallbackData])</pre> Note that this can be used to access both dispensers and droppers in a similar way. The function returns false if there is neither dispenser nor dropper, or if there is, it returns the bool value that the callback has returned." },
DoWithDropperAt = { Params = "X, Y, Z, CallbackFunction, [CallbackData]", Return = "bool", Notes = "If there is a dropper at the specified coords, calls the CallbackFunction with the {{cDropperEntity}} parameter representing the dropper. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cDropperEntity|DropperEntity}}, [CallbackData])</pre> The function returns false if there is no dropper, or if there is, it returns the bool value that the callback has returned." }, DoWithDropperAt = { Params = "X, Y, Z, CallbackFunction, [CallbackData]", Return = "bool", Notes = "If there is a dropper at the specified coords, calls the CallbackFunction with the {{cDropperEntity}} parameter representing the dropper. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cDropperEntity|DropperEntity}}, [CallbackData])</pre> The function returns false if there is no dropper, or if there is, it returns the bool value that the callback has returned." },
DoWithEntityByID = { Params = "EntityID, CallbackFunction, [CallbackData]", Return = "bool", Notes = "If an entity with the specified ID exists, calls the callback with the {{cEntity}} parameter representing the entity. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cEntity|Entity}}, [CallbackData])</pre> The function returns false if the entity was not found, and it returns the same bool value that the callback has returned if the entity was found." }, DoWithEntityByID = { Params = "EntityID, CallbackFunction, [CallbackData]", Return = "bool", Notes = "If an entity with the specified ID exists, calls the callback with the {{cEntity}} parameter representing the entity. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cEntity|Entity}}, [CallbackData])</pre> The function returns false if the entity was not found, and it returns the same bool value that the callback has returned if the entity was found." },
DoWithFlowerPotAt = { Params = "X, Y, Z, CallbackFunction, [CallbackData]", Return = "bool", Notes = "If there is a flower pot at the specified coords, calls the CallbackFunction with the {{cFlowerPotEntity}} parameter representing the flower pot. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cFlowerPotEntity|FlowerPotEntity}}, [CallbackData])</pre> The function returns false if there is no flower pot, or if there is, it returns the bool value that the callback has returned." },
DoWithFurnaceAt = { Params = "X, Y, Z, CallbackFunction, [CallbackData]", Return = "bool", Notes = "If there is a furnace at the specified coords, calls the CallbackFunction with the {{cFurnaceEntity}} parameter representing the furnace. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cFurnaceEntity|FurnaceEntity}}, [CallbackData])</pre> The function returns false if there is no furnace, or if there is, it returns the bool value that the callback has returned." }, DoWithFurnaceAt = { Params = "X, Y, Z, CallbackFunction, [CallbackData]", Return = "bool", Notes = "If there is a furnace at the specified coords, calls the CallbackFunction with the {{cFurnaceEntity}} parameter representing the furnace. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cFurnaceEntity|FurnaceEntity}}, [CallbackData])</pre> The function returns false if there is no furnace, or if there is, it returns the bool value that the callback has returned." },
DoWithMobHeadAt = { Params = "X, Y, Z, CallbackFunction, [CallbackData]", Return = "bool", Notes = "If there is a mob head at the specified coords, calls the CallbackFunction with the {{cMobHeadEntity}} parameter representing the furnace. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cMobHeadEntity|MobHeadEntity}}, [CallbackData])</pre> The function returns false if there is no mob head, or if there is, it returns the bool value that the callback has returned." },
DoWithNoteBlockAt = { Params = "BlockX, BlockY, BlockZ, CallbackFunction, [CallbackData]", Return = "bool", Notes = "If there is a note block at the specified coords, calls the CallbackFunction with the {{cNoteEntity}} parameter representing the note block. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cNoteEntity|NoteEntity}}, [CallbackData])</pre> The function returns false if there is no note block, or if there is, it returns the bool value that the callback has returned." }, DoWithNoteBlockAt = { Params = "BlockX, BlockY, BlockZ, CallbackFunction, [CallbackData]", Return = "bool", Notes = "If there is a note block at the specified coords, calls the CallbackFunction with the {{cNoteEntity}} parameter representing the note block. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cNoteEntity|NoteEntity}}, [CallbackData])</pre> The function returns false if there is no note block, or if there is, it returns the bool value that the callback has returned." },
DoWithPlayer = { Params = "PlayerName, CallbackFunction, [CallbackData]", Return = "bool", Notes = "If there is a player of the specified name (exact match), calls the CallbackFunction with the {{cPlayer}} parameter representing the player. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cPlayer|Player}}, [CallbackData])</pre> The function returns false if the player was not found, or whatever bool value the callback returned if the player was found." }, DoWithPlayer = { Params = "PlayerName, CallbackFunction, [CallbackData]", Return = "bool", Notes = "If there is a player of the specified name (exact match), calls the CallbackFunction with the {{cPlayer}} parameter representing the player. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cPlayer|Player}}, [CallbackData])</pre> The function returns false if the player was not found, or whatever bool value the callback returned if the player was found." },
FastSetBlock = FastSetBlock =
@ -2701,16 +2757,16 @@ end
IgnoreClasses = IgnoreClasses =
{ {
"coroutine", "^coroutine$",
"debug", "^debug$",
"io", "^io$",
"math", "^math$",
"package", "^package$",
"os", "^os$",
"string", "^string$",
"table", "^table$",
"g_Stats", "^g_Stats$",
"g_TrackedPages", "^g_TrackedPages$",
}, },
IgnoreFunctions = IgnoreFunctions =

View File

@ -238,6 +238,20 @@ World:ForEachChestInChunk(Player:GetChunkX(), Player:GetChunkZ(),
}, },
Inherits = "cBlockEntity"; Inherits = "cBlockEntity";
}, -- cSignEntity }, -- cSignEntity
cFlowerPotEntity =
{
Desc = [[
This class represents a flower pot entity in the world.
]],
Functions =
{
IsItemInPot = { Params = "", Return = "bool", Notes = "Is a flower in the pot?" },
GetItem = { Params = "", Return = "{{cItem|Item}}", Notes = "Returns the item in the flower pot." },
SetItem = { Params = "{{cItem|Item}}", Return = "", Notes = "Set the item in the flower pot" },
},
Inherits = "cBlockEntity";
}, -- cFlowerPotEntity
} }

@ -1 +1 @@
Subproject commit 3b416b07a339b3abcbc127070d56eea05b05373d Subproject commit 013a32a7fb3c8a6cfe0aef892d4c7394d4e1be59

View File

@ -30,6 +30,7 @@ function Initialize(Plugin)
PM:AddHook(cPluginManager.HOOK_CHUNK_GENERATED, OnChunkGenerated); PM:AddHook(cPluginManager.HOOK_CHUNK_GENERATED, OnChunkGenerated);
PM:AddHook(cPluginManager.HOOK_PLUGINS_LOADED, OnPluginsLoaded); PM:AddHook(cPluginManager.HOOK_PLUGINS_LOADED, OnPluginsLoaded);
PM:AddHook(cPluginManager.HOOK_PLUGIN_MESSAGE, OnPluginMessage); PM:AddHook(cPluginManager.HOOK_PLUGIN_MESSAGE, OnPluginMessage);
PM:AddHook(cPluginManager.HOOK_PLAYER_JOINED, OnPlayerJoined)
PM:BindCommand("/le", "debuggers", HandleListEntitiesCmd, "- Shows a list of all the loaded entities"); PM:BindCommand("/le", "debuggers", HandleListEntitiesCmd, "- Shows a list of all the loaded entities");
PM:BindCommand("/ke", "debuggers", HandleKillEntitiesCmd, "- Kills all the loaded entities"); PM:BindCommand("/ke", "debuggers", HandleKillEntitiesCmd, "- Kills all the loaded entities");
@ -70,6 +71,8 @@ function Initialize(Plugin)
-- TestExpatBindings(); -- TestExpatBindings();
-- TestPluginCalls(); -- TestPluginCalls();
TestBlockAreasString()
return true return true
end; end;
@ -201,6 +204,42 @@ end
function TestBlockAreasString()
-- Write one area to string, then to file:
local BA1 = cBlockArea()
BA1:Create(5, 5, 5, cBlockArea.baTypes + cBlockArea.baMetas)
BA1:Fill(cBlockArea.baTypes, E_BLOCK_DIAMOND_BLOCK)
BA1:FillRelCuboid(1, 3, 1, 3, 1, 3, cBlockArea.baTypes, E_BLOCK_GOLD_BLOCK)
local Data = BA1:SaveToSchematicString()
if ((type(Data) ~= "string") or (Data == "")) then
LOG("Cannot save schematic to string")
return
end
cFile:CreateFolder("schematics")
local f = io.open("schematics/StringTest.schematic", "w")
f:write(Data)
f:close()
-- Load a second area from that file:
local BA2 = cBlockArea()
if not(BA2:LoadFromSchematicFile("schematics/StringTest.schematic")) then
LOG("Cannot read schematic from string test file")
return
end
BA2:Clear()
-- Load another area from a string in that file:
f = io.open("schematics/StringTest.schematic", "r")
Data = f:read("*all")
if not(BA2:LoadFromSchematicString(Data)) then
LOG("Cannot load schematic from string")
end
end
function TestSQLiteBindings() function TestSQLiteBindings()
LOG("Testing SQLite bindings..."); LOG("Testing SQLite bindings...");
@ -1258,3 +1297,17 @@ end
function OnPlayerJoined(a_Player)
-- Test composite chat chaining:
a_Player:SendMessage(cCompositeChat()
:AddTextPart("Hello, ")
:AddUrlPart(a_Player:GetName(), "www.mc-server.org", "u@2")
:AddSuggestCommandPart(", and welcome.", "/help", "u")
:AddRunCommandPart(" SetDay", "/time set 0")
)
end

View File

@ -317,26 +317,19 @@ local function WriteCommandsCategoryGithub(a_Category, f)
if (CategoryName == "") then if (CategoryName == "") then
CategoryName = "General"; CategoryName = "General";
end end
f:write("\n## ", GithubizeString(a_Category.DisplayName or CategoryName), "\n"); f:write("\n### ", GithubizeString(a_Category.DisplayName or CategoryName), "\n");
-- Write description: -- Write description:
if (a_Category.Description ~= "") then if (a_Category.Description ~= "") then
f:write(GithubizeString(a_Category.Description), "\n"); f:write(GithubizeString(a_Category.Description), "\n\n");
end end
f:write("| Command | Permission | Description | \n")
f:write("| ------- | ---------- | ----------- | \n")
-- Write commands: -- Write commands:
f:write("\n");
for idx2, cmd in ipairs(a_Category.Commands) do for idx2, cmd in ipairs(a_Category.Commands) do
f:write("\n### ", cmd.CommandString, "\n", GithubizeString(cmd.Info.HelpString or "UNDOCUMENTED"), "\n\n"); f:write("|", cmd.CommandString, " | ", cmd.Info.Permission or "", " | ", GithubizeString(cmd.Info.HelpString or "UNDOCUMENTED"), "| \n")
if (cmd.Info.Permission ~= nil) then
f:write("Permission required: **", cmd.Info.Permission, "**\n\n");
end
if (cmd.Info.DetailedDescription ~= nil) then
f:write(GithubizeString(cmd.Info.DetailedDescription));
end
if (cmd.Info.ParameterCombinations ~= nil) then
WriteCommandParameterCombinationsGithub(cmd.CommandString, cmd.Info.ParameterCombinations, f);
end
end end
f:write("\n\n") f:write("\n\n")
end end
@ -537,12 +530,13 @@ local function DumpPermissionsGithub(a_PluginInfo, f)
-- Dump the permissions: -- Dump the permissions:
f:write("\n# Permissions\n"); f:write("\n# Permissions\n");
f:write("| Permissions | Description | Commands | Recommended groups |\n")
f:write("| ----------- | ----------- | -------- | ------------------ |\n")
for idx, perm in ipairs(Permissions) do for idx, perm in ipairs(Permissions) do
f:write("### ", perm.Name, "\n"); f:write(perm.Name, " | ");
f:write(GithubizeString(perm.Info.Description or "")); f:write(GithubizeString(perm.Info.Description or ""), " | ");
local CommandsAffected = perm.Info.CommandsAffected or {}; local CommandsAffected = perm.Info.CommandsAffected or {};
if (#CommandsAffected > 0) then if (#CommandsAffected > 0) then
f:write("\n\nCommands affected:\n - ");
local Affects = {}; local Affects = {};
for idx2, cmd in ipairs(CommandsAffected) do for idx2, cmd in ipairs(CommandsAffected) do
if (type(cmd) == "string") then if (type(cmd) == "string") then
@ -551,11 +545,10 @@ local function DumpPermissionsGithub(a_PluginInfo, f)
table.insert(Affects, GetCommandRefGithub(cmd.Name, cmd)); table.insert(Affects, GetCommandRefGithub(cmd.Name, cmd));
end end
end end
f:write(table.concat(Affects, "\n - ")); f:write(table.concat(Affects, ", "), " | ");
f:write("\n");
end end
if (perm.Info.RecommendedGroups ~= nil) then if (perm.Info.RecommendedGroups ~= nil) then
f:write("\n\nRecommended groups: ", perm.Info.RecommendedGroups, "\n"); f:write(perm.Info.RecommendedGroups, " |");
end end
f:write("\n"); f:write("\n");
end end

View File

@ -1,5 +1,3 @@
macro (add_flags_lnk FLAGS) macro (add_flags_lnk FLAGS)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${FLAGS}") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${FLAGS}")
set(CMAKE_EXE_LINKER_FLAGS_DEBUG "${CMAKE_EXE_LINKER_FLAGS_DEBUG} ${FLAGS}") set(CMAKE_EXE_LINKER_FLAGS_DEBUG "${CMAKE_EXE_LINKER_FLAGS_DEBUG} ${FLAGS}")
@ -62,6 +60,7 @@ macro(set_flags)
# We use a signed char (fixes #640 on RasPi) # We use a signed char (fixes #640 on RasPi)
add_flags_cxx("-fsigned-char") add_flags_cxx("-fsigned-char")
endif() endif()
@ -184,6 +183,14 @@ macro(set_exe_flags)
string(REPLACE "-w" "" CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}") string(REPLACE "-w" "" CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}")
string(REPLACE "-w" "" CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG}") string(REPLACE "-w" "" CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG}")
add_flags_cxx("-Wall -Wextra") add_flags_cxx("-Wall -Wextra")
# we support non-IEEE 754 fpus so can make no guarentees about error
add_flags_cxx("-ffast-math")
# clang does not provide the __extern_always_inline macro and a part of libm depends on this when using fast-math
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
add_flags_cxx("-D__extern_always_inline=inline")
endif()
endif() endif()
endmacro() endmacro()

@ -1 +1 @@
Subproject commit 2ceda579893ceb23c5eb0d56df47dc235644e0f4 Subproject commit 2cb1a0c4009ecf368ecc74eb428394e10f9e6d00

View File

@ -58,6 +58,8 @@ $cfile "../BlockEntities/HopperEntity.h"
$cfile "../BlockEntities/JukeboxEntity.h" $cfile "../BlockEntities/JukeboxEntity.h"
$cfile "../BlockEntities/NoteEntity.h" $cfile "../BlockEntities/NoteEntity.h"
$cfile "../BlockEntities/SignEntity.h" $cfile "../BlockEntities/SignEntity.h"
$cfile "../BlockEntities/MobHeadEntity.h"
$cfile "../BlockEntities/FlowerPotEntity.h"
$cfile "../WebAdmin.h" $cfile "../WebAdmin.h"
$cfile "../Root.h" $cfile "../Root.h"
$cfile "../Vector3f.h" $cfile "../Vector3f.h"

View File

@ -677,6 +677,7 @@ void cLuaState::Push(Vector3i * a_Vector)
void cLuaState::Push(void * a_Ptr) void cLuaState::Push(void * a_Ptr)
{ {
UNUSED(a_Ptr);
ASSERT(IsValid()); ASSERT(IsValid());
// Investigate the cause of this - what is the callstack? // Investigate the cause of this - what is the callstack?
@ -716,7 +717,7 @@ void cLuaState::Push(cBlockEntity * a_BlockEntity)
void cLuaState::GetReturn(int a_StackPos, bool & a_ReturnedVal) void cLuaState::GetStackValue(int a_StackPos, bool & a_ReturnedVal)
{ {
a_ReturnedVal = (tolua_toboolean(m_LuaState, a_StackPos, a_ReturnedVal ? 1 : 0) > 0); a_ReturnedVal = (tolua_toboolean(m_LuaState, a_StackPos, a_ReturnedVal ? 1 : 0) > 0);
} }
@ -725,11 +726,17 @@ void cLuaState::GetReturn(int a_StackPos, bool & a_ReturnedVal)
void cLuaState::GetReturn(int a_StackPos, AString & a_ReturnedVal) void cLuaState::GetStackValue(int a_StackPos, AString & a_Value)
{ {
if (lua_isstring(m_LuaState, a_StackPos)) size_t len = 0;
const char * data = lua_tolstring(m_LuaState, a_StackPos, &len);
if (data != NULL)
{ {
a_ReturnedVal = tolua_tocppstring(m_LuaState, a_StackPos, a_ReturnedVal.c_str()); a_Value.assign(data, len);
}
else
{
a_Value.clear();
} }
} }
@ -737,7 +744,7 @@ void cLuaState::GetReturn(int a_StackPos, AString & a_ReturnedVal)
void cLuaState::GetReturn(int a_StackPos, int & a_ReturnedVal) void cLuaState::GetStackValue(int a_StackPos, int & a_ReturnedVal)
{ {
if (lua_isnumber(m_LuaState, a_StackPos)) if (lua_isnumber(m_LuaState, a_StackPos))
{ {
@ -749,7 +756,7 @@ void cLuaState::GetReturn(int a_StackPos, int & a_ReturnedVal)
void cLuaState::GetReturn(int a_StackPos, double & a_ReturnedVal) void cLuaState::GetStackValue(int a_StackPos, double & a_ReturnedVal)
{ {
if (lua_isnumber(m_LuaState, a_StackPos)) if (lua_isnumber(m_LuaState, a_StackPos))
{ {

View File

@ -197,6 +197,19 @@ public:
void Push(void * a_Ptr); void Push(void * a_Ptr);
void Push(cHopperEntity * a_Hopper); void Push(cHopperEntity * a_Hopper);
void Push(cBlockEntity * a_BlockEntity); void Push(cBlockEntity * a_BlockEntity);
/** Retrieve value at a_StackPos, if it is a valid bool. If not, a_Value is unchanged */
void GetStackValue(int a_StackPos, bool & a_Value);
/** Retrieve value at a_StackPos, if it is a valid string. If not, a_Value is unchanged */
void GetStackValue(int a_StackPos, AString & a_Value);
/** Retrieve value at a_StackPos, if it is a valid number. If not, a_Value is unchanged */
void GetStackValue(int a_StackPos, int & a_Value);
/** Retrieve value at a_StackPos, if it is a valid number. If not, a_Value is unchanged */
void GetStackValue(int a_StackPos, double & a_Value);
/** Call any 0-param 0-return Lua function in a single line: */ /** Call any 0-param 0-return Lua function in a single line: */
template <typename FnT> template <typename FnT>
@ -270,7 +283,7 @@ public:
{ {
return false; return false;
} }
GetReturn(-1, a_Ret1); GetStackValue(-1, a_Ret1);
lua_pop(m_LuaState, 1); lua_pop(m_LuaState, 1);
return true; return true;
} }
@ -292,7 +305,7 @@ public:
{ {
return false; return false;
} }
GetReturn(-1, a_Ret1); GetStackValue(-1, a_Ret1);
lua_pop(m_LuaState, 1); lua_pop(m_LuaState, 1);
ASSERT(InitialTop == lua_gettop(m_LuaState)); ASSERT(InitialTop == lua_gettop(m_LuaState));
return true; return true;
@ -315,7 +328,7 @@ public:
{ {
return false; return false;
} }
GetReturn(-1, a_Ret1); GetStackValue(-1, a_Ret1);
lua_pop(m_LuaState, 1); lua_pop(m_LuaState, 1);
return true; return true;
} }
@ -338,7 +351,7 @@ public:
{ {
return false; return false;
} }
GetReturn(-1, a_Ret1); GetStackValue(-1, a_Ret1);
lua_pop(m_LuaState, 1); lua_pop(m_LuaState, 1);
return true; return true;
} }
@ -362,7 +375,7 @@ public:
{ {
return false; return false;
} }
GetReturn(-1, a_Ret1); GetStackValue(-1, a_Ret1);
lua_pop(m_LuaState, 1); lua_pop(m_LuaState, 1);
return true; return true;
} }
@ -387,7 +400,7 @@ public:
{ {
return false; return false;
} }
GetReturn(-1, a_Ret1); GetStackValue(-1, a_Ret1);
lua_pop(m_LuaState, 1); lua_pop(m_LuaState, 1);
return true; return true;
} }
@ -414,7 +427,7 @@ public:
{ {
return false; return false;
} }
GetReturn(-1, a_Ret1); GetStackValue(-1, a_Ret1);
lua_pop(m_LuaState, 1); lua_pop(m_LuaState, 1);
return true; return true;
} }
@ -442,7 +455,7 @@ public:
{ {
return false; return false;
} }
GetReturn(-1, a_Ret1); GetStackValue(-1, a_Ret1);
lua_pop(m_LuaState, 1); lua_pop(m_LuaState, 1);
return true; return true;
} }
@ -471,7 +484,7 @@ public:
{ {
return false; return false;
} }
GetReturn(-1, a_Ret1); GetStackValue(-1, a_Ret1);
lua_pop(m_LuaState, 1); lua_pop(m_LuaState, 1);
return true; return true;
} }
@ -501,7 +514,7 @@ public:
{ {
return false; return false;
} }
GetReturn(-1, a_Ret1); GetStackValue(-1, a_Ret1);
lua_pop(m_LuaState, 1); lua_pop(m_LuaState, 1);
return true; return true;
} }
@ -532,7 +545,7 @@ public:
{ {
return false; return false;
} }
GetReturn(-1, a_Ret1); GetStackValue(-1, a_Ret1);
lua_pop(m_LuaState, 1); lua_pop(m_LuaState, 1);
return true; return true;
} }
@ -553,8 +566,8 @@ public:
{ {
return false; return false;
} }
GetReturn(-2, a_Ret1); GetStackValue(-2, a_Ret1);
GetReturn(-1, a_Ret2); GetStackValue(-1, a_Ret2);
lua_pop(m_LuaState, 2); lua_pop(m_LuaState, 2);
return true; return true;
} }
@ -576,8 +589,8 @@ public:
{ {
return false; return false;
} }
GetReturn(-2, a_Ret1); GetStackValue(-2, a_Ret1);
GetReturn(-1, a_Ret2); GetStackValue(-1, a_Ret2);
lua_pop(m_LuaState, 2); lua_pop(m_LuaState, 2);
return true; return true;
} }
@ -601,8 +614,8 @@ public:
{ {
return false; return false;
} }
GetReturn(-2, a_Ret1); GetStackValue(-2, a_Ret1);
GetReturn(-1, a_Ret2); GetStackValue(-1, a_Ret2);
lua_pop(m_LuaState, 2); lua_pop(m_LuaState, 2);
return true; return true;
} }
@ -627,8 +640,8 @@ public:
{ {
return false; return false;
} }
GetReturn(-2, a_Ret1); GetStackValue(-2, a_Ret1);
GetReturn(-1, a_Ret2); GetStackValue(-1, a_Ret2);
lua_pop(m_LuaState, 2); lua_pop(m_LuaState, 2);
return true; return true;
} }
@ -654,8 +667,8 @@ public:
{ {
return false; return false;
} }
GetReturn(-2, a_Ret1); GetStackValue(-2, a_Ret1);
GetReturn(-1, a_Ret2); GetStackValue(-1, a_Ret2);
lua_pop(m_LuaState, 2); lua_pop(m_LuaState, 2);
return true; return true;
} }
@ -683,8 +696,8 @@ public:
{ {
return false; return false;
} }
GetReturn(-2, a_Ret1); GetStackValue(-2, a_Ret1);
GetReturn(-1, a_Ret2); GetStackValue(-1, a_Ret2);
lua_pop(m_LuaState, 2); lua_pop(m_LuaState, 2);
return true; return true;
} }
@ -713,8 +726,8 @@ public:
{ {
return false; return false;
} }
GetReturn(-2, a_Ret1); GetStackValue(-2, a_Ret1);
GetReturn(-1, a_Ret2); GetStackValue(-1, a_Ret2);
lua_pop(m_LuaState, 2); lua_pop(m_LuaState, 2);
return true; return true;
} }
@ -743,9 +756,9 @@ public:
{ {
return false; return false;
} }
GetReturn(-3, a_Ret1); GetStackValue(-3, a_Ret1);
GetReturn(-2, a_Ret2); GetStackValue(-2, a_Ret2);
GetReturn(-1, a_Ret3); GetStackValue(-1, a_Ret3);
lua_pop(m_LuaState, 3); lua_pop(m_LuaState, 3);
return true; return true;
} }
@ -775,9 +788,9 @@ public:
{ {
return false; return false;
} }
GetReturn(-3, a_Ret1); GetStackValue(-3, a_Ret1);
GetReturn(-2, a_Ret2); GetStackValue(-2, a_Ret2);
GetReturn(-1, a_Ret3); GetStackValue(-1, a_Ret3);
lua_pop(m_LuaState, 3); lua_pop(m_LuaState, 3);
return true; return true;
} }
@ -808,11 +821,11 @@ public:
{ {
return false; return false;
} }
GetReturn(-5, a_Ret1); GetStackValue(-5, a_Ret1);
GetReturn(-4, a_Ret2); GetStackValue(-4, a_Ret2);
GetReturn(-3, a_Ret3); GetStackValue(-3, a_Ret3);
GetReturn(-2, a_Ret4); GetStackValue(-2, a_Ret4);
GetReturn(-1, a_Ret5); GetStackValue(-1, a_Ret5);
lua_pop(m_LuaState, 5); lua_pop(m_LuaState, 5);
return true; return true;
} }
@ -918,18 +931,6 @@ protected:
/** Pushes a usertype of the specified class type onto the stack */ /** Pushes a usertype of the specified class type onto the stack */
void PushUserType(void * a_Object, const char * a_Type); void PushUserType(void * a_Object, const char * a_Type);
/** Retrieve value returned at a_StackPos, if it is a valid bool. If not, a_ReturnedVal is unchanged */
void GetReturn(int a_StackPos, bool & a_ReturnedVal);
/** Retrieve value returned at a_StackPos, if it is a valid string. If not, a_ReturnedVal is unchanged */
void GetReturn(int a_StackPos, AString & a_ReturnedVal);
/** Retrieve value returned at a_StackPos, if it is a valid number. If not, a_ReturnedVal is unchanged */
void GetReturn(int a_StackPos, int & a_ReturnedVal);
/** Retrieve value returned at a_StackPos, if it is a valid number. If not, a_ReturnedVal is unchanged */
void GetReturn(int a_StackPos, double & a_ReturnedVal);
/** /**
Calls the function that has been pushed onto the stack by PushFunction(), Calls the function that has been pushed onto the stack by PushFunction(),
with arguments pushed by PushXXX(). with arguments pushed by PushXXX().

View File

@ -23,9 +23,11 @@
#include "../BlockEntities/HopperEntity.h" #include "../BlockEntities/HopperEntity.h"
#include "../BlockEntities/NoteEntity.h" #include "../BlockEntities/NoteEntity.h"
#include "../BlockEntities/MobHeadEntity.h" #include "../BlockEntities/MobHeadEntity.h"
#include "../BlockEntities/FlowerPotEntity.h"
#include "md5/md5.h" #include "md5/md5.h"
#include "../LineBlockTracer.h" #include "../LineBlockTracer.h"
#include "../WorldStorage/SchematicFileSerializer.h" #include "../WorldStorage/SchematicFileSerializer.h"
#include "../CompositeChat.h"
@ -2455,7 +2457,7 @@ static int tolua_cBlockArea_GetSize(lua_State * tolua_S)
static int tolua_cBlockArea_LoadFromSchematicFile(lua_State * tolua_S) static int tolua_cBlockArea_LoadFromSchematicFile(lua_State * tolua_S)
{ {
// function cBlockArea::LoadFromSchematicFile // function cBlockArea::LoadFromSchematicFile
// Exported manually because function has been moved to SchematicFileSerilizer.cpp // Exported manually because function has been moved to SchematicFileSerializer.cpp
cLuaState L(tolua_S); cLuaState L(tolua_S);
if ( if (
!L.CheckParamUserType(1, "cBlockArea") || !L.CheckParamUserType(1, "cBlockArea") ||
@ -2482,10 +2484,41 @@ static int tolua_cBlockArea_LoadFromSchematicFile(lua_State * tolua_S)
static int tolua_cBlockArea_LoadFromSchematicString(lua_State * tolua_S)
{
// function cBlockArea::LoadFromSchematicString
// Exported manually because function has been moved to SchematicFileSerializer.cpp
cLuaState L(tolua_S);
if (
!L.CheckParamUserType(1, "cBlockArea") ||
!L.CheckParamString (2) ||
!L.CheckParamEnd (3)
)
{
return 0;
}
cBlockArea * self = (cBlockArea *)tolua_tousertype(tolua_S, 1, NULL);
if (self == NULL)
{
tolua_error(tolua_S, "invalid 'self' in function 'cBlockArea::LoadFromSchematicFile'", NULL);
return 0;
}
AString Data;
L.GetStackValue(2, Data);
bool res = cSchematicFileSerializer::LoadFromSchematicString(*self, Data);
tolua_pushboolean(tolua_S, res);
return 1;
}
static int tolua_cBlockArea_SaveToSchematicFile(lua_State * tolua_S) static int tolua_cBlockArea_SaveToSchematicFile(lua_State * tolua_S)
{ {
// function cBlockArea::SaveToSchematicFile // function cBlockArea::SaveToSchematicFile
// Exported manually because function has been moved to SchematicFileSerilizer.cpp // Exported manually because function has been moved to SchematicFileSerializer.cpp
cLuaState L(tolua_S); cLuaState L(tolua_S);
if ( if (
!L.CheckParamUserType(1, "cBlockArea") || !L.CheckParamUserType(1, "cBlockArea") ||
@ -2511,6 +2544,285 @@ static int tolua_cBlockArea_SaveToSchematicFile(lua_State * tolua_S)
static int tolua_cBlockArea_SaveToSchematicString(lua_State * tolua_S)
{
// function cBlockArea::SaveToSchematicString
// Exported manually because function has been moved to SchematicFileSerializer.cpp
cLuaState L(tolua_S);
if (
!L.CheckParamUserType(1, "cBlockArea") ||
!L.CheckParamEnd (2)
)
{
return 0;
}
cBlockArea * self = (cBlockArea *)tolua_tousertype(tolua_S, 1, NULL);
if (self == NULL)
{
tolua_error(tolua_S, "invalid 'self' in function 'cBlockArea::SaveToSchematicFile'", NULL);
return 0;
}
AString Data;
if (cSchematicFileSerializer::SaveToSchematicString(*self, Data))
{
L.Push(Data);
return 1;
}
return 0;
}
static int tolua_cCompositeChat_AddRunCommandPart(lua_State * tolua_S)
{
// function cCompositeChat:AddRunCommandPart(Message, Command, [Style])
// Exported manually to support call-chaining (return *this)
// Check params:
cLuaState L(tolua_S);
if (
!L.CheckParamUserType(1, "cCompositeChat") ||
!L.CheckParamString(2, 3)
)
{
return 0;
}
cCompositeChat * self = (cCompositeChat *)tolua_tousertype(tolua_S, 1, NULL);
if (self == NULL)
{
tolua_error(tolua_S, "invalid 'self' in function 'cCompositeChat:AddRunCommandPart'", NULL);
return 0;
}
// Add the part:
AString Text, Command, Style;
L.GetStackValue(2, Text);
L.GetStackValue(3, Command);
L.GetStackValue(4, Style);
self->AddRunCommandPart(Text, Command, Style);
// Cut away everything from the stack except for the cCompositeChat instance; return that:
lua_settop(L, 1);
return 1;
}
static int tolua_cCompositeChat_AddSuggestCommandPart(lua_State * tolua_S)
{
// function cCompositeChat:AddSuggestCommandPart(Message, Command, [Style])
// Exported manually to support call-chaining (return *this)
// Check params:
cLuaState L(tolua_S);
if (
!L.CheckParamUserType(1, "cCompositeChat") ||
!L.CheckParamString(2, 3)
)
{
return 0;
}
cCompositeChat * self = (cCompositeChat *)tolua_tousertype(tolua_S, 1, NULL);
if (self == NULL)
{
tolua_error(tolua_S, "invalid 'self' in function 'cCompositeChat:AddSuggestCommandPart'", NULL);
return 0;
}
// Add the part:
AString Text, Command, Style;
L.GetStackValue(2, Text);
L.GetStackValue(3, Command);
L.GetStackValue(4, Style);
self->AddSuggestCommandPart(Text, Command, Style);
// Cut away everything from the stack except for the cCompositeChat instance; return that:
lua_settop(L, 1);
return 1;
}
static int tolua_cCompositeChat_AddTextPart(lua_State * tolua_S)
{
// function cCompositeChat:AddTextPart(Message, [Style])
// Exported manually to support call-chaining (return *this)
// Check params:
cLuaState L(tolua_S);
if (
!L.CheckParamUserType(1, "cCompositeChat") ||
!L.CheckParamString(2)
)
{
return 0;
}
cCompositeChat * self = (cCompositeChat *)tolua_tousertype(tolua_S, 1, NULL);
if (self == NULL)
{
tolua_error(tolua_S, "invalid 'self' in function 'cCompositeChat:AddTextPart'", NULL);
return 0;
}
// Add the part:
AString Text, Style;
L.GetStackValue(2, Text);
L.GetStackValue(3, Style);
self->AddTextPart(Text, Style);
// Cut away everything from the stack except for the cCompositeChat instance; return that:
lua_settop(L, 1);
return 1;
}
static int tolua_cCompositeChat_AddUrlPart(lua_State * tolua_S)
{
// function cCompositeChat:AddTextPart(Message, Url, [Style])
// Exported manually to support call-chaining (return *this)
// Check params:
cLuaState L(tolua_S);
if (
!L.CheckParamUserType(1, "cCompositeChat") ||
!L.CheckParamString(2, 3)
)
{
return 0;
}
cCompositeChat * self = (cCompositeChat *)tolua_tousertype(tolua_S, 1, NULL);
if (self == NULL)
{
tolua_error(tolua_S, "invalid 'self' in function 'cCompositeChat:AddUrlPart'", NULL);
return 0;
}
// Add the part:
AString Text, Url, Style;
L.GetStackValue(2, Text);
L.GetStackValue(3, Url);
L.GetStackValue(4, Style);
self->AddUrlPart(Text, Url, Style);
// Cut away everything from the stack except for the cCompositeChat instance; return that:
lua_settop(L, 1);
return 1;
}
static int tolua_cCompositeChat_ParseText(lua_State * tolua_S)
{
// function cCompositeChat:ParseText(TextMessage)
// Exported manually to support call-chaining (return *this)
// Check params:
cLuaState L(tolua_S);
if (
!L.CheckParamUserType(1, "cCompositeChat") ||
!L.CheckParamString(2)
)
{
return 0;
}
cCompositeChat * self = (cCompositeChat *)tolua_tousertype(tolua_S, 1, NULL);
if (self == NULL)
{
tolua_error(tolua_S, "invalid 'self' in function 'cCompositeChat:ParseText'", NULL);
return 0;
}
// Parse the text:
AString Text;
L.GetStackValue(2, Text);
self->ParseText(Text);
// Cut away everything from the stack except for the cCompositeChat instance; return that:
lua_settop(L, 1);
return 1;
}
static int tolua_cCompositeChat_SetMessageType(lua_State * tolua_S)
{
// function cCompositeChat:SetMessageType(MessageType)
// Exported manually to support call-chaining (return *this)
// Check params:
cLuaState L(tolua_S);
if (
!L.CheckParamUserType(1, "cCompositeChat") ||
!L.CheckParamNumber(2)
)
{
return 0;
}
cCompositeChat * self = (cCompositeChat *)tolua_tousertype(tolua_S, 1, NULL);
if (self == NULL)
{
tolua_error(tolua_S, "invalid 'self' in function 'cCompositeChat:SetMessageType'", NULL);
return 0;
}
// Set the type:
int MessageType;
L.GetStackValue(1, MessageType);
self->SetMessageType((eMessageType)MessageType);
// Cut away everything from the stack except for the cCompositeChat instance; return that:
lua_settop(L, 1);
return 1;
}
static int tolua_cCompositeChat_UnderlineUrls(lua_State * tolua_S)
{
// function cCompositeChat:UnderlineUrls()
// Exported manually to support call-chaining (return *this)
// Check params:
cLuaState L(tolua_S);
if (!L.CheckParamUserType(1, "cCompositeChat"))
{
return 0;
}
cCompositeChat * self = (cCompositeChat *)tolua_tousertype(tolua_S, 1, NULL);
if (self == NULL)
{
tolua_error(tolua_S, "invalid 'self' in function 'cCompositeChat:UnderlineUrls'", NULL);
return 0;
}
// Call the processing
self->UnderlineUrls();
// Cut away everything from the stack except for the cCompositeChat instance; return that:
lua_settop(L, 1);
return 1;
}
void ManualBindings::Bind(lua_State * tolua_S) void ManualBindings::Bind(lua_State * tolua_S)
{ {
tolua_beginmodule(tolua_S, NULL); tolua_beginmodule(tolua_S, NULL);
@ -2527,12 +2839,24 @@ void ManualBindings::Bind(lua_State * tolua_S)
tolua_endmodule(tolua_S); tolua_endmodule(tolua_S);
tolua_beginmodule(tolua_S, "cBlockArea"); tolua_beginmodule(tolua_S, "cBlockArea");
tolua_function(tolua_S, "GetBlockTypeMeta", tolua_cBlockArea_GetBlockTypeMeta); tolua_function(tolua_S, "GetBlockTypeMeta", tolua_cBlockArea_GetBlockTypeMeta);
tolua_function(tolua_S, "GetOrigin", tolua_cBlockArea_GetOrigin); tolua_function(tolua_S, "GetOrigin", tolua_cBlockArea_GetOrigin);
tolua_function(tolua_S, "GetRelBlockTypeMeta", tolua_cBlockArea_GetRelBlockTypeMeta); tolua_function(tolua_S, "GetRelBlockTypeMeta", tolua_cBlockArea_GetRelBlockTypeMeta);
tolua_function(tolua_S, "GetSize", tolua_cBlockArea_GetSize); tolua_function(tolua_S, "GetSize", tolua_cBlockArea_GetSize);
tolua_function(tolua_S, "LoadFromSchematicFile", tolua_cBlockArea_LoadFromSchematicFile); tolua_function(tolua_S, "LoadFromSchematicFile", tolua_cBlockArea_LoadFromSchematicFile);
tolua_function(tolua_S, "SaveToSchematicFile", tolua_cBlockArea_SaveToSchematicFile); tolua_function(tolua_S, "LoadFromSchematicString", tolua_cBlockArea_LoadFromSchematicString);
tolua_function(tolua_S, "SaveToSchematicFile", tolua_cBlockArea_SaveToSchematicFile);
tolua_function(tolua_S, "SaveToSchematicString", tolua_cBlockArea_SaveToSchematicString);
tolua_endmodule(tolua_S);
tolua_beginmodule(tolua_S, "cCompositeChat");
tolua_function(tolua_S, "AddRunCommandPart", tolua_cCompositeChat_AddRunCommandPart);
tolua_function(tolua_S, "AddSuggestCommandPart", tolua_cCompositeChat_AddSuggestCommandPart);
tolua_function(tolua_S, "AddTextPart", tolua_cCompositeChat_AddTextPart);
tolua_function(tolua_S, "AddUrlPart", tolua_cCompositeChat_AddUrlPart);
tolua_function(tolua_S, "ParseText", tolua_cCompositeChat_ParseText);
tolua_function(tolua_S, "SetMessageType", tolua_cCompositeChat_SetMessageType);
tolua_function(tolua_S, "UnderlineUrls", tolua_cCompositeChat_UnderlineUrls);
tolua_endmodule(tolua_S); tolua_endmodule(tolua_S);
tolua_beginmodule(tolua_S, "cHopperEntity"); tolua_beginmodule(tolua_S, "cHopperEntity");
@ -2561,7 +2885,8 @@ void ManualBindings::Bind(lua_State * tolua_S)
tolua_function(tolua_S, "DoWithFurnaceAt", tolua_DoWithXYZ<cWorld, cFurnaceEntity, &cWorld::DoWithFurnaceAt>); tolua_function(tolua_S, "DoWithFurnaceAt", tolua_DoWithXYZ<cWorld, cFurnaceEntity, &cWorld::DoWithFurnaceAt>);
tolua_function(tolua_S, "DoWithNoteBlockAt", tolua_DoWithXYZ<cWorld, cNoteEntity, &cWorld::DoWithNoteBlockAt>); tolua_function(tolua_S, "DoWithNoteBlockAt", tolua_DoWithXYZ<cWorld, cNoteEntity, &cWorld::DoWithNoteBlockAt>);
tolua_function(tolua_S, "DoWithCommandBlockAt", tolua_DoWithXYZ<cWorld, cCommandBlockEntity, &cWorld::DoWithCommandBlockAt>); tolua_function(tolua_S, "DoWithCommandBlockAt", tolua_DoWithXYZ<cWorld, cCommandBlockEntity, &cWorld::DoWithCommandBlockAt>);
tolua_function(tolua_S, "DoWithMobHeadBlockAt", tolua_DoWithXYZ<cWorld, cMobHeadEntity, &cWorld::DoWithMobHeadBlockAt>); tolua_function(tolua_S, "DoWithMobHeadAt", tolua_DoWithXYZ<cWorld, cMobHeadEntity, &cWorld::DoWithMobHeadAt>);
tolua_function(tolua_S, "DoWithFlowerPotAt", tolua_DoWithXYZ<cWorld, cFlowerPotEntity, &cWorld::DoWithFlowerPotAt>);
tolua_function(tolua_S, "DoWithPlayer", tolua_DoWith< cWorld, cPlayer, &cWorld::DoWithPlayer>); tolua_function(tolua_S, "DoWithPlayer", tolua_DoWith< cWorld, cPlayer, &cWorld::DoWithPlayer>);
tolua_function(tolua_S, "FindAndDoWithPlayer", tolua_DoWith< cWorld, cPlayer, &cWorld::FindAndDoWithPlayer>); tolua_function(tolua_S, "FindAndDoWithPlayer", tolua_DoWith< cWorld, cPlayer, &cWorld::FindAndDoWithPlayer>);
tolua_function(tolua_S, "ForEachBlockEntityInChunk", tolua_ForEachInChunk<cWorld, cBlockEntity, &cWorld::ForEachBlockEntityInChunk>); tolua_function(tolua_S, "ForEachBlockEntityInChunk", tolua_ForEachInChunk<cWorld, cBlockEntity, &cWorld::ForEachBlockEntityInChunk>);

View File

@ -10,6 +10,7 @@
#include "DispenserEntity.h" #include "DispenserEntity.h"
#include "DropperEntity.h" #include "DropperEntity.h"
#include "EnderChestEntity.h" #include "EnderChestEntity.h"
#include "FlowerPotEntity.h"
#include "FurnaceEntity.h" #include "FurnaceEntity.h"
#include "HopperEntity.h" #include "HopperEntity.h"
#include "JukeboxEntity.h" #include "JukeboxEntity.h"
@ -30,6 +31,7 @@ cBlockEntity * cBlockEntity::CreateByBlockType(BLOCKTYPE a_BlockType, NIBBLETYPE
case E_BLOCK_DISPENSER: return new cDispenserEntity (a_BlockX, a_BlockY, a_BlockZ, a_World); case E_BLOCK_DISPENSER: return new cDispenserEntity (a_BlockX, a_BlockY, a_BlockZ, a_World);
case E_BLOCK_DROPPER: return new cDropperEntity (a_BlockX, a_BlockY, a_BlockZ, a_World); case E_BLOCK_DROPPER: return new cDropperEntity (a_BlockX, a_BlockY, a_BlockZ, a_World);
case E_BLOCK_ENDER_CHEST: return new cEnderChestEntity (a_BlockX, a_BlockY, a_BlockZ, a_World); case E_BLOCK_ENDER_CHEST: return new cEnderChestEntity (a_BlockX, a_BlockY, a_BlockZ, a_World);
case E_BLOCK_FLOWER_POT: return new cFlowerPotEntity (a_BlockX, a_BlockY, a_BlockZ, a_World);
case E_BLOCK_HEAD: return new cMobHeadEntity (a_BlockX, a_BlockY, a_BlockZ, a_World); case E_BLOCK_HEAD: return new cMobHeadEntity (a_BlockX, a_BlockY, a_BlockZ, a_World);
case E_BLOCK_LIT_FURNACE: return new cFurnaceEntity (a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta, a_World); case E_BLOCK_LIT_FURNACE: return new cFurnaceEntity (a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta, a_World);
case E_BLOCK_FURNACE: return new cFurnaceEntity (a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta, a_World); case E_BLOCK_FURNACE: return new cFurnaceEntity (a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta, a_World);

View File

@ -0,0 +1,134 @@
// FlowerPotEntity.cpp
// Implements the cFlowerPotEntity class representing a single sign in the world
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
#include "json/json.h"
#include "FlowerPotEntity.h"
#include "../Entities/Player.h"
#include "../Item.h"
cFlowerPotEntity::cFlowerPotEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World) :
super(E_BLOCK_FLOWER_POT, a_BlockX, a_BlockY, a_BlockZ, a_World)
{
}
// It don't do anything when 'used'
void cFlowerPotEntity::UsedBy(cPlayer * a_Player)
{
if (IsItemInPot())
{
return;
}
cItem SelectedItem = a_Player->GetInventory().GetEquippedItem();
if (IsFlower(SelectedItem.m_ItemType, SelectedItem.m_ItemDamage))
{
m_Item = SelectedItem.CopyOne();
if (!a_Player->IsGameModeCreative())
{
a_Player->GetInventory().RemoveOneEquippedItem();
}
m_World->BroadcastBlockEntity(m_PosX, m_PosY, m_PosZ, a_Player->GetClientHandle());
}
}
void cFlowerPotEntity::SendTo(cClientHandle & a_Client)
{
a_Client.SendUpdateBlockEntity(*this);
}
void cFlowerPotEntity::Destroy(void)
{
// Drop the contents as pickups:
if (!m_Item.IsEmpty())
{
ASSERT(m_World != NULL);
cItems Pickups;
Pickups.Add(m_Item);
m_World->SpawnItemPickups(Pickups, m_PosX + 0.5, m_PosY + 0.5, m_PosZ + 0.5);
m_Item.Empty();
}
}
bool cFlowerPotEntity::LoadFromJson(const Json::Value & a_Value)
{
m_PosX = a_Value.get("x", 0).asInt();
m_PosY = a_Value.get("y", 0).asInt();
m_PosZ = a_Value.get("z", 0).asInt();
m_Item = cItem();
m_Item.FromJson(a_Value.get("Item", 0));
return true;
}
void cFlowerPotEntity::SaveToJson(Json::Value & a_Value)
{
a_Value["x"] = m_PosX;
a_Value["y"] = m_PosY;
a_Value["z"] = m_PosZ;
Json::Value Item;
m_Item.GetJson(Item);
a_Value["Item"] = Item;
}
bool cFlowerPotEntity::IsFlower(short m_ItemType, short m_ItemData)
{
switch (m_ItemType)
{
case E_BLOCK_DANDELION:
case E_BLOCK_FLOWER:
case E_BLOCK_CACTUS:
case E_BLOCK_BROWN_MUSHROOM:
case E_BLOCK_RED_MUSHROOM:
case E_BLOCK_SAPLING:
case E_BLOCK_DEAD_BUSH:
{
return true;
}
case E_BLOCK_TALL_GRASS:
{
return (m_ItemData == (short) 2);
}
default:
{
return false;
}
}
}

View File

@ -0,0 +1,74 @@
// FlowerPotEntity.h
// Declares the cFlowerPotEntity class representing a single sign in the world
#pragma once
#include "BlockEntity.h"
class cItem;
namespace Json
{
class Value;
}
// tolua_begin
class cFlowerPotEntity :
public cBlockEntity
{
typedef cBlockEntity super;
public:
// tolua_end
/** Creates a new flowerpot entity at the specified block coords. a_World may be NULL */
cFlowerPotEntity(int a_BlocX, int a_BlockY, int a_BlockZ, cWorld * a_World);
bool LoadFromJson( const Json::Value& a_Value );
virtual void SaveToJson(Json::Value& a_Value ) override;
virtual void Destroy(void) override;
// tolua_begin
/** Is a flower in the pot? */
bool IsItemInPot(void) { return !m_Item.IsEmpty(); }
/** Get the item in the flower pot */
cItem GetItem(void) const { return m_Item; }
/** Set the item in the flower pot */
void SetItem(const cItem a_Item) { m_Item = a_Item; }
// tolua_end
virtual void UsedBy(cPlayer * a_Player) override;
virtual void SendTo(cClientHandle & a_Client) override;
static bool IsFlower(short m_ItemType, short m_ItemData);
static const char * GetClassStatic(void) { return "cFlowerPotEntity"; }
private:
cItem m_Item;
} ; // tolua_export

View File

@ -2,6 +2,7 @@
#include "Globals.h" #include "Globals.h"
#include "BlockInfo.h" #include "BlockInfo.h"
#include "Blocks/BlockHandler.h"
@ -23,16 +24,24 @@ cBlockInfo::cBlockInfo()
, m_RequiresSpecialTool(false) , m_RequiresSpecialTool(false)
, m_IsSolid(true) , m_IsSolid(true)
, m_FullyOccupiesVoxel(false) , m_FullyOccupiesVoxel(false)
, m_Handler(NULL)
{} {}
cBlockInfo::~cBlockInfo()
{
delete m_Handler;
}
cBlockInfo & cBlockInfo::Get(BLOCKTYPE a_Type) cBlockInfo & cBlockInfo::Get(BLOCKTYPE a_Type)
{ {
ASSERT(a_Type < 256);
return ms_Info[a_Type]; return ms_Info[a_Type];
} }
@ -42,6 +51,14 @@ cBlockInfo & cBlockInfo::Get(BLOCKTYPE a_Type)
void cBlockInfo::Initialize(void) void cBlockInfo::Initialize(void)
{ {
for (unsigned int i = 0; i < 256; ++i)
{
if (ms_Info[i].m_Handler == NULL)
{
ms_Info[i].m_Handler = cBlockHandler::CreateBlockHandler((BLOCKTYPE) i);
}
}
// Emissive blocks // Emissive blocks
ms_Info[E_BLOCK_FIRE ].m_LightValue = 15; ms_Info[E_BLOCK_FIRE ].m_LightValue = 15;
ms_Info[E_BLOCK_GLOWSTONE ].m_LightValue = 15; ms_Info[E_BLOCK_GLOWSTONE ].m_LightValue = 15;
@ -252,6 +269,11 @@ void cBlockInfo::Initialize(void)
ms_Info[E_BLOCK_VINES ].m_IsSnowable = false; ms_Info[E_BLOCK_VINES ].m_IsSnowable = false;
ms_Info[E_BLOCK_WALLSIGN ].m_IsSnowable = false; ms_Info[E_BLOCK_WALLSIGN ].m_IsSnowable = false;
ms_Info[E_BLOCK_WATER ].m_IsSnowable = false; ms_Info[E_BLOCK_WATER ].m_IsSnowable = false;
ms_Info[E_BLOCK_RAIL ].m_IsSnowable = false;
ms_Info[E_BLOCK_ACTIVATOR_RAIL ].m_IsSnowable = false;
ms_Info[E_BLOCK_POWERED_RAIL ].m_IsSnowable = false;
ms_Info[E_BLOCK_DETECTOR_RAIL ].m_IsSnowable = false;
ms_Info[E_BLOCK_COBWEB ].m_IsSnowable = false;
// Blocks that don't drop without a special tool: // Blocks that don't drop without a special tool:
@ -290,6 +312,10 @@ void cBlockInfo::Initialize(void)
ms_Info[E_BLOCK_STONE_PRESSURE_PLATE].m_RequiresSpecialTool = true; ms_Info[E_BLOCK_STONE_PRESSURE_PLATE].m_RequiresSpecialTool = true;
ms_Info[E_BLOCK_STONE_SLAB ].m_RequiresSpecialTool = true; ms_Info[E_BLOCK_STONE_SLAB ].m_RequiresSpecialTool = true;
ms_Info[E_BLOCK_VINES ].m_RequiresSpecialTool = true; ms_Info[E_BLOCK_VINES ].m_RequiresSpecialTool = true;
ms_Info[E_BLOCK_FURNACE ].m_RequiresSpecialTool = true;
ms_Info[E_BLOCK_LIT_FURNACE ].m_RequiresSpecialTool = true;
ms_Info[E_BLOCK_ANVIL ].m_RequiresSpecialTool = true;
ms_Info[E_BLOCK_ENCHANTMENT_TABLE ].m_RequiresSpecialTool = true;
// Nonsolid blocks: // Nonsolid blocks:

View File

@ -5,6 +5,13 @@
// fwd:
class cBlockHandler;
// tolua_begin // tolua_begin
class cBlockInfo class cBlockInfo
{ {
@ -13,6 +20,8 @@ public:
cBlockInfo(); cBlockInfo();
~cBlockInfo();
/** (Re-)Initializes the internal BlockInfo structures. */ /** (Re-)Initializes the internal BlockInfo structures. */
static void Initialize(void); static void Initialize(void);
@ -49,6 +58,12 @@ public:
/** Does this block fully occupy its voxel - is it a 'full' block? */ /** Does this block fully occupy its voxel - is it a 'full' block? */
bool m_FullyOccupiesVoxel; bool m_FullyOccupiesVoxel;
// tolua_end
/** Associated block handler. */
cBlockHandler * m_Handler;
// tolua_begin
inline static NIBBLETYPE GetLightValue (BLOCKTYPE a_Type) { return Get(a_Type).m_LightValue; } inline static NIBBLETYPE GetLightValue (BLOCKTYPE a_Type) { return Get(a_Type).m_LightValue; }
inline static NIBBLETYPE GetSpreadLightFalloff(BLOCKTYPE a_Type) { return Get(a_Type).m_SpreadLightFalloff; } inline static NIBBLETYPE GetSpreadLightFalloff(BLOCKTYPE a_Type) { return Get(a_Type).m_SpreadLightFalloff; }
@ -62,6 +77,8 @@ public:
// tolua_end // tolua_end
inline static cBlockHandler * GetHandler (BLOCKTYPE a_Type) { return Get(a_Type).m_Handler; }
protected: protected:
@ -74,3 +91,13 @@ protected:
// Shortcut to get the blockhandler for a specific block
inline cBlockHandler * BlockHandler(BLOCKTYPE a_BlockType)
{
return cBlockInfo::Get(a_BlockType).m_Handler;
}

View File

@ -4,6 +4,7 @@
#include "BlockHandler.h" #include "BlockHandler.h"
#include "ChunkInterface.h" #include "ChunkInterface.h"
#include "WorldInterface.h" #include "WorldInterface.h"
#include "MetaRotater.h"
#include "../Entities/Player.h" #include "../Entities/Player.h"
@ -11,11 +12,11 @@
class cBlockBedHandler : class cBlockBedHandler :
public cBlockHandler public cMetaRotater<cBlockHandler, 0x3, 0x02, 0x03, 0x00, 0x01, true>
{ {
public: public:
cBlockBedHandler(BLOCKTYPE a_BlockType) cBlockBedHandler(BLOCKTYPE a_BlockType)
: cBlockHandler(a_BlockType) : cMetaRotater<cBlockHandler, 0x3, 0x02, 0x03, 0x00, 0x01,true>(a_BlockType)
{ {
} }

View File

@ -2,16 +2,17 @@
#include "BlockHandler.h" #include "BlockHandler.h"
#include "Chunk.h" #include "Chunk.h"
#include "MetaRotater.h"
class cBlockButtonHandler : class cBlockButtonHandler :
public cBlockHandler public cMetaRotater<cBlockHandler, 0x07, 0x04, 0x01, 0x03, 0x02, true>
{ {
public: public:
cBlockButtonHandler(BLOCKTYPE a_BlockType) cBlockButtonHandler(BLOCKTYPE a_BlockType)
: cBlockHandler(a_BlockType) : cMetaRotater<cBlockHandler, 0x07, 0x04, 0x01, 0x03, 0x02, true>(a_BlockType)
{ {
} }

View File

@ -4,17 +4,18 @@
#include "BlockEntity.h" #include "BlockEntity.h"
#include "../BlockArea.h" #include "../BlockArea.h"
#include "../Entities/Player.h" #include "../Entities/Player.h"
#include "MetaRotater.h"
class cBlockChestHandler : class cBlockChestHandler :
public cBlockEntityHandler public cMetaRotater<cBlockEntityHandler, 0x07, 0x04, 0x01, 0x03, 0x02, true>
{ {
public: public:
cBlockChestHandler(BLOCKTYPE a_BlockType) cBlockChestHandler(BLOCKTYPE a_BlockType)
: cBlockEntityHandler(a_BlockType) : cMetaRotater<cBlockEntityHandler, 0x07, 0x04, 0x01, 0x03, 0x02, true>(a_BlockType)
{ {
} }

View File

@ -3,17 +3,18 @@
#include "BlockHandler.h" #include "BlockHandler.h"
#include "BlockRedstoneRepeater.h" #include "BlockRedstoneRepeater.h"
#include "MetaRotater.h"
class cBlockComparatorHandler : class cBlockComparatorHandler :
public cBlockHandler public cMetaRotater<cBlockHandler, 0x03, 0x00, 0x01, 0x02, 0x03, true>
{ {
public: public:
cBlockComparatorHandler(BLOCKTYPE a_BlockType) cBlockComparatorHandler(BLOCKTYPE a_BlockType)
: cBlockHandler(a_BlockType) : cMetaRotater<cBlockHandler, 0x03, 0x00, 0x01, 0x02, 0x03, true>(a_BlockType)
{ {
} }
@ -26,6 +27,13 @@ public:
} }
virtual void OnCancelRightClick(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace) override
{
UNUSED(a_ChunkInterface);
a_WorldInterface.SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, a_Player);
}
virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
{ {
// Reset meta to 0 // Reset meta to 0

View File

@ -9,7 +9,7 @@
cBlockDoorHandler::cBlockDoorHandler(BLOCKTYPE a_BlockType) cBlockDoorHandler::cBlockDoorHandler(BLOCKTYPE a_BlockType)
: cBlockHandler(a_BlockType) : super(a_BlockType)
{ {
} }
@ -55,6 +55,29 @@ void cBlockDoorHandler::OnUse(cChunkInterface & a_ChunkInterface, cWorldInterfac
void cBlockDoorHandler::OnCancelRightClick(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace)
{
UNUSED(a_ChunkInterface);
a_WorldInterface.SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, a_Player);
NIBBLETYPE Meta = a_ChunkInterface.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ);
if (Meta & 8)
{
// Current block is top of the door
a_WorldInterface.SendBlockTo(a_BlockX, a_BlockY - 1, a_BlockZ, a_Player);
}
else
{
// Current block is bottom of the door
a_WorldInterface.SendBlockTo(a_BlockX, a_BlockY + 1, a_BlockZ, a_Player);
}
}
void cBlockDoorHandler::OnPlacedByPlayer( void cBlockDoorHandler::OnPlacedByPlayer(
cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player, cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace,

View File

@ -4,18 +4,21 @@
#include "BlockHandler.h" #include "BlockHandler.h"
#include "../Entities/Player.h" #include "../Entities/Player.h"
#include "Chunk.h" #include "Chunk.h"
#include "MetaRotater.h"
class cBlockDoorHandler : class cBlockDoorHandler :
public cBlockHandler public cMetaRotater<cBlockHandler, 0x03, 0x01, 0x02, 0x03, 0x00, true>
{ {
typedef cMetaRotater<cBlockHandler, 0x03, 0x01, 0x02, 0x03, 0x00, true> super;
public: public:
cBlockDoorHandler(BLOCKTYPE a_BlockType); cBlockDoorHandler(BLOCKTYPE a_BlockType);
virtual void OnDestroyed(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, int a_BlockX, int a_BlockY, int a_BlockZ) override; 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 void OnUse(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override;
virtual void OnCancelRightClick(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace) override;
virtual const char * GetStepSound(void) override; virtual const char * GetStepSound(void) override;
@ -167,6 +170,60 @@ public:
} }
virtual NIBBLETYPE MetaRotateCCW(NIBBLETYPE a_Meta) override
{
if (a_Meta & 0x08)
{
return a_Meta;
}
else
{
return super::MetaRotateCCW(a_Meta);
}
}
virtual NIBBLETYPE MetaRotateCW(NIBBLETYPE a_Meta) override
{
if (a_Meta & 0x08)
{
return a_Meta;
}
else
{
return super::MetaRotateCW(a_Meta);
}
}
virtual NIBBLETYPE MetaMirrorXY(NIBBLETYPE a_Meta) override
{
if (a_Meta & 0x08)
{
return a_Meta;
}
else
{
return super::MetaMirrorXY(a_Meta);
}
}
virtual NIBBLETYPE MetaMirrorYZ(NIBBLETYPE a_Meta) override
{
if (a_Meta & 0x08)
{
return a_Meta;
}
else
{
return super::MetaMirrorYZ(a_Meta);
}
}
} ; } ;

View File

@ -6,17 +6,18 @@
#pragma once #pragma once
#include "../Piston.h" #include "../Piston.h"
#include "MetaRotater.h"
class cBlockDropSpenserHandler : class cBlockDropSpenserHandler :
public cBlockEntityHandler public cMetaRotater<cBlockEntityHandler, 0x07, 0x02, 0x05, 0x03, 0x04>
{ {
public: public:
cBlockDropSpenserHandler(BLOCKTYPE a_BlockType) : cBlockDropSpenserHandler(BLOCKTYPE a_BlockType) :
cBlockEntityHandler(a_BlockType) cMetaRotater<cBlockEntityHandler, 0x07, 0x02, 0x05, 0x03, 0x04>(a_BlockType)
{ {
} }
@ -34,6 +35,20 @@ public:
a_BlockMeta = cPiston::RotationPitchToMetaData(a_Player->GetYaw(), a_Player->GetPitch()); a_BlockMeta = cPiston::RotationPitchToMetaData(a_Player->GetYaw(), a_Player->GetPitch());
return true; return true;
} }
virtual NIBBLETYPE MetaMirrorXZ(NIBBLETYPE a_Meta) override
{
// Bit 0x08 is a flag. Lowest three bits are position. 0x08 == 1000
NIBBLETYPE OtherMeta = a_Meta & 0x08;
// Mirrors defined by by a table. (Source, mincraft.gamepedia.com) 0x07 == 0111
switch (a_Meta & 0x07)
{
case 0x00: return 0x01 + OtherMeta; // Down -> Up
case 0x01: return 0x00 + OtherMeta; // Up -> Down
}
// Not Facing Up or Down; No change.
return a_Meta;
}
} ; } ;

View File

@ -2,17 +2,17 @@
#pragma once #pragma once
#include "BlockEntity.h" #include "BlockEntity.h"
#include "MetaRotater.h"
class cBlockEnderchestHandler : class cBlockEnderchestHandler :
public cBlockEntityHandler public cMetaRotater<cBlockEntityHandler, 0x07, 0x02, 0x05, 0x03, 0x04>
{ {
public: public:
cBlockEnderchestHandler(BLOCKTYPE a_BlockType) cBlockEnderchestHandler(BLOCKTYPE a_BlockType)
: cBlockEntityHandler(a_BlockType) : cMetaRotater<cBlockEntityHandler, 0x07, 0x02, 0x05, 0x03, 0x04>(a_BlockType)
{ {
} }

View File

@ -2,17 +2,17 @@
#pragma once #pragma once
#include "BlockHandler.h" #include "BlockHandler.h"
#include "MetaRotater.h"
class cBlockFenceGateHandler : class cBlockFenceGateHandler :
public cBlockHandler public cMetaRotater<cBlockHandler, 0x03, 0x02, 0x03, 0x00, 0x01, true>
{ {
public: public:
cBlockFenceGateHandler(BLOCKTYPE a_BlockType) : cBlockFenceGateHandler(BLOCKTYPE a_BlockType) :
cBlockHandler(a_BlockType) cMetaRotater<cBlockHandler, 0x03, 0x02, 0x03, 0x00, 0x01, true>(a_BlockType)
{ {
} }
@ -48,6 +48,12 @@ public:
} }
virtual void OnCancelRightClick(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace) override
{
a_WorldInterface.SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, a_Player);
}
virtual bool IsUseable(void) override virtual bool IsUseable(void) override
{ {
return true; return true;

View File

@ -2,101 +2,24 @@
#pragma once #pragma once
#include "BlockHandler.h" #include "BlockHandler.h"
#include "BlockEntity.h"
class cBlockFlowerPotHandler : class cBlockFlowerPotHandler :
public cBlockHandler public cBlockEntityHandler
{ {
public: public:
cBlockFlowerPotHandler(BLOCKTYPE a_BlockType) : cBlockFlowerPotHandler(BLOCKTYPE a_BlockType) :
cBlockHandler(a_BlockType) cBlockEntityHandler(a_BlockType)
{ {
} }
virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
{ {
a_Pickups.push_back(cItem(E_ITEM_FLOWER_POT, 1, 0)); a_Pickups.push_back(cItem(E_ITEM_FLOWER_POT, 1, 0));
if (a_BlockMeta == 0)
{
return;
}
cItem Plant;
switch (a_BlockMeta)
{
case 1: Plant = cItem(E_BLOCK_RED_ROSE, 1, 0); break;
case 2: Plant = cItem(E_BLOCK_YELLOW_FLOWER, 1, 0); break;
case 3: Plant = cItem(E_BLOCK_SAPLING, 1, E_META_SAPLING_APPLE); break;
case 4: Plant = cItem(E_BLOCK_SAPLING, 1, E_META_SAPLING_CONIFER); break;
case 5: Plant = cItem(E_BLOCK_SAPLING, 1, E_META_SAPLING_BIRCH); break;
case 6: Plant = cItem(E_BLOCK_SAPLING, 1, E_META_SAPLING_JUNGLE); break;
case 7: Plant = cItem(E_BLOCK_RED_MUSHROOM, 1, 0); break;
case 8: Plant = cItem(E_BLOCK_BROWN_MUSHROOM, 1, 0); break;
case 9: Plant = cItem(E_BLOCK_CACTUS, 1, 0); break;
case 10: Plant = cItem(E_BLOCK_DEAD_BUSH, 1, 0); break;
case 11: Plant = cItem(E_BLOCK_TALL_GRASS, 1, E_META_TALL_GRASS_FERN); break;
default: return;
}
a_Pickups.push_back(Plant);
}
void OnUse(cWorld * a_World, cWorldInterface * a_WorldInterface, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ)
{
NIBBLETYPE Meta = a_World->GetBlockMeta( a_BlockX, a_BlockY, a_BlockZ );
if (Meta != 0)
{
// Already filled
return;
}
switch (a_Player->GetEquippedItem().m_ItemType)
{
case E_BLOCK_RED_ROSE: Meta = 1; break;
case E_BLOCK_YELLOW_FLOWER: Meta = 2; break;
case E_BLOCK_SAPLING:
{
switch (a_Player->GetEquippedItem().m_ItemDamage)
{
case E_META_SAPLING_APPLE: Meta = 3; break;
case E_META_SAPLING_CONIFER: Meta = 4; break;
case E_META_SAPLING_BIRCH: Meta = 5; break;
case E_META_SAPLING_JUNGLE: Meta = 6; break;
}
break;
}
case E_BLOCK_RED_MUSHROOM: Meta = 7; break;
case E_BLOCK_BROWN_MUSHROOM: Meta = 8; break;
case E_BLOCK_CACTUS: Meta = 9; break;
case E_BLOCK_DEAD_BUSH: Meta = 10; break;
case E_BLOCK_TALL_GRASS:
{
if (a_Player->GetEquippedItem().m_ItemDamage == E_META_TALL_GRASS_FERN)
{
Meta = 11;
}
else
{
return;
}
break;
}
}
if (a_Player->GetGameMode() != gmCreative)
{
a_Player->GetInventory().RemoveOneEquippedItem();
}
a_World->SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, Meta);
}
virtual bool IsUseable(void) override
{
return true;
} }
} ; } ;

View File

@ -57,6 +57,7 @@
#include "BlockRedstoneLamp.h" #include "BlockRedstoneLamp.h"
#include "BlockRedstoneRepeater.h" #include "BlockRedstoneRepeater.h"
#include "BlockRedstoneTorch.h" #include "BlockRedstoneTorch.h"
#include "BlockTNT.h"
#include "BlockSand.h" #include "BlockSand.h"
#include "BlockSapling.h" #include "BlockSapling.h"
#include "BlockSideways.h" #include "BlockSideways.h"
@ -77,33 +78,6 @@
bool cBlockHandler::m_HandlerInitialized = false;
cBlockHandler * cBlockHandler::m_BlockHandler[256];
cBlockHandler * cBlockHandler::GetBlockHandler(BLOCKTYPE a_BlockType)
{
if (!m_HandlerInitialized)
{
// We have to initialize
memset(m_BlockHandler, 0, sizeof(m_BlockHandler));
m_HandlerInitialized = true;
}
if (m_BlockHandler[a_BlockType] != NULL)
{
return m_BlockHandler[a_BlockType];
}
return m_BlockHandler[a_BlockType] = CreateBlockHandler(a_BlockType);
}
cBlockHandler * cBlockHandler::CreateBlockHandler(BLOCKTYPE a_BlockType) cBlockHandler * cBlockHandler::CreateBlockHandler(BLOCKTYPE a_BlockType)
{ {
switch(a_BlockType) switch(a_BlockType)
@ -172,6 +146,7 @@ cBlockHandler * cBlockHandler::CreateBlockHandler(BLOCKTYPE a_BlockType)
case E_BLOCK_NETHER_BRICK_STAIRS: return new cBlockStairsHandler (a_BlockType); case E_BLOCK_NETHER_BRICK_STAIRS: return new cBlockStairsHandler (a_BlockType);
case E_BLOCK_NETHER_PORTAL: return new cBlockPortalHandler (a_BlockType); case E_BLOCK_NETHER_PORTAL: return new cBlockPortalHandler (a_BlockType);
case E_BLOCK_NETHER_WART: return new cBlockNetherWartHandler (a_BlockType); case E_BLOCK_NETHER_WART: return new cBlockNetherWartHandler (a_BlockType);
case E_BLOCK_NETHER_QUARTZ_ORE: return new cBlockOreHandler (a_BlockType);
case E_BLOCK_NEW_LEAVES: return new cBlockNewLeavesHandler (a_BlockType); case E_BLOCK_NEW_LEAVES: return new cBlockNewLeavesHandler (a_BlockType);
case E_BLOCK_NEW_LOG: return new cBlockSidewaysHandler (a_BlockType); case E_BLOCK_NEW_LOG: return new cBlockSidewaysHandler (a_BlockType);
case E_BLOCK_NOTE_BLOCK: return new cBlockNoteHandler (a_BlockType); case E_BLOCK_NOTE_BLOCK: return new cBlockNoteHandler (a_BlockType);
@ -192,7 +167,7 @@ cBlockHandler * cBlockHandler::CreateBlockHandler(BLOCKTYPE a_BlockType)
case E_BLOCK_REDSTONE_REPEATER_ON: return new cBlockRedstoneRepeaterHandler(a_BlockType); case E_BLOCK_REDSTONE_REPEATER_ON: return new cBlockRedstoneRepeaterHandler(a_BlockType);
case E_BLOCK_REDSTONE_TORCH_OFF: return new cBlockRedstoneTorchHandler (a_BlockType); case E_BLOCK_REDSTONE_TORCH_OFF: return new cBlockRedstoneTorchHandler (a_BlockType);
case E_BLOCK_REDSTONE_TORCH_ON: return new cBlockRedstoneTorchHandler (a_BlockType); case E_BLOCK_REDSTONE_TORCH_ON: return new cBlockRedstoneTorchHandler (a_BlockType);
case E_BLOCK_REDSTONE_WIRE: return new cBlockRedstoneHandler (a_BlockType); case E_BLOCK_REDSTONE_WIRE: return new cBlockRedstoneHandler (a_BlockType);
case E_BLOCK_RED_MUSHROOM: return new cBlockMushroomHandler (a_BlockType); case E_BLOCK_RED_MUSHROOM: return new cBlockMushroomHandler (a_BlockType);
case E_BLOCK_RED_ROSE: return new cBlockFlowerHandler (a_BlockType); case E_BLOCK_RED_ROSE: return new cBlockFlowerHandler (a_BlockType);
case E_BLOCK_SAND: return new cBlockSandHandler (a_BlockType); case E_BLOCK_SAND: return new cBlockSandHandler (a_BlockType);
@ -212,6 +187,7 @@ cBlockHandler * cBlockHandler::CreateBlockHandler(BLOCKTYPE a_BlockType)
case E_BLOCK_TALL_GRASS: return new cBlockTallGrassHandler (a_BlockType); case E_BLOCK_TALL_GRASS: return new cBlockTallGrassHandler (a_BlockType);
case E_BLOCK_TORCH: return new cBlockTorchHandler (a_BlockType); case E_BLOCK_TORCH: return new cBlockTorchHandler (a_BlockType);
case E_BLOCK_TRAPDOOR: return new cBlockTrapdoorHandler (a_BlockType); case E_BLOCK_TRAPDOOR: return new cBlockTrapdoorHandler (a_BlockType);
case E_BLOCK_TNT: return new cBlockTNTHandler (a_BlockType);
case E_BLOCK_VINES: return new cBlockVineHandler (a_BlockType); case E_BLOCK_VINES: return new cBlockVineHandler (a_BlockType);
case E_BLOCK_WALLSIGN: return new cBlockSignHandler (a_BlockType); case E_BLOCK_WALLSIGN: return new cBlockSignHandler (a_BlockType);
case E_BLOCK_WATER: return new cBlockFluidHandler (a_BlockType); case E_BLOCK_WATER: return new cBlockFluidHandler (a_BlockType);
@ -231,20 +207,6 @@ cBlockHandler * cBlockHandler::CreateBlockHandler(BLOCKTYPE a_BlockType)
void cBlockHandler::Deinit()
{
for (int i = 0; i < 256; i++)
{
delete m_BlockHandler[i];
}
memset(m_BlockHandler, 0, sizeof(m_BlockHandler)); // Don't leave any dangling pointers around, just in case
m_HandlerInitialized = false;
}
cBlockHandler::cBlockHandler(BLOCKTYPE a_BlockType) cBlockHandler::cBlockHandler(BLOCKTYPE a_BlockType)
{ {
m_BlockType = a_BlockType; m_BlockType = a_BlockType;
@ -329,7 +291,7 @@ void cBlockHandler::NeighborChanged(cChunkInterface & a_ChunkInterface, int a_Bl
{ {
if ((a_BlockY >= 0) && (a_BlockY < cChunkDef::Height)) if ((a_BlockY >= 0) && (a_BlockY < cChunkDef::Height))
{ {
GetBlockHandler(a_ChunkInterface.GetBlock(a_BlockX, a_BlockY, a_BlockZ))->OnNeighborChanged(a_ChunkInterface, a_BlockX, a_BlockY, a_BlockZ); cBlockInfo::GetHandler(a_ChunkInterface.GetBlock(a_BlockX, a_BlockY, a_BlockZ))->OnNeighborChanged(a_ChunkInterface, a_BlockX, a_BlockY, a_BlockZ);
} }
} }
@ -361,6 +323,14 @@ void cBlockHandler::OnUse(cChunkInterface & a_ChunkInterface, cWorldInterface &
void cBlockHandler::OnCancelRightClick(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer *a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace)
{
}
void cBlockHandler::ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) void cBlockHandler::ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta)
{ {
// Setting the meta to a_BlockMeta keeps most textures. The few other blocks have to override this. // Setting the meta to a_BlockMeta keeps most textures. The few other blocks have to override this.

View File

@ -23,6 +23,8 @@ class cBlockHandler
{ {
public: public:
cBlockHandler(BLOCKTYPE a_BlockType); cBlockHandler(BLOCKTYPE a_BlockType);
virtual ~cBlockHandler() {}
/// Called when the block gets ticked either by a random tick or by a queued tick. /// Called when the block gets ticked either by a random tick or by a queued tick.
/// Note that the coords are chunk-relative! /// Note that the coords are chunk-relative!
@ -69,6 +71,9 @@ public:
/// Called if the user right clicks the block and the block is useable /// Called if the user right clicks the block and the block is useable
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); 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);
/** Called when a Right Click to this Block is cancelled */
virtual void OnCancelRightClick(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace);
/// <summary>Called when the item is mined to convert it into pickups. Pickups may specify multiple items. Appends items to a_Pickups, preserves its original contents</summary> /// <summary>Called when the item is mined to convert it into pickups. Pickups may specify multiple items. Appends items to a_Pickups, preserves its original contents</summary>
virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta); virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta);
@ -136,31 +141,15 @@ public:
/// <returns>Block meta following mirroring</returns> /// <returns>Block meta following mirroring</returns>
virtual NIBBLETYPE MetaMirrorYZ(NIBBLETYPE a_Meta) { return a_Meta; } virtual NIBBLETYPE MetaMirrorYZ(NIBBLETYPE a_Meta) { return a_Meta; }
/// <summary>Get the blockhandler for a specific block id</summary>
static cBlockHandler * GetBlockHandler(BLOCKTYPE a_BlockType);
/// <summary>Deletes all initialised block handlers</summary>
static void Deinit();
protected: protected:
BLOCKTYPE m_BlockType; BLOCKTYPE m_BlockType;
// Creates a new blockhandler for the given block type. For internal use only, use ::GetBlockHandler() instead. // Creates a new blockhandler for the given block type. For internal use only, use ::GetBlockHandler() instead.
static cBlockHandler *CreateBlockHandler(BLOCKTYPE a_BlockType); static cBlockHandler * CreateBlockHandler(BLOCKTYPE a_BlockType);
static cBlockHandler *m_BlockHandler[256];
static bool m_HandlerInitialized; //used to detect if the blockhandlers are initialized friend class cBlockInfo;
}; };
// Shortcut to get the blockhandler for a specific block
inline cBlockHandler * BlockHandler(BLOCKTYPE a_BlockType)
{
return cBlockHandler::GetBlockHandler(a_BlockType);
}

View File

@ -29,7 +29,7 @@ public:
BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta
) override ) override
{ {
class cCallback : public cMobHeadBlockCallback class cCallback : public cMobHeadCallback
{ {
cPlayer * m_Player; cPlayer * m_Player;
NIBBLETYPE m_OldBlockMeta; NIBBLETYPE m_OldBlockMeta;
@ -45,6 +45,7 @@ public:
a_MobHeadEntity->SetType(static_cast<eMobHeadType>(m_OldBlockMeta)); a_MobHeadEntity->SetType(static_cast<eMobHeadType>(m_OldBlockMeta));
a_MobHeadEntity->SetRotation(static_cast<eMobHeadRotation>(Rotation)); a_MobHeadEntity->SetRotation(static_cast<eMobHeadRotation>(Rotation));
a_MobHeadEntity->GetWorld()->BroadcastBlockEntity(a_MobHeadEntity->GetPosX(), a_MobHeadEntity->GetPosY(), a_MobHeadEntity->GetPosZ(), m_Player->GetClientHandle());
return false; return false;
} }
@ -59,7 +60,7 @@ public:
a_BlockMeta = a_BlockFace; a_BlockMeta = a_BlockFace;
cWorld * World = (cWorld *) &a_WorldInterface; cWorld * World = (cWorld *) &a_WorldInterface;
World->DoWithMobHeadBlockAt(a_BlockX, a_BlockY, a_BlockZ, Callback); World->DoWithMobHeadAt(a_BlockX, a_BlockY, a_BlockZ, Callback);
a_ChunkInterface.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, a_BlockMeta); a_ChunkInterface.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, a_BlockMeta);
} }
} ; } ;

View File

@ -29,7 +29,7 @@ public:
a_BlockMeta = RepeaterRotationToMetaData(a_Player->GetYaw()); a_BlockMeta = RepeaterRotationToMetaData(a_Player->GetYaw());
return true; return true;
} }
virtual void OnUse(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override virtual void 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
{ {
@ -37,6 +37,13 @@ public:
} }
virtual void OnCancelRightClick(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace) override
{
UNUSED(a_ChunkInterface);
a_WorldInterface.SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, a_Player);
}
virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
{ {
// Reset meta to 0 // Reset meta to 0

View File

@ -2,17 +2,17 @@
#pragma once #pragma once
#include "BlockHandler.h" #include "BlockHandler.h"
#include "MetaRotater.h"
class cBlockStairsHandler : class cBlockStairsHandler :
public cBlockHandler public cMetaRotater<cBlockHandler, 0x03, 0x03, 0x00, 0x02, 0x01, true>
{ {
public: public:
cBlockStairsHandler(BLOCKTYPE a_BlockType) : cBlockStairsHandler(BLOCKTYPE a_BlockType) :
cBlockHandler(a_BlockType) cMetaRotater<cBlockHandler, 0x03, 0x03, 0x00, 0x02, 0x01, true>(a_BlockType)
{ {
} }
@ -25,6 +25,14 @@ public:
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
) override ) override
{ {
UNUSED(a_ChunkInterface);
UNUSED(a_BlockX);
UNUSED(a_BlockY);
UNUSED(a_BlockZ);
UNUSED(a_CursorX);
UNUSED(a_CursorY);
UNUSED(a_CursorZ);
UNUSED(a_BlockMeta);
a_BlockType = m_BlockType; a_BlockType = m_BlockType;
a_BlockMeta = RotationToMetaData(a_Player->GetYaw()); a_BlockMeta = RotationToMetaData(a_Player->GetYaw());
switch (a_BlockFace) switch (a_BlockFace)
@ -96,54 +104,6 @@ public:
} }
virtual NIBBLETYPE MetaRotateCCW(NIBBLETYPE a_Meta) override
{
// Bits 3 and 4 stay, the rest is swapped around according to a table:
NIBBLETYPE TopBits = (a_Meta & 0x0c);
switch (a_Meta & 0x03)
{
case 0x00: return TopBits | 0x03; // East -> North
case 0x01: return TopBits | 0x02; // West -> South
case 0x02: return TopBits | 0x00; // South -> East
case 0x03: return TopBits | 0x01; // North -> West
}
// Not reachable, but to avoid a compiler warning:
return 0;
}
virtual NIBBLETYPE MetaRotateCW(NIBBLETYPE a_Meta) override
{
// Bits 3 and 4 stay, the rest is swapped around according to a table:
NIBBLETYPE TopBits = (a_Meta & 0x0c);
switch (a_Meta & 0x03)
{
case 0x00: return TopBits | 0x02; // East -> South
case 0x01: return TopBits | 0x03; // West -> North
case 0x02: return TopBits | 0x01; // South -> West
case 0x03: return TopBits | 0x00; // North -> East
}
// Not reachable, but to avoid a compiler warning:
return 0;
}
virtual NIBBLETYPE MetaMirrorXY(NIBBLETYPE a_Meta) override
{
// Bits 3 and 4 stay, the rest is swapped around according to a table:
NIBBLETYPE TopBits = (a_Meta & 0x0c);
switch (a_Meta & 0x03)
{
case 0x00: return TopBits | 0x00; // East -> East
case 0x01: return TopBits | 0x01; // West -> West
case 0x02: return TopBits | 0x03; // South -> North
case 0x03: return TopBits | 0x02; // North -> South
}
// Not reachable, but to avoid a compiler warning:
return 0;
}
virtual NIBBLETYPE MetaMirrorXZ(NIBBLETYPE a_Meta) override virtual NIBBLETYPE MetaMirrorXZ(NIBBLETYPE a_Meta) override
{ {
// Toggle bit 3: // Toggle bit 3:
@ -151,20 +111,6 @@ public:
} }
virtual NIBBLETYPE MetaMirrorYZ(NIBBLETYPE a_Meta) override
{
// Bits 3 and 4 stay, the rest is swapped around according to a table:
NIBBLETYPE TopBits = (a_Meta & 0x0c);
switch (a_Meta & 0x03)
{
case 0x00: return TopBits | 0x01; // East -> West
case 0x01: return TopBits | 0x00; // West -> East
case 0x02: return TopBits | 0x02; // South -> South
case 0x03: return TopBits | 0x03; // North -> North
}
// Not reachable, but to avoid a compiler warning:
return 0;
}
} ; } ;

32
src/Blocks/BlockTNT.h Normal file
View File

@ -0,0 +1,32 @@
#pragma once
#include "BlockHandler.h"
class cBlockTNTHandler :
public cBlockHandler
{
public:
cBlockTNTHandler(BLOCKTYPE a_BlockType)
: cBlockHandler(a_BlockType)
{
}
virtual const char * GetStepSound(void) override
{
return "step.grass";
}
virtual void OnCancelRightClick(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace) override
{
a_WorldInterface.SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, a_Player);
}
};

View File

@ -2,17 +2,17 @@
#include "BlockHandler.h" #include "BlockHandler.h"
#include "../Chunk.h" #include "../Chunk.h"
#include "MetaRotater.h"
class cBlockTorchHandler : class cBlockTorchHandler :
public cBlockHandler public cMetaRotater<cBlockHandler, 0x7, 0x4, 0x1, 0x3, 0x2>
{ {
public: public:
cBlockTorchHandler(BLOCKTYPE a_BlockType) cBlockTorchHandler(BLOCKTYPE a_BlockType)
: cBlockHandler(a_BlockType) : cMetaRotater<cBlockHandler, 0x7, 0x4, 0x1, 0x3, 0x2>(a_BlockType)
{ {
} }
@ -185,67 +185,6 @@ public:
{ {
return "step.wood"; return "step.wood";
} }
virtual NIBBLETYPE MetaRotateCCW(NIBBLETYPE a_Meta) override
{
// Bit 4 stays, the rest is swapped around according to a table:
NIBBLETYPE TopBits = (a_Meta & 0x08);
switch (a_Meta & 0x07)
{
case 0x01: return TopBits | 0x04; // East -> North
case 0x02: return TopBits | 0x03; // West -> South
case 0x03: return TopBits | 0x01; // South -> East
case 0x04: return TopBits | 0x02; // North -> West
default: return a_Meta; // Floor -> Floor
}
}
virtual NIBBLETYPE MetaRotateCW(NIBBLETYPE a_Meta) override
{
// Bit 4 stays, the rest is swapped around according to a table:
NIBBLETYPE TopBits = (a_Meta & 0x08);
switch (a_Meta & 0x07)
{
case 0x01: return TopBits | 0x03; // East -> South
case 0x02: return TopBits | 0x04; // West -> North
case 0x03: return TopBits | 0x02; // South -> West
case 0x04: return TopBits | 0x01; // North -> East
default: return a_Meta; // Floor -> Floor
}
}
virtual NIBBLETYPE MetaMirrorXY(NIBBLETYPE a_Meta) override
{
// Bit 4 stays, the rest is swapped around according to a table:
NIBBLETYPE TopBits = (a_Meta & 0x08);
switch (a_Meta & 0x07)
{
case 0x03: return TopBits | 0x04; // South -> North
case 0x04: return TopBits | 0x03; // North -> South
default: return a_Meta; // Keep the rest
}
}
// Mirroring around the XZ plane doesn't make sense for floor torches,
// the others stay the same, so let's keep all the metas the same.
// The base class does tht for us, no need to override MetaMirrorXZ()
virtual NIBBLETYPE MetaMirrorYZ(NIBBLETYPE a_Meta) override
{
// Bit 4 stays, the rest is swapped around according to a table:
NIBBLETYPE TopBits = (a_Meta & 0x08);
switch (a_Meta & 0x07)
{
case 0x01: return TopBits | 0x02; // East -> West
case 0x02: return TopBits | 0x01; // West -> East
default: return a_Meta; // Keep the rest
}
}
} ; } ;

View File

@ -36,8 +36,16 @@ public:
{ {
// Flip the ON bit on/off using the XOR bitwise operation // Flip the ON bit on/off using the XOR bitwise operation
NIBBLETYPE Meta = (a_ChunkInterface.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) ^ 0x04); NIBBLETYPE Meta = (a_ChunkInterface.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) ^ 0x04);
a_ChunkInterface.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, Meta); a_ChunkInterface.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, Meta);
cWorld * World = (cWorld *) &a_WorldInterface;
World->BroadcastSoundParticleEffect(1003, a_BlockX, a_BlockY, a_BlockZ, 0, a_Player->GetClientHandle());
}
virtual void OnCancelRightClick(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace) override
{
UNUSED(a_ChunkInterface);
a_WorldInterface.SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, a_Player);
} }
virtual bool GetPlacementBlockTypeMeta( virtual bool GetPlacementBlockTypeMeta(

View File

@ -1,8 +1,7 @@
#pragma once #pragma once
#include "BlockHandler.h" #include "BlockHandler.h"
#include "MetaRotater.h"
@ -24,6 +23,10 @@ public:
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
) override ) override
{ {
UNUSED(a_Player);
UNUSED(a_CursorX);
UNUSED(a_CursorY);
UNUSED(a_CursorZ);
// TODO: Disallow placement where the vine doesn't attach to something properly // TODO: Disallow placement where the vine doesn't attach to something properly
BLOCKTYPE BlockType = 0; BLOCKTYPE BlockType = 0;
NIBBLETYPE BlockMeta; NIBBLETYPE BlockMeta;
@ -162,11 +165,17 @@ public:
return false; return false;
} }
virtual void OnUpdate(cWorld * a_World, int X, int Y, int Z) virtual void OnUpdate(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cBlockPluginInterface & a_BlockPluginInterface, cChunk & a_Chunk, int a_RelX, int a_RelY, int a_RelZ)
{ {
if (a_World->GetBlock(X, Y - 1, Z) == E_BLOCK_AIR) UNUSED(a_ChunkInterface);
UNUSED(a_WorldInterface);
UNUSED(a_BlockPluginInterface);
BLOCKTYPE Block;
a_Chunk.UnboundedRelGetBlockType(a_RelX, a_RelY - 1, a_RelZ, Block);
if (Block == E_BLOCK_AIR)
{ {
a_World->SetBlock(X, Y - 1, Z, E_BLOCK_VINES, a_World->GetBlockMeta(X, Y, Z)); a_Chunk.UnboundedRelSetBlock(a_RelX, a_RelY - 1, a_RelZ, E_BLOCK_VINES, a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ));
} }
} }
@ -180,8 +189,8 @@ public:
{ {
return ((a_Meta << 1) | (a_Meta >> 3)) & 0x0f; // Rotate bits to the left return ((a_Meta << 1) | (a_Meta >> 3)) & 0x0f; // Rotate bits to the left
} }
virtual NIBBLETYPE MetaMirrorXY(NIBBLETYPE a_Meta) override virtual NIBBLETYPE MetaMirrorXY(NIBBLETYPE a_Meta) override
{ {
// Bits 2 and 4 stay, bits 1 and 3 swap // Bits 2 and 4 stay, bits 1 and 3 swap
@ -194,6 +203,7 @@ public:
// Bits 1 and 3 stay, bits 2 and 4 swap // Bits 1 and 3 stay, bits 2 and 4 swap
return ((a_Meta & 0x05) | ((a_Meta & 0x02) << 2) | ((a_Meta & 0x08) >> 2)); return ((a_Meta & 0x05) | ((a_Meta & 0x02) << 2) | ((a_Meta & 0x08) >> 2));
} }
} ; } ;

View File

@ -6,7 +6,7 @@
bool cChunkInterface::DigBlock(cWorldInterface & a_WorldInterface, int a_X, int a_Y, int a_Z) bool cChunkInterface::DigBlock(cWorldInterface & a_WorldInterface, int a_X, int a_Y, int a_Z)
{ {
cBlockHandler *Handler = cBlockHandler::GetBlockHandler(GetBlock(a_X, a_Y, a_Z)); cBlockHandler * Handler = cBlockInfo::GetHandler(GetBlock(a_X, a_Y, a_Z));
Handler->OnDestroyed(*this, a_WorldInterface, a_X, a_Y, a_Z); Handler->OnDestroyed(*this, a_WorldInterface, a_X, a_Y, a_Z);
return m_ChunkMap->DigBlock(a_X, a_Y, a_Z); return m_ChunkMap->DigBlock(a_X, a_Y, a_Z);
} }

120
src/Blocks/MetaRotater.h Normal file
View File

@ -0,0 +1,120 @@
// MetaRotater.h
// Provides a mixin for rotations and reflections
#pragma once
// MSVC generates warnings for the templated AssertIfNotMatched parameter conditions, so disable it:
#ifdef _MSC_VER
#pragma warning(disable: 4127) // Conditional expression is constant
#endif
/*
Provides a mixin for rotations and reflections following the standard pattern of apply mask then use case.
Usage:
Inherit from this class providing your base class as Base, the BitMask for the direction bits in bitmask and the masked value for the directions in North, East, South, West. There is also an aptional parameter AssertIfNotMatched. Set this if it is invalid for a block to exist in any other state.
*/
template<class Base, NIBBLETYPE BitMask, NIBBLETYPE North, NIBBLETYPE East, NIBBLETYPE South, NIBBLETYPE West, bool AssertIfNotMatched = false>
class cMetaRotater : public Base
{
public:
cMetaRotater(BLOCKTYPE a_BlockType) :
Base(a_BlockType)
{}
virtual ~cMetaRotater() {}
virtual NIBBLETYPE MetaRotateCCW(NIBBLETYPE a_Meta) override;
virtual NIBBLETYPE MetaRotateCW(NIBBLETYPE a_Meta) override;
virtual NIBBLETYPE MetaMirrorXY(NIBBLETYPE a_Meta) override;
virtual NIBBLETYPE MetaMirrorYZ(NIBBLETYPE a_Meta) override;
};
template<class Base, NIBBLETYPE BitMask, NIBBLETYPE North, NIBBLETYPE East, NIBBLETYPE South, NIBBLETYPE West, bool AssertIfNotMatched>
NIBBLETYPE cMetaRotater<Base, BitMask, North, East, South, West, AssertIfNotMatched>::MetaRotateCW(NIBBLETYPE a_Meta)
{
NIBBLETYPE OtherMeta = a_Meta & (~BitMask);
switch (a_Meta & BitMask)
{
case South: return West | OtherMeta;
case West: return North | OtherMeta;
case North: return East | OtherMeta;
case East: return South | OtherMeta;
}
if (AssertIfNotMatched)
{
ASSERT(!"Invalid Meta value");
}
return a_Meta;
}
template<class Base, NIBBLETYPE BitMask, NIBBLETYPE North, NIBBLETYPE East, NIBBLETYPE South, NIBBLETYPE West, bool AssertIfNotMatched>
NIBBLETYPE cMetaRotater<Base, BitMask, North, East, South, West, AssertIfNotMatched>::MetaRotateCCW(NIBBLETYPE a_Meta)
{
NIBBLETYPE OtherMeta = a_Meta & (~BitMask);
switch (a_Meta & BitMask)
{
case South: return East | OtherMeta;
case East: return North | OtherMeta;
case North: return West | OtherMeta;
case West: return South | OtherMeta;
}
if (AssertIfNotMatched)
{
ASSERT(!"Invalid Meta value");
}
return a_Meta;
}
template<class Base, NIBBLETYPE BitMask, NIBBLETYPE North, NIBBLETYPE East, NIBBLETYPE South, NIBBLETYPE West, bool AssertIfNotMatched>
NIBBLETYPE cMetaRotater<Base, BitMask, North, East, South, West, AssertIfNotMatched>::MetaMirrorXY(NIBBLETYPE a_Meta)
{
NIBBLETYPE OtherMeta = a_Meta & (~BitMask);
switch (a_Meta & BitMask)
{
case South: return North | OtherMeta;
case North: return South | OtherMeta;
}
// Not Facing North or South; No change.
return a_Meta;
}
template<class Base, NIBBLETYPE BitMask, NIBBLETYPE North, NIBBLETYPE East, NIBBLETYPE South, NIBBLETYPE West, bool AssertIfNotMatched>
NIBBLETYPE cMetaRotater<Base, BitMask, North, East, South, West, AssertIfNotMatched>::MetaMirrorYZ(NIBBLETYPE a_Meta)
{
NIBBLETYPE OtherMeta = a_Meta & (~BitMask);
switch (a_Meta & BitMask)
{
case West: return East | OtherMeta;
case East: return West | OtherMeta;
}
// Not Facing East or West; No change.
return a_Meta;
}

View File

@ -27,4 +27,7 @@ public:
/** Spawns a mob of the specified type. Returns the mob's EntityID if recognized and spawned, <0 otherwise */ /** Spawns a mob of the specified type. Returns the mob's EntityID if recognized and spawned, <0 otherwise */
virtual int SpawnMob(double a_PosX, double a_PosY, double a_PosZ, cMonster::eType a_MonsterType) = 0; virtual int SpawnMob(double a_PosX, double a_PosY, double a_PosZ, cMonster::eType a_MonsterType) = 0;
/** Sends the block on those coords to the player */
virtual void SendBlockTo(int a_BlockX, int a_BlockY, int a_BlockZ, cPlayer * a_Player) = 0;
}; };

View File

@ -44,10 +44,10 @@
#if 0 #ifdef SELF_TEST
/// Self-test of the VarInt-reading and writing code /// Self-test of the VarInt-reading and writing code
class cByteBufferSelfTest static class cByteBufferSelfTest
{ {
public: public:
cByteBufferSelfTest(void) cByteBufferSelfTest(void)
@ -86,7 +86,7 @@ public:
cByteBuffer buf(3); cByteBuffer buf(3);
for (int i = 0; i < 1000; i++) for (int i = 0; i < 1000; i++)
{ {
int FreeSpace = buf.GetFreeSpace(); size_t FreeSpace = buf.GetFreeSpace();
assert(buf.GetReadableSpace() == 0); assert(buf.GetReadableSpace() == 0);
assert(FreeSpace > 0); assert(FreeSpace > 0);
assert(buf.Write("a", 1)); assert(buf.Write("a", 1));
@ -171,21 +171,22 @@ cByteBuffer::~cByteBuffer()
bool cByteBuffer::Write(const char * a_Bytes, int a_Count) bool cByteBuffer::Write(const char * a_Bytes, size_t a_Count)
{ {
CHECK_THREAD; CHECK_THREAD;
CheckValid(); CheckValid();
// Store the current free space for a check after writing: // Store the current free space for a check after writing:
int CurFreeSpace = GetFreeSpace(); size_t CurFreeSpace = GetFreeSpace();
int CurReadableSpace = GetReadableSpace(); size_t CurReadableSpace = GetReadableSpace();
int WrittenBytes = 0; size_t WrittenBytes = 0;
if (CurFreeSpace < a_Count) if (CurFreeSpace < a_Count)
{ {
return false; return false;
} }
int TillEnd = m_BufferSize - m_WritePos; ASSERT(m_BufferSize >= m_WritePos);
size_t TillEnd = m_BufferSize - m_WritePos;
if (TillEnd <= a_Count) if (TillEnd <= a_Count)
{ {
// Need to wrap around the ringbuffer end // Need to wrap around the ringbuffer end
@ -216,16 +217,20 @@ bool cByteBuffer::Write(const char * a_Bytes, int a_Count)
int cByteBuffer::GetFreeSpace(void) const size_t cByteBuffer::GetFreeSpace(void) const
{ {
CHECK_THREAD; CHECK_THREAD;
CheckValid(); CheckValid();
if (m_WritePos >= m_DataStart) if (m_WritePos >= m_DataStart)
{ {
// Wrap around the buffer end: // Wrap around the buffer end:
ASSERT(m_BufferSize >= m_WritePos);
ASSERT((m_BufferSize - m_WritePos + m_DataStart) >= 1);
return m_BufferSize - m_WritePos + m_DataStart - 1; return m_BufferSize - m_WritePos + m_DataStart - 1;
} }
// Single free space partition: // Single free space partition:
ASSERT(m_BufferSize >= m_WritePos);
ASSERT(m_BufferSize - m_WritePos >= 1);
return m_DataStart - m_WritePos - 1; return m_DataStart - m_WritePos - 1;
} }
@ -234,10 +239,12 @@ int cByteBuffer::GetFreeSpace(void) const
/// Returns the number of bytes that are currently in the ringbuffer. Note GetReadableBytes() /// Returns the number of bytes that are currently in the ringbuffer. Note GetReadableBytes()
int cByteBuffer::GetUsedSpace(void) const size_t cByteBuffer::GetUsedSpace(void) const
{ {
CHECK_THREAD; CHECK_THREAD;
CheckValid(); CheckValid();
ASSERT(m_BufferSize >= GetFreeSpace());
ASSERT((m_BufferSize - GetFreeSpace()) >= 1);
return m_BufferSize - GetFreeSpace() - 1; return m_BufferSize - GetFreeSpace() - 1;
} }
@ -246,16 +253,18 @@ int cByteBuffer::GetUsedSpace(void) const
/// Returns the number of bytes that are currently available for reading (may be less than UsedSpace due to some data having been read already) /// Returns the number of bytes that are currently available for reading (may be less than UsedSpace due to some data having been read already)
int cByteBuffer::GetReadableSpace(void) const size_t cByteBuffer::GetReadableSpace(void) const
{ {
CHECK_THREAD; CHECK_THREAD;
CheckValid(); CheckValid();
if (m_ReadPos > m_WritePos) if (m_ReadPos > m_WritePos)
{ {
// Wrap around the buffer end: // Wrap around the buffer end:
ASSERT(m_BufferSize >= m_ReadPos);
return m_BufferSize - m_ReadPos + m_WritePos; return m_BufferSize - m_ReadPos + m_WritePos;
} }
// Single readable space partition: // Single readable space partition:
ASSERT(m_WritePos >= m_ReadPos);
return m_WritePos - m_ReadPos ; return m_WritePos - m_ReadPos ;
} }
@ -263,7 +272,7 @@ int cByteBuffer::GetReadableSpace(void) const
bool cByteBuffer::CanReadBytes(int a_Count) const bool cByteBuffer::CanReadBytes(size_t a_Count) const
{ {
CHECK_THREAD; CHECK_THREAD;
CheckValid(); CheckValid();
@ -274,7 +283,7 @@ bool cByteBuffer::CanReadBytes(int a_Count) const
bool cByteBuffer::CanWriteBytes(int a_Count) const bool cByteBuffer::CanWriteBytes(size_t a_Count) const
{ {
CHECK_THREAD; CHECK_THREAD;
CheckValid(); CheckValid();
@ -650,15 +659,14 @@ bool cByteBuffer::WriteLEInt(int a_Value)
bool cByteBuffer::ReadBuf(void * a_Buffer, int a_Count) bool cByteBuffer::ReadBuf(void * a_Buffer, size_t a_Count)
{ {
CHECK_THREAD; CHECK_THREAD;
CheckValid(); CheckValid();
ASSERT(a_Count >= 0);
NEEDBYTES(a_Count); NEEDBYTES(a_Count);
char * Dst = (char *)a_Buffer; // So that we can do byte math char * Dst = (char *)a_Buffer; // So that we can do byte math
int BytesToEndOfBuffer = m_BufferSize - m_ReadPos; ASSERT(m_BufferSize >= m_ReadPos);
ASSERT(BytesToEndOfBuffer >= 0); // Sanity check size_t BytesToEndOfBuffer = m_BufferSize - m_ReadPos;
if (BytesToEndOfBuffer <= a_Count) if (BytesToEndOfBuffer <= a_Count)
{ {
// Reading across the ringbuffer end, read the first part and adjust parameters: // Reading across the ringbuffer end, read the first part and adjust parameters:
@ -684,14 +692,14 @@ bool cByteBuffer::ReadBuf(void * a_Buffer, int a_Count)
bool cByteBuffer::WriteBuf(const void * a_Buffer, int a_Count) bool cByteBuffer::WriteBuf(const void * a_Buffer, size_t a_Count)
{ {
CHECK_THREAD; CHECK_THREAD;
CheckValid(); CheckValid();
ASSERT(a_Count >= 0);
PUTBYTES(a_Count); PUTBYTES(a_Count);
char * Src = (char *)a_Buffer; // So that we can do byte math char * Src = (char *)a_Buffer; // So that we can do byte math
int BytesToEndOfBuffer = m_BufferSize - m_WritePos; ASSERT(m_BufferSize >= m_ReadPos);
size_t BytesToEndOfBuffer = m_BufferSize - m_WritePos;
if (BytesToEndOfBuffer <= a_Count) if (BytesToEndOfBuffer <= a_Count)
{ {
// Reading across the ringbuffer end, read the first part and adjust parameters: // Reading across the ringbuffer end, read the first part and adjust parameters:
@ -714,22 +722,22 @@ bool cByteBuffer::WriteBuf(const void * a_Buffer, int a_Count)
bool cByteBuffer::ReadString(AString & a_String, int a_Count) bool cByteBuffer::ReadString(AString & a_String, size_t a_Count)
{ {
CHECK_THREAD; CHECK_THREAD;
CheckValid(); CheckValid();
ASSERT(a_Count >= 0);
NEEDBYTES(a_Count); NEEDBYTES(a_Count);
a_String.clear(); a_String.clear();
a_String.reserve(a_Count); a_String.reserve(a_Count);
int BytesToEndOfBuffer = m_BufferSize - m_ReadPos; ASSERT(m_BufferSize >= m_ReadPos);
ASSERT(BytesToEndOfBuffer >= 0); // Sanity check size_t BytesToEndOfBuffer = m_BufferSize - m_ReadPos;
if (BytesToEndOfBuffer <= a_Count) if (BytesToEndOfBuffer <= a_Count)
{ {
// Reading across the ringbuffer end, read the first part and adjust parameters: // Reading across the ringbuffer end, read the first part and adjust parameters:
if (BytesToEndOfBuffer > 0) if (BytesToEndOfBuffer > 0)
{ {
a_String.assign(m_Buffer + m_ReadPos, BytesToEndOfBuffer); a_String.assign(m_Buffer + m_ReadPos, BytesToEndOfBuffer);
ASSERT(a_Count >= BytesToEndOfBuffer);
a_Count -= BytesToEndOfBuffer; a_Count -= BytesToEndOfBuffer;
} }
m_ReadPos = 0; m_ReadPos = 0;
@ -767,11 +775,10 @@ bool cByteBuffer::ReadUTF16String(AString & a_String, int a_NumChars)
bool cByteBuffer::SkipRead(int a_Count) bool cByteBuffer::SkipRead(size_t a_Count)
{ {
CHECK_THREAD; CHECK_THREAD;
CheckValid(); CheckValid();
ASSERT(a_Count >= 0);
if (!CanReadBytes(a_Count)) if (!CanReadBytes(a_Count))
{ {
return false; return false;
@ -809,6 +816,7 @@ bool cByteBuffer::ReadToByteBuffer(cByteBuffer & a_Dst, size_t a_NumBytes)
size_t num = (a_NumBytes > sizeof(buf)) ? sizeof(buf) : a_NumBytes; size_t num = (a_NumBytes > sizeof(buf)) ? sizeof(buf) : a_NumBytes;
VERIFY(ReadBuf(buf, num)); VERIFY(ReadBuf(buf, num));
VERIFY(a_Dst.Write(buf, num)); VERIFY(a_Dst.Write(buf, num));
ASSERT(a_NumBytes >= num);
a_NumBytes -= num; a_NumBytes -= num;
} }
return true; return true;
@ -846,13 +854,15 @@ void cByteBuffer::ReadAgain(AString & a_Out)
// Used by ProtoProxy to repeat communication twice, once for parsing and the other time for the remote party // Used by ProtoProxy to repeat communication twice, once for parsing and the other time for the remote party
CHECK_THREAD; CHECK_THREAD;
CheckValid(); CheckValid();
int DataStart = m_DataStart; size_t DataStart = m_DataStart;
if (m_ReadPos < m_DataStart) if (m_ReadPos < m_DataStart)
{ {
// Across the ringbuffer end, read the first part and adjust next part's start: // Across the ringbuffer end, read the first part and adjust next part's start:
ASSERT(m_BufferSize >= m_DataStart);
a_Out.append(m_Buffer + m_DataStart, m_BufferSize - m_DataStart); a_Out.append(m_Buffer + m_DataStart, m_BufferSize - m_DataStart);
DataStart = 0; DataStart = 0;
} }
ASSERT(m_ReadPos >= DataStart);
a_Out.append(m_Buffer + DataStart, m_ReadPos - DataStart); a_Out.append(m_Buffer + DataStart, m_ReadPos - DataStart);
} }
@ -860,7 +870,7 @@ void cByteBuffer::ReadAgain(AString & a_Out)
void cByteBuffer::AdvanceReadPos(int a_Count) void cByteBuffer::AdvanceReadPos(size_t a_Count)
{ {
CHECK_THREAD; CHECK_THREAD;
CheckValid(); CheckValid();

View File

@ -31,25 +31,25 @@ public:
~cByteBuffer(); ~cByteBuffer();
/// Writes the bytes specified to the ringbuffer. Returns true if successful, false if not /// Writes the bytes specified to the ringbuffer. Returns true if successful, false if not
bool Write(const char * a_Bytes, int a_Count); bool Write(const char * a_Bytes, size_t a_Count);
/// Returns the number of bytes that can be successfully written to the ringbuffer /// Returns the number of bytes that can be successfully written to the ringbuffer
int GetFreeSpace(void) const; size_t GetFreeSpace(void) const;
/// Returns the number of bytes that are currently in the ringbuffer. Note GetReadableBytes() /// Returns the number of bytes that are currently in the ringbuffer. Note GetReadableBytes()
int GetUsedSpace(void) const; size_t GetUsedSpace(void) const;
/// Returns the number of bytes that are currently available for reading (may be less than UsedSpace due to some data having been read already) /// Returns the number of bytes that are currently available for reading (may be less than UsedSpace due to some data having been read already)
int GetReadableSpace(void) const; size_t GetReadableSpace(void) const;
/// Returns the current data start index. For debugging purposes. /// Returns the current data start index. For debugging purposes.
int GetDataStart(void) const { return m_DataStart; } int GetDataStart(void) const { return m_DataStart; }
/// Returns true if the specified amount of bytes are available for reading /// Returns true if the specified amount of bytes are available for reading
bool CanReadBytes(int a_Count) const; bool CanReadBytes(size_t a_Count) const;
/// Returns true if the specified amount of bytes are available for writing /// Returns true if the specified amount of bytes are available for writing
bool CanWriteBytes(int a_Count) const; bool CanWriteBytes(size_t a_Count) const;
// Read the specified datatype and advance the read pointer; return true if successfully read: // Read the specified datatype and advance the read pointer; return true if successfully read:
bool ReadChar (char & a_Value); bool ReadChar (char & a_Value);
@ -92,19 +92,19 @@ public:
bool WriteLEInt (int a_Value); bool WriteLEInt (int a_Value);
/// Reads a_Count bytes into a_Buffer; returns true if successful /// Reads a_Count bytes into a_Buffer; returns true if successful
bool ReadBuf(void * a_Buffer, int a_Count); bool ReadBuf(void * a_Buffer, size_t a_Count);
/// Writes a_Count bytes into a_Buffer; returns true if successful /// Writes a_Count bytes into a_Buffer; returns true if successful
bool WriteBuf(const void * a_Buffer, int a_Count); bool WriteBuf(const void * a_Buffer, size_t a_Count);
/// Reads a_Count bytes into a_String; returns true if successful /// Reads a_Count bytes into a_String; returns true if successful
bool ReadString(AString & a_String, int a_Count); bool ReadString(AString & a_String, size_t a_Count);
/// Reads 2 * a_NumChars bytes and interprets it as a UTF16-BE string, converting it into UTF8 string a_String /// Reads 2 * a_NumChars bytes and interprets it as a UTF16-BE string, converting it into UTF8 string a_String
bool ReadUTF16String(AString & a_String, int a_NumChars); bool ReadUTF16String(AString & a_String, int a_NumChars);
/// Skips reading by a_Count bytes; returns false if not enough bytes in the ringbuffer /// Skips reading by a_Count bytes; returns false if not enough bytes in the ringbuffer
bool SkipRead(int a_Count); bool SkipRead(size_t a_Count);
/// Reads all available data into a_Data /// Reads all available data into a_Data
void ReadAll(AString & a_Data); void ReadAll(AString & a_Data);
@ -126,18 +126,18 @@ public:
protected: protected:
char * m_Buffer; char * m_Buffer;
int m_BufferSize; // Total size of the ringbuffer size_t m_BufferSize; // Total size of the ringbuffer
#ifdef _DEBUG #ifdef _DEBUG
volatile unsigned long m_ThreadID; // Thread that is currently accessing the object, checked via cSingleThreadAccessChecker volatile unsigned long m_ThreadID; // Thread that is currently accessing the object, checked via cSingleThreadAccessChecker
#endif // _DEBUG #endif // _DEBUG
int m_DataStart; // Where the data starts in the ringbuffer size_t m_DataStart; // Where the data starts in the ringbuffer
int m_WritePos; // Where the data ends in the ringbuffer size_t m_WritePos; // Where the data ends in the ringbuffer
int m_ReadPos; // Where the next read will start in the ringbuffer size_t m_ReadPos; // Where the next read will start in the ringbuffer
/// Advances the m_ReadPos by a_Count bytes /// Advances the m_ReadPos by a_Count bytes
void AdvanceReadPos(int a_Count); void AdvanceReadPos(size_t a_Count);
} ; } ;

View File

@ -40,6 +40,7 @@ if (NOT MSVC)
BlockEntities/NoteEntity.h BlockEntities/NoteEntity.h
BlockEntities/SignEntity.h BlockEntities/SignEntity.h
BlockEntities/MobHeadEntity.h BlockEntities/MobHeadEntity.h
BlockEntities/FlowerPotEntity.h
BlockID.h BlockID.h
BoundingBox.h BoundingBox.h
ChatColor.h ChatColor.h

View File

@ -20,6 +20,7 @@
#include "BlockEntities/NoteEntity.h" #include "BlockEntities/NoteEntity.h"
#include "BlockEntities/SignEntity.h" #include "BlockEntities/SignEntity.h"
#include "BlockEntities/MobHeadEntity.h" #include "BlockEntities/MobHeadEntity.h"
#include "BlockEntities/FlowerPotEntity.h"
#include "Entities/Pickup.h" #include "Entities/Pickup.h"
#include "Item.h" #include "Item.h"
#include "Noise.h" #include "Noise.h"
@ -1311,6 +1312,7 @@ void cChunk::CreateBlockEntities(void)
case E_BLOCK_HEAD: case E_BLOCK_HEAD:
case E_BLOCK_NOTE_BLOCK: case E_BLOCK_NOTE_BLOCK:
case E_BLOCK_JUKEBOX: case E_BLOCK_JUKEBOX:
case E_BLOCK_FLOWER_POT:
{ {
if (!HasBlockEntityAt(x + m_PosX * Width, y + m_PosY * Height, z + m_PosZ * Width)) if (!HasBlockEntityAt(x + m_PosX * Width, y + m_PosY * Height, z + m_PosZ * Width))
{ {
@ -1440,6 +1442,7 @@ void cChunk::SetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType,
case E_BLOCK_HEAD: case E_BLOCK_HEAD:
case E_BLOCK_NOTE_BLOCK: case E_BLOCK_NOTE_BLOCK:
case E_BLOCK_JUKEBOX: case E_BLOCK_JUKEBOX:
case E_BLOCK_FLOWER_POT:
{ {
AddBlockEntity(cBlockEntity::CreateByBlockType(a_BlockType, a_BlockMeta, WorldPos.x, WorldPos.y, WorldPos.z, m_World)); AddBlockEntity(cBlockEntity::CreateByBlockType(a_BlockType, a_BlockMeta, WorldPos.x, WorldPos.y, WorldPos.z, m_World));
break; break;
@ -2337,7 +2340,7 @@ bool cChunk::DoWithCommandBlockAt(int a_BlockX, int a_BlockY, int a_BlockZ, cCom
bool cChunk::DoWithMobHeadBlockAt(int a_BlockX, int a_BlockY, int a_BlockZ, cMobHeadBlockCallback & a_Callback) bool cChunk::DoWithMobHeadAt(int a_BlockX, int a_BlockY, int a_BlockZ, cMobHeadCallback & a_Callback)
{ {
// The blockentity list is locked by the parent chunkmap's CS // The blockentity list is locked by the parent chunkmap's CS
for (cBlockEntityList::iterator itr = m_BlockEntities.begin(), itr2 = itr; itr != m_BlockEntities.end(); itr = itr2) for (cBlockEntityList::iterator itr = m_BlockEntities.begin(), itr2 = itr; itr != m_BlockEntities.end(); itr = itr2)
@ -2369,6 +2372,38 @@ bool cChunk::DoWithMobHeadBlockAt(int a_BlockX, int a_BlockY, int a_BlockZ, cMob
bool cChunk::DoWithFlowerPotAt(int a_BlockX, int a_BlockY, int a_BlockZ, cFlowerPotCallback & a_Callback)
{
// The blockentity list is locked by the parent chunkmap's CS
for (cBlockEntityList::iterator itr = m_BlockEntities.begin(), itr2 = itr; itr != m_BlockEntities.end(); itr = itr2)
{
++itr2;
if (((*itr)->GetPosX() != a_BlockX) || ((*itr)->GetPosY() != a_BlockY) || ((*itr)->GetPosZ() != a_BlockZ))
{
continue;
}
if ((*itr)->GetBlockType() != E_BLOCK_FLOWER_POT)
{
// There is a block entity here, but of different type. No other block entity can be here, so we can safely bail out
return false;
}
// The correct block entity is here,
if (a_Callback.Item((cFlowerPotEntity *)*itr))
{
return false;
}
return true;
} // for itr - m_BlockEntitites[]
// Not found:
return false;
}
bool cChunk::GetSignLines(int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4) bool cChunk::GetSignLines(int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4)
{ {
// The blockentity list is locked by the parent chunkmap's CS // The blockentity list is locked by the parent chunkmap's CS

View File

@ -32,6 +32,7 @@ class cDispenserEntity;
class cFurnaceEntity; class cFurnaceEntity;
class cNoteEntity; class cNoteEntity;
class cMobHeadEntity; class cMobHeadEntity;
class cFlowerPotEntity;
class cBlockArea; class cBlockArea;
class cPawn; class cPawn;
class cPickup; class cPickup;
@ -48,7 +49,8 @@ typedef cItemCallback<cDispenserEntity> cDispenserCallback;
typedef cItemCallback<cFurnaceEntity> cFurnaceCallback; typedef cItemCallback<cFurnaceEntity> cFurnaceCallback;
typedef cItemCallback<cNoteEntity> cNoteBlockCallback; typedef cItemCallback<cNoteEntity> cNoteBlockCallback;
typedef cItemCallback<cCommandBlockEntity> cCommandBlockCallback; typedef cItemCallback<cCommandBlockEntity> cCommandBlockCallback;
typedef cItemCallback<cMobHeadEntity> cMobHeadBlockCallback; typedef cItemCallback<cMobHeadEntity> cMobHeadCallback;
typedef cItemCallback<cFlowerPotEntity> cFlowerPotCallback;
@ -253,8 +255,11 @@ public:
/** Calls the callback for the command block at the specified coords; returns false if there's no command block at those coords or callback returns true, returns true if found */ /** Calls the callback for the command block at the specified coords; returns false if there's no command block at those coords or callback returns true, returns true if found */
bool DoWithCommandBlockAt(int a_BlockX, int a_BlockY, int a_BlockZ, cCommandBlockCallback & a_Callback); bool DoWithCommandBlockAt(int a_BlockX, int a_BlockY, int a_BlockZ, cCommandBlockCallback & a_Callback);
/** Calls the callback for the mob head block at the specified coords; returns false if there's no mob header block at those coords or callback returns true, returns true if found */ /** Calls the callback for the mob head block at the specified coords; returns false if there's no mob head block at those coords or callback returns true, returns true if found */
bool DoWithMobHeadBlockAt(int a_BlockX, int a_BlockY, int a_BlockZ, cMobHeadBlockCallback & a_Callback); bool DoWithMobHeadAt(int a_BlockX, int a_BlockY, int a_BlockZ, cMobHeadCallback & a_Callback);
/** Calls the callback for the flower pot at the specified coords; returns false if there's no flower pot at those coords or callback returns true, returns true if found */
bool DoWithFlowerPotAt(int a_BlockX, int a_BlockY, int a_BlockZ, cFlowerPotCallback & a_Callback);
/** Retrieves the test on the sign at the specified coords; returns false if there's no sign at those coords, true if found */ /** Retrieves the test on the sign at the specified coords; returns false if there's no sign at those coords, true if found */
bool GetSignLines (int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4); // Lua-accessible bool GetSignLines (int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4); // Lua-accessible

View File

@ -2191,7 +2191,7 @@ bool cChunkMap::DoWithCommandBlockAt(int a_BlockX, int a_BlockY, int a_BlockZ, c
bool cChunkMap::DoWithMobHeadBlockAt(int a_BlockX, int a_BlockY, int a_BlockZ, cMobHeadBlockCallback & a_Callback) bool cChunkMap::DoWithMobHeadAt(int a_BlockX, int a_BlockY, int a_BlockZ, cMobHeadCallback & a_Callback)
{ {
int ChunkX, ChunkZ; int ChunkX, ChunkZ;
int BlockX = a_BlockX, BlockY = a_BlockY, BlockZ = a_BlockZ; int BlockX = a_BlockX, BlockY = a_BlockY, BlockZ = a_BlockZ;
@ -2202,7 +2202,25 @@ bool cChunkMap::DoWithMobHeadBlockAt(int a_BlockX, int a_BlockY, int a_BlockZ, c
{ {
return false; return false;
} }
return Chunk->DoWithMobHeadBlockAt(a_BlockX, a_BlockY, a_BlockZ, a_Callback); return Chunk->DoWithMobHeadAt(a_BlockX, a_BlockY, a_BlockZ, a_Callback);
}
bool cChunkMap::DoWithFlowerPotAt(int a_BlockX, int a_BlockY, int a_BlockZ, cFlowerPotCallback & a_Callback)
{
int ChunkX, ChunkZ;
int BlockX = a_BlockX, BlockY = a_BlockY, BlockZ = a_BlockZ;
cChunkDef::AbsoluteToRelative(BlockX, BlockY, BlockZ, ChunkX, ChunkZ);
cCSLock Lock(m_CSLayers);
cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ);
if ((Chunk == NULL) && !Chunk->IsValid())
{
return false;
}
return Chunk->DoWithFlowerPotAt(a_BlockX, a_BlockY, a_BlockZ, a_Callback);
} }

View File

@ -26,6 +26,7 @@ class cFurnaceEntity;
class cNoteEntity; class cNoteEntity;
class cCommandBlockEntity; class cCommandBlockEntity;
class cMobHeadEntity; class cMobHeadEntity;
class cFlowerPotEntity;
class cPawn; class cPawn;
class cPickup; class cPickup;
class cChunkDataSerializer; class cChunkDataSerializer;
@ -41,10 +42,11 @@ typedef cItemCallback<cChestEntity> cChestCallback;
typedef cItemCallback<cDispenserEntity> cDispenserCallback; typedef cItemCallback<cDispenserEntity> cDispenserCallback;
typedef cItemCallback<cDropperEntity> cDropperCallback; typedef cItemCallback<cDropperEntity> cDropperCallback;
typedef cItemCallback<cDropSpenserEntity> cDropSpenserCallback; typedef cItemCallback<cDropSpenserEntity> cDropSpenserCallback;
typedef cItemCallback<cFlowerPotEntity> cFlowerPotCallback;
typedef cItemCallback<cFurnaceEntity> cFurnaceCallback; typedef cItemCallback<cFurnaceEntity> cFurnaceCallback;
typedef cItemCallback<cNoteEntity> cNoteBlockCallback; typedef cItemCallback<cNoteEntity> cNoteBlockCallback;
typedef cItemCallback<cCommandBlockEntity> cCommandBlockCallback; typedef cItemCallback<cCommandBlockEntity> cCommandBlockCallback;
typedef cItemCallback<cMobHeadEntity> cMobHeadBlockCallback; typedef cItemCallback<cMobHeadEntity> cMobHeadCallback;
typedef cItemCallback<cChunk> cChunkCallback; typedef cItemCallback<cChunk> cChunkCallback;
@ -257,7 +259,10 @@ public:
bool DoWithCommandBlockAt(int a_BlockX, int a_BlockY, int a_BlockZ, cCommandBlockCallback & a_Callback); // Lua-accessible bool DoWithCommandBlockAt(int a_BlockX, int a_BlockY, int a_BlockZ, cCommandBlockCallback & a_Callback); // Lua-accessible
/** Calls the callback for the mob head block at the specified coords; returns false if there's no mob head block at those coords or callback returns true, returns true if found */ /** Calls the callback for the mob head block at the specified coords; returns false if there's no mob head block at those coords or callback returns true, returns true if found */
bool DoWithMobHeadBlockAt(int a_BlockX, int a_BlockY, int a_BlockZ, cMobHeadBlockCallback & a_Callback); // Lua-accessible bool DoWithMobHeadAt(int a_BlockX, int a_BlockY, int a_BlockZ, cMobHeadCallback & a_Callback); // Lua-accessible
/** Calls the callback for the flower pot at the specified coords; returns false if there's no flower pot at those coords or callback returns true, returns true if found */
bool DoWithFlowerPotAt(int a_BlockX, int a_BlockY, int a_BlockZ, cFlowerPotCallback & a_Callback); // Lua-accessible
/** Retrieves the test on the sign at the specified coords; returns false if there's no sign at those coords, true if found */ /** Retrieves the test on the sign at the specified coords; returns false if there's no sign at those coords, true if found */
bool GetSignLines (int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4); // Lua-accessible bool GetSignLines (int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4); // Lua-accessible

View File

@ -96,8 +96,8 @@ cClientHandle::cClientHandle(const cSocket * a_Socket, int a_ViewDistance) :
m_ShouldCheckDownloaded(false), m_ShouldCheckDownloaded(false),
m_NumExplosionsThisTick(0), m_NumExplosionsThisTick(0),
m_UniqueID(0), m_UniqueID(0),
m_Locale("en_GB"), m_HasSentPlayerChunk(false),
m_HasSentPlayerChunk(false) m_Locale("en_GB")
{ {
m_Protocol = new cProtocolRecognizer(this); m_Protocol = new cProtocolRecognizer(this);
@ -555,12 +555,25 @@ void cClientHandle::HandlePluginMessage(const AString & a_Channel, const AString
} }
else if (a_Channel == "REGISTER") else if (a_Channel == "REGISTER")
{ {
if (HasPluginChannel(a_Channel))
{
SendPluginMessage("UNREGISTER", a_Channel);
return; // Can't register again if already taken - kinda defeats the point of plugin messaging!
}
RegisterPluginChannels(BreakApartPluginChannels(a_Message)); RegisterPluginChannels(BreakApartPluginChannels(a_Message));
} }
else if (a_Channel == "UNREGISTER") else if (a_Channel == "UNREGISTER")
{ {
UnregisterPluginChannels(BreakApartPluginChannels(a_Message)); UnregisterPluginChannels(BreakApartPluginChannels(a_Message));
} }
else if (!HasPluginChannel(a_Channel))
{
// Ignore if client sent something but didn't register the channel first
LOGD("Player %s sent a plugin message on channel \"%s\", but didn't REGISTER it first", GetUsername().c_str(), a_Channel.c_str());
SendPluginMessage("UNREGISTER", a_Channel);
return;
}
cPluginManager::Get()->CallHookPluginMessage(*this, a_Channel, a_Message); cPluginManager::Get()->CallHookPluginMessage(*this, a_Channel, a_Message);
} }
@ -838,7 +851,7 @@ void cClientHandle::HandleBlockDigStarted(int a_BlockX, int a_BlockY, int a_Bloc
cWorld * World = m_Player->GetWorld(); cWorld * World = m_Player->GetWorld();
cChunkInterface ChunkInterface(World->GetChunkMap()); cChunkInterface ChunkInterface(World->GetChunkMap());
cBlockHandler * Handler = cBlockHandler::GetBlockHandler(a_OldBlock); cBlockHandler * Handler = cBlockInfo::GetHandler(a_OldBlock);
Handler->OnDigging(ChunkInterface, *World, m_Player, a_BlockX, a_BlockY, a_BlockZ); Handler->OnDigging(ChunkInterface, *World, m_Player, a_BlockX, a_BlockY, a_BlockZ);
cItemHandler * ItemHandler = cItemHandler::GetItemHandler(m_Player->GetEquippedItem()); cItemHandler * ItemHandler = cItemHandler::GetItemHandler(m_Player->GetEquippedItem());
@ -852,7 +865,7 @@ void cClientHandle::HandleBlockDigStarted(int a_BlockX, int a_BlockY, int a_Bloc
int pZ = a_BlockZ; int pZ = a_BlockZ;
AddFaceDirection(pX, pY, pZ, a_BlockFace); // Get the block in front of the clicked coordinates (m_bInverse defaulted to false) AddFaceDirection(pX, pY, pZ, a_BlockFace); // Get the block in front of the clicked coordinates (m_bInverse defaulted to false)
Handler = cBlockHandler::GetBlockHandler(World->GetBlock(pX, pY, pZ)); Handler = cBlockInfo::GetHandler(World->GetBlock(pX, pY, pZ));
if (Handler->IsClickedThrough()) if (Handler->IsClickedThrough())
{ {
@ -920,14 +933,22 @@ void cClientHandle::HandleRightClick(int a_BlockX, int a_BlockY, int a_BlockZ, e
a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, ItemToFullString(a_HeldItem).c_str() a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, ItemToFullString(a_HeldItem).c_str()
); );
cWorld * World = m_Player->GetWorld();
cPluginManager * PlgMgr = cRoot::Get()->GetPluginManager(); cPluginManager * PlgMgr = cRoot::Get()->GetPluginManager();
if (PlgMgr->CallHookPlayerRightClick(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ)) if (PlgMgr->CallHookPlayerRightClick(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ))
{ {
// A plugin doesn't agree with the action, replace the block on the client and quit: // A plugin doesn't agree with the action, replace the block on the client and quit:
cChunkInterface ChunkInterface(World->GetChunkMap());
BLOCKTYPE BlockType = World->GetBlock(a_BlockX, a_BlockY, a_BlockZ);
cBlockHandler * BlockHandler = cBlockInfo::GetHandler(BlockType);
BlockHandler->OnCancelRightClick(ChunkInterface, *World, m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace);
if (a_BlockFace > -1) if (a_BlockFace > -1)
{ {
AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace);
m_Player->GetWorld()->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, m_Player); World->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, m_Player);
World->SendBlockTo(a_BlockX, a_BlockY + 1, a_BlockZ, m_Player); //2 block high things
} }
return; return;
} }
@ -953,17 +974,15 @@ void cClientHandle::HandleRightClick(int a_BlockX, int a_BlockY, int a_BlockZ, e
if (a_BlockFace > -1) if (a_BlockFace > -1)
{ {
AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace);
m_Player->GetWorld()->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, m_Player); World->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, m_Player);
} }
return; return;
} }
cWorld * World = m_Player->GetWorld();
BLOCKTYPE BlockType; BLOCKTYPE BlockType;
NIBBLETYPE BlockMeta; NIBBLETYPE BlockMeta;
World->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta); World->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta);
cBlockHandler * BlockHandler = cBlockHandler::GetBlockHandler(BlockType); cBlockHandler * BlockHandler = cBlockInfo::GetHandler(BlockType);
if (BlockHandler->IsUseable() && !m_Player->IsCrouched()) if (BlockHandler->IsUseable() && !m_Player->IsCrouched())
{ {
@ -1447,7 +1466,7 @@ bool cClientHandle::HandleHandshake(const AString & a_Username)
void cClientHandle::HandleEntityAction(int a_EntityID, char a_ActionID) void cClientHandle::HandleEntityCrouch(int a_EntityID, bool a_IsCrouching)
{ {
if (a_EntityID != m_Player->GetUniqueID()) if (a_EntityID != m_Player->GetUniqueID())
{ {
@ -1455,35 +1474,37 @@ void cClientHandle::HandleEntityAction(int a_EntityID, char a_ActionID)
return; return;
} }
switch (a_ActionID) m_Player->SetCrouch(a_IsCrouching);
}
void cClientHandle::HandleEntityLeaveBed(int a_EntityID)
{
if (a_EntityID != m_Player->GetUniqueID())
{ {
case 1: // Crouch // We should only receive entity actions from the entity that is performing the action
{ return;
m_Player->SetCrouch(true);
break;
}
case 2: // Uncrouch
{
m_Player->SetCrouch(false);
break;
}
case 3: // Leave bed
{
m_Player->GetWorld()->BroadcastEntityAnimation(*m_Player, 2);
break;
}
case 4: // Start sprinting
{
m_Player->SetSprint(true);
break;
}
case 5: // Stop sprinting
{
m_Player->SetSprint(false);
SendPlayerMaxSpeed();
break;
}
} }
m_Player->GetWorld()->BroadcastEntityAnimation(*m_Player, 2);
}
void cClientHandle::HandleEntitySprinting(int a_EntityID, bool a_IsSprinting)
{
if (a_EntityID != m_Player->GetUniqueID())
{
// We should only receive entity actions from the entity that is performing the action
return;
}
m_Player->SetSprint(a_IsSprinting);
} }

View File

@ -188,7 +188,9 @@ public:
void HandleChat (const AString & a_Message); void HandleChat (const AString & a_Message);
void HandleCreativeInventory(short a_SlotNum, const cItem & a_HeldItem); void HandleCreativeInventory(short a_SlotNum, const cItem & a_HeldItem);
void HandleDisconnect (const AString & a_Reason); void HandleDisconnect (const AString & a_Reason);
void HandleEntityAction (int a_EntityID, char a_ActionID); void HandleEntityCrouch (int a_EntityID, bool a_IsCrouching);
void HandleEntityLeaveBed (int a_EntityID);
void HandleEntitySprinting (int a_EntityID, bool a_IsSprinting);
/** Called when the protocol handshake has been received (for protocol versions that support it; /** Called when the protocol handshake has been received (for protocol versions that support it;
otherwise the first instant when a username is received). otherwise the first instant when a username is received).

View File

@ -124,14 +124,15 @@ public:
/** Removes all parts from the object. */ /** Removes all parts from the object. */
void Clear(void); void Clear(void);
// tolua_end
// The following are exported in ManualBindings in order to support chaining - they return *this in Lua (#755)
/** Adds a plain text part, with optional style. /** Adds a plain text part, with optional style.
The default style is plain white text. */ The default style is plain white text. */
void AddTextPart(const AString & a_Message, const AString & a_Style = ""); void AddTextPart(const AString & a_Message, const AString & a_Style = "");
// tolua_end /** Adds a part that is translated client-side, with the formatting parameters and optional style. */
/** Adds a part that is translated client-side, with the formatting parameters and optional style.
Exported in ManualBindings due to AStringVector usage - Lua uses an array-table of strings. */
void AddClientTranslatedPart(const AString & a_TranslationID, const AStringVector & a_Parameters, const AString & a_Style = ""); void AddClientTranslatedPart(const AString & a_TranslationID, const AStringVector & a_Parameters, const AString & a_Style = "");
// tolua_begin // tolua_begin
@ -155,12 +156,14 @@ public:
/** Sets the message type, which is indicated by prefixes added to the message when serializing. */ /** Sets the message type, which is indicated by prefixes added to the message when serializing. */
void SetMessageType(eMessageType a_MessageType); void SetMessageType(eMessageType a_MessageType);
/** Returns the message type set previously by SetMessageType(). */
eMessageType GetMessageType(void) const { return m_MessageType; }
/** Adds the "underline" style to each part that is an URL. */ /** Adds the "underline" style to each part that is an URL. */
void UnderlineUrls(void); void UnderlineUrls(void);
// tolua_begin
/** Returns the message type set previously by SetMessageType(). */
eMessageType GetMessageType(void) const { return m_MessageType; }
// tolua_end // tolua_end
const cParts & GetParts(void) const { return m_Parts; } const cParts & GetParts(void) const { return m_Parts; }

View File

@ -235,8 +235,8 @@ inline eBlockFace MirrorBlockFaceY(eBlockFace a_BlockFace)
case BLOCK_FACE_XP: return BLOCK_FACE_XM; case BLOCK_FACE_XP: return BLOCK_FACE_XM;
case BLOCK_FACE_ZM: return BLOCK_FACE_ZP; case BLOCK_FACE_ZM: return BLOCK_FACE_ZP;
case BLOCK_FACE_ZP: return BLOCK_FACE_ZM; case BLOCK_FACE_ZP: return BLOCK_FACE_ZM;
default: return a_BlockFace;
} }
return a_BlockFace;
} }
@ -252,8 +252,8 @@ inline eBlockFace RotateBlockFaceCCW(eBlockFace a_BlockFace)
case BLOCK_FACE_XP: return BLOCK_FACE_ZM; case BLOCK_FACE_XP: return BLOCK_FACE_ZM;
case BLOCK_FACE_ZM: return BLOCK_FACE_XM; case BLOCK_FACE_ZM: return BLOCK_FACE_XM;
case BLOCK_FACE_ZP: return BLOCK_FACE_XP; case BLOCK_FACE_ZP: return BLOCK_FACE_XP;
default: return a_BlockFace;
} }
return a_BlockFace;
} }
@ -268,8 +268,8 @@ inline eBlockFace RotateBlockFaceCW(eBlockFace a_BlockFace)
case BLOCK_FACE_XP: return BLOCK_FACE_ZP; case BLOCK_FACE_XP: return BLOCK_FACE_ZP;
case BLOCK_FACE_ZM: return BLOCK_FACE_XP; case BLOCK_FACE_ZM: return BLOCK_FACE_XP;
case BLOCK_FACE_ZP: return BLOCK_FACE_XM; case BLOCK_FACE_ZP: return BLOCK_FACE_XM;
default: return a_BlockFace;
} }
return a_BlockFace;
} }

View File

@ -11,7 +11,7 @@
class cFloater : class cFloater :
public cEntity public cEntity
{ {
typedef cFloater super; typedef cEntity super;
public: public:
//tolua_end //tolua_end

View File

@ -1031,9 +1031,9 @@ cMinecartWithChest::cMinecartWithChest(double a_X, double a_Y, double a_Z) :
void cMinecartWithChest::SetSlot(int a_Idx, const cItem & a_Item) void cMinecartWithChest::SetSlot(size_t a_Idx, const cItem & a_Item)
{ {
ASSERT((a_Idx >= 0) && (a_Idx < ARRAYCOUNT(m_Items))); ASSERT(a_Idx < ARRAYCOUNT(m_Items));
m_Items[a_Idx] = a_Item; m_Items[a_Idx] = a_Item;
} }

View File

@ -122,7 +122,7 @@ public:
const cItem & GetSlot(int a_Idx) const { return m_Items[a_Idx]; } const cItem & GetSlot(int a_Idx) const { return m_Items[a_Idx]; }
cItem & GetSlot(int a_Idx) { return m_Items[a_Idx]; } cItem & GetSlot(int a_Idx) { return m_Items[a_Idx]; }
void SetSlot(int a_Idx, const cItem & a_Item); void SetSlot(size_t a_Idx, const cItem & a_Item);
protected: protected:
@ -193,4 +193,4 @@ public:
CLASS_PROTODEF(cMinecartWithHopper); CLASS_PROTODEF(cMinecartWithHopper);
cMinecartWithHopper(double a_X, double a_Y, double a_Z); cMinecartWithHopper(double a_X, double a_Y, double a_Z);
} ; } ;

View File

@ -69,6 +69,8 @@ public:
m_BoundingBox(a_BoundingBox) m_BoundingBox(a_BoundingBox)
{ {
} }
virtual ~cMineShaft() {}
/// Returns true if this mineshaft intersects the specified cuboid /// Returns true if this mineshaft intersects the specified cuboid
bool DoesIntersect(const cCuboid & a_Other) bool DoesIntersect(const cCuboid & a_Other)

View File

@ -595,7 +595,7 @@ void GetPineTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise
{ {
break; break;
} }
ASSERT(LayerSize < ARRAYCOUNT(BigOs)); ASSERT((size_t)LayerSize < ARRAYCOUNT(BigOs));
PushCoordBlocks(a_BlockX, h, a_BlockZ, a_OtherBlocks, BigOs[LayerSize].Coords, BigOs[LayerSize].Count, E_BLOCK_LEAVES, E_META_LEAVES_CONIFER); PushCoordBlocks(a_BlockX, h, a_BlockZ, a_OtherBlocks, BigOs[LayerSize].Coords, BigOs[LayerSize].Count, E_BLOCK_LEAVES, E_META_LEAVES_CONIFER);
h--; h--;
} }

View File

@ -204,6 +204,12 @@ void cInventory::SetSlot(int a_SlotNum, const cItem & a_Item)
return; return;
} }
Grid->SetSlot(GridSlotNum, a_Item); Grid->SetSlot(GridSlotNum, a_Item);
// Broadcast the Equipped Item, if the Slot is changed.
if ((Grid == &m_HotbarSlots) && (m_EquippedSlotNum == (a_SlotNum - invHotbarOffset)))
{
m_Owner.GetWorld()->BroadcastEntityEquipment(m_Owner, 0, a_Item, m_Owner.GetClientHandle());
}
} }

View File

@ -285,7 +285,7 @@ void cItemHandler::OnBlockDestroyed(cWorld * a_World, cPlayer * a_Player, const
UNUSED(a_Item); UNUSED(a_Item);
BLOCKTYPE Block = a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ); BLOCKTYPE Block = a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ);
cBlockHandler * Handler = cBlockHandler::GetBlockHandler(Block); cBlockHandler * Handler = cBlockInfo::GetHandler(Block);
if (a_Player->IsGameModeSurvival()) if (a_Player->IsGameModeSurvival())
{ {

View File

@ -21,6 +21,9 @@ class cItemHandler
public: public:
cItemHandler(int a_ItemType); cItemHandler(int a_ItemType);
// Force virtual destructor
virtual ~cItemHandler() {}
/// Called when the player tries to use the item (right mouse button). Return false to make the item unusable. DEFAULT: False /// Called when the player tries to use the item (right mouse button). Return false to make the item unusable. DEFAULT: False
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); 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);

View File

@ -19,17 +19,13 @@ public:
{ {
switch(m_ItemType) switch(m_ItemType)
{ {
case E_ITEM_WOODEN_PICKAXE: case E_ITEM_WOODEN_PICKAXE: return 1;
case E_ITEM_GOLD_PICKAXE: case E_ITEM_GOLD_PICKAXE: return 1;
return 1; case E_ITEM_STONE_PICKAXE: return 2;
case E_ITEM_STONE_PICKAXE: case E_ITEM_IRON_PICKAXE: return 3;
return 2; case E_ITEM_DIAMOND_PICKAXE: return 4;
case E_ITEM_IRON_PICKAXE:
return 3; default: return 0;
case E_ITEM_DIAMOND_PICKAXE:
return 4;
default:
return 0;
} }
} }
@ -61,6 +57,10 @@ public:
return PickaxeLevel() >= 2; return PickaxeLevel() >= 2;
} }
case E_BLOCK_ANVIL:
case E_BLOCK_ENCHANTMENT_TABLE:
case E_BLOCK_FURNACE:
case E_BLOCK_LIT_FURNACE:
case E_BLOCK_COAL_ORE: case E_BLOCK_COAL_ORE:
case E_BLOCK_STONE: case E_BLOCK_STONE:
case E_BLOCK_COBBLESTONE: case E_BLOCK_COBBLESTONE:

View File

@ -150,7 +150,7 @@ void cVillager::HandleFarmerTryHarvestCrops()
BLOCKTYPE CropBlock = m_World->GetBlock(m_CropsPos.x, m_CropsPos.y, m_CropsPos.z); BLOCKTYPE CropBlock = m_World->GetBlock(m_CropsPos.x, m_CropsPos.y, m_CropsPos.z);
if (IsBlockFarmable(CropBlock) && m_World->GetBlockMeta(m_CropsPos.x, m_CropsPos.y, m_CropsPos.z) == 0x7) if (IsBlockFarmable(CropBlock) && m_World->GetBlockMeta(m_CropsPos.x, m_CropsPos.y, m_CropsPos.z) == 0x7)
{ {
cBlockHandler * Handler = cBlockHandler::GetBlockHandler(CropBlock); cBlockHandler * Handler = cBlockInfo::GetHandler(CropBlock);
cChunkInterface ChunkInterface(m_World->GetChunkMap()); cChunkInterface ChunkInterface(m_World->GetChunkMap());
cBlockInServerPluginInterface PluginInterface(*m_World); cBlockInServerPluginInterface PluginInterface(*m_World);
Handler->DropBlock(ChunkInterface, *m_World, PluginInterface, this, m_CropsPos.x, m_CropsPos.y, m_CropsPos.z); Handler->DropBlock(ChunkInterface, *m_World, PluginInterface, this, m_CropsPos.x, m_CropsPos.y, m_CropsPos.z);

View File

@ -103,7 +103,7 @@ private:
public: public:
cSocketThread(cSocketThreads * a_Parent); cSocketThread(cSocketThreads * a_Parent);
~cSocketThread(); virtual ~cSocketThread();
// All these methods assume parent's m_CS is locked // All these methods assume parent's m_CS is locked
bool HasEmptySlot(void) const {return m_NumSlots < MAX_SLOTS; } bool HasEmptySlot(void) const {return m_NumSlots < MAX_SLOTS; }

View File

@ -52,7 +52,7 @@ public:
virtual ~cProtocol() {} virtual ~cProtocol() {}
/// Called when client sends some data /// Called when client sends some data
virtual void DataReceived(const char * a_Data, int a_Size) = 0; virtual void DataReceived(const char * a_Data, size_t a_Size) = 0;
// Sending stuff to clients (alphabetically sorted): // Sending stuff to clients (alphabetically sorted):
virtual void SendAttachEntity (const cEntity & a_Entity, const cEntity * a_Vehicle) = 0; virtual void SendAttachEntity (const cEntity & a_Entity, const cEntity * a_Vehicle) = 0;

View File

@ -1186,7 +1186,7 @@ void cProtocol125::SendData(const char * a_Data, int a_Size)
void cProtocol125::DataReceived(const char * a_Data, int a_Size) void cProtocol125::DataReceived(const char * a_Data, size_t a_Size)
{ {
if (!m_ReceivedData.Write(a_Data, a_Size)) if (!m_ReceivedData.Write(a_Data, a_Size))
{ {
@ -1375,7 +1375,16 @@ int cProtocol125::ParseEntityAction(void)
{ {
HANDLE_PACKET_READ(ReadBEInt, int, EntityID); HANDLE_PACKET_READ(ReadBEInt, int, EntityID);
HANDLE_PACKET_READ(ReadChar, char, ActionID); HANDLE_PACKET_READ(ReadChar, char, ActionID);
m_Client->HandleEntityAction(EntityID, ActionID);
switch (ActionID)
{
case 1: m_Client->HandleEntityCrouch(EntityID, true); break; // Crouch
case 2: m_Client->HandleEntityCrouch(EntityID, false); break; // Uncrouch
case 3: m_Client->HandleEntityLeaveBed(EntityID); break; // Leave Bed
case 4: m_Client->HandleEntitySprinting(EntityID, true); break; // Start sprinting
case 5: m_Client->HandleEntitySprinting(EntityID, false); break; // Stop sprinting
}
return PARSE_OK; return PARSE_OK;
} }

View File

@ -11,6 +11,7 @@
#include "Protocol.h" #include "Protocol.h"
#include "../ByteBuffer.h" #include "../ByteBuffer.h"
#include "../Entities/Painting.h"
@ -24,7 +25,7 @@ public:
cProtocol125(cClientHandle * a_Client); cProtocol125(cClientHandle * a_Client);
/// Called when client sends some data: /// Called when client sends some data:
virtual void DataReceived(const char * a_Data, int a_Size) override; virtual void DataReceived(const char * a_Data, size_t a_Size) override;
/// Sending stuff to clients (alphabetically sorted): /// Sending stuff to clients (alphabetically sorted):
virtual void SendAttachEntity (const cEntity & a_Entity, const cEntity * a_Vehicle) override; virtual void SendAttachEntity (const cEntity & a_Entity, const cEntity * a_Vehicle) override;
@ -57,9 +58,17 @@ public:
virtual void SendLogin (const cPlayer & a_Player, const cWorld & a_World) override; virtual void SendLogin (const cPlayer & a_Player, const cWorld & a_World) override;
virtual void SendMapColumn (int a_ID, int a_X, int a_Y, const Byte * a_Colors, unsigned int a_Length) override; virtual void SendMapColumn (int a_ID, int a_X, int a_Y, const Byte * a_Colors, unsigned int a_Length) override;
virtual void SendMapDecorators (int a_ID, const cMapDecoratorList & a_Decorators) override; virtual void SendMapDecorators (int a_ID, const cMapDecoratorList & a_Decorators) override;
virtual void SendMapInfo (int a_ID, unsigned int a_Scale) override {} // This protocol doesn't support such message virtual void SendMapInfo (int a_ID, unsigned int a_Scale) override
{
// This protocol doesn't support such message
UNUSED(a_ID);
UNUSED(a_Scale);
}
virtual void SendParticleEffect (const AString & a_ParticleName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmmount) override; virtual void SendParticleEffect (const AString & a_ParticleName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmmount) override;
virtual void SendPaintingSpawn (const cPainting & a_Painting) override {}; virtual void SendPaintingSpawn (const cPainting & a_Painting) override
{
UNUSED(a_Painting);
};
virtual void SendPickupSpawn (const cPickup & a_Pickup) override; virtual void SendPickupSpawn (const cPickup & a_Pickup) override;
virtual void SendPlayerAbilities (void) override {} // This protocol doesn't support such message virtual void SendPlayerAbilities (void) override {} // This protocol doesn't support such message
virtual void SendEntityAnimation (const cEntity & a_Entity, char a_Animation) override; virtual void SendEntityAnimation (const cEntity & a_Entity, char a_Animation) override;
@ -73,7 +82,12 @@ public:
virtual void SendRespawn (void) override; virtual void SendRespawn (void) override;
virtual void SendExperience (void) override; virtual void SendExperience (void) override;
virtual void SendExperienceOrb (const cExpOrb & a_ExpOrb) override; virtual void SendExperienceOrb (const cExpOrb & a_ExpOrb) override;
virtual void SendScoreboardObjective (const AString & a_Name, const AString & a_DisplayName, Byte a_Mode) override {} // This protocol doesn't support such message virtual void SendScoreboardObjective (const AString & a_Name, const AString & a_DisplayName, Byte a_Mode) override
{
UNUSED(a_Name);
UNUSED(a_DisplayName);
UNUSED(a_Mode);
} // This protocol doesn't support such message
virtual void SendScoreUpdate (const AString & a_Objective, const AString & a_Player, cObjective::Score a_Score, Byte a_Mode) override {} // This protocol doesn't support such message virtual void SendScoreUpdate (const AString & a_Objective, const AString & a_Player, cObjective::Score a_Score, Byte a_Mode) override {} // This protocol doesn't support such message
virtual void SendDisplayObjective (const AString & a_Objective, cScoreboard::eDisplaySlot a_Display) override {} // This protocol doesn't support such message virtual void SendDisplayObjective (const AString & a_Objective, cScoreboard::eDisplaySlot a_Display) override {} // This protocol doesn't support such message
virtual void SendSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) override; // a_Src coords are Block * 8 virtual void SendSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) override; // a_Src coords are Block * 8

View File

@ -108,7 +108,7 @@ cProtocol132::~cProtocol132()
void cProtocol132::DataReceived(const char * a_Data, int a_Size) void cProtocol132::DataReceived(const char * a_Data, size_t a_Size)
{ {
if (m_IsEncrypted) if (m_IsEncrypted)
{ {

View File

@ -40,7 +40,7 @@ public:
virtual ~cProtocol132(); virtual ~cProtocol132();
/// Called when client sends some data: /// Called when client sends some data:
virtual void DataReceived(const char * a_Data, int a_Size) override; virtual void DataReceived(const char * a_Data, size_t a_Size) override;
// Sending commands (alphabetically sorted): // Sending commands (alphabetically sorted):
virtual void SendBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) override; virtual void SendBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) override;

View File

@ -184,7 +184,16 @@ int cProtocol161::ParseEntityAction(void)
HANDLE_PACKET_READ(ReadBEInt, int, EntityID); HANDLE_PACKET_READ(ReadBEInt, int, EntityID);
HANDLE_PACKET_READ(ReadChar, char, ActionID); HANDLE_PACKET_READ(ReadChar, char, ActionID);
HANDLE_PACKET_READ(ReadBEInt, int, UnknownHorseVal); HANDLE_PACKET_READ(ReadBEInt, int, UnknownHorseVal);
m_Client->HandleEntityAction(EntityID, ActionID);
switch (ActionID)
{
case 1: m_Client->HandleEntityCrouch(EntityID, true); break; // Crouch
case 2: m_Client->HandleEntityCrouch(EntityID, false); break; // Uncrouch
case 3: m_Client->HandleEntityLeaveBed(EntityID); break; // Leave Bed
case 4: m_Client->HandleEntitySprinting(EntityID, true); break; // Start sprinting
case 5: m_Client->HandleEntitySprinting(EntityID, false); break; // Stop sprinting
}
return PARSE_OK; return PARSE_OK;
} }

View File

@ -29,6 +29,7 @@ Implements the 1.7.x protocol classes:
#include "../UI/Window.h" #include "../UI/Window.h"
#include "../BlockEntities/CommandBlockEntity.h" #include "../BlockEntities/CommandBlockEntity.h"
#include "../BlockEntities/MobHeadEntity.h" #include "../BlockEntities/MobHeadEntity.h"
#include "../BlockEntities/FlowerPotEntity.h"
#include "../CompositeChat.h" #include "../CompositeChat.h"
@ -97,7 +98,7 @@ cProtocol172::cProtocol172(cClientHandle * a_Client, const AString & a_ServerAdd
void cProtocol172::DataReceived(const char * a_Data, int a_Size) void cProtocol172::DataReceived(const char * a_Data, size_t a_Size)
{ {
if (m_IsEncrypted) if (m_IsEncrypted)
{ {
@ -1115,6 +1116,7 @@ void cProtocol172::SendUpdateBlockEntity(cBlockEntity & a_BlockEntity)
case E_BLOCK_MOB_SPAWNER: Action = 1; break; // Update mob spawner spinny mob thing case E_BLOCK_MOB_SPAWNER: Action = 1; break; // Update mob spawner spinny mob thing
case E_BLOCK_COMMAND_BLOCK: Action = 2; break; // Update command block text case E_BLOCK_COMMAND_BLOCK: Action = 2; break; // Update command block text
case E_BLOCK_HEAD: Action = 4; break; // Update Mobhead entity case E_BLOCK_HEAD: Action = 4; break; // Update Mobhead entity
case E_BLOCK_FLOWER_POT: Action = 5; break; // Update flower pot
default: ASSERT(!"Unhandled or unimplemented BlockEntity update request!"); break; default: ASSERT(!"Unhandled or unimplemented BlockEntity update request!"); break;
} }
Pkt.WriteByte(Action); Pkt.WriteByte(Action);
@ -1242,7 +1244,7 @@ void cProtocol172::AddReceivedData(const char * a_Data, int a_Size)
if (m_ReceivedData.GetReadableSpace() > 0) if (m_ReceivedData.GetReadableSpace() > 0)
{ {
AString AllData; AString AllData;
int OldReadableSpace = m_ReceivedData.GetReadableSpace(); size_t OldReadableSpace = m_ReceivedData.GetReadableSpace();
m_ReceivedData.ReadAll(AllData); m_ReceivedData.ReadAll(AllData);
m_ReceivedData.ResetRead(); m_ReceivedData.ResetRead();
m_ReceivedData.SkipRead(m_ReceivedData.GetReadableSpace() - OldReadableSpace); m_ReceivedData.SkipRead(m_ReceivedData.GetReadableSpace() - OldReadableSpace);
@ -1364,7 +1366,7 @@ void cProtocol172::AddReceivedData(const char * a_Data, int a_Size)
if (g_ShouldLogCommIn && (m_ReceivedData.GetReadableSpace() > 0)) if (g_ShouldLogCommIn && (m_ReceivedData.GetReadableSpace() > 0))
{ {
AString AllData; AString AllData;
int OldReadableSpace = m_ReceivedData.GetReadableSpace(); size_t OldReadableSpace = m_ReceivedData.GetReadableSpace();
m_ReceivedData.ReadAll(AllData); m_ReceivedData.ReadAll(AllData);
m_ReceivedData.ResetRead(); m_ReceivedData.ResetRead();
m_ReceivedData.SkipRead(m_ReceivedData.GetReadableSpace() - OldReadableSpace); m_ReceivedData.SkipRead(m_ReceivedData.GetReadableSpace() - OldReadableSpace);
@ -1730,7 +1732,15 @@ void cProtocol172::HandlePacketEntityAction(cByteBuffer & a_ByteBuffer)
HANDLE_READ(a_ByteBuffer, ReadBEInt, int, PlayerID); HANDLE_READ(a_ByteBuffer, ReadBEInt, int, PlayerID);
HANDLE_READ(a_ByteBuffer, ReadByte, Byte, Action); HANDLE_READ(a_ByteBuffer, ReadByte, Byte, Action);
HANDLE_READ(a_ByteBuffer, ReadBEInt, int, JumpBoost); HANDLE_READ(a_ByteBuffer, ReadBEInt, int, JumpBoost);
m_Client->HandleEntityAction(PlayerID, Action);
switch (Action)
{
case 1: m_Client->HandleEntityCrouch(PlayerID, true); break; // Crouch
case 2: m_Client->HandleEntityCrouch(PlayerID, false); break; // Uncrouch
case 3: m_Client->HandleEntityLeaveBed(PlayerID); break; // Leave Bed
case 4: m_Client->HandleEntitySprinting(PlayerID, true); break; // Start sprinting
case 5: m_Client->HandleEntitySprinting(PlayerID, false); break; // Stop sprinting
}
} }
@ -2345,7 +2355,7 @@ void cProtocol172::cPacketizer::WriteBlockEntity(const cBlockEntity & a_BlockEnt
case E_BLOCK_HEAD: case E_BLOCK_HEAD:
{ {
cMobHeadEntity & MobHeadEntity = (cMobHeadEntity &)a_BlockEntity; cMobHeadEntity & MobHeadEntity = (cMobHeadEntity &)a_BlockEntity;
Writer.AddInt("x", MobHeadEntity.GetPosX()); Writer.AddInt("x", MobHeadEntity.GetPosX());
Writer.AddInt("y", MobHeadEntity.GetPosY()); Writer.AddInt("y", MobHeadEntity.GetPosY());
Writer.AddInt("z", MobHeadEntity.GetPosZ()); Writer.AddInt("z", MobHeadEntity.GetPosZ());
@ -2355,6 +2365,18 @@ void cProtocol172::cPacketizer::WriteBlockEntity(const cBlockEntity & a_BlockEnt
Writer.AddString("id", "Skull"); // "Tile Entity ID" - MC wiki; vanilla server always seems to send this though Writer.AddString("id", "Skull"); // "Tile Entity ID" - MC wiki; vanilla server always seems to send this though
break; break;
} }
case E_BLOCK_FLOWER_POT:
{
cFlowerPotEntity & FlowerPotEntity = (cFlowerPotEntity &)a_BlockEntity;
Writer.AddInt("x", FlowerPotEntity.GetPosX());
Writer.AddInt("y", FlowerPotEntity.GetPosY());
Writer.AddInt("z", FlowerPotEntity.GetPosZ());
Writer.AddInt("Item", (Int32) FlowerPotEntity.GetItem().m_ItemType);
Writer.AddInt("Data", (Int32) FlowerPotEntity.GetItem().m_ItemDamage);
Writer.AddString("id", "FlowerPot"); // "Tile Entity ID" - MC wiki; vanilla server always seems to send this though
break;
}
default: break; default: break;
} }

View File

@ -56,7 +56,7 @@ public:
cProtocol172(cClientHandle * a_Client, const AString & a_ServerAddress, UInt16 a_ServerPort, UInt32 a_State); cProtocol172(cClientHandle * a_Client, const AString & a_ServerAddress, UInt16 a_ServerPort, UInt32 a_State);
/** Called when client sends some data: */ /** Called when client sends some data: */
virtual void DataReceived(const char * a_Data, int a_Size) override; virtual void DataReceived(const char * a_Data, size_t a_Size) override;
/** Sending stuff to clients (alphabetically sorted): */ /** Sending stuff to clients (alphabetically sorted): */
virtual void SendAttachEntity (const cEntity & a_Entity, const cEntity * a_Vehicle) override; virtual void SendAttachEntity (const cEntity & a_Entity, const cEntity * a_Vehicle) override;

View File

@ -68,7 +68,7 @@ AString cProtocolRecognizer::GetVersionTextFromInt(int a_ProtocolVersion)
void cProtocolRecognizer::DataReceived(const char * a_Data, int a_Size) void cProtocolRecognizer::DataReceived(const char * a_Data, size_t a_Size)
{ {
if (m_Protocol == NULL) if (m_Protocol == NULL)
{ {

View File

@ -59,7 +59,7 @@ public:
static AString GetVersionTextFromInt(int a_ProtocolVersion); static AString GetVersionTextFromInt(int a_ProtocolVersion);
/// Called when client sends some data: /// Called when client sends some data:
virtual void DataReceived(const char * a_Data, int a_Size) override; virtual void DataReceived(const char * a_Data, size_t a_Size) override;
/// Sending stuff to clients (alphabetically sorted): /// Sending stuff to clients (alphabetically sorted):
virtual void SendAttachEntity (const cEntity & a_Entity, const cEntity * a_Vehicle) override; virtual void SendAttachEntity (const cEntity & a_Entity, const cEntity * a_Vehicle) override;

View File

@ -245,7 +245,6 @@ void cRoot::Start(void)
delete m_PluginManager; m_PluginManager = NULL; delete m_PluginManager; m_PluginManager = NULL;
cItemHandler::Deinit(); cItemHandler::Deinit();
cBlockHandler::Deinit();
LOG("Cleaning up..."); LOG("Cleaning up...");
delete m_Server; m_Server = NULL; delete m_Server; m_Server = NULL;
@ -594,7 +593,6 @@ bool cRoot::FindAndDoWithPlayer(const AString & a_PlayerName, cPlayerListCallbac
unsigned m_NameLength; unsigned m_NameLength;
const AString m_PlayerName; const AString m_PlayerName;
cPlayerListCallback & m_Callback;
virtual bool Item (cPlayer * a_pPlayer) virtual bool Item (cPlayer * a_pPlayer)
{ {
unsigned int Rating = RateCompareString (m_PlayerName, a_pPlayer->GetName()); unsigned int Rating = RateCompareString (m_PlayerName, a_pPlayer->GetName());
@ -616,18 +614,17 @@ bool cRoot::FindAndDoWithPlayer(const AString & a_PlayerName, cPlayerListCallbac
} }
public: public:
cCallback (const AString & a_PlayerName, cPlayerListCallback & a_Callback) : cCallback (const AString & a_PlayerName) :
m_BestRating(0), m_BestRating(0),
m_NameLength(a_PlayerName.length()), m_NameLength(a_PlayerName.length()),
m_PlayerName(a_PlayerName), m_PlayerName(a_PlayerName),
m_Callback(a_Callback),
m_BestMatch(NULL), m_BestMatch(NULL),
m_NumMatches(0) m_NumMatches(0)
{} {}
cPlayer * m_BestMatch; cPlayer * m_BestMatch;
unsigned m_NumMatches; unsigned m_NumMatches;
} Callback (a_PlayerName, a_Callback); } Callback (a_PlayerName);
ForEachPlayer( Callback ); ForEachPlayer( Callback );
if (Callback.m_NumMatches == 1) if (Callback.m_NumMatches == 1)

View File

@ -312,12 +312,18 @@ bool cScoreboard::RemoveObjective(const AString & a_Name)
return false; return false;
} }
m_Objectives.erase(it);
ASSERT(m_World != NULL); ASSERT(m_World != NULL);
m_World->BroadcastScoreboardObjective(it->second.GetName(), it->second.GetDisplayName(), 1); m_World->BroadcastScoreboardObjective(it->second.GetName(), it->second.GetDisplayName(), 1);
// TODO 2014-03-01 xdot: Remove objective from display slot for (unsigned int i = 0; i < (unsigned int) dsCount; ++i)
{
if (m_Display[i] == &it->second)
{
SetDisplay(NULL, (eDisplaySlot) i);
}
}
m_Objectives.erase(it);
return true; return true;
} }
@ -500,7 +506,7 @@ bool cScoreboard::ForEachObjective(cObjectiveCallback& a_Callback)
bool cScoreboard::ForEachTeam(cTeamCallback& a_Callback) bool cScoreboard::ForEachTeam(cTeamCallback& a_Callback)
{ {
cCSLock Lock(m_CSObjectives); cCSLock Lock(m_CSTeams);
for (cTeamMap::iterator it = m_Teams.begin(); it != m_Teams.end(); ++it) for (cTeamMap::iterator it = m_Teams.begin(); it != m_Teams.end(); ++it)
{ {

View File

@ -20,7 +20,7 @@
bool cDelayedFluidSimulatorChunkData::cSlot::Add(int a_RelX, int a_RelY, int a_RelZ) bool cDelayedFluidSimulatorChunkData::cSlot::Add(int a_RelX, int a_RelY, int a_RelZ)
{ {
ASSERT(a_RelZ >= 0); ASSERT(a_RelZ >= 0);
ASSERT(a_RelZ < ARRAYCOUNT(m_Blocks)); ASSERT(a_RelZ < static_cast<int>(ARRAYCOUNT(m_Blocks)));
cCoordWithIntVector & Blocks = m_Blocks[a_RelZ]; cCoordWithIntVector & Blocks = m_Blocks[a_RelZ];
int Index = cChunkDef::MakeIndexNoCheck(a_RelX, a_RelY, a_RelZ); int Index = cChunkDef::MakeIndexNoCheck(a_RelX, a_RelY, a_RelZ);

View File

@ -54,14 +54,23 @@ void cFloodyFluidSimulator::SimulateBlock(cChunk * a_Chunk, int a_RelX, int a_Re
a_Chunk->GetMeta(a_RelX, a_RelY, a_RelZ) a_Chunk->GetMeta(a_RelX, a_RelY, a_RelZ)
); );
NIBBLETYPE MyMeta = a_Chunk->GetMeta(a_RelX, a_RelY, a_RelZ); BLOCKTYPE MyBlock; NIBBLETYPE MyMeta;
if (!IsAnyFluidBlock(a_Chunk->GetBlock(a_RelX, a_RelY, a_RelZ))) a_Chunk->GetBlockTypeMeta(a_RelX, a_RelY, a_RelZ, MyBlock, MyMeta);
if (!IsAnyFluidBlock(MyBlock))
{ {
// Can happen - if a block is scheduled for simulating and gets replaced in the meantime. // Can happen - if a block is scheduled for simulating and gets replaced in the meantime.
FLOG(" BadBlockType exit"); FLOG(" BadBlockType exit");
return; return;
} }
// When in contact with water, lava should harden
if (HardenBlock(a_Chunk, a_RelX, a_RelY, a_RelZ, MyBlock, MyMeta))
{
// Block was changed, bail out
return;
}
if (MyMeta != 0) if (MyMeta != 0)
{ {
// Source blocks aren't checked for tributaries, others are. // Source blocks aren't checked for tributaries, others are.
@ -86,7 +95,12 @@ void cFloodyFluidSimulator::SimulateBlock(cChunk * a_Chunk, int a_RelX, int a_Re
{ {
// Spread only down, possibly washing away what's there or turning lava to stone / cobble / obsidian: // Spread only down, possibly washing away what's there or turning lava to stone / cobble / obsidian:
SpreadToNeighbor(a_Chunk, a_RelX, a_RelY - 1, a_RelZ, 8); SpreadToNeighbor(a_Chunk, a_RelX, a_RelY - 1, a_RelZ, 8);
SpreadFurther = false;
// Source blocks spread both downwards and sideways
if (MyMeta != 0)
{
SpreadFurther = false;
}
} }
// If source creation is on, check for it here: // If source creation is on, check for it here:
else if ( else if (
@ -105,10 +119,7 @@ void cFloodyFluidSimulator::SimulateBlock(cChunk * a_Chunk, int a_RelX, int a_Re
if (SpreadFurther && (NewMeta < 8)) if (SpreadFurther && (NewMeta < 8))
{ {
// Spread to the neighbors: // Spread to the neighbors:
SpreadToNeighbor(a_Chunk, a_RelX - 1, a_RelY, a_RelZ, NewMeta); Spread(a_Chunk, a_RelX, a_RelY, a_RelZ, NewMeta);
SpreadToNeighbor(a_Chunk, a_RelX + 1, a_RelY, a_RelZ, NewMeta);
SpreadToNeighbor(a_Chunk, a_RelX, a_RelY, a_RelZ - 1, NewMeta);
SpreadToNeighbor(a_Chunk, a_RelX, a_RelY, a_RelZ + 1, NewMeta);
} }
// Mark as processed: // Mark as processed:
@ -119,6 +130,17 @@ void cFloodyFluidSimulator::SimulateBlock(cChunk * a_Chunk, int a_RelX, int a_Re
void cFloodyFluidSimulator::Spread(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_NewMeta)
{
SpreadToNeighbor(a_Chunk, a_RelX - 1, a_RelY, a_RelZ, a_NewMeta);
SpreadToNeighbor(a_Chunk, a_RelX + 1, a_RelY, a_RelZ, a_NewMeta);
SpreadToNeighbor(a_Chunk, a_RelX, a_RelY, a_RelZ - 1, a_NewMeta);
SpreadToNeighbor(a_Chunk, a_RelX, a_RelY, a_RelZ + 1, a_NewMeta);
}
bool cFloodyFluidSimulator::CheckTributaries(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_MyMeta) bool cFloodyFluidSimulator::CheckTributaries(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_MyMeta)
{ {
// If we have a section above, check if there's fluid above this block that would feed it: // If we have a section above, check if there's fluid above this block that would feed it:
@ -296,6 +318,8 @@ void cFloodyFluidSimulator::SpreadToNeighbor(cChunk * a_NearChunk, int a_RelX, i
a_NewMeta a_NewMeta
); );
a_NearChunk->UnboundedRelSetBlock(a_RelX, a_RelY, a_RelZ, m_FluidBlock, a_NewMeta); a_NearChunk->UnboundedRelSetBlock(a_RelX, a_RelY, a_RelZ, m_FluidBlock, a_NewMeta);
HardenBlock(a_NearChunk, a_RelX, a_RelY, a_RelZ, m_FluidBlock, a_NewMeta);
} }
@ -348,3 +372,56 @@ bool cFloodyFluidSimulator::CheckNeighborsForSource(cChunk * a_Chunk, int a_RelX
bool cFloodyFluidSimulator::HardenBlock(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_Meta)
{
// Only lava blocks can harden
if (!IsBlockLava(a_BlockType))
{
return false;
}
bool ShouldHarden = false;
BLOCKTYPE BlockType;
NIBBLETYPE BlockMeta;
static const Vector3i Coords[] =
{
Vector3i( 1, 0, 0),
Vector3i(-1, 0, 0),
Vector3i( 0, 0, 1),
Vector3i( 0, 0, -1),
};
for (size_t i = 0; i < ARRAYCOUNT(Coords); i++)
{
if (!a_Chunk->UnboundedRelGetBlock(a_RelX + Coords[i].x, a_RelY, a_RelZ + Coords[i].z, BlockType, BlockMeta))
{
continue;
}
if (IsBlockWater(BlockType))
{
ShouldHarden = true;
}
} // for i - Coords[]
if (ShouldHarden)
{
if (a_Meta == 0)
{
// Source lava block
a_Chunk->UnboundedRelSetBlock(a_RelX, a_RelY, a_RelZ, E_BLOCK_OBSIDIAN, 0);
return true;
}
// Ignore last lava level
else if (a_Meta <= 4)
{
a_Chunk->UnboundedRelSetBlock(a_RelX, a_RelY, a_RelZ, E_BLOCK_COBBLESTONE, 0);
return true;
}
}
return false;
}

View File

@ -38,14 +38,26 @@ protected:
// cDelayedFluidSimulator overrides: // cDelayedFluidSimulator overrides:
virtual void SimulateBlock(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ) override; virtual void SimulateBlock(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ) override;
/// Checks tributaries, if not fed, decreases the block's level and returns true /** Checks tributaries, if not fed, decreases the block's level and returns true. */
bool CheckTributaries(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_MyMeta); bool CheckTributaries(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_MyMeta);
/// Spreads into the specified block, if the blocktype there allows. a_Area is for checking. /** Spreads into the specified block, if the blocktype there allows. a_Area is for checking. */
void SpreadToNeighbor(cChunk * a_NearChunk, int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_NewMeta); void SpreadToNeighbor(cChunk * a_NearChunk, int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_NewMeta);
/// Checks if there are enough neighbors to create a source at the coords specified; turns into source and returns true if so /** Checks if there are enough neighbors to create a source at the coords specified; turns into source and returns true if so. */
bool CheckNeighborsForSource(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ); bool CheckNeighborsForSource(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ);
/** Checks if the specified block should harden (Water/Lava interaction) and if so, converts it to a suitable block.
*
* Returns whether the block was changed or not.
*/
bool HardenBlock(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_Meta);
/** Spread water to neighbors.
*
* May be overridden to provide more sophisticated algorithms.
*/
virtual void Spread(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_NewMeta);
} ; } ;

View File

@ -937,17 +937,15 @@ void cIncrementalRedstoneSimulator::HandleTrapdoor(int a_BlockX, int a_BlockY, i
{ {
if (!AreCoordsSimulated(a_BlockX, a_BlockY, a_BlockZ, true)) if (!AreCoordsSimulated(a_BlockX, a_BlockY, a_BlockZ, true))
{ {
m_World.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, m_World.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) | 0x4); m_World.SetTrapdoorOpen(a_BlockX, a_BlockY, a_BlockZ, true);
m_World.BroadcastSoundParticleEffect(1003, a_BlockX, a_BlockY, a_BlockZ, 0);
SetPlayerToggleableBlockAsSimulated(a_BlockX, a_BlockY, a_BlockZ, true); SetPlayerToggleableBlockAsSimulated(a_BlockX, a_BlockY, a_BlockZ, true);
} }
} }
else else
{ {
if (!AreCoordsSimulated(a_BlockX, a_BlockY, a_BlockZ, false)) if (!AreCoordsSimulated(a_BlockX, a_BlockY, a_BlockZ, false))
{ {
m_World.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, m_World.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) & 0xB); // Take into account that the fourth bit is needed for trapdoors too m_World.SetTrapdoorOpen(a_BlockX, a_BlockY, a_BlockZ, false);
m_World.BroadcastSoundParticleEffect(1003, a_BlockX, a_BlockY, a_BlockZ, 0);
SetPlayerToggleableBlockAsSimulated(a_BlockX, a_BlockY, a_BlockZ, false); SetPlayerToggleableBlockAsSimulated(a_BlockX, a_BlockY, a_BlockZ, false);
} }
} }

View File

@ -0,0 +1,150 @@
// VanillaFluidSimulator.cpp
#include "Globals.h"
#include "VanillaFluidSimulator.h"
#include "../World.h"
#include "../Chunk.h"
#include "../BlockArea.h"
#include "../Blocks/BlockHandler.h"
#include "../BlockInServerPluginInterface.h"
static const int InfiniteCost = 100;
cVanillaFluidSimulator::cVanillaFluidSimulator(
cWorld & a_World,
BLOCKTYPE a_Fluid,
BLOCKTYPE a_StationaryFluid,
NIBBLETYPE a_Falloff,
int a_TickDelay,
int a_NumNeighborsForSource
) : super(a_World, a_Fluid, a_StationaryFluid, a_Falloff, a_TickDelay, a_NumNeighborsForSource)
{
}
void cVanillaFluidSimulator::Spread(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_NewMeta)
{
int Cost[4];
Cost[0] = CalculateFlowCost(a_Chunk, a_RelX + 1, a_RelY, a_RelZ, X_PLUS);
Cost[1] = CalculateFlowCost(a_Chunk, a_RelX - 1, a_RelY, a_RelZ, X_MINUS);
Cost[2] = CalculateFlowCost(a_Chunk, a_RelX, a_RelY, a_RelZ + 1, Z_PLUS);
Cost[3] = CalculateFlowCost(a_Chunk, a_RelX, a_RelY, a_RelZ - 1, Z_MINUS);
int MinCost = InfiniteCost;
for (unsigned int i = 0; i < ARRAYCOUNT(Cost); ++i)
{
if (Cost[i] < MinCost)
{
MinCost = Cost[i];
}
}
if (Cost[0] == MinCost)
{
SpreadToNeighbor(a_Chunk, a_RelX + 1, a_RelY, a_RelZ, a_NewMeta);
}
if (Cost[1] == MinCost)
{
SpreadToNeighbor(a_Chunk, a_RelX - 1, a_RelY, a_RelZ, a_NewMeta);
}
if (Cost[2] == MinCost)
{
SpreadToNeighbor(a_Chunk, a_RelX, a_RelY, a_RelZ + 1, a_NewMeta);
}
if (Cost[3] == MinCost)
{
SpreadToNeighbor(a_Chunk, a_RelX, a_RelY, a_RelZ - 1, a_NewMeta);
}
}
int cVanillaFluidSimulator::CalculateFlowCost(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ, Direction a_Dir, unsigned a_Iteration)
{
int Cost = InfiniteCost;
BLOCKTYPE BlockType;
NIBBLETYPE BlockMeta;
// Check if block is passable
if (!a_Chunk->UnboundedRelGetBlock(a_RelX, a_RelY, a_RelZ, BlockType, BlockMeta))
{
return Cost;
}
if (!IsPassableForFluid(BlockType) && !IsBlockLiquid(BlockType))
{
return Cost;
}
// Check if block below is passable
if (!a_Chunk->UnboundedRelGetBlock(a_RelX, a_RelY - 1, a_RelZ, BlockType, BlockMeta))
{
return Cost;
}
if (IsPassableForFluid(BlockType) || IsBlockLiquid(BlockType))
{
// Path found, exit
return a_Iteration;
}
// 5 blocks away, bail out
if (a_Iteration > 3)
{
return Cost;
}
// Recurse
if (a_Dir != X_MINUS)
{
int NextCost = CalculateFlowCost(a_Chunk, a_RelX + 1, a_RelY, a_RelZ, X_PLUS, a_Iteration + 1);
if (NextCost < Cost)
{
Cost = NextCost;
}
}
if (a_Dir != X_PLUS)
{
int NextCost = CalculateFlowCost(a_Chunk, a_RelX - 1, a_RelY, a_RelZ, X_MINUS, a_Iteration + 1);
if (NextCost < Cost)
{
Cost = NextCost;
}
}
if (a_Dir != Z_MINUS)
{
int NextCost = CalculateFlowCost(a_Chunk, a_RelX, a_RelY, a_RelZ + 1, Z_PLUS, a_Iteration + 1);
if (NextCost < Cost)
{
Cost = NextCost;
}
}
if (a_Dir != Z_PLUS)
{
int NextCost = CalculateFlowCost(a_Chunk, a_RelX, a_RelY, a_RelZ - 1, Z_MINUS, a_Iteration + 1);
if (NextCost < Cost)
{
Cost = NextCost;
}
}
return Cost;
}

View File

@ -0,0 +1,42 @@
// VanillaFluidSimulator.h
#pragma once
#include "FloodyFluidSimulator.h"
// fwd:
class cBlockArea;
class cVanillaFluidSimulator :
public cFloodyFluidSimulator
{
typedef cFloodyFluidSimulator super;
public:
cVanillaFluidSimulator(cWorld & a_World, BLOCKTYPE a_Fluid, BLOCKTYPE a_StationaryFluid, NIBBLETYPE a_Falloff, int a_TickDelay, int a_NumNeighborsForSource);
protected:
// cFloodyFluidSimulator overrides:
virtual void Spread(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_NewMeta) override;
/** Recursively calculates the minimum number of blocks needed to descend a level. */
int CalculateFlowCost(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ, Direction a_Dir, unsigned a_Iteration = 0);
} ;

View File

@ -34,6 +34,7 @@
#include "Simulator/NoopRedstoneSimulator.h" #include "Simulator/NoopRedstoneSimulator.h"
#include "Simulator/SandSimulator.h" #include "Simulator/SandSimulator.h"
#include "Simulator/IncrementalRedstoneSimulator.h" #include "Simulator/IncrementalRedstoneSimulator.h"
#include "Simulator/VanillaFluidSimulator.h"
#include "Simulator/VaporizeFluidSimulator.h" #include "Simulator/VaporizeFluidSimulator.h"
// Mobs: // Mobs:
@ -262,8 +263,6 @@ cWorld::cWorld(const AString & a_WorldName) :
// Load the scoreboard // Load the scoreboard
cScoreboardSerializer Serializer(m_WorldName, &m_Scoreboard); cScoreboardSerializer Serializer(m_WorldName, &m_Scoreboard);
Serializer.Load(); Serializer.Load();
m_MapManager.LoadMapData();
} }
@ -305,25 +304,52 @@ void cWorld::CastThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ)
int cWorld::GetDefaultWeatherInterval(eWeather a_Weather)
{
switch (a_Weather)
{
case eWeather_Sunny:
{
return 14400 + (m_TickRand.randInt() % 4800); // 12 - 16 minutes
}
case eWeather_Rain:
{
return 9600 + (m_TickRand.randInt() % 7200); // 8 - 14 minutes
}
case eWeather_ThunderStorm:
{
return 2400 + (m_TickRand.randInt() % 4800); // 2 - 6 minutes
}
default:
{
LOGWARNING("%s: Missing default weather interval for weather %d.", __FUNCTION__, a_Weather);
return -1;
}
} // switch (Weather)
}
void cWorld::SetWeather(eWeather a_NewWeather) void cWorld::SetWeather(eWeather a_NewWeather)
{ {
// Do the plugins agree? Do they want a different weather? // Do the plugins agree? Do they want a different weather?
cRoot::Get()->GetPluginManager()->CallHookWeatherChanging(*this, a_NewWeather); if (cRoot::Get()->GetPluginManager()->CallHookWeatherChanging(*this, a_NewWeather))
{
m_WeatherInterval = GetDefaultWeatherInterval(m_Weather);
return;
}
// Set new period for the selected weather: // Set new period for the selected weather:
switch (a_NewWeather) m_WeatherInterval = GetDefaultWeatherInterval(a_NewWeather);
// The weather can't be found:
if (m_WeatherInterval < 0)
{ {
case eWeather_Sunny: m_WeatherInterval = 14400 + (m_TickRand.randInt() % 4800); break; // 12 - 16 minutes return;
case eWeather_Rain: m_WeatherInterval = 9600 + (m_TickRand.randInt() % 7200); break; // 8 - 14 minutes }
case eWeather_ThunderStorm: m_WeatherInterval = 2400 + (m_TickRand.randInt() % 4800); break; // 2 - 6 minutes
default:
{
LOGWARNING("Requested unknown weather %d, setting sunny for a minute instead.", a_NewWeather);
a_NewWeather = eWeather_Sunny;
m_WeatherInterval = 1200;
break;
}
} // switch (NewWeather)
m_Weather = a_NewWeather; m_Weather = a_NewWeather;
BroadcastWeather(m_Weather); BroadcastWeather(m_Weather);
@ -624,13 +650,13 @@ void cWorld::Start(void)
m_LastSpawnMonster.insert(std::map<cMonster::eFamily, Int64>::value_type(cMonster::mfAmbient, 0)); m_LastSpawnMonster.insert(std::map<cMonster::eFamily, Int64>::value_type(cMonster::mfAmbient, 0));
m_LastSpawnMonster.insert(std::map<cMonster::eFamily, Int64>::value_type(cMonster::mfWater, 0)); m_LastSpawnMonster.insert(std::map<cMonster::eFamily, Int64>::value_type(cMonster::mfWater, 0));
m_MapManager.LoadMapData();
// Save any changes that the defaults may have done to the ini file: // Save any changes that the defaults may have done to the ini file:
if (!IniFile.WriteFile(m_IniFileName)) if (!IniFile.WriteFile(m_IniFileName))
{ {
LOGWARNING("Could not write world config to %s", m_IniFileName.c_str()); LOGWARNING("Could not write world config to %s", m_IniFileName.c_str());
} }
} }
@ -1171,9 +1197,18 @@ bool cWorld::DoWithCommandBlockAt(int a_BlockX, int a_BlockY, int a_BlockZ, cCom
bool cWorld::DoWithMobHeadBlockAt(int a_BlockX, int a_BlockY, int a_BlockZ, cMobHeadBlockCallback & a_Callback) bool cWorld::DoWithMobHeadAt(int a_BlockX, int a_BlockY, int a_BlockZ, cMobHeadCallback & a_Callback)
{ {
return m_ChunkMap->DoWithMobHeadBlockAt(a_BlockX, a_BlockY, a_BlockZ, a_Callback); return m_ChunkMap->DoWithMobHeadAt(a_BlockX, a_BlockY, a_BlockZ, a_Callback);
}
bool cWorld::DoWithFlowerPotAt(int a_BlockX, int a_BlockY, int a_BlockZ, cFlowerPotCallback & a_Callback)
{
return m_ChunkMap->DoWithFlowerPotAt(a_BlockX, a_BlockY, a_BlockZ, a_Callback);
} }
@ -1726,7 +1761,7 @@ bool cWorld::GetBlocks(sSetBlockVector & a_Blocks, bool a_ContinueOnFailure)
bool cWorld::DigBlock(int a_X, int a_Y, int a_Z) bool cWorld::DigBlock(int a_X, int a_Y, int a_Z)
{ {
cBlockHandler *Handler = cBlockHandler::GetBlockHandler(GetBlock(a_X, a_Y, a_Z)); cBlockHandler * Handler = cBlockInfo::GetHandler(GetBlock(a_X, a_Y, a_Z));
cChunkInterface ChunkInterface(GetChunkMap()); cChunkInterface ChunkInterface(GetChunkMap());
Handler->OnDestroyed(ChunkInterface, *this, a_X, a_Y, a_Z); Handler->OnDestroyed(ChunkInterface, *this, a_X, a_Y, a_Z);
return m_ChunkMap->DigBlock(a_X, a_Y, a_Z); return m_ChunkMap->DigBlock(a_X, a_Y, a_Z);
@ -2650,6 +2685,47 @@ bool cWorld::SetCommandBlockCommand(int a_BlockX, int a_BlockY, int a_BlockZ, co
bool cWorld::IsTrapdoorOpen(int a_BlockX, int a_BlockY, int a_BlockZ)
{
BLOCKTYPE Block;
NIBBLETYPE Meta;
GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, Block, Meta);
if (Block != E_BLOCK_TRAPDOOR)
{
return false;
}
return (Meta & 0x4) > 0;
}
bool cWorld::SetTrapdoorOpen(int a_BlockX, int a_BlockY, int a_BlockZ, bool a_Open)
{
BLOCKTYPE Block;
NIBBLETYPE Meta;
GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, Block, Meta);
if (Block != E_BLOCK_TRAPDOOR)
{
return false;
}
bool IsOpen = (Meta & 0x4) > 0;
if (a_Open != IsOpen)
{
SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, Meta ^ 0x4);
BroadcastSoundParticleEffect(1003, a_BlockX, a_BlockY, a_BlockZ, 0);
return true;
}
return false;
}
void cWorld::RegenerateChunk(int a_ChunkX, int a_ChunkZ) void cWorld::RegenerateChunk(int a_ChunkX, int a_ChunkZ)
{ {
m_ChunkMap->MarkChunkRegenerating(a_ChunkX, a_ChunkZ); m_ChunkMap->MarkChunkRegenerating(a_ChunkX, a_ChunkZ);
@ -2990,8 +3066,8 @@ cFluidSimulator * cWorld::InitializeFluidSimulator(cIniFile & a_IniFile, const c
AString SimulatorName = a_IniFile.GetValueSet("Physics", SimulatorNameKey, ""); AString SimulatorName = a_IniFile.GetValueSet("Physics", SimulatorNameKey, "");
if (SimulatorName.empty()) if (SimulatorName.empty())
{ {
LOGWARNING("[Physics] %s not present or empty in %s, using the default of \"Floody\".", SimulatorNameKey.c_str(), GetIniFileName().c_str()); LOGWARNING("[Physics] %s not present or empty in %s, using the default of \"Vanilla\".", SimulatorNameKey.c_str(), GetIniFileName().c_str());
SimulatorName = "Floody"; SimulatorName = "Vanilla";
} }
cFluidSimulator * res = NULL; cFluidSimulator * res = NULL;
@ -3015,15 +3091,24 @@ cFluidSimulator * cWorld::InitializeFluidSimulator(cIniFile & a_IniFile, const c
} }
else else
{ {
if (NoCaseCompare(SimulatorName, "floody") != 0)
{
// The simulator name doesn't match anything we have, issue a warning:
LOGWARNING("%s [Physics]:%s specifies an unknown simulator, using the default \"Floody\".", GetIniFileName().c_str(), SimulatorNameKey.c_str());
}
int Falloff = a_IniFile.GetValueSetI(SimulatorSectionName, "Falloff", IsWater ? 1 : 2); int Falloff = a_IniFile.GetValueSetI(SimulatorSectionName, "Falloff", IsWater ? 1 : 2);
int TickDelay = a_IniFile.GetValueSetI(SimulatorSectionName, "TickDelay", IsWater ? 5 : 30); int TickDelay = a_IniFile.GetValueSetI(SimulatorSectionName, "TickDelay", IsWater ? 5 : 30);
int NumNeighborsForSource = a_IniFile.GetValueSetI(SimulatorSectionName, "NumNeighborsForSource", IsWater ? 2 : -1); int NumNeighborsForSource = a_IniFile.GetValueSetI(SimulatorSectionName, "NumNeighborsForSource", IsWater ? 2 : -1);
res = new cFloodyFluidSimulator(*this, a_SimulateBlock, a_StationaryBlock, Falloff, TickDelay, NumNeighborsForSource);
if (NoCaseCompare(SimulatorName, "floody") == 0)
{
res = new cFloodyFluidSimulator(*this, a_SimulateBlock, a_StationaryBlock, Falloff, TickDelay, NumNeighborsForSource);
}
else if (NoCaseCompare(SimulatorName, "vanilla") == 0)
{
res = new cVanillaFluidSimulator(*this, a_SimulateBlock, a_StationaryBlock, Falloff, TickDelay, NumNeighborsForSource);
}
else
{
// The simulator name doesn't match anything we have, issue a warning:
LOGWARNING("%s [Physics]:%s specifies an unknown simulator, using the default \"Vanilla\".", GetIniFileName().c_str(), SimulatorNameKey.c_str());
res = new cVanillaFluidSimulator(*this, a_SimulateBlock, a_StationaryBlock, Falloff, TickDelay, NumNeighborsForSource);
}
} }
m_SimulatorManager->RegisterSimulator(res, Rate); m_SimulatorManager->RegisterSimulator(res, Rate);

View File

@ -44,6 +44,7 @@ class cWorldGenerator; // The generator that actually generates the chunks for
class cChunkGenerator; // The thread responsible for generating chunks class cChunkGenerator; // The thread responsible for generating chunks
class cChestEntity; class cChestEntity;
class cDispenserEntity; class cDispenserEntity;
class cFlowerPotEntity;
class cFurnaceEntity; class cFurnaceEntity;
class cNoteEntity; class cNoteEntity;
class cMobHeadEntity; class cMobHeadEntity;
@ -60,7 +61,8 @@ typedef cItemCallback<cDispenserEntity> cDispenserCallback;
typedef cItemCallback<cFurnaceEntity> cFurnaceCallback; typedef cItemCallback<cFurnaceEntity> cFurnaceCallback;
typedef cItemCallback<cNoteEntity> cNoteBlockCallback; typedef cItemCallback<cNoteEntity> cNoteBlockCallback;
typedef cItemCallback<cCommandBlockEntity> cCommandBlockCallback; typedef cItemCallback<cCommandBlockEntity> cCommandBlockCallback;
typedef cItemCallback<cMobHeadEntity> cMobHeadBlockCallback; typedef cItemCallback<cMobHeadEntity> cMobHeadCallback;
typedef cItemCallback<cFlowerPotEntity> cFlowerPotCallback;
@ -139,6 +141,10 @@ public:
BroadcastTimeUpdate(); BroadcastTimeUpdate();
} }
/** Returns the default weather interval for the specific weather type.
Returns -1 for any unknown weather. */
int GetDefaultWeatherInterval(eWeather a_Weather);
/** Returns the current game mode. Partly OBSOLETE, you should use IsGameModeXXX() functions wherever applicable */ /** Returns the current game mode. Partly OBSOLETE, you should use IsGameModeXXX() functions wherever applicable */
eGameMode GetGameMode(void) const { return m_GameMode; } eGameMode GetGameMode(void) const { return m_GameMode; }
@ -342,6 +348,12 @@ public:
/** Sets the command block command. Returns true if command changed. */ /** Sets the command block command. Returns true if command changed. */
bool SetCommandBlockCommand(int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Command); // tolua_export bool SetCommandBlockCommand(int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Command); // tolua_export
/** Is the trapdoor open? Returns false if there is no trapdoor at the specified coords. */
bool IsTrapdoorOpen(int a_BlockX, int a_BlockY, int a_BlockZ); // tolua_export
/** Set the state of a trapdoor. Returns true if the trapdoor was update, false if there was no trapdoor at those coords. */
bool SetTrapdoorOpen(int a_BlockX, int a_BlockY, int a_BlockZ, bool a_Open); // tolua_export
/** Regenerate the given chunk: */ /** Regenerate the given chunk: */
void RegenerateChunk(int a_ChunkX, int a_ChunkZ); // tolua_export void RegenerateChunk(int a_ChunkX, int a_ChunkZ); // tolua_export
@ -445,7 +457,7 @@ public:
// tolua_begin // tolua_begin
bool DigBlock (int a_X, int a_Y, int a_Z); bool DigBlock (int a_X, int a_Y, int a_Z);
void SendBlockTo(int a_X, int a_Y, int a_Z, cPlayer * a_Player ); virtual void SendBlockTo(int a_X, int a_Y, int a_Z, cPlayer * a_Player);
double GetSpawnX(void) const { return m_SpawnX; } double GetSpawnX(void) const { return m_SpawnX; }
double GetSpawnY(void) const { return m_SpawnY; } double GetSpawnY(void) const { return m_SpawnY; }
@ -521,10 +533,13 @@ public:
/** Calls the callback for the command block at the specified coords; returns false if there's no command block at those coords or callback returns true, returns true if found */ /** Calls the callback for the command block at the specified coords; returns false if there's no command block at those coords or callback returns true, returns true if found */
bool DoWithCommandBlockAt(int a_BlockX, int a_BlockY, int a_BlockZ, cCommandBlockCallback & a_Callback); // Exported in ManualBindings.cpp bool DoWithCommandBlockAt(int a_BlockX, int a_BlockY, int a_BlockZ, cCommandBlockCallback & a_Callback); // Exported in ManualBindings.cpp
/** Calls the callback for the mob head block at the specified coords; returns false if there's no mob head block at those coords or callback returns true, returns true if found */ /** Calls the callback for the mob head block at the specified coords; returns false if there's no mob head block at those coords or callback returns true, returns true if found */
bool DoWithMobHeadBlockAt(int a_BlockX, int a_BlockY, int a_BlockZ, cMobHeadBlockCallback & a_Callback); // Exported in ManualBindings.cpp bool DoWithMobHeadAt(int a_BlockX, int a_BlockY, int a_BlockZ, cMobHeadCallback & a_Callback); // Exported in ManualBindings.cpp
/** Calls the callback for the flower pot at the specified coords; returns false if there's no flower pot at those coords or callback returns true, returns true if found */
bool DoWithFlowerPotAt(int a_BlockX, int a_BlockY, int a_BlockZ, cFlowerPotCallback & a_Callback); // Exported in ManualBindings.cpp
/** Retrieves the test on the sign at the specified coords; returns false if there's no sign at those coords, true if found */ /** Retrieves the test on the sign at the specified coords; returns false if there's no sign at those coords, true if found */
bool GetSignLines (int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4); // Exported in ManualBindings.cpp bool GetSignLines (int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4); // Exported in ManualBindings.cpp

View File

@ -20,6 +20,7 @@
#include "../BlockEntities/NoteEntity.h" #include "../BlockEntities/NoteEntity.h"
#include "../BlockEntities/SignEntity.h" #include "../BlockEntities/SignEntity.h"
#include "../BlockEntities/MobHeadEntity.h" #include "../BlockEntities/MobHeadEntity.h"
#include "../BlockEntities/FlowerPotEntity.h"
#include "../Entities/Entity.h" #include "../Entities/Entity.h"
#include "../Entities/FallingBlock.h" #include "../Entities/FallingBlock.h"
@ -275,6 +276,19 @@ void cNBTChunkSerializer::AddMobHeadEntity(cMobHeadEntity * a_MobHead)
void cNBTChunkSerializer::AddFlowerPotEntity(cFlowerPotEntity * a_FlowerPot)
{
m_Writer.BeginCompound("");
AddBasicTileEntity(a_FlowerPot, "FlowerPot");
m_Writer.AddInt ("Item", (Int32) a_FlowerPot->GetItem().m_ItemType);
m_Writer.AddInt ("Data", (Int32) a_FlowerPot->GetItem().m_ItemDamage);
m_Writer.EndCompound();
}
void cNBTChunkSerializer::AddBasicEntity(cEntity * a_Entity, const AString & a_ClassName) void cNBTChunkSerializer::AddBasicEntity(cEntity * a_Entity, const AString & a_ClassName)
{ {
m_Writer.AddString("id", a_ClassName); m_Writer.AddString("id", a_ClassName);
@ -687,6 +701,7 @@ void cNBTChunkSerializer::BlockEntity(cBlockEntity * a_Entity)
case E_BLOCK_CHEST: AddChestEntity ((cChestEntity *) a_Entity); break; case E_BLOCK_CHEST: AddChestEntity ((cChestEntity *) a_Entity); break;
case E_BLOCK_DISPENSER: AddDispenserEntity ((cDispenserEntity *) a_Entity); break; case E_BLOCK_DISPENSER: AddDispenserEntity ((cDispenserEntity *) a_Entity); break;
case E_BLOCK_DROPPER: AddDropperEntity ((cDropperEntity *) a_Entity); break; case E_BLOCK_DROPPER: AddDropperEntity ((cDropperEntity *) a_Entity); break;
case E_BLOCK_FLOWER_POT: AddFlowerPotEntity ((cFlowerPotEntity *) a_Entity); break;
case E_BLOCK_FURNACE: AddFurnaceEntity ((cFurnaceEntity *) a_Entity); break; case E_BLOCK_FURNACE: AddFurnaceEntity ((cFurnaceEntity *) a_Entity); break;
case E_BLOCK_HOPPER: AddHopperEntity ((cHopperEntity *) a_Entity); break; case E_BLOCK_HOPPER: AddHopperEntity ((cHopperEntity *) a_Entity); break;
case E_BLOCK_SIGN_POST: case E_BLOCK_SIGN_POST:

View File

@ -30,6 +30,7 @@ class cJukeboxEntity;
class cNoteEntity; class cNoteEntity;
class cSignEntity; class cSignEntity;
class cMobHeadEntity; class cMobHeadEntity;
class cFlowerPotEntity;
class cFallingBlock; class cFallingBlock;
class cMinecart; class cMinecart;
class cMinecartWithChest; class cMinecartWithChest;
@ -96,6 +97,7 @@ protected:
void AddSignEntity (cSignEntity * a_Sign); void AddSignEntity (cSignEntity * a_Sign);
void AddMobHeadEntity (cMobHeadEntity * a_MobHead); void AddMobHeadEntity (cMobHeadEntity * a_MobHead);
void AddCommandBlockEntity(cCommandBlockEntity * a_CmdBlock); void AddCommandBlockEntity(cCommandBlockEntity * a_CmdBlock);
void AddFlowerPotEntity(cFlowerPotEntity * a_FlowerPot);
// Entities: // Entities:
void AddBasicEntity (cEntity * a_Entity, const AString & a_ClassName); void AddBasicEntity (cEntity * a_Entity, const AString & a_ClassName);

View File

@ -1,10 +1,18 @@
// SchematicFileSerializer.cpp
// Implements the cSchematicFileSerializer class representing the interface to load and save cBlockArea to a .schematic file
#include "Globals.h" #include "Globals.h"
#include "OSSupport/GZipFile.h" #include "OSSupport/GZipFile.h"
#include "FastNBT.h" #include "FastNBT.h"
#include "SchematicFileSerializer.h" #include "SchematicFileSerializer.h"
#include "../StringCompression.h"
bool cSchematicFileSerializer::LoadFromSchematicFile(cBlockArea & a_BlockArea, const AString & a_FileName) bool cSchematicFileSerializer::LoadFromSchematicFile(cBlockArea & a_BlockArea, const AString & a_FileName)
{ {
@ -40,48 +48,51 @@ bool cSchematicFileSerializer::LoadFromSchematicFile(cBlockArea & a_BlockArea, c
bool cSchematicFileSerializer::SaveToSchematicFile(cBlockArea & a_BlockArea, const AString & a_FileName) bool cSchematicFileSerializer::LoadFromSchematicString(cBlockArea & a_BlockArea, const AString & a_SchematicData)
{ {
cFastNBTWriter Writer("Schematic"); // Uncompress the data:
Writer.AddShort("Width", a_BlockArea.m_SizeX); AString UngzippedData;
Writer.AddShort("Height", a_BlockArea.m_SizeY); if (UncompressStringGZIP(a_SchematicData.data(), a_SchematicData.size(), UngzippedData) != Z_OK)
Writer.AddShort("Length", a_BlockArea.m_SizeZ);
Writer.AddString("Materials", "Alpha");
if (a_BlockArea.HasBlockTypes())
{ {
Writer.AddByteArray("Blocks", (const char *)a_BlockArea.m_BlockTypes, a_BlockArea.GetBlockCount()); LOG("%s: Cannot unGZip the schematic data.", __FUNCTION__);
return false;
} }
else
// Parse the NBT:
cParsedNBT NBT(UngzippedData.data(), UngzippedData.size());
if (!NBT.IsValid())
{ {
AString Dummy(a_BlockArea.GetBlockCount(), 0); LOG("%s: Cannot parse the NBT in the schematic data.", __FUNCTION__);
Writer.AddByteArray("Blocks", Dummy.data(), Dummy.size()); return false;
} }
if (a_BlockArea.HasBlockMetas())
return LoadFromSchematicNBT(a_BlockArea, NBT);
}
bool cSchematicFileSerializer::SaveToSchematicFile(const cBlockArea & a_BlockArea, const AString & a_FileName)
{
// Serialize into NBT data:
AString NBT = SaveToSchematicNBT(a_BlockArea);
if (NBT.empty())
{ {
Writer.AddByteArray("Data", (const char *)a_BlockArea.m_BlockMetas, a_BlockArea.GetBlockCount()); LOG("%s: Cannot serialize the area into an NBT representation for file \"%s\".", __FUNCTION__, a_FileName.c_str());
return false;
} }
else
{
AString Dummy(a_BlockArea.GetBlockCount(), 0);
Writer.AddByteArray("Data", Dummy.data(), Dummy.size());
}
// TODO: Save entities and block entities
Writer.BeginList("Entities", TAG_Compound);
Writer.EndList();
Writer.BeginList("TileEntities", TAG_Compound);
Writer.EndList();
Writer.Finish();
// Save to file // Save to file
cGZipFile File; cGZipFile File;
if (!File.Open(a_FileName, cGZipFile::fmWrite)) if (!File.Open(a_FileName, cGZipFile::fmWrite))
{ {
LOG("Cannot open file \"%s\" for writing.", a_FileName.c_str()); LOG("%s: Cannot open file \"%s\" for writing.", __FUNCTION__, a_FileName.c_str());
return false; return false;
} }
if (!File.Write(Writer.GetResult())) if (!File.Write(NBT))
{ {
LOG("Cannot write data to file \"%s\".", a_FileName.c_str()); LOG("%s: Cannot write data to file \"%s\".", __FUNCTION__, a_FileName.c_str());
return false; return false;
} }
return true; return true;
@ -92,6 +103,30 @@ bool cSchematicFileSerializer::SaveToSchematicFile(cBlockArea & a_BlockArea, con
bool cSchematicFileSerializer::SaveToSchematicString(const cBlockArea & a_BlockArea, AString & a_Out)
{
// Serialize into NBT data:
AString NBT = SaveToSchematicNBT(a_BlockArea);
if (NBT.empty())
{
LOG("%s: Cannot serialize the area into an NBT representation.", __FUNCTION__);
return false;
}
// Gzip the data:
int res = CompressStringGZIP(NBT.data(), NBT.size(), a_Out);
if (res != Z_OK)
{
LOG("%s: Cannot Gzip the area data NBT representation: %d", __FUNCTION__, res);
return false;
}
return true;
}
bool cSchematicFileSerializer::LoadFromSchematicNBT(cBlockArea & a_BlockArea, cParsedNBT & a_NBT) bool cSchematicFileSerializer::LoadFromSchematicNBT(cBlockArea & a_BlockArea, cParsedNBT & a_NBT)
{ {
int TMaterials = a_NBT.FindChildByName(a_NBT.GetRoot(), "Materials"); int TMaterials = a_NBT.FindChildByName(a_NBT.GetRoot(), "Materials");
@ -170,3 +205,45 @@ bool cSchematicFileSerializer::LoadFromSchematicNBT(cBlockArea & a_BlockArea, cP
} }
AString cSchematicFileSerializer::SaveToSchematicNBT(const cBlockArea & a_BlockArea)
{
cFastNBTWriter Writer("Schematic");
Writer.AddShort("Width", a_BlockArea.m_SizeX);
Writer.AddShort("Height", a_BlockArea.m_SizeY);
Writer.AddShort("Length", a_BlockArea.m_SizeZ);
Writer.AddString("Materials", "Alpha");
if (a_BlockArea.HasBlockTypes())
{
Writer.AddByteArray("Blocks", (const char *)a_BlockArea.m_BlockTypes, a_BlockArea.GetBlockCount());
}
else
{
AString Dummy(a_BlockArea.GetBlockCount(), 0);
Writer.AddByteArray("Blocks", Dummy.data(), Dummy.size());
}
if (a_BlockArea.HasBlockMetas())
{
Writer.AddByteArray("Data", (const char *)a_BlockArea.m_BlockMetas, a_BlockArea.GetBlockCount());
}
else
{
AString Dummy(a_BlockArea.GetBlockCount(), 0);
Writer.AddByteArray("Data", Dummy.data(), Dummy.size());
}
// TODO: Save entities and block entities
Writer.BeginList("Entities", TAG_Compound);
Writer.EndList();
Writer.BeginList("TileEntities", TAG_Compound);
Writer.EndList();
Writer.Finish();
return Writer.GetResult();
}

View File

@ -1,4 +1,12 @@
// SchematicFileSerializer.h
// Declares the cSchematicFileSerializer class representing the interface to load and save cBlockArea to a .schematic file
#pragma once #pragma once
#include "../BlockArea.h" #include "../BlockArea.h"
@ -13,17 +21,34 @@ class cParsedNBT;
class cSchematicFileSerializer class cSchematicFileSerializer
{ {
public: public:
/// Loads an area from a .schematic file. Returns true if successful /** Loads an area from a .schematic file. Returns true if successful. */
static bool LoadFromSchematicFile(cBlockArea & a_BlockArea, const AString & a_FileName); static bool LoadFromSchematicFile(cBlockArea & a_BlockArea, const AString & a_FileName);
/// Saves the area into a .schematic file. Returns true if successful /** Loads an area from a string containing the .schematic file data. Returns true if successful. */
static bool SaveToSchematicFile(cBlockArea & a_BlockArea, const AString & a_FileName); static bool LoadFromSchematicString(cBlockArea & a_BlockArea, const AString & a_SchematicData);
/** Saves the area into a .schematic file. Returns true if successful. */
static bool SaveToSchematicFile(const cBlockArea & a_BlockArea, const AString & a_FileName);
/** Saves the area into a string containing the .schematic file data.
Returns true if successful, false on failure. The data is stored into a_Out. */
static bool SaveToSchematicString(const cBlockArea & a_BlockArea, AString & a_Out);
private: private:
/// Loads the area from a schematic file uncompressed and parsed into a NBT tree. Returns true if successful. /** Loads the area from a schematic file uncompressed and parsed into a NBT tree.
Returns true if successful. */
static bool LoadFromSchematicNBT(cBlockArea & a_BlockArea, cParsedNBT & a_NBT); static bool LoadFromSchematicNBT(cBlockArea & a_BlockArea, cParsedNBT & a_NBT);
/** Saves the area into a NBT representation and returns the NBT data as a string.
Returns an empty string if failed. */
static AString SaveToSchematicNBT(const cBlockArea & a_BlockArea);
}; };

View File

@ -25,6 +25,7 @@
#include "../BlockEntities/NoteEntity.h" #include "../BlockEntities/NoteEntity.h"
#include "../BlockEntities/SignEntity.h" #include "../BlockEntities/SignEntity.h"
#include "../BlockEntities/MobHeadEntity.h" #include "../BlockEntities/MobHeadEntity.h"
#include "../BlockEntities/FlowerPotEntity.h"
#include "../Mobs/Monster.h" #include "../Mobs/Monster.h"
@ -578,6 +579,10 @@ void cWSSAnvil::LoadBlockEntitiesFromNBT(cBlockEntityList & a_BlockEntities, con
{ {
LoadDropperFromNBT(a_BlockEntities, a_NBT, Child); LoadDropperFromNBT(a_BlockEntities, a_NBT, Child);
} }
else if (strncmp(a_NBT.GetData(sID), "FlowerPot", a_NBT.GetDataLength(sID)) == 0)
{
LoadFlowerPotFromNBT(a_BlockEntities, a_NBT, Child);
}
else if (strncmp(a_NBT.GetData(sID), "Furnace", a_NBT.GetDataLength(sID)) == 0) else if (strncmp(a_NBT.GetData(sID), "Furnace", a_NBT.GetDataLength(sID)) == 0)
{ {
LoadFurnaceFromNBT(a_BlockEntities, a_NBT, Child, a_BlockTypes, a_BlockMetas); LoadFurnaceFromNBT(a_BlockEntities, a_NBT, Child, a_BlockTypes, a_BlockMetas);
@ -760,6 +765,37 @@ void cWSSAnvil::LoadDropperFromNBT(cBlockEntityList & a_BlockEntities, const cPa
void cWSSAnvil::LoadFlowerPotFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx)
{
ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound);
int x, y, z;
if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z))
{
return;
}
std::auto_ptr<cFlowerPotEntity> FlowerPot(new cFlowerPotEntity(x, y, z, m_World));
short ItemType = 0, ItemData = 0;
int currentLine = a_NBT.FindChildByName(a_TagIdx, "Item");
if (currentLine >= 0)
{
ItemType = (short) a_NBT.GetInt(currentLine);
}
currentLine = a_NBT.FindChildByName(a_TagIdx, "Data");
if (currentLine >= 0)
{
ItemData = (short) a_NBT.GetInt(currentLine);
}
FlowerPot->SetItem(cItem(ItemType, 1, ItemData));
a_BlockEntities.push_back(FlowerPot.release());
}
void cWSSAnvil::LoadFurnaceFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx, BLOCKTYPE * a_BlockTypes, NIBBLETYPE * a_BlockMetas) void cWSSAnvil::LoadFurnaceFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx, BLOCKTYPE * a_BlockTypes, NIBBLETYPE * a_BlockMetas)
{ {
ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound); ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound);

View File

@ -135,6 +135,7 @@ protected:
void LoadChestFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx); void LoadChestFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
void LoadDispenserFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx); void LoadDispenserFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
void LoadDropperFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx); void LoadDropperFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
void LoadFlowerPotFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
void LoadFurnaceFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx, BLOCKTYPE * a_BlockTypes, NIBBLETYPE * a_BlockMetas); void LoadFurnaceFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx, BLOCKTYPE * a_BlockTypes, NIBBLETYPE * a_BlockMetas);
void LoadHopperFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx); void LoadHopperFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
void LoadJukeboxFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx); void LoadJukeboxFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);

View File

@ -12,8 +12,10 @@
#include "../BlockEntities/ChestEntity.h" #include "../BlockEntities/ChestEntity.h"
#include "../BlockEntities/CommandBlockEntity.h" #include "../BlockEntities/CommandBlockEntity.h"
#include "../BlockEntities/DispenserEntity.h" #include "../BlockEntities/DispenserEntity.h"
#include "../BlockEntities/FlowerPotEntity.h"
#include "../BlockEntities/FurnaceEntity.h" #include "../BlockEntities/FurnaceEntity.h"
#include "../BlockEntities/JukeboxEntity.h" #include "../BlockEntities/JukeboxEntity.h"
#include "../BlockEntities/MobHeadEntity.h"
#include "../BlockEntities/NoteEntity.h" #include "../BlockEntities/NoteEntity.h"
#include "../BlockEntities/SignEntity.h" #include "../BlockEntities/SignEntity.h"
@ -75,12 +77,14 @@ void cJsonChunkSerializer::BlockEntity(cBlockEntity * a_BlockEntity)
case E_BLOCK_CHEST: SaveInto = "Chests"; break; case E_BLOCK_CHEST: SaveInto = "Chests"; break;
case E_BLOCK_DISPENSER: SaveInto = "Dispensers"; break; case E_BLOCK_DISPENSER: SaveInto = "Dispensers"; break;
case E_BLOCK_DROPPER: SaveInto = "Droppers"; break; case E_BLOCK_DROPPER: SaveInto = "Droppers"; break;
case E_BLOCK_FLOWER_POT: SaveInto = "FlowerPots"; break;
case E_BLOCK_FURNACE: SaveInto = "Furnaces"; break; case E_BLOCK_FURNACE: SaveInto = "Furnaces"; break;
case E_BLOCK_SIGN_POST: SaveInto = "Signs"; break; case E_BLOCK_SIGN_POST: SaveInto = "Signs"; break;
case E_BLOCK_WALLSIGN: SaveInto = "Signs"; break; case E_BLOCK_WALLSIGN: SaveInto = "Signs"; break;
case E_BLOCK_NOTE_BLOCK: SaveInto = "Notes"; break; case E_BLOCK_NOTE_BLOCK: SaveInto = "Notes"; break;
case E_BLOCK_JUKEBOX: SaveInto = "Jukeboxes"; break; case E_BLOCK_JUKEBOX: SaveInto = "Jukeboxes"; break;
case E_BLOCK_COMMAND_BLOCK: SaveInto = "CommandBlocks"; break; case E_BLOCK_COMMAND_BLOCK: SaveInto = "CommandBlocks"; break;
case E_BLOCK_HEAD: SaveInto = "MobHeads"; break;
default: default:
{ {
@ -298,6 +302,21 @@ void cWSSCompact::LoadEntitiesFromJson(Json::Value & a_Value, cEntityList & a_En
} }
} // for itr - AllDispensers[] } // for itr - AllDispensers[]
// Load Flowerpots:
Json::Value AllFlowerPots = a_Value.get("FlowerPots", Json::nullValue);
for (Json::Value::iterator itr = AllFlowerPots.begin(); itr != AllFlowerPots.end(); ++itr)
{
std::auto_ptr<cFlowerPotEntity> FlowerPotEntity(new cFlowerPotEntity(0, 0, 0, a_World));
if (!FlowerPotEntity->LoadFromJson(*itr))
{
LOGWARNING("ERROR READING FLOWERPOT FROM JSON!" );
}
else
{
a_BlockEntities.push_back(FlowerPotEntity.release());
}
} // for itr - AllFlowerPots[]
// Load furnaces: // Load furnaces:
Json::Value AllFurnaces = a_Value.get("Furnaces", Json::nullValue); Json::Value AllFurnaces = a_Value.get("Furnaces", Json::nullValue);
for (Json::Value::iterator itr = AllFurnaces.begin(); itr != AllFurnaces.end(); ++itr) for (Json::Value::iterator itr = AllFurnaces.begin(); itr != AllFurnaces.end(); ++itr)
@ -331,7 +350,7 @@ void cWSSCompact::LoadEntitiesFromJson(Json::Value & a_Value, cEntityList & a_En
// Load note blocks: // Load note blocks:
Json::Value AllNotes = a_Value.get("Notes", Json::nullValue); Json::Value AllNotes = a_Value.get("Notes", Json::nullValue);
for( Json::Value::iterator itr = AllNotes.begin(); itr != AllNotes.end(); ++itr ) for (Json::Value::iterator itr = AllNotes.begin(); itr != AllNotes.end(); ++itr)
{ {
std::auto_ptr<cNoteEntity> NoteEntity(new cNoteEntity(0, 0, 0, a_World)); std::auto_ptr<cNoteEntity> NoteEntity(new cNoteEntity(0, 0, 0, a_World));
if (!NoteEntity->LoadFromJson(*itr)) if (!NoteEntity->LoadFromJson(*itr))
@ -346,7 +365,7 @@ void cWSSCompact::LoadEntitiesFromJson(Json::Value & a_Value, cEntityList & a_En
// Load jukeboxes: // Load jukeboxes:
Json::Value AllJukeboxes = a_Value.get("Jukeboxes", Json::nullValue); Json::Value AllJukeboxes = a_Value.get("Jukeboxes", Json::nullValue);
for( Json::Value::iterator itr = AllJukeboxes.begin(); itr != AllJukeboxes.end(); ++itr ) for (Json::Value::iterator itr = AllJukeboxes.begin(); itr != AllJukeboxes.end(); ++itr)
{ {
std::auto_ptr<cJukeboxEntity> JukeboxEntity(new cJukeboxEntity(0, 0, 0, a_World)); std::auto_ptr<cJukeboxEntity> JukeboxEntity(new cJukeboxEntity(0, 0, 0, a_World));
if (!JukeboxEntity->LoadFromJson(*itr)) if (!JukeboxEntity->LoadFromJson(*itr))
@ -361,7 +380,7 @@ void cWSSCompact::LoadEntitiesFromJson(Json::Value & a_Value, cEntityList & a_En
// Load command blocks: // Load command blocks:
Json::Value AllCommandBlocks = a_Value.get("CommandBlocks", Json::nullValue); Json::Value AllCommandBlocks = a_Value.get("CommandBlocks", Json::nullValue);
for( Json::Value::iterator itr = AllCommandBlocks.begin(); itr != AllCommandBlocks.end(); ++itr ) for (Json::Value::iterator itr = AllCommandBlocks.begin(); itr != AllCommandBlocks.end(); ++itr)
{ {
std::auto_ptr<cCommandBlockEntity> CommandBlockEntity(new cCommandBlockEntity(0, 0, 0, a_World)); std::auto_ptr<cCommandBlockEntity> CommandBlockEntity(new cCommandBlockEntity(0, 0, 0, a_World));
if (!CommandBlockEntity->LoadFromJson(*itr)) if (!CommandBlockEntity->LoadFromJson(*itr))
@ -373,6 +392,21 @@ void cWSSCompact::LoadEntitiesFromJson(Json::Value & a_Value, cEntityList & a_En
a_BlockEntities.push_back(CommandBlockEntity.release()); a_BlockEntities.push_back(CommandBlockEntity.release());
} }
} // for itr - AllCommandBlocks[] } // for itr - AllCommandBlocks[]
// Load mob heads:
Json::Value AllMobHeads = a_Value.get("MobHeads", Json::nullValue);
for (Json::Value::iterator itr = AllMobHeads.begin(); itr != AllMobHeads.end(); ++itr)
{
std::auto_ptr<cMobHeadEntity> MobHeadEntity(new cMobHeadEntity(0, 0, 0, a_World));
if (!MobHeadEntity->LoadFromJson(*itr))
{
LOGWARNING("ERROR READING MOB HEAD FROM JSON!" );
}
else
{
a_BlockEntities.push_back(MobHeadEntity.release());
}
} // for itr - AllMobHeads[]
} }