{"id":208,"date":"2017-02-01T13:58:59","date_gmt":"2017-02-01T13:58:59","guid":{"rendered":"http:\/\/blog.qubekwest.com\/?p=208"},"modified":"2017-02-01T20:32:41","modified_gmt":"2017-02-01T20:32:41","slug":"chunk-proxy","status":"publish","type":"post","link":"https:\/\/blog.qubekwest.com\/?p=208","title":{"rendered":"Chunk Proxy"},"content":{"rendered":"<p>This post may have a bit of repeated information for those that follow along closely. \u00a0A whole lot of Chunk related\u00a0refactoring happened because I had a new idea about how to do some things. \u00a0Thus, to make sure everyone is on the same page, I&#8217;m going to explain the way it was, and the way it ended up, and why. \u00a0Feel free to skip around if that suits you.<\/p>\n<p>Chunks are collections of blocks that are 16x16x16 and before the refactoring there were three kinds of them in QubeKwest under the covers. \u00a0The first is an EmptyChunk, it is a chunk where the whole thing is air. \u00a0It gives the ability to represent vast empty sections of sky with virtually no storage space. \u00a0The second is a UniformChunk, it is a chunk where the whole thing is made out of a single material. \u00a0For example, under a mountain where the whole thing is rock or way underground where you might encounter all lava. \u00a0Technically, there is nothing stopping you from using a UniformChunk full of air instead of an EmptyChunk but it will take a little more space, so it&#8217;s a bit of a waste. \u00a0The third kind is called a FullChunk (formerly called just Chunk). \u00a0It details every single block individually and can thus hold anything in any arrangement. \u00a0All of these types of chunks extend ChunkParent as their base class so they can be treated as the same thing.<\/p>\n<p>The basic concept behind all these different kinds of chunks was that they could be promoted to other types based on blocks being changed inside them. \u00a0For example if you have an EmptyChunk or a UniformChunk and you change even a single block within it, you have to also change it into a FullChunk. \u00a0One of the problems with this was that the space it takes to store a chunk has no middle ground. \u00a0You go from just a few bytes for either an EmptyChunk or a UniformChunk to around 65,000 bytes for a FullChunk. \u00a0The other problem is, where does this promotion occur so it&#8217;s easy to deal with?<\/p>\n<p>The first problem inspired thought about how to make a chunk that can store somewhere between a single type of block and one that has all 4096 individually detailed. \u00a0This got me thinking back to something I&#8217;d coded as a kid. \u00a0The original Doom saved screen shots in the PCX graphics file format. \u00a0This ancient format was both palettized and run-length encoded. \u00a0I learned the inner workings of the PCX file format and coded a PCX decoder so I could make little stop motion movies using Doom screen shots. \u00a0For QubeKwest, I&#8217;d originally considered run-length encoding for blocks in a chunk, but ruled it out because of the constant heavy updates to the encoding when changes are made. \u00a0This left the idea of using a block palette to reduce the space needed to save a chunk. \u00a0Basically it&#8217;s like an artist&#8217;s palette that has things like &#8220;1 = air&#8221; and &#8220;2 = granite&#8221; in it so I can reference all of the pieces of granite in a chunk just by using the number 2 instead of a whole block description.<\/p>\n<p>The new PalettizedChunk was born. \u00a0First, I considered using a palette that used a short (16-bit) index. \u00a0This was a bad choice because it would actually allow enough entries in the palette to represent every single block in a chunk individually, and could thus result in chunks that take more storage than the original FullChunk did. \u00a0Next, I considered using a byte (8-bit) index. \u00a0This would limit the number of blocks in the palette pretty heavily, but it would drastically reduce the space needed to store a chunk. \u00a0I further realized that because java uses stupid signed bytes, I decided to limit the indexes to 0 to 127. \u00a0I could have applied a bit of math to the index to use all 256 possible spots in a byte, but I didn&#8217;t like all of the primitive type promotion it would involve so I didn&#8217;t end up doing it that way. \u00a0This new version can store 128 kinds of blocks and only takes about 10% of the space as a FullChunk.<\/p>\n<p>The promotion can now go from either EmptyChunk or UniformChunk to a PalettizedChunk when a single block is changed, and then to be promoted to a FullChunk only when the chunk has more than 128 different types of blocks in it. \u00a0This still leaves the problem of where do the promotions between chunk types occur? \u00a0And when they occur, how do the various references to chunks get updated without taking a lot of work? \u00a0The answer to both questions is the idea of a ChunkProxy object (later renamed simply &#8220;Chunk&#8221; so I didn&#8217;t have to type &#8220;Proxy&#8221; in a million places in the code).<\/p>\n<p>This new object wraps any child of the ChunkParent class (which all 4 types of chunk are) and provides access to the various methods that are actually on the chunk inside. \u00a0This proxy knows which kind of chunk it is wrapping, and promotes it as needed automatically when an action performed on it requires it. \u00a0Now the rest of the codebase can just use ChunkProxy objects where they previously used ChunkParent objects. \u00a0If a chunk gets promoted, the proxy that wraps it stays the same. \u00a0That means that other things that refer to it don&#8217;t actually need to change, and in fact have no idea the promotion has occurred. \u00a0It&#8217;s the best of both worlds.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>This post may have a bit of repeated information for those that follow along closely. \u00a0A whole lot of Chunk related\u00a0refactoring happened because I had a new idea about how to do some things. \u00a0Thus, to make sure everyone is on the same page, I&#8217;m going to explain the way it was, and the way &hellip; <a href=\"https:\/\/blog.qubekwest.com\/?p=208\" class=\"more-link\">Continue reading <span class=\"screen-reader-text\">Chunk Proxy<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[3],"tags":[],"class_list":["post-208","post","type-post","status-publish","format-standard","hentry","category-dev"],"_links":{"self":[{"href":"https:\/\/blog.qubekwest.com\/index.php?rest_route=\/wp\/v2\/posts\/208","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.qubekwest.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.qubekwest.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.qubekwest.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.qubekwest.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=208"}],"version-history":[{"count":5,"href":"https:\/\/blog.qubekwest.com\/index.php?rest_route=\/wp\/v2\/posts\/208\/revisions"}],"predecessor-version":[{"id":213,"href":"https:\/\/blog.qubekwest.com\/index.php?rest_route=\/wp\/v2\/posts\/208\/revisions\/213"}],"wp:attachment":[{"href":"https:\/\/blog.qubekwest.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=208"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.qubekwest.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=208"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.qubekwest.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=208"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}