summaryrefslogtreecommitdiffstats
path: root/MCServer/Plugins/APIDump/UsingChunkStays.html
diff options
context:
space:
mode:
Diffstat (limited to 'MCServer/Plugins/APIDump/UsingChunkStays.html')
-rw-r--r--MCServer/Plugins/APIDump/UsingChunkStays.html167
1 files changed, 0 insertions, 167 deletions
diff --git a/MCServer/Plugins/APIDump/UsingChunkStays.html b/MCServer/Plugins/APIDump/UsingChunkStays.html
deleted file mode 100644
index a7cb5302a..000000000
--- a/MCServer/Plugins/APIDump/UsingChunkStays.html
+++ /dev/null
@@ -1,167 +0,0 @@
-<!DOCTYPE html>
-<html>
- <head>
- <title>Cuberite - Using ChunkStays</title>
- <link rel="stylesheet" type="text/css" href="main.css" />
- <link rel="stylesheet" type="text/css" href="prettify.css" />
- <script src="prettify.js"></script>
- <script src="lang-lua.js"></script>
- <meta charset="UTF-8">
- </head>
- <body>
- <div id="content">
- <h1>Using ChunkStays</h1>
- <p>
- A plugin may need to manipulate data in arbitrary chunks, and it needs a way to make the server
- guarantee that the chunks are available in memory.</p>
-
- <h2>The problem</h2>
- <p>
- Usually when plugins want to manipulate larger areas of world data, they need to make sure that the
- server has the appropriate chunks loaded in the memory. When the data being manipulated can be further
- away from the connected players, or the data is being manipulated from a console handler, there is a
- real chance that the chunks are not loaded.</p>
- <p>
- This gets even more important when using the <a href="cBlockArea.html">cBlockArea</a> class for reading
- and writing. Those functions will fail when any of the required chunks aren't valid. This means that
- either the block area has incomplete data (Read() failed) or incomplete data has been written to the
- world (Write() failed). Recovery from this is near impossible - you can't simply read or write again
- later, because the world may have changed in the meantime.</p>
-
- <h2>The solution</h2>
- <p>
- The naive solution would be to monitor chunk loads and unloads, and postpone the operations until all
- the chunks are available. This would be quite ineffective and also very soon it would become very
- difficult to maintain, if there were multiple code paths requiring this handling.</p>
- <p>
- An alternate approach has been implemented, accessible through a single (somewhat hidden) function
- call: <a href="cWorld.html">cWorld:ChunkStay()</a>. All that this call basically does is, it tells the
- server "Load these chunks for me, and call this callback function once you have them all." And the
- server does exactly that - it remembers the callback and asks the world loader / generator to provide
- the chunks. Once the chunks become available, it calls the callback function for the plugin.</p>
- <p>
- There are a few gotcha-s, though. If the code that was requesting the read or write had access to some
- of the volatile objects, such as <a href="cPlayer.html">cPlayer</a> or
- <a href="cEntity.html">cEntity</a> objects, those cannot be accessed by the callback anymore, because
- they may have become invalid in the meantime - the player may have disconnected, the entity may have
- despawned. So the callback must use the longer way to access such objects, such as calling
- <a href="cWorld.html">cWorld:DoWithEntityByID()</a> or
- <a href="cWorld.html">cWorld:DoWithPlayer()</a>.</p>
-
- <h2>The example</h2>
- <p>
- As a simple example, consider a theoretical plugin that allows a player to save the immediate
- surroundings of the spawn into a schematic file. The player issues a command to initiate the save, and
- the plugin reads a 50 x 50 x 50 block area around the spawn into a cBlockArea and saves it on the disk
- as "<PlayerName>_spawn.schematic". When it's done with the saving, it wants to send a message to the
- player to let them know the command has succeeded.</p>
- <p>
- The first attempt shows the naive approach. It simply reads the block area and saves it, then sends the
- message. I'll repeat once more, this code is <b>the wrong way</b> to do it!</p>
-<pre class="prettyprint lang-lua">
-function HandleCommandSaveSpawn(a_Split, a_Player)
- -- Get the coords for the spawn:
- local SpawnX = a_Player:GetWorld():GetSpawnX()
- local SpawnY = a_Player:GetWorld():GetSpawnY()
- local SpawnZ = a_Player:GetWorld():GetSpawnZ()
- local Bounds = cCuboid(SpawnX - 25, SpawnY - 25, SpawnZ - 25, SpawnX + 25, SpawnY + 25, SpawnZ + 25)
- Bounds:ClampY(0, 255)
-
- -- Read the area around spawn into a cBlockArea, save to file:
- local Area = cBlockArea()
- local FileName = a_Player:GetName() .. "_spawn.schematic"
- Area:Read(a_Player:GetWorld(), Bounds, cBlockArea.baTypes + cBlockArea.baMetas)
- Area:SaveToSchematicFile(FileName)
-
- -- Notify the player:
- a_Player:SendMessage(cCompositeChat("The spawn has been saved", mtInfo))
- return true
-end
-</pre>
- <p>
- Now if the player goes exploring far and uses the command to save their spawn, the chunks aren't
- loaded, so the BlockArea reading fails, the BlockArea contains bad data. Note that the plugin fails to
- do any error checking and if the area isn't read from the world, it happily saves the incomplete data
- and says "hey, everything's right", althought it has just trashed any previous backup of the spawn
- schematic with nonsense data.</p>
- <hr/>
- <p>
- The following script uses the ChunkStay method to alleviate chunk-related problems. This is <b>the
- right way</b> of doing it:</p>
-<pre class="prettyprint lang-lua">
-function HandleCommandSaveSpawn(a_Split, a_Player)
- -- Get the coords for the spawn:
- local SpawnX = a_Player:GetWorld():GetSpawnX()
- local SpawnY = a_Player:GetWorld():GetSpawnY()
- local SpawnZ = a_Player:GetWorld():GetSpawnZ()
- local Bounds = cCuboid(SpawnX - 25, SpawnY - 25, SpawnZ - 25, SpawnX + 25, SpawnY + 25, SpawnZ + 25)
- Bounds:ClampY(0, 255)
-
- -- Get a list of chunks that we need loaded:
- local MinChunkX = math.floor((SpawnX - 25) / 16)
- local MaxChunkX = math.ceil ((SpawnX + 25) / 16)
- local MinChunkZ = math.floor((SpawnZ - 25) / 16)
- local MaxChunkZ = math.ceil ((SpawnZ + 25) / 16)
- local Chunks = {}
- for x = MinChunkX, MaxChunkX do
- for z = MinChunkZ, MaxChunkZ do
- table.insert(Chunks, {x, z})
- end
- end -- for x
-
- -- Store the player's name and world to use in the callback, because the a_Player object may no longer be valid:
- local PlayerName = a_Player:GetName()
- local World = a_Player:GetWorld()
-
- -- This is the callback that is executed once all the chunks are loaded:
- local OnAllChunksAvailable = function()
- -- Read the area around spawn into a cBlockArea, save to file:
- local Area = cBlockArea()
- local FileName = PlayerName .. "_spawn.schematic"
- if (Area:Read(World, Bounds, cBlockArea.baTypes + cBlockArea.baMetas)) then
- Area:SaveToSchematicFile(FileName)
- Msg = cCompositeChat("The spawn has been saved", mtInfo)
- else
- Msg = cCompositeChat("Cannot save the spawn", mtFailure)
- end
-
- -- Notify the player:
- -- Note that we cannot use a_Player here, because it may no longer be valid (if the player disconnected before the command completes)
- World:DoWithPlayer(PlayerName,
- function (a_CBPlayer)
- a_CBPlayer:SendMessage(Msg)
- end
- )
- end
-
- -- Ask the server to load our chunks and notify us once it's done:
- World:ChunkStay(Chunks, nil, OnAllChunksAvailable)
-
- -- Note that code here may get executed before the callback is called!
- -- The ChunkStay says "once you have the chunks", not "wait until you have the chunks"
- -- So you can't notify the player here, because the saving needn't have occurred yet.
-
- return true
-end
-</pre>
- <p>
- Note that this code does its error checking of the Area:Read() function, and it will not overwrite the
- previous file unless it actually has the correct data. If you're wondering how the reading could fail
- when we've got the chunks loaded, there's still the issue of free RAM - if the memory for the area
- cannot be allocated, it cannot be read even with all the chunks present. So we still do need that
- check.</p>
-
- <h2>The conclusion</h2>
- <p>
- Although it makes the code a little bit longer and is a bit more difficult to grasp at first, the
- ChunkStay is a useful technique to add to your repertoire. It is to be used whenever you need access to
- chunks that may potentially be inaccessible, and you really need the data.</p>
- <p>Possibly the biggest hurdle in using the ChunkStay is the fact that it does its work in the
- background, thus invalidating all cPlayer and cEntity objects your function may hold, so you need to
- re-acquire them from their IDs and names. This is the penalty for using multi-threaded code.</p>
- <script>
- prettyPrint();
- </script>
- </div>
- </body>
-</html>