From 2236e7c3115512c9a9ff859aa5de542310289d55 Mon Sep 17 00:00:00 2001 From: burgerguy Date: Fri, 15 May 2020 23:04:52 -0400 Subject: [PATCH 01/17] Speed up some things in EndIslandOracle, add End Gateways as an icon --- pom.xml | 9 ++ src/main/java/amidst/AmidstSettings.java | 2 + .../amidst/fragment/layer/LayerBuilder.java | 7 +- .../java/amidst/fragment/layer/LayerIds.java | 3 +- .../java/amidst/gui/main/menu/LayersMenu.java | 10 +- .../amidst/gui/main/menu/MenuShortcuts.java | 1 + .../java/amidst/mojangapi/world/World.java | 9 +- .../amidst/mojangapi/world/WorldBuilder.java | 5 +- .../amidst/mojangapi/world/WorldSeed.java | 1 - .../icon/producer/EndGatewayProducer.java | 145 ++++++++++++++++++ .../icon/type/DefaultWorldIconTypes.java | 38 ++--- .../world/oracle/EndIslandOracle.java | 38 +---- .../mojangapi/world/oracle/SimplexNoise.java | 12 +- .../DefaultVersionFeatures.java | 27 ++-- .../world/versionfeatures/FeatureKey.java | 49 +++--- .../amidst/gui/main/icon/end_gateway.png | Bin 0 -> 444 bytes .../gui/main/icon/possible_end_gateway.png | Bin 0 -> 1072 bytes 17 files changed, 256 insertions(+), 100 deletions(-) create mode 100644 src/main/java/amidst/mojangapi/world/icon/producer/EndGatewayProducer.java create mode 100644 src/main/resources/amidst/gui/main/icon/end_gateway.png create mode 100644 src/main/resources/amidst/gui/main/icon/possible_end_gateway.png diff --git a/pom.xml b/pom.xml index 2fa826913..e0c9e6e94 100644 --- a/pom.xml +++ b/pom.xml @@ -10,6 +10,10 @@ local-m2 file://${project.basedir}/m2 + + jitpack.io + https://jitpack.io + yyyy-MM-dd HH:mm @@ -114,5 +118,10 @@ provided maven-plugin + + com.github.KaptainWutax + SeedUtils + master-SNAPSHOT + \ No newline at end of file diff --git a/src/main/java/amidst/AmidstSettings.java b/src/main/java/amidst/AmidstSettings.java index af681a34b..63f773869 100644 --- a/src/main/java/amidst/AmidstSettings.java +++ b/src/main/java/amidst/AmidstSettings.java @@ -28,6 +28,7 @@ public class AmidstSettings { public final Setting showOceanFeatures; public final Setting showNetherFortresses; public final Setting showEndCities; + public final Setting showEndGateways; public final Setting smoothScrolling; public final Setting fragmentFading; @@ -62,6 +63,7 @@ public AmidstSettings(Preferences preferences) { showOceanFeatures = Setting.createBoolean( preferences, "oceanFeaturesIcons", true); showNetherFortresses = Setting.createBoolean( preferences, "netherFortressIcons", false); showEndCities = Setting.createBoolean( preferences, "endCityIcons", false); + showEndGateways = Setting.createBoolean( preferences, "endGatewayIcons", false); smoothScrolling = Setting.createBoolean( preferences, "mapFlicking", true); fragmentFading = Setting.createBoolean( preferences, "mapFading", true); diff --git a/src/main/java/amidst/fragment/layer/LayerBuilder.java b/src/main/java/amidst/fragment/layer/LayerBuilder.java index 98ef5082f..8a6551347 100644 --- a/src/main/java/amidst/fragment/layer/LayerBuilder.java +++ b/src/main/java/amidst/fragment/layer/LayerBuilder.java @@ -99,6 +99,7 @@ private List createDeclarations(AmidstSettings settings, List< declare(settings, declarations, enabledLayers, LayerIds.OCEAN_FEATURES, Dimension.OVERWORLD, false, settings.showOceanFeatures); declare(settings, declarations, enabledLayers, LayerIds.NETHER_FORTRESS, Dimension.OVERWORLD, false, settings.showNetherFortresses); declare(settings, declarations, enabledLayers, LayerIds.END_CITY, Dimension.END, false, settings.showEndCities); + declare(settings, declarations, enabledLayers, LayerIds.END_GATEWAY, Dimension.END, false, settings.showEndGateways); // @formatter:on return Collections.unmodifiableList(Arrays.asList(declarations)); } @@ -144,7 +145,8 @@ private Iterable createLoaders( new WorldIconLoader<>(declarations.get(LayerIds.WOODLAND_MANSION),world.getWoodlandMansionProducer()), new WorldIconLoader<>(declarations.get(LayerIds.OCEAN_FEATURES), world.getOceanFeaturesProducer()), new WorldIconLoader<>(declarations.get(LayerIds.NETHER_FORTRESS), world.getNetherFortressProducer()), - new WorldIconLoader<>(declarations.get(LayerIds.END_CITY), world.getEndCityProducer(), Fragment::getEndIslands) + new WorldIconLoader<>(declarations.get(LayerIds.END_CITY), world.getEndCityProducer(), Fragment::getEndIslands), + new WorldIconLoader<>(declarations.get(LayerIds.END_GATEWAY), world.getEndGatewayProducer(), Fragment::getEndIslands) )); // @formatter:on } @@ -173,7 +175,8 @@ private Iterable createDrawers( new WorldIconDrawer(declarations.get(LayerIds.WOODLAND_MANSION),zoom, worldIconSelection), new WorldIconDrawer(declarations.get(LayerIds.OCEAN_FEATURES), zoom, worldIconSelection), new WorldIconDrawer(declarations.get(LayerIds.NETHER_FORTRESS), zoom, worldIconSelection), - new WorldIconDrawer(declarations.get(LayerIds.END_CITY), zoom, worldIconSelection) + new WorldIconDrawer(declarations.get(LayerIds.END_CITY), zoom, worldIconSelection), + new WorldIconDrawer(declarations.get(LayerIds.END_GATEWAY), zoom, worldIconSelection) )); // @formatter:on } diff --git a/src/main/java/amidst/fragment/layer/LayerIds.java b/src/main/java/amidst/fragment/layer/LayerIds.java index 51d527a48..f4b1f4b7a 100644 --- a/src/main/java/amidst/fragment/layer/LayerIds.java +++ b/src/main/java/amidst/fragment/layer/LayerIds.java @@ -26,6 +26,7 @@ public class LayerIds { public static final int OCEAN_FEATURES = 14; public static final int NETHER_FORTRESS = 15; public static final int END_CITY = 16; - public static final int NUMBER_OF_LAYERS = 17; + public static final int END_GATEWAY = 17; + public static final int NUMBER_OF_LAYERS = 18; // @formatter:on } diff --git a/src/main/java/amidst/gui/main/menu/LayersMenu.java b/src/main/java/amidst/gui/main/menu/LayersMenu.java index d9fb678b3..83b046f43 100644 --- a/src/main/java/amidst/gui/main/menu/LayersMenu.java +++ b/src/main/java/amidst/gui/main/menu/LayersMenu.java @@ -78,7 +78,7 @@ private void createOverworldAndEndLayers(Dimension dimension) { createOverworldLayers(dimension); menu.addSeparator(); Menus.radio( menu, dimensionSetting, group, Dimension.END, MenuShortcuts.DISPLAY_DIMENSION_END); - endLayer( settings.showEndCities, "End City Icons", getIcon("end_city.png"), MenuShortcuts.SHOW_END_CITIES, dimension, LayerIds.END_CITY); + createEndLayers(dimension); // @formatter:on } @@ -97,6 +97,14 @@ private void createOverworldLayers(Dimension dimension) { overworldLayer(settings.showNetherFortresses, "Nether Fortress Icons", getIcon("nether_fortress.png"), MenuShortcuts.SHOW_NETHER_FORTRESSES, dimension, LayerIds.NETHER_FORTRESS); // @formatter:on } + + @CalledOnlyBy(AmidstThread.EDT) + private void createEndLayers(Dimension dimension) { + // @formatter:off + endLayer( settings.showEndCities, "End City Icons", getIcon("end_city.png"), MenuShortcuts.SHOW_END_CITIES, dimension, LayerIds.END_CITY); + endLayer( settings.showEndGateways, "End Gateway Icons", getIcon("end_gateway.png"), MenuShortcuts.SHOW_END_GATEWAYS, dimension, LayerIds.END_GATEWAY); + // @formatter:on + } @CalledOnlyBy(AmidstThread.EDT) private void createAllDimensions() { diff --git a/src/main/java/amidst/gui/main/menu/MenuShortcuts.java b/src/main/java/amidst/gui/main/menu/MenuShortcuts.java index bb096924a..1e758965d 100644 --- a/src/main/java/amidst/gui/main/menu/MenuShortcuts.java +++ b/src/main/java/amidst/gui/main/menu/MenuShortcuts.java @@ -48,6 +48,7 @@ public enum MenuShortcuts implements MenuShortcut { // It's okay to duplicate the Overworld layers shortcuts here, because // the End layers will never be active at the same time. SHOW_END_CITIES("menu 1"), + SHOW_END_GATEWAYS("menu 2"), SHOW_GRID("menu G"), SHOW_PLAYERS("menu P"), diff --git a/src/main/java/amidst/mojangapi/world/World.java b/src/main/java/amidst/mojangapi/world/World.java index 9a145b969..3cbde560e 100644 --- a/src/main/java/amidst/mojangapi/world/World.java +++ b/src/main/java/amidst/mojangapi/world/World.java @@ -39,6 +39,7 @@ public class World { private final WorldIconProducer oceanFeaturesProducer; private final WorldIconProducer netherFortressProducer; private final WorldIconProducer> endCityProducer; + private final WorldIconProducer> endGatewayProducer; public World( Consumer onDisposeWorld, @@ -60,8 +61,9 @@ public World( WorldIconProducer woodlandMansionProducer, WorldIconProducer oceanFeaturesProducer, WorldIconProducer netherFortressProducer, - WorldIconProducer> endCityProducer) { this.onDisposeWorld = onDisposeWorld; + WorldIconProducer> endCityProducer, + WorldIconProducer> endGatewayProducer) { this.worldOptions = worldOptions; this.movablePlayerList = movablePlayerList; this.recognisedVersion = recognisedVersion; @@ -81,6 +83,7 @@ public World( this.oceanFeaturesProducer = oceanFeaturesProducer; this.netherFortressProducer = netherFortressProducer; this.endCityProducer = endCityProducer; + this.endGatewayProducer = endGatewayProducer; } public WorldOptions getWorldOptions() { @@ -150,6 +153,10 @@ public WorldIconProducer getNetherFortressProducer() { public WorldIconProducer> getEndCityProducer() { return endCityProducer; } + + public WorldIconProducer> getEndGatewayProducer() { + return endGatewayProducer; + } public WorldIconProducer getWoodlandMansionProducer() { return woodlandMansionProducer; diff --git a/src/main/java/amidst/mojangapi/world/WorldBuilder.java b/src/main/java/amidst/mojangapi/world/WorldBuilder.java index 42cde6409..167fb541c 100644 --- a/src/main/java/amidst/mojangapi/world/WorldBuilder.java +++ b/src/main/java/amidst/mojangapi/world/WorldBuilder.java @@ -207,9 +207,10 @@ private World create( new StructureProducer<>( Resolution.CHUNK, 8, - versionFeatures.get(FeatureKey.END_ISLAND_LOCATION_CHECKER), + versionFeatures.get(FeatureKey.END_CITY_LOCATION_CHECKER), new EndCityWorldIconTypeProvider(), Dimension.END, - false)); + false), + versionFeatures.get(FeatureKey.END_GATEWAY_PRODUCER)); } } diff --git a/src/main/java/amidst/mojangapi/world/WorldSeed.java b/src/main/java/amidst/mojangapi/world/WorldSeed.java index a5a4453d5..56ea51511 100644 --- a/src/main/java/amidst/mojangapi/world/WorldSeed.java +++ b/src/main/java/amidst/mojangapi/world/WorldSeed.java @@ -1,6 +1,5 @@ package amidst.mojangapi.world; -import java.nio.ByteBuffer; import java.security.SecureRandom; import amidst.documentation.Immutable; diff --git a/src/main/java/amidst/mojangapi/world/icon/producer/EndGatewayProducer.java b/src/main/java/amidst/mojangapi/world/icon/producer/EndGatewayProducer.java new file mode 100644 index 000000000..6d3494a31 --- /dev/null +++ b/src/main/java/amidst/mojangapi/world/icon/producer/EndGatewayProducer.java @@ -0,0 +1,145 @@ +package amidst.mojangapi.world.icon.producer; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; + +import amidst.documentation.ThreadSafe; +import amidst.fragment.Fragment; +import amidst.mojangapi.world.Dimension; +import amidst.mojangapi.world.coordinates.CoordinatesInWorld; +import amidst.mojangapi.world.coordinates.Resolution; +import amidst.mojangapi.world.icon.WorldIcon; +import amidst.mojangapi.world.oracle.EndIsland; +import kaptainwutax.seedutils.mc.ChunkRand; +import kaptainwutax.seedutils.mc.MCVersion; + +import static amidst.mojangapi.world.icon.type.DefaultWorldIconTypes.END_GATEWAY; +import static amidst.mojangapi.world.icon.type.DefaultWorldIconTypes.POSSIBLE_END_GATEWAY;; + +@ThreadSafe +public class EndGatewayProducer extends WorldIconProducer> { + private static final int END_GATEWAY_CHANCE = 700; + private static final Resolution RESOLUTION = Resolution.CHUNK; + private static final int SIZE = RESOLUTION.getStepsPerFragment(); + + /* + * We add a buffer room of 16 blocks to go into nearby fragments for if it + * moves out of the original one during tryGetValidLocationFromChunk. This + * does slow it down a bit but it allows us to put the icons in the right + * fragments. + */ + private static final int BUFFER_SIZE = (int) RESOLUTION.convertFromWorldToThis(16); + + /** + * The influence has to be atleast 40 for End Highlands biomes to spawn, + * which is where End Gateways are. + */ + private static final int REQUIRED_BIOME_INFLUENCE = 40; + + /** + * Used as a cache only for the spawn gateways. + */ + private static final EndSpawnGatewayProducer spawnProducer = new EndSpawnGatewayProducer(); + + private final long seed; + + public EndGatewayProducer(long seed) { + this.seed = seed; + } + + @Override + public void produce(CoordinatesInWorld corner, Consumer consumer, List endIslands) { + for (int xRelativeToFragment = -BUFFER_SIZE; xRelativeToFragment < SIZE + BUFFER_SIZE; xRelativeToFragment++) { + for (int yRelativeToFragment = -BUFFER_SIZE; yRelativeToFragment < SIZE + BUFFER_SIZE; yRelativeToFragment++) { + generateAt(corner, consumer, endIslands, xRelativeToFragment, yRelativeToFragment); + } + } + spawnProducer.produce(corner, consumer, null); + } + + // TODO: use longs? + private void generateAt( + CoordinatesInWorld corner, + Consumer consumer, + List endIslands, + int xRelativeToFragment, + int yRelativeToFragment) { + int x = xRelativeToFragment + (int) corner.getXAs(RESOLUTION); + int y = yRelativeToFragment + (int) corner.getYAs(RESOLUTION); + CoordinatesInWorld possibleCoordinates = tryGetValidLocationFromChunk(x, y, endIslands, corner); + if (possibleCoordinates != null) { + consumer.accept( + new WorldIcon( + possibleCoordinates, + END_GATEWAY.getLabel(), + END_GATEWAY.getImage(), + Dimension.END, + false)); + } + } + + /** + * Made with help from + * KaptainWutax. + */ + public CoordinatesInWorld tryGetValidLocationFromChunk(int chunkX, int chunkY, List endIslands, CoordinatesInWorld corner) { + if((chunkX * chunkX + chunkY * chunkY) > 4096) { + ChunkRand rand = new ChunkRand(); + int blockX = chunkX << 4; + int blockY = chunkY << 4; + + rand.setDecoratorSeed(seed, blockX, blockY, 0, 3, MCVersion.v1_13); + + if(rand.nextInt(END_GATEWAY_CHANCE) == 0) { + for(EndIsland island : endIslands) { + float biomeInfluence = island.influenceAtChunk(chunkX, chunkY); + if(biomeInfluence >= REQUIRED_BIOME_INFLUENCE) { + int gatewayX = rand.nextInt(16) + blockX; + int gatewayY = rand.nextInt(16) + blockY; + CoordinatesInWorld coordinates = new CoordinatesInWorld(gatewayX, gatewayY); + if(coordinates.isInBoundsOf(corner, Fragment.SIZE)) { + // While this barely ever is false due to the biome influence check, we do it anyway just to make sure + float placementInfluence = island.influenceAtBlock(gatewayX, gatewayY); + if(placementInfluence > 0.0F) { + return coordinates; + } + } + } + } + } + } else { + } + + return null; + } + + private static class EndSpawnGatewayProducer extends CachedWorldIconProducer { + private static int NUMBER_OF_SPAWN_GATEWAYS = 20; + + @Override + protected List doCreateCache() { + List iconList = new ArrayList(); + for(int i = 0; i < NUMBER_OF_SPAWN_GATEWAYS; i++) { + int x = floor(96.0D * Math.cos(2.0D * (-Math.PI + 0.15707963267948966D * (double) i))); + int y = floor(96.0D * Math.sin(2.0D * (-Math.PI + 0.15707963267948966D * (double) i))); + CoordinatesInWorld possibleCoordinates = new CoordinatesInWorld(x, y); + iconList.add(new WorldIcon( + possibleCoordinates, + POSSIBLE_END_GATEWAY.getLabel(), + POSSIBLE_END_GATEWAY.getImage(), + Dimension.END, + false + ) + ); + } + return iconList; + } + + private static int floor(double value) { + int i = (int) value; + return value < (double) i ? i - 1 : i; + } + } + +} diff --git a/src/main/java/amidst/mojangapi/world/icon/type/DefaultWorldIconTypes.java b/src/main/java/amidst/mojangapi/world/icon/type/DefaultWorldIconTypes.java index 67decc6e6..2f5dacca1 100644 --- a/src/main/java/amidst/mojangapi/world/icon/type/DefaultWorldIconTypes.java +++ b/src/main/java/amidst/mojangapi/world/icon/type/DefaultWorldIconTypes.java @@ -16,24 +16,26 @@ @Immutable public enum DefaultWorldIconTypes { // @formatter:off - NETHER_FORTRESS ("nether_fortress", "Nether Fortress"), - PLAYER ("player", "Player"), - STRONGHOLD ("stronghold", "Stronghold"), - JUNGLE ("jungle", "Jungle Temple"), - DESERT ("desert", "Desert Temple"), - VILLAGE ("village", "Village"), - SPAWN ("spawn", "World Spawn"), - WITCH ("witch", "Witch Hut"), - OCEAN_MONUMENT ("ocean_monument", "Ocean Monument"), - IGLOO ("igloo", "Igloo"), - MINESHAFT ("mineshaft", "Mineshaft"), - WOODLAND_MANSION ("woodland_mansion", "Woodland Mansion"), - END_CITY ("end_city", "Likely End City"), - POSSIBLE_END_CITY ("possible_end_city", "Possible End City"), - OCEAN_RUINS ("ocean_ruins", "Ocean Ruins"), - SHIPWRECK ("shipwreck", "Shipwreck"), - BURIED_TREASURE ("buried_treasure", "Buried Treasure"), - PILLAGER_OUTPOST ("pillager_outpost", "Pillager Outpost"); + NETHER_FORTRESS ("nether_fortress", "Nether Fortress"), + PLAYER ("player", "Player"), + STRONGHOLD ("stronghold", "Stronghold"), + JUNGLE ("jungle", "Jungle Temple"), + DESERT ("desert", "Desert Temple"), + VILLAGE ("village", "Village"), + SPAWN ("spawn", "World Spawn"), + WITCH ("witch", "Witch Hut"), + OCEAN_MONUMENT ("ocean_monument", "Ocean Monument"), + IGLOO ("igloo", "Igloo"), + MINESHAFT ("mineshaft", "Mineshaft"), + WOODLAND_MANSION ("woodland_mansion", "Woodland Mansion"), + END_CITY ("end_city", "Likely End City"), + POSSIBLE_END_CITY ("possible_end_city", "Possible End City"), + END_GATEWAY ("end_gateway", "End Gateway"), + POSSIBLE_END_GATEWAY("possible_end_gateway", "Possible End Gateway"), + OCEAN_RUINS ("ocean_ruins", "Ocean Ruins"), + SHIPWRECK ("shipwreck", "Shipwreck"), + BURIED_TREASURE ("buried_treasure", "Buried Treasure"), + PILLAGER_OUTPOST ("pillager_outpost", "Pillager Outpost"); // @formatter:on private static final Map typeMap = createTypeMap(); diff --git a/src/main/java/amidst/mojangapi/world/oracle/EndIslandOracle.java b/src/main/java/amidst/mojangapi/world/oracle/EndIslandOracle.java index a67e6cd24..951979807 100644 --- a/src/main/java/amidst/mojangapi/world/oracle/EndIslandOracle.java +++ b/src/main/java/amidst/mojangapi/world/oracle/EndIslandOracle.java @@ -2,11 +2,11 @@ import java.util.LinkedList; import java.util.List; -import java.util.Random; import amidst.documentation.ThreadSafe; import amidst.mojangapi.world.coordinates.CoordinatesInWorld; import amidst.mojangapi.world.coordinates.Resolution; +import kaptainwutax.seedutils.lcg.rand.JRand; @ThreadSafe public class EndIslandOracle { @@ -18,38 +18,12 @@ public static EndIslandOracle from(long seed) { * Returns the noise function using the current seed. */ private static SimplexNoise createNoiseFunction(long seed) { - Random random = new Random(seed); - fakePerlin3dOctavesConstructor(random, 16); - fakePerlin3dOctavesConstructor(random, 16); - fakePerlin3dOctavesConstructor(random, 8); - fakePerlin3dOctavesConstructor(random, 10); - fakePerlin3dOctavesConstructor(random, 16); + JRand random = new JRand(seed); + // Mimics the side-effects to the random number generator caused by Minecraft. + random.advance(17292); return new SimplexNoise(random); } - /** - * Mimics the side-effects to the random number generator caused by - * constructing a Perlin3dOctaves instance. - */ - private static void fakePerlin3dOctavesConstructor(Random random, int octaveCount) { - for (int i = 0; i < octaveCount; i++) { - fakePerlin3dConstructor(random); - } - } - - /** - * Mimics the side-effects to the random number generator caused by - * constructing a Perlin3d instance. - */ - private static void fakePerlin3dConstructor(Random random) { - random.nextDouble(); - random.nextDouble(); - random.nextDouble(); - for (int i = 0; i < 256; i++) { - random.nextInt(256 - i); - } - } - /** * Minecraft checks 12 chunks either side of a chunk when assessing island * influence. @@ -128,9 +102,7 @@ private EndIsland createMainEndIsland(int chunkX, int chunkY) { /** * The chunk is in the outer-islands band */ - // TODO: check for threading, do we need synchonized or is the SimplexNoise - // class thread safe? - private synchronized EndIsland tryCreateEndIslandInOuterLands(int chunkX, int chunkY) { + private EndIsland tryCreateEndIslandInOuterLands(int chunkX, int chunkY) { if (noiseFunction.noise(chunkX, chunkY) < ISLAND_DENSITY_THRESHOLD) { return new EndIsland(chunkX, chunkY, getErosionFactor(chunkX, chunkY)); } else { diff --git a/src/main/java/amidst/mojangapi/world/oracle/SimplexNoise.java b/src/main/java/amidst/mojangapi/world/oracle/SimplexNoise.java index a119f1fff..7d613b395 100644 --- a/src/main/java/amidst/mojangapi/world/oracle/SimplexNoise.java +++ b/src/main/java/amidst/mojangapi/world/oracle/SimplexNoise.java @@ -1,6 +1,6 @@ package amidst.mojangapi.world.oracle; -import java.util.Random; +import kaptainwutax.seedutils.lcg.rand.JRand; /** * A speed-improved simplex noise algorithm for 2D, 3D and 4D in Java. @@ -54,17 +54,15 @@ public class SimplexNoise { private static final double F4 = (Math.sqrt(5.0) - 1.0) / 4.0; private static final double G4 = (5.0 - Math.sqrt(5.0)) / 20.0; - public SimplexNoise(Random random) { + public SimplexNoise(JRand random) { // To remove the need for index wrapping, double the permutation table // length perm = new short[512]; permMod12 = new short[512]; - // Minecraft uses the PRNG 3 times before building the - // permutation table. - random.nextDouble(); - random.nextDouble(); - random.nextDouble(); + // Minecraft calls nextDouble 3 times before building the permutation + // table, which is equivalent to 6 next calls. + random.advance(6); // Build a permutation table using our seeded PRNG for (short i = 0; i < 256; ++i) { diff --git a/src/main/java/amidst/mojangapi/world/versionfeatures/DefaultVersionFeatures.java b/src/main/java/amidst/mojangapi/world/versionfeatures/DefaultVersionFeatures.java index a8c2fe7db..736696b63 100644 --- a/src/main/java/amidst/mojangapi/world/versionfeatures/DefaultVersionFeatures.java +++ b/src/main/java/amidst/mojangapi/world/versionfeatures/DefaultVersionFeatures.java @@ -27,6 +27,7 @@ import amidst.mojangapi.world.icon.locationchecker.VillageLocationChecker; import amidst.mojangapi.world.icon.locationchecker.WoodlandMansionLocationChecker; import amidst.mojangapi.world.icon.producer.CachedWorldIconProducer; +import amidst.mojangapi.world.icon.producer.EndGatewayProducer; import amidst.mojangapi.world.icon.producer.StrongholdProducer_128Algorithm; import amidst.mojangapi.world.icon.producer.StrongholdProducer_Buggy128Algorithm; import amidst.mojangapi.world.icon.producer.StrongholdProducer_Original; @@ -38,18 +39,18 @@ public enum DefaultVersionFeatures { ; - public static VersionFeatures.Builder builder(WorldOptions worldOptions, MinecraftInterface minecraftInterface) { - if (worldOptions == null || minecraftInterface == null) { + public static VersionFeatures.Builder builder(WorldOptions worldOptions, MinecraftInterface.World minecraftWorld) { + if (worldOptions == null || minecraftWorld == null) { return FEATURES_BUILDER.clone(); } else { return FEATURES_BUILDER.clone() .withValue(FeatureKey.WORLD_OPTIONS, worldOptions) - .withValue(MINECRAFT_INTERFACE, minecraftInterface); + .withValue(MINECRAFT_WORLD, minecraftWorld); } } // @formatter:off - private static final FeatureKey MINECRAFT_INTERFACE = FeatureKey.make(); + private static final FeatureKey MINECRAFT_WORLD = FeatureKey.make(); public static final FeatureKey> VALID_BIOMES_FOR_STRUCTURE_SPAWN = FeatureKey.make(); private static final FeatureKey> VALID_BIOMES_AT_MIDDLE_OF_CHUNK_STRONGHOLD = FeatureKey.make(); private static final FeatureKey> VALID_BIOMES_FOR_STRUCTURE_VILLAGE = FeatureKey.make(); @@ -80,11 +81,10 @@ public static VersionFeatures.Builder builder(WorldOptions worldOptions, Minecra private static final FeatureKey BUGGY_STRUCTURE_COORDINATE_MATH = FeatureKey.make(); private static final VersionFeatures.Builder FEATURES_BUILDER = VersionFeatures.builder() - .with(FeatureKey.BIOME_DATA_ORACLE, VersionFeature.bind(features -> - VersionFeature.constant(new BiomeDataOracle( - features.get(MINECRAFT_INTERFACE), - features.get(FeatureKey.BIOME_LIST) - )) + .with(FeatureKey.BIOME_DATA_ORACLE, (recognisedVersion, features) -> new BiomeDataOracle( + features.get(MINECRAFT_WORLD), + recognisedVersion, + features.get(FeatureKey.BIOME_LIST) )) .with(FeatureKey.ENABLED_LAYERS, VersionFeature. listBuilder() .init( @@ -111,7 +111,8 @@ public static VersionFeatures.Builder builder(WorldOptions worldOptions, Minecra ).sinceExtend(RecognisedVersion._16w43a, LayerIds.WOODLAND_MANSION ).sinceExtend(RecognisedVersion._18w09a, - LayerIds.OCEAN_FEATURES + LayerIds.OCEAN_FEATURES, + LayerIds.END_GATEWAY // were introduced in 16w39a, but we can only find them past here ).construct()) .with(FeatureKey.BIOME_LIST, DefaultBiomes.DEFAULT_BIOMES) @@ -148,9 +149,13 @@ public static VersionFeatures.Builder builder(WorldOptions worldOptions, Minecra VersionFeature.constant(new NetherFortressAlgorithm(getWorldSeed(features))) )) - .with(FeatureKey.END_ISLAND_LOCATION_CHECKER, VersionFeature.bind(features -> + .with(FeatureKey.END_CITY_LOCATION_CHECKER, VersionFeature.bind(features -> VersionFeature.constant(new EndCityLocationChecker(getWorldSeed(features))) )) + + .with(FeatureKey.END_GATEWAY_PRODUCER, VersionFeature.bind(features -> + VersionFeature.constant(new EndGatewayProducer(getWorldSeed(features))) + )) .with(FeatureKey.MINESHAFT_LOCATION_CHECKER, VersionFeature.bind(features -> VersionFeature. builder() diff --git a/src/main/java/amidst/mojangapi/world/versionfeatures/FeatureKey.java b/src/main/java/amidst/mojangapi/world/versionfeatures/FeatureKey.java index 2ef9d0e17..8ef05946c 100644 --- a/src/main/java/amidst/mojangapi/world/versionfeatures/FeatureKey.java +++ b/src/main/java/amidst/mojangapi/world/versionfeatures/FeatureKey.java @@ -7,7 +7,9 @@ import amidst.mojangapi.world.biome.BiomeList; import amidst.mojangapi.world.icon.locationchecker.LocationChecker; import amidst.mojangapi.world.icon.producer.CachedWorldIconProducer; +import amidst.mojangapi.world.icon.producer.WorldIconProducer; import amidst.mojangapi.world.oracle.BiomeDataOracle; +import amidst.mojangapi.world.oracle.EndIsland; import amidst.mojangapi.world.oracle.EndIslandOracle; import amidst.mojangapi.world.oracle.SlimeChunkOracle; import amidst.mojangapi.world.oracle.WorldSpawnOracle; @@ -16,29 +18,30 @@ public class FeatureKey { // @formatter:off - public static final FeatureKey WORLD_OPTIONS = make(); - public static final FeatureKey BIOME_DATA_ORACLE = make(); - public static final FeatureKey BIOME_LIST = make(); - - public static final FeatureKey> ENABLED_LAYERS = make(); - public static final FeatureKey SLIME_CHUNK_ORACLE = make(); - public static final FeatureKey END_ISLAND_ORACLE = make(); - public static final FeatureKey WORLD_SPAWN_ORACLE = make(); - public static final FeatureKey NETHER_FORTRESS_LOCATION_CHECKER = make(); - public static final FeatureKey END_ISLAND_LOCATION_CHECKER = make(); - public static final FeatureKey MINESHAFT_LOCATION_CHECKER = make(); - public static final FeatureKey STRONGHOLD_PRODUCER = make(); - public static final FeatureKey VILLAGE_LOCATION_CHECKER = make(); - public static final FeatureKey PILLAGER_OUTPOST_LOCATION_CHECKER = make(); - public static final FeatureKey DESERT_TEMPLE_LOCATION_CHECKER = make(); - public static final FeatureKey IGLOO_LOCATION_CHECKER = make(); - public static final FeatureKey JUNGLE_TEMPLE_LOCATION_CHECKER = make(); - public static final FeatureKey WITCH_HUT_LOCATION_CHECKER = make(); - public static final FeatureKey OCEAN_MONUMENT_LOCATION_CHECKER = make(); - public static final FeatureKey WOODLAND_MANSION_LOCATION_CHECKER = make(); - public static final FeatureKey OCEAN_RUINS_LOCATION_CHECKER = make(); - public static final FeatureKey SHIPWRECK_LOCATION_CHECKER = make(); - public static final FeatureKey BURIED_TREASURE_LOCATION_CHECKER = make(); + public static final FeatureKey WORLD_OPTIONS = make(); + public static final FeatureKey BIOME_DATA_ORACLE = make(); + public static final FeatureKey BIOME_LIST = make(); + + public static final FeatureKey> ENABLED_LAYERS = make(); + public static final FeatureKey SLIME_CHUNK_ORACLE = make(); + public static final FeatureKey END_ISLAND_ORACLE = make(); + public static final FeatureKey WORLD_SPAWN_ORACLE = make(); + public static final FeatureKey NETHER_FORTRESS_LOCATION_CHECKER = make(); + public static final FeatureKey END_CITY_LOCATION_CHECKER = make(); + public static final FeatureKey>> END_GATEWAY_PRODUCER = make(); + public static final FeatureKey MINESHAFT_LOCATION_CHECKER = make(); + public static final FeatureKey STRONGHOLD_PRODUCER = make(); + public static final FeatureKey VILLAGE_LOCATION_CHECKER = make(); + public static final FeatureKey PILLAGER_OUTPOST_LOCATION_CHECKER = make(); + public static final FeatureKey DESERT_TEMPLE_LOCATION_CHECKER = make(); + public static final FeatureKey IGLOO_LOCATION_CHECKER = make(); + public static final FeatureKey JUNGLE_TEMPLE_LOCATION_CHECKER = make(); + public static final FeatureKey WITCH_HUT_LOCATION_CHECKER = make(); + public static final FeatureKey OCEAN_MONUMENT_LOCATION_CHECKER = make(); + public static final FeatureKey WOODLAND_MANSION_LOCATION_CHECKER = make(); + public static final FeatureKey OCEAN_RUINS_LOCATION_CHECKER = make(); + public static final FeatureKey SHIPWRECK_LOCATION_CHECKER = make(); + public static final FeatureKey BURIED_TREASURE_LOCATION_CHECKER = make(); // @formatter:on diff --git a/src/main/resources/amidst/gui/main/icon/end_gateway.png b/src/main/resources/amidst/gui/main/icon/end_gateway.png new file mode 100644 index 0000000000000000000000000000000000000000..57b498c1dafa805ccb5de2c7ad6d5afca700c55c GIT binary patch literal 444 zcmV;t0YmPx#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D0Z~arK~y+Tm6S1Z zgfI+6Wp*c%Nu6$6s=5wEZ+fj z$sz8%Uf#$&zmWaDl5xF1Kij71SGqTT8BsmCnP$ONk8V$$l%X5;AP9RD^UxZ mU13jULUO1MLzEQ85&Z(wf0kiPx#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D1I0;1K~zXf?UPSz z+f*39Kfh-?j-9m0mS!taI>gd-nkrVcY^VZ>GQ@!kmr)AWtwJhSgb)%4v@=&!w3QtY zLh29)5R(u?ETH{iV#z3sW!1WMjoLIxlQ@l&IDY;|o*PF=T5;yUPqIJ1_rCw%GXqUV z!*3q&+5NBZC=laWQNqA`=sv{+q8TR9RI8LGqup+`J7Gj3(XUV-fI}&=;&oR=Fhn1wrlaYZlAD!@@oH~T4 zL(0Qc$v*$_O>z0w8VihNYJ6?1xqSuk_Pz+hya$8V`_daRkt)A8bGiC@>dhlA3PCPLr@LM(spdS1Ks?ISh2o|lQKR4g!!zpihg*WNxZByMb2 zrHriVX2Y;+@H<*Ae5cgx;#E~I-F1r979U6 zzD%OR*r1~4&m+L&mZgupd*Rd(SuPEJ^3J6Y$MQ=JqgYfM^7otCpQU#9C>$$Ha7aYK zU>JXRahfAFY+pF{Gto@Ns*zH$M&ujG%9krtx6=?IBt)p;vGHLv_Us|%%GW8=0L856 znWl01ZlZMY$ChrFVViQX0;;M)HeG?|zB^HD?fdKg{e7(>91S06VGOGX0< zg!DZ~GYn%>qeLRHlWHs$gP(qgb4$1H<41qz@ct+K+y|e(%uc`c0@v>kVFow^hTvR$ zs!MSUS5{VF^0`SE9XkexN21OFrK(gNIr{Wbm_B{l`F3$qotc?&7OP1;D%b-a0mAJX zWhJv`s$Isqm@XOxzjgYzSI!lU4Ck%Q0pJ+>rh zP;E3&N!i>;s?DPpcQkg`HAiB}s)955BL6V0u-QCmPPBE@MOUgWq3Cg75CgWUIqSlD z%8{M4n?pO>3!N=xVPV0MF74*b&(Av>@p#;k)Crwer!#kcQ&UqgH#fICB2}P=luk#T zv?IE6+$(~M>x`zoYmP4T?sh^Id!dHCv}>l`X@h7@O}gf|r=c6sO?Po|k&1u602Fr& qwC3K%xa=9RC41?L_&@(40R90w`ngL8TT^!c0000 Date: Fri, 15 May 2020 23:05:59 -0400 Subject: [PATCH 02/17] fix weird merge bug --- src/main/java/amidst/mojangapi/world/World.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/amidst/mojangapi/world/World.java b/src/main/java/amidst/mojangapi/world/World.java index 3cbde560e..7b979fe3c 100644 --- a/src/main/java/amidst/mojangapi/world/World.java +++ b/src/main/java/amidst/mojangapi/world/World.java @@ -61,9 +61,9 @@ public World( WorldIconProducer woodlandMansionProducer, WorldIconProducer oceanFeaturesProducer, WorldIconProducer netherFortressProducer, - this.onDisposeWorld = onDisposeWorld; WorldIconProducer> endCityProducer, WorldIconProducer> endGatewayProducer) { + this.onDisposeWorld = onDisposeWorld; this.worldOptions = worldOptions; this.movablePlayerList = movablePlayerList; this.recognisedVersion = recognisedVersion; From 476052b7e0f50cbd1fc875c4d08617c21da5d3d4 Mon Sep 17 00:00:00 2001 From: burgerguy Date: Fri, 15 May 2020 23:09:32 -0400 Subject: [PATCH 03/17] change some stuff that messed up in merge --- .../versionfeatures/DefaultVersionFeatures.java | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/main/java/amidst/mojangapi/world/versionfeatures/DefaultVersionFeatures.java b/src/main/java/amidst/mojangapi/world/versionfeatures/DefaultVersionFeatures.java index 736696b63..23c4c0c34 100644 --- a/src/main/java/amidst/mojangapi/world/versionfeatures/DefaultVersionFeatures.java +++ b/src/main/java/amidst/mojangapi/world/versionfeatures/DefaultVersionFeatures.java @@ -39,18 +39,18 @@ public enum DefaultVersionFeatures { ; - public static VersionFeatures.Builder builder(WorldOptions worldOptions, MinecraftInterface.World minecraftWorld) { - if (worldOptions == null || minecraftWorld == null) { + public static VersionFeatures.Builder builder(WorldOptions worldOptions, MinecraftInterface minecraftInterface) { + if (worldOptions == null || minecraftInterface == null) { return FEATURES_BUILDER.clone(); } else { return FEATURES_BUILDER.clone() .withValue(FeatureKey.WORLD_OPTIONS, worldOptions) - .withValue(MINECRAFT_WORLD, minecraftWorld); + .withValue(MINECRAFT_INTERFACE, minecraftInterface); } } // @formatter:off - private static final FeatureKey MINECRAFT_WORLD = FeatureKey.make(); + private static final FeatureKey MINECRAFT_INTERFACE = FeatureKey.make(); public static final FeatureKey> VALID_BIOMES_FOR_STRUCTURE_SPAWN = FeatureKey.make(); private static final FeatureKey> VALID_BIOMES_AT_MIDDLE_OF_CHUNK_STRONGHOLD = FeatureKey.make(); private static final FeatureKey> VALID_BIOMES_FOR_STRUCTURE_VILLAGE = FeatureKey.make(); @@ -81,10 +81,11 @@ public static VersionFeatures.Builder builder(WorldOptions worldOptions, Minecra private static final FeatureKey BUGGY_STRUCTURE_COORDINATE_MATH = FeatureKey.make(); private static final VersionFeatures.Builder FEATURES_BUILDER = VersionFeatures.builder() - .with(FeatureKey.BIOME_DATA_ORACLE, (recognisedVersion, features) -> new BiomeDataOracle( - features.get(MINECRAFT_WORLD), - recognisedVersion, - features.get(FeatureKey.BIOME_LIST) + .with(FeatureKey.BIOME_DATA_ORACLE, VersionFeature.bind(features -> + VersionFeature.constant(new BiomeDataOracle( + features.get(MINECRAFT_INTERFACE), + features.get(FeatureKey.BIOME_LIST) + )) )) .with(FeatureKey.ENABLED_LAYERS, VersionFeature. listBuilder() .init( From c4aed0bc75da98ff1807cb62b159e4d902f1c58a Mon Sep 17 00:00:00 2001 From: burgerguy <30326913+burgerguy@users.noreply.github.com> Date: Sat, 16 May 2020 14:07:44 -0400 Subject: [PATCH 04/17] redo some icons --- .../gui/main/icon/possible_end_city.png | Bin 3660 -> 1102 bytes .../gui/main/icon/possible_end_gateway.png | Bin 1072 -> 1107 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/src/main/resources/amidst/gui/main/icon/possible_end_city.png b/src/main/resources/amidst/gui/main/icon/possible_end_city.png index 424e39b6a9566e3f82815e16cd3901154fb85132..cac1a137bc78185c1cf40a80445d0713009863ee 100644 GIT binary patch delta 1082 zcmV-A1jYNz9L@-kBYy-HNklE_=&fy1BHK+_bf&Hed~i5{qc8 z##SQ)Vh}_OUS8XPMWIm8DrjwdQVfy`K51&9Emk8GsiZabhO`)K7Hv{#(`>S4ce~xp z&g{%NKCH83vl|PNJn8o`oc}-HH{buCGv^E;gkmw8BJIsO4}b1Zn6Hiz#YYI!06>U* z){#~w@65+dJAGs#`P~g6oDwcyEY=*^wn^u~ZMqU#Ou3HKJ0OiD-OGn{&&gixg^ z(*6~ID@zL2huXA)SY)F@^;IwJepK!3ZbP&>2mrV|Hc5t$o~s%f7}W_O3;-I!4}Eks zIbv57RG6IC>s_A3{1N0mz}xPOJL|UPfnk8wU5E^fIFO z7*+3HC|F#Py1;!ZBWi$Awd(Qid*Joz7#$gdkhYPtlYcSOzB&2pg&Wq`r3plQ{ zUb_V$ZNriIc*>ZRWrBHOz_e3eQo?j8g-xmA6C$6rM0VQ4BO5r?LOb8u&Rg%_fWZ%s zLr8nxk$?HDBdqa->p%d!vM|7is#WRr4LT3kC{)+|+S)pe2Yh||p62ZxtvK<`dGzl& zX6Niw=2mWM`dVi6q?AIG_0WYQEi-BS7E9)?TvnLht1_?W)z`Q2_KsE@J#-5Fdyd(b zlQpu|Od?^7Us@Ze zO3AO8jX1G5`Po07*qoM6N<$g4Ik0 Ag8%>k literal 3660 zcmV-S4zuxzP)pPPiaF#P*7-ZbZ>KLZ*U+IBfRsybQWXdwQbLP>6pAqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uhf59&ghTmgWD0l;*T zI709Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_IfqH8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 zfg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vFIcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya? z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0 z?2xS?_ve_-kiKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$ z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4 z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i& z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01 z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3| zawq-H%e&ckC+@AhPrP6BKT#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot2z=00004XF*Lt006O$eEU(80000WV@Og>004R=004l4008;_004mL004C` z008P>0026e000+nl3&F}000ALNklS94i*|F`0vk&@qvLV0GXe0|%S1?MARp90M5}SlER@ov!S{O06Jk zUE6h}Sx!Eh6W ztxpmgfbFnBo&z=lO3m698}OPvUIOY`0qhYV3hV-62L^X5lh&s=ZRkwDY!};QvtR#L z3?uf&&cP2~Qu^XO^!J`1F*45cFKpw~h5Z2Xk^P@V?7cI<=0HsX20DwOzI|Rg9 zrS+0kTf$gkWIPlT;tH?;OgV*~>gwd@(J5@;wB7VZfF>{rbl9ipcm%MsFWw_Np4z_6 z0-0I>0f;ZZOTN}7q71jX?db|Pq%mIP5zInUD`e+t!Inrq+IJLyKuZ9CN=a(I`Af3- z)^xi0$!B7;`$Am%{0=)JJ<0VUS$%qIcq-hXWHVY-@xYmZ#tZ>UnjuwmQ!-3T3U63P zE?YfaQFLjFBId&#N_M5ZMTUM)^*ULGWt*WDJswkS3wS24el_~|*TX-xc-&%M_KIn_ zxv8k;)D=x@$nc)3`4$SgFRhk0R4S$dU>cU&Fifdf(!Hi>39B-^{@Zl`PF#K&fGfkZ z-prM9FjXoAPM+TRhfkHopcyCt_Pf_0DZw1!*FkG=rsrDYph) z>2;wd^|8%jN))ly#ISeIQ1uB3f*~MIUb=KDaCcfus`F)Upxs@3(EzA%KN$JiD_!1*Dw0_`%%Y^YDkn#{hi)?eP^8oOx$oeLVP6j&6^wny`HR#|O)B z@jpEG);Z@SmX#+5n*soL#5zV{({s1>xOjEovBwF+!z#f9QS^6-Ae{b%UB$^R_F e{{jDf{5b$IIzY542xwmb0000u|De1*{vBVgfpus@M7+)}!MJ0l@Y-o$ErLWuV zw!1U4&peNZb(Y=2AK=07Ws>Lle&+jonIR$t0MHRVy3=Hh$A2_6vPC5=LZli1AVhIJ zkbbTh@~Hf$Cy$RRfyFhlqzK)iaDm#v=Av;NA&0g z0C(7qrb9*viawz!<|g~(^LqdBLuiRL0s#KFIY}2sMjOrzUNwnG1%USE!)Mf3-JYjv zwCK~i8rk~xsehM@*X%yz^B$Z_zu@-RG@f~`8wU?}fpo6^`r}_rLKFkx&rs*)R|aae z*j|Ix@3?&HLoCi`@v8MPgbX|as57xmKRx&+0N~~Qr{MAIoi%^XQB>0FRYh;;JN_hU zYfW7J?k0rf1(#>WJ-#|QmYVf%-k3m3tP!#8bx?@asDGrjvOxN|K)A~nhkvX6=G?ow zl!4EMx59;+Fn9Jg?d?q%IrsftA$dNK#q~h=f2vt`su0m)zX#G^=fZUh#ih1bw4=45zPa|j z(@FNii+?@1^z|qPKf1EOMPbq7tCMbV`Jzfh$huR-`Cr8k-}Soqt(EE}NIdAYYhYz0s0&C-(u^M?@e1 z1BpZewr%4l=XQ7Ip6CobBMK;NX+8%bdC^B^G0i3uD%b`G&Kd9A-T~SdI6NcbUKYAM~q|@p0efVURZww7#V&$D> zSyg3M?J-m$k$9k|jbZ@KN-4_+L^KeVIy5u{+qRKPr68p|3jjSmJ>^7f1jEFz+&JOk z;WGHI2TCOrDom^#N~NTfpp76AJ@`SC%x52V{=fbc`Ui|s1fIYrRlfiL002ovPDHLk FV1jw4488yW delta 1052 zcmV+%1mpYD2(So{BYyw^b5ch_0Itp)=>Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi z!vFvd!vV){sAK>D1I0;1K~zXf?UPSz+f*39Kfh-?j-9m0mS!taI>gd-nkrVcY^VZ> zGQ@!kmr)AWtwJhSgb)%4v@=&!w3QtYLh29)5R(u?ETH{iVt>gfjAhljb&c9INs~B@ zlQ@31CVP=7#R`-41|eDvY4AR&h55wIe{C>COd?q|up zIy(}2)z2}~kbj4jq{x$zfioYS@SmJIgs4Nx!&J#W|M5+6`PLc>jAd$kZLGO{1@ZR2 z2*SJvgV+1g8!?e8zc+Ka`g-gOnLc#={->vWES9{eSpsJHVY-Kd(vT)X-~>V}fA4x; zyY}rPHM^ddiK$d9Fpa;iZ=u)TJ}x9~Y*?j?tm>u^(ZnzLE|BklDLB#CLRf=vV-`NAR>3ODQS zJ9}<2kV@vPU%y!{YgT1jGvo*B_1kwGLrSr}OrpZrprYr`Bf#U9rH{OO;nWdXE)9P2 z&ZQ8?@_$PWqgYfM^7otCpQU#9C>$$Ha7aYKU>JXRahfAFY+pF{Gto@Ns*zH$M&ujG z%9krtx6=?IBt)p;vGHLv_Us|%%GW8=0L856nWl01ZlZMY$ChrFVViQX0;;M)HeG#8v6E^n7K5LDh;vJ~ z@8d^*=kWd~{M-khzsyd*^#a%L4`Bv41cu;Te5y-v3|CfGVDh<17#%wXhex8$0i~)` z9e+9c^ih~TecJhUaZ;U`nQ<1YNj)mq10Dgw?HXkzvuCPZ%@Npu81bGA_xXod4;GPT zsuqE5V{PkNv8rd3mYRd}BX`21;BS$G&-gvIBxq1=G*C&|+(@d;qZfBHcGxvXV#%t4 zGx;L_Fs-oJJZVm}b<{;ysxG1EabOSwwtuNP>%w}c0v_< zp@zM*Yo^|5gJ?}ny5_j2p&QXncX4r%ihsWV6n6}?=HAA*>>05od+Cb!KmQ>B{uKc_ W`ngL8TT^!c0000W From d1359b504dfe5f0c9d04680fe705e379b3c200a5 Mon Sep 17 00:00:00 2001 From: burgerguy Date: Fri, 22 May 2020 19:52:44 -0400 Subject: [PATCH 05/17] merge stuff from master-changes --- .../main/viewer/PerViewerFacadeInjector.java | 2 +- .../widget/CursorInformationWidget.java | 7 +- .../java/amidst/mojangapi/world/World.java | 2 +- .../icon/producer/EndGatewayProducer.java | 135 +++++++++++++++--- .../mojangapi/world/oracle/EndIsland.java | 22 +-- .../world/oracle/EndIslandOracle.java | 85 +++++++++-- .../DefaultVersionFeatures.java | 2 +- 7 files changed, 211 insertions(+), 44 deletions(-) diff --git a/src/main/java/amidst/gui/main/viewer/PerViewerFacadeInjector.java b/src/main/java/amidst/gui/main/viewer/PerViewerFacadeInjector.java index 325d2c9f5..c3aea41b5 100644 --- a/src/main/java/amidst/gui/main/viewer/PerViewerFacadeInjector.java +++ b/src/main/java/amidst/gui/main/viewer/PerViewerFacadeInjector.java @@ -62,7 +62,7 @@ private static List createWidgets( new SeedAndWorldTypeWidget( CornerAnchorPoint.TOP_LEFT, worldOptions.getWorldSeed(), worldOptions.getWorldType()), new SelectedIconWidget( CornerAnchorPoint.TOP_LEFT, worldIconSelection), debugWidget, - new CursorInformationWidget( CornerAnchorPoint.TOP_RIGHT, graph, translator, settings.dimension, world.getBiomeList()), + new CursorInformationWidget( CornerAnchorPoint.TOP_RIGHT, graph, translator, world.getEndIslandOracle(), settings.dimension, world.getBiomeList()), biomeToggleWidget, new BiomeExporterProgressWidget(CornerAnchorPoint.BOTTOM_RIGHT, progressEntrySupplier, -20, settings.showDebug, debugWidget, biomeToggleWidget.getWidth()), biomeWidget diff --git a/src/main/java/amidst/gui/main/viewer/widget/CursorInformationWidget.java b/src/main/java/amidst/gui/main/viewer/widget/CursorInformationWidget.java index a395e2553..375c74807 100644 --- a/src/main/java/amidst/gui/main/viewer/widget/CursorInformationWidget.java +++ b/src/main/java/amidst/gui/main/viewer/widget/CursorInformationWidget.java @@ -16,7 +16,7 @@ import amidst.mojangapi.world.biome.BiomeList; import amidst.mojangapi.world.coordinates.CoordinatesInWorld; import amidst.mojangapi.world.coordinates.Resolution; -import amidst.mojangapi.world.versionfeatures.DefaultBiomes; +import amidst.mojangapi.world.oracle.EndIslandOracle; import amidst.settings.Setting; @NotThreadSafe @@ -25,6 +25,7 @@ public class CursorInformationWidget extends TextWidget { private final FragmentGraph graph; private final FragmentGraphToScreenTranslator translator; + private final EndIslandOracle endIslandOracle; private final Setting dimensionSetting; private final BiomeList biomeList; @@ -33,11 +34,13 @@ public CursorInformationWidget( CornerAnchorPoint anchor, FragmentGraph graph, FragmentGraphToScreenTranslator translator, + EndIslandOracle endIslandOracle, Setting dimensionSetting, BiomeList biomeList) { super(anchor); this.graph = graph; this.translator = translator; + this.endIslandOracle = endIslandOracle; this.dimensionSetting = dimensionSetting; this.biomeList = biomeList; } @@ -61,7 +64,7 @@ private String getBiomeNameAt(CoordinatesInWorld coordinates) { if (dimension.equals(Dimension.OVERWORLD)) { return getOverworldBiomeNameAt(coordinates); } else if (dimension.equals(Dimension.END)) { - return biomeList.getByIdOrNull(DefaultBiomes.theEnd).getName(); + return biomeList.getByIdOrNull(endIslandOracle.getBiomeAtBlock(coordinates)).getName(); } else { AmidstLogger.warn("unsupported dimension"); return UNKNOWN_BIOME_NAME; diff --git a/src/main/java/amidst/mojangapi/world/World.java b/src/main/java/amidst/mojangapi/world/World.java index 2e2bbf0f2..ebba11263 100644 --- a/src/main/java/amidst/mojangapi/world/World.java +++ b/src/main/java/amidst/mojangapi/world/World.java @@ -58,7 +58,7 @@ public World( WorldIconProducer oceanFeaturesProducer, WorldIconProducer netherFortressProducer, WorldIconProducer> endCityProducer, - WorldIconProducer> endGatewayProducer) { + WorldIconProducer> endGatewayProducer) { this.worldOptions = worldOptions; this.movablePlayerList = movablePlayerList; this.recognisedVersion = recognisedVersion; diff --git a/src/main/java/amidst/mojangapi/world/icon/producer/EndGatewayProducer.java b/src/main/java/amidst/mojangapi/world/icon/producer/EndGatewayProducer.java index 6d3494a31..426c53993 100644 --- a/src/main/java/amidst/mojangapi/world/icon/producer/EndGatewayProducer.java +++ b/src/main/java/amidst/mojangapi/world/icon/producer/EndGatewayProducer.java @@ -11,6 +11,7 @@ import amidst.mojangapi.world.coordinates.Resolution; import amidst.mojangapi.world.icon.WorldIcon; import amidst.mojangapi.world.oracle.EndIsland; +import amidst.mojangapi.world.oracle.EndIslandOracle; import kaptainwutax.seedutils.mc.ChunkRand; import kaptainwutax.seedutils.mc.MCVersion; @@ -35,16 +36,16 @@ public class EndGatewayProducer extends WorldIconProducer> { * The influence has to be atleast 40 for End Highlands biomes to spawn, * which is where End Gateways are. */ - private static final int REQUIRED_BIOME_INFLUENCE = 40; + private static final float REQUIRED_BIOME_INFLUENCE = 40.0F; /** * Used as a cache only for the spawn gateways. */ - private static final EndSpawnGatewayProducer spawnProducer = new EndSpawnGatewayProducer(); - + private final EndSpawnGatewayProducer spawnProducer; private final long seed; - public EndGatewayProducer(long seed) { + public EndGatewayProducer(long seed, EndIslandOracle oracle) { + this.spawnProducer = new EndSpawnGatewayProducer(oracle); this.seed = seed; } @@ -58,15 +59,14 @@ public void produce(CoordinatesInWorld corner, Consumer consumer, Lis spawnProducer.produce(corner, consumer, null); } - // TODO: use longs? private void generateAt( CoordinatesInWorld corner, Consumer consumer, List endIslands, - int xRelativeToFragment, - int yRelativeToFragment) { - int x = xRelativeToFragment + (int) corner.getXAs(RESOLUTION); - int y = yRelativeToFragment + (int) corner.getYAs(RESOLUTION); + long xRelativeToFragment, + long yRelativeToFragment) { + long x = xRelativeToFragment + corner.getXAs(RESOLUTION); + long y = yRelativeToFragment + corner.getYAs(RESOLUTION); CoordinatesInWorld possibleCoordinates = tryGetValidLocationFromChunk(x, y, endIslands, corner); if (possibleCoordinates != null) { consumer.accept( @@ -83,20 +83,20 @@ private void generateAt( * Made with help from * KaptainWutax. */ - public CoordinatesInWorld tryGetValidLocationFromChunk(int chunkX, int chunkY, List endIslands, CoordinatesInWorld corner) { + public CoordinatesInWorld tryGetValidLocationFromChunk(long chunkX, long chunkY, List endIslands, CoordinatesInWorld corner) { if((chunkX * chunkX + chunkY * chunkY) > 4096) { ChunkRand rand = new ChunkRand(); - int blockX = chunkX << 4; - int blockY = chunkY << 4; + long blockX = chunkX << 4; + long blockY = chunkY << 4; - rand.setDecoratorSeed(seed, blockX, blockY, 0, 3, MCVersion.v1_13); + rand.setDecoratorSeed(seed, (int) blockX, (int) blockY, 0, 3, MCVersion.v1_13); if(rand.nextInt(END_GATEWAY_CHANCE) == 0) { for(EndIsland island : endIslands) { float biomeInfluence = island.influenceAtChunk(chunkX, chunkY); if(biomeInfluence >= REQUIRED_BIOME_INFLUENCE) { - int gatewayX = rand.nextInt(16) + blockX; - int gatewayY = rand.nextInt(16) + blockY; + long gatewayX = rand.nextInt(16) + blockX; + long gatewayY = rand.nextInt(16) + blockY; CoordinatesInWorld coordinates = new CoordinatesInWorld(gatewayX, gatewayY); if(coordinates.isInBoundsOf(corner, Fragment.SIZE)) { // While this barely ever is false due to the biome influence check, we do it anyway just to make sure @@ -115,12 +115,19 @@ public CoordinatesInWorld tryGetValidLocationFromChunk(int chunkX, int chunkY, L } private static class EndSpawnGatewayProducer extends CachedWorldIconProducer { - private static int NUMBER_OF_SPAWN_GATEWAYS = 20; + private static final int NUMBER_OF_SPAWN_GATEWAYS = 20; + + private final EndIslandOracle oracle; + + public EndSpawnGatewayProducer(EndIslandOracle oracle) { + this.oracle = oracle; + } @Override protected List doCreateCache() { List iconList = new ArrayList(); for(int i = 0; i < NUMBER_OF_SPAWN_GATEWAYS; i++) { + // Generate inner WorldIcon int x = floor(96.0D * Math.cos(2.0D * (-Math.PI + 0.15707963267948966D * (double) i))); int y = floor(96.0D * Math.sin(2.0D * (-Math.PI + 0.15707963267948966D * (double) i))); CoordinatesInWorld possibleCoordinates = new CoordinatesInWorld(x, y); @@ -132,10 +139,106 @@ protected List doCreateCache() { false ) ); + + // Generate outer WorldIcon + // Normalize the coordinates the same way MC would + double doubleX = 0; + double doubleY = 0; + double d0 = (double) ((float) (Math.sqrt((double) x * (double) x + (double) y * (double) y))); // There are this many casts in the MC code + if (d0 >= 1.0E-4D) { + doubleX = (double) x / d0; + doubleY = (double) y / d0; + } + // Scale the coordinates + CoordinatesInWorld coordinates = new CoordinatesInWorld((long) (doubleX * 1024.0D), (long) (doubleY * 1024.0D)); + + // Check for closest land along vector + for (int j = 16; !isChunkIslandlessSlow(coordinates) && j-- > 0; coordinates = coordinates.add((long) (doubleX * -16.0D), (long) (doubleY * -16.0D))) { + } + + for (int k = 16; isChunkIslandlessSlow(coordinates) && k-- > 0; coordinates = coordinates.add((long) (doubleX * 16.0D), (long) (doubleY * 16.0D))) { + } + + // Mess with the coords a bit more + coordinates = findSpawnpoint(coordinates, true); + coordinates = findHighestBlock(coordinates, 16); + + iconList.add(new WorldIcon( + coordinates, + POSSIBLE_END_GATEWAY.getLabel(), + POSSIBLE_END_GATEWAY.getImage(), + Dimension.END, + false + ) + ); } return iconList; } + /* + * This number is supposed to sort of guess whether end + * stone might be at a particular location. + */ + private static final float ISLAND_INFLUENCE_THRESHOLD = -20.0F; + + @SuppressWarnings("unused") + private boolean isChunkIslandlessFast(CoordinatesInWorld blockCoords) { + for(EndIsland island : oracle.getAt(blockCoords)) { + if(island.influenceAtChunk(blockCoords.getX() >> 4, blockCoords.getY() >> 4) >= ISLAND_INFLUENCE_THRESHOLD) { + return false; + } + } + return true; + } + + private boolean isChunkIslandlessSlow(CoordinatesInWorld blockCoords) { + for(EndIsland island : oracle.getAt(blockCoords)) { + for (long x = blockCoords.getX() & -16; x < (blockCoords.getX() | 15); x++) { + for (long y = blockCoords.getY() & -16; y < (blockCoords.getY() | 15); y++) { + if(island.influenceAtBlock(x, y) >= ISLAND_INFLUENCE_THRESHOLD) { + return false; + } + } + } + } + return true; + } + + private CoordinatesInWorld findSpawnpoint(CoordinatesInWorld blockCoords, boolean guaranteeEndStone) { + for (long y = blockCoords.getY() & -16; y < (blockCoords.getY() | 15); y++) { + for (long x = blockCoords.getX() & -16; x < (blockCoords.getX() | 15); x++) { + for (EndIsland island : oracle.getAt(blockCoords)) { + if (island.influenceAtBlock(x, y) >= (guaranteeEndStone ? 0.0F : ISLAND_INFLUENCE_THRESHOLD)) { + return new CoordinatesInWorld(x, y); + } + } + } + } + return blockCoords; + } + + private CoordinatesInWorld findHighestBlock(CoordinatesInWorld blockCoords, int radius) { + return findHighestBlock(blockCoords, -radius, -radius, radius, radius); + } + + private CoordinatesInWorld findHighestBlock(CoordinatesInWorld blockCoords, int startX, int startY, int endX, int endY) { + float highestInfluence = -100.0F; + long highestX = blockCoords.getX(); + long highestY = blockCoords.getY(); + + for (long x = blockCoords.getX() + startX; x <= blockCoords.getX() + endX; ++x) { + for (long y = blockCoords.getY() + startY; y <= blockCoords.getY() + endY; ++y) { + float coordInfluence = oracle.getInfluenceAtBlock(x, y); + if(coordInfluence > highestInfluence) { + highestInfluence = coordInfluence; + highestX = x; + highestY = y; + } + } + } + return new CoordinatesInWorld(highestX, highestY); + } + private static int floor(double value) { int i = (int) value; return value < (double) i ? i - 1 : i; diff --git a/src/main/java/amidst/mojangapi/world/oracle/EndIsland.java b/src/main/java/amidst/mojangapi/world/oracle/EndIsland.java index 7c11cd14b..3849dd39d 100644 --- a/src/main/java/amidst/mojangapi/world/oracle/EndIsland.java +++ b/src/main/java/amidst/mojangapi/world/oracle/EndIsland.java @@ -5,14 +5,14 @@ // TODO: check sign of differences ... (this.chunkX - chunkX) vs (chunkX - this.chunkX) @Immutable public class EndIsland { - private static final int X_ADJUSTMENT = 1; - private static final int Y_ADJUSTMENT = 1; + private static final short X_ADJUSTMENT = 1; + private static final short Y_ADJUSTMENT = 1; - private final int chunkX; - private final int chunkY; + private final long chunkX; + private final long chunkY; private final float erosionFactor; - protected EndIsland(int chunkX, int chunkY, float erosionFactor) { + protected EndIsland(long chunkX, long chunkY, float erosionFactor) { this.chunkX = chunkX; this.chunkY = chunkY; this.erosionFactor = erosionFactor; @@ -25,7 +25,7 @@ protected EndIsland(int chunkX, int chunkY, float erosionFactor) { * rocky-island-shore, which might be solid ground (but that becomes less * likely the lower the value). */ - public float influenceAtBlock(int x, int y) { + public float influenceAtBlock(long x, long y) { // Add 8 blocks to both axis because all the Minecraft calculations are // done using chunk coordinates and are converted as being the center of // the chunk whenever translated to block coordinates, whereas Amidst @@ -43,9 +43,9 @@ public float influenceAtBlock(int x, int y) { * A version of influenceAt() that more exactly adheres to Minecraft's * algorithm, for use in testing for End Cities. */ - public float influenceAtChunk(int chunkX, int chunkY) { - int adjustedX = (chunkX - this.chunkX) * 2 + X_ADJUSTMENT; - int adjustedY = (chunkY - this.chunkY) * 2 + Y_ADJUSTMENT; + public float influenceAtChunk(long chunkX, long chunkY) { + long adjustedX = (chunkX - this.chunkX) * 2 + X_ADJUSTMENT; + long adjustedY = (chunkY - this.chunkY) * 2 + Y_ADJUSTMENT; return getResult(adjustedX * adjustedX + adjustedY * adjustedY); } @@ -64,8 +64,8 @@ private float getResult(double squared) { public int hashCode() { final int prime = 31; int result = 1; - result = prime * result + chunkX; - result = prime * result + chunkY; + result = (int) (prime * result + chunkX); + result = (int) (prime * result + chunkY); result = prime * result + Float.floatToIntBits(erosionFactor); return result; } diff --git a/src/main/java/amidst/mojangapi/world/oracle/EndIslandOracle.java b/src/main/java/amidst/mojangapi/world/oracle/EndIslandOracle.java index 951979807..14e9d3cc6 100644 --- a/src/main/java/amidst/mojangapi/world/oracle/EndIslandOracle.java +++ b/src/main/java/amidst/mojangapi/world/oracle/EndIslandOracle.java @@ -1,11 +1,13 @@ package amidst.mojangapi.world.oracle; -import java.util.LinkedList; +import java.util.ArrayList; import java.util.List; import amidst.documentation.ThreadSafe; import amidst.mojangapi.world.coordinates.CoordinatesInWorld; import amidst.mojangapi.world.coordinates.Resolution; +import amidst.mojangapi.world.versionfeatures.DefaultBiomes; + import kaptainwutax.seedutils.lcg.rand.JRand; @ThreadSafe @@ -47,11 +49,70 @@ public EndIslandOracle(SimplexNoise noiseFunction) { this.noiseFunction = noiseFunction; } + public int getBiomeAtBlock(long x, long y) { + if (x * x + y * y <= 4096L) { + return DefaultBiomes.theEnd; + } else { + float influence = getInfluenceAtBlock(x, y); + if (influence > 40.0F) { + return DefaultBiomes.theEndHigh; + } else if (influence >= 0.0F) { + return DefaultBiomes.theEndMedium; + } else { + return influence < -20.0F ? DefaultBiomes.theEndLow : DefaultBiomes.theEndBarren; + } + } + } + + public int getBiomeAtBlock(CoordinatesInWorld coords) { + long x = coords.getX(); + long y = coords.getY(); + + if (x * x + y * y <= 4096L) { + return DefaultBiomes.theEnd; + } else { + float influence = getInfluenceAtBlock(coords); + if (influence > 40.0F) { + return DefaultBiomes.theEndHigh; + } else if (influence >= 0.0F) { + return DefaultBiomes.theEndMedium; + } else { + return influence < -20.0F ? DefaultBiomes.theEndLow : DefaultBiomes.theEndBarren; + } + } + } + + public float getInfluenceAtBlock(long x, long y) { + float highestInfluence = -100.0f; + + for(EndIsland island : getAt(new CoordinatesInWorld(x, y))) { + float tempInfluence = island.influenceAtBlock(x, y); + if(tempInfluence > highestInfluence) { + highestInfluence = tempInfluence; + } + } + return highestInfluence; + } + + public float getInfluenceAtBlock(CoordinatesInWorld coords) { + float highestInfluence = -100.0f; + long x = coords.getX(); + long y = coords.getY(); + + for (EndIsland island : getAt(coords)) { + float tempInfluence = island.influenceAtBlock(x, y); + if (tempInfluence > highestInfluence) { + highestInfluence = tempInfluence; + } + } + return highestInfluence; + } + public List getAt(CoordinatesInWorld corner) { int steps = Resolution.CHUNK.getStepsPerFragment(); return findSurroundingIslands( - (int) corner.getXAs(Resolution.CHUNK), - (int) corner.getYAs(Resolution.CHUNK), + corner.getXAs(Resolution.CHUNK), + corner.getYAs(Resolution.CHUNK), steps, steps); } @@ -60,11 +121,11 @@ public List getAt(CoordinatesInWorld corner) { * Returns a list of all islands that might be touching a chunk-area. */ private List findSurroundingIslands( - int chunkX, - int chunkY, + long chunkX, + long chunkY, int chunksPerFragmentX, int chunksPerFragmentY) { - List result = new LinkedList<>(); + List result = new ArrayList<>(); for (int y = -SURROUNDING_CHUNKS; y <= chunksPerFragmentY + SURROUNDING_CHUNKS; y++) { for (int x = -SURROUNDING_CHUNKS; x <= chunksPerFragmentX + SURROUNDING_CHUNKS; x++) { EndIsland island = tryCreateEndIsland(chunkX + x, chunkY + y); @@ -80,7 +141,7 @@ private List findSurroundingIslands( * Returns an EndIsland if one has 'grown out' from the chunk, otherwise * null */ - private EndIsland tryCreateEndIsland(int chunkX, int chunkY) { + private EndIsland tryCreateEndIsland(long chunkX, long chunkY) { if (chunkX == 0 && chunkY == 0) { return createMainEndIsland(chunkX, chunkY); @@ -95,14 +156,14 @@ private EndIsland tryCreateEndIsland(int chunkX, int chunkY) { * The main island grows from the origin, with a hard-coded erosion factor * of 8 */ - private EndIsland createMainEndIsland(int chunkX, int chunkY) { + private EndIsland createMainEndIsland(long chunkX, long chunkY) { return new EndIsland(chunkX, chunkY, 8.0f); } /** * The chunk is in the outer-islands band */ - private EndIsland tryCreateEndIslandInOuterLands(int chunkX, int chunkY) { + private EndIsland tryCreateEndIslandInOuterLands(long chunkX, long chunkY) { if (noiseFunction.noise(chunkX, chunkY) < ISLAND_DENSITY_THRESHOLD) { return new EndIsland(chunkX, chunkY, getErosionFactor(chunkX, chunkY)); } else { @@ -115,15 +176,15 @@ private EndIsland tryCreateEndIslandInOuterLands(int chunkX, int chunkY) { * erosion factor between 9 and 21 (i.e. they will be smaller than the main * island). */ - private int getErosionFactor(int chunkX, int chunkY) { + private int getErosionFactor(long chunkX, long chunkY) { // Convert coordinates to long to guard against overflow - return (int) ((Math.abs((long) chunkX) * 3439 + Math.abs((long) chunkY) * 147) % 13 + 9); + return (int) ((Math.abs(chunkX) * 3439 + Math.abs(chunkY) * 147) % 13 + 9); } /** * Is the point (x, y) inside the disk of radius d centered at the origin? */ - private boolean isInRange(int x, int y, int d) { + private boolean isInRange(long x, long y, int d) { // Guard against overflow if (x < -d || x > d || y < -d || y > d) return false; diff --git a/src/main/java/amidst/mojangapi/world/versionfeatures/DefaultVersionFeatures.java b/src/main/java/amidst/mojangapi/world/versionfeatures/DefaultVersionFeatures.java index 736696b63..1cbc19954 100644 --- a/src/main/java/amidst/mojangapi/world/versionfeatures/DefaultVersionFeatures.java +++ b/src/main/java/amidst/mojangapi/world/versionfeatures/DefaultVersionFeatures.java @@ -154,7 +154,7 @@ public static VersionFeatures.Builder builder(WorldOptions worldOptions, Minecra )) .with(FeatureKey.END_GATEWAY_PRODUCER, VersionFeature.bind(features -> - VersionFeature.constant(new EndGatewayProducer(getWorldSeed(features))) + VersionFeature.constant(new EndGatewayProducer(getWorldSeed(features), features.get(FeatureKey.END_ISLAND_ORACLE))) )) .with(FeatureKey.MINESHAFT_LOCATION_CHECKER, VersionFeature.bind(features -> From b4d738333a026afea9dc452733ef0e8c81d717b4 Mon Sep 17 00:00:00 2001 From: burgerguy Date: Tue, 30 Jun 2020 16:20:20 -0400 Subject: [PATCH 06/17] wip small islands in end --- src/main/java/amidst/fragment/Fragment.java | 18 +- .../colorprovider/TheEndColorProvider.java | 51 ++- .../constructor/EndIslandsConstructor.java | 3 +- .../amidst/fragment/layer/LayerBuilder.java | 4 +- .../fragment/loader/EndIslandsLoader.java | 8 +- .../main/viewer/PerViewerFacadeInjector.java | 2 +- .../widget/CursorInformationWidget.java | 15 +- .../java/amidst/mojangapi/world/World.java | 17 +- .../icon/producer/EndGatewayProducer.java | 310 +++++++++--------- .../type/EndCityWorldIconTypeProvider.java | 12 +- .../world/oracle/EndIslandOracle.java | 193 ----------- .../world/oracle/end/EndIslandList.java | 25 ++ .../world/oracle/end/EndIslandOracle.java | 307 +++++++++++++++++ .../LargeEndIsland.java} | 192 +++++------ .../world/oracle/end/SmallEndIsland.java | 31 ++ .../DefaultVersionFeatures.java | 12 +- .../world/versionfeatures/FeatureKey.java | 6 +- .../DefaultTestWorldDirectoryDeclaration.java | 2 +- .../storage/json/EndIslandsJson.java | 11 +- 19 files changed, 728 insertions(+), 491 deletions(-) delete mode 100644 src/main/java/amidst/mojangapi/world/oracle/EndIslandOracle.java create mode 100644 src/main/java/amidst/mojangapi/world/oracle/end/EndIslandList.java create mode 100644 src/main/java/amidst/mojangapi/world/oracle/end/EndIslandOracle.java rename src/main/java/amidst/mojangapi/world/oracle/{EndIsland.java => end/LargeEndIsland.java} (90%) create mode 100644 src/main/java/amidst/mojangapi/world/oracle/end/SmallEndIsland.java diff --git a/src/main/java/amidst/fragment/Fragment.java b/src/main/java/amidst/fragment/Fragment.java index 6b42e1282..129e8fde2 100644 --- a/src/main/java/amidst/fragment/Fragment.java +++ b/src/main/java/amidst/fragment/Fragment.java @@ -14,7 +14,9 @@ import amidst.mojangapi.world.coordinates.Resolution; import amidst.mojangapi.world.icon.WorldIcon; import amidst.mojangapi.world.oracle.BiomeDataOracle; -import amidst.mojangapi.world.oracle.EndIsland; +import amidst.mojangapi.world.oracle.end.EndIslandList; +import amidst.mojangapi.world.oracle.end.LargeEndIsland; +import amidst.mojangapi.world.oracle.end.SmallEndIsland; /** * This class contains nearly no logic but only simple and atomic getters and @@ -84,7 +86,7 @@ public class Fragment { private volatile float alpha; private volatile short[][] biomeData; - private volatile List endIslands; + private volatile EndIslandList endIslands; private final AtomicReferenceArray images; private final AtomicReferenceArray> worldIcons; @@ -114,11 +116,19 @@ public short getBiomeDataAt(int x, int y) { return biomeData[x][y]; } - public void setEndIslands(List endIslands) { + public void setEndIslands(EndIslandList endIslands) { this.endIslands = endIslands; } - public List getEndIslands() { + public List getLargeEndIslands() { + return endIslands.getLargeIslands(); + } + + public List getSmallEndIslands() { + return endIslands.getSmallIslands(); + } + + public EndIslandList getEndIslands() { return endIslands; } diff --git a/src/main/java/amidst/fragment/colorprovider/TheEndColorProvider.java b/src/main/java/amidst/fragment/colorprovider/TheEndColorProvider.java index 31e58be56..4ad112d1e 100644 --- a/src/main/java/amidst/fragment/colorprovider/TheEndColorProvider.java +++ b/src/main/java/amidst/fragment/colorprovider/TheEndColorProvider.java @@ -1,13 +1,20 @@ package amidst.fragment.colorprovider; import java.awt.image.BufferedImage; -import java.util.List; import amidst.ResourceLoader; import amidst.documentation.ThreadSafe; import amidst.fragment.Fragment; +import amidst.mojangapi.minecraftinterface.RecognisedVersion; import amidst.mojangapi.world.Dimension; -import amidst.mojangapi.world.oracle.EndIsland; +import amidst.mojangapi.world.coordinates.CoordinateUtils; +import amidst.mojangapi.world.coordinates.CoordinatesInWorld; +import amidst.mojangapi.world.coordinates.Resolution; +import amidst.mojangapi.world.oracle.end.EndIslandList; +import amidst.mojangapi.world.oracle.end.EndIslandOracle; +import amidst.mojangapi.world.oracle.end.LargeEndIsland; +import amidst.mojangapi.world.oracle.end.SmallEndIsland; +import amidst.mojangapi.world.versionfeatures.DefaultBiomes; @ThreadSafe public class TheEndColorProvider implements ColorProvider { @@ -25,6 +32,12 @@ public class TheEndColorProvider implements ColorProvider { private static final float INFLUENCE_FADE_START = 0; private static final float INFLUENCE_FADE_FINISH = -8; private static final float INFLUENCE_FADE_RANGE = INFLUENCE_FADE_START - INFLUENCE_FADE_FINISH; + + private final RecognisedVersion version; + + public TheEndColorProvider(RecognisedVersion version) { + this.version = version; + } @Override public int getColorAt(Dimension dimension, Fragment fragment, long cornerX, long cornerY, int x, int y) { @@ -47,7 +60,19 @@ private int getColorAt( long chunkY, int textureX, int textureY, - List endIslands) { + EndIslandList endIslands) { + + // small islands + if (RecognisedVersion.isNewerOrEqualTo(version, RecognisedVersion._1_13)) { + if(EndIslandOracle.getBiomeAtBlock(x, y, endIslands.getLargeIslands()) == DefaultBiomes.theEndLow) { + for(SmallEndIsland smallIsland : endIslands.getSmallIslands()) { + if(new CoordinatesInWorld(x, y).getDistance(smallIsland.getX(), smallIsland.getY()) <= smallIsland.getSize()) { + return getEndStoneTextureAt(textureX, textureY); + } + } + } + } + // Determine whether this float maxInfluence = getMaxInfluence(x, y, endIslands); if (maxInfluence >= INFLUENCE_FADE_START) { @@ -58,9 +83,9 @@ private int getColorAt( } } - private float getMaxInfluence(int x, int y, List endIslands) { + private float getMaxInfluence(int x, int y, EndIslandList endIslands) { float result = -100.0f; - for (EndIsland island : endIslands) { + for (LargeEndIsland island : endIslands.getLargeIslands()) { float influence = island.influenceAtBlock(x, y); if (result < influence) { result = influence; @@ -71,9 +96,11 @@ private float getMaxInfluence(int x, int y, List endIslands) { private int getFadingColorAt(long chunkX, long chunkY, int textureX, int textureY, float maxInfluence) { int result = VOID_TRANSPARENT_BLACK; + if (showRockyShores(chunkX, chunkY)) { result = getRockyShoresTextureAt(textureX, textureY); } + if (maxInfluence > INFLUENCE_FADE_FINISH) { // Fade out the endstone - this is the edge of an island int pixelAlpha = result >>> 24; @@ -88,15 +115,15 @@ private int getFadingColorAt(long chunkX, long chunkY, int textureX, int texture return result; } + private int getFadingIslandAlpha(float maxInfluence) { + return 255 - (int) (255 * (INFLUENCE_FADE_START - maxInfluence) / INFLUENCE_FADE_RANGE); + } + /** - * Determine if the chunk may contain miniature islands. + * Determine whether to show the rocky shores texture. */ private boolean showRockyShores(long chunkX, long chunkY) { - return (chunkX * chunkX + chunkY * chunkY) > 4096; - } - - private int getFadingIslandAlpha(float maxInfluence) { - return 255 - (int) (255 * (INFLUENCE_FADE_START - maxInfluence) / INFLUENCE_FADE_RANGE); + return (chunkX * chunkX + chunkY * chunkY) > 4096 && RecognisedVersion.isOlder(version, RecognisedVersion._1_13); } private int getEndStoneTextureAt(int textureX, int textureY) { @@ -108,6 +135,8 @@ private int getEndStoneTextureAt(int textureX, int textureY) { * from the world seed, like chorus plants they are decorations whose PRNG * state depends on the order chunks are created/explored in. This makes me * sad :( Let's use a symbolic texture, since we can't plot them properly. + * + * EDIT: This isn't true past 1.13 I think. */ private int getRockyShoresTextureAt(int textureX, int textureY) { return TEXTURES.getRGB(textureX, textureY + TEXTURES_HEIGHT); diff --git a/src/main/java/amidst/fragment/constructor/EndIslandsConstructor.java b/src/main/java/amidst/fragment/constructor/EndIslandsConstructor.java index c5f929a1b..e4cfb28a7 100644 --- a/src/main/java/amidst/fragment/constructor/EndIslandsConstructor.java +++ b/src/main/java/amidst/fragment/constructor/EndIslandsConstructor.java @@ -6,12 +6,13 @@ import amidst.documentation.CalledOnlyBy; import amidst.documentation.Immutable; import amidst.fragment.Fragment; +import amidst.mojangapi.world.oracle.end.EndIslandList; @Immutable public class EndIslandsConstructor implements FragmentConstructor { @CalledOnlyBy(AmidstThread.EDT) @Override public void construct(Fragment fragment) { - fragment.setEndIslands(Collections.emptyList()); + fragment.setEndIslands(new EndIslandList(Collections.emptyList(), Collections.emptyList())); } } diff --git a/src/main/java/amidst/fragment/layer/LayerBuilder.java b/src/main/java/amidst/fragment/layer/LayerBuilder.java index 8a6551347..c96dcb467 100644 --- a/src/main/java/amidst/fragment/layer/LayerBuilder.java +++ b/src/main/java/amidst/fragment/layer/LayerBuilder.java @@ -133,7 +133,7 @@ private Iterable createLoaders( new AlphaInitializer( declarations.get(LayerIds.ALPHA), settings.fragmentFading), new BiomeDataLoader( declarations.get(LayerIds.BIOME_DATA), world.getBiomeDataOracle()), new EndIslandsLoader( declarations.get(LayerIds.END_ISLANDS), world.getEndIslandOracle()), - new ImageLoader( declarations.get(LayerIds.BACKGROUND), Resolution.QUARTER, new BackgroundColorProvider(new BiomeColorProvider(biomeSelection, settings.biomeProfileSelection), new TheEndColorProvider())), + new ImageLoader( declarations.get(LayerIds.BACKGROUND), Resolution.QUARTER, new BackgroundColorProvider(new BiomeColorProvider(biomeSelection, settings.biomeProfileSelection), new TheEndColorProvider(world.getRecognisedVersion()))), new ImageLoader( declarations.get(LayerIds.SLIME), Resolution.CHUNK, new SlimeColorProvider(world.getSlimeChunkOracle())), new WorldIconLoader<>(declarations.get(LayerIds.SPAWN), world.getSpawnProducer()), new WorldIconLoader<>(declarations.get(LayerIds.STRONGHOLD), world.getStrongholdProducer()), @@ -145,7 +145,7 @@ private Iterable createLoaders( new WorldIconLoader<>(declarations.get(LayerIds.WOODLAND_MANSION),world.getWoodlandMansionProducer()), new WorldIconLoader<>(declarations.get(LayerIds.OCEAN_FEATURES), world.getOceanFeaturesProducer()), new WorldIconLoader<>(declarations.get(LayerIds.NETHER_FORTRESS), world.getNetherFortressProducer()), - new WorldIconLoader<>(declarations.get(LayerIds.END_CITY), world.getEndCityProducer(), Fragment::getEndIslands), + new WorldIconLoader<>(declarations.get(LayerIds.END_CITY), world.getEndCityProducer(), Fragment::getLargeEndIslands), new WorldIconLoader<>(declarations.get(LayerIds.END_GATEWAY), world.getEndGatewayProducer(), Fragment::getEndIslands) )); // @formatter:on diff --git a/src/main/java/amidst/fragment/loader/EndIslandsLoader.java b/src/main/java/amidst/fragment/loader/EndIslandsLoader.java index 90a2c336a..d1276044c 100644 --- a/src/main/java/amidst/fragment/loader/EndIslandsLoader.java +++ b/src/main/java/amidst/fragment/loader/EndIslandsLoader.java @@ -1,7 +1,5 @@ package amidst.fragment.loader; -import java.util.List; - import amidst.documentation.AmidstThread; import amidst.documentation.CalledByAny; import amidst.documentation.CalledOnlyBy; @@ -10,8 +8,8 @@ import amidst.fragment.layer.LayerDeclaration; import amidst.mojangapi.world.Dimension; import amidst.mojangapi.world.coordinates.CoordinatesInWorld; -import amidst.mojangapi.world.oracle.EndIsland; -import amidst.mojangapi.world.oracle.EndIslandOracle; +import amidst.mojangapi.world.oracle.end.EndIslandList; +import amidst.mojangapi.world.oracle.end.EndIslandOracle; //TODO: use longs? @NotThreadSafe @@ -42,7 +40,7 @@ private void doLoad(Fragment fragment) { } @CalledOnlyBy(AmidstThread.FRAGMENT_LOADER) - private List getEndIslands(CoordinatesInWorld corner) { + private EndIslandList getEndIslands(CoordinatesInWorld corner) { return endIslandOracle.getAt(corner); } } diff --git a/src/main/java/amidst/gui/main/viewer/PerViewerFacadeInjector.java b/src/main/java/amidst/gui/main/viewer/PerViewerFacadeInjector.java index c3aea41b5..6ad52fdf1 100644 --- a/src/main/java/amidst/gui/main/viewer/PerViewerFacadeInjector.java +++ b/src/main/java/amidst/gui/main/viewer/PerViewerFacadeInjector.java @@ -62,7 +62,7 @@ private static List createWidgets( new SeedAndWorldTypeWidget( CornerAnchorPoint.TOP_LEFT, worldOptions.getWorldSeed(), worldOptions.getWorldType()), new SelectedIconWidget( CornerAnchorPoint.TOP_LEFT, worldIconSelection), debugWidget, - new CursorInformationWidget( CornerAnchorPoint.TOP_RIGHT, graph, translator, world.getEndIslandOracle(), settings.dimension, world.getBiomeList()), + new CursorInformationWidget( CornerAnchorPoint.TOP_RIGHT, graph, translator, settings.dimension, world.getBiomeList()), biomeToggleWidget, new BiomeExporterProgressWidget(CornerAnchorPoint.BOTTOM_RIGHT, progressEntrySupplier, -20, settings.showDebug, debugWidget, biomeToggleWidget.getWidth()), biomeWidget diff --git a/src/main/java/amidst/gui/main/viewer/widget/CursorInformationWidget.java b/src/main/java/amidst/gui/main/viewer/widget/CursorInformationWidget.java index 375c74807..23aafe4ed 100644 --- a/src/main/java/amidst/gui/main/viewer/widget/CursorInformationWidget.java +++ b/src/main/java/amidst/gui/main/viewer/widget/CursorInformationWidget.java @@ -16,7 +16,7 @@ import amidst.mojangapi.world.biome.BiomeList; import amidst.mojangapi.world.coordinates.CoordinatesInWorld; import amidst.mojangapi.world.coordinates.Resolution; -import amidst.mojangapi.world.oracle.EndIslandOracle; +import amidst.mojangapi.world.oracle.end.EndIslandOracle; import amidst.settings.Setting; @NotThreadSafe @@ -25,7 +25,6 @@ public class CursorInformationWidget extends TextWidget { private final FragmentGraph graph; private final FragmentGraphToScreenTranslator translator; - private final EndIslandOracle endIslandOracle; private final Setting dimensionSetting; private final BiomeList biomeList; @@ -34,13 +33,11 @@ public CursorInformationWidget( CornerAnchorPoint anchor, FragmentGraph graph, FragmentGraphToScreenTranslator translator, - EndIslandOracle endIslandOracle, Setting dimensionSetting, BiomeList biomeList) { super(anchor); this.graph = graph; this.translator = translator; - this.endIslandOracle = endIslandOracle; this.dimensionSetting = dimensionSetting; this.biomeList = biomeList; } @@ -64,12 +61,20 @@ private String getBiomeNameAt(CoordinatesInWorld coordinates) { if (dimension.equals(Dimension.OVERWORLD)) { return getOverworldBiomeNameAt(coordinates); } else if (dimension.equals(Dimension.END)) { - return biomeList.getByIdOrNull(endIslandOracle.getBiomeAtBlock(coordinates)).getName(); + return getEndBiomeNameAt(coordinates); } else { AmidstLogger.warn("unsupported dimension"); return UNKNOWN_BIOME_NAME; } } + + public String getEndBiomeNameAt(CoordinatesInWorld coordinates) { + Fragment fragment = graph.getFragmentAt(coordinates); + if (fragment != null && fragment.isLoaded()) { + return biomeList.getByIdOrNull(EndIslandOracle.getBiomeAtBlock(coordinates.getX(), coordinates.getY(), graph.getFragmentAt(coordinates).getLargeEndIslands())).getName(); + } + return UNKNOWN_BIOME_NAME; + } @CalledOnlyBy(AmidstThread.EDT) private String getOverworldBiomeNameAt(CoordinatesInWorld coordinates) { diff --git a/src/main/java/amidst/mojangapi/world/World.java b/src/main/java/amidst/mojangapi/world/World.java index ebba11263..4b88c5ff1 100644 --- a/src/main/java/amidst/mojangapi/world/World.java +++ b/src/main/java/amidst/mojangapi/world/World.java @@ -9,9 +9,10 @@ import amidst.mojangapi.world.icon.producer.CachedWorldIconProducer; import amidst.mojangapi.world.icon.producer.WorldIconProducer; import amidst.mojangapi.world.oracle.BiomeDataOracle; -import amidst.mojangapi.world.oracle.EndIsland; -import amidst.mojangapi.world.oracle.EndIslandOracle; import amidst.mojangapi.world.oracle.SlimeChunkOracle; +import amidst.mojangapi.world.oracle.end.EndIslandList; +import amidst.mojangapi.world.oracle.end.EndIslandOracle; +import amidst.mojangapi.world.oracle.end.LargeEndIsland; import amidst.mojangapi.world.player.MovablePlayerList; @ThreadSafe @@ -35,8 +36,8 @@ public class World { private final WorldIconProducer woodlandMansionProducer; private final WorldIconProducer oceanFeaturesProducer; private final WorldIconProducer netherFortressProducer; - private final WorldIconProducer> endCityProducer; - private final WorldIconProducer> endGatewayProducer; + private final WorldIconProducer> endCityProducer; + private final WorldIconProducer endGatewayProducer; public World( WorldOptions worldOptions, @@ -57,8 +58,8 @@ public World( WorldIconProducer woodlandMansionProducer, WorldIconProducer oceanFeaturesProducer, WorldIconProducer netherFortressProducer, - WorldIconProducer> endCityProducer, - WorldIconProducer> endGatewayProducer) { + WorldIconProducer> endCityProducer, + WorldIconProducer endGatewayProducer) { this.worldOptions = worldOptions; this.movablePlayerList = movablePlayerList; this.recognisedVersion = recognisedVersion; @@ -145,11 +146,11 @@ public WorldIconProducer getNetherFortressProducer() { return netherFortressProducer; } - public WorldIconProducer> getEndCityProducer() { + public WorldIconProducer> getEndCityProducer() { return endCityProducer; } - public WorldIconProducer> getEndGatewayProducer() { + public WorldIconProducer getEndGatewayProducer() { return endGatewayProducer; } diff --git a/src/main/java/amidst/mojangapi/world/icon/producer/EndGatewayProducer.java b/src/main/java/amidst/mojangapi/world/icon/producer/EndGatewayProducer.java index 426c53993..0dffe3488 100644 --- a/src/main/java/amidst/mojangapi/world/icon/producer/EndGatewayProducer.java +++ b/src/main/java/amidst/mojangapi/world/icon/producer/EndGatewayProducer.java @@ -2,6 +2,7 @@ import java.util.ArrayList; import java.util.List; +import java.util.Map.Entry; import java.util.function.Consumer; import amidst.documentation.ThreadSafe; @@ -10,8 +11,10 @@ import amidst.mojangapi.world.coordinates.CoordinatesInWorld; import amidst.mojangapi.world.coordinates.Resolution; import amidst.mojangapi.world.icon.WorldIcon; -import amidst.mojangapi.world.oracle.EndIsland; -import amidst.mojangapi.world.oracle.EndIslandOracle; +import amidst.mojangapi.world.oracle.end.EndIslandList; +import amidst.mojangapi.world.oracle.end.EndIslandOracle; +import amidst.mojangapi.world.oracle.end.LargeEndIsland; +import amidst.mojangapi.world.oracle.end.SmallEndIsland; import kaptainwutax.seedutils.mc.ChunkRand; import kaptainwutax.seedutils.mc.MCVersion; @@ -19,7 +22,7 @@ import static amidst.mojangapi.world.icon.type.DefaultWorldIconTypes.POSSIBLE_END_GATEWAY;; @ThreadSafe -public class EndGatewayProducer extends WorldIconProducer> { +public class EndGatewayProducer extends WorldIconProducer { private static final int END_GATEWAY_CHANCE = 700; private static final Resolution RESOLUTION = Resolution.CHUNK; private static final int SIZE = RESOLUTION.getStepsPerFragment(); @@ -41,28 +44,33 @@ public class EndGatewayProducer extends WorldIconProducer> { /** * Used as a cache only for the spawn gateways. */ - private final EndSpawnGatewayProducer spawnProducer; + //private final EndSpawnGatewayProducer spawnProducer; private final long seed; + private final int featureIndex; + private final int generationStage; - public EndGatewayProducer(long seed, EndIslandOracle oracle) { - this.spawnProducer = new EndSpawnGatewayProducer(oracle); + public EndGatewayProducer(long seed, int featureIndex, int generationStage, EndIslandOracle oracle) { + //this.spawnProducer = new EndSpawnGatewayProducer(oracle); this.seed = seed; + this.featureIndex = featureIndex; + this.generationStage = generationStage; } @Override - public void produce(CoordinatesInWorld corner, Consumer consumer, List endIslands) { - for (int xRelativeToFragment = -BUFFER_SIZE; xRelativeToFragment < SIZE + BUFFER_SIZE; xRelativeToFragment++) { - for (int yRelativeToFragment = -BUFFER_SIZE; yRelativeToFragment < SIZE + BUFFER_SIZE; yRelativeToFragment++) { + public void produce(CoordinatesInWorld corner, Consumer consumer, EndIslandList endIslands) { + // The buffer only needs to account for positive changes because it's not possible for it to move backwards out of the fragment. + for (int xRelativeToFragment = -BUFFER_SIZE; xRelativeToFragment < SIZE; xRelativeToFragment++) { + for (int yRelativeToFragment = -BUFFER_SIZE; yRelativeToFragment < SIZE; yRelativeToFragment++) { generateAt(corner, consumer, endIslands, xRelativeToFragment, yRelativeToFragment); } } - spawnProducer.produce(corner, consumer, null); + //spawnProducer.produce(corner, consumer, null); } private void generateAt( CoordinatesInWorld corner, Consumer consumer, - List endIslands, + EndIslandList endIslands, long xRelativeToFragment, long yRelativeToFragment) { long x = xRelativeToFragment + corner.getXAs(RESOLUTION); @@ -83,166 +91,174 @@ private void generateAt( * Made with help from * KaptainWutax. */ - public CoordinatesInWorld tryGetValidLocationFromChunk(long chunkX, long chunkY, List endIslands, CoordinatesInWorld corner) { + public CoordinatesInWorld tryGetValidLocationFromChunk(long chunkX, long chunkY, EndIslandList endIslands, CoordinatesInWorld corner) { + if((chunkX * chunkX + chunkY * chunkY) > 4096) { ChunkRand rand = new ChunkRand(); long blockX = chunkX << 4; long blockY = chunkY << 4; - rand.setDecoratorSeed(seed, (int) blockX, (int) blockY, 0, 3, MCVersion.v1_13); + rand.setDecoratorSeed(seed, (int) blockX, (int) blockY, featureIndex, generationStage, MCVersion.v1_13); if(rand.nextInt(END_GATEWAY_CHANCE) == 0) { - for(EndIsland island : endIslands) { - float biomeInfluence = island.influenceAtChunk(chunkX, chunkY); + for(LargeEndIsland largeIsland : endIslands.getLargeIslands()) { + float biomeInfluence = largeIsland.influenceAtChunk(chunkX, chunkY); if(biomeInfluence >= REQUIRED_BIOME_INFLUENCE) { long gatewayX = rand.nextInt(16) + blockX; long gatewayY = rand.nextInt(16) + blockY; CoordinatesInWorld coordinates = new CoordinatesInWorld(gatewayX, gatewayY); if(coordinates.isInBoundsOf(corner, Fragment.SIZE)) { // While this barely ever is false due to the biome influence check, we do it anyway just to make sure - float placementInfluence = island.influenceAtBlock(gatewayX, gatewayY); + float placementInfluence = largeIsland.influenceAtBlock(gatewayX, gatewayY); if(placementInfluence > 0.0F) { return coordinates; + } else { + // If this check fails, there's a very small chance that it landed on a small island + for(SmallEndIsland smallIsland : endIslands.getSmallIslands()) { + if((int) coordinates.getDistance(smallIsland.getX(), smallIsland.getY()) <= smallIsland.getSize()) { + return coordinates; + } + } } } } } } - } else { } return null; } - private static class EndSpawnGatewayProducer extends CachedWorldIconProducer { - private static final int NUMBER_OF_SPAWN_GATEWAYS = 20; - - private final EndIslandOracle oracle; - - public EndSpawnGatewayProducer(EndIslandOracle oracle) { - this.oracle = oracle; - } - - @Override - protected List doCreateCache() { - List iconList = new ArrayList(); - for(int i = 0; i < NUMBER_OF_SPAWN_GATEWAYS; i++) { - // Generate inner WorldIcon - int x = floor(96.0D * Math.cos(2.0D * (-Math.PI + 0.15707963267948966D * (double) i))); - int y = floor(96.0D * Math.sin(2.0D * (-Math.PI + 0.15707963267948966D * (double) i))); - CoordinatesInWorld possibleCoordinates = new CoordinatesInWorld(x, y); - iconList.add(new WorldIcon( - possibleCoordinates, - POSSIBLE_END_GATEWAY.getLabel(), - POSSIBLE_END_GATEWAY.getImage(), - Dimension.END, - false - ) - ); - - // Generate outer WorldIcon - // Normalize the coordinates the same way MC would - double doubleX = 0; - double doubleY = 0; - double d0 = (double) ((float) (Math.sqrt((double) x * (double) x + (double) y * (double) y))); // There are this many casts in the MC code - if (d0 >= 1.0E-4D) { - doubleX = (double) x / d0; - doubleY = (double) y / d0; - } - // Scale the coordinates - CoordinatesInWorld coordinates = new CoordinatesInWorld((long) (doubleX * 1024.0D), (long) (doubleY * 1024.0D)); - - // Check for closest land along vector - for (int j = 16; !isChunkIslandlessSlow(coordinates) && j-- > 0; coordinates = coordinates.add((long) (doubleX * -16.0D), (long) (doubleY * -16.0D))) { - } - - for (int k = 16; isChunkIslandlessSlow(coordinates) && k-- > 0; coordinates = coordinates.add((long) (doubleX * 16.0D), (long) (doubleY * 16.0D))) { - } - - // Mess with the coords a bit more - coordinates = findSpawnpoint(coordinates, true); - coordinates = findHighestBlock(coordinates, 16); - - iconList.add(new WorldIcon( - coordinates, - POSSIBLE_END_GATEWAY.getLabel(), - POSSIBLE_END_GATEWAY.getImage(), - Dimension.END, - false - ) - ); - } - return iconList; - } - - /* - * This number is supposed to sort of guess whether end - * stone might be at a particular location. - */ - private static final float ISLAND_INFLUENCE_THRESHOLD = -20.0F; - - @SuppressWarnings("unused") - private boolean isChunkIslandlessFast(CoordinatesInWorld blockCoords) { - for(EndIsland island : oracle.getAt(blockCoords)) { - if(island.influenceAtChunk(blockCoords.getX() >> 4, blockCoords.getY() >> 4) >= ISLAND_INFLUENCE_THRESHOLD) { - return false; - } - } - return true; - } - - private boolean isChunkIslandlessSlow(CoordinatesInWorld blockCoords) { - for(EndIsland island : oracle.getAt(blockCoords)) { - for (long x = blockCoords.getX() & -16; x < (blockCoords.getX() | 15); x++) { - for (long y = blockCoords.getY() & -16; y < (blockCoords.getY() | 15); y++) { - if(island.influenceAtBlock(x, y) >= ISLAND_INFLUENCE_THRESHOLD) { - return false; - } - } - } - } - return true; - } - - private CoordinatesInWorld findSpawnpoint(CoordinatesInWorld blockCoords, boolean guaranteeEndStone) { - for (long y = blockCoords.getY() & -16; y < (blockCoords.getY() | 15); y++) { - for (long x = blockCoords.getX() & -16; x < (blockCoords.getX() | 15); x++) { - for (EndIsland island : oracle.getAt(blockCoords)) { - if (island.influenceAtBlock(x, y) >= (guaranteeEndStone ? 0.0F : ISLAND_INFLUENCE_THRESHOLD)) { - return new CoordinatesInWorld(x, y); - } - } - } - } - return blockCoords; - } - - private CoordinatesInWorld findHighestBlock(CoordinatesInWorld blockCoords, int radius) { - return findHighestBlock(blockCoords, -radius, -radius, radius, radius); - } - - private CoordinatesInWorld findHighestBlock(CoordinatesInWorld blockCoords, int startX, int startY, int endX, int endY) { - float highestInfluence = -100.0F; - long highestX = blockCoords.getX(); - long highestY = blockCoords.getY(); - - for (long x = blockCoords.getX() + startX; x <= blockCoords.getX() + endX; ++x) { - for (long y = blockCoords.getY() + startY; y <= blockCoords.getY() + endY; ++y) { - float coordInfluence = oracle.getInfluenceAtBlock(x, y); - if(coordInfluence > highestInfluence) { - highestInfluence = coordInfluence; - highestX = x; - highestY = y; - } - } - } - return new CoordinatesInWorld(highestX, highestY); - } - - private static int floor(double value) { - int i = (int) value; - return value < (double) i ? i - 1 : i; - } - } +// private static class EndSpawnGatewayProducer extends CachedWorldIconProducer { +// private static final int NUMBER_OF_SPAWN_GATEWAYS = 20; +// +// private final EndIslandOracle oracle; +// +// public EndSpawnGatewayProducer(EndIslandOracle oracle) { +// this.oracle = oracle; +// } +// +// @Override +// protected List doCreateCache() { +// List iconList = new ArrayList(); +// for(int i = 0; i < NUMBER_OF_SPAWN_GATEWAYS; i++) { +// // Generate inner WorldIcon +// int x = floor(96.0D * Math.cos(2.0D * (-Math.PI + 0.15707963267948966D * (double) i))); +// int y = floor(96.0D * Math.sin(2.0D * (-Math.PI + 0.15707963267948966D * (double) i))); +// CoordinatesInWorld possibleCoordinates = new CoordinatesInWorld(x, y); +// iconList.add(new WorldIcon( +// possibleCoordinates, +// POSSIBLE_END_GATEWAY.getLabel(), +// POSSIBLE_END_GATEWAY.getImage(), +// Dimension.END, +// false +// ) +// ); +// +// // Generate outer WorldIcon +// // Normalize the coordinates the same way MC would +// double doubleX = 0; +// double doubleY = 0; +// double d0 = (double) ((float) (Math.sqrt((double) x * (double) x + (double) y * (double) y))); // There are this many casts in the MC code +// if (d0 >= 1.0E-4D) { +// doubleX = (double) x / d0; +// doubleY = (double) y / d0; +// } +// // Scale the coordinates +// CoordinatesInWorld coordinates = new CoordinatesInWorld((long) (doubleX * 1024.0D), (long) (doubleY * 1024.0D)); +// +// // Check for closest land along vector +// for (int j = 16; !isChunkIslandlessSlow(coordinates) && j-- > 0; coordinates = coordinates.add((long) (doubleX * -16.0D), (long) (doubleY * -16.0D))) { +// } +// +// for (int k = 16; isChunkIslandlessSlow(coordinates) && k-- > 0; coordinates = coordinates.add((long) (doubleX * 16.0D), (long) (doubleY * 16.0D))) { +// } +// +// // Mess with the coords a bit more +// coordinates = findSpawnpoint(coordinates, true); +// coordinates = findHighestBlock(coordinates, 16); +// +// iconList.add(new WorldIcon( +// coordinates, +// POSSIBLE_END_GATEWAY.getLabel(), +// POSSIBLE_END_GATEWAY.getImage(), +// Dimension.END, +// false +// ) +// ); +// } +// return iconList; +// } +// +// /* +// * This number is supposed to sort of guess whether end +// * stone might be at a particular location. +// */ +// private static final float ISLAND_INFLUENCE_THRESHOLD = -20.0F; +// +// @SuppressWarnings("unused") +// private boolean isChunkIslandlessFast(CoordinatesInWorld blockCoords) { +// for(LargeEndIsland island : oracle.getLargeIslandsAt(blockCoords)) { +// if(island.influenceAtChunk(blockCoords.getX() >> 4, blockCoords.getY() >> 4) >= ISLAND_INFLUENCE_THRESHOLD) { +// return false; +// } +// } +// return true; +// } +// +// private boolean isChunkIslandlessSlow(CoordinatesInWorld blockCoords) { +// EndIslandList endIslands = oracle.getAt(blockCoords); +// for(LargeEndIsland island : endIslands.getLargeIslands()) { +// for (long x = blockCoords.getX() & -16; x < (blockCoords.getX() | 15); x++) { +// for (long y = blockCoords.getY() & -16; y < (blockCoords.getY() | 15); y++) { +// if(island.influenceAtBlock(x, y) >= ISLAND_INFLUENCE_THRESHOLD) { +// return false; //FIXME +// } +// } +// } +// } +// return true; +// } +// +// private CoordinatesInWorld findSpawnpoint(CoordinatesInWorld blockCoords, boolean guaranteeEndStone) { +// for (long y = blockCoords.getY() & -16; y < (blockCoords.getY() | 15); y++) { +// for (long x = blockCoords.getX() & -16; x < (blockCoords.getX() | 15); x++) { +// for (LargeEndIsland island : oracle.getAt(blockCoords)) {//FIXME +// if (island.influenceAtBlock(x, y) >= (guaranteeEndStone ? 0.0F : ISLAND_INFLUENCE_THRESHOLD)) { +// return new CoordinatesInWorld(x, y); +// } +// } +// } +// } +// return blockCoords; +// } +// +// private CoordinatesInWorld findHighestBlock(CoordinatesInWorld blockCoords, int radius) { +// return findHighestBlock(blockCoords, -radius, -radius, radius, radius); +// } +// +// private CoordinatesInWorld findHighestBlock(CoordinatesInWorld blockCoords, int startX, int startY, int endX, int endY) { +// float highestInfluence = -100.0F; +// long highestX = blockCoords.getX(); +// long highestY = blockCoords.getY(); +// +// for (long x = blockCoords.getX() + startX; x <= blockCoords.getX() + endX; ++x) { +// for (long y = blockCoords.getY() + startY; y <= blockCoords.getY() + endY; ++y) { +// float coordInfluence = oracle.getInfluenceAtBlock(x, y);//FIXME +// if(coordInfluence > highestInfluence) { +// highestInfluence = coordInfluence; +// highestX = x; +// highestY = y; +// } +// } +// } +// return new CoordinatesInWorld(highestX, highestY); +// } +// +// private static int floor(double value) { +// int i = (int) value; +// return value < (double) i ? i - 1 : i; +// } +// } } diff --git a/src/main/java/amidst/mojangapi/world/icon/type/EndCityWorldIconTypeProvider.java b/src/main/java/amidst/mojangapi/world/icon/type/EndCityWorldIconTypeProvider.java index a3a9a11fe..0d9738310 100644 --- a/src/main/java/amidst/mojangapi/world/icon/type/EndCityWorldIconTypeProvider.java +++ b/src/main/java/amidst/mojangapi/world/icon/type/EndCityWorldIconTypeProvider.java @@ -2,9 +2,9 @@ import java.util.List; -import amidst.mojangapi.world.oracle.EndIsland; +import amidst.mojangapi.world.oracle.end.LargeEndIsland; -public class EndCityWorldIconTypeProvider implements WorldIconTypeProvider> { +public class EndCityWorldIconTypeProvider implements WorldIconTypeProvider> { /** * REQUIRED_INFLUENCE is a value between 0 and 80 that I'm finding by trial * and error. If the island influence is 0 or higher then an End City can @@ -16,9 +16,9 @@ public class EndCityWorldIconTypeProvider implements WorldIconTypeProvider endIslands) { + public DefaultWorldIconTypes get(int chunkX, int chunkY, List largeEndIslands) { if ((chunkX * chunkX + chunkY * chunkY) > 4096) { - return hasSuitableIslandFoundation(chunkX, chunkY, endIslands); + return hasSuitableIslandFoundation(chunkX, chunkY, largeEndIslands); } else { return null; } @@ -35,9 +35,9 @@ public DefaultWorldIconTypes get(int chunkX, int chunkY, List endIsla * * In the meantime, fall back on the requiredInfluence heuristic */ - private DefaultWorldIconTypes hasSuitableIslandFoundation(int chunkX, int chunkY, List endIslands) { + private DefaultWorldIconTypes hasSuitableIslandFoundation(int chunkX, int chunkY, List largeEndIslands) { DefaultWorldIconTypes result = null; - for (EndIsland island : endIslands) { + for (LargeEndIsland island : largeEndIslands) { float influence = island.influenceAtChunk(chunkX, chunkY); if (influence >= REQUIRED_INFLUENCE) { return DefaultWorldIconTypes.END_CITY; diff --git a/src/main/java/amidst/mojangapi/world/oracle/EndIslandOracle.java b/src/main/java/amidst/mojangapi/world/oracle/EndIslandOracle.java deleted file mode 100644 index 14e9d3cc6..000000000 --- a/src/main/java/amidst/mojangapi/world/oracle/EndIslandOracle.java +++ /dev/null @@ -1,193 +0,0 @@ -package amidst.mojangapi.world.oracle; - -import java.util.ArrayList; -import java.util.List; - -import amidst.documentation.ThreadSafe; -import amidst.mojangapi.world.coordinates.CoordinatesInWorld; -import amidst.mojangapi.world.coordinates.Resolution; -import amidst.mojangapi.world.versionfeatures.DefaultBiomes; - -import kaptainwutax.seedutils.lcg.rand.JRand; - -@ThreadSafe -public class EndIslandOracle { - public static EndIslandOracle from(long seed) { - return new EndIslandOracle(createNoiseFunction(seed)); - } - - /** - * Returns the noise function using the current seed. - */ - private static SimplexNoise createNoiseFunction(long seed) { - JRand random = new JRand(seed); - // Mimics the side-effects to the random number generator caused by Minecraft. - random.advance(17292); - return new SimplexNoise(random); - } - - /** - * Minecraft checks 12 chunks either side of a chunk when assessing island - * influence. - */ - private static final int SURROUNDING_CHUNKS = 12; - - /** - * When cast to double, -0.9 will become -0.8999999761581421, which is why - * you might see that value in Minecraft's .jar - */ - private static final float ISLAND_DENSITY_THRESHOLD = -0.9f; - - /** - * The distance from (0;0) at which islands start to generated - */ - private static final int OUTER_LANDS_DISTANCE_IN_CHUNKS = 64; - - private final SimplexNoise noiseFunction; - - public EndIslandOracle(SimplexNoise noiseFunction) { - this.noiseFunction = noiseFunction; - } - - public int getBiomeAtBlock(long x, long y) { - if (x * x + y * y <= 4096L) { - return DefaultBiomes.theEnd; - } else { - float influence = getInfluenceAtBlock(x, y); - if (influence > 40.0F) { - return DefaultBiomes.theEndHigh; - } else if (influence >= 0.0F) { - return DefaultBiomes.theEndMedium; - } else { - return influence < -20.0F ? DefaultBiomes.theEndLow : DefaultBiomes.theEndBarren; - } - } - } - - public int getBiomeAtBlock(CoordinatesInWorld coords) { - long x = coords.getX(); - long y = coords.getY(); - - if (x * x + y * y <= 4096L) { - return DefaultBiomes.theEnd; - } else { - float influence = getInfluenceAtBlock(coords); - if (influence > 40.0F) { - return DefaultBiomes.theEndHigh; - } else if (influence >= 0.0F) { - return DefaultBiomes.theEndMedium; - } else { - return influence < -20.0F ? DefaultBiomes.theEndLow : DefaultBiomes.theEndBarren; - } - } - } - - public float getInfluenceAtBlock(long x, long y) { - float highestInfluence = -100.0f; - - for(EndIsland island : getAt(new CoordinatesInWorld(x, y))) { - float tempInfluence = island.influenceAtBlock(x, y); - if(tempInfluence > highestInfluence) { - highestInfluence = tempInfluence; - } - } - return highestInfluence; - } - - public float getInfluenceAtBlock(CoordinatesInWorld coords) { - float highestInfluence = -100.0f; - long x = coords.getX(); - long y = coords.getY(); - - for (EndIsland island : getAt(coords)) { - float tempInfluence = island.influenceAtBlock(x, y); - if (tempInfluence > highestInfluence) { - highestInfluence = tempInfluence; - } - } - return highestInfluence; - } - - public List getAt(CoordinatesInWorld corner) { - int steps = Resolution.CHUNK.getStepsPerFragment(); - return findSurroundingIslands( - corner.getXAs(Resolution.CHUNK), - corner.getYAs(Resolution.CHUNK), - steps, - steps); - } - - /** - * Returns a list of all islands that might be touching a chunk-area. - */ - private List findSurroundingIslands( - long chunkX, - long chunkY, - int chunksPerFragmentX, - int chunksPerFragmentY) { - List result = new ArrayList<>(); - for (int y = -SURROUNDING_CHUNKS; y <= chunksPerFragmentY + SURROUNDING_CHUNKS; y++) { - for (int x = -SURROUNDING_CHUNKS; x <= chunksPerFragmentX + SURROUNDING_CHUNKS; x++) { - EndIsland island = tryCreateEndIsland(chunkX + x, chunkY + y); - if (island != null) { - result.add(island); - } - } - } - return result; - } - - /** - * Returns an EndIsland if one has 'grown out' from the chunk, otherwise - * null - */ - private EndIsland tryCreateEndIsland(long chunkX, long chunkY) { - - if (chunkX == 0 && chunkY == 0) { - return createMainEndIsland(chunkX, chunkY); - } else if (!isInRange(chunkX, chunkY, OUTER_LANDS_DISTANCE_IN_CHUNKS)) { - return tryCreateEndIslandInOuterLands(chunkX, chunkY); - } else { - return null; - } - } - - /** - * The main island grows from the origin, with a hard-coded erosion factor - * of 8 - */ - private EndIsland createMainEndIsland(long chunkX, long chunkY) { - return new EndIsland(chunkX, chunkY, 8.0f); - } - - /** - * The chunk is in the outer-islands band - */ - private EndIsland tryCreateEndIslandInOuterLands(long chunkX, long chunkY) { - if (noiseFunction.noise(chunkX, chunkY) < ISLAND_DENSITY_THRESHOLD) { - return new EndIsland(chunkX, chunkY, getErosionFactor(chunkX, chunkY)); - } else { - return null; - } - } - - /** - * An island (or part of an island) grows out from this chunk, with an - * erosion factor between 9 and 21 (i.e. they will be smaller than the main - * island). - */ - private int getErosionFactor(long chunkX, long chunkY) { - // Convert coordinates to long to guard against overflow - return (int) ((Math.abs(chunkX) * 3439 + Math.abs(chunkY) * 147) % 13 + 9); - } - - /** - * Is the point (x, y) inside the disk of radius d centered at the origin? - */ - private boolean isInRange(long x, long y, int d) { - // Guard against overflow - if (x < -d || x > d || y < -d || y > d) - return false; - return x * x + y * y <= d * d; - } -} diff --git a/src/main/java/amidst/mojangapi/world/oracle/end/EndIslandList.java b/src/main/java/amidst/mojangapi/world/oracle/end/EndIslandList.java new file mode 100644 index 000000000..6aada026c --- /dev/null +++ b/src/main/java/amidst/mojangapi/world/oracle/end/EndIslandList.java @@ -0,0 +1,25 @@ +package amidst.mojangapi.world.oracle.end; + +import java.util.List; + +import amidst.documentation.Immutable; + +@Immutable +public class EndIslandList { + private final List largeIslands; + private final List smallIslands; + + public EndIslandList(List smallIslands, List largeIslands) { + this.largeIslands = largeIslands; + this.smallIslands = smallIslands; + } + + public List getLargeIslands() { + return largeIslands; + } + + public List getSmallIslands() { + return smallIslands; + } + +} diff --git a/src/main/java/amidst/mojangapi/world/oracle/end/EndIslandOracle.java b/src/main/java/amidst/mojangapi/world/oracle/end/EndIslandOracle.java new file mode 100644 index 000000000..f4a93c819 --- /dev/null +++ b/src/main/java/amidst/mojangapi/world/oracle/end/EndIslandOracle.java @@ -0,0 +1,307 @@ +package amidst.mojangapi.world.oracle.end; + +import java.util.ArrayList; +import java.util.List; + +import amidst.documentation.ThreadSafe; +import amidst.mojangapi.world.coordinates.CoordinatesInWorld; +import amidst.mojangapi.world.coordinates.Resolution; +import amidst.mojangapi.world.oracle.SimplexNoise; +import amidst.mojangapi.world.versionfeatures.DefaultBiomes; + +import kaptainwutax.seedutils.lcg.rand.JRand; +import kaptainwutax.seedutils.mc.ChunkRand; +import kaptainwutax.seedutils.mc.MCVersion; + +@ThreadSafe +public class EndIslandOracle { + public static EndIslandOracle from(long seed) { + return new EndIslandOracle(createNoiseFunction(seed), seed); + } + + /** + * Returns the noise function using the current seed. + */ + private static SimplexNoise createNoiseFunction(long seed) { + JRand random = new JRand(seed); + // Mimics the side-effects to the random number generator caused by Minecraft. + // Past 1.13, it just skips the random 17292 times. + random.advance(17292); + return new SimplexNoise(random); + } + + /** + * Minecraft checks 12 chunks either side of a chunk when assessing island + * influence. + */ + private static final int SURROUNDING_CHUNKS = 12; + + /** + * When cast to double, -0.9 will become -0.8999999761581421, which is why + * you might see that value in Minecraft's .jar + */ + private static final float ISLAND_DENSITY_THRESHOLD = -0.9f; + + /** + * The distance from (0;0) at which islands start to generated + */ + private static final int OUTER_LANDS_DISTANCE_IN_CHUNKS = 64; + + private final SimplexNoise noiseFunction; + private final long seed; + + public EndIslandOracle(SimplexNoise noiseFunction, long seed) { + this.noiseFunction = noiseFunction; + this.seed = seed; + } + + public static int getBiomeAtBlock(long x, long y, List largeIslands) { + if (x * x + y * y <= 1048576L) { + return DefaultBiomes.theEnd; + } else { + float influence = getInfluenceAtBlock(x, y, largeIslands); + if (influence > 40.0F) { + return DefaultBiomes.theEndHigh; + } else if (influence >= 0.0F) { + return DefaultBiomes.theEndMedium; + } else { + return influence < -20.0F ? DefaultBiomes.theEndLow : DefaultBiomes.theEndBarren; + } + } + } + + public static float getInfluenceAtBlock(long x, long y, List largeIslands) { + float highestInfluence = -100.0f; + + for (LargeEndIsland island : largeIslands) { + if (island instanceof LargeEndIsland) { + float tempInfluence = ((LargeEndIsland) island).influenceAtBlock(x, y); + if (tempInfluence > highestInfluence) { + highestInfluence = tempInfluence; + } + } + } + return highestInfluence; + } + + @Deprecated + private int getBiomeAtBlock(long x, long y) { + if (x * x + y * y <= 1048576L) { + return DefaultBiomes.theEnd; + } else { + float influence = getInfluenceAtBlock(x, y); + if (influence > 40.0F) { + return DefaultBiomes.theEndHigh; + } else if (influence >= 0.0F) { + return DefaultBiomes.theEndMedium; + } else { + return influence < -20.0F ? DefaultBiomes.theEndLow : DefaultBiomes.theEndBarren; + } + } + } + + @Deprecated + private float getInfluenceAtBlock(long x, long y) { + float highestInfluence = -100.0f; + + for (LargeEndIsland island : getLargeIslandsAt(new CoordinatesInWorld(x, y))) { + if (island instanceof LargeEndIsland) { + float tempInfluence = ((LargeEndIsland) island).influenceAtBlock(x, y); + if (tempInfluence > highestInfluence) { + highestInfluence = tempInfluence; + } + } + } + return highestInfluence; + } + + public EndIslandList getAt(CoordinatesInWorld corner) { + int steps = Resolution.CHUNK.getStepsPerFragment(); + return findSurroundingIslands( + corner.getXAs(Resolution.CHUNK), + corner.getYAs(Resolution.CHUNK), + steps, + steps); + } + + /** + * Returns a list of all islands that might be touching a chunk-area. + */ + private EndIslandList findSurroundingIslands( + long chunkX, + long chunkY, + int chunksPerFragmentX, + int chunksPerFragmentY) { + List largeEndIslands = findSurroundingLargeIslands(chunkX, chunkY, chunksPerFragmentX, chunksPerFragmentY); + List smallEndIslands = findSurroundingSmallIslands(chunkX, chunkY, chunksPerFragmentX, chunksPerFragmentY, largeEndIslands); + return new EndIslandList(smallEndIslands, largeEndIslands); + } + + public List getLargeIslandsAt(CoordinatesInWorld corner) { + int steps = Resolution.CHUNK.getStepsPerFragment(); + return findSurroundingLargeIslands( + corner.getXAs(Resolution.CHUNK), + corner.getYAs(Resolution.CHUNK), + steps, + steps); + } + + /** + * Returns a list of all large islands that might be touching a chunk-area. + */ + private List findSurroundingLargeIslands( + long chunkX, + long chunkY, + int chunksPerFragmentX, + int chunksPerFragmentY) { + List result = new ArrayList<>(); + for (int y = -SURROUNDING_CHUNKS; y <= chunksPerFragmentY + SURROUNDING_CHUNKS; y++) { + for (int x = -SURROUNDING_CHUNKS; x <= chunksPerFragmentX + SURROUNDING_CHUNKS; x++) { + LargeEndIsland island = tryCreateLargeEndIsland(chunkX + x, chunkY + y); + if (island != null) { + result.add(island); + } + } + } + return result; + } + + /** + * Returns a LargeEndIsland if one has 'grown out' from the chunk, otherwise + * null + */ + private LargeEndIsland tryCreateLargeEndIsland(long chunkX, long chunkY) { + + if (chunkX == 0 && chunkY == 0) { + return createMainEndIsland(chunkX, chunkY); + } else if (!isInRange(chunkX, chunkY, OUTER_LANDS_DISTANCE_IN_CHUNKS)) { + return tryCreateLargeEndIslandInOuterLands(chunkX, chunkY); + } else { + return null; + } + } + + /** + * The main island grows from the origin, with a hard-coded erosion factor + * of 8 + */ + private LargeEndIsland createMainEndIsland(long chunkX, long chunkY) { + return new LargeEndIsland(chunkX, chunkY, 8.0f); + } + + /** + * The chunk is in the outer-islands band + */ + private LargeEndIsland tryCreateLargeEndIslandInOuterLands(long chunkX, long chunkY) { + if (noiseFunction.noise(chunkX, chunkY) < ISLAND_DENSITY_THRESHOLD) { + return new LargeEndIsland(chunkX, chunkY, getErosionFactor(chunkX, chunkY)); + } else { + return null; + } + } + + /** + * An island (or part of an island) grows out from this chunk, with an + * erosion factor between 9 and 21 (i.e. they will be smaller than the main + * island). + */ + private int getErosionFactor(long chunkX, long chunkY) { + // Convert coordinates to long to guard against overflow + return (int) ((Math.abs(chunkX) * 3439 + Math.abs(chunkY) * 147) % 13 + 9); + } + + /** + * Is the point (x, y) inside the disk of radius d centered at the origin? + */ + private boolean isInRange(long x, long y, int d) { + // Guard against overflow + if (x < -d || x > d || y < -d || y > d) + return false; + return x * x + y * y <= d * d; + } + + public List getSmallIslandsAt(CoordinatesInWorld corner) { + int steps = Resolution.CHUNK.getStepsPerFragment(); + return findSurroundingSmallIslands( + corner.getXAs(Resolution.CHUNK), + corner.getYAs(Resolution.CHUNK), + steps, + steps); + } + + @Deprecated + private List findSurroundingSmallIslands( + long chunkX, + long chunkY, + int chunksPerFragmentX, + int chunksPerFragmentY) { + List result = new ArrayList<>(); + for (int y = 0; y <= chunksPerFragmentY; y++) { + for (int x = 0; x <= chunksPerFragmentX; x++) { + addSmallIslandsInChunk(chunkX + x, chunkY + y, result); + } + } + return result; + } + + @Deprecated + private void addSmallIslandsInChunk(long chunkX, long chunkY, List islands) { + long blockX = chunkX << 4; + long blockY = chunkY << 4; + if (getBiomeAtBlock(blockX, blockY) == DefaultBiomes.theEndLow) { + ChunkRand rand = new ChunkRand(); + rand.setDecoratorSeed(seed, (int) blockX, (int) blockY, 0, 3, MCVersion.v1_13); + + if (rand.nextInt(14) == 0) { + long resultX = blockX + rand.nextInt(16); + int resultH = 55 + rand.nextInt(16); + long rexultY = blockY + rand.nextInt(16); + islands.add(new SmallEndIsland(resultX, rexultY, resultH, 4)); + if (rand.nextInt(4) == 0) { + resultX = blockX + rand.nextInt(16); + resultH = 55 + rand.nextInt(16); + rexultY = blockY + rand.nextInt(16); + islands.add(new SmallEndIsland(resultX, rexultY, resultH, 4)); + } + } + } + } + + private List findSurroundingSmallIslands( + long chunkX, + long chunkY, + int chunksPerFragmentX, + int chunksPerFragmentY, + List largeIslands) { + List result = new ArrayList<>(); + for (int y = 0; y <= chunksPerFragmentY; y++) { + for (int x = 0; x <= chunksPerFragmentX; x++) { + addSmallIslandsInChunk(chunkX + x, chunkY + y, result, largeIslands); + } + } + return result; + } + + private void addSmallIslandsInChunk(long chunkX, long chunkY, List smallIslands, List largeIslands) { + long blockX = chunkX << 4; + long blockY = chunkY << 4; + if (getBiomeAtBlock(blockX, blockY, largeIslands) == DefaultBiomes.theEndLow) { + ChunkRand rand = new ChunkRand(); + rand.setDecoratorSeed(seed, (int) blockX, (int) blockY, 0, 0, MCVersion.v1_13); + + if (rand.nextInt(14) == 0) { + long resultX = blockX + rand.nextInt(16); + int resultH = 55 + rand.nextInt(16); + long rexultY = blockY + rand.nextInt(16); + smallIslands.add(new SmallEndIsland(resultX, rexultY, resultH, 4)); + if (rand.nextInt(4) == 0) { + resultX = blockX + rand.nextInt(16); + resultH = 55 + rand.nextInt(16); + rexultY = blockY + rand.nextInt(16); + smallIslands.add(new SmallEndIsland(resultX, rexultY, resultH, 4)); + } + } + } + } + +} diff --git a/src/main/java/amidst/mojangapi/world/oracle/EndIsland.java b/src/main/java/amidst/mojangapi/world/oracle/end/LargeEndIsland.java similarity index 90% rename from src/main/java/amidst/mojangapi/world/oracle/EndIsland.java rename to src/main/java/amidst/mojangapi/world/oracle/end/LargeEndIsland.java index 3849dd39d..8324495ba 100644 --- a/src/main/java/amidst/mojangapi/world/oracle/EndIsland.java +++ b/src/main/java/amidst/mojangapi/world/oracle/end/LargeEndIsland.java @@ -1,96 +1,96 @@ -package amidst.mojangapi.world.oracle; - -import amidst.documentation.Immutable; - -// TODO: check sign of differences ... (this.chunkX - chunkX) vs (chunkX - this.chunkX) -@Immutable -public class EndIsland { - private static final short X_ADJUSTMENT = 1; - private static final short Y_ADJUSTMENT = 1; - - private final long chunkX; - private final long chunkY; - private final float erosionFactor; - - protected EndIsland(long chunkX, long chunkY, float erosionFactor) { - this.chunkX = chunkX; - this.chunkY = chunkY; - this.erosionFactor = erosionFactor; - } - - /** - * Retuns a value between 80 and -100 which indicates this island's - * influence at the block coordindates given. A non-negative value indicates - * there will be solid ground, while a negative value indicates the - * rocky-island-shore, which might be solid ground (but that becomes less - * likely the lower the value). - */ - public float influenceAtBlock(long x, long y) { - // Add 8 blocks to both axis because all the Minecraft calculations are - // done using chunk coordinates and are converted as being the center of - // the chunk whenever translated to block coordinates, whereas Amidst - // treats chunk coords as blockCoordinates >> 4. - // This function also does a floating point divide by 16 instead of - // shifting by 4 in order to maintain sub-chunk accuracy with x & y. - float chunkX = (x + 8) / 16.0f; - float chunkY = (y + 8) / 16.0f; - float adjustedX = (this.chunkX - chunkX) * 2 + X_ADJUSTMENT; - float adjustedY = (this.chunkY - chunkY) * 2 + Y_ADJUSTMENT; - return getResult(adjustedX * adjustedX + adjustedY * adjustedY); - } - - /** - * A version of influenceAt() that more exactly adheres to Minecraft's - * algorithm, for use in testing for End Cities. - */ - public float influenceAtChunk(long chunkX, long chunkY) { - long adjustedX = (chunkX - this.chunkX) * 2 + X_ADJUSTMENT; - long adjustedY = (chunkY - this.chunkY) * 2 + Y_ADJUSTMENT; - return getResult(adjustedX * adjustedX + adjustedY * adjustedY); - } - - private float getResult(double squared) { - float result = 100.0f - (float) Math.sqrt(squared) * erosionFactor; - if (result > 80.0f) { - return 80.0f; - } else if (result < -100.0f) { - return -100.0f; - } else { - return result; - } - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = (int) (prime * result + chunkX); - result = (int) (prime * result + chunkY); - result = prime * result + Float.floatToIntBits(erosionFactor); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (!(obj instanceof EndIsland)) { - return false; - } - EndIsland other = (EndIsland) obj; - if (chunkX != other.chunkX) { - return false; - } - if (chunkY != other.chunkY) { - return false; - } - if (Float.floatToIntBits(erosionFactor) != Float.floatToIntBits(other.erosionFactor)) { - return false; - } - return true; - } -} +package amidst.mojangapi.world.oracle.end; + +import amidst.documentation.Immutable; + +// TODO: check sign of differences ... (this.chunkX - chunkX) vs (chunkX - this.chunkX) +@Immutable +public class LargeEndIsland { + private static final short X_ADJUSTMENT = 1; + private static final short Y_ADJUSTMENT = 1; + + private final long chunkX; + private final long chunkY; + private final float erosionFactor; + + protected LargeEndIsland(long chunkX, long chunkY, float erosionFactor) { + this.chunkX = chunkX; + this.chunkY = chunkY; + this.erosionFactor = erosionFactor; + } + + /** + * Retuns a value between 80 and -100 which indicates this island's + * influence at the block coordindates given. A non-negative value indicates + * there will be solid ground, while a negative value indicates the + * rocky-island-shore, which might be solid ground (but that becomes less + * likely the lower the value). + */ + public float influenceAtBlock(long x, long y) { + // Add 8 blocks to both axis because all the Minecraft calculations are + // done using chunk coordinates and are converted as being the center of + // the chunk whenever translated to block coordinates, whereas Amidst + // treats chunk coords as blockCoordinates >> 4. + // This function also does a floating point divide by 16 instead of + // shifting by 4 in order to maintain sub-chunk accuracy with x & y. + float chunkX = (x + 8) / 16.0f; + float chunkY = (y + 8) / 16.0f; + float adjustedX = (this.chunkX - chunkX) * 2 + X_ADJUSTMENT; + float adjustedY = (this.chunkY - chunkY) * 2 + Y_ADJUSTMENT; + return getResult(adjustedX * adjustedX + adjustedY * adjustedY); + } + + /** + * A version of influenceAt() that more exactly adheres to Minecraft's + * algorithm, for use in testing for End Cities. + */ + public float influenceAtChunk(long chunkX, long chunkY) { + long adjustedX = (chunkX - this.chunkX) * 2 + X_ADJUSTMENT; + long adjustedY = (chunkY - this.chunkY) * 2 + Y_ADJUSTMENT; + return getResult(adjustedX * adjustedX + adjustedY * adjustedY); + } + + private float getResult(double squared) { + float result = 100.0f - (float) Math.sqrt(squared) * erosionFactor; + if (result > 80.0f) { + return 80.0f; + } else if (result < -100.0f) { + return -100.0f; + } else { + return result; + } + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = (int) (prime * result + chunkX); + result = (int) (prime * result + chunkY); + result = prime * result + Float.floatToIntBits(erosionFactor); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof LargeEndIsland)) { + return false; + } + LargeEndIsland other = (LargeEndIsland) obj; + if (chunkX != other.chunkX) { + return false; + } + if (chunkY != other.chunkY) { + return false; + } + if (Float.floatToIntBits(erosionFactor) != Float.floatToIntBits(other.erosionFactor)) { + return false; + } + return true; + } +} diff --git a/src/main/java/amidst/mojangapi/world/oracle/end/SmallEndIsland.java b/src/main/java/amidst/mojangapi/world/oracle/end/SmallEndIsland.java new file mode 100644 index 000000000..e19ffdb65 --- /dev/null +++ b/src/main/java/amidst/mojangapi/world/oracle/end/SmallEndIsland.java @@ -0,0 +1,31 @@ +package amidst.mojangapi.world.oracle.end; + +public class SmallEndIsland { + private final long blockX; + private final long blockY; + private final int height; + private final int size; + + protected SmallEndIsland(long blockX, long blockY, int height, int size) { + this.blockX = blockX; + this.blockY = blockY; + this.height = height; + this.size = size; + } + + public long getX() { + return blockX; + } + + public long getY() { + return blockY; + } + + public int getHeight() { + return height; + } + + public int getSize() { + return size; + } +} diff --git a/src/main/java/amidst/mojangapi/world/versionfeatures/DefaultVersionFeatures.java b/src/main/java/amidst/mojangapi/world/versionfeatures/DefaultVersionFeatures.java index 1cbc19954..b25e872a8 100644 --- a/src/main/java/amidst/mojangapi/world/versionfeatures/DefaultVersionFeatures.java +++ b/src/main/java/amidst/mojangapi/world/versionfeatures/DefaultVersionFeatures.java @@ -31,10 +31,12 @@ import amidst.mojangapi.world.icon.producer.StrongholdProducer_128Algorithm; import amidst.mojangapi.world.icon.producer.StrongholdProducer_Buggy128Algorithm; import amidst.mojangapi.world.icon.producer.StrongholdProducer_Original; +import amidst.mojangapi.world.icon.producer.WorldIconProducer; import amidst.mojangapi.world.oracle.BiomeDataOracle; -import amidst.mojangapi.world.oracle.EndIslandOracle; import amidst.mojangapi.world.oracle.HeuristicWorldSpawnOracle; import amidst.mojangapi.world.oracle.SlimeChunkOracle; +import amidst.mojangapi.world.oracle.end.EndIslandList; +import amidst.mojangapi.world.oracle.end.EndIslandOracle; public enum DefaultVersionFeatures { ; @@ -154,7 +156,13 @@ public static VersionFeatures.Builder builder(WorldOptions worldOptions, Minecra )) .with(FeatureKey.END_GATEWAY_PRODUCER, VersionFeature.bind(features -> - VersionFeature.constant(new EndGatewayProducer(getWorldSeed(features), features.get(FeatureKey.END_ISLAND_ORACLE))) + VersionFeature.> builder() + .init( + new EndGatewayProducer(getWorldSeed(features), 0, 3, features.get(FeatureKey.END_ISLAND_ORACLE)) + ).since(RecognisedVersion._20w06a, // TODO: confirm this version is correct; this changed after 1.15.2 and before 1.16 + new EndGatewayProducer(getWorldSeed(features), 1, 4, features.get(FeatureKey.END_ISLAND_ORACLE)) + ) + .construct() )) .with(FeatureKey.MINESHAFT_LOCATION_CHECKER, VersionFeature.bind(features -> diff --git a/src/main/java/amidst/mojangapi/world/versionfeatures/FeatureKey.java b/src/main/java/amidst/mojangapi/world/versionfeatures/FeatureKey.java index 8ef05946c..ce6ea8a77 100644 --- a/src/main/java/amidst/mojangapi/world/versionfeatures/FeatureKey.java +++ b/src/main/java/amidst/mojangapi/world/versionfeatures/FeatureKey.java @@ -9,10 +9,10 @@ import amidst.mojangapi.world.icon.producer.CachedWorldIconProducer; import amidst.mojangapi.world.icon.producer.WorldIconProducer; import amidst.mojangapi.world.oracle.BiomeDataOracle; -import amidst.mojangapi.world.oracle.EndIsland; -import amidst.mojangapi.world.oracle.EndIslandOracle; import amidst.mojangapi.world.oracle.SlimeChunkOracle; import amidst.mojangapi.world.oracle.WorldSpawnOracle; +import amidst.mojangapi.world.oracle.end.EndIslandList; +import amidst.mojangapi.world.oracle.end.EndIslandOracle; @Immutable public class FeatureKey { @@ -28,7 +28,7 @@ public class FeatureKey { public static final FeatureKey WORLD_SPAWN_ORACLE = make(); public static final FeatureKey NETHER_FORTRESS_LOCATION_CHECKER = make(); public static final FeatureKey END_CITY_LOCATION_CHECKER = make(); - public static final FeatureKey>> END_GATEWAY_PRODUCER = make(); + public static final FeatureKey> END_GATEWAY_PRODUCER = make(); public static final FeatureKey MINESHAFT_LOCATION_CHECKER = make(); public static final FeatureKey STRONGHOLD_PRODUCER = make(); public static final FeatureKey VILLAGE_LOCATION_CHECKER = make(); diff --git a/src/test/java/amidst/mojangapi/world/testworld/DefaultTestWorldDirectoryDeclaration.java b/src/test/java/amidst/mojangapi/world/testworld/DefaultTestWorldDirectoryDeclaration.java index 21ebb53bb..78afc2e15 100644 --- a/src/test/java/amidst/mojangapi/world/testworld/DefaultTestWorldDirectoryDeclaration.java +++ b/src/test/java/amidst/mojangapi/world/testworld/DefaultTestWorldDirectoryDeclaration.java @@ -166,7 +166,7 @@ private Function endCityExtractor(DefaultWorld return world -> CoordinatesCollectionJson.extractWorldIcons( world.getEndCityProducer(), worldIconType.getLabel(), - corner -> world.getEndIslandOracle().getAt(corner), + corner -> world.getEndIslandOracle().getLargeIslandsAt(corner), END_FRAGMENTS_AROUND_ORIGIN, MINIMAL_NUMBER_OF_COORDINATES); } diff --git a/src/test/java/amidst/mojangapi/world/testworld/storage/json/EndIslandsJson.java b/src/test/java/amidst/mojangapi/world/testworld/storage/json/EndIslandsJson.java index 557fcd81e..7c6b3f1dd 100644 --- a/src/test/java/amidst/mojangapi/world/testworld/storage/json/EndIslandsJson.java +++ b/src/test/java/amidst/mojangapi/world/testworld/storage/json/EndIslandsJson.java @@ -1,6 +1,5 @@ package amidst.mojangapi.world.testworld.storage.json; -import java.util.List; import java.util.SortedMap; import java.util.TreeMap; @@ -8,25 +7,25 @@ import amidst.documentation.Immutable; import amidst.mojangapi.mocking.FragmentCornerWalker; import amidst.mojangapi.world.coordinates.CoordinatesInWorld; -import amidst.mojangapi.world.oracle.EndIsland; -import amidst.mojangapi.world.oracle.EndIslandOracle; +import amidst.mojangapi.world.oracle.end.EndIslandList; +import amidst.mojangapi.world.oracle.end.EndIslandOracle; @Immutable public class EndIslandsJson { public static EndIslandsJson extract(EndIslandOracle oracle, int fragmentsAroundOrigin) { - SortedMap> result = new TreeMap<>(); + SortedMap result = new TreeMap<>(); FragmentCornerWalker.walkFragmentsAroundOrigin(fragmentsAroundOrigin).walk( corner -> result.put(corner, oracle.getAt(corner))); return new EndIslandsJson(result); } - private volatile SortedMap> endIslands; + private volatile SortedMap endIslands; @GsonConstructor public EndIslandsJson() { } - public EndIslandsJson(SortedMap> endIslands) { + public EndIslandsJson(SortedMap endIslands) { this.endIslands = endIslands; } From 97099d6239a6adc2c483f899e1916e8ddbe65184 Mon Sep 17 00:00:00 2001 From: burgerguy Date: Tue, 30 Jun 2020 23:33:24 -0400 Subject: [PATCH 07/17] Fix end gateways for 1.16, make possible end gateways more accurate, add small end islands. --- .../colorprovider/TheEndColorProvider.java | 92 ++--- .../icon/producer/EndGatewayProducer.java | 314 ++++++++++-------- .../world/oracle/end/EndIslandOracle.java | 123 ++----- .../world/oracle/end/SmallEndIsland.java | 51 ++- .../DefaultVersionFeatures.java | 2 +- 5 files changed, 320 insertions(+), 262 deletions(-) diff --git a/src/main/java/amidst/fragment/colorprovider/TheEndColorProvider.java b/src/main/java/amidst/fragment/colorprovider/TheEndColorProvider.java index 4ad112d1e..ee94d27bc 100644 --- a/src/main/java/amidst/fragment/colorprovider/TheEndColorProvider.java +++ b/src/main/java/amidst/fragment/colorprovider/TheEndColorProvider.java @@ -1,20 +1,16 @@ package amidst.fragment.colorprovider; import java.awt.image.BufferedImage; +import java.util.List; import amidst.ResourceLoader; import amidst.documentation.ThreadSafe; import amidst.fragment.Fragment; import amidst.mojangapi.minecraftinterface.RecognisedVersion; import amidst.mojangapi.world.Dimension; -import amidst.mojangapi.world.coordinates.CoordinateUtils; -import amidst.mojangapi.world.coordinates.CoordinatesInWorld; -import amidst.mojangapi.world.coordinates.Resolution; import amidst.mojangapi.world.oracle.end.EndIslandList; -import amidst.mojangapi.world.oracle.end.EndIslandOracle; import amidst.mojangapi.world.oracle.end.LargeEndIsland; import amidst.mojangapi.world.oracle.end.SmallEndIsland; -import amidst.mojangapi.world.versionfeatures.DefaultBiomes; @ThreadSafe public class TheEndColorProvider implements ColorProvider { @@ -44,8 +40,8 @@ public int getColorAt(Dimension dimension, Fragment fragment, long cornerX, long long xAsQuarter = cornerX + x; long yAsQuarter = cornerY + y; return getColorAt( - (int) (xAsQuarter << 2), - (int) (yAsQuarter << 2), + xAsQuarter << 2, + yAsQuarter << 2, xAsQuarter >> 2, yAsQuarter >> 2, (int) (x % TEXTURES_WIDTH), @@ -54,36 +50,25 @@ public int getColorAt(Dimension dimension, Fragment fragment, long cornerX, long } private int getColorAt( - int x, - int y, + long x, + long y, long chunkX, long chunkY, int textureX, int textureY, EndIslandList endIslands) { - // small islands - if (RecognisedVersion.isNewerOrEqualTo(version, RecognisedVersion._1_13)) { - if(EndIslandOracle.getBiomeAtBlock(x, y, endIslands.getLargeIslands()) == DefaultBiomes.theEndLow) { - for(SmallEndIsland smallIsland : endIslands.getSmallIslands()) { - if(new CoordinatesInWorld(x, y).getDistance(smallIsland.getX(), smallIsland.getY()) <= smallIsland.getSize()) { - return getEndStoneTextureAt(textureX, textureY); - } - } - } - } - - // Determine whether this float maxInfluence = getMaxInfluence(x, y, endIslands); if (maxInfluence >= INFLUENCE_FADE_START) { // Draw endstone island return getEndStoneTextureAt(textureX, textureY); } else { - return getFadingColorAt(chunkX, chunkY, textureX, textureY, maxInfluence); + // Draw fade and small islands + return getOuterColorAt(x, y, chunkX, chunkY, textureX, textureY, maxInfluence, endIslands); } } - private float getMaxInfluence(int x, int y, EndIslandList endIslands) { + private float getMaxInfluence(long x, long y, EndIslandList endIslands) { float result = -100.0f; for (LargeEndIsland island : endIslands.getLargeIslands()) { float influence = island.influenceAtBlock(x, y); @@ -94,26 +79,59 @@ private float getMaxInfluence(int x, int y, EndIslandList endIslands) { return result; } - private int getFadingColorAt(long chunkX, long chunkY, int textureX, int textureY, float maxInfluence) { + private int getOuterColorAt( + long x, + long y, + long chunkX, + long chunkY, + int textureX, + int textureY, + float maxInfluence, + EndIslandList endIslands) { int result = VOID_TRANSPARENT_BLACK; - if (showRockyShores(chunkX, chunkY)) { + if (showOldRockyShores(chunkX, chunkY)) { result = getRockyShoresTextureAt(textureX, textureY); } + // small islands + if (RecognisedVersion.isNewerOrEqualTo(version, RecognisedVersion._1_13)) { + // Small islands can leak into other biomes if they spawn close enough, so we want to set this to when large islands start fading so they merge smoothly + if(maxInfluence <= INFLUENCE_FADE_START) { + result = getSmallIslandSSAAPixel(x, y, textureX, textureY, endIslands.getSmallIslands()); + } + } + if (maxInfluence > INFLUENCE_FADE_FINISH) { // Fade out the endstone - this is the edge of an island - int pixelAlpha = result >>> 24; - int fadingIslandAlpha = getFadingIslandAlpha(maxInfluence); - if (fadingIslandAlpha > pixelAlpha) { - // favor the island pixel instead of the rocky shores pixel - // (Should look perfect without needing to blend, because - // rocky shore is still endstone texture) - return getFadedEndStoneTextureAt(textureX, textureY, fadingIslandAlpha); - } + int pixelAlpha = (result >>> 24) + getFadingIslandAlpha(maxInfluence); + // Add alphas together to blend + return getFadedEndStoneTextureAt(textureX, textureY, pixelAlpha); } return result; } + + private static final double ALPHA_INCREMENT = 63.75d; + private static final int[] NEIGHBORING_PIXEL_TABLE = { + 0, 0, + 1, 0, + 0, 1, + 1, 1 + }; + + // Anti-aliased pixel through taking 4 samples and blending them + private int getSmallIslandSSAAPixel(long x, long y, int textureX, int textureY, List smallIslands) { + double alpha = 0; + for(SmallEndIsland smallIsland : smallIslands) { + for(int i = 0; i <= 3; i++) { + if(smallIsland.isOnIsland(x + NEIGHBORING_PIXEL_TABLE[i], y + NEIGHBORING_PIXEL_TABLE[i + 1])) { + alpha += ALPHA_INCREMENT; + } + } + } + + return alpha == 0 ? VOID_TRANSPARENT_BLACK : getFadedEndStoneTextureAt(textureX, textureY, (int) alpha); + } private int getFadingIslandAlpha(float maxInfluence) { return 255 - (int) (255 * (INFLUENCE_FADE_START - maxInfluence) / INFLUENCE_FADE_RANGE); @@ -122,8 +140,8 @@ private int getFadingIslandAlpha(float maxInfluence) { /** * Determine whether to show the rocky shores texture. */ - private boolean showRockyShores(long chunkX, long chunkY) { - return (chunkX * chunkX + chunkY * chunkY) > 4096 && RecognisedVersion.isOlder(version, RecognisedVersion._1_13); + private boolean showOldRockyShores(long chunkX, long chunkY) { + return RecognisedVersion.isOlder(version, RecognisedVersion._1_13) && (chunkX * chunkX + chunkY * chunkY) > 4096L; } private int getEndStoneTextureAt(int textureX, int textureY) { @@ -136,13 +154,13 @@ private int getEndStoneTextureAt(int textureX, int textureY) { * state depends on the order chunks are created/explored in. This makes me * sad :( Let's use a symbolic texture, since we can't plot them properly. * - * EDIT: This isn't true past 1.13 I think. + * EDIT: This isn't true past 1.13, they can be generated from the seed. */ private int getRockyShoresTextureAt(int textureX, int textureY) { return TEXTURES.getRGB(textureX, textureY + TEXTURES_HEIGHT); } private int getFadedEndStoneTextureAt(int textureX, int textureY, int alpha) { - return (getEndStoneTextureAt(textureX, textureY) & 0x00FFFFFF) | (alpha << 24); + return (getEndStoneTextureAt(textureX, textureY) & 0x00FFFFFF) | (Math.min(alpha, 0xFF) << 24); } } diff --git a/src/main/java/amidst/mojangapi/world/icon/producer/EndGatewayProducer.java b/src/main/java/amidst/mojangapi/world/icon/producer/EndGatewayProducer.java index 0dffe3488..aa5960372 100644 --- a/src/main/java/amidst/mojangapi/world/icon/producer/EndGatewayProducer.java +++ b/src/main/java/amidst/mojangapi/world/icon/producer/EndGatewayProducer.java @@ -1,8 +1,8 @@ package amidst.mojangapi.world.icon.producer; import java.util.ArrayList; +import java.util.Comparator; import java.util.List; -import java.util.Map.Entry; import java.util.function.Consumer; import amidst.documentation.ThreadSafe; @@ -44,13 +44,13 @@ public class EndGatewayProducer extends WorldIconProducer { /** * Used as a cache only for the spawn gateways. */ - //private final EndSpawnGatewayProducer spawnProducer; + private final EndSpawnGatewayProducer spawnProducer; private final long seed; private final int featureIndex; private final int generationStage; public EndGatewayProducer(long seed, int featureIndex, int generationStage, EndIslandOracle oracle) { - //this.spawnProducer = new EndSpawnGatewayProducer(oracle); + this.spawnProducer = new EndSpawnGatewayProducer(oracle); this.seed = seed; this.featureIndex = featureIndex; this.generationStage = generationStage; @@ -64,7 +64,7 @@ public void produce(CoordinatesInWorld corner, Consumer consumer, End generateAt(corner, consumer, endIslands, xRelativeToFragment, yRelativeToFragment); } } - //spawnProducer.produce(corner, consumer, null); + spawnProducer.produce(corner, consumer, null); } private void generateAt( @@ -115,7 +115,7 @@ public CoordinatesInWorld tryGetValidLocationFromChunk(long chunkX, long chunkY, } else { // If this check fails, there's a very small chance that it landed on a small island for(SmallEndIsland smallIsland : endIslands.getSmallIslands()) { - if((int) coordinates.getDistance(smallIsland.getX(), smallIsland.getY()) <= smallIsland.getSize()) { + if(smallIsland.isOnIsland(gatewayX, gatewayY)) { return coordinates; } } @@ -129,136 +129,178 @@ public CoordinatesInWorld tryGetValidLocationFromChunk(long chunkX, long chunkY, return null; } -// private static class EndSpawnGatewayProducer extends CachedWorldIconProducer { -// private static final int NUMBER_OF_SPAWN_GATEWAYS = 20; -// -// private final EndIslandOracle oracle; -// -// public EndSpawnGatewayProducer(EndIslandOracle oracle) { -// this.oracle = oracle; -// } -// -// @Override -// protected List doCreateCache() { -// List iconList = new ArrayList(); -// for(int i = 0; i < NUMBER_OF_SPAWN_GATEWAYS; i++) { -// // Generate inner WorldIcon -// int x = floor(96.0D * Math.cos(2.0D * (-Math.PI + 0.15707963267948966D * (double) i))); -// int y = floor(96.0D * Math.sin(2.0D * (-Math.PI + 0.15707963267948966D * (double) i))); -// CoordinatesInWorld possibleCoordinates = new CoordinatesInWorld(x, y); -// iconList.add(new WorldIcon( -// possibleCoordinates, -// POSSIBLE_END_GATEWAY.getLabel(), -// POSSIBLE_END_GATEWAY.getImage(), -// Dimension.END, -// false -// ) -// ); -// -// // Generate outer WorldIcon -// // Normalize the coordinates the same way MC would -// double doubleX = 0; -// double doubleY = 0; -// double d0 = (double) ((float) (Math.sqrt((double) x * (double) x + (double) y * (double) y))); // There are this many casts in the MC code -// if (d0 >= 1.0E-4D) { -// doubleX = (double) x / d0; -// doubleY = (double) y / d0; -// } -// // Scale the coordinates -// CoordinatesInWorld coordinates = new CoordinatesInWorld((long) (doubleX * 1024.0D), (long) (doubleY * 1024.0D)); -// -// // Check for closest land along vector -// for (int j = 16; !isChunkIslandlessSlow(coordinates) && j-- > 0; coordinates = coordinates.add((long) (doubleX * -16.0D), (long) (doubleY * -16.0D))) { -// } -// -// for (int k = 16; isChunkIslandlessSlow(coordinates) && k-- > 0; coordinates = coordinates.add((long) (doubleX * 16.0D), (long) (doubleY * 16.0D))) { -// } -// -// // Mess with the coords a bit more -// coordinates = findSpawnpoint(coordinates, true); -// coordinates = findHighestBlock(coordinates, 16); -// -// iconList.add(new WorldIcon( -// coordinates, -// POSSIBLE_END_GATEWAY.getLabel(), -// POSSIBLE_END_GATEWAY.getImage(), -// Dimension.END, -// false -// ) -// ); -// } -// return iconList; -// } -// -// /* -// * This number is supposed to sort of guess whether end -// * stone might be at a particular location. -// */ -// private static final float ISLAND_INFLUENCE_THRESHOLD = -20.0F; -// -// @SuppressWarnings("unused") -// private boolean isChunkIslandlessFast(CoordinatesInWorld blockCoords) { -// for(LargeEndIsland island : oracle.getLargeIslandsAt(blockCoords)) { -// if(island.influenceAtChunk(blockCoords.getX() >> 4, blockCoords.getY() >> 4) >= ISLAND_INFLUENCE_THRESHOLD) { -// return false; -// } -// } -// return true; -// } -// -// private boolean isChunkIslandlessSlow(CoordinatesInWorld blockCoords) { -// EndIslandList endIslands = oracle.getAt(blockCoords); -// for(LargeEndIsland island : endIslands.getLargeIslands()) { -// for (long x = blockCoords.getX() & -16; x < (blockCoords.getX() | 15); x++) { -// for (long y = blockCoords.getY() & -16; y < (blockCoords.getY() | 15); y++) { -// if(island.influenceAtBlock(x, y) >= ISLAND_INFLUENCE_THRESHOLD) { -// return false; //FIXME -// } -// } -// } -// } -// return true; -// } -// -// private CoordinatesInWorld findSpawnpoint(CoordinatesInWorld blockCoords, boolean guaranteeEndStone) { -// for (long y = blockCoords.getY() & -16; y < (blockCoords.getY() | 15); y++) { -// for (long x = blockCoords.getX() & -16; x < (blockCoords.getX() | 15); x++) { -// for (LargeEndIsland island : oracle.getAt(blockCoords)) {//FIXME -// if (island.influenceAtBlock(x, y) >= (guaranteeEndStone ? 0.0F : ISLAND_INFLUENCE_THRESHOLD)) { -// return new CoordinatesInWorld(x, y); -// } -// } -// } -// } -// return blockCoords; -// } -// -// private CoordinatesInWorld findHighestBlock(CoordinatesInWorld blockCoords, int radius) { -// return findHighestBlock(blockCoords, -radius, -radius, radius, radius); -// } -// -// private CoordinatesInWorld findHighestBlock(CoordinatesInWorld blockCoords, int startX, int startY, int endX, int endY) { -// float highestInfluence = -100.0F; -// long highestX = blockCoords.getX(); -// long highestY = blockCoords.getY(); -// -// for (long x = blockCoords.getX() + startX; x <= blockCoords.getX() + endX; ++x) { -// for (long y = blockCoords.getY() + startY; y <= blockCoords.getY() + endY; ++y) { -// float coordInfluence = oracle.getInfluenceAtBlock(x, y);//FIXME -// if(coordInfluence > highestInfluence) { -// highestInfluence = coordInfluence; -// highestX = x; -// highestY = y; -// } -// } -// } -// return new CoordinatesInWorld(highestX, highestY); -// } -// -// private static int floor(double value) { -// int i = (int) value; -// return value < (double) i ? i - 1 : i; -// } -// } + private static class EndSpawnGatewayProducer extends CachedWorldIconProducer { + private static final int NUMBER_OF_SPAWN_GATEWAYS = 20; + + private final EndIslandOracle oracle; + + public EndSpawnGatewayProducer(EndIslandOracle oracle) { + this.oracle = oracle; + } + + // I'm sorry for writing this. It's slow, ambiguous, and inaccurate. + // It's the best we have though, and I'm done with messing with this + // method. + @Override + protected List doCreateCache() { + List iconList = new ArrayList(); + for(int i = 0; i < NUMBER_OF_SPAWN_GATEWAYS; i++) { + // Generate inner WorldIcon + int x = floor(96.0D * Math.cos(2.0D * (-Math.PI + 0.15707963267948966D * (double) i))); + int y = floor(96.0D * Math.sin(2.0D * (-Math.PI + 0.15707963267948966D * (double) i))); + CoordinatesInWorld possibleCoordinates = new CoordinatesInWorld(x, y); + iconList.add(new WorldIcon( + possibleCoordinates, + POSSIBLE_END_GATEWAY.getLabel(), + POSSIBLE_END_GATEWAY.getImage(), + Dimension.END, + false + ) + ); + + // Generate outer WorldIcon + // Normalize the coordinates the same way MC would + double doubleX = 0; + double doubleY = 0; + double d0 = (double) ((float) (Math.sqrt((double) x * (double) x + (double) y * (double) y))); // There are this many casts in the MC code + if (d0 >= 1.0E-4D) { + doubleX = (double) x / d0; + doubleY = (double) y / d0; + } + // Scale the coordinates + CoordinatesInWorld coordinates = new CoordinatesInWorld((long) (doubleX * 1024.0D), (long) (doubleY * 1024.0D)); + + // Check for closest land along vector + for (int j = 16; !isChunkIslandlessSlow(coordinates) && j-- > 0; coordinates = coordinates.add((long) (doubleX * -16.0D), (long) (doubleY * -16.0D))) { + } + + for (int k = 16; isChunkIslandlessSlow(coordinates) && k-- > 0; coordinates = coordinates.add((long) (doubleX * 16.0D), (long) (doubleY * 16.0D))) { + } + + // Mess with the coords a bit more + coordinates = findSpawnpoint(coordinates, true); + coordinates = findHighestBlock(coordinates, 16); + + iconList.add(new WorldIcon( + coordinates, + POSSIBLE_END_GATEWAY.getLabel(), + POSSIBLE_END_GATEWAY.getImage(), + Dimension.END, + false + ) + ); + } + return iconList; + } + + /* + * This number is supposed to sort of guess whether end + * stone might be at a particular location. + */ + private static final float ISLAND_INFLUENCE_THRESHOLD = -20.0F; + + @SuppressWarnings("unused") + private boolean isChunkIslandlessFast(CoordinatesInWorld blockCoords) { + for(LargeEndIsland island : oracle.getLargeIslandsAt(blockCoords)) { + if(island.influenceAtChunk(blockCoords.getX() >> 4, blockCoords.getY() >> 4) >= ISLAND_INFLUENCE_THRESHOLD) { + return false; + } + } + return true; + } + + private boolean isChunkIslandlessSlow(CoordinatesInWorld blockCoords) { + EndIslandList endIslands = oracle.getAt(blockCoords); + // Small Islands + for(SmallEndIsland island : endIslands.getSmallIslands()) { + for (long x = blockCoords.getX() & -16; x < (blockCoords.getX() | 15); x++) { + for (long y = blockCoords.getY() & -16; y < (blockCoords.getY() | 15); y++) { + if(island.isOnIsland(x, y)) { + return false; + } + } + } + } + + // Large Islands + for(LargeEndIsland island : endIslands.getLargeIslands()) { + for (long x = blockCoords.getX() & -16; x < (blockCoords.getX() | 15); x++) { + for (long y = blockCoords.getY() & -16; y < (blockCoords.getY() | 15); y++) { + if(island.influenceAtBlock(x, y) >= ISLAND_INFLUENCE_THRESHOLD) { + return false; //FIXME + } + } + } + } + return true; + } + + private CoordinatesInWorld findSpawnpoint(CoordinatesInWorld blockCoords, boolean guaranteeEndStone) { + EndIslandList endIslands = oracle.getAt(blockCoords); + for (long y = blockCoords.getY() & -16; y < (blockCoords.getY() | 15); y++) { + for (long x = blockCoords.getX() & -16; x < (blockCoords.getX() | 15); x++) { + // We do the large end islands first because they tend to be not as high + for (LargeEndIsland island : endIslands.getLargeIslands()) { + if (island.influenceAtBlock(x, y) >= (guaranteeEndStone ? 0.0F : ISLAND_INFLUENCE_THRESHOLD)) { + return new CoordinatesInWorld(x, y); + } + } + // We want the lowest small end islands first, so we sort them. + List smallIslands = endIslands.getSmallIslands(); + smallIslands.sort(((Comparator)(e1, e2) -> Integer.compare(e1.getHeight(), e2.getHeight())).reversed()); + for (SmallEndIsland island : endIslands.getSmallIslands()) { + if (island.isOnIsland(x, y)) { + return new CoordinatesInWorld(x, y); + } + } + } + } + return blockCoords; + } + + private CoordinatesInWorld findHighestBlock(CoordinatesInWorld blockCoords, int radius) { + return findHighestBlock(blockCoords, -radius, -radius, radius, radius); + } + + private CoordinatesInWorld findHighestBlock(CoordinatesInWorld blockCoords, int startX, int startY, int endX, int endY) { + float highestInfluence = -100.0F; + int highestBlock = 0; + long highestX = blockCoords.getX(); + long highestY = blockCoords.getY(); + EndIslandList endIslands = oracle.getAt(blockCoords); + + for (long x = blockCoords.getX() + startX; x <= blockCoords.getX() + endX; ++x) { + for (long y = blockCoords.getY() + startY; y <= blockCoords.getY() + endY; ++y) { + float coordInfluence = EndIslandOracle.getInfluenceAtBlock(x, y, endIslands.getLargeIslands()); + if(coordInfluence > highestInfluence) { + highestInfluence = coordInfluence; + highestX = x; + highestY = y; + } + } + } + + for (long x = blockCoords.getX() + startX; x <= blockCoords.getX() + endX; ++x) { + for (long y = blockCoords.getY() + startY; y <= blockCoords.getY() + endY; ++y) { + for (SmallEndIsland island : endIslands.getSmallIslands()) { + if (island.isOnIsland(x, y)) { + int coordHeight = island.getHeight(); + if(coordHeight > highestBlock) { + highestBlock = coordHeight; + highestX = x; + highestY = y; + } + } + } + } + } + return new CoordinatesInWorld(highestX, highestY); + } + + private static int floor(double value) { + int i = (int) value; + return value < (double) i ? i - 1 : i; + } + } } diff --git a/src/main/java/amidst/mojangapi/world/oracle/end/EndIslandOracle.java b/src/main/java/amidst/mojangapi/world/oracle/end/EndIslandOracle.java index f4a93c819..1458da30a 100644 --- a/src/main/java/amidst/mojangapi/world/oracle/end/EndIslandOracle.java +++ b/src/main/java/amidst/mojangapi/world/oracle/end/EndIslandOracle.java @@ -34,7 +34,12 @@ private static SimplexNoise createNoiseFunction(long seed) { * Minecraft checks 12 chunks either side of a chunk when assessing island * influence. */ - private static final int SURROUNDING_CHUNKS = 12; + private static final int LARGE_ISLAND_SURROUNDING_CHUNKS = 12; + + /** + * add some padding for small islands on fragment borders. + */ + private static final int SMALL_ISLAND_SURROUNDING_CHUNKS = 1; /** * When cast to double, -0.9 will become -0.8999999761581421, which is why @@ -83,37 +88,6 @@ public static float getInfluenceAtBlock(long x, long y, List lar } return highestInfluence; } - - @Deprecated - private int getBiomeAtBlock(long x, long y) { - if (x * x + y * y <= 1048576L) { - return DefaultBiomes.theEnd; - } else { - float influence = getInfluenceAtBlock(x, y); - if (influence > 40.0F) { - return DefaultBiomes.theEndHigh; - } else if (influence >= 0.0F) { - return DefaultBiomes.theEndMedium; - } else { - return influence < -20.0F ? DefaultBiomes.theEndLow : DefaultBiomes.theEndBarren; - } - } - } - - @Deprecated - private float getInfluenceAtBlock(long x, long y) { - float highestInfluence = -100.0f; - - for (LargeEndIsland island : getLargeIslandsAt(new CoordinatesInWorld(x, y))) { - if (island instanceof LargeEndIsland) { - float tempInfluence = ((LargeEndIsland) island).influenceAtBlock(x, y); - if (tempInfluence > highestInfluence) { - highestInfluence = tempInfluence; - } - } - } - return highestInfluence; - } public EndIslandList getAt(CoordinatesInWorld corner) { int steps = Resolution.CHUNK.getStepsPerFragment(); @@ -155,8 +129,8 @@ private List findSurroundingLargeIslands( int chunksPerFragmentX, int chunksPerFragmentY) { List result = new ArrayList<>(); - for (int y = -SURROUNDING_CHUNKS; y <= chunksPerFragmentY + SURROUNDING_CHUNKS; y++) { - for (int x = -SURROUNDING_CHUNKS; x <= chunksPerFragmentX + SURROUNDING_CHUNKS; x++) { + for (int y = -LARGE_ISLAND_SURROUNDING_CHUNKS; y <= chunksPerFragmentY + LARGE_ISLAND_SURROUNDING_CHUNKS; y++) { + for (int x = -LARGE_ISLAND_SURROUNDING_CHUNKS; x <= chunksPerFragmentX + LARGE_ISLAND_SURROUNDING_CHUNKS; x++) { LargeEndIsland island = tryCreateLargeEndIsland(chunkX + x, chunkY + y); if (island != null) { result.add(island); @@ -219,53 +193,6 @@ private boolean isInRange(long x, long y, int d) { return false; return x * x + y * y <= d * d; } - - public List getSmallIslandsAt(CoordinatesInWorld corner) { - int steps = Resolution.CHUNK.getStepsPerFragment(); - return findSurroundingSmallIslands( - corner.getXAs(Resolution.CHUNK), - corner.getYAs(Resolution.CHUNK), - steps, - steps); - } - - @Deprecated - private List findSurroundingSmallIslands( - long chunkX, - long chunkY, - int chunksPerFragmentX, - int chunksPerFragmentY) { - List result = new ArrayList<>(); - for (int y = 0; y <= chunksPerFragmentY; y++) { - for (int x = 0; x <= chunksPerFragmentX; x++) { - addSmallIslandsInChunk(chunkX + x, chunkY + y, result); - } - } - return result; - } - - @Deprecated - private void addSmallIslandsInChunk(long chunkX, long chunkY, List islands) { - long blockX = chunkX << 4; - long blockY = chunkY << 4; - if (getBiomeAtBlock(blockX, blockY) == DefaultBiomes.theEndLow) { - ChunkRand rand = new ChunkRand(); - rand.setDecoratorSeed(seed, (int) blockX, (int) blockY, 0, 3, MCVersion.v1_13); - - if (rand.nextInt(14) == 0) { - long resultX = blockX + rand.nextInt(16); - int resultH = 55 + rand.nextInt(16); - long rexultY = blockY + rand.nextInt(16); - islands.add(new SmallEndIsland(resultX, rexultY, resultH, 4)); - if (rand.nextInt(4) == 0) { - resultX = blockX + rand.nextInt(16); - resultH = 55 + rand.nextInt(16); - rexultY = blockY + rand.nextInt(16); - islands.add(new SmallEndIsland(resultX, rexultY, resultH, 4)); - } - } - } - } private List findSurroundingSmallIslands( long chunkX, @@ -274,34 +201,56 @@ private List findSurroundingSmallIslands( int chunksPerFragmentY, List largeIslands) { List result = new ArrayList<>(); - for (int y = 0; y <= chunksPerFragmentY; y++) { - for (int x = 0; x <= chunksPerFragmentX; x++) { - addSmallIslandsInChunk(chunkX + x, chunkY + y, result, largeIslands); + for (int y = -SMALL_ISLAND_SURROUNDING_CHUNKS; y <= chunksPerFragmentY + SMALL_ISLAND_SURROUNDING_CHUNKS; y++) { + for (int x = -SMALL_ISLAND_SURROUNDING_CHUNKS; x <= chunksPerFragmentX + SMALL_ISLAND_SURROUNDING_CHUNKS; x++) { + List smallIslands = getSmallIslandsInChunk(chunkX + x, chunkY + y, largeIslands); + if(smallIslands != null) { + result.addAll(smallIslands); + } } } return result; } - private void addSmallIslandsInChunk(long chunkX, long chunkY, List smallIslands, List largeIslands) { + private List getSmallIslandsInChunk(long chunkX, long chunkY, List largeIslands) { long blockX = chunkX << 4; long blockY = chunkY << 4; if (getBiomeAtBlock(blockX, blockY, largeIslands) == DefaultBiomes.theEndLow) { ChunkRand rand = new ChunkRand(); rand.setDecoratorSeed(seed, (int) blockX, (int) blockY, 0, 0, MCVersion.v1_13); + List smallIslands = new ArrayList<>(); + if (rand.nextInt(14) == 0) { long resultX = blockX + rand.nextInt(16); int resultH = 55 + rand.nextInt(16); long rexultY = blockY + rand.nextInt(16); - smallIslands.add(new SmallEndIsland(resultX, rexultY, resultH, 4)); + smallIslands.add(new SmallEndIsland(resultX, rexultY, resultH)); if (rand.nextInt(4) == 0) { resultX = blockX + rand.nextInt(16); resultH = 55 + rand.nextInt(16); rexultY = blockY + rand.nextInt(16); - smallIslands.add(new SmallEndIsland(resultX, rexultY, resultH, 4)); + smallIslands.add(new SmallEndIsland(resultX, rexultY, resultH)); } } + + // mc calculates the sizes of the islands after their locations are generated + for(SmallEndIsland island : smallIslands) { + int size = rand.nextInt(3) + 4; + + island.setSize(size + 1); // the size gets padded with an extra block when generated in mc + + // we have to do this so the random gets set correctly for the next end island + float sizeFloat = (float) size; + + while(sizeFloat > 0.5F) { + sizeFloat = (float) ((double) sizeFloat - ((double) rand.nextInt(2) + 0.5D)); + } + } + + return smallIslands; } + return null; } } diff --git a/src/main/java/amidst/mojangapi/world/oracle/end/SmallEndIsland.java b/src/main/java/amidst/mojangapi/world/oracle/end/SmallEndIsland.java index e19ffdb65..2f07e807d 100644 --- a/src/main/java/amidst/mojangapi/world/oracle/end/SmallEndIsland.java +++ b/src/main/java/amidst/mojangapi/world/oracle/end/SmallEndIsland.java @@ -1,10 +1,13 @@ package amidst.mojangapi.world.oracle.end; +import java.awt.Point; +import java.util.Objects; + public class SmallEndIsland { private final long blockX; private final long blockY; private final int height; - private final int size; + private int size; protected SmallEndIsland(long blockX, long blockY, int height, int size) { this.blockX = blockX; @@ -12,6 +15,12 @@ protected SmallEndIsland(long blockX, long blockY, int height, int size) { this.height = height; this.size = size; } + + protected SmallEndIsland(long blockX, long blockY, int height) { + this.blockX = blockX; + this.blockY = blockY; + this.height = height; + } public long getX() { return blockX; @@ -24,8 +33,48 @@ public long getY() { public int getHeight() { return height; } + + public void setSize(int size) { + this.size = size; + } public int getSize() { return size; } + + public boolean isOnIsland(long x, long y) { + return Point.distanceSq(x, y, blockX, blockY) <= size * size; + } + + @Override + public int hashCode() { + return Objects.hash(blockX, blockY, height, size); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof SmallEndIsland)) { + return false; + } + SmallEndIsland other = (SmallEndIsland) obj; + if (blockX != other.blockX) { + return false; + } + if (blockY != other.blockY) { + return false; + } + if (height != other.height) { + return false; + } + if (size != other.size) { + return false; + } + return true; + } } diff --git a/src/main/java/amidst/mojangapi/world/versionfeatures/DefaultVersionFeatures.java b/src/main/java/amidst/mojangapi/world/versionfeatures/DefaultVersionFeatures.java index d90e99e9b..eba0261e6 100644 --- a/src/main/java/amidst/mojangapi/world/versionfeatures/DefaultVersionFeatures.java +++ b/src/main/java/amidst/mojangapi/world/versionfeatures/DefaultVersionFeatures.java @@ -184,7 +184,7 @@ VersionFeature.> builder() .init( new EndGatewayProducer(getWorldSeed(features), 0, 3, features.get(FeatureKey.END_ISLAND_ORACLE)) ).since(RecognisedVersion._20w06a, // TODO: confirm this version is correct; this changed after 1.15.2 and before 1.16 - new EndGatewayProducer(getWorldSeed(features), 1, 4, features.get(FeatureKey.END_ISLAND_ORACLE)) + new EndGatewayProducer(getWorldSeed(features), 13, 4, features.get(FeatureKey.END_ISLAND_ORACLE)) ) .construct() )) From f3be919991742b80282e3e148bc6ff606179e435 Mon Sep 17 00:00:00 2001 From: burgerguy Date: Tue, 30 Jun 2020 23:42:29 -0400 Subject: [PATCH 08/17] fix cursorinformationwidget for versions before 18w06a --- .../gui/main/viewer/widget/CursorInformationWidget.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/main/java/amidst/gui/main/viewer/widget/CursorInformationWidget.java b/src/main/java/amidst/gui/main/viewer/widget/CursorInformationWidget.java index 23aafe4ed..2004be839 100644 --- a/src/main/java/amidst/gui/main/viewer/widget/CursorInformationWidget.java +++ b/src/main/java/amidst/gui/main/viewer/widget/CursorInformationWidget.java @@ -17,6 +17,7 @@ import amidst.mojangapi.world.coordinates.CoordinatesInWorld; import amidst.mojangapi.world.coordinates.Resolution; import amidst.mojangapi.world.oracle.end.EndIslandOracle; +import amidst.mojangapi.world.versionfeatures.DefaultBiomes; import amidst.settings.Setting; @NotThreadSafe @@ -71,7 +72,13 @@ private String getBiomeNameAt(CoordinatesInWorld coordinates) { public String getEndBiomeNameAt(CoordinatesInWorld coordinates) { Fragment fragment = graph.getFragmentAt(coordinates); if (fragment != null && fragment.isLoaded()) { - return biomeList.getByIdOrNull(EndIslandOracle.getBiomeAtBlock(coordinates.getX(), coordinates.getY(), graph.getFragmentAt(coordinates).getLargeEndIslands())).getName(); + Biome biome = biomeList.getByIdOrNull(EndIslandOracle.getBiomeAtBlock(coordinates.getX(), coordinates.getY(), graph.getFragmentAt(coordinates).getLargeEndIslands())); + + if (biome != null) { + return biome.getName(); + } else { + return biomeList.getByIdOrNull(DefaultBiomes.theEnd).getName(); + } } return UNKNOWN_BIOME_NAME; } From 9c2348d17e7445ba46e03110e5f285e11bcfd0a0 Mon Sep 17 00:00:00 2001 From: burgerguy Date: Wed, 1 Jul 2020 16:57:51 -0400 Subject: [PATCH 09/17] remove unneeded //FIXME --- .../mojangapi/world/icon/producer/EndGatewayProducer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/amidst/mojangapi/world/icon/producer/EndGatewayProducer.java b/src/main/java/amidst/mojangapi/world/icon/producer/EndGatewayProducer.java index aa5960372..109d0ec69 100644 --- a/src/main/java/amidst/mojangapi/world/icon/producer/EndGatewayProducer.java +++ b/src/main/java/amidst/mojangapi/world/icon/producer/EndGatewayProducer.java @@ -227,7 +227,7 @@ private boolean isChunkIslandlessSlow(CoordinatesInWorld blockCoords) { for (long x = blockCoords.getX() & -16; x < (blockCoords.getX() | 15); x++) { for (long y = blockCoords.getY() & -16; y < (blockCoords.getY() | 15); y++) { if(island.influenceAtBlock(x, y) >= ISLAND_INFLUENCE_THRESHOLD) { - return false; //FIXME + return false; } } } From f4df81a81a6b22f6220fe9683d69b1a5ca2b20b7 Mon Sep 17 00:00:00 2001 From: burgerguy Date: Sun, 19 Jul 2020 23:14:56 -0400 Subject: [PATCH 10/17] Update pom.xml --- pom.xml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/pom.xml b/pom.xml index 4c01b52c4..b5d39a1a1 100644 --- a/pom.xml +++ b/pom.xml @@ -10,10 +10,6 @@ jitpack.io https://jitpack.io - - jitpack.io - https://jitpack.io - yyyy-MM-dd HH:mm From d2a05bf7db944368414c770acf9da398206431fd Mon Sep 17 00:00:00 2001 From: burgerguy Date: Fri, 24 Jul 2020 14:29:58 -0400 Subject: [PATCH 11/17] fix tests by only using large end islands --- .../colorprovider/TheEndColorProvider.java | 19 ++++--------- .../amidst/fragment/layer/LayerBuilder.java | 2 +- .../world/oracle/end/EndIslandOracle.java | 28 +++++++++++++------ .../DefaultVersionFeatures.java | 6 ++-- .../DefaultTestWorldDirectoryDeclaration.java | 14 +++++----- .../world/testworld/TestWorldEntryNames.java | 4 +-- .../io/TestWorldEntrySerializer.java | 6 ++-- ...andsJson.java => LargeEndIslandsJson.java} | 23 +++++++-------- 8 files changed, 52 insertions(+), 50 deletions(-) rename src/test/java/amidst/mojangapi/world/testworld/storage/json/{EndIslandsJson.java => LargeEndIslandsJson.java} (59%) diff --git a/src/main/java/amidst/fragment/colorprovider/TheEndColorProvider.java b/src/main/java/amidst/fragment/colorprovider/TheEndColorProvider.java index ee94d27bc..f910bd4ac 100644 --- a/src/main/java/amidst/fragment/colorprovider/TheEndColorProvider.java +++ b/src/main/java/amidst/fragment/colorprovider/TheEndColorProvider.java @@ -6,7 +6,6 @@ import amidst.ResourceLoader; import amidst.documentation.ThreadSafe; import amidst.fragment.Fragment; -import amidst.mojangapi.minecraftinterface.RecognisedVersion; import amidst.mojangapi.world.Dimension; import amidst.mojangapi.world.oracle.end.EndIslandList; import amidst.mojangapi.world.oracle.end.LargeEndIsland; @@ -28,12 +27,6 @@ public class TheEndColorProvider implements ColorProvider { private static final float INFLUENCE_FADE_START = 0; private static final float INFLUENCE_FADE_FINISH = -8; private static final float INFLUENCE_FADE_RANGE = INFLUENCE_FADE_START - INFLUENCE_FADE_FINISH; - - private final RecognisedVersion version; - - public TheEndColorProvider(RecognisedVersion version) { - this.version = version; - } @Override public int getColorAt(Dimension dimension, Fragment fragment, long cornerX, long cornerY, int x, int y) { @@ -90,16 +83,14 @@ private int getOuterColorAt( EndIslandList endIslands) { int result = VOID_TRANSPARENT_BLACK; - if (showOldRockyShores(chunkX, chunkY)) { - result = getRockyShoresTextureAt(textureX, textureY); - } - - // small islands - if (RecognisedVersion.isNewerOrEqualTo(version, RecognisedVersion._1_13)) { + // The small islands list is null if the version doesn't support them + if (endIslands.getSmallIslands() != null) { // Small islands can leak into other biomes if they spawn close enough, so we want to set this to when large islands start fading so they merge smoothly if(maxInfluence <= INFLUENCE_FADE_START) { result = getSmallIslandSSAAPixel(x, y, textureX, textureY, endIslands.getSmallIslands()); } + } else if (showOldRockyShores(chunkX, chunkY)) { + result = getRockyShoresTextureAt(textureX, textureY); } if (maxInfluence > INFLUENCE_FADE_FINISH) { @@ -141,7 +132,7 @@ private int getFadingIslandAlpha(float maxInfluence) { * Determine whether to show the rocky shores texture. */ private boolean showOldRockyShores(long chunkX, long chunkY) { - return RecognisedVersion.isOlder(version, RecognisedVersion._1_13) && (chunkX * chunkX + chunkY * chunkY) > 4096L; + return (chunkX * chunkX + chunkY * chunkY) > 4096L; } private int getEndStoneTextureAt(int textureX, int textureY) { diff --git a/src/main/java/amidst/fragment/layer/LayerBuilder.java b/src/main/java/amidst/fragment/layer/LayerBuilder.java index c96dcb467..0a1ba16f8 100644 --- a/src/main/java/amidst/fragment/layer/LayerBuilder.java +++ b/src/main/java/amidst/fragment/layer/LayerBuilder.java @@ -133,7 +133,7 @@ private Iterable createLoaders( new AlphaInitializer( declarations.get(LayerIds.ALPHA), settings.fragmentFading), new BiomeDataLoader( declarations.get(LayerIds.BIOME_DATA), world.getBiomeDataOracle()), new EndIslandsLoader( declarations.get(LayerIds.END_ISLANDS), world.getEndIslandOracle()), - new ImageLoader( declarations.get(LayerIds.BACKGROUND), Resolution.QUARTER, new BackgroundColorProvider(new BiomeColorProvider(biomeSelection, settings.biomeProfileSelection), new TheEndColorProvider(world.getRecognisedVersion()))), + new ImageLoader( declarations.get(LayerIds.BACKGROUND), Resolution.QUARTER, new BackgroundColorProvider(new BiomeColorProvider(biomeSelection, settings.biomeProfileSelection), new TheEndColorProvider())), new ImageLoader( declarations.get(LayerIds.SLIME), Resolution.CHUNK, new SlimeColorProvider(world.getSlimeChunkOracle())), new WorldIconLoader<>(declarations.get(LayerIds.SPAWN), world.getSpawnProducer()), new WorldIconLoader<>(declarations.get(LayerIds.STRONGHOLD), world.getStrongholdProducer()), diff --git a/src/main/java/amidst/mojangapi/world/oracle/end/EndIslandOracle.java b/src/main/java/amidst/mojangapi/world/oracle/end/EndIslandOracle.java index 1458da30a..fd9c860c1 100644 --- a/src/main/java/amidst/mojangapi/world/oracle/end/EndIslandOracle.java +++ b/src/main/java/amidst/mojangapi/world/oracle/end/EndIslandOracle.java @@ -4,6 +4,7 @@ import java.util.List; import amidst.documentation.ThreadSafe; +import amidst.mojangapi.minecraftinterface.RecognisedVersion; import amidst.mojangapi.world.coordinates.CoordinatesInWorld; import amidst.mojangapi.world.coordinates.Resolution; import amidst.mojangapi.world.oracle.SimplexNoise; @@ -15,8 +16,8 @@ @ThreadSafe public class EndIslandOracle { - public static EndIslandOracle from(long seed) { - return new EndIslandOracle(createNoiseFunction(seed), seed); + public static EndIslandOracle from(long seed, RecognisedVersion recognisedVersion) { + return new EndIslandOracle(createNoiseFunction(seed), seed, recognisedVersion); } /** @@ -54,10 +55,12 @@ private static SimplexNoise createNoiseFunction(long seed) { private final SimplexNoise noiseFunction; private final long seed; + private final RecognisedVersion recognisedVersion; - public EndIslandOracle(SimplexNoise noiseFunction, long seed) { + public EndIslandOracle(SimplexNoise noiseFunction, long seed, RecognisedVersion recognisedVersion) { this.noiseFunction = noiseFunction; this.seed = seed; + this.recognisedVersion = recognisedVersion; } public static int getBiomeAtBlock(long x, long y, List largeIslands) { @@ -200,12 +203,15 @@ private List findSurroundingSmallIslands( int chunksPerFragmentX, int chunksPerFragmentY, List largeIslands) { - List result = new ArrayList<>(); - for (int y = -SMALL_ISLAND_SURROUNDING_CHUNKS; y <= chunksPerFragmentY + SMALL_ISLAND_SURROUNDING_CHUNKS; y++) { - for (int x = -SMALL_ISLAND_SURROUNDING_CHUNKS; x <= chunksPerFragmentX + SMALL_ISLAND_SURROUNDING_CHUNKS; x++) { - List smallIslands = getSmallIslandsInChunk(chunkX + x, chunkY + y, largeIslands); - if(smallIslands != null) { - result.addAll(smallIslands); + List result = null; + if(RecognisedVersion.isNewerOrEqualTo(recognisedVersion, RecognisedVersion._1_13)) { // TODO: need confirmation on this version + result = new ArrayList<>(); + for (int y = -SMALL_ISLAND_SURROUNDING_CHUNKS; y <= chunksPerFragmentY + SMALL_ISLAND_SURROUNDING_CHUNKS; y++) { + for (int x = -SMALL_ISLAND_SURROUNDING_CHUNKS; x <= chunksPerFragmentX + SMALL_ISLAND_SURROUNDING_CHUNKS; x++) { + List smallIslands = getSmallIslandsInChunk(chunkX + x, chunkY + y, largeIslands); + if(smallIslands != null) { + result.addAll(smallIslands); + } } } } @@ -253,4 +259,8 @@ private List getSmallIslandsInChunk(long chunkX, long chunkY, L return null; } + public RecognisedVersion getRecognisedVersion() { + return recognisedVersion; + } + } diff --git a/src/main/java/amidst/mojangapi/world/versionfeatures/DefaultVersionFeatures.java b/src/main/java/amidst/mojangapi/world/versionfeatures/DefaultVersionFeatures.java index eba0261e6..867c3ab7b 100644 --- a/src/main/java/amidst/mojangapi/world/versionfeatures/DefaultVersionFeatures.java +++ b/src/main/java/amidst/mojangapi/world/versionfeatures/DefaultVersionFeatures.java @@ -121,9 +121,9 @@ public static VersionFeatures.Builder builder(WorldOptions worldOptions, Minecra ).construct()) .with(FeatureKey.BIOME_LIST, DefaultBiomes.DEFAULT_BIOMES) - .with(FeatureKey.END_ISLAND_ORACLE, VersionFeature.bind(features -> - VersionFeature.constant(EndIslandOracle.from(getWorldSeed(features))) - )) + .with(FeatureKey.END_ISLAND_ORACLE, (recognisedVersion, features) -> + EndIslandOracle.from(getWorldSeed(features), recognisedVersion) + ) .with(FeatureKey.SLIME_CHUNK_ORACLE, VersionFeature.bind(features -> VersionFeature.constant(new SlimeChunkOracle(getWorldSeed(features))) diff --git a/src/test/java/amidst/mojangapi/world/testworld/DefaultTestWorldDirectoryDeclaration.java b/src/test/java/amidst/mojangapi/world/testworld/DefaultTestWorldDirectoryDeclaration.java index 78afc2e15..cc58d34a8 100644 --- a/src/test/java/amidst/mojangapi/world/testworld/DefaultTestWorldDirectoryDeclaration.java +++ b/src/test/java/amidst/mojangapi/world/testworld/DefaultTestWorldDirectoryDeclaration.java @@ -10,7 +10,7 @@ import amidst.mojangapi.world.testworld.io.TestWorldEntrySerializer; import amidst.mojangapi.world.testworld.storage.json.BiomeDataJson; import amidst.mojangapi.world.testworld.storage.json.CoordinatesCollectionJson; -import amidst.mojangapi.world.testworld.storage.json.EndIslandsJson; +import amidst.mojangapi.world.testworld.storage.json.LargeEndIslandsJson; import amidst.mojangapi.world.testworld.storage.json.SlimeChunksJson; import amidst.mojangapi.world.testworld.storage.json.WorldMetadataJson; @@ -51,11 +51,11 @@ private TestWorldDirectoryDeclaration createDeclaration() { .deserializer(TestWorldEntrySerializer::readBiomeData) .extractor(biomeDataExtractor()) .skipEqualityCheck() - .entry(TestWorldEntryNames.END_ISLANDS, EndIslandsJson.class) + .entry(TestWorldEntryNames.LARGE_END_ISLANDS, LargeEndIslandsJson.class) .serializer(TestWorldEntrySerializer::writeJson) - .deserializer(TestWorldEntrySerializer::readEndIslands) - .extractor(endIslandExtractor()) - .equalityChecker(EndIslandsJson::equals) + .deserializer(TestWorldEntrySerializer::readLargeEndIslands) + .extractor(largeEndIslandExtractor()) + .equalityChecker(LargeEndIslandsJson::equals) .entry(TestWorldEntryNames.SLIME_CHUNKS, SlimeChunksJson.class) .serializer(TestWorldEntrySerializer::writeJson) .deserializer(TestWorldEntrySerializer::readSlimeChunks) @@ -147,8 +147,8 @@ private Function biomeDataExtractor() { }; } - private Function endIslandExtractor() { - return world -> EndIslandsJson.extract(world.getEndIslandOracle(), END_FRAGMENTS_AROUND_ORIGIN); + private Function largeEndIslandExtractor() { + return world -> LargeEndIslandsJson.extract(world.getEndIslandOracle(), END_FRAGMENTS_AROUND_ORIGIN); } private Function worldIconExtractor( diff --git a/src/test/java/amidst/mojangapi/world/testworld/TestWorldEntryNames.java b/src/test/java/amidst/mojangapi/world/testworld/TestWorldEntryNames.java index 3c23ec71f..06aadc466 100644 --- a/src/test/java/amidst/mojangapi/world/testworld/TestWorldEntryNames.java +++ b/src/test/java/amidst/mojangapi/world/testworld/TestWorldEntryNames.java @@ -7,7 +7,7 @@ public class TestWorldEntryNames { public static final String METADATA = "metadata"; public static final String QUARTER_RESOLUTION_BIOME_DATA = "quarter-resolution-biome-data"; public static final String FULL_RESOLUTION_BIOME_DATA = "full-resolution-biome-data"; - public static final String END_ISLANDS = "end-islands"; + public static final String LARGE_END_ISLANDS = "end-islands"; public static final String SLIME_CHUNKS = "slime-chunks"; public static final String SPAWN = "spawn"; public static final String STRONGHOLDS = "strongholds"; @@ -29,7 +29,7 @@ public class TestWorldEntryNames { TestWorldEntryNames.METADATA, TestWorldEntryNames.QUARTER_RESOLUTION_BIOME_DATA, TestWorldEntryNames.FULL_RESOLUTION_BIOME_DATA, - TestWorldEntryNames.END_ISLANDS, + TestWorldEntryNames.LARGE_END_ISLANDS, TestWorldEntryNames.SLIME_CHUNKS, TestWorldEntryNames.SPAWN, TestWorldEntryNames.STRONGHOLDS, diff --git a/src/test/java/amidst/mojangapi/world/testworld/io/TestWorldEntrySerializer.java b/src/test/java/amidst/mojangapi/world/testworld/io/TestWorldEntrySerializer.java index d7d25235b..97f26515c 100644 --- a/src/test/java/amidst/mojangapi/world/testworld/io/TestWorldEntrySerializer.java +++ b/src/test/java/amidst/mojangapi/world/testworld/io/TestWorldEntrySerializer.java @@ -14,7 +14,7 @@ import amidst.documentation.Immutable; import amidst.mojangapi.world.testworld.storage.json.BiomeDataJson; import amidst.mojangapi.world.testworld.storage.json.CoordinatesCollectionJson; -import amidst.mojangapi.world.testworld.storage.json.EndIslandsJson; +import amidst.mojangapi.world.testworld.storage.json.LargeEndIslandsJson; import amidst.mojangapi.world.testworld.storage.json.SlimeChunksJson; import amidst.mojangapi.world.testworld.storage.json.WorldMetadataJson; @@ -35,8 +35,8 @@ public static BiomeDataJson readBiomeData(InputStream is) { return readJson(is, BiomeDataJson.class); } - public static EndIslandsJson readEndIslands(InputStream is) { - return readJson(is, EndIslandsJson.class); + public static LargeEndIslandsJson readLargeEndIslands(InputStream is) { + return readJson(is, LargeEndIslandsJson.class); } public static SlimeChunksJson readSlimeChunks(InputStream is) { diff --git a/src/test/java/amidst/mojangapi/world/testworld/storage/json/EndIslandsJson.java b/src/test/java/amidst/mojangapi/world/testworld/storage/json/LargeEndIslandsJson.java similarity index 59% rename from src/test/java/amidst/mojangapi/world/testworld/storage/json/EndIslandsJson.java rename to src/test/java/amidst/mojangapi/world/testworld/storage/json/LargeEndIslandsJson.java index 7c6b3f1dd..4a6cd4c30 100644 --- a/src/test/java/amidst/mojangapi/world/testworld/storage/json/EndIslandsJson.java +++ b/src/test/java/amidst/mojangapi/world/testworld/storage/json/LargeEndIslandsJson.java @@ -1,5 +1,6 @@ package amidst.mojangapi.world.testworld.storage.json; +import java.util.List; import java.util.SortedMap; import java.util.TreeMap; @@ -7,25 +8,25 @@ import amidst.documentation.Immutable; import amidst.mojangapi.mocking.FragmentCornerWalker; import amidst.mojangapi.world.coordinates.CoordinatesInWorld; -import amidst.mojangapi.world.oracle.end.EndIslandList; import amidst.mojangapi.world.oracle.end.EndIslandOracle; +import amidst.mojangapi.world.oracle.end.LargeEndIsland; @Immutable -public class EndIslandsJson { - public static EndIslandsJson extract(EndIslandOracle oracle, int fragmentsAroundOrigin) { - SortedMap result = new TreeMap<>(); +public class LargeEndIslandsJson { + public static LargeEndIslandsJson extract(EndIslandOracle oracle, int fragmentsAroundOrigin) { + SortedMap> result = new TreeMap<>(); FragmentCornerWalker.walkFragmentsAroundOrigin(fragmentsAroundOrigin).walk( - corner -> result.put(corner, oracle.getAt(corner))); - return new EndIslandsJson(result); + corner -> result.put(corner, oracle.getLargeIslandsAt(corner))); + return new LargeEndIslandsJson(result); } - private volatile SortedMap endIslands; + private volatile SortedMap> endIslands; @GsonConstructor - public EndIslandsJson() { + public LargeEndIslandsJson() { } - public EndIslandsJson(SortedMap endIslands) { + public LargeEndIslandsJson(SortedMap> endIslands) { this.endIslands = endIslands; } @@ -45,10 +46,10 @@ public boolean equals(Object obj) { if (obj == null) { return false; } - if (!(obj instanceof EndIslandsJson)) { + if (!(obj instanceof LargeEndIslandsJson)) { return false; } - EndIslandsJson other = (EndIslandsJson) obj; + LargeEndIslandsJson other = (LargeEndIslandsJson) obj; if (endIslands == null) { if (other.endIslands != null) { return false; From 7e62b00e11ba4788466992cfbe3a20bb1d03fe73 Mon Sep 17 00:00:00 2001 From: burgerguy Date: Fri, 28 Aug 2020 16:18:16 -0400 Subject: [PATCH 12/17] changes to work with master merge --- .../world/icon/producer/EmptyProducer.java | 14 ++++++++++++++ .../world/oracle/end/EndIslandOracle.java | 18 ++++++------------ 2 files changed, 20 insertions(+), 12 deletions(-) create mode 100644 src/main/java/amidst/mojangapi/world/icon/producer/EmptyProducer.java diff --git a/src/main/java/amidst/mojangapi/world/icon/producer/EmptyProducer.java b/src/main/java/amidst/mojangapi/world/icon/producer/EmptyProducer.java new file mode 100644 index 000000000..4ffc2dd85 --- /dev/null +++ b/src/main/java/amidst/mojangapi/world/icon/producer/EmptyProducer.java @@ -0,0 +1,14 @@ +package amidst.mojangapi.world.icon.producer; + +import java.util.function.Consumer; + +import amidst.mojangapi.world.coordinates.CoordinatesInWorld; +import amidst.mojangapi.world.icon.WorldIcon; + +public class EmptyProducer extends WorldIconProducer { + + @Override + public void produce(CoordinatesInWorld corner, Consumer consumer, T additionalData) { + } + +} diff --git a/src/main/java/amidst/mojangapi/world/oracle/end/EndIslandOracle.java b/src/main/java/amidst/mojangapi/world/oracle/end/EndIslandOracle.java index fd9c860c1..621f109e1 100644 --- a/src/main/java/amidst/mojangapi/world/oracle/end/EndIslandOracle.java +++ b/src/main/java/amidst/mojangapi/world/oracle/end/EndIslandOracle.java @@ -4,7 +4,6 @@ import java.util.List; import amidst.documentation.ThreadSafe; -import amidst.mojangapi.minecraftinterface.RecognisedVersion; import amidst.mojangapi.world.coordinates.CoordinatesInWorld; import amidst.mojangapi.world.coordinates.Resolution; import amidst.mojangapi.world.oracle.SimplexNoise; @@ -16,8 +15,8 @@ @ThreadSafe public class EndIslandOracle { - public static EndIslandOracle from(long seed, RecognisedVersion recognisedVersion) { - return new EndIslandOracle(createNoiseFunction(seed), seed, recognisedVersion); + public static EndIslandOracle from(long seed, boolean canGenerateSmallIslands) { + return new EndIslandOracle(createNoiseFunction(seed), seed, canGenerateSmallIslands); } /** @@ -55,12 +54,12 @@ private static SimplexNoise createNoiseFunction(long seed) { private final SimplexNoise noiseFunction; private final long seed; - private final RecognisedVersion recognisedVersion; + private final boolean canGenerateSmallIslands; - public EndIslandOracle(SimplexNoise noiseFunction, long seed, RecognisedVersion recognisedVersion) { + public EndIslandOracle(SimplexNoise noiseFunction, long seed, boolean canGenerateSmallIslands) { this.noiseFunction = noiseFunction; this.seed = seed; - this.recognisedVersion = recognisedVersion; + this.canGenerateSmallIslands = canGenerateSmallIslands; } public static int getBiomeAtBlock(long x, long y, List largeIslands) { @@ -204,7 +203,7 @@ private List findSurroundingSmallIslands( int chunksPerFragmentY, List largeIslands) { List result = null; - if(RecognisedVersion.isNewerOrEqualTo(recognisedVersion, RecognisedVersion._1_13)) { // TODO: need confirmation on this version + if(canGenerateSmallIslands) { result = new ArrayList<>(); for (int y = -SMALL_ISLAND_SURROUNDING_CHUNKS; y <= chunksPerFragmentY + SMALL_ISLAND_SURROUNDING_CHUNKS; y++) { for (int x = -SMALL_ISLAND_SURROUNDING_CHUNKS; x <= chunksPerFragmentX + SMALL_ISLAND_SURROUNDING_CHUNKS; x++) { @@ -258,9 +257,4 @@ private List getSmallIslandsInChunk(long chunkX, long chunkY, L } return null; } - - public RecognisedVersion getRecognisedVersion() { - return recognisedVersion; - } - } From b83a6f4e9286895ca5b3a68c1e2babfd1f2a4374 Mon Sep 17 00:00:00 2001 From: burgerguy Date: Fri, 28 Aug 2020 16:50:14 -0400 Subject: [PATCH 13/17] fix versions for end gateways, add proper null checks for small end islands, possibly fix a few wrong gateway locations --- .../minecraftinterface/RecognisedVersion.java | 1 + .../icon/producer/EndGatewayProducer.java | 83 ++++++++++++------- .../world/oracle/end/EndIslandOracle.java | 4 + .../DefaultVersionFeatures.java | 12 +-- 4 files changed, 67 insertions(+), 33 deletions(-) diff --git a/src/main/java/amidst/mojangapi/minecraftinterface/RecognisedVersion.java b/src/main/java/amidst/mojangapi/minecraftinterface/RecognisedVersion.java index eda436827..7170abee0 100644 --- a/src/main/java/amidst/mojangapi/minecraftinterface/RecognisedVersion.java +++ b/src/main/java/amidst/mojangapi/minecraftinterface/RecognisedVersion.java @@ -150,6 +150,7 @@ public enum RecognisedVersion { _1_11 ("1.11", "rroumhkfph[Llw;mt[J[[Jmp"), // matches the launcher version id: 1.11 1.11-pre1 _16w44a ("16w44a", "rqotmgkfpg[Llv;ms[J[[Jmo"), // matches the launcher version id: 16w44a _16w43a ("16w43a", "rpotmgkfpg[Llv;ms[J[[Jmo"), // matches the launcher version id: 16w43a 16w42a 16w41a 16w40a 16w39c + _16w39a ("16w39a", "rnotmgkfpg[Llv;ms[J[[Jmo"), // matches the launcher version id: 16w39a _16w38a ("16w38a", "rlosmfkepf[Llu;mr[J[[Jmn"), // matches the launcher version id: 16w38a _16w36a ("16w36a", "rkosmfkepf[Llu;mr[J[[Jmn"), // matches the launcher version id: 16w36a _16w35a ("16w35a", "rjosmfkepf[Llu;mr[J[[Jmn"), // matches the launcher version id: 16w35a 16w33a 16w32b diff --git a/src/main/java/amidst/mojangapi/world/icon/producer/EndGatewayProducer.java b/src/main/java/amidst/mojangapi/world/icon/producer/EndGatewayProducer.java index 109d0ec69..a178f01ab 100644 --- a/src/main/java/amidst/mojangapi/world/icon/producer/EndGatewayProducer.java +++ b/src/main/java/amidst/mojangapi/world/icon/producer/EndGatewayProducer.java @@ -46,6 +46,8 @@ public class EndGatewayProducer extends WorldIconProducer { */ private final EndSpawnGatewayProducer spawnProducer; private final long seed; + + private final boolean generateDecoratorGateways; private final int featureIndex; private final int generationStage; @@ -54,14 +56,25 @@ public EndGatewayProducer(long seed, int featureIndex, int generationStage, EndI this.seed = seed; this.featureIndex = featureIndex; this.generationStage = generationStage; + this.generateDecoratorGateways = true; + } + + public EndGatewayProducer(long seed, EndIslandOracle oracle) { + this.spawnProducer = new EndSpawnGatewayProducer(oracle); + this.seed = seed; + this.featureIndex = 0; + this.generationStage = 0; + this.generateDecoratorGateways = false; } @Override public void produce(CoordinatesInWorld corner, Consumer consumer, EndIslandList endIslands) { - // The buffer only needs to account for positive changes because it's not possible for it to move backwards out of the fragment. - for (int xRelativeToFragment = -BUFFER_SIZE; xRelativeToFragment < SIZE; xRelativeToFragment++) { - for (int yRelativeToFragment = -BUFFER_SIZE; yRelativeToFragment < SIZE; yRelativeToFragment++) { - generateAt(corner, consumer, endIslands, xRelativeToFragment, yRelativeToFragment); + if (generateDecoratorGateways) { + // The buffer only needs to account for positive changes because it's not possible for it to move backwards out of the fragment. + for (int xRelativeToFragment = -BUFFER_SIZE; xRelativeToFragment < SIZE; xRelativeToFragment++) { + for (int yRelativeToFragment = -BUFFER_SIZE; yRelativeToFragment < SIZE; yRelativeToFragment++) { + generateAt(corner, consumer, endIslands, xRelativeToFragment, yRelativeToFragment); + } } } spawnProducer.produce(corner, consumer, null); @@ -113,10 +126,13 @@ public CoordinatesInWorld tryGetValidLocationFromChunk(long chunkX, long chunkY, if(placementInfluence > 0.0F) { return coordinates; } else { - // If this check fails, there's a very small chance that it landed on a small island - for(SmallEndIsland smallIsland : endIslands.getSmallIslands()) { - if(smallIsland.isOnIsland(gatewayX, gatewayY)) { - return coordinates; + List smallIslands = endIslands.getSmallIslands(); + if(smallIslands != null) { + // If this check fails, there's a very small chance that it landed on a small island + for(SmallEndIsland smallIsland : smallIslands) { + if(smallIsland.isOnIsland(gatewayX, gatewayY)) { + return coordinates; + } } } } @@ -211,12 +227,16 @@ private boolean isChunkIslandlessFast(CoordinatesInWorld blockCoords) { private boolean isChunkIslandlessSlow(CoordinatesInWorld blockCoords) { EndIslandList endIslands = oracle.getAt(blockCoords); - // Small Islands - for(SmallEndIsland island : endIslands.getSmallIslands()) { - for (long x = blockCoords.getX() & -16; x < (blockCoords.getX() | 15); x++) { - for (long y = blockCoords.getY() & -16; y < (blockCoords.getY() | 15); y++) { - if(island.isOnIsland(x, y)) { - return false; + + List smallIslands = endIslands.getSmallIslands(); + if(smallIslands != null) { + // Small Islands + for(SmallEndIsland island : smallIslands) { + for (long x = blockCoords.getX() & -16; x < (blockCoords.getX() | 15); x++) { + for (long y = blockCoords.getY() & -16; y < (blockCoords.getY() | 15); y++) { + if(island.isOnIsland(x, y)) { + return false; + } } } } @@ -245,12 +265,15 @@ private CoordinatesInWorld findSpawnpoint(CoordinatesInWorld blockCoords, boolea return new CoordinatesInWorld(x, y); } } - // We want the lowest small end islands first, so we sort them. + List smallIslands = endIslands.getSmallIslands(); - smallIslands.sort(((Comparator)(e1, e2) -> Integer.compare(e1.getHeight(), e2.getHeight())).reversed()); - for (SmallEndIsland island : endIslands.getSmallIslands()) { - if (island.isOnIsland(x, y)) { - return new CoordinatesInWorld(x, y); + if(smallIslands != null) { + // We want the lowest small end islands first, so we sort them. + smallIslands.sort(((Comparator)(e1, e2) -> Integer.compare(e1.getHeight(), e2.getHeight())).reversed()); + for (SmallEndIsland island : smallIslands) { + if (island.isOnIsland(x, y)) { + return new CoordinatesInWorld(x, y); + } } } } @@ -280,20 +303,24 @@ private CoordinatesInWorld findHighestBlock(CoordinatesInWorld blockCoords, int } } - for (long x = blockCoords.getX() + startX; x <= blockCoords.getX() + endX; ++x) { - for (long y = blockCoords.getY() + startY; y <= blockCoords.getY() + endY; ++y) { - for (SmallEndIsland island : endIslands.getSmallIslands()) { - if (island.isOnIsland(x, y)) { - int coordHeight = island.getHeight(); - if(coordHeight > highestBlock) { - highestBlock = coordHeight; - highestX = x; - highestY = y; + List smallIslands = endIslands.getSmallIslands(); + if(smallIslands != null) { + for (long x = blockCoords.getX() + startX; x <= blockCoords.getX() + endX; ++x) { + for (long y = blockCoords.getY() + startY; y <= blockCoords.getY() + endY; ++y) { + for (SmallEndIsland island : smallIslands) { + if (island.isOnIsland(x, y)) { + int coordHeight = island.getHeight(); + if(coordHeight > highestBlock) { + highestBlock = coordHeight; + highestX = x; + highestY = y; + } } } } } } + return new CoordinatesInWorld(highestX, highestY); } diff --git a/src/main/java/amidst/mojangapi/world/oracle/end/EndIslandOracle.java b/src/main/java/amidst/mojangapi/world/oracle/end/EndIslandOracle.java index 621f109e1..ccd33289c 100644 --- a/src/main/java/amidst/mojangapi/world/oracle/end/EndIslandOracle.java +++ b/src/main/java/amidst/mojangapi/world/oracle/end/EndIslandOracle.java @@ -257,4 +257,8 @@ private List getSmallIslandsInChunk(long chunkX, long chunkY, L } return null; } + + public boolean canGenerateSmallIslands() { + return canGenerateSmallIslands; + } } diff --git a/src/main/java/amidst/mojangapi/world/versionfeatures/DefaultVersionFeatures.java b/src/main/java/amidst/mojangapi/world/versionfeatures/DefaultVersionFeatures.java index 6490c455c..14a07d2f7 100644 --- a/src/main/java/amidst/mojangapi/world/versionfeatures/DefaultVersionFeatures.java +++ b/src/main/java/amidst/mojangapi/world/versionfeatures/DefaultVersionFeatures.java @@ -163,14 +163,14 @@ public static VersionFeatures.Builder builder(WorldOptions worldOptions, Minecra LayerIds.TEMPLE ).sinceExtend(RecognisedVersion._1_8, LayerIds.OCEAN_MONUMENT - ).sinceExtend(RecognisedVersion._15w31c, + ).sinceExtend(RecognisedVersion._15w31c, // Actually 15w31a LayerIds.END_ISLANDS, - LayerIds.END_CITY + LayerIds.END_CITY, + LayerIds.END_GATEWAY ).sinceExtend(RecognisedVersion._16w43a, LayerIds.WOODLAND_MANSION ).sinceExtend(RecognisedVersion._18w09a, - LayerIds.OCEAN_FEATURES, - LayerIds.END_GATEWAY // were introduced in 16w39a, but we can only find them past here + LayerIds.OCEAN_FEATURES ).construct()) .with(FeatureKey.BIOME_LIST, DefaultBiomes.DEFAULT_BIOMES) @@ -267,7 +267,9 @@ public static VersionFeatures.Builder builder(WorldOptions worldOptions, Minecra .with(FeatureKey.END_GATEWAY_PRODUCER, VersionFeature.> builder() .init((WorldIconProducer) EMPTY_PRODUCER) - .since(RecognisedVersion._15w31c, + .since(RecognisedVersion._15w31c, // Actually 15w31a + VersionFeature.fixed(features -> new EndGatewayProducer(getWorldSeed(features), features.get(FeatureKey.END_ISLAND_ORACLE))) + ).since(RecognisedVersion._16w39a, VersionFeature.fixed(features -> new EndGatewayProducer(getWorldSeed(features), 0, 3, features.get(FeatureKey.END_ISLAND_ORACLE))) ).since(RecognisedVersion._20w06a, // TODO: confirm this version is correct; this changed after 1.15.2 and before 1.16 VersionFeature.fixed(features -> new EndGatewayProducer(getWorldSeed(features), 13, 4, features.get(FeatureKey.END_ISLAND_ORACLE))) From a69a58079e80b26937c27cdcf6b9f3c2daf1939c Mon Sep 17 00:00:00 2001 From: burgerguy Date: Mon, 31 Aug 2020 16:50:58 -0400 Subject: [PATCH 14/17] merge master --- .../icon/producer/EndGatewayProducer.java | 22 ++++++----- .../world/oracle/end/EndIslandOracle.java | 30 ++++++++------- src/main/java/amidst/util/FastRand.java | 37 +++++++++++++++++-- 3 files changed, 63 insertions(+), 26 deletions(-) diff --git a/src/main/java/amidst/mojangapi/world/icon/producer/EndGatewayProducer.java b/src/main/java/amidst/mojangapi/world/icon/producer/EndGatewayProducer.java index a178f01ab..07e299eaa 100644 --- a/src/main/java/amidst/mojangapi/world/icon/producer/EndGatewayProducer.java +++ b/src/main/java/amidst/mojangapi/world/icon/producer/EndGatewayProducer.java @@ -15,9 +15,7 @@ import amidst.mojangapi.world.oracle.end.EndIslandOracle; import amidst.mojangapi.world.oracle.end.LargeEndIsland; import amidst.mojangapi.world.oracle.end.SmallEndIsland; -import kaptainwutax.seedutils.mc.ChunkRand; -import kaptainwutax.seedutils.mc.MCVersion; - +import amidst.util.FastRand; import static amidst.mojangapi.world.icon.type.DefaultWorldIconTypes.END_GATEWAY; import static amidst.mojangapi.world.icon.type.DefaultWorldIconTypes.POSSIBLE_END_GATEWAY;; @@ -45,23 +43,23 @@ public class EndGatewayProducer extends WorldIconProducer { * Used as a cache only for the spawn gateways. */ private final EndSpawnGatewayProducer spawnProducer; - private final long seed; + private final long worldSeed; private final boolean generateDecoratorGateways; private final int featureIndex; private final int generationStage; - public EndGatewayProducer(long seed, int featureIndex, int generationStage, EndIslandOracle oracle) { + public EndGatewayProducer(long worldSeed, int featureIndex, int generationStage, EndIslandOracle oracle) { this.spawnProducer = new EndSpawnGatewayProducer(oracle); - this.seed = seed; + this.worldSeed = worldSeed; this.featureIndex = featureIndex; this.generationStage = generationStage; this.generateDecoratorGateways = true; } - public EndGatewayProducer(long seed, EndIslandOracle oracle) { + public EndGatewayProducer(long worldSeed, EndIslandOracle oracle) { this.spawnProducer = new EndSpawnGatewayProducer(oracle); - this.seed = seed; + this.worldSeed = worldSeed; this.featureIndex = 0; this.generationStage = 0; this.generateDecoratorGateways = false; @@ -107,11 +105,15 @@ private void generateAt( public CoordinatesInWorld tryGetValidLocationFromChunk(long chunkX, long chunkY, EndIslandList endIslands, CoordinatesInWorld corner) { if((chunkX * chunkX + chunkY * chunkY) > 4096) { - ChunkRand rand = new ChunkRand(); long blockX = chunkX << 4; long blockY = chunkY << 4; - rand.setDecoratorSeed(seed, (int) blockX, (int) blockY, featureIndex, generationStage, MCVersion.v1_13); + FastRand rand = new FastRand(worldSeed); + long a = rand.nextLong() | 1L; + long b = rand.nextLong() | 1L; + long populationSeed = (long)(int) blockX * a + (long)(int) blockY * b ^ worldSeed; // we do the long -> int -> long conversion to replicate what minecraft does. + long decoratorSeed = populationSeed + featureIndex + 10000 * generationStage; + rand.setSeed(decoratorSeed); if(rand.nextInt(END_GATEWAY_CHANCE) == 0) { for(LargeEndIsland largeIsland : endIslands.getLargeIslands()) { diff --git a/src/main/java/amidst/mojangapi/world/oracle/end/EndIslandOracle.java b/src/main/java/amidst/mojangapi/world/oracle/end/EndIslandOracle.java index ccd33289c..d3c55859a 100644 --- a/src/main/java/amidst/mojangapi/world/oracle/end/EndIslandOracle.java +++ b/src/main/java/amidst/mojangapi/world/oracle/end/EndIslandOracle.java @@ -8,22 +8,19 @@ import amidst.mojangapi.world.coordinates.Resolution; import amidst.mojangapi.world.oracle.SimplexNoise; import amidst.mojangapi.world.versionfeatures.DefaultBiomes; - -import kaptainwutax.seedutils.lcg.rand.JRand; -import kaptainwutax.seedutils.mc.ChunkRand; -import kaptainwutax.seedutils.mc.MCVersion; +import amidst.util.FastRand; @ThreadSafe public class EndIslandOracle { - public static EndIslandOracle from(long seed, boolean canGenerateSmallIslands) { - return new EndIslandOracle(createNoiseFunction(seed), seed, canGenerateSmallIslands); + public static EndIslandOracle from(long worldSeed, boolean canGenerateSmallIslands) { + return new EndIslandOracle(createNoiseFunction(worldSeed), worldSeed, canGenerateSmallIslands); } /** * Returns the noise function using the current seed. */ - private static SimplexNoise createNoiseFunction(long seed) { - JRand random = new JRand(seed); + private static SimplexNoise createNoiseFunction(long worldSeed) { + FastRand random = new FastRand(worldSeed); // Mimics the side-effects to the random number generator caused by Minecraft. // Past 1.13, it just skips the random 17292 times. random.advance(17292); @@ -53,12 +50,12 @@ private static SimplexNoise createNoiseFunction(long seed) { private static final int OUTER_LANDS_DISTANCE_IN_CHUNKS = 64; private final SimplexNoise noiseFunction; - private final long seed; + private final long worldSeed; private final boolean canGenerateSmallIslands; - public EndIslandOracle(SimplexNoise noiseFunction, long seed, boolean canGenerateSmallIslands) { + public EndIslandOracle(SimplexNoise noiseFunction, long worldSeed, boolean canGenerateSmallIslands) { this.noiseFunction = noiseFunction; - this.seed = seed; + this.worldSeed = worldSeed; this.canGenerateSmallIslands = canGenerateSmallIslands; } @@ -216,13 +213,20 @@ private List findSurroundingSmallIslands( } return result; } + + private static final int SMALL_ISLANDS_FEATURE_INDEX = 0; + private static final int SMALL_ISLANDS_GENERATION_STAGE = 0; private List getSmallIslandsInChunk(long chunkX, long chunkY, List largeIslands) { long blockX = chunkX << 4; long blockY = chunkY << 4; if (getBiomeAtBlock(blockX, blockY, largeIslands) == DefaultBiomes.theEndLow) { - ChunkRand rand = new ChunkRand(); - rand.setDecoratorSeed(seed, (int) blockX, (int) blockY, 0, 0, MCVersion.v1_13); + FastRand rand = new FastRand(worldSeed); + long a = rand.nextLong() | 1L; + long b = rand.nextLong() | 1L; + long populationSeed = (long)(int) blockX * a + (long)(int) blockY * b ^ worldSeed; // we do the long -> int -> long conversion to replicate what minecraft does. + long decoratorSeed = populationSeed + SMALL_ISLANDS_FEATURE_INDEX + 10000 * SMALL_ISLANDS_GENERATION_STAGE; + rand.setSeed(decoratorSeed); List smallIslands = new ArrayList<>(); diff --git a/src/main/java/amidst/util/FastRand.java b/src/main/java/amidst/util/FastRand.java index 839ef93a0..449810306 100644 --- a/src/main/java/amidst/util/FastRand.java +++ b/src/main/java/amidst/util/FastRand.java @@ -13,7 +13,7 @@ public class FastRand { private static final long MULTIPLIER = 0x5DEECE66DL; private static final long ADDEND = 0xBL; - private static final long MASK = (1L << 48) - 1; + private static final long MODULUS = 1L << 48; private static final double DOUBLE_UNIT = 0x1.0p-53; @@ -24,15 +24,46 @@ public FastRand(long seed) { } private long initialScramble(long seed) { - return (seed ^ MULTIPLIER) & MASK; + return (seed ^ MULTIPLIER) & MODULUS - 1; } public void setSeed(long seed) { this.seed = initialScramble(seed); } + public long mod(long n) { + return n & (MODULUS - 1); + } + public void advance() { - seed = (seed * MULTIPLIER + ADDEND) & MASK; + seed = this.mod(seed * MULTIPLIER + ADDEND); + } + + /** + * Stolen from here + */ + public void advance(int steps) { + long multiplier = 1; + long addend = 0; + + long intermediateMultiplier = MULTIPLIER; + long intermediateAddend = ADDEND; + + for(long k = steps; k != 0; k >>>= 1) { + if((k & 1) != 0) { + multiplier *= intermediateMultiplier; + addend = intermediateMultiplier * addend + intermediateAddend; + } + + intermediateAddend = (intermediateMultiplier + 1) * intermediateAddend; + intermediateMultiplier *= intermediateMultiplier; + } + + multiplier = this.mod(multiplier); + addend = this.mod(addend); + + seed = this.mod(seed * multiplier + addend); } private int next(int bits) { From 05a3d0d9866774343916cc6289ab4f18e88fd96b Mon Sep 17 00:00:00 2001 From: burgerguy Date: Mon, 31 Aug 2020 16:51:34 -0400 Subject: [PATCH 15/17] Update pom.xml --- pom.xml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/pom.xml b/pom.xml index 2f84346bb..453043f39 100644 --- a/pom.xml +++ b/pom.xml @@ -124,10 +124,5 @@ provided maven-plugin - - com.github.KaptainWutax - SeedUtils - master-SNAPSHOT - \ No newline at end of file From 727f0094e90f47ec2497ba932d4f7f8f9d2f4d3d Mon Sep 17 00:00:00 2001 From: burgerguy Date: Mon, 31 Aug 2020 19:18:13 -0400 Subject: [PATCH 16/17] Update EndIslandOracle.java --- .../world/oracle/end/EndIslandOracle.java | 32 ++++++++----------- 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/src/main/java/amidst/mojangapi/world/oracle/end/EndIslandOracle.java b/src/main/java/amidst/mojangapi/world/oracle/end/EndIslandOracle.java index d3c55859a..a9a579c90 100644 --- a/src/main/java/amidst/mojangapi/world/oracle/end/EndIslandOracle.java +++ b/src/main/java/amidst/mojangapi/world/oracle/end/EndIslandOracle.java @@ -49,6 +49,8 @@ private static SimplexNoise createNoiseFunction(long worldSeed) { */ private static final int OUTER_LANDS_DISTANCE_IN_CHUNKS = 64; + private static final long OUTER_LANDS_DISTANCE_IN_BLOCKS = Resolution.CHUNK.convertFromThisToWorld(OUTER_LANDS_DISTANCE_IN_CHUNKS); + private final SimplexNoise noiseFunction; private final long worldSeed; private final boolean canGenerateSmallIslands; @@ -58,9 +60,9 @@ public EndIslandOracle(SimplexNoise noiseFunction, long worldSeed, boolean canGe this.worldSeed = worldSeed; this.canGenerateSmallIslands = canGenerateSmallIslands; } - + public static int getBiomeAtBlock(long x, long y, List largeIslands) { - if (x * x + y * y <= 1048576L) { + if (isInRange(x, y, OUTER_LANDS_DISTANCE_IN_BLOCKS)) { return DefaultBiomes.theEnd; } else { float influence = getInfluenceAtBlock(x, y, largeIslands); @@ -73,7 +75,7 @@ public static int getBiomeAtBlock(long x, long y, List largeIsla } } } - + public static float getInfluenceAtBlock(long x, long y, List largeIslands) { float highestInfluence = -100.0f; @@ -96,7 +98,7 @@ public EndIslandList getAt(CoordinatesInWorld corner) { steps, steps); } - + /** * Returns a list of all islands that might be touching a chunk-area. */ @@ -109,7 +111,7 @@ private EndIslandList findSurroundingIslands( List smallEndIslands = findSurroundingSmallIslands(chunkX, chunkY, chunksPerFragmentX, chunksPerFragmentY, largeEndIslands); return new EndIslandList(smallEndIslands, largeEndIslands); } - + public List getLargeIslandsAt(CoordinatesInWorld corner) { int steps = Resolution.CHUNK.getStepsPerFragment(); return findSurroundingLargeIslands( @@ -182,17 +184,11 @@ private int getErosionFactor(long chunkX, long chunkY) { // Convert coordinates to long to guard against overflow return (int) ((Math.abs(chunkX) * 3439 + Math.abs(chunkY) * 147) % 13 + 9); } - - /** - * Is the point (x, y) inside the disk of radius d centered at the origin? - */ - private boolean isInRange(long x, long y, int d) { - // Guard against overflow - if (x < -d || x > d || y < -d || y > d) - return false; - return x * x + y * y <= d * d; - } + private static boolean isInRange(long x, long y, long d) { + return x * x + y * y <= d * d; + } + private List findSurroundingSmallIslands( long chunkX, long chunkY, @@ -213,10 +209,10 @@ private List findSurroundingSmallIslands( } return result; } - + private static final int SMALL_ISLANDS_FEATURE_INDEX = 0; private static final int SMALL_ISLANDS_GENERATION_STAGE = 0; - + private List getSmallIslandsInChunk(long chunkX, long chunkY, List largeIslands) { long blockX = chunkX << 4; long blockY = chunkY << 4; @@ -261,7 +257,7 @@ private List getSmallIslandsInChunk(long chunkX, long chunkY, L } return null; } - + public boolean canGenerateSmallIslands() { return canGenerateSmallIslands; } From 156e5d56ebd4092c70e30dcf53d24c7afe34214f Mon Sep 17 00:00:00 2001 From: burgerguy Date: Wed, 9 Sep 2020 10:55:38 -0400 Subject: [PATCH 17/17] fix small island pixels --- .../java/amidst/fragment/colorprovider/TheEndColorProvider.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/amidst/fragment/colorprovider/TheEndColorProvider.java b/src/main/java/amidst/fragment/colorprovider/TheEndColorProvider.java index f910bd4ac..59403310f 100644 --- a/src/main/java/amidst/fragment/colorprovider/TheEndColorProvider.java +++ b/src/main/java/amidst/fragment/colorprovider/TheEndColorProvider.java @@ -115,7 +115,7 @@ private int getSmallIslandSSAAPixel(long x, long y, int textureX, int textureY, double alpha = 0; for(SmallEndIsland smallIsland : smallIslands) { for(int i = 0; i <= 3; i++) { - if(smallIsland.isOnIsland(x + NEIGHBORING_PIXEL_TABLE[i], y + NEIGHBORING_PIXEL_TABLE[i + 1])) { + if(smallIsland.isOnIsland(x + NEIGHBORING_PIXEL_TABLE[(i * 2)], y + NEIGHBORING_PIXEL_TABLE[(i * 2) + 1])) { alpha += ALPHA_INCREMENT; } }