|
4 | 4 | import com.google.common.cache.LoadingCache; |
5 | 5 | import de.btegermany.terraplusminus.Terraplusminus; |
6 | 6 | import de.btegermany.terraplusminus.gen.tree.TreePopulator; |
7 | | -import de.btegermany.terraplusminus.utils.ConfigurationHelper; |
8 | 7 | import de.btegermany.terraplusminus.utils.Properties; |
9 | 8 | import lombok.Getter; |
10 | 9 | import net.buildtheearth.terraminusminus.generator.CachedChunkData; |
11 | 10 | import net.buildtheearth.terraminusminus.generator.ChunkDataLoader; |
12 | 11 | import net.buildtheearth.terraminusminus.generator.EarthGeneratorSettings; |
13 | 12 | import net.buildtheearth.terraminusminus.projection.GeographicProjection; |
14 | 13 | import net.buildtheearth.terraminusminus.projection.transform.OffsetProjectionTransform; |
15 | | -import net.buildtheearth.terraminusminus.substitutes.BlockState; |
16 | 14 | import net.buildtheearth.terraminusminus.substitutes.ChunkPos; |
17 | 15 | import net.buildtheearth.terraminusminus.util.http.Http; |
18 | 16 | import org.bukkit.HeightMap; |
|
21 | 19 | import org.bukkit.World; |
22 | 20 | import org.bukkit.block.Biome; |
23 | 21 | import org.bukkit.block.Block; |
| 22 | +import org.bukkit.block.data.BlockData; |
24 | 23 | import org.bukkit.generator.BiomeProvider; |
25 | 24 | import org.bukkit.generator.BlockPopulator; |
26 | 25 | import org.bukkit.generator.ChunkGenerator; |
|
34 | 33 | import java.util.concurrent.TimeUnit; |
35 | 34 |
|
36 | 35 | import static java.lang.Math.min; |
| 36 | +import static java.util.Collections.singletonList; |
37 | 37 | import static net.buildtheearth.terraminusminus.substitutes.ChunkPos.blockToCube; |
38 | 38 | import static net.buildtheearth.terraminusminus.substitutes.ChunkPos.cubeToMinBlock; |
39 | | -import static net.buildtheearth.terraminusminus.substitutes.TerraBukkit.toBukkitBlockData; |
40 | 39 | import static org.bukkit.Material.*; |
41 | 40 | import static org.bukkit.block.Biome.*; |
42 | 41 |
|
| 42 | +/** |
| 43 | + * A world generator using Terra-- as the generation engine. |
| 44 | + * It is opinionated and optimized for BTE creative building |
| 45 | + * (very bland terrain with no features at all). |
| 46 | + */ |
43 | 47 | public class RealWorldGenerator extends ChunkGenerator { |
44 | 48 |
|
45 | 49 | @Getter |
46 | 50 | private final EarthGeneratorSettings settings; |
47 | 51 | @Getter |
48 | 52 | private final int yOffset; |
49 | | - private Location spawnLocation = null; |
50 | 53 |
|
51 | | - private final LoadingCache<ChunkPos, CompletableFuture<CachedChunkData>> cache; |
| 54 | + private final LoadingCache<@NotNull ChunkPos, @NotNull CompletableFuture<CachedChunkData>> cache; |
52 | 55 | private final CustomBiomeProvider customBiomeProvider; |
53 | 56 |
|
54 | 57 |
|
55 | | - private final Material surfaceMaterial; |
56 | | - private final Map<String, Material> materialMapping; |
| 58 | + private final BlockData defaultSurfaceBlock; |
| 59 | + private final BlockData mountainSurfaceBlock = STONE.createBlockData(); |
| 60 | + private final BlockData underwaterBlock = DIRT.createBlockData(); |
| 61 | + private final Map<Biome, BlockData> defaultBiomeSurfaceBlocks = Map.of( |
| 62 | + DESERT, SAND.createBlockData(), |
| 63 | + SNOWY_SLOPES, SNOW_BLOCK.createBlockData(), |
| 64 | + SNOWY_PLAINS, SNOW_BLOCK.createBlockData(), |
| 65 | + FROZEN_PEAKS, SNOW_BLOCK.createBlockData() |
| 66 | + ); |
| 67 | + private final BlockMapper blockMapper; |
57 | 68 |
|
58 | 69 | private static final Set<Material> GRASS_LIKE_MATERIALS = Set.of( |
59 | 70 | GRASS_BLOCK, |
@@ -88,53 +99,32 @@ public RealWorldGenerator(int yOffset, Terraplusminus plugin) { |
88 | 99 | .softValues() |
89 | 100 | .build(new ChunkDataLoader(this.settings)); |
90 | 101 |
|
91 | | - this.surfaceMaterial = ConfigurationHelper.getMaterial(plugin.getConfig(), Properties.SURFACE_MATERIAL, GRASS_BLOCK); |
92 | | - this.materialMapping = Map.of( |
93 | | - "minecraft:bricks", ConfigurationHelper.getMaterial(plugin.getConfig(), Properties.BUILDING_OUTLINES_MATERIAL, BRICKS), |
94 | | - "minecraft:gray_concrete", ConfigurationHelper.getMaterial(plugin.getConfig(), Properties.ROAD_MATERIAL, GRAY_CONCRETE_POWDER), |
95 | | - "minecraft:dirt_path", ConfigurationHelper.getMaterial(plugin.getConfig(), Properties.PATH_MATERIAL, MOSS_BLOCK) |
96 | | - ); |
97 | | - |
| 102 | + // This code is explicitly there for backward compatibility and is legitimate in using the deprecated config keys |
| 103 | + this.blockMapper = BlockMapper.fromPlugin(plugin) |
| 104 | + .withStaticGenericSurface(GRASS_BLOCK) |
| 105 | + .withConfiguredGenericSurface(Properties.SURFACE_MATERIAL) // Overrides the static definition if present |
| 106 | + .withConfiguredMapping("minecraft:bricks", Properties.BUILDING_OUTLINES_MATERIAL) |
| 107 | + .withConfiguredMapping("minecraft:gray_concrete", Properties.ROAD_MATERIAL) |
| 108 | + .withConfiguredMapping("minecraft:dirt_path", Properties.PATH_MATERIAL) |
| 109 | + .build(); |
| 110 | + this.defaultSurfaceBlock = this.blockMapper.genericSurfaceBlock(); |
98 | 111 | } |
99 | 112 |
|
100 | 113 |
|
101 | 114 | @Override |
102 | 115 | public void generateNoise(@NotNull WorldInfo worldInfo, @NotNull Random random, int chunkX, int chunkZ, @NotNull ChunkData chunkData) { |
103 | | - |
104 | 116 | CachedChunkData terraData = this.getTerraChunkData(chunkX, chunkZ); |
105 | 117 |
|
106 | 118 | int minWorldY = worldInfo.getMinHeight(); |
107 | 119 | int maxWorldY = worldInfo.getMaxHeight(); |
108 | 120 |
|
109 | | - // We start by finding the lowest 16x16x16 cube that's not underground |
110 | | - //TODO expose the minimum surface Y in Terra-- so we don't have to scan this way |
| 121 | + // Optimization: if the entire chunk is above the surface, there is nothing to do |
111 | 122 | int minSurfaceCubeY = blockToCube(minWorldY - this.yOffset); |
112 | | - int maxWorldCubeY = blockToCube(maxWorldY - this.yOffset); |
113 | 123 | if (terraData.aboveSurface(minSurfaceCubeY)) { |
114 | | - return; // All done, it's all air |
115 | | - } |
116 | | - while (minSurfaceCubeY < maxWorldCubeY && terraData.belowSurface(minSurfaceCubeY)) { |
117 | | - minSurfaceCubeY++; |
118 | | - } |
119 | | - |
120 | | - // We can now fill most of the underground in a single call. |
121 | | - // Hopefully the underlying implementation can take advantage of that... |
122 | | - if (minSurfaceCubeY >= maxWorldCubeY) { |
123 | | - chunkData.setRegion( |
124 | | - 0, minWorldY, 0, |
125 | | - 16, maxWorldY, 16, |
126 | | - STONE |
127 | | - ); |
128 | | - return; // All done, everything is underground |
129 | | - } else { |
130 | | - chunkData.setRegion( |
131 | | - 0, minWorldY, 0, |
132 | | - 0, cubeToMinBlock(minSurfaceCubeY), 0, |
133 | | - STONE |
134 | | - ); |
| 124 | + return; |
135 | 125 | } |
136 | 126 |
|
137 | | - // And now, we build the actual terrain shape on top of everything |
| 127 | + // And now, we build the actual terrain shape |
138 | 128 | for (int x = 0; x < 16; x++) { |
139 | 129 | for (int z = 0; z < 16; z++) { |
140 | 130 | int groundHeight = min(terraData.groundHeight(x, z) + this.yOffset, maxWorldY - 1); |
@@ -175,39 +165,24 @@ public void generateSurface(@NotNull WorldInfo worldInfo, @NotNull Random random |
175 | 165 | continue; // We are not within vertical bounds, continue |
176 | 166 | } |
177 | 167 |
|
178 | | - Material material; |
179 | | - |
180 | | - BlockState state = terraData.surfaceBlock(x, z); |
181 | | - if (state != null) { |
182 | | - // Terra--'s OSM config says a feature should be drawn there, let's transform it to respect our config |
183 | | - material = this.materialMapping.get(state.getBlock().toString()); |
184 | | - if (material == null) { |
185 | | - // We don't know what material this is, let's respect what the Terra-- configuration says |
186 | | - material = toBukkitBlockData(state).getMaterial(); |
187 | | - } |
188 | | - } else if (groundY >= startMountainHeight) { |
189 | | - material = STONE; // Mountains stare bare |
190 | | - } else { |
191 | | - // Fallback to a generic block that matches the biome |
192 | | - Biome biome = chunkData.getBiome(x, groundY, z); |
193 | | - if (biome == DESERT) { |
194 | | - material = Material.SAND; |
195 | | - |
196 | | - } else if (biome == SNOWY_SLOPES || biome == SNOWY_PLAINS || biome == FROZEN_PEAKS){ |
197 | | - material = SNOW_BLOCK; |
198 | | - |
| 168 | + BlockData surfaceBlock = this.blockMapper.map(terraData.surfaceBlock(x, z)); |
| 169 | + if (surfaceBlock == null) { |
| 170 | + if (groundY >= startMountainHeight) { |
| 171 | + surfaceBlock = this.mountainSurfaceBlock; // Mountains stay bare |
199 | 172 | } else { |
200 | | - material = this.surfaceMaterial; |
| 173 | + // Fallback to a generic block that matches the biome, or to the default block |
| 174 | + Biome biome = chunkData.getBiome(x, groundY, z); |
| 175 | + surfaceBlock = this.defaultBiomeSurfaceBlocks.getOrDefault(biome, this.defaultSurfaceBlock); |
201 | 176 | } |
202 | 177 | } |
203 | 178 |
|
204 | | - // We don't want grass, snow, and all underwater |
| 179 | + // We don't want grass, snow, and all that underwater |
205 | 180 | boolean isUnderWater = groundY + 1 >= maxWorldY || chunkData.getBlockData(x, groundY + 1, z).getMaterial().equals(WATER); |
206 | | - if (isUnderWater && GRASS_LIKE_MATERIALS.contains(material)) { |
207 | | - material = DIRT; |
| 181 | + if (isUnderWater && GRASS_LIKE_MATERIALS.contains(surfaceBlock.getMaterial())) { |
| 182 | + surfaceBlock = this.underwaterBlock; |
208 | 183 | } |
209 | 184 |
|
210 | | - chunkData.setBlock(x, groundY, z, material); |
| 185 | + chunkData.setBlock(x, groundY, z, surfaceBlock); |
211 | 186 |
|
212 | 187 | } |
213 | 188 | } |
@@ -264,14 +239,13 @@ public boolean canSpawn(@NotNull World world, int x, int z) { |
264 | 239 | @Override |
265 | 240 | @NotNull |
266 | 241 | public List<BlockPopulator> getDefaultPopulators(@NotNull World world) { |
267 | | - return Collections.singletonList(new TreePopulator(customBiomeProvider, yOffset)); |
| 242 | + return singletonList(new TreePopulator(this.customBiomeProvider, yOffset)); |
268 | 243 | } |
269 | 244 |
|
270 | | - @Override |
271 | 245 | @Nullable |
| 246 | + @Override |
272 | 247 | public Location getFixedSpawnLocation(@NotNull World world, @NotNull Random random) { |
273 | | - if (spawnLocation == null) |
274 | | - spawnLocation = new Location(world, 3517417, 58, -5288234); |
275 | | - return spawnLocation; |
| 248 | + return new Location(world, 3517417, 58, -5288234); |
276 | 249 | } |
| 250 | + |
277 | 251 | } |
0 commit comments