summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
m---------MCServer/Plugins/Core0
-rw-r--r--MCServer/Plugins/Debuggers/Debuggers.lua124
-rw-r--r--MCServer/Plugins/Debuggers/Info.lua223
-rw-r--r--src/Bindings/ManualBindings.cpp67
-rw-r--r--src/ChunkDef.h23
-rw-r--r--src/ChunkMap.cpp97
-rw-r--r--src/ChunkMap.h14
-rw-r--r--src/Generating/ChunkGenerator.cpp47
-rw-r--r--src/Generating/ChunkGenerator.h47
-rw-r--r--src/LightingThread.cpp14
-rw-r--r--src/OSSupport/Queue.h23
-rw-r--r--src/Root.cpp2
-rw-r--r--src/World.cpp13
-rw-r--r--src/World.h6
-rw-r--r--src/WorldStorage/WorldStorage.cpp58
-rw-r--r--src/WorldStorage/WorldStorage.h6
16 files changed, 677 insertions, 87 deletions
diff --git a/MCServer/Plugins/Core b/MCServer/Plugins/Core
-Subproject 4183d6cfb2049d0757d811a65bd4e75ed78b9f4
+Subproject 39d980e3a3447ac23f61c7d65426b33ee6c0718
diff --git a/MCServer/Plugins/Debuggers/Debuggers.lua b/MCServer/Plugins/Debuggers/Debuggers.lua
index 3dcd4ebee..a047488b5 100644
--- a/MCServer/Plugins/Debuggers/Debuggers.lua
+++ b/MCServer/Plugins/Debuggers/Debuggers.lua
@@ -35,39 +35,15 @@ function Initialize(Plugin)
-- _X: Disabled so that the normal operation doesn't interfere with anything
-- PM:AddHook(cPluginManager.HOOK_CHUNK_GENERATED, OnChunkGenerated);
- PM:BindCommand("/nick", "debuggers", HandleNickCmd, "- Gives you a custom name");
- 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("/wool", "debuggers", HandleWoolCmd, "- Sets all your armor to blue wool");
- PM:BindCommand("/testwnd", "debuggers", HandleTestWndCmd, "- Opens up a window using plugin API");
- PM:BindCommand("/gc", "debuggers", HandleGCCmd, "- Activates the Lua garbage collector");
- PM:BindCommand("/fast", "debuggers", HandleFastCmd, "- Switches between fast and normal movement speed");
- PM:BindCommand("/dash", "debuggers", HandleDashCmd, "- Switches between fast and normal sprinting speed");
- PM:BindCommand("/hunger", "debuggers", HandleHungerCmd, "- Lists the current hunger-related variables");
- PM:BindCommand("/poison", "debuggers", HandlePoisonCmd, "- Sets food-poisoning for 15 seconds");
- PM:BindCommand("/starve", "debuggers", HandleStarveCmd, "- Sets the food level to zero");
- PM:BindCommand("/fl", "debuggers", HandleFoodLevelCmd, "- Sets the food level to the given value");
- PM:BindCommand("/spidey", "debuggers", HandleSpideyCmd, "- Shoots a line of web blocks until it hits non-air");
- PM:BindCommand("/ench", "debuggers", HandleEnchCmd, "- Provides an instant dummy enchantment window");
- PM:BindCommand("/fs", "debuggers", HandleFoodStatsCmd, "- Turns regular foodstats message on or off");
- PM:BindCommand("/arr", "debuggers", HandleArrowCmd, "- Creates an arrow going away from the player");
- PM:BindCommand("/fb", "debuggers", HandleFireballCmd, "- Creates a ghast fireball as if shot by the player");
- PM:BindCommand("/xpa", "debuggers", HandleAddExperience, "- Adds 200 experience to the player");
- PM:BindCommand("/xpr", "debuggers", HandleRemoveXp, "- Remove all xp");
- PM:BindCommand("/fill", "debuggers", HandleFill, "- Fills all block entities in current chunk with junk");
- PM:BindCommand("/fr", "debuggers", HandleFurnaceRecipe, "- Shows the furnace recipe for the currently held item");
- PM:BindCommand("/ff", "debuggers", HandleFurnaceFuel, "- Shows how long the currently held item would burn in a furnace");
- PM:BindCommand("/sched", "debuggers", HandleSched, "- Schedules a simple countdown using cWorld:ScheduleTask()");
- PM:BindCommand("/cs", "debuggers", HandleChunkStay, "- Tests the ChunkStay Lua integration for the specified chunk coords");
- PM:BindCommand("/compo", "debuggers", HandleCompo, "- Tests the cCompositeChat bindings");
- PM:BindCommand("/sb", "debuggers", HandleSetBiome, "- Sets the biome around you to the specified one");
- PM:BindCommand("/wesel", "debuggers", HandleWESel, "- Expands the current WE selection by 1 block in X/Z");
- PM:BindCommand("/rmitem", "debuggers", HandleRMItem, "- Remove the specified item from the inventory.");
- PM:BindCommand("/pickups", "debuggers", HandlePickups, "- Spawns random pickups around you");
- PM:BindCommand("/poof", "debuggers", HandlePoof, "- Nudges pickups close to you away from you");
-
- PM:BindConsoleCommand("sched", HandleConsoleSchedule, "Tests the world scheduling");
-
+ -- Load the InfoReg shared library:
+ dofile(cPluginManager:GetPluginsPath() .. "/InfoReg.lua")
+
+ -- Bind all the commands:
+ RegisterPluginInfoCommands();
+
+ -- Bind all the console commands:
+ RegisterPluginInfoConsoleCommands();
+
Plugin:AddWebTab("Debuggers", HandleRequest_Debuggers)
Plugin:AddWebTab("StressTest", HandleRequest_StressTest)
@@ -1643,3 +1619,85 @@ end
+
+function HandleConsoleLoadChunk(a_Split)
+ -- Check params:
+ local numParams = #a_Split
+ if (numParams ~= 3) and (numParams ~= 4) then
+ return true, "Usage: " .. a_Split[1] .. " <ChunkX> <ChunkZ> [<WorldName>]"
+ end
+
+ -- Get the chunk coords:
+ local chunkX = tonumber(a_Split[2])
+ if (chunkX == nil) then
+ return true, "Not a number: '" .. a_Split[2] .. "'"
+ end
+ local chunkZ = tonumber(a_Split[3])
+ if (chunkZ == nil) then
+ return true, "Not a number: '" .. a_Split[3] .. "'"
+ end
+
+ -- Get the world:
+ local world
+ if (a_Split[4] == nil) then
+ world = cRoot:Get():GetDefaultWorld()
+ else
+ world = cRoot:Get():GetWorld(a_Split[4])
+ if (world == nil) then
+ return true, "There's no world named '" .. a_Split[4] .. "'."
+ end
+ end
+
+ -- Queue a ChunkStay for the chunk, log a message when the chunk is loaded:
+ world:ChunkStay({{chunkX, chunkZ}}, nil,
+ function()
+ LOG("Chunk [" .. chunkX .. ", " .. chunkZ .. "] is loaded")
+ end
+ )
+ return true
+end
+
+
+
+
+
+function HandleConsolePrepareChunk(a_Split)
+ -- Check params:
+ local numParams = #a_Split
+ if (numParams ~= 3) and (numParams ~= 4) then
+ return true, "Usage: " .. a_Split[1] .. " <ChunkX> <ChunkZ> [<WorldName>]"
+ end
+
+ -- Get the chunk coords:
+ local chunkX = tonumber(a_Split[2])
+ if (chunkX == nil) then
+ return true, "Not a number: '" .. a_Split[2] .. "'"
+ end
+ local chunkZ = tonumber(a_Split[3])
+ if (chunkZ == nil) then
+ return true, "Not a number: '" .. a_Split[3] .. "'"
+ end
+
+ -- Get the world:
+ local world
+ if (a_Split[4] == nil) then
+ world = cRoot:Get():GetDefaultWorld()
+ else
+ world = cRoot:Get():GetWorld(a_Split[4])
+ if (world == nil) then
+ return true, "There's no world named '" .. a_Split[4] .. "'."
+ end
+ end
+
+ -- Queue the chunk for preparing, log a message when prepared:
+ world:PrepareChunk(chunkX, chunkZ,
+ function(a_CBChunkX, a_CBChunkZ)
+ LOG("Chunk [" .. chunkX .. ", " .. chunkZ .. "] has been prepared")
+ end
+ )
+ return true
+end
+
+
+
+
diff --git a/MCServer/Plugins/Debuggers/Info.lua b/MCServer/Plugins/Debuggers/Info.lua
new file mode 100644
index 000000000..b96ef3de5
--- /dev/null
+++ b/MCServer/Plugins/Debuggers/Info.lua
@@ -0,0 +1,223 @@
+
+-- Info.lua
+
+-- Implements the g_PluginInfo standard plugin description
+
+
+
+
+
+g_PluginInfo =
+{
+ Name = "Debuggers",
+ Version = "14",
+ Date = "2014-12-11",
+ Description = [[Contains code for testing and debugging the server. Should not be enabled on a production server!]],
+
+ Commands =
+ {
+ ["/arr"] =
+ {
+ Permission = "debuggers",
+ Handler = HandleArrowCmd,
+ HelpString = "Creates an arrow going away from the player"
+ },
+ ["/compo"] =
+ {
+ Permission = "debuggers",
+ Handler = HandleCompo,
+ HelpString = "Tests the cCompositeChat bindings"
+ },
+ ["/cs"] =
+ {
+ Permission = "debuggers",
+ Handler = HandleChunkStay,
+ HelpString = "Tests the ChunkStay Lua integration for the specified chunk coords"
+ },
+ ["/dash"] =
+ {
+ Permission = "debuggers",
+ Handler = HandleDashCmd,
+ HelpString = "Switches between fast and normal sprinting speed"
+ },
+ ["/ench"] =
+ {
+ Permission = "debuggers",
+ Handler = HandleEnchCmd,
+ HelpString = "Provides an instant dummy enchantment window"
+ },
+ ["/fast"] =
+ {
+ Permission = "debuggers",
+ Handler = HandleFastCmd,
+ HelpString = "Switches between fast and normal movement speed"
+ },
+ ["/fb"] =
+ {
+ Permission = "debuggers",
+ Handler = HandleFireballCmd,
+ HelpString = "Creates a ghast fireball as if shot by the player"
+ },
+ ["/ff"] =
+ {
+ Permission = "debuggers",
+ Handler = HandleFurnaceFuel,
+ HelpString = "Shows how long the currently held item would burn in a furnace"
+ },
+ ["/fill"] =
+ {
+ Permission = "debuggers",
+ Handler = HandleFill,
+ HelpString = "Fills all block entities in current chunk with junk"
+ },
+ ["/fl"] =
+ {
+ Permission = "debuggers",
+ Handler = HandleFoodLevelCmd,
+ HelpString = "Sets the food level to the given value"
+ },
+ ["/fr"] =
+ {
+ Permission = "debuggers",
+ Handler = HandleFurnaceRecipe,
+ HelpString = "Shows the furnace recipe for the currently held item"
+ },
+ ["/fs"] =
+ {
+ Permission = "debuggers",
+ Handler = HandleFoodStatsCmd,
+ HelpString = "Turns regular foodstats message on or off"
+ },
+ ["/gc"] =
+ {
+ Permission = "debuggers",
+ Handler = HandleGCCmd,
+ HelpString = "Activates the Lua garbage collector"
+ },
+ ["/hunger"] =
+ {
+ Permission = "debuggers",
+ Handler = HandleHungerCmd,
+ HelpString = "Lists the current hunger-related variables"
+ },
+ ["/ke"] =
+ {
+ Permission = "debuggers",
+ Handler = HandleKillEntitiesCmd,
+ HelpString = "Kills all the loaded entities"
+ },
+ ["/le"] =
+ {
+ Permission = "debuggers",
+ Handler = HandleListEntitiesCmd,
+ HelpString = "Shows a list of all the loaded entities"
+ },
+ ["/nick"] =
+ {
+ Permission = "debuggers",
+ Handler = HandleNickCmd,
+ HelpString = "Gives you a custom name",
+ },
+ ["/pickups"] =
+ {
+ Permission = "debuggers",
+ Handler = HandlePickups,
+ HelpString = "Spawns random pickups around you"
+ },
+ ["/poison"] =
+ {
+ Permission = "debuggers",
+ Handler = HandlePoisonCmd,
+ HelpString = "Sets food-poisoning for 15 seconds"
+ },
+ ["/poof"] =
+ {
+ Permission = "debuggers",
+ Handler = HandlePoof,
+ HelpString = "Nudges pickups close to you away from you"
+ },
+ ["/rmitem"] =
+ {
+ Permission = "debuggers",
+ Handler = HandleRMItem,
+ HelpString = "Remove the specified item from the inventory."
+ },
+ ["/sb"] =
+ {
+ Permission = "debuggers",
+ Handler = HandleSetBiome,
+ HelpString = "Sets the biome around you to the specified one"
+ },
+ ["/sched"] =
+ {
+ Permission = "debuggers",
+ Handler = HandleSched,
+ HelpString = "Schedules a simple countdown using cWorld:ScheduleTask()"
+ },
+ ["/spidey"] =
+ {
+ Permission = "debuggers",
+ Handler = HandleSpideyCmd,
+ HelpString = "Shoots a line of web blocks until it hits non-air"
+ },
+ ["/starve"] =
+ {
+ Permission = "debuggers",
+ Handler = HandleStarveCmd,
+ HelpString = "Sets the food level to zero"
+ },
+ ["/testwnd"] =
+ {
+ Permission = "debuggers",
+ Handler = HandleTestWndCmd,
+ HelpString = "Opens up a window using plugin API"
+ },
+ ["/wesel"] =
+ {
+ Permission = "debuggers",
+ Handler = HandleWESel,
+ HelpString = "Expands the current WE selection by 1 block in X/Z"
+ },
+ ["/wool"] =
+ {
+ Permission = "debuggers",
+ Handler = HandleWoolCmd,
+ HelpString = "Sets all your armor to blue wool"
+ },
+ ["/xpa"] =
+ {
+ Permission = "debuggers",
+ Handler = HandleAddExperience,
+ HelpString = "Adds 200 experience to the player"
+ },
+ ["/xpr"] =
+ {
+ Permission = "debuggers",
+ Handler = HandleRemoveXp,
+ HelpString = "Remove all xp"
+ },
+ }, -- Commands
+
+ ConsoleCommands =
+ {
+ ["sched"] =
+ {
+ Handler = HandleConsoleSchedule,
+ HelpString = "Tests the world scheduling",
+ },
+ ["loadchunk"] =
+ {
+ Handler = HandleConsoleLoadChunk,
+ HelpString = "Loads the specified chunk into memory",
+ },
+ ["preparechunk"] =
+ {
+ Handler = HandleConsolePrepareChunk,
+ HelpString = "Prepares the specified chunk completely (load / gen / light)",
+ }
+ }, -- ConsoleCommands
+} -- g_PluginInfo
+
+
+
+
diff --git a/src/Bindings/ManualBindings.cpp b/src/Bindings/ManualBindings.cpp
index 750f7c65a..56f2e73bc 100644
--- a/src/Bindings/ManualBindings.cpp
+++ b/src/Bindings/ManualBindings.cpp
@@ -1874,6 +1874,72 @@ static int tolua_cWorld_ChunkStay(lua_State * tolua_S)
+static int tolua_cWorld_PrepareChunk(lua_State * tolua_S)
+{
+ /* Function signature:
+ World:PrepareChunk(ChunkX, ChunkZ, Callback)
+ */
+
+ // Check the param types:
+ cLuaState L(tolua_S);
+ if (
+ !L.CheckParamUserType (1, "cWorld") ||
+ !L.CheckParamNumber (2, 3) ||
+ !L.CheckParamFunctionOrNil(4)
+ )
+ {
+ return 0;
+ }
+
+ // Read the params:
+ cWorld * world = nullptr;
+ int chunkX = 0, chunkZ = 0;
+ L.GetStackValues(1, world, chunkX, chunkZ);
+ if (world == nullptr)
+ {
+ LOGWARNING("World:PrepareChunk(): invalid world parameter");
+ L.LogStackTrace();
+ return 0;
+ }
+
+ // Wrap the Lua callback inside a C++ callback class:
+ class cCallback:
+ public cChunkCoordCallback
+ {
+ public:
+ cCallback(lua_State * a_LuaState):
+ m_LuaState(a_LuaState),
+ m_Callback(m_LuaState, 4)
+ {
+ }
+
+ // cChunkCoordCallback override:
+ virtual void Call(int a_CBChunkX, int a_CBChunkZ) override
+ {
+ if (m_Callback.IsValid())
+ {
+ m_LuaState.Call(m_Callback, a_CBChunkX, a_CBChunkZ);
+ }
+
+ // This is the last reference of this object, we must delete it so that it doesn't leak:
+ delete this;
+ }
+
+ protected:
+ cLuaState m_LuaState;
+ cLuaState::cRef m_Callback;
+ };
+ cCallback * callback = new cCallback(tolua_S);
+
+ // Call the chunk preparation:
+ world->PrepareChunk(chunkX, chunkZ, callback);
+ return 0;
+}
+
+
+
+
+
static int tolua_cPlayer_GetPermissions(lua_State * tolua_S)
{
// Function signature: cPlayer:GetPermissions() -> {permissions-array}
@@ -3401,6 +3467,7 @@ void ManualBindings::Bind(lua_State * tolua_S)
tolua_function(tolua_S, "GetBlockInfo", tolua_cWorld_GetBlockInfo);
tolua_function(tolua_S, "GetBlockTypeMeta", tolua_cWorld_GetBlockTypeMeta);
tolua_function(tolua_S, "GetSignLines", tolua_cWorld_GetSignLines);
+ tolua_function(tolua_S, "PrepareChunk", tolua_cWorld_PrepareChunk);
tolua_function(tolua_S, "QueueTask", tolua_cWorld_QueueTask);
tolua_function(tolua_S, "ScheduleTask", tolua_cWorld_ScheduleTask);
tolua_function(tolua_S, "SetSignLines", tolua_cWorld_SetSignLines);
diff --git a/src/ChunkDef.h b/src/ChunkDef.h
index 260aace58..8f1d416ad 100644
--- a/src/ChunkDef.h
+++ b/src/ChunkDef.h
@@ -410,7 +410,7 @@ typedef std::list<cChunkCoordsWithBool> cChunkCoordsWithBoolList;
-/// Interface class used as a callback for operations that involve chunk coords
+/** Interface class used as a callback for operations that involve chunk coords */
class cChunkCoordCallback
{
public:
@@ -424,6 +424,27 @@ public:
+/** Provides storage for a set of chunk coords together with a callback.
+Used for chunk queues that notify about processed items. */
+class cChunkCoordsWithCallback
+{
+public:
+ cChunkCoordsWithCallback(int a_ChunkX, int a_ChunkZ, cChunkCoordCallback * a_Callback):
+ m_ChunkX(a_ChunkX),
+ m_ChunkZ(a_ChunkZ),
+ m_Callback(a_Callback)
+ {
+ }
+
+ int m_ChunkX;
+ int m_ChunkZ;
+ cChunkCoordCallback * m_Callback;
+};
+
+
+
+
+
/** Generic template that can store any kind of data together with a triplet of 3 coords*/
template <typename X> class cCoordWithData
{
diff --git a/src/ChunkMap.cpp b/src/ChunkMap.cpp
index 8a8f17a1b..222a756b1 100644
--- a/src/ChunkMap.cpp
+++ b/src/ChunkMap.cpp
@@ -2349,6 +2349,103 @@ void cChunkMap::TouchChunk(int a_ChunkX, int a_ChunkZ)
+void cChunkMap::PrepareChunk(int a_ChunkX, int a_ChunkZ, cChunkCoordCallback * a_Callback)
+{
+ cCSLock Lock(m_CSLayers);
+ cChunkPtr Chunk = GetChunkNoLoad(a_ChunkX, a_ChunkZ);
+
+ // If the chunk is not prepared, queue it in the lighting thread, that will do all the needed processing:
+ if ((Chunk == nullptr) || !Chunk->IsValid() || !Chunk->IsLightValid())
+ {
+ m_World->GetLightingThread().QueueChunk(a_ChunkX, a_ChunkZ, a_Callback);
+ return;
+ }
+
+ // The chunk is present and lit, just call the callback:
+ if (a_Callback != nullptr)
+ {
+ a_Callback->Call(a_ChunkX, a_ChunkZ);
+ }
+}
+
+
+
+
+
+bool cChunkMap::GenerateChunk(int a_ChunkX, int a_ChunkZ, cChunkCoordCallback * a_Callback)
+{
+ cCSLock Lock(m_CSLayers);
+ cChunkPtr Chunk = GetChunkNoLoad(a_ChunkX, a_ChunkZ);
+ if (Chunk == nullptr)
+ {
+ // Generic error while getting the chunk - out of memory?
+ return false;
+ }
+
+ // Try loading the chunk:
+ if ((Chunk == nullptr) || (!Chunk->IsValid()))
+ {
+ class cPrepareLoadCallback: public cChunkCoordCallback
+ {
+ public:
+ cPrepareLoadCallback(cWorld & a_World, cChunkMap & a_ChunkMap, cChunkCoordCallback * a_Callback):
+ m_World(a_World),
+ m_ChunkMap(a_ChunkMap),
+ m_Callback(a_Callback)
+ {
+ }
+
+ // cChunkCoordCallback override:
+ virtual void Call(int a_CBChunkX, int a_CBChunkZ) override
+ {
+ // The chunk has been loaded or an error occurred, check if it's valid now:
+ cChunkPtr CBChunk = m_ChunkMap.GetChunkNoLoad(a_CBChunkX, a_CBChunkZ);
+
+ if (CBChunk == nullptr)
+ {
+ // An error occurred, but we promised to call the callback, so call it even when there's no real chunk data:
+ if (m_Callback != nullptr)
+ {
+ m_Callback->Call(a_CBChunkX, a_CBChunkZ);
+ }
+ return;
+ }
+
+ // If the chunk is not valid, queue it in the generator:
+ if (!CBChunk->IsValid())
+ {
+ m_World.GetGenerator().QueueGenerateChunk(a_CBChunkX, a_CBChunkZ, false, m_Callback);
+ return;
+ }
+
+ // The chunk was loaded, call the callback:
+ if (m_Callback != nullptr)
+ {
+ m_Callback->Call(a_CBChunkX, a_CBChunkZ);
+ }
+ }
+
+ protected:
+ cWorld & m_World;
+ cChunkMap & m_ChunkMap;
+ cChunkCoordCallback * m_Callback;
+ };
+ m_World->GetStorage().QueueLoadChunk(a_ChunkX, a_ChunkZ, new cPrepareLoadCallback(*m_World, *this, a_Callback));
+ return true;
+ }
+
+ // The chunk is valid, just call the callback:
+ if (a_Callback != nullptr)
+ {
+ a_Callback->Call(a_ChunkX, a_ChunkZ);
+ }
+ return true;
+}
+
+
+
+
+
void cChunkMap::ChunkLoadFailed(int a_ChunkX, int a_ChunkZ)
{
cCSLock Lock(m_CSLayers);
diff --git a/src/ChunkMap.h b/src/ChunkMap.h
index 51be5cb3d..6a858b06d 100644
--- a/src/ChunkMap.h
+++ b/src/ChunkMap.h
@@ -280,6 +280,20 @@ public:
/** Touches the chunk, causing it to be loaded or generated */
void TouchChunk(int a_ChunkX, int a_ChunkZ);
+
+ /** Queues the chunk for preparing - making sure that it's generated and lit.
+ The specified chunk is queued to be loaded or generated, and lit if needed.
+ The specified callback is called after the chunk has been prepared. If there's no preparation to do, only the callback is called.
+ It is legal to call without the callback. */
+ void PrepareChunk(int a_ChunkX, int a_ChunkZ, cChunkCoordCallback * a_CallAfter = nullptr); // Lua-accessible
+
+ /** Queues the chunk for generating.
+ First attempts to load the chunk from the storage. If that fails, queues the chunk for generating.
+ The specified callback is called after the chunk has been loaded / generated.
+ It is legal to call without the callback.
+ Returns true if successful, false if not (possibly an out-of-memory error).
+ If the return value is true, the callback was / will be called. */
+ bool GenerateChunk(int a_ChunkX, int a_ChunkZ, cChunkCoordCallback * a_CallAfter = nullptr); // Lua-accessible
/** Marks the chunk as failed-to-load */
void ChunkLoadFailed(int a_ChunkX, int a_ChunkZ);
diff --git a/src/Generating/ChunkGenerator.cpp b/src/Generating/ChunkGenerator.cpp
index d2e7b47b4..854aac60d 100644
--- a/src/Generating/ChunkGenerator.cpp
+++ b/src/Generating/ChunkGenerator.cpp
@@ -110,29 +110,19 @@ void cChunkGenerator::Stop(void)
-void cChunkGenerator::QueueGenerateChunk(int a_ChunkX, int a_ChunkZ, bool a_ForceGenerate)
+void cChunkGenerator::QueueGenerateChunk(int a_ChunkX, int a_ChunkZ, bool a_ForceGenerate, cChunkCoordCallback * a_Callback)
{
ASSERT(m_ChunkSink->IsChunkQueued(a_ChunkX, a_ChunkZ));
{
cCSLock Lock(m_CS);
- // Check if it is already in the queue:
- for (cChunkCoordsWithBoolList::iterator itr = m_Queue.begin(); itr != m_Queue.end(); ++itr)
- {
- if ((itr->m_ChunkX == a_ChunkX) && (itr->m_ChunkZ == a_ChunkZ))
- {
- // Already in the queue, bail out
- return;
- }
- } // for itr - m_Queue[]
-
// Add to queue, issue a warning if too many:
if (m_Queue.size() >= QUEUE_WARNING_LIMIT)
{
LOGWARN("WARNING: Adding chunk [%i, %i] to generation queue; Queue is too big! (" SIZE_T_FMT ")", a_ChunkX, a_ChunkZ, m_Queue.size());
}
- m_Queue.push_back(cChunkCoordsWithBool(a_ChunkX, a_ChunkZ, a_ForceGenerate));
+ m_Queue.push_back(cQueueItem{a_ChunkX, a_ChunkZ, a_ForceGenerate, a_Callback});
}
m_Event.Set();
@@ -242,9 +232,9 @@ void cChunkGenerator::Execute(void)
continue;
}
- cChunkCoordsWithBool coords = m_Queue.front(); // Get next coord from queue
+ cQueueItem item = m_Queue.front(); // Get next chunk from the queue
bool SkipEnabled = (m_Queue.size() > QUEUE_SKIP_LIMIT);
- m_Queue.erase(m_Queue.begin()); // Remove coordinate from queue
+ m_Queue.erase(m_Queue.begin()); // Remove the item from the queue
Lock.Unlock(); // Unlock ASAP
m_evtRemoved.Set();
@@ -258,22 +248,35 @@ void cChunkGenerator::Execute(void)
LastReportTick = clock();
}
- if (!coords.m_ForceGenerate && m_ChunkSink->IsChunkValid(coords.m_ChunkX, coords.m_ChunkZ))
+ // Skip the chunk if it's already generated and regeneration is not forced:
+ if (!item.m_ForceGenerate && m_ChunkSink->IsChunkValid(item.m_ChunkX, item.m_ChunkZ))
{
- LOGD("Chunk [%d, %d] already generated, skipping generation", coords.m_ChunkX, coords.m_ChunkZ);
- // Already generated, ignore request
+ LOGD("Chunk [%d, %d] already generated, skipping generation", item.m_ChunkX, item.m_ChunkZ);
+ if (item.m_Callback != nullptr)
+ {
+ item.m_Callback->Call(item.m_ChunkX, item.m_ChunkZ);
+ }
continue;
}
- if (SkipEnabled && !m_ChunkSink->HasChunkAnyClients(coords.m_ChunkX, coords.m_ChunkZ))
+ // Skip the chunk if the generator is overloaded:
+ if (SkipEnabled && !m_ChunkSink->HasChunkAnyClients(item.m_ChunkX, item.m_ChunkZ))
{
- LOGWARNING("Chunk generator overloaded, skipping chunk [%d, %d]", coords.m_ChunkX, coords.m_ChunkZ);
+ LOGWARNING("Chunk generator overloaded, skipping chunk [%d, %d]", item.m_ChunkX, item.m_ChunkZ);
+ if (item.m_Callback != nullptr)
+ {
+ item.m_Callback->Call(item.m_ChunkX, item.m_ChunkZ);
+ }
continue;
}
- LOGD("Generating chunk [%d, %d]", coords.m_ChunkX, coords.m_ChunkZ);
- DoGenerate(coords.m_ChunkX, coords.m_ChunkZ);
-
+ // Generate the chunk:
+ LOGD("Generating chunk [%d, %d]", item.m_ChunkX, item.m_ChunkZ);
+ DoGenerate(item.m_ChunkX, item.m_ChunkZ);
+ if (item.m_Callback != nullptr)
+ {
+ item.m_Callback->Call(item.m_ChunkX, item.m_ChunkZ);
+ }
NumChunksGenerated++;
} // while (!bStop)
}
diff --git a/src/Generating/ChunkGenerator.h b/src/Generating/ChunkGenerator.h
index 190d9e616..4af486ae1 100644
--- a/src/Generating/ChunkGenerator.h
+++ b/src/Generating/ChunkGenerator.h
@@ -119,8 +119,12 @@ public:
bool Start(cPluginInterface & a_PluginInterface, cChunkSink & a_ChunkSink, cIniFile & a_IniFile);
void Stop(void);
- /// Queues the chunk for generation; removes duplicate requests
- void QueueGenerateChunk(int a_ChunkX, int a_ChunkZ, bool a_ForceGenerate);
+ /** Queues the chunk for generation
+ If a-ForceGenerate is set, the chunk is regenerated even if the data is already present in the chunksink.
+ a_Callback is called after the chunk is generated. If the chunk was already present, the callback is still called, even if not regenerating.
+ It is legal to set the callback to nullptr, no callback is called then.
+ If the generator becomes overloaded and skips this chunk, the callback is still called. */
+ void QueueGenerateChunk(int a_ChunkX, int a_ChunkZ, bool a_ForceGenerate, cChunkCoordCallback * a_Callback = nullptr);
/// Generates the biomes for the specified chunk (directly, not in a separate thread). Used by the world loader if biomes failed loading.
void GenerateBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap);
@@ -131,22 +135,46 @@ public:
int GetSeed(void) const { return m_Seed; }
- /// Returns the biome at the specified coords. Used by ChunkMap if an invalid chunk is queried for biome
+ /** Returns the biome at the specified coords. Used by ChunkMap if an invalid chunk is queried for biome */
EMCSBiome GetBiomeAt(int a_BlockX, int a_BlockZ);
- /// Reads a block type from the ini file; returns the blocktype on success, emits a warning and returns a_Default's representation on failure.
+ /** Reads a block type from the ini file; returns the blocktype on success, emits a warning and returns a_Default's representation on failure. */
static BLOCKTYPE GetIniBlock(cIniFile & a_IniFile, const AString & a_SectionName, const AString & a_ValueName, const AString & a_Default);
private:
+ struct cQueueItem
+ {
+ /** The chunk coords */
+ int m_ChunkX, m_ChunkZ;
+
+ /** Force the regeneration of an already existing chunk */
+ bool m_ForceGenerate;
+
+ /** Callback to call after generating.*/
+ cChunkCoordCallback * m_Callback;
+ };
+
+ typedef std::list<cQueueItem> cGenQueue;
+
+
+ /** Seed used for the generator. */
int m_Seed;
- cCriticalSection m_CS;
- cChunkCoordsWithBoolList m_Queue;
- cEvent m_Event; ///< Set when an item is added to the queue or the thread should terminate
- cEvent m_evtRemoved; ///< Set when an item is removed from the queue
+ /** CS protecting access to the queue. */
+ cCriticalSection m_CS;
+
+ /** Queue of the chunks to be generated. Protected against multithreaded access by m_CS. */
+ cGenQueue m_Queue;
+
+ /** Set when an item is added to the queue or the thread should terminate. */
+ cEvent m_Event;
+
+ /** Set when an item is removed from the queue. */
+ cEvent m_evtRemoved;
- cGenerator * m_Generator; ///< The actual generator engine used to generate chunks
+ /** The actual generator engine used to generate chunks. */
+ cGenerator * m_Generator;
/** The plugin interface that may modify the generated chunks */
cPluginInterface * m_PluginInterface;
@@ -158,6 +186,7 @@ private:
// cIsThread override:
virtual void Execute(void) override;
+ /** Generates the specified chunk and sets it into the chunksink. */
void DoGenerate(int a_ChunkX, int a_ChunkZ);
};
diff --git a/src/LightingThread.cpp b/src/LightingThread.cpp
index ae5e746e7..ced95d4e1 100644
--- a/src/LightingThread.cpp
+++ b/src/LightingThread.cpp
@@ -226,6 +226,8 @@ void cLightingThread::Execute(void)
} // CSLock(m_CS)
LightChunk(*Item);
+ Item->Disable();
+ delete Item;
}
}
@@ -236,6 +238,16 @@ void cLightingThread::Execute(void)
void cLightingThread::LightChunk(cLightingChunkStay & a_Item)
{
+ // If the chunk is already lit, skip it:
+ if (m_World->IsChunkLighted(a_Item.m_ChunkX, a_Item.m_ChunkZ))
+ {
+ if (a_Item.m_CallbackAfter != nullptr)
+ {
+ a_Item.m_CallbackAfter->Call(a_Item.m_ChunkX, a_Item.m_ChunkZ);
+ }
+ return;
+ }
+
cChunkDef::BlockNibbles BlockLight, SkyLight;
ReadChunks(a_Item.m_ChunkX, a_Item.m_ChunkZ);
@@ -314,8 +326,6 @@ void cLightingThread::LightChunk(cLightingChunkStay & a_Item)
{
a_Item.m_CallbackAfter->Call(a_Item.m_ChunkX, a_Item.m_ChunkZ);
}
- a_Item.Disable();
- delete &a_Item;
}
diff --git a/src/OSSupport/Queue.h b/src/OSSupport/Queue.h
index 8d096fe29..82f221453 100644
--- a/src/OSSupport/Queue.h
+++ b/src/OSSupport/Queue.h
@@ -163,6 +163,29 @@ public:
return false;
}
+
+ /** Removes all items for which the predicate returns true. */
+ template <class Predicate>
+ void RemoveIf(Predicate a_Predicate)
+ {
+ cCSLock Lock(m_CS);
+ for (auto itr = m_Contents.begin(); itr != m_Contents.end();)
+ {
+ if (a_Predicate(*itr))
+ {
+ auto itr2 = itr;
+ ++itr2;
+ m_Contents.erase(itr);
+ m_evtRemoved.Set();
+ itr = itr2;
+ }
+ else
+ {
+ ++itr;
+ }
+ } // for itr - m_Contents[]
+ }
+
private:
/// The contents of the queue
QueueType m_Contents;
diff --git a/src/Root.cpp b/src/Root.cpp
index 29daaedcc..9f8ffeeff 100644
--- a/src/Root.cpp
+++ b/src/Root.cpp
@@ -196,7 +196,7 @@ void cRoot::Start(void)
}
#endif
- LOG("Startup complete, took %ld ms!", std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - BeginTime).count());
+ LOG("Startup complete, took %ld ms!", static_cast<long int>(std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - BeginTime).count()));
#ifdef _WIN32
EnableMenuItem(hmenu, SC_CLOSE, MF_ENABLED); // Re-enable close button
#endif
diff --git a/src/World.cpp b/src/World.cpp
index db19649f3..1bee6e344 100644
--- a/src/World.cpp
+++ b/src/World.cpp
@@ -115,7 +115,7 @@ public:
{
int chunkX, chunkZ;
DecodeChunkCoords(i, chunkX, chunkZ);
- m_World.GetLightingThread().QueueChunk(chunkX, chunkZ, this);
+ m_World.PrepareChunk(chunkX, chunkZ, this);
} // for i
// Wait for the lighting thread to prepare everything. Event is set in the Call() callback:
@@ -2907,6 +2907,15 @@ void cWorld::TouchChunk(int a_ChunkX, int a_ChunkZ)
+void cWorld::PrepareChunk(int a_ChunkX, int a_ChunkZ, cChunkCoordCallback * a_CallAfter)
+{
+ m_ChunkMap->PrepareChunk(a_ChunkX, a_ChunkZ, a_CallAfter);
+}
+
+
+
+
+
void cWorld::ChunkLoadFailed(int a_ChunkX, int a_ChunkZ)
{
m_ChunkMap->ChunkLoadFailed(a_ChunkX, a_ChunkZ);
@@ -3017,7 +3026,7 @@ void cWorld::RegenerateChunk(int a_ChunkX, int a_ChunkZ)
void cWorld::GenerateChunk(int a_ChunkX, int a_ChunkZ)
{
- m_ChunkMap->TouchChunk(a_ChunkX, a_ChunkZ);
+ m_ChunkMap->GenerateChunk(a_ChunkX, a_ChunkZ);
}
diff --git a/src/World.h b/src/World.h
index 2f51d60a1..6f28ba534 100644
--- a/src/World.h
+++ b/src/World.h
@@ -375,6 +375,12 @@ public:
/** Touches the chunk, causing it to be loaded or generated */
void TouchChunk(int a_ChunkX, int a_ChunkZ);
+
+ /** Queues the chunk for preparing - making sure that it's generated and lit.
+ The specified chunk is queued to be loaded or generated, and lit if needed.
+ The specified callback is called after the chunk has been prepared. If there's no preparation to do, only the callback is called.
+ It is legal to call with no callback. */
+ void PrepareChunk(int a_ChunkX, int a_ChunkZ, cChunkCoordCallback * a_CallAfter = nullptr);
/** Marks the chunk as failed-to-load: */
void ChunkLoadFailed(int a_ChunkX, int a_ChunkZ);
diff --git a/src/WorldStorage/WorldStorage.cpp b/src/WorldStorage/WorldStorage.cpp
index 31318ee67..c7a295175 100644
--- a/src/WorldStorage/WorldStorage.cpp
+++ b/src/WorldStorage/WorldStorage.cpp
@@ -140,11 +140,11 @@ size_t cWorldStorage::GetSaveQueueLength(void)
-void cWorldStorage::QueueLoadChunk(int a_ChunkX, int a_ChunkZ)
+void cWorldStorage::QueueLoadChunk(int a_ChunkX, int a_ChunkZ, cChunkCoordCallback * a_Callback)
{
ASSERT(m_World->IsChunkQueued(a_ChunkX, a_ChunkZ));
- m_LoadQueue.EnqueueItem(cChunkCoords(a_ChunkX, a_ChunkZ));
+ m_LoadQueue.EnqueueItem(cChunkCoordsWithCallback(a_ChunkX, a_ChunkZ, a_Callback));
m_Event.Set();
}
@@ -152,11 +152,11 @@ void cWorldStorage::QueueLoadChunk(int a_ChunkX, int a_ChunkZ)
-void cWorldStorage::QueueSaveChunk(int a_ChunkX, int a_ChunkZ)
+void cWorldStorage::QueueSaveChunk(int a_ChunkX, int a_ChunkZ, cChunkCoordCallback * a_Callback)
{
ASSERT(m_World->IsChunkValid(a_ChunkX, a_ChunkZ));
- m_SaveQueue.EnqueueItemIfNotPresent(cChunkCoords(a_ChunkX, a_ChunkZ));
+ m_SaveQueue.EnqueueItem(cChunkCoordsWithCallback(a_ChunkX, a_ChunkZ, a_Callback));
m_Event.Set();
}
@@ -166,7 +166,11 @@ void cWorldStorage::QueueSaveChunk(int a_ChunkX, int a_ChunkZ)
void cWorldStorage::UnqueueLoad(int a_ChunkX, int a_ChunkZ)
{
- m_LoadQueue.Remove(cChunkCoords(a_ChunkX, a_ChunkZ));
+ m_LoadQueue.RemoveIf([=](cChunkCoordsWithCallback & a_Item)
+ {
+ return (a_Item.m_ChunkX == a_ChunkX) && (a_Item.m_ChunkZ == a_ChunkZ);
+ }
+ );
}
@@ -175,7 +179,11 @@ void cWorldStorage::UnqueueLoad(int a_ChunkX, int a_ChunkZ)
void cWorldStorage::UnqueueSave(const cChunkCoords & a_Chunk)
{
- m_SaveQueue.Remove(a_Chunk);
+ m_SaveQueue.RemoveIf([=](cChunkCoordsWithCallback & a_Item)
+ {
+ return (a_Item.m_ChunkX == a_Chunk.m_ChunkX) && (a_Item.m_ChunkZ == a_Chunk.m_ChunkZ);
+ }
+ );
}
@@ -244,14 +252,23 @@ void cWorldStorage::Execute(void)
bool cWorldStorage::LoadOneChunk(void)
{
- cChunkCoords ToLoad(0, 0);
+ // Dequeue an item, bail out if there's none left:
+ cChunkCoordsWithCallback ToLoad(0, 0, nullptr);
bool ShouldLoad = m_LoadQueue.TryDequeueItem(ToLoad);
+ if (!ShouldLoad)
+ {
+ return false;
+ }
- if (ShouldLoad)
+ // Load the chunk:
+ bool res = LoadChunk(ToLoad.m_ChunkX, ToLoad.m_ChunkZ);
+
+ // Call the callback, if specified:
+ if (ToLoad.m_Callback != nullptr)
{
- return LoadChunk(ToLoad.m_ChunkX, ToLoad.m_ChunkZ);
+ ToLoad.m_Callback->Call(ToLoad.m_ChunkX, ToLoad.m_ChunkZ);
}
- return false;
+ return res;
}
@@ -260,17 +277,30 @@ bool cWorldStorage::LoadOneChunk(void)
bool cWorldStorage::SaveOneChunk(void)
{
- cChunkCoords ToSave(0, 0);
+ // Dequeue one chunk to save:
+ cChunkCoordsWithCallback ToSave(0, 0, nullptr);
bool ShouldSave = m_SaveQueue.TryDequeueItem(ToSave);
- if (ShouldSave && m_World->IsChunkValid(ToSave.m_ChunkX, ToSave.m_ChunkZ))
+ if (!ShouldSave)
+ {
+ return false;
+ }
+
+ // Save the chunk, if it's valid:
+ if (m_World->IsChunkValid(ToSave.m_ChunkX, ToSave.m_ChunkZ))
{
m_World->MarkChunkSaving(ToSave.m_ChunkX, ToSave.m_ChunkZ);
- if (m_SaveSchema->SaveChunk(ToSave))
+ if (m_SaveSchema->SaveChunk(cChunkCoords(ToSave.m_ChunkX, ToSave.m_ChunkZ)))
{
m_World->MarkChunkSaved(ToSave.m_ChunkX, ToSave.m_ChunkZ);
}
}
- return ShouldSave;
+
+ // Call the callback, if specified:
+ if (ToSave.m_Callback != nullptr)
+ {
+ ToSave.m_Callback->Call(ToSave.m_ChunkX, ToSave.m_ChunkZ);
+ }
+ return true;
}
diff --git a/src/WorldStorage/WorldStorage.h b/src/WorldStorage/WorldStorage.h
index fc7e9f84c..e9ba2a8e2 100644
--- a/src/WorldStorage/WorldStorage.h
+++ b/src/WorldStorage/WorldStorage.h
@@ -25,7 +25,7 @@
// fwd:
class cWorld;
-typedef cQueue<cChunkCoords> cChunkCoordsQueue;
+typedef cQueue<cChunkCoordsWithCallback> cChunkCoordsQueue;
@@ -64,8 +64,8 @@ public:
cWorldStorage(void);
~cWorldStorage();
- void QueueLoadChunk(int a_ChunkX, int a_ChunkZ);
- void QueueSaveChunk(int a_ChunkX, int a_ChunkZ);
+ void QueueLoadChunk(int a_ChunkX, int a_ChunkZ, cChunkCoordCallback * a_Callback = nullptr);
+ void QueueSaveChunk(int a_ChunkX, int a_ChunkZ, cChunkCoordCallback * a_Callback = nullptr);
void UnqueueLoad(int a_ChunkX, int a_ChunkZ);
void UnqueueSave(const cChunkCoords & a_Chunk);