diff options
Diffstat (limited to 'MCServer/Plugins/APIDump/UsingChunkStays.html')
-rw-r--r-- | MCServer/Plugins/APIDump/UsingChunkStays.html | 167 |
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> |