summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--MCServer/Plugins/APIDump/APIDesc.lua74
-rw-r--r--MCServer/Plugins/Debuggers/Debuggers.lua38
-rw-r--r--MCServer/Plugins/InfoDump.lua31
m---------lib/polarssl0
-rw-r--r--src/Bindings/ManualBindings.cpp73
-rw-r--r--src/Scoreboard.cpp2
-rw-r--r--src/Simulator/FloodyFluidSimulator.cpp23
-rw-r--r--src/Simulator/FloodyFluidSimulator.h12
-rw-r--r--src/Simulator/VanillaFluidSimulator.cpp150
-rw-r--r--src/Simulator/VanillaFluidSimulator.h42
-rw-r--r--src/World.cpp26
-rw-r--r--src/WorldStorage/SchematicFileSerializer.cpp134
-rw-r--r--src/WorldStorage/SchematicFileSerializer.h33
13 files changed, 554 insertions, 84 deletions
diff --git a/MCServer/Plugins/APIDump/APIDesc.lua b/MCServer/Plugins/APIDump/APIDesc.lua
index 0b6f33b37..30ce4cc2b 100644
--- a/MCServer/Plugins/APIDump/APIDesc.lua
+++ b/MCServer/Plugins/APIDump/APIDesc.lua
@@ -134,6 +134,7 @@ g_APIDesc =
HasBlockSkyLights = { Params = "", Return = "bool", Notes = "Returns true if current datatypes include skylight" },
HasBlockTypes = { Params = "", Return = "bool", Notes = "Returns true if current datatypes include block types" },
LoadFromSchematicFile = { Params = "FileName", Return = "", Notes = "Clears current content and loads new content from the specified schematic file. Returns true if successful. Returns false and logs error if unsuccessful, old content is preserved in such a case." },
+ 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 =
{
{ 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." },
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." },
+ SaveToSchematicString = { Params = "", Return = "string", Notes = "Saves the current contents to a string (in a .schematic file format). Returns the data if successful, empty string if failed." },
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" },
SetBlockSkyLight = { Params = "BlockX, BlockY, BlockZ, SkyLight", Return = "", Notes = "Sets the skylight at the specified absolute coords" },
@@ -483,6 +485,58 @@ end
},
}, -- 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 =
{
Desc = [[
@@ -2701,16 +2755,16 @@ end
IgnoreClasses =
{
- "coroutine",
- "debug",
- "io",
- "math",
- "package",
- "os",
- "string",
- "table",
- "g_Stats",
- "g_TrackedPages",
+ "^coroutine$",
+ "^debug$",
+ "^io$",
+ "^math$",
+ "^package$",
+ "^os$",
+ "^string$",
+ "^table$",
+ "^g_Stats$",
+ "^g_TrackedPages$",
},
IgnoreFunctions =
diff --git a/MCServer/Plugins/Debuggers/Debuggers.lua b/MCServer/Plugins/Debuggers/Debuggers.lua
index 329a652cd..f99c48242 100644
--- a/MCServer/Plugins/Debuggers/Debuggers.lua
+++ b/MCServer/Plugins/Debuggers/Debuggers.lua
@@ -71,6 +71,8 @@ function Initialize(Plugin)
-- TestExpatBindings();
-- TestPluginCalls();
+ TestBlockAreasString()
+
return true
end;
@@ -202,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()
LOG("Testing SQLite bindings...");
diff --git a/MCServer/Plugins/InfoDump.lua b/MCServer/Plugins/InfoDump.lua
index e7ed157e3..c61f9c9e6 100644
--- a/MCServer/Plugins/InfoDump.lua
+++ b/MCServer/Plugins/InfoDump.lua
@@ -317,26 +317,19 @@ local function WriteCommandsCategoryGithub(a_Category, f)
if (CategoryName == "") then
CategoryName = "General";
end
- f:write("\n## ", GithubizeString(a_Category.DisplayName or CategoryName), "\n");
+ f:write("\n### ", GithubizeString(a_Category.DisplayName or CategoryName), "\n");
-- Write description:
if (a_Category.Description ~= "") then
- f:write(GithubizeString(a_Category.Description), "\n");
+ f:write(GithubizeString(a_Category.Description), "\n\n");
end
+ f:write("| Command | Permission | Description | \n")
+ f:write("| ------- | ---------- | ----------- | \n")
+
-- Write commands:
- f:write("\n");
for idx2, cmd in ipairs(a_Category.Commands) do
- f:write("\n### ", cmd.CommandString, "\n", GithubizeString(cmd.Info.HelpString or "UNDOCUMENTED"), "\n\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
+ f:write("|", cmd.CommandString, " | ", cmd.Info.Permission or "", " | ", GithubizeString(cmd.Info.HelpString or "UNDOCUMENTED"), "| \n")
end
f:write("\n\n")
end
@@ -537,12 +530,13 @@ local function DumpPermissionsGithub(a_PluginInfo, f)
-- Dump the permissions:
f:write("\n# Permissions\n");
+ f:write("| Permissions | Description | Commands | Recommended groups |\n")
+ f:write("| ----------- | ----------- | -------- | ------------------ |\n")
for idx, perm in ipairs(Permissions) do
- f:write("### ", perm.Name, "\n");
- f:write(GithubizeString(perm.Info.Description or ""));
+ f:write(perm.Name, " | ");
+ f:write(GithubizeString(perm.Info.Description or ""), " | ");
local CommandsAffected = perm.Info.CommandsAffected or {};
if (#CommandsAffected > 0) then
- f:write("\n\nCommands affected:\n - ");
local Affects = {};
for idx2, cmd in ipairs(CommandsAffected) do
if (type(cmd) == "string") then
@@ -551,11 +545,10 @@ local function DumpPermissionsGithub(a_PluginInfo, f)
table.insert(Affects, GetCommandRefGithub(cmd.Name, cmd));
end
end
- f:write(table.concat(Affects, "\n - "));
- f:write("\n");
+ f:write(table.concat(Affects, ", "), " | ");
end
if (perm.Info.RecommendedGroups ~= nil) then
- f:write("\n\nRecommended groups: ", perm.Info.RecommendedGroups, "\n");
+ f:write(perm.Info.RecommendedGroups, " |");
end
f:write("\n");
end
diff --git a/lib/polarssl b/lib/polarssl
-Subproject 2ceda579893ceb23c5eb0d56df47dc235644e0f
+Subproject 2cb1a0c4009ecf368ecc74eb428394e10f9e6d0
diff --git a/src/Bindings/ManualBindings.cpp b/src/Bindings/ManualBindings.cpp
index 9fbc2e842..f9d04ce90 100644
--- a/src/Bindings/ManualBindings.cpp
+++ b/src/Bindings/ManualBindings.cpp
@@ -2483,6 +2483,37 @@ 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 SchematicFileSerilizer.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)
{
// function cBlockArea::SaveToSchematicFile
@@ -2512,6 +2543,34 @@ 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 SchematicFileSerilizer.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 = cSchematicFileSerializer::SaveToSchematicString(*self);
+ L.Push(Data);
+ return 1;
+}
+
+
+
+
+
static int tolua_cCompositeChat_AddRunCommandPart(lua_State * tolua_S)
{
// function cCompositeChat:AddRunCommandPart(Message, Command, [Style])
@@ -2775,12 +2834,14 @@ void ManualBindings::Bind(lua_State * tolua_S)
tolua_endmodule(tolua_S);
tolua_beginmodule(tolua_S, "cBlockArea");
- tolua_function(tolua_S, "GetBlockTypeMeta", tolua_cBlockArea_GetBlockTypeMeta);
- tolua_function(tolua_S, "GetOrigin", tolua_cBlockArea_GetOrigin);
- tolua_function(tolua_S, "GetRelBlockTypeMeta", tolua_cBlockArea_GetRelBlockTypeMeta);
- tolua_function(tolua_S, "GetSize", tolua_cBlockArea_GetSize);
- tolua_function(tolua_S, "LoadFromSchematicFile", tolua_cBlockArea_LoadFromSchematicFile);
- tolua_function(tolua_S, "SaveToSchematicFile", tolua_cBlockArea_SaveToSchematicFile);
+ tolua_function(tolua_S, "GetBlockTypeMeta", tolua_cBlockArea_GetBlockTypeMeta);
+ tolua_function(tolua_S, "GetOrigin", tolua_cBlockArea_GetOrigin);
+ tolua_function(tolua_S, "GetRelBlockTypeMeta", tolua_cBlockArea_GetRelBlockTypeMeta);
+ tolua_function(tolua_S, "GetSize", tolua_cBlockArea_GetSize);
+ tolua_function(tolua_S, "LoadFromSchematicFile", tolua_cBlockArea_LoadFromSchematicFile);
+ tolua_function(tolua_S, "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");
diff --git a/src/Scoreboard.cpp b/src/Scoreboard.cpp
index c1da27086..8088e624b 100644
--- a/src/Scoreboard.cpp
+++ b/src/Scoreboard.cpp
@@ -506,7 +506,7 @@ bool cScoreboard::ForEachObjective(cObjectiveCallback& 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)
{
diff --git a/src/Simulator/FloodyFluidSimulator.cpp b/src/Simulator/FloodyFluidSimulator.cpp
index 95182345c..b1ac734d7 100644
--- a/src/Simulator/FloodyFluidSimulator.cpp
+++ b/src/Simulator/FloodyFluidSimulator.cpp
@@ -86,7 +86,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:
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:
else if (
@@ -105,10 +110,7 @@ void cFloodyFluidSimulator::SimulateBlock(cChunk * a_Chunk, int a_RelX, int a_Re
if (SpreadFurther && (NewMeta < 8))
{
// Spread to the neighbors:
- SpreadToNeighbor(a_Chunk, a_RelX - 1, 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);
+ Spread(a_Chunk, a_RelX, a_RelY, a_RelZ, NewMeta);
}
// Mark as processed:
@@ -119,6 +121,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)
{
// If we have a section above, check if there's fluid above this block that would feed it:
diff --git a/src/Simulator/FloodyFluidSimulator.h b/src/Simulator/FloodyFluidSimulator.h
index c4af2e246..5fd91b2b1 100644
--- a/src/Simulator/FloodyFluidSimulator.h
+++ b/src/Simulator/FloodyFluidSimulator.h
@@ -38,14 +38,20 @@ protected:
// cDelayedFluidSimulator overrides:
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);
- /// 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);
- /// 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);
+
+ /** 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);
} ;
diff --git a/src/Simulator/VanillaFluidSimulator.cpp b/src/Simulator/VanillaFluidSimulator.cpp
new file mode 100644
index 000000000..5308d162b
--- /dev/null
+++ b/src/Simulator/VanillaFluidSimulator.cpp
@@ -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))
+ {
+ 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))
+ {
+ // 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;
+}
+
+
+
+
diff --git a/src/Simulator/VanillaFluidSimulator.h b/src/Simulator/VanillaFluidSimulator.h
new file mode 100644
index 000000000..a9ea98b5a
--- /dev/null
+++ b/src/Simulator/VanillaFluidSimulator.h
@@ -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);
+
+} ;
+
+
+
+
diff --git a/src/World.cpp b/src/World.cpp
index 37c07b398..73157b5bf 100644
--- a/src/World.cpp
+++ b/src/World.cpp
@@ -34,6 +34,7 @@
#include "Simulator/NoopRedstoneSimulator.h"
#include "Simulator/SandSimulator.h"
#include "Simulator/IncrementalRedstoneSimulator.h"
+#include "Simulator/VanillaFluidSimulator.h"
#include "Simulator/VaporizeFluidSimulator.h"
// Mobs:
@@ -3053,8 +3054,8 @@ cFluidSimulator * cWorld::InitializeFluidSimulator(cIniFile & a_IniFile, const c
AString SimulatorName = a_IniFile.GetValueSet("Physics", SimulatorNameKey, "");
if (SimulatorName.empty())
{
- LOGWARNING("[Physics] %s not present or empty in %s, using the default of \"Floody\".", SimulatorNameKey.c_str(), GetIniFileName().c_str());
- SimulatorName = "Floody";
+ LOGWARNING("[Physics] %s not present or empty in %s, using the default of \"Vanilla\".", SimulatorNameKey.c_str(), GetIniFileName().c_str());
+ SimulatorName = "Vanilla";
}
cFluidSimulator * res = NULL;
@@ -3078,15 +3079,24 @@ cFluidSimulator * cWorld::InitializeFluidSimulator(cIniFile & a_IniFile, const c
}
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 TickDelay = a_IniFile.GetValueSetI(SimulatorSectionName, "TickDelay", IsWater ? 5 : 30);
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);
diff --git a/src/WorldStorage/SchematicFileSerializer.cpp b/src/WorldStorage/SchematicFileSerializer.cpp
index 45fd967bd..a6ae8d8e0 100644
--- a/src/WorldStorage/SchematicFileSerializer.cpp
+++ b/src/WorldStorage/SchematicFileSerializer.cpp
@@ -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 "OSSupport/GZipFile.h"
#include "FastNBT.h"
-
#include "SchematicFileSerializer.h"
+#include "../StringCompression.h"
+
+
+
+
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");
- 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())
+ // Uncompress the data:
+ AString UngzippedData;
+ if (UncompressStringGZIP(a_SchematicData.data(), a_SchematicData.size(), UngzippedData) != Z_OK)
{
- 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());
+ LOG("%s: Cannot unGZip the schematic data.", __FUNCTION__);
+ return false;
}
- if (a_BlockArea.HasBlockMetas())
+
+ // Parse the NBT:
+ cParsedNBT NBT(UngzippedData.data(), UngzippedData.size());
+ if (!NBT.IsValid())
{
- Writer.AddByteArray("Data", (const char *)a_BlockArea.m_BlockMetas, a_BlockArea.GetBlockCount());
+ LOG("%s: Cannot parse the NBT in the schematic data.", __FUNCTION__);
+ return false;
}
- else
+
+ 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())
{
- AString Dummy(a_BlockArea.GetBlockCount(), 0);
- Writer.AddByteArray("Data", Dummy.data(), Dummy.size());
+ LOG("%s: Cannot serialize the area into an NBT representation for file \"%s\".", __FUNCTION__, a_FileName.c_str());
+ return false;
}
- // 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
cGZipFile File;
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;
}
- 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 true;
@@ -92,6 +103,31 @@ bool cSchematicFileSerializer::SaveToSchematicFile(cBlockArea & a_BlockArea, con
+AString cSchematicFileSerializer::SaveToSchematicString(const cBlockArea & a_BlockArea)
+{
+ // 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:
+ AString Compressed;
+ int res = CompressStringGZIP(NBT.data(), NBT.size(), Compressed);
+ if (res != Z_OK)
+ {
+ LOG("%s: Cannot Gzip the area data NBT representation: %d", __FUNCTION__, res);
+ return false;
+ }
+ return Compressed;
+}
+
+
+
+
+
bool cSchematicFileSerializer::LoadFromSchematicNBT(cBlockArea & a_BlockArea, cParsedNBT & a_NBT)
{
int TMaterials = a_NBT.FindChildByName(a_NBT.GetRoot(), "Materials");
@@ -170,3 +206,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();
+}
+
+
+
+
diff --git a/src/WorldStorage/SchematicFileSerializer.h b/src/WorldStorage/SchematicFileSerializer.h
index 9be2e5b57..f6ce1c16c 100644
--- a/src/WorldStorage/SchematicFileSerializer.h
+++ b/src/WorldStorage/SchematicFileSerializer.h
@@ -1,4 +1,12 @@
+// SchematicFileSerializer.h
+
+// Declares the cSchematicFileSerializer class representing the interface to load and save cBlockArea to a .schematic file
+
+
+
+
+
#pragma once
#include "../BlockArea.h"
@@ -13,17 +21,34 @@ class cParsedNBT;
+
class cSchematicFileSerializer
{
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);
- /// Saves the area into a .schematic file. Returns true if successful
- static bool SaveToSchematicFile(cBlockArea & a_BlockArea, const AString & a_FileName);
+ /** Loads an area from a string containing the .schematic file data. Returns true if successful. */
+ 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 the data, or empty string if failed. */
+ static AString SaveToSchematicString(const cBlockArea & a_BlockArea);
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);
+
+ /** 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);
};
+
+
+
+