From 0cd8482f07aadab6de27aa7395de5408737093ad Mon Sep 17 00:00:00 2001
From: William 'psyFi' Hatcher We're seeing an increased need to store "prefabs" - little areas with predefined block contents, such as village houses or fortress rooms - in collections. We have one collection of village houses for the plains village, one collection for the desert village, one collection for the nether fortress... And there are plans in the future to use even more collections - trees, overworld fortresses, more village types and even custom structures. The point that they have in common is that they need to store not only the prefabs, but also metadata for those prefabs - how often they generate, how they connect together. There's even need for metadata for the entire collection, such as what the accepted biomes are, what block should village roads use, and various other generator parameters. So we need a file format that could store all this information together. There are some existing formats available to consider first:
+Contents
+
+
+Abstract - what and why
+
+
+Obviously none of these fully satisfy our needs, so we'll need to either extend one of them or create yet another one. Extending the .schematic file would mean that the exporter plugin would need to change most of the export code, which was deemed too unmaintainable. Because the bob format is not implemented at all, it wasn't even considered. The cpp format would have been a great candidate if it weren't so difficult to parse. However, it sparked an idea - something similar in form to the cpp format, but easily parsed. Since we already have the Lua interpreter, why not base the new format in Lua?
+With Lua, we could store any metadata for the prefabs, any additional information related to the entire set of prefabs. There's nothing stopping us from adding more items in a backward- and forward-compatible way. The prefabs can be stored very similar to the cpp format, an array of strings plus a charmap, or they can be stored externally in individual .schematic files and the Lua file would provide just the metadata. The server has already vast amounts of Lua-related support code that can be used for accessing the data. In the end this became the chosen solution. The format has been named "Cubeset" - a set of cube-based prefabs.
+ ++The Cubeset file has a .cubeset extension. Internally it is a Lua source file that provides a global value, Cubeset, which is a table containing the structured data. The loader checks the file's first 8 KiB to see if there is a "CubesetFormatVersion =" string in it (note the space between the word and the equals-sign), and if it is, the file is considered a Cubeset file and further loading is attempted. It is therefore crucial that tools producing this file format include this string as early as possible in the file.
++The top-level Cubeset table must contain at least two sub-tables: Metadata and Pieces. The Metadata table contains the metadata relevant to the entire set of prefabs in the file, the Pieces table contains the definitions and metadata for the individual prefabs. It is advised to make the Metadata table the first one, because it contains the signature used to identify the file ("CubesetFormatVersion ="). Apart from these two subtables the server ignores everything else.
+ ++The Cubeset.Metadata table is used to store metadata for the entire set of prefabs, and also for the format and version identification. It is a regular dictionary-style Lua table; the following elements are recognized: +
Name | Type | Content |
---|---|---|
CubesetFormatVersion | number | This is the format identification and at the same time it specifies the version of the file. Currently the file version is 1. Note that Cuberite checks the presence of the string "CubesetFormatVersion =", including the space between the word and the equals-sign, within the first 8 KiB of the file. |
ExportDate | string | The date when this file was exported, in the ISO format ("2015-06-16 13:34:03"). Inserted by GalExport for versioning purposes. Ignored elsewhere. |
ExternalSchematic | boolean | Flag inserted by GalExport to indicate that the individual prefabs are stored in separate .schematic files. Ignored elsewhere. |
IntendedUse | string | String identifying the generator part that the cubeset file is intended for. The server logs a warning when loading a cubeset file without an IntendedUse metadata; individual generators log a warning if a wrong IntendedUse is detected in a file they are asked to use. |
Additional values are recognized by the specific generator (which is indicated by the IntendedUse value):
+Generator (IntendedUse) | Name | Type | Content | Notes |
---|---|---|---|---|
Village / PieceStructures / Trees | AllowedBiomes | string | Comma-separated list of biomes | The generator only generates the structure / village / tree in the specified biomes. If empty, all biomes are eligible. |
Village | MaxDensity | number | Maximum density (0 - 100) at which the connectors are populated. | The village generator picks a density between Min and Max, and then only that many percent of the free connectors are actually attempted. This eventually reduces the number of houses to make the village look more "roomy". |
MinDensity | number | Minimum density (0 - 100) at which the connectors are populated. | ||
VillageRoadBlockType | number | The block type used in the village for regular roads on the solid surface | The generator replaces the top terrain block with the specified block type and meta to form the roads. The generator can distinguish when it's replacing water and when it's replacing regular terrain, allowing the villages to include "bridges" as their roads. | |
VillageRoadBlockMeta | number | The block meta used in the village for regular roads on the solid surface | ||
VillageWaterRoadBlockType | number | The block type used in the village for roads on the surface of water | ||
VillageWaterRoadBlockMeta | number | The block meta used in the village for roads on the surface of water | ||
PieceStructures | GridSizeX | number | Size, in blocks, of the seed grid | The generator starts each generated structure in a "seed", these two parameters control the (average) distances between two adjacent seeds. |
GridSizeZ | number | |||
MaxOffsetX | number | Maximum offset, in blocks, of the seed from the grid's center | The generator starts each generated structure in a "seed", these two parameters control the maximum distance of the seed from the regular grid (specified by GridSizeX and GridSizeZ). When zero, the structures are generated exactly on a rectangular grid. Recommended value is about half of the grid's size. | |
MaxOffsetZ | number | |||
MaxStructureSizeX | number | Size, in blocks, of the bounding box for a single structure. | The generator will not connect any prefab to the rest of the structure if it crosses the bounding box. | |
MaxStructureSizeZ | number | |||
MaxDepth | number | Maximum depth of the generated piece tree | The maximum number of connectors, away from the starting piece | |
SeedOffset | number | Number that is added to world's seed for this generator | Each cubeset file should have a different seed offset, otherwise it runs the risk of its structures being generated directly over other cubeset file's that the server admin has enabled. Since the seed is used for placement, if two cubeset files share the same seed, they will share the structure placement as well. |
+The Cubeset.Pieces table is an array containing individual prefabs. Each element describes a single prefab and its associated metadata. The following values are recognized: +
Name | Type | Content |
---|---|---|
OriginData | table | Inserted by GalExport to identify the gallery area from which the prefab is exported. Ignored elsewhere. |
Hitbox | table | The relative coords of the prefab's hitbox (where the prefab is not allowed to overlap another prefab when generating). Members: MinX, MinY, MinZ, MaxX, MaxY, MaxZ, all numbers. |
Connectors | table | Array of connector definitions. The table must be present for each prefab, even if the prefab doesn't have any connectors (use an empty table, then). |
SchematicFileName | string | Name of the .schematic file that contains the block data for the prefab. |
Size | table | Table containing the dimensions of the prefab, if it is inlined into the BlockData element. Contains three members, x, y, z, each is a number. |
BlockData | table | Array of strings that are processed to produce the block data for the prefab. Each letter in the string corresponds to one block, the type of the block is translated through the BlockDefinitions table. The blocks are ordered YZX, that is, the X coord varies the most. |
BlockDefinitions | table | Array of strings that defines the mapping of each letter in BlockData to a specific blocktype. Each string should have the format "Letter: BlockType: BlockMeta". |
Metadata | table | Dictionary-style table of various per-prefab metadata values. |
+The connector definitions table is an array of tables, each element describing one connector. The following values are recognized: +
Name | type | Content | ||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Type | number | The connector's type. The piece generator will only connect the connectors of inverse types ('1'-type connector will connect only to '-1'-type connector). | ||||||||||||||
RelX | number | X coord of the connector, relative to the prefab's zero point ({0, 0, 0} - the first block in the image). | ||||||||||||||
RelY | number | Y coord of the connector, relative to the prefab's zero point ({0, 0, 0} - the first block in the image). | ||||||||||||||
RelZ | number | Z coord of the connector, relative to the prefab's zero point ({0, 0, 0} - the first block in the image). | ||||||||||||||
Direction | number | The direction in which the connector is facing. Corresponds to the eBlockFace constants:
+
|
+Each piece contains additional metadata describing its properties. The server ignores metadata that it doesn't understand. The following values are recognized: +
Name | Type | IsRequired | Contents |
---|---|---|---|
IsStarting | number | Yes | Zero means that the piece is a regular piece, nonzero means that the piece is a starting piece (the "seed" of the structure). Required even for cubesets that don't represent a piece-generator data (such as trees). |
AllowedRotations | number | Number representing a bitmask for which rotations the piece supports. Defaults to 0 (no rotations). Bit 0 (value 1) represents whether 1 counter-clockwise rotation is allowed, bit 1 (value 2) represents whether 2 rotations (180 degrees) are allowed, bit 2 (value 4) represents whether 1 clockwise rotation is allowed. | |
AddWeightIfSame | number | How much weight (chance to generate) should the piece generator add to this piece when the parent piece is the same. It is possible to have negative values, meaning that the piece doesn't like repeating itself. Defaults to 0. | |
DefaultWeight | number | How much weight (chance to generate) does the piece have by default, without any modifiers (AddWeightIfSame, DepthWeight). Defaults to 0. | |
DepthWeight | string | Override for DefaultWeight for specific depth (in the tree used by the piece generator). String in the format "Depth1:Weight1|Depth2:Weight2|...". Each unlisted depth gets the DefaultWeight. Defaults to empty string (no override). | |
MergeStrategy | string | Which merge strategy should be used when drawing the prefab into the world. String representation of one of the cBlockArea:eMergeStrategy constants: "msOverwrite", "msFillAir", "msImprint", "msLake", "msSpongePrint", "msDifference", "msSimpleCompare", "msMask". Defaults to "msSpongePrint". | |
MoveToGround | number | Zero means that the piece will stay where generated by the piece generator, nonzero means that the piece will be moved Y-wise so that its first connector will be on the top block of the existing terrain. Useful for village houses. Defaults to 0. | |
ShouldExpandFloor | number | Nonzero means that the prefab's lowest slice will be repeated downwards until it hits a solid block, effectively creating a foundation for the piece. Useful for nether fortresses and village houses. Defaults to 0. |
+The following example defines a cubeset with two pieces. The first piece is inlined into the cubeset file, the second piece uses an external schematic file.
++Cubeset = +{ + Metadata = + { + CubesetFormatVersion = 1, + IntendedUse = "PieceStructures", + GridSizeX = 128, + GridSizeZ = 128, + MaxStructureSizeX = 64, + MaxStructureSizeZ = 64, + MaxOffsetX = 16, + MaxOffsetZ = 16, + MaxDepth = 4, + SeedOffset = 13, + }, + + Pieces = + { + -- The following piece was exported from the Gallery server by the GalExport plugin in the "cubeset" format: + { + OriginData = + { + ExportName = "DarkCorridor", + Name = "Nether 3", + GalleryName = "Nether", + GalleryIndex = "3", + ID = "30", + CreatorName = "STR_Warrior", + }, + Size = + { + x = 14, + y = 6, + z = 5, + }, + Hitbox = + { + MinX = 0, + MinY = 0, + MinZ = 0, + MaxX = 13, + MaxY = 5, + MaxZ = 4, + }, + Connectors = + { + { + Type = 1, + RelX = 0, + RelY = 1, + RelZ = 2, + Direction = 4, -- X- + }, + { + Type = 1, + RelX = 13, + RelY = 1, + RelZ = 2, + Direction = 5, -- X+ + }, + { + Type = -1, + RelX = 0, + RelY = 1, + RelZ = 2, + Direction = 4, -- X- + }, + { + Type = -1, + RelX = 13, + RelY = 1, + RelZ = 2, + Direction = 5, -- X+ + }, + }, + Metadata = + { + ["DefaultWeight"] = "100", + ["IsStarting"] = "0", + ["AllowedRotations"] = "7", + ["MergeStrategy"] = "msSpongePrint", + ["DepthWeight"] = "", + ["ShouldExpandFloor"] = "1", + ["MoveToGround"] = "0", + ["AddWeightIfSame"] = "0", + }, + BlockDefinitions = + { + ".: 0: 0", -- air + "a:112: 0", -- netherbrick + "b:113: 0", -- netherbrickfence + "c:114: 2", -- netherbrickstairs + "d:114: 3", -- netherbrickstairs + "m: 19: 0", -- sponge + }, + BlockData = + { + -- Level 0 + "aaaaaaaaaaaaaa", -- 0 + "aaaaaaaaaaaaaa", -- 1 + "aaaaaaaaaaaaaa", -- 2 + "aaaaaaaaaaaaaa", -- 3 + "aaaaaaaaaaaaaa", -- 4 + + -- Level 1 + "aaaaaaaaaaaaaa", -- 0 + "..............", -- 1 + "..............", -- 2 + "..............", -- 3 + "aaaaaaaaaaaaaa", -- 4 + + -- Level 2 + "aabaaaaaaaabaa", -- 0 + "..............", -- 1 + "..............", -- 2 + "..............", -- 3 + "aabaaaaaaaabaa", -- 4 + + -- Level 3 + "aabaaaaaaaabaa", -- 0 + "..............", -- 1 + "..............", -- 2 + "..............", -- 3 + "aabaaaaaaaabaa", -- 4 + + -- Level 4 + "aabaaaaaaaabaa", -- 0 + "..............", -- 1 + "..............", -- 2 + "..............", -- 3 + "aabaaaaaaaabaa", -- 4 + + -- Level 5 + "cccccccccccccc", -- 0 + "aaaaaaaaaaaaaa", -- 1 + "aaaaaaaaaaaaaa", -- 2 + "aaaaaaaaaaaaaa", -- 3 + "dddddddddddddd", -- 4 + }, + }, -- DarkCorridor + + -- The following piece was exported from the Gallery server by the GalExport plugin in the "cubesetext" format: + { + OriginData = + { + ExportName = "DoublePlantBed", + Name = "Plains 5", + GalleryName = "Plains", + GalleryIndex = "5", + ID = "20", + CreatorName = "tonibm1999", + }, + Size = + { + x = 15, + y = 8, + z = 9, + }, + Hitbox = + { + MinX = 0, + MinY = 0, + MinZ = 0, + MaxX = 14, + MaxY = 7, + MaxZ = 8, + }, + Connectors = + { + { + Type = -1, + RelX = 7, + RelY = 2, + RelZ = 8, + Direction = 3, -- Z+ + }, + }, + Metadata = + { + ["DefaultWeight"] = "100", + ["IsStarting"] = "0", + ["AllowedRotations"] = "7", + ["MergeStrategy"] = "msSpongePrint", + ["DepthWeight"] = "", + ["ShouldExpandFloor"] = "1", + ["MoveToGround"] = "1", + ["AddWeightIfSame"] = "0", + }, + SchematicFile = "PlainsVillage/20.schematic", + }, -- DoublePlantBed + } -- Pieces +} ++ + + diff --git a/dev-docs/Generator.html b/dev-docs/Generator.html new file mode 100644 index 000000000..4d17826c1 --- /dev/null +++ b/dev-docs/Generator.html @@ -0,0 +1,697 @@ + + +
This article explains the principles behind the terrain generator in Cuberite. It is not strictly specific to Cuberite, though, it can be viewed as a generic guide to various terrain-generating algorithms, with specific implementation notes regarding Cuberite.
+ +Contents: +
The nature has many complicated geological, physical and biological processes working on all scales from microscopic to planet-wide scale, that have shaped the terrain into what we see today. The tectonic plates collide, push mountain ranges up and ocean trenches down. Erosion dulls the sharp shapes. Plantlife takes over to further change the overall look of the world.
+ +Generally speaking, the processes take what's there and change it. Unlike computer generating, which usually creates a finished terrain from scratch, or maybe with only a few iterations. It would be unfeasible for software to emulate all the natural processes in enough detail to provide world generation for a game, mainly because in the nature everything interacts with everything. If a mountain range rises, it changes the way that the precipitation is carried by the wind to the lands beyond the mountains, thus changing the erosion rate there and the vegetation type.
+ + +For a MineCraft-like game terrain generator we need the generator to have several properties: +
As already mentioned, the nature works basically by generating raw terrain composition, then "applying" erosion, vegetation and finally this leads to biomes being formed. Let's now try a somewhat inverse approach: First generate biomes, then fit them with appropriate terrain, and finally cover in vegetation and all the other stuff.
+ +Splitting the parts like this suddenly makes it possible to create a generator with the required properties. We can generate a reasonable biome map chunk-wise, independently of all the other data. Once we have the biomes, we can compose the terrain for the chunk by using the biome data for the chunk, and possibly even for neighboring chunks. Note that we're not breaking the first property, the biomes can be generated separately so a neighboring chunk's biome map can be generated without the need for the entire neighboring chunk to be present. Similarly, once we have the terrain composition for a chunk, we can generate all the vegetation and structures in it, and those can again use the terrain composition in neighboring chunks.
+ + +This leads us directly to the main pipeline that is used for generating terrain in Cuberite. For technical reasons, the terrain composition step is further subdivided into Height generation and Composition generation, and the structures are really called Finishers. For each chunk the generator generates, in this sequence: +
The beautiful thing about this is that the individual components can be changed independently. You can have 5 biome generators and 3 height generators and you can let the users mix'n'match.
+This pipeline had been used in Cuberite for about a year, before we realized that is has a flaw: There is no way for it to generate overhangs. We tried to implement a Finisher that would actually carve overhangs into the terrain; this approach has several problems, most severe one of those is that tree and village generation becomes unbelievably difficult - those finishers need to know the basic terrain composition of the neighboring chunks in order to generate, and the composition would be different after the overhangs are carved. So we need to come up with a better way, something that directly generates the overhangs at latest by the Terrain composition stage.
+Luckily we have just the thing. Instead of generating a 2D heightmap, we generate a 3D "density map" - we decide about each block in the chunk being generated, whether it is a solid block or an air block. The following pictures try to illustrate this in one less dimension - the heightmap is a 1D function and the density map is a 2D function:
+ + + + +This way we can have generators that produce overhangs and yet allow finishers that need the entire composition of the neighboring chunks. However, we pay the price for this in performance, because a 3D noise for the density map needs order of magnitude more CPU cycles than 2D noise for heightmap. Also the RAM usage is increased because instead of storing 16 * 16 height values we need to store 16 * 256 * 16 density values.
+ +For a great tutorial on coherent noise, see the LibNoise documentation.
+Coherent noise is a type of noise that has three important properties that we can use to our advantage: +
We'll be mostly using Perlin noise in this article. It is the easiest one to visualise and use and is one of the most useful kinds of coherent noises. Here's an example of a Perlin noise generated in 2 dimensions:
+ + +It comes only naturally that such a 2D noise can be used as a terrain height map directly:
+ + +However, this is not the only use for this noise, and 2 dimensions is not the limit - this noise can be generated for any number of dimensions.
+ + + +The easiest way to generate biomes is to not generate them at all - simply assign a single constant biome to everywhere. And indeed there are times when this kind of "generator" is useful - for the MineCraft's Flat world type, or for testing purposes, or for tematic maps. In Cuberite, this is exactly what the Constant biome generator does.
+ +Of course, there are more interesting test scenarios for which multiple biomes must be generated as easy as possible. For these special needs, there's a CheckerBoard biome generator. As the name suggests, it generates a grid of alternating biomes.
+ +Those two generators were more of a technicality, we need to make something more interesting if we're going for a natural look. The Voronoi generator is the first step towards such a change. Recall that a Voronoi diagram is a construct that creates a set of areas where each point in an area is closer to the appropriate seed of the area than the seeds of any other area:
+ + +To generate biomes using this approach, you select random "seeds", assign a biome to each one, and then for each "column" of the world you find the seed that is the nearest to that column, and use that seed's biome.
+ +The overall shape of a Voronoi diagram is governed by the placement of the seeds. In extreme cases, a seed could affect the entire diagram, which is what we don't want - we need our locality, so that we can generate a chunk's worth of biome data. We also don't want the too much irregular diagrams that are produced when the seeds are in small clusters. We need our seeds to come in random, yet somewhat uniform fashion.
+ +Luckily, we have just the tool: Grid with jitter. Originally used in antialiasing techniques, they can be successfully applied as a source of the seeds for a Voronoi diagram. Simply take a regular 2D grid of seeds with the grid distance being N, and move each seed along the X and Y axis by a random distance, usually in the range [-N / 2, +N / 2]:
+ + +Such a grid is the ideal seed source for a Voronoi biome generator, because not only are the Voronoi cells "reasonable", but the seed placement's effect on the diagram is localized - each pixel in the diagram depends on at most 4 x 4 seeds around it. In the following picture, the seed for the requested point (blue) must be within the indicated circle. Even the second-nearest seed, which we will need later, is inside that circle.
+ + +Calculating the jitter for each cell can be done easily by using a 2D Perlin noise for each coord. We calculate the noise's value at [X, Z], which gives us a number in the range [-1; 1]. We then multiply the number by N / 2, this gives us the required range of [-N / 2, +N / 2]. Adding this number to the X coord gives us the seed's X position. We use another Perlin noise and the same calculation for the Z coord of the seed.
+ +Here's an example of a biome map generated using the Voronoi + jitter grid, as implemented by the Voronoi biome generator in Cuberite:
+ + +The biomes are starting to look interesting, but now they have straight-line borders, which looks rather weird and the players will most likely notice very soon. We need to somehow distort the borders to make them look more natural. By far the easiest way to achieve that is to use a little trick: When the generator is asked for the biome at column [X, Z], instead of calculating the Voronoi biome for column [X, Z], we first calculate a random offset for each coord, and add it to the coordinates. So the generator actually responds with the biome for [X + rndX, Z + rndZ].
+ +In order to keep the property that generating for the second time gives us the same result, we need the "random offset" to be replicatable - same output for the same input. This is where we use yet another Perlin noise - just like with the jitter for the Voronoi grid, we add a value from a separate noise to each coordinate before sending the coordinates down to the Voronoi generator:
+
+DistortedVoronoiBiome(X, Z) := VoronoiBiome(X + PerlinX(X, Z), Z + PerlinZ(X, Z))
+
+
+The following image shows the effects of the change, as generated by Cuberite's DistortedVoronoi biome generator. It is actually using the very same Voronoi map as the previous image, the only change has been the addition of the distortion:
+ + +As you can see, this already looks reasonable enough, it could be considered natural biomes, if it weren't for several drawbacks: +
Our next goal is to remove the first defect of the distorted Voronoi generator: unrelated biomes generating next to each other. You are highly unlikely to find a jungle biome next to a desert biome in the real world, so we want to have as few of those borders as possible in our generator, too. We could further improve on the selection of biome-to-seed in the Voronoi generator. Or we can try a completely different idea altogether.
+ +Recall how we talked about the nature, where the biomes are formed by the specific conditions of a place. What if we could make a similar dependency, but without the terrain? It turns out this is possible rather easily - instead of depending on the terrain, we choose two completely artificial measures. Let's call them Temperature and Humidity. If we knew the temperature of the place, we know what set of biomes are possible for such temperatures - we won't place deserts in the cold and tundra in the hot anymore. Similarly, the humidity will help us sort out the desert vs jungle issue. But how do we get a temperature and humidity? Once again, the Perlin noise comes to the rescue. We can use a simple 2D Perlin noise as the temperature map, and another one as the humidity map.
+ +What we need next is a decision of what biome to generate in certain temperature and humidity combinations. The fastest way for a computer is to have a 2D array, where the temperature is one dimension and humidity the other, and the values in the array specify the biome to generate:
+ + +We can even "misuse" the above diagram to include the hill variants of the biomes and have those hills neighbor each other properly, simply by declaring some of the decision diagram's parts as hills:
+ + +The problem with this approach is that there are biomes that should not depend on temperature or humidity, they generate across all of their values. Biomes like Oceans, Rivers and Mushroom. We could either add them somewhere into the decision diagram, or we can make the generator use a multi-step decision: +
This is the approach implemented in Cuberite's MultiStepMap biome generator. It generates biome maps like this:
+ + +To decide whether the point is in the ocean, land or mushroom, the generator first chooses seeds in a grid that will be later fed to a DistortedVoronoi algorithm, the seeds get the "ocean" and "land" values. Then it considers all the "ocean" seeds that are surrounded by 8 other "ocean" seeds and turns a random few of them into "mushroom". This special seed processing makes the mushroom biomes mostly surrounded by ocean. The following image shows an example seeds grid that the generator might consider, only the two framed cells are allowed to change into mushroom. L = land, O = ocean:
+ + +Next, the generator calculates the DistortedVoronoi for the seeds. For the areas that are calculated as mushroom, the distance to the nearest-seed is used to further shrink the mushroom biome and then to distinguish between mushroom and mushroom-shore (image depicts a Voronoi cell for illustration purposes, it works similarly with DistortedVoronoi). O = ocean, M = mushroom, MS = mushroom shore:
+ + + +The rivers are added only to the areas that have been previously marked as land. A simple 2D Perlin noise is used as the base, where its value is between 0 and a configured threshold value, a river is created. This creates the rivers in a closed-loop-like shapes, occasionally splitting two branches off:
+ + + + + +For the leftover land biomes, the two Perlin noises, representing temperature and humidity, are used to generate the biomes, as described earlier. Additionally, the temperature map is used to turn the Ocean biome into FrozenOcean, and the River biome into FrozenRiver, wherever the temperature drops below a threshold.
+ +The 1.7 MineCraft update brought a completely new terrain generation, which has sparked renewed interest in the biome generation. A new, potentially simpler way of generating biomes was found, the two-level DistortedVoronoi generator.
+ +The main idea behind it all is that we create large areas of similar biomes. There are several groups of related biomes that can be generated near each other: Desert biomes, Ice biomes, Forest biomes, Mesa biomes. Technically, the Ocean biomes were added as yet another group, so that the oceans will generate in approximately the size of the larger areas, too.
+ +For each column a DistortedVoronoi is used to select, which large area to use. This in turn results in the list of biomes from which to choose. Another DistortedVoronoi, this time with a smaller grid size, is used to select one biome out of that list. Additionally, the smaller DistortedVoronoi calculates not only the nearest seed's distance, but also the distance to the second-nearest seed; the ratio between these two is used as an indicator whether the column is in the "inside" or on the "outskirt" of the smaller Voronoi cell. This allows us to give certain biomes an "edge" biome - the Mushroom biome has a MushroomShore edge, the ExtremeHills biome have an ExtremeHillsEdge biome on the edge, etc.
+ +The images below illustrate the process with regular Voronoi diagrams, for clarity purposes. The real generator uses distortion before querying the small areas.
+The following image shows an example output of a TwoLevel biome generator in Cuberite. Note how the mushroom biomes (violet) have mushroom shores (pink) on their edges.
+ + +Note that rivers are currently not implemented in this generator in Cuberite, but they could be added +using the same approach as in MultiStepMap - by using a thresholded 2D Perlin noise.
+ + + +This generator uses a completely new approach to biome generation. Internally, it uses 2D arrays of integers of varying sizes, and defines a few operations on those arrays. At various points in the generator's pipeline, the integers are interpreted as having a different meaning. At the first stage, they diffentiate between ocean and land. Later on they are interpreted as biome groups - ocean biomes, dry biomes, temperate biomes, mountain biomes or ice biomes. In the final stages they represent individual biomes, each number in the array representing the biome of a single-block-wide column in the world. Still, most of the operations are agnostic of this interpretation, they only "see numbers".
+At the core of the generator is the "Zoom" operation, that enlarges the array almost twice in size (N -> 2*N - 1). For each 2x2 neighboring numbers in the original array it produces a 3x3 array, where the corner values inherit from their corner counterparts of the original array, and the values in the middle get chosen randomly from their appropriate neighbors:
+
|
+--- zooom --> | +
|
+
This scheme is repeated for larger arrays accordingly, for example (highlighted values are the ones directly copied from the source array):
+
|
+--- Zooom --> | +
|
+
The basic idea is that we're having a low-resolution image of the "land" and we're zooming in; in each zoom iteration we're adding random details - the randomly chosen numbers. This becomes apparent when we enlarge each image to the same dimensions:
+ + + + + + + +As you can see, the areas take a nice random-looking shape, but the edges are a little bit too noisy. There's where the second most important operation comes in: the "Smooth" slightly reduces the array size (N -> N - 2), losing the values on the edge of the array, and for the internal numbers it considers their 4 neighbors. If both the horizontal neighbors are the same and the vertical neighbors are the same (but not necessarily the same as the horizontal ones), the value is set randomly to either the horizontal or the vertical neihbors' value. If both the horizontal neighbors are the same, the value is set to the value of those neighbors, otherwise if both the vertical neighbors are the same, the value is set to the value of those neighbors. In all the rest cases, the value is kept at its original.
+
|
+
|
+
The following examples show how the decisions work:
+Input | Output | Notes | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|
Neither pair of neighbors are the same, so the value is left intact | +||||||||||||
Horizontal neighbors are the same, so the value is copied from them | +||||||||||||
Vertical neighbors are the same, so the value is copied from them | +||||||||||||
Each pair of neighbors are the same, so the value is chosen from one of the pairs randomly | +
This decision is repeated for each value in the array, for example:
+
|
+--- Smooth --> | +
|
+||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Highlighted area is processed into output | ++ |
+Original value kept +Value forced by both horizontal and vertical neighbors, random +Value forced by horizontal neighbors +Value forced by vertical neighbors + |
The following example shows multiple successive Smooth operations performed on the same data set over and over again:
+ + + + + + + +As you can see, the smoothing operation doesn't make much difference after its first pass, so it usually isn't used more than once after each zoom.
+One important thing to note is that both the Zoom and Smooth operations only output the numbers already present in the array, they don't create new numbers. This is important because it allows the late stages of the generator to grow indepent biomes next to each other without them "bleeding" into different biomes on their edges.
+The Grown generator uses several more supplementary operations, such as "AddIslands", "ReplaceRandomly", "River", "Beaches" and more. There isn't anything too special to those, they perform mostly trivial operations, manipulating the numbers in some way; the main power of the generator lies in the zoom and smooth operations. Perhaps noteworthy is the generation of rivers: it starts with the regular bitmap (only 0 and 1 used), zooms in and smooths for a while and then performs edge detection - a river biome is set in pixels whose neighbors are different, and no change applied when the neighbors are the same. Among other things, this means that there are actually two chains of array operations, and their results are combined together in the "MixRivers" operation.
+The following table summarizes the operations, visually:
+Operation | Input 1 | Input 2 | Output | Notes |
---|---|---|---|---|
AddIslands | ++ | - | ++ | Adds a configurable (by percentage) amount of islands to ocean biome group. | +
AlternateBiomes | ++ | + | + | Mostly copies the first input, but where the second input has non-ocean biome group, turns the first input's biomes into their alternatives. | +
Beaches | ++ | - | ++ | Any biome neighboring an ocean is turned into a corresponding beach. | +
BiomeEdges | ++ | - | ++ | If the neighbors of a biome are incompatible (such as desert vs ice plains, or jungle vs anything etc.), turns the biome into a corresponding neutral biome (plains, jungle-edge etc.) | +
Biomes | ++ | - | ++ | Input is interpreted as biome groups, for each point a random biome corresponding to the group is chosen for the output. | +
BiomeGroupEdges | ++ | - | ++ | Converts biome groups on an edge between two incompatible groups (such as desert and ice) into a neutral one (temperate). | +
MBiomes | ++ | + | + | Where the second input is zero, copies the first input's biomes; where the second input is nonzero, converts first input's biomes into their M variants. | +
MixRivers | ++ | + | + | Copies first input's biomes into the output, unless there's a river biome in the second input and a land biome in the first input - then it sets a river biome in the output instead. | +
River | ++ | - | ++ | Somewhat of an edge detector - wherever the input has a different biome neighbors, sets a river biome; otherwise sets an ocean biome. | +
SetRandomly | ++ | - | ++ | Randomly sets points to a specified biome. The amount of changed points is settable as a percentage. | +
Of further note is the existence of two sets of the IntGen classes, representing the individual operations. There are the cProtIntGen class descendants, which are used for prototyping the connections between the operations - it's easy to just chain several operations after each other and they automatically use the correct array dimensions. However, it is possible to further optimize the calculations by moving the array dimensions into template parameters (so that they are, in fact, constant from the code's point of view, and so highly optimizable). This is what the cIntGen class descendants do. Unfortunately, this optimization makes it difficult to change the operation chain - when a new operation is added or removed in the chain, the array sizes for the rest of the chain change and they all have to be updated manually. So the optimal strategy was to use the cProtIntGen classes to find out the best-looking combination of operations, and once the combination was found, to rewrite it using cIntGen classes for performance. +
+ ++ + + | +
+ + | +
+ + | +
+ + | +
+ + | +
As with biomes, the easiest way to generate terrain height is not generating at all - assigning a constant +height value to all columns. This is again useful either for internal tests, and for worlds like MineCraft's +Flat world.
+ +For a somewhat more realistic landscape, we will employ the good old 2D Perlin noise. We can use it +directly as a heightmap - each value we get from the noise is stretched into the desired range (usually from +40 to 120 blocks for regular MineCraft worlds) and used as the height value. However, this doesn't play too +well with the biomes we've just generated. If the biome says "ocean" and the Perlin noise says "mountain", +the end result will be unpleasant.
+ +So we want a height generator that is biome-aware. The easiest way of doing this is to have a separate +generator for each biome. Simply use the biome map to select which generator to use, then ask the appropriate +generator for the height value. Again, this doesn't work too well - imagine an ExtremeHills biome right next +to an Ocean biome. If no extra care is taken, the border between these two will be a high wall. The following +image shows a 2D representation (for simplification purposes) of the problem:
+ + +This requires some further processing. What we need is for the terrain height to be dependent not only on +the immediate biome for that column, but also on the close surroundings of the column. This is exactly the +kind of task that averaging is designed for. If we take the area of 9x9 biomes centered around the queried +column, generate height for each of the biomes therein, sum them up and divide by 81 (the number of biomes +summed), we will be effectively making a 9-long running average over the terrain, and all the borders will +suddenly become smooth. The following image shows the situation from the previous paragraph after applying +the averaging process:
+ + +The approach used in Cuberite's Biomal generator is based on this idea, with two slight modifications. +Instead of using a separate generator for each biome, one generator is used with a different set of input +parameters for each biomes. These input parameters modify the overall amplitude and frequency of the Perlin +noise that the generator produces, thus modifying the final terrain with regards to biomes. Additionally, the +averaging process is weighted - columns closer to the queried column get a more powerful weight in the sum +than the columns further away. The following image shows the output of Cuberite's Biomal terrain height +generator (each block type represents a different biome - ocean in the front (stone), plains and ice plains +behind it (lapis, whitewool), extreme hills back right (soulsand), desert hills back left (mossy +cobble)):
+ + +One key observation about this whole approach is that in order for it to work, the biomes must be +available for columns outside the currently generated chunk, otherwise the columns at the chunk's edge would +not be able to properly average their height. This requirement can be fulfilled only by biome generators that +adhere to the second Expected property - that re-generating will produce +the same data. If the biome generator returned different data for the same chunk each time it was invoked, it +would become impossible to apply the averaging.
+ +(TODO: height with variations (N/A in MCS yet)
+ + +As with the other generators, the composition generator category has its easy and debugging items, too. +There's the "special" composition of "all the blocks are the same type", which fills the entire column, from +the bottom to the height, with a single blocktype. This generator is useful when testing the generators in +the other categories, to speed up the generation by leaving out unnecessary calculations. Another special +compositor is a similar one, that fills all blocks with the same type, but the type varies for each biome. +This way it's easy to see the generated biomes and possibly the heights for those biomes, as shown in the +previous section on the height averaging screenshot.
+ +For a natural look, we need to put together a more complicated algorithm. The standard set forth in +MineCraft is that the top of the world is covered in grass, then there are a few blocks of dirt and finally +stone. This basic layout is then varied for different biomes - deserts have sand and sandstone instead of the +grass and dirt layer. Mushroom biomes have mycelium in place of the grass. This per-biome dependency is +trivial to implement - when compositing, simply use the appropriate layout for the column's biome.
+ +The next change concerns oceans. The generated heightmap doesn't include any waterlevel indication +whatsoever. So it's up to the terrain compositor to actually decide where to place water. We do this by +configuration - simply have a value in the config file specifying the sealevel height. The compositor then +has to add water above any column which has a height lower than that. Additionally, the water needs to +override per-biome layout selection - we don't want grass blocks to generate under water when the terrain +height in the plains biome drops below the sealevel accidentally.
+ +The final feature in the compositor is the decision between multiple composition layouts within a single +biome. A megataiga biome contains patches of non-grass dirt and podzol blocks, and the ocean floor can be +made of dirt, gravel, sand or clay. A simple 2D Perlin noise can be used to select the layout to use for a +specific column - simply threshold the noise's value by as many thresholds as there are layout variations, +and use the layout corresponding to the threshold:
+ + + + +So far we've been discussing only the Overworld generator. But MineCraft contains more than that. The +Nether has a completely different look and feel, and quite different processes are required to generate that. +Recall that MineCraft's Nether is 128 blocks high, with bedrock both at the top and the bottom. Between these +two, the terrain looks more like a cavern than a surface. Not surprisingly, the Nether doesn't need a +complicated height generator, it can use the flat height. However, the terrain composition must take an +altogether different approach.
+ +The very first idea is to use the Perlin noise, but generate it in 3D, rather than 2D. Then, for each +block, evaluate the noise value, if below 0, make it air, if not, make it netherrack. + +
To make it so that the bedrock at the top and at the bottom is never revealed, we can add a value +increasing the more the Y coord gets towards the bottom or the top. This way the thresholding then guarantees +that there will be no air anywhere near the bedrock.
+ +(TODO)
+ + +Finishers are a vast category of various additions to the terrain generator. They range from very easy +ones, such as generating snow on top of the terrain in cold biomes, through medium ones, such as growing +patches of flowers, complicated ones, such as placing trees and generating caves, all the way to very +complicated ones such as villages and nether fortresses. There is no formal distinction between all these +"categories", the only thing they have in common is that they take a chunk of blocks and modify it in some +way.
+ +Snow is probably the easiest of the finishers. It generates a block of snow on top of each block that is +on top of the terrain and is not marked as non-snowable. It checks the chunk's heightmap to determine the top +block, then checks whether the block supports snow on its top. Rails, levers and tall grass don't support +snow, for example.
+ +Another example of an easy finisher. This scans through the world and turn each water block on the surface +into an ice block if the biome is cold. This means that any water block that is under any kind of other +block, such as under a tree's leaves, will still stay water. Thus an additional improvement could be made by +scanning down from the surface block through blocks that we deem as non-surface, such as leaves, torches, +ladders, fences etc. Note that Cuberite currently implements only the easy solution.
+ +Most worlds in MineCraft have lava lakes at their bottom. Generating these is pretty straightforward: Use +the user-configured depth and replace all the air blocks below this depth with lava blocks. Note however, +that this makes this generator dependent on the order in which the finishers are applied. If the mineshafts +generate before bottom lava, the mineshafts that are below the lava level will get filled with lava. On the +other hand, if bottom lava is generated before the mineshafts, it is possible for a mineshaft to "drill +through" a lake of lava. Cuberite doesn't try to solve this and instead lets the admin choose whichever they +prefer.
+ +There are generators for specific kinds of foliage. The dead bushes in the desert biome and lilypads in +the swamp biome both share the same generating pattern. They are both specific to a single biome and they +both require a specific block underneath them in order to generate. Their implementation is simple: pick +several random columns in the chunk. If the column is of the correct biome and has the correct top block, +add the foliage block on top.
+ +In order to generate the same set of coordinates when the chunk is re-generated, we use the Perlin noise's +basis functions (the ones providing the random values for Perlin cell vertices). These basically work as a +hash function for the coorinates - the same input coordinates generate the same output value. We use the +chunk's coordinates as two of the coords, and the iteration number as the third coordinate, to generate a +random number. We then check the biome and the top block at those coordinates, if they allow, we generate the +foliage block on top.
+ +Another example of specific foliage is the tall grass in the plains biome. There are quite a lot of these +tall grass blocks, it would be inefficient to generate them using the random-coords approach described above. +Instead, we will use a 2D Perlin noise again, with a threshold defining where to put the grass and where +not.
+ +For the flowers, grass, mushrooms in caves etc. we want to use a slightly different algorithm. These +foliage blocks are customarily generated in small "clumps" - there are several blocks of the same type near +together. To generate these, we first select random coords, using the coord hash functions, for a center of a +clump. Then we select the type of block to generate. Finally, we loop over adding a random (coord hash) +number to the clump center coords to get the block where to generate the foliage block:
+ + +In order to make the clump more "round" and "centered", we want the offsets to be closer to the clump +center more often. This is done using a thing called Gaussian function distribution. Instead of having each +random number generate with the same probability, we want higher probability for the numbers around zero, +like this:
+ + +Instead of doing complicated calculations to match this shape exactly, we will use a much easier shape. +By adding together two random numbers in the same range, we get the probability distribution that has a +"roof" shape, enough for our needs:
+ + +(For the curious, there is a proof that adding together infinitely many uniform-distributed random numbers +produces random numbers with the Gaussian distribution.)
+ +This scheme can be used to produce clumps of flowers, when we select the 2D coords of the clump center on +the top surface of the terrain. We simply generate the 2D coords of the foliage blocks and use the terrain +height to find the third coord. If we want to generate clumps of mushrooms in the caves, however, we need to +generate the clump center coords in 3D and either use 3 offsets for the mushrooms, or use 2 offsets plus +searching for the closest opening Y-wise in the terrain.
+ +Note that the clumps generated by this scheme may overlap several chunks. Therefore it's crucial to +actually check the surrounding chunks if their clumps overlap into the currently generated chunk, and apply +those as well, otherwise there will be visible cuts in the foliage along the chunks borders.
+ +Water and lava springs are essential for making the underground quite a lot more interesting. They are +rather easy to generate, but a bit more difficult to get right. Generating simply means that a few random +locations (obtained by our familiar coord hashing) are checked and if the block type in there is stone. Then +we see all the horizontal neighbors of the block, plus the block underneath. If all of them except one are +stone, and the one left is air, our block is suitable for turning into a spring. If there were more air +neighbors, the spring would look somewhat unnatural; if there were no air neighbors, the spring won't flow +anywhere, so it would be rather useless.
+ +The difficult part about springs is the amount of them to generate. There should be a few springs on the +surface, perhaps a bit more in the mountaineous biomes. There should be quite a few more springs underground, +but there should definitely be more water springs than lava springs in the upper levels of the terrain, while +there should be more lava springs and almost no water springs near the bottom. To accomodate this, the +Cuberite team has made a tool that scanned through MineCraft's terrain and counted the amount of both types +of springs in relation to their height. Two curves have been found for the distribution of each type of the +spring:
+ + +Cuberite uses an approximation of the above curves to choose the height at which to generate the +spring.
+ +Caves are definitely one of the main things people notice about MineCraft terrain. There are quite a lot +of different algorithms available to generate terrain with caves, each with different results. Cuberite currently implements three finishers that generate caves:
+(TODO)
+ +Much of the terain generation consists of doing the same thing for every single column or block in a chunk. This +sort of computation is much faster on a GPU as GPUs are massively parallel. High end GPUs can execute up to 30,000 +threads simultaneously, which would allow them to generate every block in half a chunk in parallel or every column +in over 100 chunks in parallel. A naive comparison suggests that a 800MHz GPU with 15,000 threads can execute parallel +code 250 times faster than a 3GHz CPU with 128 bit SIMD. Obviously we want to harness that power.
+ + diff --git a/dev-docs/Login sequence.txt b/dev-docs/Login sequence.txt new file mode 100644 index 000000000..df9d386c6 --- /dev/null +++ b/dev-docs/Login sequence.txt @@ -0,0 +1,26 @@ + +This is the connection sequence of the 1.6.2 client to a vanilla server, after the encryption has been established: + +S->C: 0xfc (encryption key response) empty payload (4 * 0x00) +C->S: 0xcd (client statuses) +S->C: 0x01 (login) +S->C: 0xfa (plugin message) - "MC|Brand": "vanilla" +S->C: 0x06 (compass) +S->C: 0xca (player capabilities) +S->C: 0x10 (slot select) +S->C: 0x04 (time update) +S->C: 0xc9 (player list item) +S->C: 0x0d (player pos + look) +S->C: 0x04 (time update) +S->C: 0x68 (window contents) +S->C: 0x67 (slot contents) - Window -1, slot -1 +S->C: 0x67 (slot contents) - Window 0, slot 9 .. 44, only occupied slots +S->C: 0x38 (chunk bulk) +S->C: 0x28 (entity metadata) player metadata +S->C: 0x2c (entity properties) player properties +S->C: 0x04 (time update) +S->C: 0x38 (chunk bulk) +C->S: 0xcc (client settings / locale and view) +C->S: 0xfa (plugin message) - "MC|Brand": "vanilla" +C->S: 0x0d (player pos + look) +S->C: 0x38 (chunk bulk) \ No newline at end of file diff --git a/dev-docs/NBT Examples/single chunk NBT data.txt b/dev-docs/NBT Examples/single chunk NBT data.txt new file mode 100644 index 000000000..905d6465c --- /dev/null +++ b/dev-docs/NBT Examples/single chunk NBT data.txt @@ -0,0 +1,71 @@ +TAG_Compound(""): 1 items +{ + TAG_Compound("Level"): 10 items + { + TAG_List("TileTicks"): 0 items of type 1 + { + } + TAG_List("Entities"): 0 items of type 1 + { + } + TAG_ByteArray("Biomes"): 256 bytes + TAG_Long("LastUpdate"): 1041959 + TAG_Int("xPos"): 0 + TAG_Int("zPos"): 0 + TAG_List("TileEntities"): 0 items of type 1 + { + } + TAG_Byte("TerrainPopulated"): 1 + TAG_List("Sections"): 6 items of type 10 + { + TAG_Compound(""): 5 items + { + TAG_ByteArray("Data"): 2048 bytes + TAG_ByteArray("SkyLight"): 2048 bytes + TAG_ByteArray("BlockLight"): 2048 bytes + TAG_Byte("Y"): 0 + TAG_ByteArray("Blocks"): 4096 bytes + } + TAG_Compound(""): 5 items + { + TAG_ByteArray("Data"): 2048 bytes + TAG_ByteArray("SkyLight"): 2048 bytes + TAG_ByteArray("BlockLight"): 2048 bytes + TAG_Byte("Y"): 1 + TAG_ByteArray("Blocks"): 4096 bytes + } + TAG_Compound(""): 5 items + { + TAG_ByteArray("Data"): 2048 bytes + TAG_ByteArray("SkyLight"): 2048 bytes + TAG_ByteArray("BlockLight"): 2048 bytes + TAG_Byte("Y"): 2 + TAG_ByteArray("Blocks"): 4096 bytes + } + TAG_Compound(""): 5 items + { + TAG_ByteArray("Data"): 2048 bytes + TAG_ByteArray("SkyLight"): 2048 bytes + TAG_ByteArray("BlockLight"): 2048 bytes + TAG_Byte("Y"): 3 + TAG_ByteArray("Blocks"): 4096 bytes + } + TAG_Compound(""): 5 items + { + TAG_ByteArray("Data"): 2048 bytes + TAG_ByteArray("SkyLight"): 2048 bytes + TAG_ByteArray("BlockLight"): 2048 bytes + TAG_Byte("Y"): 4 + TAG_ByteArray("Blocks"): 4096 bytes + } + TAG_Compound(""): 5 items + { + TAG_ByteArray("Data"): 2048 bytes + TAG_ByteArray("SkyLight"): 2048 bytes + TAG_ByteArray("BlockLight"): 2048 bytes + TAG_Byte("Y"): 5 + TAG_ByteArray("Blocks"): 4096 bytes + } + } + } +} diff --git a/dev-docs/NBT Examples/tile entities.txt b/dev-docs/NBT Examples/tile entities.txt new file mode 100644 index 000000000..e16ae45a7 --- /dev/null +++ b/dev-docs/NBT Examples/tile entities.txt @@ -0,0 +1,109 @@ +TAG_List("TileEntities"): 3 items of type 10 +{ + TAG_Compound(""): 6 items + { + TAG_String("id"): 10 bytes: "MobSpawner" + TAG_Short("Delay"): 20 + TAG_Int("z"): 180 + TAG_String("EntityId"): 6 bytes: "Spider" + TAG_Int("y"): 11 + TAG_Int("x"): -6 + } + TAG_Compound(""): 5 items + { + TAG_List("Items"): 6 items of type 10 + { + TAG_Compound(""): 4 items + { + TAG_Short("id"): 325 + TAG_Short("Damage"): 0 + TAG_Byte("Count"): 1 + TAG_Byte("Slot"): 3 + } + TAG_Compound(""): 4 items + { + TAG_Short("id"): 296 + TAG_Short("Damage"): 0 + TAG_Byte("Count"): 4 + TAG_Byte("Slot"): 5 + } + TAG_Compound(""): 4 items + { + TAG_Short("id"): 325 + TAG_Short("Damage"): 0 + TAG_Byte("Count"): 1 + TAG_Byte("Slot"): 11 + } + TAG_Compound(""): 4 items + { + TAG_Short("id"): 351 + TAG_Short("Damage"): 3 + TAG_Byte("Count"): 1 + TAG_Byte("Slot"): 13 + } + TAG_Compound(""): 4 items + { + TAG_Short("id"): 296 + TAG_Short("Damage"): 0 + TAG_Byte("Count"): 1 + TAG_Byte("Slot"): 14 + } + TAG_Compound(""): 4 items + { + TAG_Short("id"): 287 + TAG_Short("Damage"): 0 + TAG_Byte("Count"): 2 + TAG_Byte("Slot"): 15 + } + } + TAG_String("id"): 5 bytes: "Chest" + TAG_Int("z"): 182 + TAG_Int("y"): 11 + TAG_Int("x"): -7 + } + TAG_Compound(""): 5 items + { + TAG_List("Items"): 5 items of type 10 + { + TAG_Compound(""): 4 items + { + TAG_Short("id"): 289 + TAG_Short("Damage"): 0 + TAG_Byte("Count"): 1 + TAG_Byte("Slot"): 5 + } + TAG_Compound(""): 4 items + { + TAG_Short("id"): 329 + TAG_Short("Damage"): 0 + TAG_Byte("Count"): 1 + TAG_Byte("Slot"): 12 + } + TAG_Compound(""): 4 items + { + TAG_Short("id"): 265 + TAG_Short("Damage"): 0 + TAG_Byte("Count"): 4 + TAG_Byte("Slot"): 17 + } + TAG_Compound(""): 4 items + { + TAG_Short("id"): 296 + TAG_Short("Damage"): 0 + TAG_Byte("Count"): 4 + TAG_Byte("Slot"): 21 + } + TAG_Compound(""): 4 items + { + TAG_Short("id"): 289 + TAG_Short("Damage"): 0 + TAG_Byte("Count"): 2 + TAG_Byte("Slot"): 22 + } + } + TAG_String("id"): 5 bytes: "Chest" + TAG_Int("z"): 181 + TAG_Int("y"): 11 + TAG_Int("x"): -8 + } +} \ No newline at end of file diff --git a/dev-docs/Object ownership.gv b/dev-docs/Object ownership.gv new file mode 100644 index 000000000..29e0407a6 --- /dev/null +++ b/dev-docs/Object ownership.gv @@ -0,0 +1,29 @@ +digraph +{ +rankdir=LR +Root -> Server +Root -> MonsterConfig +Root -> GroupManager +Root -> CraftingRecipes +Root -> FurnaceRecipe +Root -> PluginManager +Root -> Authenticator +Root -> World +Server -> ListenThreadIPv4 +Server -> ListenThreadIPv6 +Server -> ClientHandle +Server -> RCONServer +PluginManager -> Plugin_NewLua +PluginManager -> Plugin +World -> SimulatorManager +World -> SandSimulator +World -> WaterSimulator +World -> LavaSimulator +World -> FireSimulator +World -> RedstoneSimulator +World -> WorldStorage +World -> Player +World -> Generator +World -> ChunkSender +World -> LightingThread +} \ No newline at end of file diff --git a/dev-docs/Plugin API.md b/dev-docs/Plugin API.md new file mode 100644 index 000000000..6a0fd9a0e --- /dev/null +++ b/dev-docs/Plugin API.md @@ -0,0 +1,3 @@ +# Looking for the API documentation for Lua plugins? + +See the [cuberite website](api.cuberite.org) or browse the [source](https://github.com/cuberite/cuberite/tree/master/Server/Plugins/APIDump). diff --git a/dev-docs/SocketThreads states.gv b/dev-docs/SocketThreads states.gv new file mode 100644 index 000000000..5afaa5370 --- /dev/null +++ b/dev-docs/SocketThreads states.gv @@ -0,0 +1,77 @@ +digraph +{ + node + [ + shape="box" + ] + + edge + [ + d + ] + + // Forward-declarations of nodes (so that they are laid out in a specific order: + ssNormal + ssWritingRestOut + ssShuttingDown + ssShuttingDown2 + + // Nodes with special labels / shapes: + ForceClose + [ + label="Force close" + shape="ellipse" + ] + X + [ + label="Socket closed" + shape="ellipse" + ] + + // Edges: + ssNormal -> ssWritingRestOut + [ + label="cSocketThreads::RemoveClient()" + ] + ssWritingRestOut -> ssShuttingDown + [ + label="All outgoing data written" + ] + ssShuttingDown -> ssShuttingDown2 + [ + label="One thread loop" + ] + ssShuttingDown2 -> ForceClose + [ + label="One thread loop" + ] + ssNormal -> ssRemoteClosed + [ + label="Remote closed" + color="red" + fontcolor="red" + ] + ssWritingRestOut -> X + [ + label="Remote closed" + color="red" + fontcolor="red" + ] + ssShuttingDown -> X + [ + label="Remote closed" + color="red" + fontcolor="red" + ] + ssShuttingDown2 -> X + [ + label="Remote closed" + color="red" + fontcolor="red" + ] + ssRemoteClosed -> X + [ + label="cSocketThreads::RemoveClient()" + ] + ForceClose -> X +} diff --git a/dev-docs/Springs.ods b/dev-docs/Springs.ods new file mode 100644 index 000000000..4b3559f1d Binary files /dev/null and b/dev-docs/Springs.ods differ diff --git a/dev-docs/_files.txt b/dev-docs/_files.txt new file mode 100644 index 000000000..85a4253c1 --- /dev/null +++ b/dev-docs/_files.txt @@ -0,0 +1,20 @@ + +Contents of this folder: + +API class inheritance - blockentities.gv + - a GraphViz file to visualise inheritance in the API classes in the Wiki for the cBlockEntity class' descendants + +API class inheritance - entities.gv + - a GraphViz file to visualise inheritance in the API classes in the Wiki for the cEntity class' descendants + +Login Sequence.txt + - Annotated log of packets exchanged between the client and server for login; 1.6.2 protocol + +Object ownership.gv + - a GraphViz file to visualise ownership relations in the Cuberite code architecture + +SocketThreads states.gv + - a GraphViz file documenting the states for individual sockets in cSocketThreads, and transitions between them + +Springs.ods + - a spreadsheet with collected statistics about the occurrence of lava / water springs based on height. diff --git a/dev-docs/img/biomalheights.jpg b/dev-docs/img/biomalheights.jpg new file mode 100644 index 000000000..a01faef87 Binary files /dev/null and b/dev-docs/img/biomalheights.jpg differ diff --git a/dev-docs/img/biomeheights.jpg b/dev-docs/img/biomeheights.jpg new file mode 100644 index 000000000..9dda27b0e Binary files /dev/null and b/dev-docs/img/biomeheights.jpg differ diff --git a/dev-docs/img/biomeheightsavg.jpg b/dev-docs/img/biomeheightsavg.jpg new file mode 100644 index 000000000..c8217cafc Binary files /dev/null and b/dev-docs/img/biomeheightsavg.jpg differ diff --git a/dev-docs/img/biomes.jpg b/dev-docs/img/biomes.jpg new file mode 100644 index 000000000..59c23b870 Binary files /dev/null and b/dev-docs/img/biomes.jpg differ diff --git a/dev-docs/img/densitymap.jpg b/dev-docs/img/densitymap.jpg new file mode 100644 index 000000000..a7a7b3f36 Binary files /dev/null and b/dev-docs/img/densitymap.jpg differ diff --git a/dev-docs/img/distortedvoronoibiomes.png b/dev-docs/img/distortedvoronoibiomes.png new file mode 100644 index 000000000..d56dff347 Binary files /dev/null and b/dev-docs/img/distortedvoronoibiomes.png differ diff --git a/dev-docs/img/finishers.jpg b/dev-docs/img/finishers.jpg new file mode 100644 index 000000000..06f7485c3 Binary files /dev/null and b/dev-docs/img/finishers.jpg differ diff --git a/dev-docs/img/gaussprobability.jpg b/dev-docs/img/gaussprobability.jpg new file mode 100644 index 000000000..77da24748 Binary files /dev/null and b/dev-docs/img/gaussprobability.jpg differ diff --git a/dev-docs/img/grownexample_add_islands.png b/dev-docs/img/grownexample_add_islands.png new file mode 100644 index 000000000..f69faaaf1 Binary files /dev/null and b/dev-docs/img/grownexample_add_islands.png differ diff --git a/dev-docs/img/grownexample_alt_biomes.png b/dev-docs/img/grownexample_alt_biomes.png new file mode 100644 index 000000000..866d774e2 Binary files /dev/null and b/dev-docs/img/grownexample_alt_biomes.png differ diff --git a/dev-docs/img/grownexample_beaches.png b/dev-docs/img/grownexample_beaches.png new file mode 100644 index 000000000..a84fb0eff Binary files /dev/null and b/dev-docs/img/grownexample_beaches.png differ diff --git a/dev-docs/img/grownexample_biome_edges.png b/dev-docs/img/grownexample_biome_edges.png new file mode 100644 index 000000000..58de63aef Binary files /dev/null and b/dev-docs/img/grownexample_biome_edges.png differ diff --git a/dev-docs/img/grownexample_biomes.png b/dev-docs/img/grownexample_biomes.png new file mode 100644 index 000000000..ecd8af29b Binary files /dev/null and b/dev-docs/img/grownexample_biomes.png differ diff --git a/dev-docs/img/grownexample_grp_edges.png b/dev-docs/img/grownexample_grp_edges.png new file mode 100644 index 000000000..2ac32b9a6 Binary files /dev/null and b/dev-docs/img/grownexample_grp_edges.png differ diff --git a/dev-docs/img/grownexample_in1.png b/dev-docs/img/grownexample_in1.png new file mode 100644 index 000000000..2238886ab Binary files /dev/null and b/dev-docs/img/grownexample_in1.png differ diff --git a/dev-docs/img/grownexample_in2.png b/dev-docs/img/grownexample_in2.png new file mode 100644 index 000000000..9ef9f6ae2 Binary files /dev/null and b/dev-docs/img/grownexample_in2.png differ diff --git a/dev-docs/img/grownexample_in3.png b/dev-docs/img/grownexample_in3.png new file mode 100644 index 000000000..95d6608b5 Binary files /dev/null and b/dev-docs/img/grownexample_in3.png differ diff --git a/dev-docs/img/grownexample_in_alt.png b/dev-docs/img/grownexample_in_alt.png new file mode 100644 index 000000000..59979ed62 Binary files /dev/null and b/dev-docs/img/grownexample_in_alt.png differ diff --git a/dev-docs/img/grownexample_in_river.png b/dev-docs/img/grownexample_in_river.png new file mode 100644 index 000000000..58556369d Binary files /dev/null and b/dev-docs/img/grownexample_in_river.png differ diff --git a/dev-docs/img/grownexample_m_biomes.png b/dev-docs/img/grownexample_m_biomes.png new file mode 100644 index 000000000..c3d7079ae Binary files /dev/null and b/dev-docs/img/grownexample_m_biomes.png differ diff --git a/dev-docs/img/grownexample_mix_river.png b/dev-docs/img/grownexample_mix_river.png new file mode 100644 index 000000000..81899a8c7 Binary files /dev/null and b/dev-docs/img/grownexample_mix_river.png differ diff --git a/dev-docs/img/grownexample_river.png b/dev-docs/img/grownexample_river.png new file mode 100644 index 000000000..cb07f44dd Binary files /dev/null and b/dev-docs/img/grownexample_river.png differ diff --git a/dev-docs/img/grownexample_set_rnd.png b/dev-docs/img/grownexample_set_rnd.png new file mode 100644 index 000000000..5b7a2d254 Binary files /dev/null and b/dev-docs/img/grownexample_set_rnd.png differ diff --git a/dev-docs/img/grownexample_smooth.png b/dev-docs/img/grownexample_smooth.png new file mode 100644 index 000000000..bfd43f6ef Binary files /dev/null and b/dev-docs/img/grownexample_smooth.png differ diff --git a/dev-docs/img/grownexample_zoom.png b/dev-docs/img/grownexample_zoom.png new file mode 100644 index 000000000..7afffe50b Binary files /dev/null and b/dev-docs/img/grownexample_zoom.png differ diff --git a/dev-docs/img/heightmap.jpg b/dev-docs/img/heightmap.jpg new file mode 100644 index 000000000..c7eb5c865 Binary files /dev/null and b/dev-docs/img/heightmap.jpg differ diff --git a/dev-docs/img/jittergrid.jpg b/dev-docs/img/jittergrid.jpg new file mode 100644 index 000000000..f8066aa72 Binary files /dev/null and b/dev-docs/img/jittergrid.jpg differ diff --git a/dev-docs/img/jittergridlocality.jpg b/dev-docs/img/jittergridlocality.jpg new file mode 100644 index 000000000..64414c878 Binary files /dev/null and b/dev-docs/img/jittergridlocality.jpg differ diff --git a/dev-docs/img/multistepmapbiomes.png b/dev-docs/img/multistepmapbiomes.png new file mode 100644 index 000000000..d32ac3d8e Binary files /dev/null and b/dev-docs/img/multistepmapbiomes.png differ diff --git a/dev-docs/img/multistepmapdistance.jpg b/dev-docs/img/multistepmapdistance.jpg new file mode 100644 index 000000000..9f7cfd11b Binary files /dev/null and b/dev-docs/img/multistepmapdistance.jpg differ diff --git a/dev-docs/img/multistepmapgrid.jpg b/dev-docs/img/multistepmapgrid.jpg new file mode 100644 index 000000000..51dd81c46 Binary files /dev/null and b/dev-docs/img/multistepmapgrid.jpg differ diff --git a/dev-docs/img/perlin.jpg b/dev-docs/img/perlin.jpg new file mode 100644 index 000000000..499fcdeae Binary files /dev/null and b/dev-docs/img/perlin.jpg differ diff --git a/dev-docs/img/perlincompositor1.jpg b/dev-docs/img/perlincompositor1.jpg new file mode 100644 index 000000000..0d8f93cd9 Binary files /dev/null and b/dev-docs/img/perlincompositor1.jpg differ diff --git a/dev-docs/img/perlincompositor2.jpg b/dev-docs/img/perlincompositor2.jpg new file mode 100644 index 000000000..11fc5b51d Binary files /dev/null and b/dev-docs/img/perlincompositor2.jpg differ diff --git a/dev-docs/img/perlincompositor3.jpg b/dev-docs/img/perlincompositor3.jpg new file mode 100644 index 000000000..46a2583ba Binary files /dev/null and b/dev-docs/img/perlincompositor3.jpg differ diff --git a/dev-docs/img/perlinheightmap.jpg b/dev-docs/img/perlinheightmap.jpg new file mode 100644 index 000000000..d941a2fc6 Binary files /dev/null and b/dev-docs/img/perlinheightmap.jpg differ diff --git a/dev-docs/img/perlinrivers1.jpg b/dev-docs/img/perlinrivers1.jpg new file mode 100644 index 000000000..b11373fa7 Binary files /dev/null and b/dev-docs/img/perlinrivers1.jpg differ diff --git a/dev-docs/img/perlinrivers2.jpg b/dev-docs/img/perlinrivers2.jpg new file mode 100644 index 000000000..bbbcaa276 Binary files /dev/null and b/dev-docs/img/perlinrivers2.jpg differ diff --git a/dev-docs/img/perlinrivers3.jpg b/dev-docs/img/perlinrivers3.jpg new file mode 100644 index 000000000..3cf043e6e Binary files /dev/null and b/dev-docs/img/perlinrivers3.jpg differ diff --git a/dev-docs/img/roofprobability.jpg b/dev-docs/img/roofprobability.jpg new file mode 100644 index 000000000..e7a155113 Binary files /dev/null and b/dev-docs/img/roofprobability.jpg differ diff --git a/dev-docs/img/smallfoliageclumps.jpg b/dev-docs/img/smallfoliageclumps.jpg new file mode 100644 index 000000000..4cc6cbc00 Binary files /dev/null and b/dev-docs/img/smallfoliageclumps.jpg differ diff --git a/dev-docs/img/smoothedgrown_1.png b/dev-docs/img/smoothedgrown_1.png new file mode 100644 index 000000000..16e563f96 Binary files /dev/null and b/dev-docs/img/smoothedgrown_1.png differ diff --git a/dev-docs/img/smoothedgrown_2.png b/dev-docs/img/smoothedgrown_2.png new file mode 100644 index 000000000..2d97cfb4e Binary files /dev/null and b/dev-docs/img/smoothedgrown_2.png differ diff --git a/dev-docs/img/smoothedgrown_3.png b/dev-docs/img/smoothedgrown_3.png new file mode 100644 index 000000000..2d4d13f49 Binary files /dev/null and b/dev-docs/img/smoothedgrown_3.png differ diff --git a/dev-docs/img/smoothedgrown_4.png b/dev-docs/img/smoothedgrown_4.png new file mode 100644 index 000000000..d52a34bfe Binary files /dev/null and b/dev-docs/img/smoothedgrown_4.png differ diff --git a/dev-docs/img/smoothedgrown_5.png b/dev-docs/img/smoothedgrown_5.png new file mode 100644 index 000000000..ae14d9847 Binary files /dev/null and b/dev-docs/img/smoothedgrown_5.png differ diff --git a/dev-docs/img/smoothedgrown_6.png b/dev-docs/img/smoothedgrown_6.png new file mode 100644 index 000000000..0a7f17595 Binary files /dev/null and b/dev-docs/img/smoothedgrown_6.png differ diff --git a/dev-docs/img/smoothedgrown_7.png b/dev-docs/img/smoothedgrown_7.png new file mode 100644 index 000000000..4351d6881 Binary files /dev/null and b/dev-docs/img/smoothedgrown_7.png differ diff --git a/dev-docs/img/temperaturehumiditydecisionhills.jpg b/dev-docs/img/temperaturehumiditydecisionhills.jpg new file mode 100644 index 000000000..c755df158 Binary files /dev/null and b/dev-docs/img/temperaturehumiditydecisionhills.jpg differ diff --git a/dev-docs/img/temperaturehumiditydecisionsimple.jpg b/dev-docs/img/temperaturehumiditydecisionsimple.jpg new file mode 100644 index 000000000..cbb1271b5 Binary files /dev/null and b/dev-docs/img/temperaturehumiditydecisionsimple.jpg differ diff --git a/dev-docs/img/terraincomposition.jpg b/dev-docs/img/terraincomposition.jpg new file mode 100644 index 000000000..3d03e101d Binary files /dev/null and b/dev-docs/img/terraincomposition.jpg differ diff --git a/dev-docs/img/terrainheight.jpg b/dev-docs/img/terrainheight.jpg new file mode 100644 index 000000000..bcbafcfaf Binary files /dev/null and b/dev-docs/img/terrainheight.jpg differ diff --git a/dev-docs/img/twolevelbiomes.png b/dev-docs/img/twolevelbiomes.png new file mode 100644 index 000000000..a3104733f Binary files /dev/null and b/dev-docs/img/twolevelbiomes.png differ diff --git a/dev-docs/img/twolevellargeareas.jpg b/dev-docs/img/twolevellargeareas.jpg new file mode 100644 index 000000000..9d5d5ac8a Binary files /dev/null and b/dev-docs/img/twolevellargeareas.jpg differ diff --git a/dev-docs/img/twolevelsmallareas.jpg b/dev-docs/img/twolevelsmallareas.jpg new file mode 100644 index 000000000..14afbc42a Binary files /dev/null and b/dev-docs/img/twolevelsmallareas.jpg differ diff --git a/dev-docs/img/twolevelsmallgrid.jpg b/dev-docs/img/twolevelsmallgrid.jpg new file mode 100644 index 000000000..6c75e0b28 Binary files /dev/null and b/dev-docs/img/twolevelsmallgrid.jpg differ diff --git a/dev-docs/img/vanilla_springs_huge.png b/dev-docs/img/vanilla_springs_huge.png new file mode 100644 index 000000000..694389c85 Binary files /dev/null and b/dev-docs/img/vanilla_springs_huge.png differ diff --git a/dev-docs/img/voronoi.png b/dev-docs/img/voronoi.png new file mode 100644 index 000000000..e61e183ef Binary files /dev/null and b/dev-docs/img/voronoi.png differ diff --git a/dev-docs/img/voronoijitterbiomes.png b/dev-docs/img/voronoijitterbiomes.png new file mode 100644 index 000000000..42f0b7e40 Binary files /dev/null and b/dev-docs/img/voronoijitterbiomes.png differ diff --git a/dev-docs/img/zoomedgrown_1.png b/dev-docs/img/zoomedgrown_1.png new file mode 100644 index 000000000..c73326b0e Binary files /dev/null and b/dev-docs/img/zoomedgrown_1.png differ diff --git a/dev-docs/img/zoomedgrown_2.png b/dev-docs/img/zoomedgrown_2.png new file mode 100644 index 000000000..45fa6427f Binary files /dev/null and b/dev-docs/img/zoomedgrown_2.png differ diff --git a/dev-docs/img/zoomedgrown_3.png b/dev-docs/img/zoomedgrown_3.png new file mode 100644 index 000000000..3c9d89759 Binary files /dev/null and b/dev-docs/img/zoomedgrown_3.png differ diff --git a/dev-docs/img/zoomedgrown_4.png b/dev-docs/img/zoomedgrown_4.png new file mode 100644 index 000000000..221a5fc76 Binary files /dev/null and b/dev-docs/img/zoomedgrown_4.png differ diff --git a/dev-docs/img/zoomedgrown_5.png b/dev-docs/img/zoomedgrown_5.png new file mode 100644 index 000000000..3881532ca Binary files /dev/null and b/dev-docs/img/zoomedgrown_5.png differ diff --git a/dev-docs/img/zoomedgrown_6.png b/dev-docs/img/zoomedgrown_6.png new file mode 100644 index 000000000..cc03d2150 Binary files /dev/null and b/dev-docs/img/zoomedgrown_6.png differ diff --git a/dev-docs/img/zoomedgrown_7.png b/dev-docs/img/zoomedgrown_7.png new file mode 100644 index 000000000..7a9f43aac Binary files /dev/null and b/dev-docs/img/zoomedgrown_7.png differ diff --git a/dev-docs/js/ValueMap.js b/dev-docs/js/ValueMap.js new file mode 100644 index 000000000..31ddba27e --- /dev/null +++ b/dev-docs/js/ValueMap.js @@ -0,0 +1,139 @@ + +const g_DistanceBetweenSquares = 0;//.01; +const g_Colors = [ + "#0000FF", + "#00FF00", + "#FF0000", + "#FF00FF", + "#00FFFF", + "#FFFF00", + "#000000", + "#9BADFF" +] + +class ValueMap { + constructor() { + this.values = new Uint8Array(4 * 4); + this.sizeX = 4; + this.sizeZ = 4; + this.reset(); + } + + + reset() { + this.sizeX = 4; + this.sizeZ = 4; + this.values = new Uint8Array(this.sizeX * this.sizeZ); + + for (let x = 0; x < this.sizeX; x++) + { + for (let z = 0; z < this.sizeZ; z++) + { + this.values[x + this.sizeZ * z] = Math.floor(Math.random() * 8); + } + } + } + + + chooseRandomNumber() { + let numArguments = arguments.length; + return arguments[Math.floor(Math.random() * arguments.length)]; + } + + + smooth() { + let sizeZ = this.sizeZ - 2; + let sizeX = this.sizeX - 2; + let cache = new Uint8Array((this.sizeX - 2) * (this.sizeZ - 2)); + for (let z = 0; z < sizeZ; z++) + { + for (let x = 0; x < sizeX; x++) + { + let val = this.values[x + 1 + (z + 1) * this.sizeX]; + let above = this.values[x + 1 + z * this.sizeX]; + let below = this.values[x + 1 + (z + 2) * this.sizeX]; + let left = this.values[x + (z + 1) * this.sizeX]; + let right = this.values[x + 2 + (z + 1) * this.sizeX]; + + if ((left == right) && (above == below)) + { + if (Math.random() < 0.5) + { + val = left; + } + else + { + val = below; + } + } + else + { + if (left == right) + { + val = left; + } + if (above == below) + { + val = above; + } + } + cache[x + z * sizeX] = val; + } + } + this.values = cache; + this.sizeX -= 2; + this.sizeZ -= 2; + } + + + zoom() { + let lowStepX = (this.sizeX - 1) * 2; + let lowStepZ = (this.sizeZ - 1) * 2; + let cache = new Uint8Array(lowStepX * lowStepZ); + for (let z = 0; z < this.sizeZ - 1; z++) + { + let idx = (z * 2) * lowStepX; + let PrevZ0 = this.values[z * this.sizeX]; + let PrevZ1 = this.values[(z + 1) * this.sizeX]; + + for (let x = 0; x < this.sizeX - 1; x++) + { + let ValX1Z0 = this.values[x + 1 + z * this.sizeX]; + let ValX1Z1 = this.values[x + 1 + (z + 1) * this.sizeX]; + cache[idx] = PrevZ0; + cache[idx + lowStepX] = this.chooseRandomNumber(PrevZ0, PrevZ1); + cache[idx + 1] = this.chooseRandomNumber(PrevZ0, ValX1Z0); + cache[idx + 1 + lowStepX] = this.chooseRandomNumber(PrevZ0, ValX1Z0, PrevZ1, ValX1Z1); + idx += 2; + PrevZ0 = ValX1Z0; + PrevZ1 = ValX1Z1; + } + } + this.values = cache; + this.sizeX = lowStepX; + this.sizeZ = lowStepZ; + } + + + visualize(context, canvas) { + context.clearRect(0, 0, canvas.width, canvas.height); + const squareSizeX = canvas.width / (this.sizeX - 1) - g_DistanceBetweenSquares; + const squareSizeY = canvas.height / (this.sizeZ - 1) - g_DistanceBetweenSquares; + for (let x = 0; x < this.sizeX - 1; x++) + { + for (let y = 0; y < this.sizeZ - 1; y++) + { + let renderX = canvas.width / (this.sizeX - 1) * x + g_DistanceBetweenSquares; + let renderY = canvas.height / (this.sizeZ - 1) * y + g_DistanceBetweenSquares; + context.fillStyle = g_Colors[this.values[x + y * this.sizeZ]]; + context.fillRect(renderX, renderY, squareSizeX, squareSizeY); + } + } + + context.save(); + context.globalCompositeOperation = 'difference'; + context.fillStyle = 'white'; + context.fillText("Size: " + (this.sizeX - 1) + "x" + (this.sizeZ - 1), 5, 10); + context.restore(); + } +} diff --git a/dev-docs/js/grown.js b/dev-docs/js/grown.js new file mode 100644 index 000000000..1658ce21c --- /dev/null +++ b/dev-docs/js/grown.js @@ -0,0 +1,79 @@ + +let g_Canvas = null; +let g_Context = null; +let g_ValueMap = null; + + + + +function init() { + g_Canvas = document.getElementById("stage"); + g_Context = g_Canvas.getContext("2d"); + g_ValueMap = new ValueMap(); + g_ValueMap.visualize(g_Context, g_Canvas); +} + + + +function btnZoom(btn) { + g_ValueMap.zoom(); + g_ValueMap.visualize(g_Context, g_Canvas); + if ( + (g_ValueMap.sizeX * 2 - 1 > 600) || + (g_ValueMap.sizeZ * 2 - 1 > 600) + ) { + btn.disabled = true; + } +} + + + +function btnSmooth() { + g_ValueMap.smooth(); + g_ValueMap.visualize(g_Context, g_Canvas); +} + + +function btnReset() { + g_ValueMap.reset(); + g_ValueMap.visualize(g_Context, g_Canvas); + + document.getElementById("grownZoomButton").disabled = false; +} + + +function btnAutomatic(target) { + target.disabled = true; + document.getElementById("grownZoomButton").disabled = true; + + // Reset the valuemap. We don't want to continue on a 500x500 map. + g_ValueMap.reset(); + g_ValueMap.visualize(g_Context, g_Canvas); + + const animationTimeBetween = 350; + let zoom = () => { g_ValueMap.zoom() }; + let smooth = () => { g_ValueMap.smooth() }; + let actions = []; + + for (let i = 0; i < 6; i++) actions.push(zoom); // First zoom 6 times + for (let i = 0; i < 3; i++) actions.push(smooth); // Then smooth 3 times + for (let i = 0; i < 2; i++) actions.push(zoom); // Zoom 2 times + for (let i = 0; i < 2; i++) actions.push(smooth); // And finally smooth 2 more times. + + let update = () => { + if (actions[0] == null) { + target.disabled = false; + return; + } + + actions[0].call(); + g_ValueMap.visualize(g_Context, g_Canvas); + + actions.splice(0, 1); + setTimeout(update, animationTimeBetween); + }; + setTimeout(update, animationTimeBetween + 500); +} + + +window.onload = init; diff --git a/dev-docs/style.css b/dev-docs/style.css new file mode 100644 index 000000000..74419d8da --- /dev/null +++ b/dev-docs/style.css @@ -0,0 +1,12 @@ +table +{ + border: 1px outset; + border-spacing: 0px; + border-collapse: separate; +} + +td, th +{ + border: 1px inset +} + diff --git a/docs/.gitignore b/docs/.gitignore deleted file mode 100644 index f6caf1c11..000000000 --- a/docs/.gitignore +++ /dev/null @@ -1,7 +0,0 @@ -# PNG file with no filename is created by the GraphViz's GVEdit when displaying .gv files -.png - -# The PNG files generated from their .gv sources by GraphViz: -API class inheritance - blockentities.png -API class inheritance - entities.png -SocketThreads states.png diff --git a/docs/API class inheritance - blockentities.gv b/docs/API class inheritance - blockentities.gv deleted file mode 100644 index 966588c5f..000000000 --- a/docs/API class inheritance - blockentities.gv +++ /dev/null @@ -1,15 +0,0 @@ -digraph -{ -rankdir=LR - -cBlockEntity -> cBlockEntityWithItems -cBlockEntity -> cJukeboxEntity -cBlockEntity -> cNoteEntity -cBlockEntity -> cSignEntity -cBlockEntityWithItems -> cChestEntity -cBlockEntityWithItems -> cDropSpenserEntity -cBlockEntityWithItems -> cFurnaceEntity -cBlockEntityWithItems -> cHopperEntity -cDropSpenserEntity -> cDropperEntity -cDropSpenserEntity -> cDispenserEntity -} \ No newline at end of file diff --git a/docs/API class inheritance - entities.gv b/docs/API class inheritance - entities.gv deleted file mode 100644 index 4e167e1d3..000000000 --- a/docs/API class inheritance - entities.gv +++ /dev/null @@ -1,44 +0,0 @@ -digraph -{ -rankdir=LR - -# Entities: -cEntity -> cFallingBlock -cEntity -> cMinecart -cEntity -> cPawn -cEntity -> cPickup -cEntity -> cTNT -cMinecart -> cEmptyMinecart -cMinecart -> cMinecartWithChest -cMinecart -> cMinecartWithFurnace - -# Mobs: -cPawn -> cMonster -cMonster -> cAggressiveMonster -cMonster -> cPassiveMonster -cAggressiveMonster -> cPassiveAggressiveMonster -cPassiveMonster -> cBat -cAggressiveMonster -> cBlaze -cAggressiveMonster -> cCaveSpider -cPassiveMonster -> cChicken -cPassiveMonster -> cCow -cAggressiveMonster -> cCreeper -cPassiveAggressiveMonster -> cEnderman -cAggressiveMonster -> cGhast -cAggressiveMonster -> cMagmaCube -cPassiveMonster -> cMooshroom -cPassiveMonster -> cOcelot -cPassiveMonster -> cPig -cPassiveMonster -> cSheep -cAggressiveMonster -> cSilverfish -cAggressiveMonster -> cSkeleton -cAggressiveMonster -> cSlime -cAggressiveMonster -> cSpider -cPassiveMonster -> cSquid -cPassiveMonster -> cVillager -cAggressiveMonster -> cWitch -cPassiveAggressiveMonster -> cWolf -cAggressiveMonster -> cZombie -cPassiveAggressiveMonster -> cZombiePigman -cPawn -> cPlayer -} \ No newline at end of file diff --git a/docs/Cubeset file format.html b/docs/Cubeset file format.html deleted file mode 100644 index 6ead2e700..000000000 --- a/docs/Cubeset file format.html +++ /dev/null @@ -1,325 +0,0 @@ - - -We're seeing an increased need to store "prefabs" - little areas with predefined block contents, such as village houses or fortress rooms - in collections. We have one collection of village houses for the plains village, one collection for the desert village, one collection for the nether fortress... And there are plans in the future to use even more collections - trees, overworld fortresses, more village types and even custom structures. The point that they have in common is that they need to store not only the prefabs, but also metadata for those prefabs - how often they generate, how they connect together. There's even need for metadata for the entire collection, such as what the accepted biomes are, what block should village roads use, and various other generator parameters. So we need a file format that could store all this information together.
-There are some existing formats available to consider first: -
-With Lua, we could store any metadata for the prefabs, any additional information related to the entire set of prefabs. There's nothing stopping us from adding more items in a backward- and forward-compatible way. The prefabs can be stored very similar to the cpp format, an array of strings plus a charmap, or they can be stored externally in individual .schematic files and the Lua file would provide just the metadata. The server has already vast amounts of Lua-related support code that can be used for accessing the data. In the end this became the chosen solution. The format has been named "Cubeset" - a set of cube-based prefabs.
- --The Cubeset file has a .cubeset extension. Internally it is a Lua source file that provides a global value, Cubeset, which is a table containing the structured data. The loader checks the file's first 8 KiB to see if there is a "CubesetFormatVersion =" string in it (note the space between the word and the equals-sign), and if it is, the file is considered a Cubeset file and further loading is attempted. It is therefore crucial that tools producing this file format include this string as early as possible in the file.
--The top-level Cubeset table must contain at least two sub-tables: Metadata and Pieces. The Metadata table contains the metadata relevant to the entire set of prefabs in the file, the Pieces table contains the definitions and metadata for the individual prefabs. It is advised to make the Metadata table the first one, because it contains the signature used to identify the file ("CubesetFormatVersion ="). Apart from these two subtables the server ignores everything else.
- --The Cubeset.Metadata table is used to store metadata for the entire set of prefabs, and also for the format and version identification. It is a regular dictionary-style Lua table; the following elements are recognized: -
Name | Type | Content |
---|---|---|
CubesetFormatVersion | number | This is the format identification and at the same time it specifies the version of the file. Currently the file version is 1. Note that Cuberite checks the presence of the string "CubesetFormatVersion =", including the space between the word and the equals-sign, within the first 8 KiB of the file. |
ExportDate | string | The date when this file was exported, in the ISO format ("2015-06-16 13:34:03"). Inserted by GalExport for versioning purposes. Ignored elsewhere. |
ExternalSchematic | boolean | Flag inserted by GalExport to indicate that the individual prefabs are stored in separate .schematic files. Ignored elsewhere. |
IntendedUse | string | String identifying the generator part that the cubeset file is intended for. The server logs a warning when loading a cubeset file without an IntendedUse metadata; individual generators log a warning if a wrong IntendedUse is detected in a file they are asked to use. |
Additional values are recognized by the specific generator (which is indicated by the IntendedUse value):
-Generator (IntendedUse) | Name | Type | Content | Notes |
---|---|---|---|---|
Village / PieceStructures / Trees | AllowedBiomes | string | Comma-separated list of biomes | The generator only generates the structure / village / tree in the specified biomes. If empty, all biomes are eligible. |
Village | MaxDensity | number | Maximum density (0 - 100) at which the connectors are populated. | The village generator picks a density between Min and Max, and then only that many percent of the free connectors are actually attempted. This eventually reduces the number of houses to make the village look more "roomy". |
MinDensity | number | Minimum density (0 - 100) at which the connectors are populated. | ||
VillageRoadBlockType | number | The block type used in the village for regular roads on the solid surface | The generator replaces the top terrain block with the specified block type and meta to form the roads. The generator can distinguish when it's replacing water and when it's replacing regular terrain, allowing the villages to include "bridges" as their roads. | |
VillageRoadBlockMeta | number | The block meta used in the village for regular roads on the solid surface | ||
VillageWaterRoadBlockType | number | The block type used in the village for roads on the surface of water | ||
VillageWaterRoadBlockMeta | number | The block meta used in the village for roads on the surface of water | ||
PieceStructures | GridSizeX | number | Size, in blocks, of the seed grid | The generator starts each generated structure in a "seed", these two parameters control the (average) distances between two adjacent seeds. |
GridSizeZ | number | |||
MaxOffsetX | number | Maximum offset, in blocks, of the seed from the grid's center | The generator starts each generated structure in a "seed", these two parameters control the maximum distance of the seed from the regular grid (specified by GridSizeX and GridSizeZ). When zero, the structures are generated exactly on a rectangular grid. Recommended value is about half of the grid's size. | |
MaxOffsetZ | number | |||
MaxStructureSizeX | number | Size, in blocks, of the bounding box for a single structure. | The generator will not connect any prefab to the rest of the structure if it crosses the bounding box. | |
MaxStructureSizeZ | number | |||
MaxDepth | number | Maximum depth of the generated piece tree | The maximum number of connectors, away from the starting piece | |
SeedOffset | number | Number that is added to world's seed for this generator | Each cubeset file should have a different seed offset, otherwise it runs the risk of its structures being generated directly over other cubeset file's that the server admin has enabled. Since the seed is used for placement, if two cubeset files share the same seed, they will share the structure placement as well. |
-The Cubeset.Pieces table is an array containing individual prefabs. Each element describes a single prefab and its associated metadata. The following values are recognized: -
Name | Type | Content |
---|---|---|
OriginData | table | Inserted by GalExport to identify the gallery area from which the prefab is exported. Ignored elsewhere. |
Hitbox | table | The relative coords of the prefab's hitbox (where the prefab is not allowed to overlap another prefab when generating). Members: MinX, MinY, MinZ, MaxX, MaxY, MaxZ, all numbers. |
Connectors | table | Array of connector definitions. The table must be present for each prefab, even if the prefab doesn't have any connectors (use an empty table, then). |
SchematicFileName | string | Name of the .schematic file that contains the block data for the prefab. |
Size | table | Table containing the dimensions of the prefab, if it is inlined into the BlockData element. Contains three members, x, y, z, each is a number. |
BlockData | table | Array of strings that are processed to produce the block data for the prefab. Each letter in the string corresponds to one block, the type of the block is translated through the BlockDefinitions table. The blocks are ordered YZX, that is, the X coord varies the most. |
BlockDefinitions | table | Array of strings that defines the mapping of each letter in BlockData to a specific blocktype. Each string should have the format "Letter: BlockType: BlockMeta". |
Metadata | table | Dictionary-style table of various per-prefab metadata values. |
-The connector definitions table is an array of tables, each element describing one connector. The following values are recognized: -
Name | type | Content | ||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Type | number | The connector's type. The piece generator will only connect the connectors of inverse types ('1'-type connector will connect only to '-1'-type connector). | ||||||||||||||
RelX | number | X coord of the connector, relative to the prefab's zero point ({0, 0, 0} - the first block in the image). | ||||||||||||||
RelY | number | Y coord of the connector, relative to the prefab's zero point ({0, 0, 0} - the first block in the image). | ||||||||||||||
RelZ | number | Z coord of the connector, relative to the prefab's zero point ({0, 0, 0} - the first block in the image). | ||||||||||||||
Direction | number | The direction in which the connector is facing. Corresponds to the eBlockFace constants:
-
|
-Each piece contains additional metadata describing its properties. The server ignores metadata that it doesn't understand. The following values are recognized: -
Name | Type | IsRequired | Contents |
---|---|---|---|
IsStarting | number | Yes | Zero means that the piece is a regular piece, nonzero means that the piece is a starting piece (the "seed" of the structure). Required even for cubesets that don't represent a piece-generator data (such as trees). |
AllowedRotations | number | Number representing a bitmask for which rotations the piece supports. Defaults to 0 (no rotations). Bit 0 (value 1) represents whether 1 counter-clockwise rotation is allowed, bit 1 (value 2) represents whether 2 rotations (180 degrees) are allowed, bit 2 (value 4) represents whether 1 clockwise rotation is allowed. | |
AddWeightIfSame | number | How much weight (chance to generate) should the piece generator add to this piece when the parent piece is the same. It is possible to have negative values, meaning that the piece doesn't like repeating itself. Defaults to 0. | |
DefaultWeight | number | How much weight (chance to generate) does the piece have by default, without any modifiers (AddWeightIfSame, DepthWeight). Defaults to 0. | |
DepthWeight | string | Override for DefaultWeight for specific depth (in the tree used by the piece generator). String in the format "Depth1:Weight1|Depth2:Weight2|...". Each unlisted depth gets the DefaultWeight. Defaults to empty string (no override). | |
MergeStrategy | string | Which merge strategy should be used when drawing the prefab into the world. String representation of one of the cBlockArea:eMergeStrategy constants: "msOverwrite", "msFillAir", "msImprint", "msLake", "msSpongePrint", "msDifference", "msSimpleCompare", "msMask". Defaults to "msSpongePrint". | |
MoveToGround | number | Zero means that the piece will stay where generated by the piece generator, nonzero means that the piece will be moved Y-wise so that its first connector will be on the top block of the existing terrain. Useful for village houses. Defaults to 0. | |
ShouldExpandFloor | number | Nonzero means that the prefab's lowest slice will be repeated downwards until it hits a solid block, effectively creating a foundation for the piece. Useful for nether fortresses and village houses. Defaults to 0. |
-The following example defines a cubeset with two pieces. The first piece is inlined into the cubeset file, the second piece uses an external schematic file.
--Cubeset = -{ - Metadata = - { - CubesetFormatVersion = 1, - IntendedUse = "PieceStructures", - GridSizeX = 128, - GridSizeZ = 128, - MaxStructureSizeX = 64, - MaxStructureSizeZ = 64, - MaxOffsetX = 16, - MaxOffsetZ = 16, - MaxDepth = 4, - SeedOffset = 13, - }, - - Pieces = - { - -- The following piece was exported from the Gallery server by the GalExport plugin in the "cubeset" format: - { - OriginData = - { - ExportName = "DarkCorridor", - Name = "Nether 3", - GalleryName = "Nether", - GalleryIndex = "3", - ID = "30", - CreatorName = "STR_Warrior", - }, - Size = - { - x = 14, - y = 6, - z = 5, - }, - Hitbox = - { - MinX = 0, - MinY = 0, - MinZ = 0, - MaxX = 13, - MaxY = 5, - MaxZ = 4, - }, - Connectors = - { - { - Type = 1, - RelX = 0, - RelY = 1, - RelZ = 2, - Direction = 4, -- X- - }, - { - Type = 1, - RelX = 13, - RelY = 1, - RelZ = 2, - Direction = 5, -- X+ - }, - { - Type = -1, - RelX = 0, - RelY = 1, - RelZ = 2, - Direction = 4, -- X- - }, - { - Type = -1, - RelX = 13, - RelY = 1, - RelZ = 2, - Direction = 5, -- X+ - }, - }, - Metadata = - { - ["DefaultWeight"] = "100", - ["IsStarting"] = "0", - ["AllowedRotations"] = "7", - ["MergeStrategy"] = "msSpongePrint", - ["DepthWeight"] = "", - ["ShouldExpandFloor"] = "1", - ["MoveToGround"] = "0", - ["AddWeightIfSame"] = "0", - }, - BlockDefinitions = - { - ".: 0: 0", -- air - "a:112: 0", -- netherbrick - "b:113: 0", -- netherbrickfence - "c:114: 2", -- netherbrickstairs - "d:114: 3", -- netherbrickstairs - "m: 19: 0", -- sponge - }, - BlockData = - { - -- Level 0 - "aaaaaaaaaaaaaa", -- 0 - "aaaaaaaaaaaaaa", -- 1 - "aaaaaaaaaaaaaa", -- 2 - "aaaaaaaaaaaaaa", -- 3 - "aaaaaaaaaaaaaa", -- 4 - - -- Level 1 - "aaaaaaaaaaaaaa", -- 0 - "..............", -- 1 - "..............", -- 2 - "..............", -- 3 - "aaaaaaaaaaaaaa", -- 4 - - -- Level 2 - "aabaaaaaaaabaa", -- 0 - "..............", -- 1 - "..............", -- 2 - "..............", -- 3 - "aabaaaaaaaabaa", -- 4 - - -- Level 3 - "aabaaaaaaaabaa", -- 0 - "..............", -- 1 - "..............", -- 2 - "..............", -- 3 - "aabaaaaaaaabaa", -- 4 - - -- Level 4 - "aabaaaaaaaabaa", -- 0 - "..............", -- 1 - "..............", -- 2 - "..............", -- 3 - "aabaaaaaaaabaa", -- 4 - - -- Level 5 - "cccccccccccccc", -- 0 - "aaaaaaaaaaaaaa", -- 1 - "aaaaaaaaaaaaaa", -- 2 - "aaaaaaaaaaaaaa", -- 3 - "dddddddddddddd", -- 4 - }, - }, -- DarkCorridor - - -- The following piece was exported from the Gallery server by the GalExport plugin in the "cubesetext" format: - { - OriginData = - { - ExportName = "DoublePlantBed", - Name = "Plains 5", - GalleryName = "Plains", - GalleryIndex = "5", - ID = "20", - CreatorName = "tonibm1999", - }, - Size = - { - x = 15, - y = 8, - z = 9, - }, - Hitbox = - { - MinX = 0, - MinY = 0, - MinZ = 0, - MaxX = 14, - MaxY = 7, - MaxZ = 8, - }, - Connectors = - { - { - Type = -1, - RelX = 7, - RelY = 2, - RelZ = 8, - Direction = 3, -- Z+ - }, - }, - Metadata = - { - ["DefaultWeight"] = "100", - ["IsStarting"] = "0", - ["AllowedRotations"] = "7", - ["MergeStrategy"] = "msSpongePrint", - ["DepthWeight"] = "", - ["ShouldExpandFloor"] = "1", - ["MoveToGround"] = "1", - ["AddWeightIfSame"] = "0", - }, - SchematicFile = "PlainsVillage/20.schematic", - }, -- DoublePlantBed - } -- Pieces -} -- - - diff --git a/docs/Generator.html b/docs/Generator.html deleted file mode 100644 index 4d17826c1..000000000 --- a/docs/Generator.html +++ /dev/null @@ -1,697 +0,0 @@ - - -
This article explains the principles behind the terrain generator in Cuberite. It is not strictly specific to Cuberite, though, it can be viewed as a generic guide to various terrain-generating algorithms, with specific implementation notes regarding Cuberite.
- -Contents: -
The nature has many complicated geological, physical and biological processes working on all scales from microscopic to planet-wide scale, that have shaped the terrain into what we see today. The tectonic plates collide, push mountain ranges up and ocean trenches down. Erosion dulls the sharp shapes. Plantlife takes over to further change the overall look of the world.
- -Generally speaking, the processes take what's there and change it. Unlike computer generating, which usually creates a finished terrain from scratch, or maybe with only a few iterations. It would be unfeasible for software to emulate all the natural processes in enough detail to provide world generation for a game, mainly because in the nature everything interacts with everything. If a mountain range rises, it changes the way that the precipitation is carried by the wind to the lands beyond the mountains, thus changing the erosion rate there and the vegetation type.
- - -For a MineCraft-like game terrain generator we need the generator to have several properties: -
As already mentioned, the nature works basically by generating raw terrain composition, then "applying" erosion, vegetation and finally this leads to biomes being formed. Let's now try a somewhat inverse approach: First generate biomes, then fit them with appropriate terrain, and finally cover in vegetation and all the other stuff.
- -Splitting the parts like this suddenly makes it possible to create a generator with the required properties. We can generate a reasonable biome map chunk-wise, independently of all the other data. Once we have the biomes, we can compose the terrain for the chunk by using the biome data for the chunk, and possibly even for neighboring chunks. Note that we're not breaking the first property, the biomes can be generated separately so a neighboring chunk's biome map can be generated without the need for the entire neighboring chunk to be present. Similarly, once we have the terrain composition for a chunk, we can generate all the vegetation and structures in it, and those can again use the terrain composition in neighboring chunks.
- - -This leads us directly to the main pipeline that is used for generating terrain in Cuberite. For technical reasons, the terrain composition step is further subdivided into Height generation and Composition generation, and the structures are really called Finishers. For each chunk the generator generates, in this sequence: -
The beautiful thing about this is that the individual components can be changed independently. You can have 5 biome generators and 3 height generators and you can let the users mix'n'match.
-This pipeline had been used in Cuberite for about a year, before we realized that is has a flaw: There is no way for it to generate overhangs. We tried to implement a Finisher that would actually carve overhangs into the terrain; this approach has several problems, most severe one of those is that tree and village generation becomes unbelievably difficult - those finishers need to know the basic terrain composition of the neighboring chunks in order to generate, and the composition would be different after the overhangs are carved. So we need to come up with a better way, something that directly generates the overhangs at latest by the Terrain composition stage.
-Luckily we have just the thing. Instead of generating a 2D heightmap, we generate a 3D "density map" - we decide about each block in the chunk being generated, whether it is a solid block or an air block. The following pictures try to illustrate this in one less dimension - the heightmap is a 1D function and the density map is a 2D function:
- - - - -This way we can have generators that produce overhangs and yet allow finishers that need the entire composition of the neighboring chunks. However, we pay the price for this in performance, because a 3D noise for the density map needs order of magnitude more CPU cycles than 2D noise for heightmap. Also the RAM usage is increased because instead of storing 16 * 16 height values we need to store 16 * 256 * 16 density values.
- -For a great tutorial on coherent noise, see the LibNoise documentation.
-Coherent noise is a type of noise that has three important properties that we can use to our advantage: -
We'll be mostly using Perlin noise in this article. It is the easiest one to visualise and use and is one of the most useful kinds of coherent noises. Here's an example of a Perlin noise generated in 2 dimensions:
- - -It comes only naturally that such a 2D noise can be used as a terrain height map directly:
- - -However, this is not the only use for this noise, and 2 dimensions is not the limit - this noise can be generated for any number of dimensions.
- - - -The easiest way to generate biomes is to not generate them at all - simply assign a single constant biome to everywhere. And indeed there are times when this kind of "generator" is useful - for the MineCraft's Flat world type, or for testing purposes, or for tematic maps. In Cuberite, this is exactly what the Constant biome generator does.
- -Of course, there are more interesting test scenarios for which multiple biomes must be generated as easy as possible. For these special needs, there's a CheckerBoard biome generator. As the name suggests, it generates a grid of alternating biomes.
- -Those two generators were more of a technicality, we need to make something more interesting if we're going for a natural look. The Voronoi generator is the first step towards such a change. Recall that a Voronoi diagram is a construct that creates a set of areas where each point in an area is closer to the appropriate seed of the area than the seeds of any other area:
- - -To generate biomes using this approach, you select random "seeds", assign a biome to each one, and then for each "column" of the world you find the seed that is the nearest to that column, and use that seed's biome.
- -The overall shape of a Voronoi diagram is governed by the placement of the seeds. In extreme cases, a seed could affect the entire diagram, which is what we don't want - we need our locality, so that we can generate a chunk's worth of biome data. We also don't want the too much irregular diagrams that are produced when the seeds are in small clusters. We need our seeds to come in random, yet somewhat uniform fashion.
- -Luckily, we have just the tool: Grid with jitter. Originally used in antialiasing techniques, they can be successfully applied as a source of the seeds for a Voronoi diagram. Simply take a regular 2D grid of seeds with the grid distance being N, and move each seed along the X and Y axis by a random distance, usually in the range [-N / 2, +N / 2]:
- - -Such a grid is the ideal seed source for a Voronoi biome generator, because not only are the Voronoi cells "reasonable", but the seed placement's effect on the diagram is localized - each pixel in the diagram depends on at most 4 x 4 seeds around it. In the following picture, the seed for the requested point (blue) must be within the indicated circle. Even the second-nearest seed, which we will need later, is inside that circle.
- - -Calculating the jitter for each cell can be done easily by using a 2D Perlin noise for each coord. We calculate the noise's value at [X, Z], which gives us a number in the range [-1; 1]. We then multiply the number by N / 2, this gives us the required range of [-N / 2, +N / 2]. Adding this number to the X coord gives us the seed's X position. We use another Perlin noise and the same calculation for the Z coord of the seed.
- -Here's an example of a biome map generated using the Voronoi + jitter grid, as implemented by the Voronoi biome generator in Cuberite:
- - -The biomes are starting to look interesting, but now they have straight-line borders, which looks rather weird and the players will most likely notice very soon. We need to somehow distort the borders to make them look more natural. By far the easiest way to achieve that is to use a little trick: When the generator is asked for the biome at column [X, Z], instead of calculating the Voronoi biome for column [X, Z], we first calculate a random offset for each coord, and add it to the coordinates. So the generator actually responds with the biome for [X + rndX, Z + rndZ].
- -In order to keep the property that generating for the second time gives us the same result, we need the "random offset" to be replicatable - same output for the same input. This is where we use yet another Perlin noise - just like with the jitter for the Voronoi grid, we add a value from a separate noise to each coordinate before sending the coordinates down to the Voronoi generator:
-
-DistortedVoronoiBiome(X, Z) := VoronoiBiome(X + PerlinX(X, Z), Z + PerlinZ(X, Z))
-
-
-The following image shows the effects of the change, as generated by Cuberite's DistortedVoronoi biome generator. It is actually using the very same Voronoi map as the previous image, the only change has been the addition of the distortion:
- - -As you can see, this already looks reasonable enough, it could be considered natural biomes, if it weren't for several drawbacks: -
Our next goal is to remove the first defect of the distorted Voronoi generator: unrelated biomes generating next to each other. You are highly unlikely to find a jungle biome next to a desert biome in the real world, so we want to have as few of those borders as possible in our generator, too. We could further improve on the selection of biome-to-seed in the Voronoi generator. Or we can try a completely different idea altogether.
- -Recall how we talked about the nature, where the biomes are formed by the specific conditions of a place. What if we could make a similar dependency, but without the terrain? It turns out this is possible rather easily - instead of depending on the terrain, we choose two completely artificial measures. Let's call them Temperature and Humidity. If we knew the temperature of the place, we know what set of biomes are possible for such temperatures - we won't place deserts in the cold and tundra in the hot anymore. Similarly, the humidity will help us sort out the desert vs jungle issue. But how do we get a temperature and humidity? Once again, the Perlin noise comes to the rescue. We can use a simple 2D Perlin noise as the temperature map, and another one as the humidity map.
- -What we need next is a decision of what biome to generate in certain temperature and humidity combinations. The fastest way for a computer is to have a 2D array, where the temperature is one dimension and humidity the other, and the values in the array specify the biome to generate:
- - -We can even "misuse" the above diagram to include the hill variants of the biomes and have those hills neighbor each other properly, simply by declaring some of the decision diagram's parts as hills:
- - -The problem with this approach is that there are biomes that should not depend on temperature or humidity, they generate across all of their values. Biomes like Oceans, Rivers and Mushroom. We could either add them somewhere into the decision diagram, or we can make the generator use a multi-step decision: -
This is the approach implemented in Cuberite's MultiStepMap biome generator. It generates biome maps like this:
- - -To decide whether the point is in the ocean, land or mushroom, the generator first chooses seeds in a grid that will be later fed to a DistortedVoronoi algorithm, the seeds get the "ocean" and "land" values. Then it considers all the "ocean" seeds that are surrounded by 8 other "ocean" seeds and turns a random few of them into "mushroom". This special seed processing makes the mushroom biomes mostly surrounded by ocean. The following image shows an example seeds grid that the generator might consider, only the two framed cells are allowed to change into mushroom. L = land, O = ocean:
- - -Next, the generator calculates the DistortedVoronoi for the seeds. For the areas that are calculated as mushroom, the distance to the nearest-seed is used to further shrink the mushroom biome and then to distinguish between mushroom and mushroom-shore (image depicts a Voronoi cell for illustration purposes, it works similarly with DistortedVoronoi). O = ocean, M = mushroom, MS = mushroom shore:
- - - -The rivers are added only to the areas that have been previously marked as land. A simple 2D Perlin noise is used as the base, where its value is between 0 and a configured threshold value, a river is created. This creates the rivers in a closed-loop-like shapes, occasionally splitting two branches off:
- - - - - -For the leftover land biomes, the two Perlin noises, representing temperature and humidity, are used to generate the biomes, as described earlier. Additionally, the temperature map is used to turn the Ocean biome into FrozenOcean, and the River biome into FrozenRiver, wherever the temperature drops below a threshold.
- -The 1.7 MineCraft update brought a completely new terrain generation, which has sparked renewed interest in the biome generation. A new, potentially simpler way of generating biomes was found, the two-level DistortedVoronoi generator.
- -The main idea behind it all is that we create large areas of similar biomes. There are several groups of related biomes that can be generated near each other: Desert biomes, Ice biomes, Forest biomes, Mesa biomes. Technically, the Ocean biomes were added as yet another group, so that the oceans will generate in approximately the size of the larger areas, too.
- -For each column a DistortedVoronoi is used to select, which large area to use. This in turn results in the list of biomes from which to choose. Another DistortedVoronoi, this time with a smaller grid size, is used to select one biome out of that list. Additionally, the smaller DistortedVoronoi calculates not only the nearest seed's distance, but also the distance to the second-nearest seed; the ratio between these two is used as an indicator whether the column is in the "inside" or on the "outskirt" of the smaller Voronoi cell. This allows us to give certain biomes an "edge" biome - the Mushroom biome has a MushroomShore edge, the ExtremeHills biome have an ExtremeHillsEdge biome on the edge, etc.
- -The images below illustrate the process with regular Voronoi diagrams, for clarity purposes. The real generator uses distortion before querying the small areas.
-The following image shows an example output of a TwoLevel biome generator in Cuberite. Note how the mushroom biomes (violet) have mushroom shores (pink) on their edges.
- - -Note that rivers are currently not implemented in this generator in Cuberite, but they could be added -using the same approach as in MultiStepMap - by using a thresholded 2D Perlin noise.
- - - -This generator uses a completely new approach to biome generation. Internally, it uses 2D arrays of integers of varying sizes, and defines a few operations on those arrays. At various points in the generator's pipeline, the integers are interpreted as having a different meaning. At the first stage, they diffentiate between ocean and land. Later on they are interpreted as biome groups - ocean biomes, dry biomes, temperate biomes, mountain biomes or ice biomes. In the final stages they represent individual biomes, each number in the array representing the biome of a single-block-wide column in the world. Still, most of the operations are agnostic of this interpretation, they only "see numbers".
-At the core of the generator is the "Zoom" operation, that enlarges the array almost twice in size (N -> 2*N - 1). For each 2x2 neighboring numbers in the original array it produces a 3x3 array, where the corner values inherit from their corner counterparts of the original array, and the values in the middle get chosen randomly from their appropriate neighbors:
-
|
---- zooom --> | -
|
-
This scheme is repeated for larger arrays accordingly, for example (highlighted values are the ones directly copied from the source array):
-
|
---- Zooom --> | -
|
-
The basic idea is that we're having a low-resolution image of the "land" and we're zooming in; in each zoom iteration we're adding random details - the randomly chosen numbers. This becomes apparent when we enlarge each image to the same dimensions:
- - - - - - - -As you can see, the areas take a nice random-looking shape, but the edges are a little bit too noisy. There's where the second most important operation comes in: the "Smooth" slightly reduces the array size (N -> N - 2), losing the values on the edge of the array, and for the internal numbers it considers their 4 neighbors. If both the horizontal neighbors are the same and the vertical neighbors are the same (but not necessarily the same as the horizontal ones), the value is set randomly to either the horizontal or the vertical neihbors' value. If both the horizontal neighbors are the same, the value is set to the value of those neighbors, otherwise if both the vertical neighbors are the same, the value is set to the value of those neighbors. In all the rest cases, the value is kept at its original.
-
|
-
|
-
The following examples show how the decisions work:
-Input | Output | Notes | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|
Neither pair of neighbors are the same, so the value is left intact | -||||||||||||
Horizontal neighbors are the same, so the value is copied from them | -||||||||||||
Vertical neighbors are the same, so the value is copied from them | -||||||||||||
Each pair of neighbors are the same, so the value is chosen from one of the pairs randomly | -
This decision is repeated for each value in the array, for example:
-
|
---- Smooth --> | -
|
-||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Highlighted area is processed into output | -- |
-Original value kept -Value forced by both horizontal and vertical neighbors, random -Value forced by horizontal neighbors -Value forced by vertical neighbors - |
The following example shows multiple successive Smooth operations performed on the same data set over and over again:
- - - - - - - -As you can see, the smoothing operation doesn't make much difference after its first pass, so it usually isn't used more than once after each zoom.
-One important thing to note is that both the Zoom and Smooth operations only output the numbers already present in the array, they don't create new numbers. This is important because it allows the late stages of the generator to grow indepent biomes next to each other without them "bleeding" into different biomes on their edges.
-The Grown generator uses several more supplementary operations, such as "AddIslands", "ReplaceRandomly", "River", "Beaches" and more. There isn't anything too special to those, they perform mostly trivial operations, manipulating the numbers in some way; the main power of the generator lies in the zoom and smooth operations. Perhaps noteworthy is the generation of rivers: it starts with the regular bitmap (only 0 and 1 used), zooms in and smooths for a while and then performs edge detection - a river biome is set in pixels whose neighbors are different, and no change applied when the neighbors are the same. Among other things, this means that there are actually two chains of array operations, and their results are combined together in the "MixRivers" operation.
-The following table summarizes the operations, visually:
-Operation | Input 1 | Input 2 | Output | Notes |
---|---|---|---|---|
AddIslands | -- | - | -- | Adds a configurable (by percentage) amount of islands to ocean biome group. | -
AlternateBiomes | -- | - | - | Mostly copies the first input, but where the second input has non-ocean biome group, turns the first input's biomes into their alternatives. | -
Beaches | -- | - | -- | Any biome neighboring an ocean is turned into a corresponding beach. | -
BiomeEdges | -- | - | -- | If the neighbors of a biome are incompatible (such as desert vs ice plains, or jungle vs anything etc.), turns the biome into a corresponding neutral biome (plains, jungle-edge etc.) | -
Biomes | -- | - | -- | Input is interpreted as biome groups, for each point a random biome corresponding to the group is chosen for the output. | -
BiomeGroupEdges | -- | - | -- | Converts biome groups on an edge between two incompatible groups (such as desert and ice) into a neutral one (temperate). | -
MBiomes | -- | - | - | Where the second input is zero, copies the first input's biomes; where the second input is nonzero, converts first input's biomes into their M variants. | -
MixRivers | -- | - | - | Copies first input's biomes into the output, unless there's a river biome in the second input and a land biome in the first input - then it sets a river biome in the output instead. | -
River | -- | - | -- | Somewhat of an edge detector - wherever the input has a different biome neighbors, sets a river biome; otherwise sets an ocean biome. | -
SetRandomly | -- | - | -- | Randomly sets points to a specified biome. The amount of changed points is settable as a percentage. | -
Of further note is the existence of two sets of the IntGen classes, representing the individual operations. There are the cProtIntGen class descendants, which are used for prototyping the connections between the operations - it's easy to just chain several operations after each other and they automatically use the correct array dimensions. However, it is possible to further optimize the calculations by moving the array dimensions into template parameters (so that they are, in fact, constant from the code's point of view, and so highly optimizable). This is what the cIntGen class descendants do. Unfortunately, this optimization makes it difficult to change the operation chain - when a new operation is added or removed in the chain, the array sizes for the rest of the chain change and they all have to be updated manually. So the optimal strategy was to use the cProtIntGen classes to find out the best-looking combination of operations, and once the combination was found, to rewrite it using cIntGen classes for performance. -
- -- - - | -
- - | -
- - | -
- - | -
- - | -
As with biomes, the easiest way to generate terrain height is not generating at all - assigning a constant -height value to all columns. This is again useful either for internal tests, and for worlds like MineCraft's -Flat world.
- -For a somewhat more realistic landscape, we will employ the good old 2D Perlin noise. We can use it -directly as a heightmap - each value we get from the noise is stretched into the desired range (usually from -40 to 120 blocks for regular MineCraft worlds) and used as the height value. However, this doesn't play too -well with the biomes we've just generated. If the biome says "ocean" and the Perlin noise says "mountain", -the end result will be unpleasant.
- -So we want a height generator that is biome-aware. The easiest way of doing this is to have a separate -generator for each biome. Simply use the biome map to select which generator to use, then ask the appropriate -generator for the height value. Again, this doesn't work too well - imagine an ExtremeHills biome right next -to an Ocean biome. If no extra care is taken, the border between these two will be a high wall. The following -image shows a 2D representation (for simplification purposes) of the problem:
- - -This requires some further processing. What we need is for the terrain height to be dependent not only on -the immediate biome for that column, but also on the close surroundings of the column. This is exactly the -kind of task that averaging is designed for. If we take the area of 9x9 biomes centered around the queried -column, generate height for each of the biomes therein, sum them up and divide by 81 (the number of biomes -summed), we will be effectively making a 9-long running average over the terrain, and all the borders will -suddenly become smooth. The following image shows the situation from the previous paragraph after applying -the averaging process:
- - -The approach used in Cuberite's Biomal generator is based on this idea, with two slight modifications. -Instead of using a separate generator for each biome, one generator is used with a different set of input -parameters for each biomes. These input parameters modify the overall amplitude and frequency of the Perlin -noise that the generator produces, thus modifying the final terrain with regards to biomes. Additionally, the -averaging process is weighted - columns closer to the queried column get a more powerful weight in the sum -than the columns further away. The following image shows the output of Cuberite's Biomal terrain height -generator (each block type represents a different biome - ocean in the front (stone), plains and ice plains -behind it (lapis, whitewool), extreme hills back right (soulsand), desert hills back left (mossy -cobble)):
- - -One key observation about this whole approach is that in order for it to work, the biomes must be -available for columns outside the currently generated chunk, otherwise the columns at the chunk's edge would -not be able to properly average their height. This requirement can be fulfilled only by biome generators that -adhere to the second Expected property - that re-generating will produce -the same data. If the biome generator returned different data for the same chunk each time it was invoked, it -would become impossible to apply the averaging.
- -(TODO: height with variations (N/A in MCS yet)
- - -As with the other generators, the composition generator category has its easy and debugging items, too. -There's the "special" composition of "all the blocks are the same type", which fills the entire column, from -the bottom to the height, with a single blocktype. This generator is useful when testing the generators in -the other categories, to speed up the generation by leaving out unnecessary calculations. Another special -compositor is a similar one, that fills all blocks with the same type, but the type varies for each biome. -This way it's easy to see the generated biomes and possibly the heights for those biomes, as shown in the -previous section on the height averaging screenshot.
- -For a natural look, we need to put together a more complicated algorithm. The standard set forth in -MineCraft is that the top of the world is covered in grass, then there are a few blocks of dirt and finally -stone. This basic layout is then varied for different biomes - deserts have sand and sandstone instead of the -grass and dirt layer. Mushroom biomes have mycelium in place of the grass. This per-biome dependency is -trivial to implement - when compositing, simply use the appropriate layout for the column's biome.
- -The next change concerns oceans. The generated heightmap doesn't include any waterlevel indication -whatsoever. So it's up to the terrain compositor to actually decide where to place water. We do this by -configuration - simply have a value in the config file specifying the sealevel height. The compositor then -has to add water above any column which has a height lower than that. Additionally, the water needs to -override per-biome layout selection - we don't want grass blocks to generate under water when the terrain -height in the plains biome drops below the sealevel accidentally.
- -The final feature in the compositor is the decision between multiple composition layouts within a single -biome. A megataiga biome contains patches of non-grass dirt and podzol blocks, and the ocean floor can be -made of dirt, gravel, sand or clay. A simple 2D Perlin noise can be used to select the layout to use for a -specific column - simply threshold the noise's value by as many thresholds as there are layout variations, -and use the layout corresponding to the threshold:
- - - - -So far we've been discussing only the Overworld generator. But MineCraft contains more than that. The -Nether has a completely different look and feel, and quite different processes are required to generate that. -Recall that MineCraft's Nether is 128 blocks high, with bedrock both at the top and the bottom. Between these -two, the terrain looks more like a cavern than a surface. Not surprisingly, the Nether doesn't need a -complicated height generator, it can use the flat height. However, the terrain composition must take an -altogether different approach.
- -The very first idea is to use the Perlin noise, but generate it in 3D, rather than 2D. Then, for each -block, evaluate the noise value, if below 0, make it air, if not, make it netherrack. - -
To make it so that the bedrock at the top and at the bottom is never revealed, we can add a value -increasing the more the Y coord gets towards the bottom or the top. This way the thresholding then guarantees -that there will be no air anywhere near the bedrock.
- -(TODO)
- - -Finishers are a vast category of various additions to the terrain generator. They range from very easy -ones, such as generating snow on top of the terrain in cold biomes, through medium ones, such as growing -patches of flowers, complicated ones, such as placing trees and generating caves, all the way to very -complicated ones such as villages and nether fortresses. There is no formal distinction between all these -"categories", the only thing they have in common is that they take a chunk of blocks and modify it in some -way.
- -Snow is probably the easiest of the finishers. It generates a block of snow on top of each block that is -on top of the terrain and is not marked as non-snowable. It checks the chunk's heightmap to determine the top -block, then checks whether the block supports snow on its top. Rails, levers and tall grass don't support -snow, for example.
- -Another example of an easy finisher. This scans through the world and turn each water block on the surface -into an ice block if the biome is cold. This means that any water block that is under any kind of other -block, such as under a tree's leaves, will still stay water. Thus an additional improvement could be made by -scanning down from the surface block through blocks that we deem as non-surface, such as leaves, torches, -ladders, fences etc. Note that Cuberite currently implements only the easy solution.
- -Most worlds in MineCraft have lava lakes at their bottom. Generating these is pretty straightforward: Use -the user-configured depth and replace all the air blocks below this depth with lava blocks. Note however, -that this makes this generator dependent on the order in which the finishers are applied. If the mineshafts -generate before bottom lava, the mineshafts that are below the lava level will get filled with lava. On the -other hand, if bottom lava is generated before the mineshafts, it is possible for a mineshaft to "drill -through" a lake of lava. Cuberite doesn't try to solve this and instead lets the admin choose whichever they -prefer.
- -There are generators for specific kinds of foliage. The dead bushes in the desert biome and lilypads in -the swamp biome both share the same generating pattern. They are both specific to a single biome and they -both require a specific block underneath them in order to generate. Their implementation is simple: pick -several random columns in the chunk. If the column is of the correct biome and has the correct top block, -add the foliage block on top.
- -In order to generate the same set of coordinates when the chunk is re-generated, we use the Perlin noise's -basis functions (the ones providing the random values for Perlin cell vertices). These basically work as a -hash function for the coorinates - the same input coordinates generate the same output value. We use the -chunk's coordinates as two of the coords, and the iteration number as the third coordinate, to generate a -random number. We then check the biome and the top block at those coordinates, if they allow, we generate the -foliage block on top.
- -Another example of specific foliage is the tall grass in the plains biome. There are quite a lot of these -tall grass blocks, it would be inefficient to generate them using the random-coords approach described above. -Instead, we will use a 2D Perlin noise again, with a threshold defining where to put the grass and where -not.
- -For the flowers, grass, mushrooms in caves etc. we want to use a slightly different algorithm. These -foliage blocks are customarily generated in small "clumps" - there are several blocks of the same type near -together. To generate these, we first select random coords, using the coord hash functions, for a center of a -clump. Then we select the type of block to generate. Finally, we loop over adding a random (coord hash) -number to the clump center coords to get the block where to generate the foliage block:
- - -In order to make the clump more "round" and "centered", we want the offsets to be closer to the clump -center more often. This is done using a thing called Gaussian function distribution. Instead of having each -random number generate with the same probability, we want higher probability for the numbers around zero, -like this:
- - -Instead of doing complicated calculations to match this shape exactly, we will use a much easier shape. -By adding together two random numbers in the same range, we get the probability distribution that has a -"roof" shape, enough for our needs:
- - -(For the curious, there is a proof that adding together infinitely many uniform-distributed random numbers -produces random numbers with the Gaussian distribution.)
- -This scheme can be used to produce clumps of flowers, when we select the 2D coords of the clump center on -the top surface of the terrain. We simply generate the 2D coords of the foliage blocks and use the terrain -height to find the third coord. If we want to generate clumps of mushrooms in the caves, however, we need to -generate the clump center coords in 3D and either use 3 offsets for the mushrooms, or use 2 offsets plus -searching for the closest opening Y-wise in the terrain.
- -Note that the clumps generated by this scheme may overlap several chunks. Therefore it's crucial to -actually check the surrounding chunks if their clumps overlap into the currently generated chunk, and apply -those as well, otherwise there will be visible cuts in the foliage along the chunks borders.
- -Water and lava springs are essential for making the underground quite a lot more interesting. They are -rather easy to generate, but a bit more difficult to get right. Generating simply means that a few random -locations (obtained by our familiar coord hashing) are checked and if the block type in there is stone. Then -we see all the horizontal neighbors of the block, plus the block underneath. If all of them except one are -stone, and the one left is air, our block is suitable for turning into a spring. If there were more air -neighbors, the spring would look somewhat unnatural; if there were no air neighbors, the spring won't flow -anywhere, so it would be rather useless.
- -The difficult part about springs is the amount of them to generate. There should be a few springs on the -surface, perhaps a bit more in the mountaineous biomes. There should be quite a few more springs underground, -but there should definitely be more water springs than lava springs in the upper levels of the terrain, while -there should be more lava springs and almost no water springs near the bottom. To accomodate this, the -Cuberite team has made a tool that scanned through MineCraft's terrain and counted the amount of both types -of springs in relation to their height. Two curves have been found for the distribution of each type of the -spring:
- - -Cuberite uses an approximation of the above curves to choose the height at which to generate the -spring.
- -Caves are definitely one of the main things people notice about MineCraft terrain. There are quite a lot -of different algorithms available to generate terrain with caves, each with different results. Cuberite currently implements three finishers that generate caves:
-(TODO)
- -Much of the terain generation consists of doing the same thing for every single column or block in a chunk. This -sort of computation is much faster on a GPU as GPUs are massively parallel. High end GPUs can execute up to 30,000 -threads simultaneously, which would allow them to generate every block in half a chunk in parallel or every column -in over 100 chunks in parallel. A naive comparison suggests that a 800MHz GPU with 15,000 threads can execute parallel -code 250 times faster than a 3GHz CPU with 128 bit SIMD. Obviously we want to harness that power.
- - diff --git a/docs/Login sequence.txt b/docs/Login sequence.txt deleted file mode 100644 index df9d386c6..000000000 --- a/docs/Login sequence.txt +++ /dev/null @@ -1,26 +0,0 @@ - -This is the connection sequence of the 1.6.2 client to a vanilla server, after the encryption has been established: - -S->C: 0xfc (encryption key response) empty payload (4 * 0x00) -C->S: 0xcd (client statuses) -S->C: 0x01 (login) -S->C: 0xfa (plugin message) - "MC|Brand": "vanilla" -S->C: 0x06 (compass) -S->C: 0xca (player capabilities) -S->C: 0x10 (slot select) -S->C: 0x04 (time update) -S->C: 0xc9 (player list item) -S->C: 0x0d (player pos + look) -S->C: 0x04 (time update) -S->C: 0x68 (window contents) -S->C: 0x67 (slot contents) - Window -1, slot -1 -S->C: 0x67 (slot contents) - Window 0, slot 9 .. 44, only occupied slots -S->C: 0x38 (chunk bulk) -S->C: 0x28 (entity metadata) player metadata -S->C: 0x2c (entity properties) player properties -S->C: 0x04 (time update) -S->C: 0x38 (chunk bulk) -C->S: 0xcc (client settings / locale and view) -C->S: 0xfa (plugin message) - "MC|Brand": "vanilla" -C->S: 0x0d (player pos + look) -S->C: 0x38 (chunk bulk) \ No newline at end of file diff --git a/docs/NBT Examples/single chunk NBT data.txt b/docs/NBT Examples/single chunk NBT data.txt deleted file mode 100644 index 905d6465c..000000000 --- a/docs/NBT Examples/single chunk NBT data.txt +++ /dev/null @@ -1,71 +0,0 @@ -TAG_Compound(""): 1 items -{ - TAG_Compound("Level"): 10 items - { - TAG_List("TileTicks"): 0 items of type 1 - { - } - TAG_List("Entities"): 0 items of type 1 - { - } - TAG_ByteArray("Biomes"): 256 bytes - TAG_Long("LastUpdate"): 1041959 - TAG_Int("xPos"): 0 - TAG_Int("zPos"): 0 - TAG_List("TileEntities"): 0 items of type 1 - { - } - TAG_Byte("TerrainPopulated"): 1 - TAG_List("Sections"): 6 items of type 10 - { - TAG_Compound(""): 5 items - { - TAG_ByteArray("Data"): 2048 bytes - TAG_ByteArray("SkyLight"): 2048 bytes - TAG_ByteArray("BlockLight"): 2048 bytes - TAG_Byte("Y"): 0 - TAG_ByteArray("Blocks"): 4096 bytes - } - TAG_Compound(""): 5 items - { - TAG_ByteArray("Data"): 2048 bytes - TAG_ByteArray("SkyLight"): 2048 bytes - TAG_ByteArray("BlockLight"): 2048 bytes - TAG_Byte("Y"): 1 - TAG_ByteArray("Blocks"): 4096 bytes - } - TAG_Compound(""): 5 items - { - TAG_ByteArray("Data"): 2048 bytes - TAG_ByteArray("SkyLight"): 2048 bytes - TAG_ByteArray("BlockLight"): 2048 bytes - TAG_Byte("Y"): 2 - TAG_ByteArray("Blocks"): 4096 bytes - } - TAG_Compound(""): 5 items - { - TAG_ByteArray("Data"): 2048 bytes - TAG_ByteArray("SkyLight"): 2048 bytes - TAG_ByteArray("BlockLight"): 2048 bytes - TAG_Byte("Y"): 3 - TAG_ByteArray("Blocks"): 4096 bytes - } - TAG_Compound(""): 5 items - { - TAG_ByteArray("Data"): 2048 bytes - TAG_ByteArray("SkyLight"): 2048 bytes - TAG_ByteArray("BlockLight"): 2048 bytes - TAG_Byte("Y"): 4 - TAG_ByteArray("Blocks"): 4096 bytes - } - TAG_Compound(""): 5 items - { - TAG_ByteArray("Data"): 2048 bytes - TAG_ByteArray("SkyLight"): 2048 bytes - TAG_ByteArray("BlockLight"): 2048 bytes - TAG_Byte("Y"): 5 - TAG_ByteArray("Blocks"): 4096 bytes - } - } - } -} diff --git a/docs/NBT Examples/tile entities.txt b/docs/NBT Examples/tile entities.txt deleted file mode 100644 index e16ae45a7..000000000 --- a/docs/NBT Examples/tile entities.txt +++ /dev/null @@ -1,109 +0,0 @@ -TAG_List("TileEntities"): 3 items of type 10 -{ - TAG_Compound(""): 6 items - { - TAG_String("id"): 10 bytes: "MobSpawner" - TAG_Short("Delay"): 20 - TAG_Int("z"): 180 - TAG_String("EntityId"): 6 bytes: "Spider" - TAG_Int("y"): 11 - TAG_Int("x"): -6 - } - TAG_Compound(""): 5 items - { - TAG_List("Items"): 6 items of type 10 - { - TAG_Compound(""): 4 items - { - TAG_Short("id"): 325 - TAG_Short("Damage"): 0 - TAG_Byte("Count"): 1 - TAG_Byte("Slot"): 3 - } - TAG_Compound(""): 4 items - { - TAG_Short("id"): 296 - TAG_Short("Damage"): 0 - TAG_Byte("Count"): 4 - TAG_Byte("Slot"): 5 - } - TAG_Compound(""): 4 items - { - TAG_Short("id"): 325 - TAG_Short("Damage"): 0 - TAG_Byte("Count"): 1 - TAG_Byte("Slot"): 11 - } - TAG_Compound(""): 4 items - { - TAG_Short("id"): 351 - TAG_Short("Damage"): 3 - TAG_Byte("Count"): 1 - TAG_Byte("Slot"): 13 - } - TAG_Compound(""): 4 items - { - TAG_Short("id"): 296 - TAG_Short("Damage"): 0 - TAG_Byte("Count"): 1 - TAG_Byte("Slot"): 14 - } - TAG_Compound(""): 4 items - { - TAG_Short("id"): 287 - TAG_Short("Damage"): 0 - TAG_Byte("Count"): 2 - TAG_Byte("Slot"): 15 - } - } - TAG_String("id"): 5 bytes: "Chest" - TAG_Int("z"): 182 - TAG_Int("y"): 11 - TAG_Int("x"): -7 - } - TAG_Compound(""): 5 items - { - TAG_List("Items"): 5 items of type 10 - { - TAG_Compound(""): 4 items - { - TAG_Short("id"): 289 - TAG_Short("Damage"): 0 - TAG_Byte("Count"): 1 - TAG_Byte("Slot"): 5 - } - TAG_Compound(""): 4 items - { - TAG_Short("id"): 329 - TAG_Short("Damage"): 0 - TAG_Byte("Count"): 1 - TAG_Byte("Slot"): 12 - } - TAG_Compound(""): 4 items - { - TAG_Short("id"): 265 - TAG_Short("Damage"): 0 - TAG_Byte("Count"): 4 - TAG_Byte("Slot"): 17 - } - TAG_Compound(""): 4 items - { - TAG_Short("id"): 296 - TAG_Short("Damage"): 0 - TAG_Byte("Count"): 4 - TAG_Byte("Slot"): 21 - } - TAG_Compound(""): 4 items - { - TAG_Short("id"): 289 - TAG_Short("Damage"): 0 - TAG_Byte("Count"): 2 - TAG_Byte("Slot"): 22 - } - } - TAG_String("id"): 5 bytes: "Chest" - TAG_Int("z"): 181 - TAG_Int("y"): 11 - TAG_Int("x"): -8 - } -} \ No newline at end of file diff --git a/docs/Object ownership.gv b/docs/Object ownership.gv deleted file mode 100644 index 29e0407a6..000000000 --- a/docs/Object ownership.gv +++ /dev/null @@ -1,29 +0,0 @@ -digraph -{ -rankdir=LR -Root -> Server -Root -> MonsterConfig -Root -> GroupManager -Root -> CraftingRecipes -Root -> FurnaceRecipe -Root -> PluginManager -Root -> Authenticator -Root -> World -Server -> ListenThreadIPv4 -Server -> ListenThreadIPv6 -Server -> ClientHandle -Server -> RCONServer -PluginManager -> Plugin_NewLua -PluginManager -> Plugin -World -> SimulatorManager -World -> SandSimulator -World -> WaterSimulator -World -> LavaSimulator -World -> FireSimulator -World -> RedstoneSimulator -World -> WorldStorage -World -> Player -World -> Generator -World -> ChunkSender -World -> LightingThread -} \ No newline at end of file diff --git a/docs/SocketThreads states.gv b/docs/SocketThreads states.gv deleted file mode 100644 index 5afaa5370..000000000 --- a/docs/SocketThreads states.gv +++ /dev/null @@ -1,77 +0,0 @@ -digraph -{ - node - [ - shape="box" - ] - - edge - [ - d - ] - - // Forward-declarations of nodes (so that they are laid out in a specific order: - ssNormal - ssWritingRestOut - ssShuttingDown - ssShuttingDown2 - - // Nodes with special labels / shapes: - ForceClose - [ - label="Force close" - shape="ellipse" - ] - X - [ - label="Socket closed" - shape="ellipse" - ] - - // Edges: - ssNormal -> ssWritingRestOut - [ - label="cSocketThreads::RemoveClient()" - ] - ssWritingRestOut -> ssShuttingDown - [ - label="All outgoing data written" - ] - ssShuttingDown -> ssShuttingDown2 - [ - label="One thread loop" - ] - ssShuttingDown2 -> ForceClose - [ - label="One thread loop" - ] - ssNormal -> ssRemoteClosed - [ - label="Remote closed" - color="red" - fontcolor="red" - ] - ssWritingRestOut -> X - [ - label="Remote closed" - color="red" - fontcolor="red" - ] - ssShuttingDown -> X - [ - label="Remote closed" - color="red" - fontcolor="red" - ] - ssShuttingDown2 -> X - [ - label="Remote closed" - color="red" - fontcolor="red" - ] - ssRemoteClosed -> X - [ - label="cSocketThreads::RemoveClient()" - ] - ForceClose -> X -} diff --git a/docs/Springs.ods b/docs/Springs.ods deleted file mode 100644 index 4b3559f1d..000000000 Binary files a/docs/Springs.ods and /dev/null differ diff --git a/docs/_files.txt b/docs/_files.txt deleted file mode 100644 index 85a4253c1..000000000 --- a/docs/_files.txt +++ /dev/null @@ -1,20 +0,0 @@ - -Contents of this folder: - -API class inheritance - blockentities.gv - - a GraphViz file to visualise inheritance in the API classes in the Wiki for the cBlockEntity class' descendants - -API class inheritance - entities.gv - - a GraphViz file to visualise inheritance in the API classes in the Wiki for the cEntity class' descendants - -Login Sequence.txt - - Annotated log of packets exchanged between the client and server for login; 1.6.2 protocol - -Object ownership.gv - - a GraphViz file to visualise ownership relations in the Cuberite code architecture - -SocketThreads states.gv - - a GraphViz file documenting the states for individual sockets in cSocketThreads, and transitions between them - -Springs.ods - - a spreadsheet with collected statistics about the occurrence of lava / water springs based on height. diff --git a/docs/img/biomalheights.jpg b/docs/img/biomalheights.jpg deleted file mode 100644 index a01faef87..000000000 Binary files a/docs/img/biomalheights.jpg and /dev/null differ diff --git a/docs/img/biomeheights.jpg b/docs/img/biomeheights.jpg deleted file mode 100644 index 9dda27b0e..000000000 Binary files a/docs/img/biomeheights.jpg and /dev/null differ diff --git a/docs/img/biomeheightsavg.jpg b/docs/img/biomeheightsavg.jpg deleted file mode 100644 index c8217cafc..000000000 Binary files a/docs/img/biomeheightsavg.jpg and /dev/null differ diff --git a/docs/img/biomes.jpg b/docs/img/biomes.jpg deleted file mode 100644 index 59c23b870..000000000 Binary files a/docs/img/biomes.jpg and /dev/null differ diff --git a/docs/img/densitymap.jpg b/docs/img/densitymap.jpg deleted file mode 100644 index a7a7b3f36..000000000 Binary files a/docs/img/densitymap.jpg and /dev/null differ diff --git a/docs/img/distortedvoronoibiomes.png b/docs/img/distortedvoronoibiomes.png deleted file mode 100644 index d56dff347..000000000 Binary files a/docs/img/distortedvoronoibiomes.png and /dev/null differ diff --git a/docs/img/finishers.jpg b/docs/img/finishers.jpg deleted file mode 100644 index 06f7485c3..000000000 Binary files a/docs/img/finishers.jpg and /dev/null differ diff --git a/docs/img/gaussprobability.jpg b/docs/img/gaussprobability.jpg deleted file mode 100644 index 77da24748..000000000 Binary files a/docs/img/gaussprobability.jpg and /dev/null differ diff --git a/docs/img/grownexample_add_islands.png b/docs/img/grownexample_add_islands.png deleted file mode 100644 index f69faaaf1..000000000 Binary files a/docs/img/grownexample_add_islands.png and /dev/null differ diff --git a/docs/img/grownexample_alt_biomes.png b/docs/img/grownexample_alt_biomes.png deleted file mode 100644 index 866d774e2..000000000 Binary files a/docs/img/grownexample_alt_biomes.png and /dev/null differ diff --git a/docs/img/grownexample_beaches.png b/docs/img/grownexample_beaches.png deleted file mode 100644 index a84fb0eff..000000000 Binary files a/docs/img/grownexample_beaches.png and /dev/null differ diff --git a/docs/img/grownexample_biome_edges.png b/docs/img/grownexample_biome_edges.png deleted file mode 100644 index 58de63aef..000000000 Binary files a/docs/img/grownexample_biome_edges.png and /dev/null differ diff --git a/docs/img/grownexample_biomes.png b/docs/img/grownexample_biomes.png deleted file mode 100644 index ecd8af29b..000000000 Binary files a/docs/img/grownexample_biomes.png and /dev/null differ diff --git a/docs/img/grownexample_grp_edges.png b/docs/img/grownexample_grp_edges.png deleted file mode 100644 index 2ac32b9a6..000000000 Binary files a/docs/img/grownexample_grp_edges.png and /dev/null differ diff --git a/docs/img/grownexample_in1.png b/docs/img/grownexample_in1.png deleted file mode 100644 index 2238886ab..000000000 Binary files a/docs/img/grownexample_in1.png and /dev/null differ diff --git a/docs/img/grownexample_in2.png b/docs/img/grownexample_in2.png deleted file mode 100644 index 9ef9f6ae2..000000000 Binary files a/docs/img/grownexample_in2.png and /dev/null differ diff --git a/docs/img/grownexample_in3.png b/docs/img/grownexample_in3.png deleted file mode 100644 index 95d6608b5..000000000 Binary files a/docs/img/grownexample_in3.png and /dev/null differ diff --git a/docs/img/grownexample_in_alt.png b/docs/img/grownexample_in_alt.png deleted file mode 100644 index 59979ed62..000000000 Binary files a/docs/img/grownexample_in_alt.png and /dev/null differ diff --git a/docs/img/grownexample_in_river.png b/docs/img/grownexample_in_river.png deleted file mode 100644 index 58556369d..000000000 Binary files a/docs/img/grownexample_in_river.png and /dev/null differ diff --git a/docs/img/grownexample_m_biomes.png b/docs/img/grownexample_m_biomes.png deleted file mode 100644 index c3d7079ae..000000000 Binary files a/docs/img/grownexample_m_biomes.png and /dev/null differ diff --git a/docs/img/grownexample_mix_river.png b/docs/img/grownexample_mix_river.png deleted file mode 100644 index 81899a8c7..000000000 Binary files a/docs/img/grownexample_mix_river.png and /dev/null differ diff --git a/docs/img/grownexample_river.png b/docs/img/grownexample_river.png deleted file mode 100644 index cb07f44dd..000000000 Binary files a/docs/img/grownexample_river.png and /dev/null differ diff --git a/docs/img/grownexample_set_rnd.png b/docs/img/grownexample_set_rnd.png deleted file mode 100644 index 5b7a2d254..000000000 Binary files a/docs/img/grownexample_set_rnd.png and /dev/null differ diff --git a/docs/img/grownexample_smooth.png b/docs/img/grownexample_smooth.png deleted file mode 100644 index bfd43f6ef..000000000 Binary files a/docs/img/grownexample_smooth.png and /dev/null differ diff --git a/docs/img/grownexample_zoom.png b/docs/img/grownexample_zoom.png deleted file mode 100644 index 7afffe50b..000000000 Binary files a/docs/img/grownexample_zoom.png and /dev/null differ diff --git a/docs/img/heightmap.jpg b/docs/img/heightmap.jpg deleted file mode 100644 index c7eb5c865..000000000 Binary files a/docs/img/heightmap.jpg and /dev/null differ diff --git a/docs/img/jittergrid.jpg b/docs/img/jittergrid.jpg deleted file mode 100644 index f8066aa72..000000000 Binary files a/docs/img/jittergrid.jpg and /dev/null differ diff --git a/docs/img/jittergridlocality.jpg b/docs/img/jittergridlocality.jpg deleted file mode 100644 index 64414c878..000000000 Binary files a/docs/img/jittergridlocality.jpg and /dev/null differ diff --git a/docs/img/multistepmapbiomes.png b/docs/img/multistepmapbiomes.png deleted file mode 100644 index d32ac3d8e..000000000 Binary files a/docs/img/multistepmapbiomes.png and /dev/null differ diff --git a/docs/img/multistepmapdistance.jpg b/docs/img/multistepmapdistance.jpg deleted file mode 100644 index 9f7cfd11b..000000000 Binary files a/docs/img/multistepmapdistance.jpg and /dev/null differ diff --git a/docs/img/multistepmapgrid.jpg b/docs/img/multistepmapgrid.jpg deleted file mode 100644 index 51dd81c46..000000000 Binary files a/docs/img/multistepmapgrid.jpg and /dev/null differ diff --git a/docs/img/perlin.jpg b/docs/img/perlin.jpg deleted file mode 100644 index 499fcdeae..000000000 Binary files a/docs/img/perlin.jpg and /dev/null differ diff --git a/docs/img/perlincompositor1.jpg b/docs/img/perlincompositor1.jpg deleted file mode 100644 index 0d8f93cd9..000000000 Binary files a/docs/img/perlincompositor1.jpg and /dev/null differ diff --git a/docs/img/perlincompositor2.jpg b/docs/img/perlincompositor2.jpg deleted file mode 100644 index 11fc5b51d..000000000 Binary files a/docs/img/perlincompositor2.jpg and /dev/null differ diff --git a/docs/img/perlincompositor3.jpg b/docs/img/perlincompositor3.jpg deleted file mode 100644 index 46a2583ba..000000000 Binary files a/docs/img/perlincompositor3.jpg and /dev/null differ diff --git a/docs/img/perlinheightmap.jpg b/docs/img/perlinheightmap.jpg deleted file mode 100644 index d941a2fc6..000000000 Binary files a/docs/img/perlinheightmap.jpg and /dev/null differ diff --git a/docs/img/perlinrivers1.jpg b/docs/img/perlinrivers1.jpg deleted file mode 100644 index b11373fa7..000000000 Binary files a/docs/img/perlinrivers1.jpg and /dev/null differ diff --git a/docs/img/perlinrivers2.jpg b/docs/img/perlinrivers2.jpg deleted file mode 100644 index bbbcaa276..000000000 Binary files a/docs/img/perlinrivers2.jpg and /dev/null differ diff --git a/docs/img/perlinrivers3.jpg b/docs/img/perlinrivers3.jpg deleted file mode 100644 index 3cf043e6e..000000000 Binary files a/docs/img/perlinrivers3.jpg and /dev/null differ diff --git a/docs/img/roofprobability.jpg b/docs/img/roofprobability.jpg deleted file mode 100644 index e7a155113..000000000 Binary files a/docs/img/roofprobability.jpg and /dev/null differ diff --git a/docs/img/smallfoliageclumps.jpg b/docs/img/smallfoliageclumps.jpg deleted file mode 100644 index 4cc6cbc00..000000000 Binary files a/docs/img/smallfoliageclumps.jpg and /dev/null differ diff --git a/docs/img/smoothedgrown_1.png b/docs/img/smoothedgrown_1.png deleted file mode 100644 index 16e563f96..000000000 Binary files a/docs/img/smoothedgrown_1.png and /dev/null differ diff --git a/docs/img/smoothedgrown_2.png b/docs/img/smoothedgrown_2.png deleted file mode 100644 index 2d97cfb4e..000000000 Binary files a/docs/img/smoothedgrown_2.png and /dev/null differ diff --git a/docs/img/smoothedgrown_3.png b/docs/img/smoothedgrown_3.png deleted file mode 100644 index 2d4d13f49..000000000 Binary files a/docs/img/smoothedgrown_3.png and /dev/null differ diff --git a/docs/img/smoothedgrown_4.png b/docs/img/smoothedgrown_4.png deleted file mode 100644 index d52a34bfe..000000000 Binary files a/docs/img/smoothedgrown_4.png and /dev/null differ diff --git a/docs/img/smoothedgrown_5.png b/docs/img/smoothedgrown_5.png deleted file mode 100644 index ae14d9847..000000000 Binary files a/docs/img/smoothedgrown_5.png and /dev/null differ diff --git a/docs/img/smoothedgrown_6.png b/docs/img/smoothedgrown_6.png deleted file mode 100644 index 0a7f17595..000000000 Binary files a/docs/img/smoothedgrown_6.png and /dev/null differ diff --git a/docs/img/smoothedgrown_7.png b/docs/img/smoothedgrown_7.png deleted file mode 100644 index 4351d6881..000000000 Binary files a/docs/img/smoothedgrown_7.png and /dev/null differ diff --git a/docs/img/temperaturehumiditydecisionhills.jpg b/docs/img/temperaturehumiditydecisionhills.jpg deleted file mode 100644 index c755df158..000000000 Binary files a/docs/img/temperaturehumiditydecisionhills.jpg and /dev/null differ diff --git a/docs/img/temperaturehumiditydecisionsimple.jpg b/docs/img/temperaturehumiditydecisionsimple.jpg deleted file mode 100644 index cbb1271b5..000000000 Binary files a/docs/img/temperaturehumiditydecisionsimple.jpg and /dev/null differ diff --git a/docs/img/terraincomposition.jpg b/docs/img/terraincomposition.jpg deleted file mode 100644 index 3d03e101d..000000000 Binary files a/docs/img/terraincomposition.jpg and /dev/null differ diff --git a/docs/img/terrainheight.jpg b/docs/img/terrainheight.jpg deleted file mode 100644 index bcbafcfaf..000000000 Binary files a/docs/img/terrainheight.jpg and /dev/null differ diff --git a/docs/img/twolevelbiomes.png b/docs/img/twolevelbiomes.png deleted file mode 100644 index a3104733f..000000000 Binary files a/docs/img/twolevelbiomes.png and /dev/null differ diff --git a/docs/img/twolevellargeareas.jpg b/docs/img/twolevellargeareas.jpg deleted file mode 100644 index 9d5d5ac8a..000000000 Binary files a/docs/img/twolevellargeareas.jpg and /dev/null differ diff --git a/docs/img/twolevelsmallareas.jpg b/docs/img/twolevelsmallareas.jpg deleted file mode 100644 index 14afbc42a..000000000 Binary files a/docs/img/twolevelsmallareas.jpg and /dev/null differ diff --git a/docs/img/twolevelsmallgrid.jpg b/docs/img/twolevelsmallgrid.jpg deleted file mode 100644 index 6c75e0b28..000000000 Binary files a/docs/img/twolevelsmallgrid.jpg and /dev/null differ diff --git a/docs/img/vanilla_springs_huge.png b/docs/img/vanilla_springs_huge.png deleted file mode 100644 index 694389c85..000000000 Binary files a/docs/img/vanilla_springs_huge.png and /dev/null differ diff --git a/docs/img/voronoi.png b/docs/img/voronoi.png deleted file mode 100644 index e61e183ef..000000000 Binary files a/docs/img/voronoi.png and /dev/null differ diff --git a/docs/img/voronoijitterbiomes.png b/docs/img/voronoijitterbiomes.png deleted file mode 100644 index 42f0b7e40..000000000 Binary files a/docs/img/voronoijitterbiomes.png and /dev/null differ diff --git a/docs/img/zoomedgrown_1.png b/docs/img/zoomedgrown_1.png deleted file mode 100644 index c73326b0e..000000000 Binary files a/docs/img/zoomedgrown_1.png and /dev/null differ diff --git a/docs/img/zoomedgrown_2.png b/docs/img/zoomedgrown_2.png deleted file mode 100644 index 45fa6427f..000000000 Binary files a/docs/img/zoomedgrown_2.png and /dev/null differ diff --git a/docs/img/zoomedgrown_3.png b/docs/img/zoomedgrown_3.png deleted file mode 100644 index 3c9d89759..000000000 Binary files a/docs/img/zoomedgrown_3.png and /dev/null differ diff --git a/docs/img/zoomedgrown_4.png b/docs/img/zoomedgrown_4.png deleted file mode 100644 index 221a5fc76..000000000 Binary files a/docs/img/zoomedgrown_4.png and /dev/null differ diff --git a/docs/img/zoomedgrown_5.png b/docs/img/zoomedgrown_5.png deleted file mode 100644 index 3881532ca..000000000 Binary files a/docs/img/zoomedgrown_5.png and /dev/null differ diff --git a/docs/img/zoomedgrown_6.png b/docs/img/zoomedgrown_6.png deleted file mode 100644 index cc03d2150..000000000 Binary files a/docs/img/zoomedgrown_6.png and /dev/null differ diff --git a/docs/img/zoomedgrown_7.png b/docs/img/zoomedgrown_7.png deleted file mode 100644 index 7a9f43aac..000000000 Binary files a/docs/img/zoomedgrown_7.png and /dev/null differ diff --git a/docs/js/ValueMap.js b/docs/js/ValueMap.js deleted file mode 100644 index 31ddba27e..000000000 --- a/docs/js/ValueMap.js +++ /dev/null @@ -1,139 +0,0 @@ - -const g_DistanceBetweenSquares = 0;//.01; -const g_Colors = [ - "#0000FF", - "#00FF00", - "#FF0000", - "#FF00FF", - "#00FFFF", - "#FFFF00", - "#000000", - "#9BADFF" -] - -class ValueMap { - constructor() { - this.values = new Uint8Array(4 * 4); - this.sizeX = 4; - this.sizeZ = 4; - this.reset(); - } - - - reset() { - this.sizeX = 4; - this.sizeZ = 4; - this.values = new Uint8Array(this.sizeX * this.sizeZ); - - for (let x = 0; x < this.sizeX; x++) - { - for (let z = 0; z < this.sizeZ; z++) - { - this.values[x + this.sizeZ * z] = Math.floor(Math.random() * 8); - } - } - } - - - chooseRandomNumber() { - let numArguments = arguments.length; - return arguments[Math.floor(Math.random() * arguments.length)]; - } - - - smooth() { - let sizeZ = this.sizeZ - 2; - let sizeX = this.sizeX - 2; - let cache = new Uint8Array((this.sizeX - 2) * (this.sizeZ - 2)); - for (let z = 0; z < sizeZ; z++) - { - for (let x = 0; x < sizeX; x++) - { - let val = this.values[x + 1 + (z + 1) * this.sizeX]; - let above = this.values[x + 1 + z * this.sizeX]; - let below = this.values[x + 1 + (z + 2) * this.sizeX]; - let left = this.values[x + (z + 1) * this.sizeX]; - let right = this.values[x + 2 + (z + 1) * this.sizeX]; - - if ((left == right) && (above == below)) - { - if (Math.random() < 0.5) - { - val = left; - } - else - { - val = below; - } - } - else - { - if (left == right) - { - val = left; - } - if (above == below) - { - val = above; - } - } - cache[x + z * sizeX] = val; - } - } - this.values = cache; - this.sizeX -= 2; - this.sizeZ -= 2; - } - - - zoom() { - let lowStepX = (this.sizeX - 1) * 2; - let lowStepZ = (this.sizeZ - 1) * 2; - let cache = new Uint8Array(lowStepX * lowStepZ); - for (let z = 0; z < this.sizeZ - 1; z++) - { - let idx = (z * 2) * lowStepX; - let PrevZ0 = this.values[z * this.sizeX]; - let PrevZ1 = this.values[(z + 1) * this.sizeX]; - - for (let x = 0; x < this.sizeX - 1; x++) - { - let ValX1Z0 = this.values[x + 1 + z * this.sizeX]; - let ValX1Z1 = this.values[x + 1 + (z + 1) * this.sizeX]; - cache[idx] = PrevZ0; - cache[idx + lowStepX] = this.chooseRandomNumber(PrevZ0, PrevZ1); - cache[idx + 1] = this.chooseRandomNumber(PrevZ0, ValX1Z0); - cache[idx + 1 + lowStepX] = this.chooseRandomNumber(PrevZ0, ValX1Z0, PrevZ1, ValX1Z1); - idx += 2; - PrevZ0 = ValX1Z0; - PrevZ1 = ValX1Z1; - } - } - this.values = cache; - this.sizeX = lowStepX; - this.sizeZ = lowStepZ; - } - - - visualize(context, canvas) { - context.clearRect(0, 0, canvas.width, canvas.height); - const squareSizeX = canvas.width / (this.sizeX - 1) - g_DistanceBetweenSquares; - const squareSizeY = canvas.height / (this.sizeZ - 1) - g_DistanceBetweenSquares; - for (let x = 0; x < this.sizeX - 1; x++) - { - for (let y = 0; y < this.sizeZ - 1; y++) - { - let renderX = canvas.width / (this.sizeX - 1) * x + g_DistanceBetweenSquares; - let renderY = canvas.height / (this.sizeZ - 1) * y + g_DistanceBetweenSquares; - context.fillStyle = g_Colors[this.values[x + y * this.sizeZ]]; - context.fillRect(renderX, renderY, squareSizeX, squareSizeY); - } - } - - context.save(); - context.globalCompositeOperation = 'difference'; - context.fillStyle = 'white'; - context.fillText("Size: " + (this.sizeX - 1) + "x" + (this.sizeZ - 1), 5, 10); - context.restore(); - } -} diff --git a/docs/js/grown.js b/docs/js/grown.js deleted file mode 100644 index 1658ce21c..000000000 --- a/docs/js/grown.js +++ /dev/null @@ -1,79 +0,0 @@ - -let g_Canvas = null; -let g_Context = null; -let g_ValueMap = null; - - - - -function init() { - g_Canvas = document.getElementById("stage"); - g_Context = g_Canvas.getContext("2d"); - g_ValueMap = new ValueMap(); - g_ValueMap.visualize(g_Context, g_Canvas); -} - - - -function btnZoom(btn) { - g_ValueMap.zoom(); - g_ValueMap.visualize(g_Context, g_Canvas); - if ( - (g_ValueMap.sizeX * 2 - 1 > 600) || - (g_ValueMap.sizeZ * 2 - 1 > 600) - ) { - btn.disabled = true; - } -} - - - -function btnSmooth() { - g_ValueMap.smooth(); - g_ValueMap.visualize(g_Context, g_Canvas); -} - - -function btnReset() { - g_ValueMap.reset(); - g_ValueMap.visualize(g_Context, g_Canvas); - - document.getElementById("grownZoomButton").disabled = false; -} - - -function btnAutomatic(target) { - target.disabled = true; - document.getElementById("grownZoomButton").disabled = true; - - // Reset the valuemap. We don't want to continue on a 500x500 map. - g_ValueMap.reset(); - g_ValueMap.visualize(g_Context, g_Canvas); - - const animationTimeBetween = 350; - let zoom = () => { g_ValueMap.zoom() }; - let smooth = () => { g_ValueMap.smooth() }; - let actions = []; - - for (let i = 0; i < 6; i++) actions.push(zoom); // First zoom 6 times - for (let i = 0; i < 3; i++) actions.push(smooth); // Then smooth 3 times - for (let i = 0; i < 2; i++) actions.push(zoom); // Zoom 2 times - for (let i = 0; i < 2; i++) actions.push(smooth); // And finally smooth 2 more times. - - let update = () => { - if (actions[0] == null) { - target.disabled = false; - return; - } - - actions[0].call(); - g_ValueMap.visualize(g_Context, g_Canvas); - - actions.splice(0, 1); - setTimeout(update, animationTimeBetween); - }; - setTimeout(update, animationTimeBetween + 500); -} - - -window.onload = init; diff --git a/docs/style.css b/docs/style.css deleted file mode 100644 index 74419d8da..000000000 --- a/docs/style.css +++ /dev/null @@ -1,12 +0,0 @@ -table -{ - border: 1px outset; - border-spacing: 0px; - border-collapse: separate; -} - -td, th -{ - border: 1px inset -} - -- cgit v1.2.3