From 54b7e01caa041d17b43f49a1217bce73d94dc93c Mon Sep 17 00:00:00 2001 From: moulins <> Date: Fri, 23 Jun 2017 12:20:05 +0200 Subject: [PATCH 01/13] Rename CoordinatesInWorld to Coordinates --- .../fragment/ClosestWorldIconFinder.java | 6 +-- src/main/java/amidst/fragment/Fragment.java | 8 ++-- .../java/amidst/fragment/FragmentGraph.java | 16 +++---- .../java/amidst/fragment/FragmentManager.java | 4 +- .../fragment/drawer/WorldIconDrawer.java | 4 +- .../fragment/loader/EndIslandsLoader.java | 4 +- .../amidst/fragment/loader/ImageLoader.java | 4 +- src/main/java/amidst/gui/main/Actions.java | 8 ++-- .../gui/main/menu/MovePlayerPopupMenu.java | 8 ++-- .../FragmentGraphToScreenTranslator.java | 10 ++-- .../amidst/gui/main/viewer/ViewerFacade.java | 4 +- .../widget/CursorInformationWidget.java | 8 ++-- .../java/amidst/mojangapi/file/SaveGame.java | 4 +- .../mojangapi/file/nbt/LevelDatNbt.java | 14 +++--- ...ordinatesInWorld.java => Coordinates.java} | 46 +++++++++---------- .../mojangapi/world/filter/WorldFilter.java | 6 +-- .../world/filter/WorldFilter_Structure.java | 4 +- .../mojangapi/world/icon/WorldIcon.java | 8 ++-- .../producer/CachedWorldIconProducer.java | 4 +- .../world/icon/producer/SpawnProducer.java | 8 ++-- .../producer/StrongholdProducer_Base.java | 16 +++---- .../icon/producer/StructureProducer.java | 12 ++--- .../icon/producer/WorldIconProducer.java | 6 +-- .../world/oracle/BiomeDataOracle.java | 14 +++--- .../world/oracle/EndIslandOracle.java | 4 +- .../oracle/HeuristicWorldSpawnOracle.java | 4 +- .../oracle/ImmutableWorldSpawnOracle.java | 8 ++-- .../world/oracle/WorldSpawnOracle.java | 4 +- .../amidst/mojangapi/world/player/Player.java | 4 +- .../world/player/PlayerCoordinates.java | 10 ++-- .../mocking/FragmentCornerWalker.java | 16 +++---- .../json/CoordinatesCollectionJson.java | 24 +++++----- .../storage/json/EndIslandsJson.java | 8 ++-- 33 files changed, 154 insertions(+), 154 deletions(-) rename src/main/java/amidst/mojangapi/world/coordinates/{CoordinatesInWorld.java => Coordinates.java} (71%) diff --git a/src/main/java/amidst/fragment/ClosestWorldIconFinder.java b/src/main/java/amidst/fragment/ClosestWorldIconFinder.java index 25a93d547..b33686f22 100644 --- a/src/main/java/amidst/fragment/ClosestWorldIconFinder.java +++ b/src/main/java/amidst/fragment/ClosestWorldIconFinder.java @@ -4,14 +4,14 @@ import amidst.documentation.CalledOnlyBy; import amidst.documentation.NotThreadSafe; import amidst.fragment.layer.LayerDeclaration; -import amidst.mojangapi.world.coordinates.CoordinatesInWorld; +import amidst.mojangapi.world.coordinates.Coordinates; import amidst.mojangapi.world.icon.WorldIcon; @NotThreadSafe public class ClosestWorldIconFinder { private final FragmentGraph graph; private final Iterable declarations; - private final CoordinatesInWorld positionInWorld; + private final Coordinates positionInWorld; private WorldIcon closestIcon; private double closestDistanceSq; @@ -19,7 +19,7 @@ public class ClosestWorldIconFinder { public ClosestWorldIconFinder( FragmentGraph graph, Iterable declarations, - CoordinatesInWorld positionInWorld, + Coordinates positionInWorld, double maxDistanceInWorld) { this.graph = graph; this.declarations = declarations; diff --git a/src/main/java/amidst/fragment/Fragment.java b/src/main/java/amidst/fragment/Fragment.java index 6b42e1282..d231ff261 100644 --- a/src/main/java/amidst/fragment/Fragment.java +++ b/src/main/java/amidst/fragment/Fragment.java @@ -10,7 +10,7 @@ import amidst.documentation.CalledOnlyBy; import amidst.documentation.NotThreadSafe; import amidst.gui.main.viewer.Drawer; -import amidst.mojangapi.world.coordinates.CoordinatesInWorld; +import amidst.mojangapi.world.coordinates.Coordinates; import amidst.mojangapi.world.coordinates.Resolution; import amidst.mojangapi.world.icon.WorldIcon; import amidst.mojangapi.world.oracle.BiomeDataOracle; @@ -80,7 +80,7 @@ public class Fragment { private volatile boolean isInitialized = false; private volatile boolean isLoaded = false; - private volatile CoordinatesInWorld corner; + private volatile Coordinates corner; private volatile float alpha; private volatile short[][] biomeData; @@ -172,11 +172,11 @@ public boolean isLoaded() { return isLoaded; } - public void setCorner(CoordinatesInWorld corner) { + public void setCorner(Coordinates corner) { this.corner = corner; } - public CoordinatesInWorld getCorner() { + public Coordinates getCorner() { return corner; } } diff --git a/src/main/java/amidst/fragment/FragmentGraph.java b/src/main/java/amidst/fragment/FragmentGraph.java index a6673ac5d..face428af 100644 --- a/src/main/java/amidst/fragment/FragmentGraph.java +++ b/src/main/java/amidst/fragment/FragmentGraph.java @@ -6,7 +6,7 @@ import amidst.documentation.CalledOnlyBy; import amidst.documentation.NotThreadSafe; import amidst.fragment.layer.LayerDeclaration; -import amidst.mojangapi.world.coordinates.CoordinatesInWorld; +import amidst.mojangapi.world.coordinates.Coordinates; import amidst.mojangapi.world.icon.WorldIcon; import amidst.util.Lazy; @@ -26,17 +26,17 @@ public FragmentGraph(Iterable declarations, FragmentManager fr } @CalledOnlyBy(AmidstThread.EDT) - public void init(CoordinatesInWorld coordinates) { + public void init(Coordinates coordinates) { topLeftFragment.setToValue(create(coordinates)); } @CalledOnlyBy(AmidstThread.EDT) private FragmentGraphItem createOrigin() { - return create(CoordinatesInWorld.origin()); + return create(Coordinates.origin()); } @CalledOnlyBy(AmidstThread.EDT) - private FragmentGraphItem create(CoordinatesInWorld coordinates) { + private FragmentGraphItem create(Coordinates coordinates) { recycleAll(); fragmentsPerRow = 1; fragmentsPerColumn = 1; @@ -72,7 +72,7 @@ public int getFragmentsPerColumn() { } @CalledOnlyBy(AmidstThread.EDT) - public CoordinatesInWorld getCorner() { + public Coordinates getCorner() { return topLeftFragment.getOrCreateValue().getFragment().getCorner(); } @@ -83,13 +83,13 @@ public Iterator iterator() { } @CalledOnlyBy(AmidstThread.EDT) - public WorldIcon getClosestWorldIcon(CoordinatesInWorld coordinates, double maxDistanceInWorld) { + public WorldIcon getClosestWorldIcon(Coordinates coordinates, double maxDistanceInWorld) { return new ClosestWorldIconFinder(this, declarations, coordinates, maxDistanceInWorld).getWorldIcon(); } @CalledOnlyBy(AmidstThread.EDT) - public Fragment getFragmentAt(CoordinatesInWorld coordinates) { - CoordinatesInWorld corner = coordinates.toFragmentCorner(); + public Fragment getFragmentAt(Coordinates coordinates) { + Coordinates corner = coordinates.toFragmentCorner(); for (FragmentGraphItem fragmentGraphItem : topLeftFragment.getOrCreateValue()) { Fragment fragment = fragmentGraphItem.getFragment(); if (corner.equals(fragment.getCorner())) { diff --git a/src/main/java/amidst/fragment/FragmentManager.java b/src/main/java/amidst/fragment/FragmentManager.java index d36d51e98..5ab3bf817 100644 --- a/src/main/java/amidst/fragment/FragmentManager.java +++ b/src/main/java/amidst/fragment/FragmentManager.java @@ -8,7 +8,7 @@ import amidst.fragment.constructor.FragmentConstructor; import amidst.fragment.layer.LayerManager; import amidst.mojangapi.world.Dimension; -import amidst.mojangapi.world.coordinates.CoordinatesInWorld; +import amidst.mojangapi.world.coordinates.Coordinates; import amidst.settings.Setting; @NotThreadSafe @@ -24,7 +24,7 @@ public FragmentManager(Iterable constructors, int numberOfL } @CalledOnlyBy(AmidstThread.EDT) - public Fragment requestFragment(CoordinatesInWorld coordinates) { + public Fragment requestFragment(Coordinates coordinates) { Fragment fragment; while ((fragment = availableQueue.poll()) == null) { cache.increaseSize(); diff --git a/src/main/java/amidst/fragment/drawer/WorldIconDrawer.java b/src/main/java/amidst/fragment/drawer/WorldIconDrawer.java index 7899f62b5..5e9cfb58c 100644 --- a/src/main/java/amidst/fragment/drawer/WorldIconDrawer.java +++ b/src/main/java/amidst/fragment/drawer/WorldIconDrawer.java @@ -11,7 +11,7 @@ import amidst.fragment.layer.LayerDeclaration; import amidst.gui.main.viewer.WorldIconSelection; import amidst.gui.main.viewer.Zoom; -import amidst.mojangapi.world.coordinates.CoordinatesInWorld; +import amidst.mojangapi.world.coordinates.Coordinates; import amidst.mojangapi.world.icon.WorldIcon; @NotThreadSafe @@ -45,7 +45,7 @@ private void drawIcon(WorldIcon icon, double invZoom, Graphics2D g2d) { width *= 1.5; height *= 1.5; } - CoordinatesInWorld coordinates = icon.getCoordinates(); + Coordinates coordinates = icon.getCoordinates(); g2d.translate(coordinates.getXRelativeToFragment(), coordinates.getYRelativeToFragment()); g2d.scale(invZoom, invZoom); g2d.drawImage(image, -(width >> 1), -(height >> 1), width, height, null); diff --git a/src/main/java/amidst/fragment/loader/EndIslandsLoader.java b/src/main/java/amidst/fragment/loader/EndIslandsLoader.java index 90a2c336a..8e5a18c4a 100644 --- a/src/main/java/amidst/fragment/loader/EndIslandsLoader.java +++ b/src/main/java/amidst/fragment/loader/EndIslandsLoader.java @@ -9,7 +9,7 @@ import amidst.fragment.Fragment; import amidst.fragment.layer.LayerDeclaration; import amidst.mojangapi.world.Dimension; -import amidst.mojangapi.world.coordinates.CoordinatesInWorld; +import amidst.mojangapi.world.coordinates.Coordinates; import amidst.mojangapi.world.oracle.EndIsland; import amidst.mojangapi.world.oracle.EndIslandOracle; @@ -42,7 +42,7 @@ private void doLoad(Fragment fragment) { } @CalledOnlyBy(AmidstThread.FRAGMENT_LOADER) - private List getEndIslands(CoordinatesInWorld corner) { + private List getEndIslands(Coordinates corner) { return endIslandOracle.getAt(corner); } } diff --git a/src/main/java/amidst/fragment/loader/ImageLoader.java b/src/main/java/amidst/fragment/loader/ImageLoader.java index 5c8ae7405..6f136476c 100644 --- a/src/main/java/amidst/fragment/loader/ImageLoader.java +++ b/src/main/java/amidst/fragment/loader/ImageLoader.java @@ -10,7 +10,7 @@ import amidst.fragment.colorprovider.ColorProvider; import amidst.fragment.layer.LayerDeclaration; import amidst.mojangapi.world.Dimension; -import amidst.mojangapi.world.coordinates.CoordinatesInWorld; +import amidst.mojangapi.world.coordinates.Coordinates; import amidst.mojangapi.world.coordinates.Resolution; @NotThreadSafe @@ -50,7 +50,7 @@ public void reload(Dimension dimension, Fragment fragment) { @CalledOnlyBy(AmidstThread.FRAGMENT_LOADER) private void doLoad(Dimension dimension, Fragment fragment) { - CoordinatesInWorld corner = fragment.getCorner(); + Coordinates corner = fragment.getCorner(); long cornerX = corner.getXAs(resolution); long cornerY = corner.getYAs(resolution); drawToCache(dimension, fragment, cornerX, cornerY); diff --git a/src/main/java/amidst/gui/main/Actions.java b/src/main/java/amidst/gui/main/Actions.java index 5566847f6..0fc02fae9 100644 --- a/src/main/java/amidst/gui/main/Actions.java +++ b/src/main/java/amidst/gui/main/Actions.java @@ -23,7 +23,7 @@ import amidst.logging.AmidstLogger; import amidst.mojangapi.world.WorldSeed; import amidst.mojangapi.world.WorldType; -import amidst.mojangapi.world.coordinates.CoordinatesInWorld; +import amidst.mojangapi.world.coordinates.Coordinates; import amidst.mojangapi.world.icon.WorldIcon; import amidst.mojangapi.world.player.Player; import amidst.mojangapi.world.player.PlayerCoordinates; @@ -114,7 +114,7 @@ public void goToCoordinate() { if (viewerFacade != null) { String input = dialogs.askForCoordinates(); if (input != null) { - CoordinatesInWorld coordinates = CoordinatesInWorld.tryParse(input); + Coordinates coordinates = Coordinates.tryParse(input); if (coordinates != null) { viewerFacade.centerOn(coordinates); } else { @@ -298,7 +298,7 @@ public void selectWorldIcon(WorldIcon worldIcon) { } @CalledOnlyBy(AmidstThread.EDT) - public void showPlayerPopupMenu(CoordinatesInWorld targetCoordinates, Component component, int x, int y) { + public void showPlayerPopupMenu(Coordinates targetCoordinates, Component component, int x, int y) { ViewerFacade viewerFacade = viewerFacadeSupplier.get(); if (viewerFacade != null) { if (viewerFacade.canSavePlayerLocations()) { @@ -309,7 +309,7 @@ public void showPlayerPopupMenu(CoordinatesInWorld targetCoordinates, Component } @CalledOnlyBy(AmidstThread.EDT) - public void movePlayer(Player player, CoordinatesInWorld targetCoordinates) { + public void movePlayer(Player player, Coordinates targetCoordinates) { ViewerFacade viewerFacade = viewerFacadeSupplier.get(); if (viewerFacade != null) { PlayerCoordinates currentCoordinates = player.getPlayerCoordinates(); diff --git a/src/main/java/amidst/gui/main/menu/MovePlayerPopupMenu.java b/src/main/java/amidst/gui/main/menu/MovePlayerPopupMenu.java index a87568086..6fa39effb 100644 --- a/src/main/java/amidst/gui/main/menu/MovePlayerPopupMenu.java +++ b/src/main/java/amidst/gui/main/menu/MovePlayerPopupMenu.java @@ -9,7 +9,7 @@ import amidst.documentation.NotThreadSafe; import amidst.gui.main.Actions; -import amidst.mojangapi.world.coordinates.CoordinatesInWorld; +import amidst.mojangapi.world.coordinates.Coordinates; import amidst.mojangapi.world.player.MovablePlayerList; import amidst.mojangapi.world.player.Player; @@ -17,12 +17,12 @@ public class MovePlayerPopupMenu { private final Actions actions; private final MovablePlayerList movablePlayerList; - private final CoordinatesInWorld targetCoordinates; + private final Coordinates targetCoordinates; public MovePlayerPopupMenu( Actions actions, MovablePlayerList movablePlayerList, - CoordinatesInWorld targetCoordinates) { + Coordinates targetCoordinates) { this.actions = actions; this.movablePlayerList = movablePlayerList; this.targetCoordinates = targetCoordinates; @@ -40,7 +40,7 @@ private JPopupMenu createPlayerMenu() { return result; } - private JMenuItem createPlayerMenuItem(final Player player, final CoordinatesInWorld targetCoordinates) { + private JMenuItem createPlayerMenuItem(final Player player, final Coordinates targetCoordinates) { JMenuItem result = new JMenuItem(player.getPlayerName()); result.addActionListener(new ActionListener() { @Override diff --git a/src/main/java/amidst/gui/main/viewer/FragmentGraphToScreenTranslator.java b/src/main/java/amidst/gui/main/viewer/FragmentGraphToScreenTranslator.java index 29806a1c4..e23e01d14 100644 --- a/src/main/java/amidst/gui/main/viewer/FragmentGraphToScreenTranslator.java +++ b/src/main/java/amidst/gui/main/viewer/FragmentGraphToScreenTranslator.java @@ -7,7 +7,7 @@ import amidst.documentation.NotThreadSafe; import amidst.fragment.Fragment; import amidst.fragment.FragmentGraph; -import amidst.mojangapi.world.coordinates.CoordinatesInWorld; +import amidst.mojangapi.world.coordinates.Coordinates; @NotThreadSafe public class FragmentGraphToScreenTranslator { @@ -39,7 +39,7 @@ public void update(int viewerWidth, int viewerHeight) { private void centerOnOriginIfNecessary() { if (isFirstUpdate) { isFirstUpdate = false; - centerOn(CoordinatesInWorld.origin()); + centerOn(Coordinates.origin()); } } @@ -77,7 +77,7 @@ private int getNewAbove(double fragmentSizeOnScreen) { } @CalledOnlyBy(AmidstThread.EDT) - public void centerOn(final CoordinatesInWorld coordinates) { + public void centerOn(final Coordinates coordinates) { graph.init(coordinates); int xCenterOnScreen = viewerWidth >> 1; int yCenterOnScreen = viewerHeight >> 1; @@ -125,8 +125,8 @@ public double getTopOnScreen() { } @CalledOnlyBy(AmidstThread.EDT) - public CoordinatesInWorld screenToWorld(Point pointOnScreen) { - CoordinatesInWorld corner = graph.getCorner(); + public Coordinates screenToWorld(Point pointOnScreen) { + Coordinates corner = graph.getCorner(); return corner.add( (long) zoom.screenToWorld(pointOnScreen.x - leftOnScreen), (long) zoom.screenToWorld(pointOnScreen.y - topOnScreen)); diff --git a/src/main/java/amidst/gui/main/viewer/ViewerFacade.java b/src/main/java/amidst/gui/main/viewer/ViewerFacade.java index fceab036e..85d00be44 100644 --- a/src/main/java/amidst/gui/main/viewer/ViewerFacade.java +++ b/src/main/java/amidst/gui/main/viewer/ViewerFacade.java @@ -16,7 +16,7 @@ import amidst.mojangapi.world.World; import amidst.mojangapi.world.WorldSeed; import amidst.mojangapi.world.WorldType; -import amidst.mojangapi.world.coordinates.CoordinatesInWorld; +import amidst.mojangapi.world.coordinates.Coordinates; import amidst.mojangapi.world.export.WorldExporter; import amidst.mojangapi.world.export.WorldExporterConfiguration; import amidst.mojangapi.world.icon.WorldIcon; @@ -108,7 +108,7 @@ public Runnable getOnFragmentLoaderTick() { } @CalledOnlyBy(AmidstThread.EDT) - public void centerOn(CoordinatesInWorld coordinates) { + public void centerOn(Coordinates coordinates) { translator.centerOn(coordinates); } 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 c802b72bc..628f84c0c 100644 --- a/src/main/java/amidst/gui/main/viewer/widget/CursorInformationWidget.java +++ b/src/main/java/amidst/gui/main/viewer/widget/CursorInformationWidget.java @@ -15,7 +15,7 @@ import amidst.mojangapi.world.Dimension; import amidst.mojangapi.world.biome.Biome; import amidst.mojangapi.world.biome.UnknownBiomeIndexException; -import amidst.mojangapi.world.coordinates.CoordinatesInWorld; +import amidst.mojangapi.world.coordinates.Coordinates; import amidst.mojangapi.world.coordinates.Resolution; import amidst.settings.Setting; @@ -44,7 +44,7 @@ public CursorInformationWidget( protected List updateTextLines() { Point mousePosition = getMousePosition(); if (mousePosition != null) { - CoordinatesInWorld coordinates = translator.screenToWorld(mousePosition); + Coordinates coordinates = translator.screenToWorld(mousePosition); String biomeName = getBiomeNameAt(coordinates); return Arrays.asList(biomeName + " " + coordinates.toString()); } else { @@ -53,7 +53,7 @@ protected List updateTextLines() { } @CalledOnlyBy(AmidstThread.EDT) - private String getBiomeNameAt(CoordinatesInWorld coordinates) { + private String getBiomeNameAt(Coordinates coordinates) { Dimension dimension = dimensionSetting.get(); if (dimension.equals(Dimension.OVERWORLD)) { return getOverworldBiomeNameAt(coordinates); @@ -66,7 +66,7 @@ private String getBiomeNameAt(CoordinatesInWorld coordinates) { } @CalledOnlyBy(AmidstThread.EDT) - private String getOverworldBiomeNameAt(CoordinatesInWorld coordinates) { + private String getOverworldBiomeNameAt(Coordinates coordinates) { Fragment fragment = graph.getFragmentAt(coordinates); if (fragment != null && fragment.isLoaded()) { long x = coordinates.getXRelativeToFragmentAs(Resolution.QUARTER); diff --git a/src/main/java/amidst/mojangapi/file/SaveGame.java b/src/main/java/amidst/mojangapi/file/SaveGame.java index 919338303..8887d6340 100644 --- a/src/main/java/amidst/mojangapi/file/SaveGame.java +++ b/src/main/java/amidst/mojangapi/file/SaveGame.java @@ -11,7 +11,7 @@ import amidst.mojangapi.file.nbt.LevelDatNbt; import amidst.mojangapi.file.service.SaveDirectoryService; import amidst.mojangapi.world.WorldType; -import amidst.mojangapi.world.coordinates.CoordinatesInWorld; +import amidst.mojangapi.world.coordinates.Coordinates; @Immutable public class SaveGame { @@ -28,7 +28,7 @@ public long getSeed() { return levelDatNbt.getSeed(); } - public CoordinatesInWorld getWorldSpawn() { + public Coordinates getWorldSpawn() { return levelDatNbt.getWorldSpawn(); } diff --git a/src/main/java/amidst/mojangapi/file/nbt/LevelDatNbt.java b/src/main/java/amidst/mojangapi/file/nbt/LevelDatNbt.java index 1483f2bd9..357f6b075 100644 --- a/src/main/java/amidst/mojangapi/file/nbt/LevelDatNbt.java +++ b/src/main/java/amidst/mojangapi/file/nbt/LevelDatNbt.java @@ -7,7 +7,7 @@ import amidst.documentation.Immutable; import amidst.mojangapi.world.WorldType; -import amidst.mojangapi.world.coordinates.CoordinatesInWorld; +import amidst.mojangapi.world.coordinates.Coordinates; import amidst.parsing.FormatException; @Immutable @@ -16,7 +16,7 @@ public static LevelDatNbt from(File file) throws IOException, FormatException { try { CompoundTag dataTag = readDataTag(NBTUtils.readTagFromFile(file)); long seed = readRandomSeed(dataTag); - CoordinatesInWorld worldSpawn = readWorldSpawn(dataTag); + Coordinates worldSpawn = readWorldSpawn(dataTag); WorldType worldType = readWorldType(dataTag); String generatorOptions = readGeneratorOptions(dataTag, worldType); boolean hasPlayer = hasPlayerTag(dataTag); @@ -34,8 +34,8 @@ private static long readRandomSeed(CompoundTag dataTag) { return NBTUtils.getLongValue(dataTag.getValue().get(NBTTagKeys.TAG_KEY_RANDOM_SEED)); } - private static CoordinatesInWorld readWorldSpawn(CompoundTag dataTag) { - return CoordinatesInWorld.from(readSpawnX(dataTag), readSpawnZ(dataTag)); + private static Coordinates readWorldSpawn(CompoundTag dataTag) { + return Coordinates.from(readSpawnX(dataTag), readSpawnZ(dataTag)); } private static long readSpawnX(CompoundTag dataTag) { @@ -79,14 +79,14 @@ private static boolean hasPlayerTag(CompoundTag dataTag) { } private final long seed; - private final CoordinatesInWorld worldSpawn; + private final Coordinates worldSpawn; private final WorldType worldType; private final String generatorOptions; private final boolean hasPlayer; public LevelDatNbt( long seed, - CoordinatesInWorld worldSpawn, + Coordinates worldSpawn, WorldType worldType, String generatorOptions, boolean hasPlayer) { @@ -101,7 +101,7 @@ public long getSeed() { return seed; } - public CoordinatesInWorld getWorldSpawn() { + public Coordinates getWorldSpawn() { return worldSpawn; } diff --git a/src/main/java/amidst/mojangapi/world/coordinates/CoordinatesInWorld.java b/src/main/java/amidst/mojangapi/world/coordinates/Coordinates.java similarity index 71% rename from src/main/java/amidst/mojangapi/world/coordinates/CoordinatesInWorld.java rename to src/main/java/amidst/mojangapi/world/coordinates/Coordinates.java index bcac33db9..1a8d31759 100644 --- a/src/main/java/amidst/mojangapi/world/coordinates/CoordinatesInWorld.java +++ b/src/main/java/amidst/mojangapi/world/coordinates/Coordinates.java @@ -5,43 +5,43 @@ import amidst.documentation.Immutable; @Immutable -public class CoordinatesInWorld implements Comparable { - public static CoordinatesInWorld tryParse(String coordinates) { +public class Coordinates implements Comparable { + public static Coordinates tryParse(String coordinates) { String[] parsedCoordinates = coordinates.replaceAll(" ", "").split(","); if (parsedCoordinates.length != 2) { return null; } try { - return CoordinatesInWorld.from(Long.parseLong(parsedCoordinates[0]), Long.parseLong(parsedCoordinates[1])); + return Coordinates.from(Long.parseLong(parsedCoordinates[0]), Long.parseLong(parsedCoordinates[1])); } catch (NumberFormatException e) { return null; } } - public static CoordinatesInWorld from(long xInWorld, long yInWorld) { - return new CoordinatesInWorld(xInWorld, yInWorld); + public static Coordinates from(long xInWorld, long yInWorld) { + return new Coordinates(xInWorld, yInWorld); } - public static CoordinatesInWorld from(long xAsResolution, long yAsResolution, Resolution resolution) { - return new CoordinatesInWorld( + public static Coordinates from(long xAsResolution, long yAsResolution, Resolution resolution) { + return new Coordinates( resolution.convertFromThisToWorld(xAsResolution), resolution.convertFromThisToWorld(yAsResolution)); } - private static CoordinatesInWorld from(CoordinatesInWorld base, long deltaXInWorld, long deltaYInWorld) { - return new CoordinatesInWorld(base.xInWorld + deltaXInWorld, base.yInWorld + deltaYInWorld); + private static Coordinates from(Coordinates base, long deltaXInWorld, long deltaYInWorld) { + return new Coordinates(base.xInWorld + deltaXInWorld, base.yInWorld + deltaYInWorld); } - public static CoordinatesInWorld origin() { + public static Coordinates origin() { return ORIGIN; } - private static final CoordinatesInWorld ORIGIN = CoordinatesInWorld.from(0, 0); + private static final Coordinates ORIGIN = Coordinates.from(0, 0); private final long xInWorld; private final long yInWorld; - public CoordinatesInWorld(long xInWorld, long yInWorld) { + public Coordinates(long xInWorld, long yInWorld) { this.xInWorld = xInWorld; this.yInWorld = yInWorld; } @@ -94,11 +94,11 @@ public long getYRelativeToFragmentAs(Resolution targetResolution) { return targetResolution.convertFromWorldToThis(CoordinateUtils.toFragmentRelative(yInWorld)); } - public CoordinatesInWorld toFragmentCorner() { + public Coordinates toFragmentCorner() { return from(getXCornerOfFragment(), getYCornerOfFragment()); } - public double getDistance(CoordinatesInWorld other) { + public double getDistance(Coordinates other) { return Point.distance(xInWorld, yInWorld, other.xInWorld, other.yInWorld); } @@ -106,7 +106,7 @@ public double getDistance(long xInWorld, long yInWorld) { return Point.distance(this.xInWorld, this.yInWorld, xInWorld, yInWorld); } - public double getDistanceSq(CoordinatesInWorld other) { + public double getDistanceSq(Coordinates other) { return Point.distanceSq(xInWorld, yInWorld, other.xInWorld, other.yInWorld); } @@ -114,28 +114,28 @@ public double getDistanceSq(long xInWorld, long yInWorld) { return Point.distanceSq(this.xInWorld, this.yInWorld, xInWorld, yInWorld); } - public CoordinatesInWorld add(CoordinatesInWorld other) { + public Coordinates add(Coordinates other) { return add(other.xInWorld, other.yInWorld); } - public CoordinatesInWorld add(long xInWorld, long yInWorld) { + public Coordinates add(long xInWorld, long yInWorld) { return from(this, xInWorld, yInWorld); } - public CoordinatesInWorld substract(CoordinatesInWorld other) { + public Coordinates substract(Coordinates other) { return substract(other.xInWorld, other.yInWorld); } - public CoordinatesInWorld substract(long xInWorld, long yInWorld) { + public Coordinates substract(long xInWorld, long yInWorld) { return from(this, -xInWorld, -yInWorld); } - public boolean isInBoundsOf(CoordinatesInWorld corner, long size) { + public boolean isInBoundsOf(Coordinates corner, long size) { return CoordinateUtils.isInBounds(xInWorld, yInWorld, corner.xInWorld, corner.yInWorld, size, size); } @Override - public int compareTo(CoordinatesInWorld o) { + public int compareTo(Coordinates o) { if (this == o) { return 0; } else if (this.xInWorld < o.xInWorld) { @@ -168,10 +168,10 @@ public boolean equals(Object obj) { if (obj == null) { return false; } - if (!(obj instanceof CoordinatesInWorld)) { + if (!(obj instanceof Coordinates)) { return false; } - CoordinatesInWorld other = (CoordinatesInWorld) obj; + Coordinates other = (Coordinates) obj; if (xInWorld != other.xInWorld) { return false; } diff --git a/src/main/java/amidst/mojangapi/world/filter/WorldFilter.java b/src/main/java/amidst/mojangapi/world/filter/WorldFilter.java index f38dfb35c..0e0044ff6 100644 --- a/src/main/java/amidst/mojangapi/world/filter/WorldFilter.java +++ b/src/main/java/amidst/mojangapi/world/filter/WorldFilter.java @@ -3,20 +3,20 @@ import amidst.documentation.Immutable; import amidst.fragment.Fragment; import amidst.mojangapi.world.World; -import amidst.mojangapi.world.coordinates.CoordinatesInWorld; +import amidst.mojangapi.world.coordinates.Coordinates; import amidst.mojangapi.world.coordinates.Resolution; @Immutable public abstract class WorldFilter { protected final long worldFilterSize; protected final long quarterFilterSize; - protected final CoordinatesInWorld corner; + protected final Coordinates corner; public WorldFilter(long worldFilterSize) { ensureIsMultipleOfFragmentSize(worldFilterSize); this.worldFilterSize = worldFilterSize; this.quarterFilterSize = Resolution.QUARTER.convertFromWorldToThis(this.worldFilterSize); - this.corner = new CoordinatesInWorld(-this.worldFilterSize, -this.worldFilterSize); + this.corner = new Coordinates(-this.worldFilterSize, -this.worldFilterSize); } /** diff --git a/src/main/java/amidst/mojangapi/world/filter/WorldFilter_Structure.java b/src/main/java/amidst/mojangapi/world/filter/WorldFilter_Structure.java index 866b01aeb..93301a9e1 100644 --- a/src/main/java/amidst/mojangapi/world/filter/WorldFilter_Structure.java +++ b/src/main/java/amidst/mojangapi/world/filter/WorldFilter_Structure.java @@ -2,7 +2,7 @@ import amidst.documentation.Immutable; import amidst.mojangapi.world.World; -import amidst.mojangapi.world.coordinates.CoordinatesInWorld; +import amidst.mojangapi.world.coordinates.Coordinates; import amidst.mojangapi.world.icon.producer.NameFilteredWorldIconCollector; import amidst.mojangapi.world.icon.producer.WorldIconCollector; import amidst.mojangapi.world.icon.producer.WorldIconProducer; @@ -29,7 +29,7 @@ public boolean isValid(World world) { private void procudeAndCollect(WorldIconProducer structureProducer, WorldIconCollector structureCollector) { for (long x = 0; x < 2 * worldFilterSize; x += 512) { for (long y = 0; y < 2 * worldFilterSize; y += 512) { - structureProducer.produce(CoordinatesInWorld.from(x, y).add(corner), structureCollector, null); + structureProducer.produce(Coordinates.from(x, y).add(corner), structureCollector, null); } } } diff --git a/src/main/java/amidst/mojangapi/world/icon/WorldIcon.java b/src/main/java/amidst/mojangapi/world/icon/WorldIcon.java index e08014737..121b06ab3 100644 --- a/src/main/java/amidst/mojangapi/world/icon/WorldIcon.java +++ b/src/main/java/amidst/mojangapi/world/icon/WorldIcon.java @@ -3,19 +3,19 @@ import amidst.documentation.Immutable; import amidst.documentation.NotNull; import amidst.mojangapi.world.Dimension; -import amidst.mojangapi.world.coordinates.CoordinatesInWorld; +import amidst.mojangapi.world.coordinates.Coordinates; import amidst.mojangapi.world.coordinates.Resolution; @Immutable public class WorldIcon { - private final CoordinatesInWorld coordinates; + private final Coordinates coordinates; private final String name; private final WorldIconImage image; private final Dimension dimension; private final boolean displayDimension; public WorldIcon( - CoordinatesInWorld coordinates, + Coordinates coordinates, String name, WorldIconImage image, Dimension dimension, @@ -27,7 +27,7 @@ public WorldIcon( this.displayDimension = displayDimension; } - public CoordinatesInWorld getCoordinates() { + public Coordinates getCoordinates() { return coordinates; } diff --git a/src/main/java/amidst/mojangapi/world/icon/producer/CachedWorldIconProducer.java b/src/main/java/amidst/mojangapi/world/icon/producer/CachedWorldIconProducer.java index 47e5f897d..d2723b3bd 100644 --- a/src/main/java/amidst/mojangapi/world/icon/producer/CachedWorldIconProducer.java +++ b/src/main/java/amidst/mojangapi/world/icon/producer/CachedWorldIconProducer.java @@ -6,7 +6,7 @@ import amidst.documentation.ThreadSafe; import amidst.fragment.Fragment; -import amidst.mojangapi.world.coordinates.CoordinatesInWorld; +import amidst.mojangapi.world.coordinates.Coordinates; import amidst.mojangapi.world.icon.WorldIcon; @ThreadSafe @@ -15,7 +15,7 @@ public abstract class CachedWorldIconProducer extends WorldIconProducer { private volatile List cache; @Override - public void produce(CoordinatesInWorld corner, Consumer consumer, Void additionalData) { + public void produce(Coordinates corner, Consumer consumer, Void additionalData) { for (WorldIcon icon : getCache()) { if (icon.getCoordinates().isInBoundsOf(corner, Fragment.SIZE)) { consumer.accept(icon); diff --git a/src/main/java/amidst/mojangapi/world/icon/producer/SpawnProducer.java b/src/main/java/amidst/mojangapi/world/icon/producer/SpawnProducer.java index 5a3c9736b..b9bacba40 100644 --- a/src/main/java/amidst/mojangapi/world/icon/producer/SpawnProducer.java +++ b/src/main/java/amidst/mojangapi/world/icon/producer/SpawnProducer.java @@ -6,7 +6,7 @@ import amidst.documentation.ThreadSafe; import amidst.logging.AmidstLogger; import amidst.mojangapi.world.Dimension; -import amidst.mojangapi.world.coordinates.CoordinatesInWorld; +import amidst.mojangapi.world.coordinates.Coordinates; import amidst.mojangapi.world.icon.WorldIcon; import amidst.mojangapi.world.icon.type.DefaultWorldIconTypes; import amidst.mojangapi.world.oracle.WorldSpawnOracle; @@ -25,17 +25,17 @@ protected List doCreateCache() { } private WorldIcon createSpawnWorldIcon() { - CoordinatesInWorld spawnLocation = oracle.get(); + Coordinates spawnLocation = oracle.get(); if (spawnLocation != null) { return createWorldIcon(spawnLocation); } else { - CoordinatesInWorld origin = CoordinatesInWorld.origin(); + Coordinates origin = Coordinates.origin(); AmidstLogger.info("Unable to find spawn biome. Falling back to " + origin.toString() + "."); return createWorldIcon(origin); } } - private WorldIcon createWorldIcon(CoordinatesInWorld coordinates) { + private WorldIcon createWorldIcon(Coordinates coordinates) { return new WorldIcon( coordinates, DefaultWorldIconTypes.SPAWN.getLabel(), diff --git a/src/main/java/amidst/mojangapi/world/icon/producer/StrongholdProducer_Base.java b/src/main/java/amidst/mojangapi/world/icon/producer/StrongholdProducer_Base.java index 499dce72e..f103eced6 100644 --- a/src/main/java/amidst/mojangapi/world/icon/producer/StrongholdProducer_Base.java +++ b/src/main/java/amidst/mojangapi/world/icon/producer/StrongholdProducer_Base.java @@ -7,7 +7,7 @@ import amidst.documentation.ThreadSafe; import amidst.mojangapi.world.Dimension; import amidst.mojangapi.world.biome.Biome; -import amidst.mojangapi.world.coordinates.CoordinatesInWorld; +import amidst.mojangapi.world.coordinates.Coordinates; import amidst.mojangapi.world.icon.WorldIcon; import amidst.mojangapi.world.icon.type.DefaultWorldIconTypes; import amidst.mojangapi.world.oracle.BiomeDataOracle; @@ -39,7 +39,7 @@ protected List doCreateCache() { double distance = getNextValue_distance(ring, random); int x = getX(angle, distance); int y = getY(angle, distance); - CoordinatesInWorld strongholdLocation = getStrongholdLocation(x, y, findStronghold(random, x, y)); + Coordinates strongholdLocation = getStrongholdLocation(x, y, findStronghold(random, x, y)); result.add(createWorldIcon(strongholdLocation)); angle += getAngleDelta(ring, structuresPerRing); currentRingStructureCount++; @@ -67,25 +67,25 @@ private int getY(double angle, double distance) { return (int) Math.round(Math.sin(angle) * distance); } - private CoordinatesInWorld findStronghold(Random random, int chunkX, int chunkY) { + private Coordinates findStronghold(Random random, int chunkX, int chunkY) { return biomeDataOracle.findValidLocationAtMiddleOfChunk(chunkX, chunkY, 112, validBiomes, random); } - private CoordinatesInWorld getStrongholdLocation(int x, int y, CoordinatesInWorld coordinates) { + private Coordinates getStrongholdLocation(int x, int y, Coordinates coordinates) { if (coordinates != null) { return getCornerOfChunk(coordinates); } else { - return CoordinatesInWorld.from(x << 4, y << 4); + return Coordinates.from(x << 4, y << 4); } } - private CoordinatesInWorld getCornerOfChunk(CoordinatesInWorld coordinates) { + private Coordinates getCornerOfChunk(Coordinates coordinates) { long xInWorld = (coordinates.getX() >> 4) << 4; long yInWorld = (coordinates.getY() >> 4) << 4; - return CoordinatesInWorld.from(xInWorld, yInWorld); + return Coordinates.from(xInWorld, yInWorld); } - private WorldIcon createWorldIcon(CoordinatesInWorld coordinates) { + private WorldIcon createWorldIcon(Coordinates coordinates) { return new WorldIcon( coordinates, DefaultWorldIconTypes.STRONGHOLD.getLabel(), diff --git a/src/main/java/amidst/mojangapi/world/icon/producer/StructureProducer.java b/src/main/java/amidst/mojangapi/world/icon/producer/StructureProducer.java index ff3549567..54adf4522 100644 --- a/src/main/java/amidst/mojangapi/world/icon/producer/StructureProducer.java +++ b/src/main/java/amidst/mojangapi/world/icon/producer/StructureProducer.java @@ -4,7 +4,7 @@ import amidst.documentation.ThreadSafe; import amidst.mojangapi.world.Dimension; -import amidst.mojangapi.world.coordinates.CoordinatesInWorld; +import amidst.mojangapi.world.coordinates.Coordinates; import amidst.mojangapi.world.coordinates.Resolution; import amidst.mojangapi.world.icon.WorldIcon; import amidst.mojangapi.world.icon.locationchecker.LocationChecker; @@ -38,7 +38,7 @@ public StructureProducer( } @Override - public void produce(CoordinatesInWorld corner, Consumer consumer, T additionalData) { + public void produce(Coordinates corner, Consumer consumer, T additionalData) { for (int xRelativeToFragment = 0; xRelativeToFragment < size; xRelativeToFragment++) { for (int yRelativeToFragment = 0; yRelativeToFragment < size; yRelativeToFragment++) { generateAt(corner, consumer, additionalData, xRelativeToFragment, yRelativeToFragment); @@ -48,7 +48,7 @@ public void produce(CoordinatesInWorld corner, Consumer consumer, T a // TODO: use longs? private void generateAt( - CoordinatesInWorld corner, + Coordinates corner, Consumer consumer, T additionalData, int xRelativeToFragment, @@ -58,7 +58,7 @@ private void generateAt( if (checker.isValidLocation(x, y)) { DefaultWorldIconTypes worldIconType = provider.get(x, y, additionalData); if (worldIconType != null) { - CoordinatesInWorld coordinates = createCoordinates(corner, xRelativeToFragment, yRelativeToFragment); + Coordinates coordinates = createCoordinates(corner, xRelativeToFragment, yRelativeToFragment); consumer.accept( new WorldIcon( coordinates, @@ -70,8 +70,8 @@ private void generateAt( } } - private CoordinatesInWorld createCoordinates( - CoordinatesInWorld corner, + private Coordinates createCoordinates( + Coordinates corner, int xRelativeToFragment, int yRelativeToFragment) { long xInWorld = resolution.convertFromThisToWorld(xRelativeToFragment); diff --git a/src/main/java/amidst/mojangapi/world/icon/producer/WorldIconProducer.java b/src/main/java/amidst/mojangapi/world/icon/producer/WorldIconProducer.java index 88c2b9db9..5b9ab3f1d 100644 --- a/src/main/java/amidst/mojangapi/world/icon/producer/WorldIconProducer.java +++ b/src/main/java/amidst/mojangapi/world/icon/producer/WorldIconProducer.java @@ -4,14 +4,14 @@ import java.util.function.Consumer; import amidst.documentation.ThreadSafe; -import amidst.mojangapi.world.coordinates.CoordinatesInWorld; +import amidst.mojangapi.world.coordinates.Coordinates; import amidst.mojangapi.world.icon.WorldIcon; @ThreadSafe public abstract class WorldIconProducer { - public abstract void produce(CoordinatesInWorld corner, Consumer consumer, T additionalData); + public abstract void produce(Coordinates corner, Consumer consumer, T additionalData); - public List getAt(CoordinatesInWorld corner, T additionalData) { + public List getAt(Coordinates corner, T additionalData) { WorldIconCollector collector = new WorldIconCollector(); produce(corner, collector, additionalData); return collector.get(); diff --git a/src/main/java/amidst/mojangapi/world/oracle/BiomeDataOracle.java b/src/main/java/amidst/mojangapi/world/oracle/BiomeDataOracle.java index 2608450e6..b576b9903 100644 --- a/src/main/java/amidst/mojangapi/world/oracle/BiomeDataOracle.java +++ b/src/main/java/amidst/mojangapi/world/oracle/BiomeDataOracle.java @@ -10,7 +10,7 @@ import amidst.mojangapi.minecraftinterface.MinecraftInterfaceException; import amidst.mojangapi.world.biome.Biome; import amidst.mojangapi.world.biome.UnknownBiomeIndexException; -import amidst.mojangapi.world.coordinates.CoordinatesInWorld; +import amidst.mojangapi.world.coordinates.Coordinates; import amidst.mojangapi.world.coordinates.Resolution; @ThreadSafe @@ -21,7 +21,7 @@ public BiomeDataOracle(MinecraftInterface minecraftInterface) { this.minecraftInterface = minecraftInterface; } - public void populateArray(CoordinatesInWorld corner, short[][] result, boolean useQuarterResolution) { + public void populateArray(Coordinates corner, short[][] result, boolean useQuarterResolution) { Resolution resolution = Resolution.from(useQuarterResolution); int width = result.length; if (width > 0) { @@ -97,7 +97,7 @@ public boolean isValidBiomeForStructure(int x, int y, int size, List vali } } - public CoordinatesInWorld findValidLocationAtMiddleOfChunk( + public Coordinates findValidLocationAtMiddleOfChunk( int chunkX, int chunkY, int size, @@ -107,7 +107,7 @@ public CoordinatesInWorld findValidLocationAtMiddleOfChunk( } // TODO: Find out if we should useQuarterResolution or not - public CoordinatesInWorld findValidLocation(int x, int y, int size, List validBiomes, Random random) { + public Coordinates findValidLocation(int x, int y, int size, List validBiomes, Random random) { int left = x - size >> 2; int top = y - size >> 2; int right = x + size >> 2; @@ -116,7 +116,7 @@ public CoordinatesInWorld findValidLocation(int x, int y, int size, List int height = bottom - top + 1; try { int[] biomeData = getQuarterResolutionBiomeData(left, top, width, height); - CoordinatesInWorld result = null; + Coordinates result = null; int numberOfValidLocations = 0; for (int i = 0; i < width * height; i++) { if (validBiomes.contains(Biome.getByIndex(biomeData[i])) @@ -137,10 +137,10 @@ public CoordinatesInWorld findValidLocation(int x, int y, int size, List } } - private CoordinatesInWorld createCoordinates(int left, int top, int width, int i) { + private Coordinates createCoordinates(int left, int top, int width, int i) { int x = left + i % width << 2; int y = top + i / width << 2; - return CoordinatesInWorld.from(x, y); + return Coordinates.from(x, y); } private int getMiddleOfChunk(int coordinate) { diff --git a/src/main/java/amidst/mojangapi/world/oracle/EndIslandOracle.java b/src/main/java/amidst/mojangapi/world/oracle/EndIslandOracle.java index 524d60cf4..3b7cb7c0f 100644 --- a/src/main/java/amidst/mojangapi/world/oracle/EndIslandOracle.java +++ b/src/main/java/amidst/mojangapi/world/oracle/EndIslandOracle.java @@ -5,7 +5,7 @@ import java.util.Random; import amidst.documentation.ThreadSafe; -import amidst.mojangapi.world.coordinates.CoordinatesInWorld; +import amidst.mojangapi.world.coordinates.Coordinates; import amidst.mojangapi.world.coordinates.Resolution; @ThreadSafe @@ -68,7 +68,7 @@ public EndIslandOracle(SimplexNoise noiseFunction) { this.noiseFunction = noiseFunction; } - public List getAt(CoordinatesInWorld corner) { + public List getAt(Coordinates corner) { int steps = Resolution.CHUNK.getStepsPerFragment(); return findSurroundingIslands( (int) corner.getXAs(Resolution.CHUNK), diff --git a/src/main/java/amidst/mojangapi/world/oracle/HeuristicWorldSpawnOracle.java b/src/main/java/amidst/mojangapi/world/oracle/HeuristicWorldSpawnOracle.java index 285b91cd6..1287ad091 100644 --- a/src/main/java/amidst/mojangapi/world/oracle/HeuristicWorldSpawnOracle.java +++ b/src/main/java/amidst/mojangapi/world/oracle/HeuristicWorldSpawnOracle.java @@ -5,7 +5,7 @@ import amidst.documentation.ThreadSafe; import amidst.mojangapi.world.biome.Biome; -import amidst.mojangapi.world.coordinates.CoordinatesInWorld; +import amidst.mojangapi.world.coordinates.Coordinates; @ThreadSafe public class HeuristicWorldSpawnOracle implements WorldSpawnOracle { @@ -20,7 +20,7 @@ public HeuristicWorldSpawnOracle(long seed, BiomeDataOracle biomeDataOracle, Lis } @Override - public CoordinatesInWorld get() { + public Coordinates get() { return biomeDataOracle.findValidLocation(0, 0, 256, validBiomes, new Random(seed)); } } diff --git a/src/main/java/amidst/mojangapi/world/oracle/ImmutableWorldSpawnOracle.java b/src/main/java/amidst/mojangapi/world/oracle/ImmutableWorldSpawnOracle.java index 8c2b4e85f..f9441f497 100644 --- a/src/main/java/amidst/mojangapi/world/oracle/ImmutableWorldSpawnOracle.java +++ b/src/main/java/amidst/mojangapi/world/oracle/ImmutableWorldSpawnOracle.java @@ -1,18 +1,18 @@ package amidst.mojangapi.world.oracle; import amidst.documentation.Immutable; -import amidst.mojangapi.world.coordinates.CoordinatesInWorld; +import amidst.mojangapi.world.coordinates.Coordinates; @Immutable public class ImmutableWorldSpawnOracle implements WorldSpawnOracle { - private final CoordinatesInWorld worldSpawn; + private final Coordinates worldSpawn; - public ImmutableWorldSpawnOracle(CoordinatesInWorld worldSpawn) { + public ImmutableWorldSpawnOracle(Coordinates worldSpawn) { this.worldSpawn = worldSpawn; } @Override - public CoordinatesInWorld get() { + public Coordinates get() { return worldSpawn; } } diff --git a/src/main/java/amidst/mojangapi/world/oracle/WorldSpawnOracle.java b/src/main/java/amidst/mojangapi/world/oracle/WorldSpawnOracle.java index 97c3bf866..ac2462fd1 100644 --- a/src/main/java/amidst/mojangapi/world/oracle/WorldSpawnOracle.java +++ b/src/main/java/amidst/mojangapi/world/oracle/WorldSpawnOracle.java @@ -1,9 +1,9 @@ package amidst.mojangapi.world.oracle; import amidst.documentation.ThreadSafe; -import amidst.mojangapi.world.coordinates.CoordinatesInWorld; +import amidst.mojangapi.world.coordinates.Coordinates; @ThreadSafe public interface WorldSpawnOracle { - CoordinatesInWorld get(); + Coordinates get(); } diff --git a/src/main/java/amidst/mojangapi/world/player/Player.java b/src/main/java/amidst/mojangapi/world/player/Player.java index da97a305b..0adcba89b 100644 --- a/src/main/java/amidst/mojangapi/world/player/Player.java +++ b/src/main/java/amidst/mojangapi/world/player/Player.java @@ -4,7 +4,7 @@ import amidst.logging.AmidstLogger; import amidst.mojangapi.file.SaveGamePlayer; import amidst.mojangapi.world.Dimension; -import amidst.mojangapi.world.coordinates.CoordinatesInWorld; +import amidst.mojangapi.world.coordinates.Coordinates; import amidst.mojangapi.world.icon.WorldIconImage; @ThreadSafe @@ -33,7 +33,7 @@ public PlayerCoordinates getPlayerCoordinates() { return currentCoordinates; } - public void moveTo(CoordinatesInWorld coordinates, long height, Dimension dimension) { + public void moveTo(Coordinates coordinates, long height, Dimension dimension) { this.currentCoordinates = new PlayerCoordinates(coordinates, height, dimension); } diff --git a/src/main/java/amidst/mojangapi/world/player/PlayerCoordinates.java b/src/main/java/amidst/mojangapi/world/player/PlayerCoordinates.java index d01745a93..fbd71b946 100644 --- a/src/main/java/amidst/mojangapi/world/player/PlayerCoordinates.java +++ b/src/main/java/amidst/mojangapi/world/player/PlayerCoordinates.java @@ -2,26 +2,26 @@ import amidst.documentation.Immutable; import amidst.mojangapi.world.Dimension; -import amidst.mojangapi.world.coordinates.CoordinatesInWorld; +import amidst.mojangapi.world.coordinates.Coordinates; @Immutable public class PlayerCoordinates { public static PlayerCoordinates fromNBTFile(long x, long y, long z, int dimensionId) { Dimension dimension = Dimension.from(dimensionId); - return new PlayerCoordinates(CoordinatesInWorld.from(x, z, dimension.getResolution()), y, dimension); + return new PlayerCoordinates(Coordinates.from(x, z, dimension.getResolution()), y, dimension); } - private final CoordinatesInWorld coordinates; + private final Coordinates coordinates; private final long y; private final Dimension dimension; - public PlayerCoordinates(CoordinatesInWorld coordinates, long y, Dimension dimension) { + public PlayerCoordinates(Coordinates coordinates, long y, Dimension dimension) { this.coordinates = coordinates; this.y = y; this.dimension = dimension; } - public CoordinatesInWorld getCoordinatesInWorld() { + public Coordinates getCoordinatesInWorld() { return coordinates; } diff --git a/src/test/java/amidst/mojangapi/mocking/FragmentCornerWalker.java b/src/test/java/amidst/mojangapi/mocking/FragmentCornerWalker.java index 290fcf005..928148b73 100644 --- a/src/test/java/amidst/mojangapi/mocking/FragmentCornerWalker.java +++ b/src/test/java/amidst/mojangapi/mocking/FragmentCornerWalker.java @@ -4,29 +4,29 @@ import amidst.documentation.Immutable; import amidst.fragment.Fragment; -import amidst.mojangapi.world.coordinates.CoordinatesInWorld; +import amidst.mojangapi.world.coordinates.Coordinates; @Immutable public class FragmentCornerWalker { public static FragmentCornerWalker walkFragmentsAroundOrigin(int fragmentsAroundOrigin) { long blocksAroundOrigin = fragmentsAroundOrigin * Fragment.SIZE; - CoordinatesInWorld startCorner = CoordinatesInWorld.from(-blocksAroundOrigin, -blocksAroundOrigin); - CoordinatesInWorld endCorner = startCorner.add(2 * blocksAroundOrigin, 2 * blocksAroundOrigin); + Coordinates startCorner = Coordinates.from(-blocksAroundOrigin, -blocksAroundOrigin); + Coordinates endCorner = startCorner.add(2 * blocksAroundOrigin, 2 * blocksAroundOrigin); return new FragmentCornerWalker(startCorner, endCorner); } - private final CoordinatesInWorld startCorner; - private final CoordinatesInWorld endCorner; + private final Coordinates startCorner; + private final Coordinates endCorner; - public FragmentCornerWalker(CoordinatesInWorld startCorner, CoordinatesInWorld endCorner) { + public FragmentCornerWalker(Coordinates startCorner, Coordinates endCorner) { this.startCorner = startCorner; this.endCorner = endCorner; } - public void walk(Consumer consumer) { + public void walk(Consumer consumer) { for (long x = startCorner.getX(); x < endCorner.getX(); x += Fragment.SIZE) { for (long y = startCorner.getY(); y < endCorner.getY(); y += Fragment.SIZE) { - consumer.accept(CoordinatesInWorld.from(x, y)); + consumer.accept(Coordinates.from(x, y)); } } } diff --git a/src/test/java/amidst/mojangapi/world/testworld/storage/json/CoordinatesCollectionJson.java b/src/test/java/amidst/mojangapi/world/testworld/storage/json/CoordinatesCollectionJson.java index bb9ecfeb8..a887ca37e 100644 --- a/src/test/java/amidst/mojangapi/world/testworld/storage/json/CoordinatesCollectionJson.java +++ b/src/test/java/amidst/mojangapi/world/testworld/storage/json/CoordinatesCollectionJson.java @@ -11,7 +11,7 @@ import amidst.logging.AmidstMessageBox; import amidst.mojangapi.mocking.FragmentCornerWalker; import amidst.mojangapi.world.World; -import amidst.mojangapi.world.coordinates.CoordinatesInWorld; +import amidst.mojangapi.world.coordinates.Coordinates; import amidst.mojangapi.world.icon.WorldIcon; import amidst.mojangapi.world.icon.producer.NameFilteredWorldIconCollector; import amidst.mojangapi.world.icon.producer.WorldIconProducer; @@ -19,7 +19,7 @@ @Immutable public class CoordinatesCollectionJson { public static CoordinatesCollectionJson extractWorldSpawn(World world) { - CoordinatesInWorld spawn = world.getSpawnWorldIcon().getCoordinates(); + Coordinates spawn = world.getSpawnWorldIcon().getCoordinates(); return new CoordinatesCollectionJson(createSortedSet(spawn)); } @@ -31,13 +31,13 @@ public static CoordinatesCollectionJson extractStrongholds(World world) { public static CoordinatesCollectionJson extractWorldIcons( WorldIconProducer producer, String name, - Function additionalDataFactory, + Function additionalDataFactory, int fragmentsAroundOrigin, int minimalNumberOfCoordinates) { NameFilteredWorldIconCollector consumer = new NameFilteredWorldIconCollector(name); FragmentCornerWalker.walkFragmentsAroundOrigin(fragmentsAroundOrigin).walk( corner -> producer.produce(corner, consumer, additionalDataFactory.apply(corner))); - SortedSet coordinates = createSortedSet(consumer.get()); + SortedSet coordinates = createSortedSet(consumer.get()); if (coordinates.size() < minimalNumberOfCoordinates) { String message = "not enough coordinates for '" + name + "'"; AmidstLogger.error(message); @@ -46,32 +46,32 @@ public static CoordinatesCollectionJson extractWorldIcons( return new CoordinatesCollectionJson(coordinates); } - private static SortedSet createSortedSet(List icons) { - SortedSet result = new TreeSet<>(); + private static SortedSet createSortedSet(List icons) { + SortedSet result = new TreeSet<>(); for (WorldIcon icon : icons) { result.add(icon.getCoordinates()); } return result; } - private static SortedSet createSortedSet(CoordinatesInWorld coordinates) { - SortedSet result = new TreeSet<>(); + private static SortedSet createSortedSet(Coordinates coordinates) { + SortedSet result = new TreeSet<>(); result.add(coordinates); return result; } - private volatile SortedSet coordinates; + private volatile SortedSet coordinates; @GsonConstructor public CoordinatesCollectionJson() { } - public CoordinatesCollectionJson(SortedSet coordinates) { + public CoordinatesCollectionJson(SortedSet coordinates) { this.coordinates = coordinates; } - public boolean contains(CoordinatesInWorld coordinates) { - for (CoordinatesInWorld coordinatesArrayEntry : this.coordinates) { + public boolean contains(Coordinates coordinates) { + for (Coordinates coordinatesArrayEntry : this.coordinates) { if (coordinates.equals(coordinatesArrayEntry)) { return true; } 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..3c8d1e5b5 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 @@ -7,26 +7,26 @@ import amidst.documentation.GsonConstructor; import amidst.documentation.Immutable; import amidst.mojangapi.mocking.FragmentCornerWalker; -import amidst.mojangapi.world.coordinates.CoordinatesInWorld; +import amidst.mojangapi.world.coordinates.Coordinates; import amidst.mojangapi.world.oracle.EndIsland; import amidst.mojangapi.world.oracle.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 55d42c28eb84a8350fffa062d900711f4c4ccd51 Mon Sep 17 00:00:00 2001 From: moulins <> Date: Fri, 23 Jun 2017 12:48:32 +0200 Subject: [PATCH 02/13] Add WorldOptions type This type bundles the WorldSeed, the WorldType and the String configuration of a World into a single object --- src/main/java/amidst/gui/main/Actions.java | 10 +- .../java/amidst/gui/main/WorldSwitcher.java | 8 +- .../main/viewer/PerViewerFacadeInjector.java | 4 +- .../amidst/gui/main/viewer/ViewerFacade.java | 12 +- .../amidst/gui/seedsearcher/SeedSearcher.java | 17 +- .../gui/seedsearcher/SeedSearcherWindow.java | 9 +- .../mojangapi/RunningLauncherProfile.java | 7 +- .../java/amidst/mojangapi/world/World.java | 40 ++--- .../amidst/mojangapi/world/WorldBuilder.java | 31 ++-- .../amidst/mojangapi/world/WorldOptions.java | 41 +++++ .../mojangapi/mocking/FakeWorldBuilder.java | 154 +++++++++--------- .../world/testworld/TestWorldDeclaration.java | 9 +- .../storage/json/WorldMetadataJson.java | 6 +- 13 files changed, 187 insertions(+), 161 deletions(-) create mode 100644 src/main/java/amidst/mojangapi/world/WorldOptions.java diff --git a/src/main/java/amidst/gui/main/Actions.java b/src/main/java/amidst/gui/main/Actions.java index 0fc02fae9..f2d851b7e 100644 --- a/src/main/java/amidst/gui/main/Actions.java +++ b/src/main/java/amidst/gui/main/Actions.java @@ -21,6 +21,7 @@ import amidst.gui.main.viewer.ViewerFacade; import amidst.gui.seedsearcher.SeedSearcherWindow; import amidst.logging.AmidstLogger; +import amidst.mojangapi.world.WorldOptions; import amidst.mojangapi.world.WorldSeed; import amidst.mojangapi.world.WorldType; import amidst.mojangapi.world.coordinates.Coordinates; @@ -73,7 +74,7 @@ public void newFromRandom() { private void newFromSeed(WorldSeed worldSeed) { WorldType worldType = dialogs.askForWorldType(); if (worldType != null) { - worldSwitcher.displayWorld(worldSeed, worldType); + worldSwitcher.displayWorld(new WorldOptions(worldSeed, worldType)); } } @@ -204,7 +205,7 @@ public void howCanIMoveAPlayer() { public void copySeedToClipboard() { ViewerFacade viewerFacade = viewerFacadeSupplier.get(); if (viewerFacade != null) { - String seed = "" + viewerFacade.getWorldSeed().getLong(); + String seed = "" + viewerFacade.getWorldOptions().getWorldSeed().getLong(); StringSelection selection = new StringSelection(seed); Toolkit.getDefaultToolkit().getSystemClipboard().setContents(selection, selection); } @@ -214,9 +215,10 @@ public void copySeedToClipboard() { public void takeScreenshot() { ViewerFacade viewerFacade = viewerFacadeSupplier.get(); if (viewerFacade != null) { + WorldOptions worldOptions = viewerFacade.getWorldOptions(); BufferedImage image = viewerFacade.createScreenshot(); - String suggestedFilename = "screenshot_" + viewerFacade.getWorldType().getFilenameText() + "_" - + viewerFacade.getWorldSeed().getLong() + ".png"; + String suggestedFilename = "screenshot_" + worldOptions.getWorldType().getFilenameText() + "_" + + worldOptions.getWorldSeed().getLong() + ".png"; File file = dialogs.askForScreenshotSaveFile(suggestedFilename); if (file != null) { file = appendPNGFileExtensionIfNecessary(file); diff --git a/src/main/java/amidst/gui/main/WorldSwitcher.java b/src/main/java/amidst/gui/main/WorldSwitcher.java index 485db0a0f..ce245b2a7 100644 --- a/src/main/java/amidst/gui/main/WorldSwitcher.java +++ b/src/main/java/amidst/gui/main/WorldSwitcher.java @@ -20,8 +20,7 @@ import amidst.mojangapi.file.MinecraftInstallation; import amidst.mojangapi.minecraftinterface.MinecraftInterfaceException; import amidst.mojangapi.world.World; -import amidst.mojangapi.world.WorldSeed; -import amidst.mojangapi.world.WorldType; +import amidst.mojangapi.world.WorldOptions; import amidst.mojangapi.world.player.MovablePlayerList; import amidst.mojangapi.world.player.WorldPlayerType; import amidst.parsing.FormatException; @@ -62,10 +61,10 @@ public WorldSwitcher( } @CalledOnlyBy(AmidstThread.EDT) - public void displayWorld(WorldSeed worldSeed, WorldType worldType) { + public void displayWorld(WorldOptions worldOptions) { try { clearViewerFacade(); - setWorld(runningLauncherProfile.createWorldFromSeed(worldSeed, worldType)); + setWorld(runningLauncherProfile.createWorld(worldOptions)); } catch (IllegalStateException | MinecraftInterfaceException e) { AmidstLogger.warn(e); dialogs.displayError(e); @@ -135,4 +134,5 @@ private void setViewerFacade(ViewerFacade viewerFacade) { public void clearWorld() { clearViewerFacade(); } + } diff --git a/src/main/java/amidst/gui/main/viewer/PerViewerFacadeInjector.java b/src/main/java/amidst/gui/main/viewer/PerViewerFacadeInjector.java index 3de0e451d..485ac473d 100644 --- a/src/main/java/amidst/gui/main/viewer/PerViewerFacadeInjector.java +++ b/src/main/java/amidst/gui/main/viewer/PerViewerFacadeInjector.java @@ -29,6 +29,7 @@ import amidst.gui.main.viewer.widget.Widget.CornerAnchorPoint; import amidst.gui.main.viewer.widget.WidgetManager; import amidst.mojangapi.world.World; +import amidst.mojangapi.world.WorldOptions; import amidst.mojangapi.world.export.WorldExporter; import amidst.mojangapi.world.export.WorldExporterConfiguration; import amidst.threading.WorkerExecutor; @@ -49,11 +50,12 @@ private static List createWidgets( AmidstSettings settings, Supplier progressText) { // @formatter:off + WorldOptions worldOptions = world.getWorldOptions(); return Arrays.asList( new ChangeableTextWidget( CornerAnchorPoint.CENTER, progressText), new FpsWidget( CornerAnchorPoint.BOTTOM_LEFT, new FramerateTimer(2), settings.showFPS), new ScaleWidget( CornerAnchorPoint.BOTTOM_CENTER, zoom, settings.showScale), - new SeedAndWorldTypeWidget( CornerAnchorPoint.TOP_LEFT, world.getWorldSeed(), world.getWorldType()), + new SeedAndWorldTypeWidget( CornerAnchorPoint.TOP_LEFT, worldOptions.getWorldSeed(), worldOptions.getWorldType()), new SelectedIconWidget( CornerAnchorPoint.TOP_LEFT, worldIconSelection), new DebugWidget( CornerAnchorPoint.BOTTOM_RIGHT, graph, fragmentManager, settings.showDebug, accelerationCounter), new CursorInformationWidget(CornerAnchorPoint.TOP_RIGHT, graph, translator, settings.dimension), diff --git a/src/main/java/amidst/gui/main/viewer/ViewerFacade.java b/src/main/java/amidst/gui/main/viewer/ViewerFacade.java index 85d00be44..e668e06d4 100644 --- a/src/main/java/amidst/gui/main/viewer/ViewerFacade.java +++ b/src/main/java/amidst/gui/main/viewer/ViewerFacade.java @@ -14,8 +14,7 @@ import amidst.fragment.layer.LayerReloader; import amidst.mojangapi.world.Dimension; import amidst.mojangapi.world.World; -import amidst.mojangapi.world.WorldSeed; -import amidst.mojangapi.world.WorldType; +import amidst.mojangapi.world.WorldOptions; import amidst.mojangapi.world.coordinates.Coordinates; import amidst.mojangapi.world.export.WorldExporter; import amidst.mojangapi.world.export.WorldExporterConfiguration; @@ -139,13 +138,8 @@ public void selectWorldIcon(WorldIcon worldIcon) { } @CalledOnlyBy(AmidstThread.EDT) - public WorldSeed getWorldSeed() { - return world.getWorldSeed(); - } - - @CalledOnlyBy(AmidstThread.EDT) - public WorldType getWorldType() { - return world.getWorldType(); + public WorldOptions getWorldOptions() { + return world.getWorldOptions(); } @CalledOnlyBy(AmidstThread.EDT) diff --git a/src/main/java/amidst/gui/seedsearcher/SeedSearcher.java b/src/main/java/amidst/gui/seedsearcher/SeedSearcher.java index f1e4fa858..9f2aa8dfb 100644 --- a/src/main/java/amidst/gui/seedsearcher/SeedSearcher.java +++ b/src/main/java/amidst/gui/seedsearcher/SeedSearcher.java @@ -10,6 +10,7 @@ import amidst.mojangapi.RunningLauncherProfile; import amidst.mojangapi.minecraftinterface.MinecraftInterfaceException; import amidst.mojangapi.world.World; +import amidst.mojangapi.world.WorldOptions; import amidst.mojangapi.world.WorldSeed; import amidst.threading.WorkerExecutor; import amidst.threading.worker.ProgressReporter; @@ -35,14 +36,14 @@ public SeedSearcher( } @CalledOnlyBy(AmidstThread.EDT) - public void search(SeedSearcherConfiguration configuration, Consumer onWorldSeedFound) { + public void search(SeedSearcherConfiguration configuration, Consumer onWorldFound) { this.isSearching = true; this.isStopRequested = false; - workerExecutor.run(createSearcher(configuration), onWorldSeedFound); + workerExecutor.run(createSearcher(configuration), onWorldFound); } @CalledOnlyBy(AmidstThread.EDT) - private ProgressReportingWorker createSearcher(SeedSearcherConfiguration configuration) { + private ProgressReportingWorker createSearcher(SeedSearcherConfiguration configuration) { return reporter -> this.trySearch(reporter, configuration); } @@ -67,7 +68,7 @@ public boolean isStopRequested() { } @CalledOnlyBy(AmidstThread.WORKER) - private void trySearch(ProgressReporter reporter, SeedSearcherConfiguration configuration) { + private void trySearch(ProgressReporter reporter, SeedSearcherConfiguration configuration) { try { doSearch(reporter, configuration); } catch (IllegalStateException | MinecraftInterfaceException e) { @@ -80,7 +81,7 @@ private void trySearch(ProgressReporter reporter, SeedSearcherConfigu } @CalledOnlyBy(AmidstThread.WORKER) - private void doSearch(ProgressReporter reporter, SeedSearcherConfiguration configuration) + private void doSearch(ProgressReporter reporter, SeedSearcherConfiguration configuration) throws IllegalStateException, MinecraftInterfaceException { do { @@ -89,13 +90,13 @@ private void doSearch(ProgressReporter reporter, SeedSearcherConfigur } @CalledOnlyBy(AmidstThread.WORKER) - private void doSearchOne(ProgressReporter reporter, SeedSearcherConfiguration configuration) + private void doSearchOne(ProgressReporter reporter, SeedSearcherConfiguration configuration) throws IllegalStateException, MinecraftInterfaceException { while (!isStopRequested) { - World world = runningLauncherProfile.createWorldFromSeed(WorldSeed.random(), configuration.getWorldType()); + World world = runningLauncherProfile.createWorld(new WorldOptions(WorldSeed.random(), configuration.getWorldType())); if (configuration.getWorldFilter().isValid(world)) { - reporter.report(world.getWorldSeed()); + reporter.report(world.getWorldOptions()); world.dispose(); break; } diff --git a/src/main/java/amidst/gui/seedsearcher/SeedSearcherWindow.java b/src/main/java/amidst/gui/seedsearcher/SeedSearcherWindow.java index 536d5bc8f..5fee2d3ac 100644 --- a/src/main/java/amidst/gui/seedsearcher/SeedSearcherWindow.java +++ b/src/main/java/amidst/gui/seedsearcher/SeedSearcherWindow.java @@ -21,7 +21,7 @@ import amidst.gui.main.WorldSwitcher; import amidst.logging.AmidstLogger; import amidst.mojangapi.file.json.filter.WorldFilterJson_MatchAll; -import amidst.mojangapi.world.WorldSeed; +import amidst.mojangapi.world.WorldOptions; import amidst.mojangapi.world.WorldType; import amidst.mojangapi.world.filter.WorldFilter; import net.miginfocom.swing.MigLayout; @@ -116,8 +116,7 @@ private void searchButtonClicked() { Optional configuration = createSeedSearcherConfiguration(); if (configuration.isPresent()) { SeedSearcherConfiguration seedSearcherConfiguration = configuration.get(); - WorldType worldType = seedSearcherConfiguration.getWorldType(); - seedSearcher.search(seedSearcherConfiguration, worldSeed -> seedFound(worldSeed, worldType)); + seedSearcher.search(seedSearcherConfiguration, world -> seedFound(world)); } else { AmidstLogger.warn("invalid configuration"); dialogs.displayError("invalid configuration"); @@ -143,8 +142,8 @@ private SeedSearcherConfiguration createSeedSearcherConfiguration(WorldFilter wo } @CalledOnlyBy(AmidstThread.EDT) - private void seedFound(WorldSeed worldSeed, WorldType worldType) { - worldSwitcher.displayWorld(worldSeed, worldType); + private void seedFound(WorldOptions worldOptions) { + worldSwitcher.displayWorld(worldOptions); updateGUI(); } diff --git a/src/main/java/amidst/mojangapi/RunningLauncherProfile.java b/src/main/java/amidst/mojangapi/RunningLauncherProfile.java index 58819ff98..7d43d3c08 100644 --- a/src/main/java/amidst/mojangapi/RunningLauncherProfile.java +++ b/src/main/java/amidst/mojangapi/RunningLauncherProfile.java @@ -13,8 +13,7 @@ import amidst.mojangapi.minecraftinterface.local.LocalMinecraftInterfaceCreationException; import amidst.mojangapi.world.World; import amidst.mojangapi.world.WorldBuilder; -import amidst.mojangapi.world.WorldSeed; -import amidst.mojangapi.world.WorldType; +import amidst.mojangapi.world.WorldOptions; @ThreadSafe public class RunningLauncherProfile { @@ -63,11 +62,11 @@ public RunningLauncherProfile createSilentPlayerlessCopy() { * one world at a time. Creating a new world will break all previously * created world objects. */ - public synchronized World createWorldFromSeed(WorldSeed worldSeed, WorldType worldType) + public synchronized World createWorld(WorldOptions worldOptions) throws IllegalStateException, MinecraftInterfaceException { if (currentWorld == null) { - currentWorld = worldBuilder.fromSeed(minecraftInterface, this::unlock, worldSeed, worldType); + currentWorld = worldBuilder.from(minecraftInterface, this::unlock, worldOptions); return currentWorld; } else { throw new IllegalStateException( diff --git a/src/main/java/amidst/mojangapi/world/World.java b/src/main/java/amidst/mojangapi/world/World.java index 6891a38b1..7b947e914 100644 --- a/src/main/java/amidst/mojangapi/world/World.java +++ b/src/main/java/amidst/mojangapi/world/World.java @@ -7,11 +7,13 @@ import amidst.mojangapi.minecraftinterface.RecognisedVersion; import amidst.mojangapi.world.icon.WorldIcon; import amidst.mojangapi.world.icon.producer.CachedWorldIconProducer; +import amidst.mojangapi.world.icon.producer.SpawnProducer; 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.player.MovablePlayerList; import amidst.mojangapi.world.versionfeatures.VersionFeatures; @@ -19,9 +21,7 @@ public class World { private final Consumer onDisposeWorld; - private final WorldSeed worldSeed; - private final WorldType worldType; - private final String generatorOptions; + private final WorldOptions worldOptions; private final MovablePlayerList movablePlayerList; private final RecognisedVersion recognisedVersion; private final VersionFeatures versionFeatures; @@ -29,6 +29,7 @@ public class World { private final BiomeDataOracle biomeDataOracle; private final EndIslandOracle endIslandOracle; private final SlimeChunkOracle slimeChunkOracle; + private final WorldSpawnOracle worldSpawnOracle; private final CachedWorldIconProducer spawnProducer; private final CachedWorldIconProducer strongholdProducer; private final CachedWorldIconProducer playerProducer; @@ -38,19 +39,17 @@ public class World { private final WorldIconProducer oceanMonumentProducer; private final WorldIconProducer netherFortressProducer; private final WorldIconProducer> endCityProducer; - + public World( Consumer onDisposeWorld, - WorldSeed worldSeed, - WorldType worldType, - String generatorOptions, + WorldOptions worldOptions, MovablePlayerList movablePlayerList, RecognisedVersion recognisedVersion, VersionFeatures versionFeatures, BiomeDataOracle biomeDataOracle, EndIslandOracle endIslandOracle, SlimeChunkOracle slimeChunkOracle, - CachedWorldIconProducer spawnProducer, + WorldSpawnOracle worldSpawnOracle, CachedWorldIconProducer strongholdProducer, CachedWorldIconProducer playerProducer, WorldIconProducer villageProducer, @@ -60,16 +59,15 @@ public World( WorldIconProducer netherFortressProducer, WorldIconProducer> endCityProducer) { this.onDisposeWorld = onDisposeWorld; - this.worldSeed = worldSeed; - this.worldType = worldType; - this.generatorOptions = generatorOptions; + this.worldOptions = worldOptions; this.movablePlayerList = movablePlayerList; this.recognisedVersion = recognisedVersion; this.versionFeatures = versionFeatures; this.biomeDataOracle = biomeDataOracle; this.endIslandOracle = endIslandOracle; this.slimeChunkOracle = slimeChunkOracle; - this.spawnProducer = spawnProducer; + this.worldSpawnOracle = worldSpawnOracle; + this.spawnProducer = new SpawnProducer(worldSpawnOracle); this.strongholdProducer = strongholdProducer; this.playerProducer = playerProducer; this.villageProducer = villageProducer; @@ -79,17 +77,9 @@ public World( this.netherFortressProducer = netherFortressProducer; this.endCityProducer = endCityProducer; } - - public WorldSeed getWorldSeed() { - return worldSeed; - } - - public WorldType getWorldType() { - return worldType; - } - - public String getGeneratorOptions() { - return generatorOptions; + + public WorldOptions getWorldOptions() { + return worldOptions; } public MovablePlayerList getMovablePlayerList() { @@ -115,6 +105,10 @@ public EndIslandOracle getEndIslandOracle() { public SlimeChunkOracle getSlimeChunkOracle() { return slimeChunkOracle; } + + public WorldSpawnOracle getSpawnOracle() { + return worldSpawnOracle; + } public WorldIconProducer getSpawnProducer() { return spawnProducer; diff --git a/src/main/java/amidst/mojangapi/world/WorldBuilder.java b/src/main/java/amidst/mojangapi/world/WorldBuilder.java index ba11182de..7c1979244 100644 --- a/src/main/java/amidst/mojangapi/world/WorldBuilder.java +++ b/src/main/java/amidst/mojangapi/world/WorldBuilder.java @@ -16,7 +16,6 @@ import amidst.mojangapi.world.icon.locationchecker.TempleLocationChecker; import amidst.mojangapi.world.icon.locationchecker.VillageLocationChecker; import amidst.mojangapi.world.icon.producer.PlayerProducer; -import amidst.mojangapi.world.icon.producer.SpawnProducer; import amidst.mojangapi.world.icon.producer.StructureProducer; import amidst.mojangapi.world.icon.type.DefaultWorldIconTypes; import amidst.mojangapi.world.icon.type.EndCityWorldIconTypeProvider; @@ -54,24 +53,21 @@ public WorldBuilder(PlayerInformationProvider playerInformationProvider, SeedHis this.seedHistoryLogger = seedHistoryLogger; } - public World fromSeed( + public World from( MinecraftInterface minecraftInterface, Consumer onDisposeWorld, - WorldSeed worldSeed, - WorldType worldType) throws MinecraftInterfaceException { + WorldOptions worldOptions) throws MinecraftInterfaceException { BiomeDataOracle biomeDataOracle = new BiomeDataOracle(minecraftInterface); VersionFeatures versionFeatures = DefaultVersionFeatures.create(minecraftInterface.getRecognisedVersion()); return create( minecraftInterface, onDisposeWorld, - worldSeed, - worldType, - "", + worldOptions, MovablePlayerList.dummy(), versionFeatures, biomeDataOracle, new HeuristicWorldSpawnOracle( - worldSeed.getLong(), + worldOptions.getWorldSeed().getLong(), biomeDataOracle, versionFeatures.getValidBiomesForStructure_Spawn())); } @@ -88,9 +84,7 @@ public World fromSaveGame(MinecraftInterface minecraftInterface, Consumer return create( minecraftInterface, onDisposeWorld, - WorldSeed.fromSaveGame(saveGame.getSeed()), - saveGame.getWorldType(), - saveGame.getGeneratorOptions(), + WorldOptions.fromSaveGame(saveGame), movablePlayerList, versionFeatures, new BiomeDataOracle(minecraftInterface), @@ -100,29 +94,26 @@ public World fromSaveGame(MinecraftInterface minecraftInterface, Consumer private World create( MinecraftInterface minecraftInterface, Consumer onDisposeWorld, - WorldSeed worldSeed, - WorldType worldType, - String generatorOptions, + WorldOptions worldOptions, MovablePlayerList movablePlayerList, VersionFeatures versionFeatures, BiomeDataOracle biomeDataOracle, WorldSpawnOracle worldSpawnOracle) throws MinecraftInterfaceException { RecognisedVersion recognisedVersion = minecraftInterface.getRecognisedVersion(); - seedHistoryLogger.log(recognisedVersion, worldSeed); + WorldSeed worldSeed = worldOptions.getWorldSeed(); long seed = worldSeed.getLong(); - minecraftInterface.createWorld(seed, worldType, generatorOptions); + seedHistoryLogger.log(recognisedVersion, worldSeed); + minecraftInterface.createWorld(seed, worldOptions.getWorldType(), worldOptions.getGeneratorOptions()); return new World( onDisposeWorld, - worldSeed, - worldType, - generatorOptions, + worldOptions, movablePlayerList, recognisedVersion, versionFeatures, biomeDataOracle, EndIslandOracle.from(seed), new SlimeChunkOracle(seed), - new SpawnProducer(worldSpawnOracle), + worldSpawnOracle, versionFeatures.getStrongholdProducerFactory().apply( seed, biomeDataOracle, diff --git a/src/main/java/amidst/mojangapi/world/WorldOptions.java b/src/main/java/amidst/mojangapi/world/WorldOptions.java new file mode 100644 index 000000000..09506a998 --- /dev/null +++ b/src/main/java/amidst/mojangapi/world/WorldOptions.java @@ -0,0 +1,41 @@ +package amidst.mojangapi.world; + +import amidst.documentation.Immutable; +import amidst.mojangapi.file.SaveGame; + +@Immutable +public class WorldOptions { + private final WorldSeed worldSeed; + private final WorldType worldType; + private final String genOptions; + + public WorldOptions(WorldSeed seed, WorldType type, String generatorOptions) { + worldSeed = seed; + worldType = type; + genOptions = generatorOptions; + } + + public WorldOptions(WorldSeed seed, WorldType type) { + this(seed, type, ""); + } + + public WorldOptions withGeneratorOptions(String generatorOptions) { + return new WorldOptions(worldSeed, worldType, generatorOptions); + } + + public static WorldOptions fromSaveGame(SaveGame saveGame) { + return new WorldOptions(WorldSeed.fromSaveGame(saveGame.getSeed()), saveGame.getWorldType(), saveGame.getGeneratorOptions()); + } + + public WorldSeed getWorldSeed() { + return worldSeed; + } + + public WorldType getWorldType() { + return worldType; + } + + public String getGeneratorOptions() { + return genOptions; + } +} diff --git a/src/test/java/amidst/mojangapi/mocking/FakeWorldBuilder.java b/src/test/java/amidst/mojangapi/mocking/FakeWorldBuilder.java index e9b384bdd..d54d89567 100644 --- a/src/test/java/amidst/mojangapi/mocking/FakeWorldBuilder.java +++ b/src/test/java/amidst/mojangapi/mocking/FakeWorldBuilder.java @@ -1,75 +1,79 @@ -package amidst.mojangapi.mocking; - -import java.util.Map; -import java.util.function.Consumer; - -import amidst.documentation.ThreadSafe; -import amidst.mojangapi.minecraftinterface.MinecraftInterface; -import amidst.mojangapi.minecraftinterface.MinecraftInterfaceException; -import amidst.mojangapi.world.World; -import amidst.mojangapi.world.WorldBuilder; -import amidst.mojangapi.world.WorldSeed; -import amidst.mojangapi.world.testworld.TestWorldDeclaration; -import amidst.mojangapi.world.testworld.TestWorldEntryNames; -import amidst.mojangapi.world.testworld.file.TestWorldDirectory; -import amidst.mojangapi.world.testworld.file.TestWorldDirectoryDeclaration; -import amidst.mojangapi.world.testworld.storage.json.BiomeDataJson; -import amidst.mojangapi.world.testworld.storage.json.WorldMetadataJson; - -@ThreadSafe -public class FakeWorldBuilder { - public static FakeWorldBuilder create(TestWorldDirectoryDeclaration directoryDeclaration) { - return new FakeWorldBuilder(WorldBuilder.createSilentPlayerless(), directoryDeclaration); - } - - private static final Consumer NOOP = disposedWorld -> { - }; - private final WorldBuilder builder; - private final TestWorldDirectoryDeclaration directoryDeclaration; - - public FakeWorldBuilder(WorldBuilder builder, TestWorldDirectoryDeclaration directoryDeclaration) { - this.builder = builder; - this.directoryDeclaration = directoryDeclaration; - } - - public World createRealWorld(TestWorldDeclaration worldDeclaration, MinecraftInterface realMinecraftInterface) - throws MinecraftInterfaceException { - return builder.fromSeed( - realMinecraftInterface, - NOOP, - worldDeclaration.getWorldSeed(), - worldDeclaration.getWorldType()); - } - - public World createFakeWorld(TestWorldDirectory worldDeclaration) throws MinecraftInterfaceException { - Map data = worldDeclaration.getData(); - // @formatter:off - WorldMetadataJson worldMetadata = get(TestWorldEntryNames.METADATA, WorldMetadataJson.class, data); - BiomeDataJson quarterBiomeData = get(TestWorldEntryNames.QUARTER_RESOLUTION_BIOME_DATA, BiomeDataJson.class, data); - BiomeDataJson fullBiomeData = get(TestWorldEntryNames.FULL_RESOLUTION_BIOME_DATA, BiomeDataJson.class, data); - // @formatter:on - return createFakeWorld(worldMetadata, quarterBiomeData, fullBiomeData); - } - - private T get(String name, Class clazz, Map data) { - return directoryDeclaration.getEntryDeclaration(name, clazz).extractFromDataMap(data); - } - - private World createFakeWorld( - WorldMetadataJson worldMetadata, - BiomeDataJson quarterBiomeData, - BiomeDataJson fullBiomeData) throws MinecraftInterfaceException { - return builder.fromSeed( - createFakeMinecraftInterface(worldMetadata, quarterBiomeData, fullBiomeData), - NOOP, - WorldSeed.fromUserInput(worldMetadata.getSeed() + ""), - worldMetadata.getWorldType()); - } - - private static FakeMinecraftInterface createFakeMinecraftInterface( - WorldMetadataJson worldMetadataJson, - BiomeDataJson quarterBiomeData, - BiomeDataJson fullBiomeData) { - return new FakeMinecraftInterface(worldMetadataJson, quarterBiomeData, fullBiomeData); - } -} +package amidst.mojangapi.mocking; + +import java.util.Map; +import java.util.function.Consumer; + +import amidst.documentation.ThreadSafe; +import amidst.mojangapi.minecraftinterface.MinecraftInterface; +import amidst.mojangapi.minecraftinterface.MinecraftInterfaceException; +import amidst.mojangapi.world.World; +import amidst.mojangapi.world.WorldBuilder; +import amidst.mojangapi.world.WorldOptions; +import amidst.mojangapi.world.WorldSeed; +import amidst.mojangapi.world.testworld.TestWorldDeclaration; +import amidst.mojangapi.world.testworld.TestWorldEntryNames; +import amidst.mojangapi.world.testworld.file.TestWorldDirectory; +import amidst.mojangapi.world.testworld.file.TestWorldDirectoryDeclaration; +import amidst.mojangapi.world.testworld.storage.json.BiomeDataJson; +import amidst.mojangapi.world.testworld.storage.json.WorldMetadataJson; + +@ThreadSafe +public class FakeWorldBuilder { + public static FakeWorldBuilder create(TestWorldDirectoryDeclaration directoryDeclaration) { + return new FakeWorldBuilder(WorldBuilder.createSilentPlayerless(), directoryDeclaration); + } + + private static final Consumer NOOP = disposedWorld -> { + }; + private final WorldBuilder builder; + private final TestWorldDirectoryDeclaration directoryDeclaration; + + public FakeWorldBuilder(WorldBuilder builder, TestWorldDirectoryDeclaration directoryDeclaration) { + this.builder = builder; + this.directoryDeclaration = directoryDeclaration; + } + + public World createRealWorld(TestWorldDeclaration worldDeclaration, MinecraftInterface realMinecraftInterface) + throws MinecraftInterfaceException { + return builder.from( + realMinecraftInterface, + NOOP, + worldDeclaration.getWorldOptions()); + } + + public World createFakeWorld(TestWorldDirectory worldDeclaration) throws MinecraftInterfaceException { + Map data = worldDeclaration.getData(); + // @formatter:off + WorldMetadataJson worldMetadata = get(TestWorldEntryNames.METADATA, WorldMetadataJson.class, data); + BiomeDataJson quarterBiomeData = get(TestWorldEntryNames.QUARTER_RESOLUTION_BIOME_DATA, BiomeDataJson.class, data); + BiomeDataJson fullBiomeData = get(TestWorldEntryNames.FULL_RESOLUTION_BIOME_DATA, BiomeDataJson.class, data); + // @formatter:on + return createFakeWorld(worldMetadata, quarterBiomeData, fullBiomeData); + } + + private T get(String name, Class clazz, Map data) { + return directoryDeclaration.getEntryDeclaration(name, clazz).extractFromDataMap(data); + } + + private World createFakeWorld( + WorldMetadataJson worldMetadata, + BiomeDataJson quarterBiomeData, + BiomeDataJson fullBiomeData) throws MinecraftInterfaceException { + WorldOptions worldOptions = new WorldOptions( + WorldSeed.fromUserInput(worldMetadata.getSeed() + ""), + worldMetadata.getWorldType() + ); + return builder.from( + createFakeMinecraftInterface(worldMetadata, quarterBiomeData, fullBiomeData), + NOOP, + worldOptions + ); + } + + private static FakeMinecraftInterface createFakeMinecraftInterface( + WorldMetadataJson worldMetadataJson, + BiomeDataJson quarterBiomeData, + BiomeDataJson fullBiomeData) { + return new FakeMinecraftInterface(worldMetadataJson, quarterBiomeData, fullBiomeData); + } +} diff --git a/src/test/java/amidst/mojangapi/world/testworld/TestWorldDeclaration.java b/src/test/java/amidst/mojangapi/world/testworld/TestWorldDeclaration.java index 29022e601..f44eaf005 100644 --- a/src/test/java/amidst/mojangapi/world/testworld/TestWorldDeclaration.java +++ b/src/test/java/amidst/mojangapi/world/testworld/TestWorldDeclaration.java @@ -8,6 +8,7 @@ import amidst.documentation.Immutable; import amidst.mojangapi.minecraftinterface.RecognisedVersion; +import amidst.mojangapi.world.WorldOptions; import amidst.mojangapi.world.WorldSeed; import amidst.mojangapi.world.WorldType; @@ -100,12 +101,10 @@ public RecognisedVersion getRecognisedVersion() { return recognisedVersion; } - public WorldSeed getWorldSeed() { - return worldSeed; - } - public WorldType getWorldType() { - return worldType; + + public WorldOptions getWorldOptions() { + return new WorldOptions(worldSeed, worldType); } public boolean isSupported(String name) { diff --git a/src/test/java/amidst/mojangapi/world/testworld/storage/json/WorldMetadataJson.java b/src/test/java/amidst/mojangapi/world/testworld/storage/json/WorldMetadataJson.java index 1c9035362..86e3b6f9d 100644 --- a/src/test/java/amidst/mojangapi/world/testworld/storage/json/WorldMetadataJson.java +++ b/src/test/java/amidst/mojangapi/world/testworld/storage/json/WorldMetadataJson.java @@ -1,7 +1,7 @@ package amidst.mojangapi.world.testworld.storage.json; -import amidst.documentation.GsonConstructor; import amidst.documentation.Immutable; +import amidst.documentation.GsonConstructor; import amidst.mojangapi.minecraftinterface.RecognisedVersion; import amidst.mojangapi.world.World; import amidst.mojangapi.world.WorldType; @@ -11,8 +11,8 @@ public class WorldMetadataJson { public static WorldMetadataJson from(World world) { return new WorldMetadataJson( world.getRecognisedVersion(), - world.getWorldSeed().getLong(), - world.getWorldType()); + world.getWorldOptions().getWorldSeed().getLong(), + world.getWorldOptions().getWorldType()); } private volatile RecognisedVersion recognisedVersion; From df7297fd73003915dac2d8a9ac1a3b4e3f9b324d Mon Sep 17 00:00:00 2001 From: moulins <> Date: Fri, 23 Jun 2017 13:07:02 +0200 Subject: [PATCH 03/13] Rework Gson annotations to have stricter parsing Replace @GsonConstructor by @GsonObject; add @JsonField. All json classes without custom parsing code must be annotated with @GsonConstructor; the parsing will give an error if the json object contains non-existing fields. --- .../amidst/documentation/GsonConstructor.java | 17 -- .../java/amidst/documentation/GsonObject.java | 22 +++ .../java/amidst/documentation/JsonField.java | 27 ++++ .../gui/main/UpdateInformationJson.java | 6 +- .../json/filter/WorldFilterJson_Biome.java | 4 +- .../json/filter/WorldFilterJson_MatchAll.java | 4 +- .../filter/WorldFilterJson_Structure.java | 4 +- .../launcherprofiles/LauncherProfileJson.java | 4 +- .../LauncherProfilesJson.java | 4 +- .../file/json/player/MetadataJson.java | 34 ++-- .../file/json/player/PlayerJson.java | 4 +- .../file/json/player/PropertyJson.java | 4 +- .../mojangapi/file/json/player/SKINJson.java | 4 +- .../file/json/player/SimplePlayerJson.java | 4 +- .../file/json/player/TexturesJson.java | 4 +- .../json/player/TexturesPropertyJson.java | 4 +- .../file/json/version/LibraryJson.java | 4 +- .../file/json/version/LibraryRuleJson.java | 4 +- .../file/json/version/LibraryRuleOsJson.java | 4 +- .../file/json/version/VersionJson.java | 4 +- .../versionlist/VersionListEntryJson.java | 4 +- .../json/versionlist/VersionListJson.java | 4 +- .../world/coordinates/Coordinates.java | 2 + .../amidst/parsing/json/GsonProvider.java | 36 +++++ .../java/amidst/parsing/json/JsonReader.java | 7 +- .../json/StrictTypeAdapterFactory.java | 152 ++++++++++++++++++ .../settings/biomeprofile/BiomeColorJson.java | 4 +- .../settings/biomeprofile/BiomeProfile.java | 4 +- .../testworld/storage/json/AreaJson.java | 4 +- .../testworld/storage/json/BiomeDataJson.java | 4 +- .../json/CoordinatesCollectionJson.java | 4 +- .../storage/json/EndIslandsJson.java | 4 +- .../storage/json/SlimeChunksJson.java | 4 +- .../storage/json/WorldMetadataJson.java | 4 +- 34 files changed, 311 insertions(+), 92 deletions(-) delete mode 100644 src/main/java/amidst/documentation/GsonConstructor.java create mode 100644 src/main/java/amidst/documentation/GsonObject.java create mode 100644 src/main/java/amidst/documentation/JsonField.java create mode 100644 src/main/java/amidst/parsing/json/GsonProvider.java create mode 100644 src/main/java/amidst/parsing/json/StrictTypeAdapterFactory.java diff --git a/src/main/java/amidst/documentation/GsonConstructor.java b/src/main/java/amidst/documentation/GsonConstructor.java deleted file mode 100644 index 92de5514e..000000000 --- a/src/main/java/amidst/documentation/GsonConstructor.java +++ /dev/null @@ -1,17 +0,0 @@ -package amidst.documentation; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * This annotation is added to no argument-constructors that are used by gson to - * deserialize object instances. - */ -@Retention(RetentionPolicy.SOURCE) -@Documented -@Target(ElementType.CONSTRUCTOR) -public @interface GsonConstructor { -} diff --git a/src/main/java/amidst/documentation/GsonObject.java b/src/main/java/amidst/documentation/GsonObject.java new file mode 100644 index 000000000..5ecdaac49 --- /dev/null +++ b/src/main/java/amidst/documentation/GsonObject.java @@ -0,0 +1,22 @@ +package amidst.documentation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * This annotation is added to classes which can be (de)serialised with GSON. + * These classes must have a no-argument constructor, which will be used by GSON + * to construct instances for deserialisation. + */ +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Target(ElementType.TYPE) +public @interface GsonObject { + /** + * Whether or not to ignore unknown fields when deserialising an instance. + */ + public boolean ignoreUnknown() default false; +} diff --git a/src/main/java/amidst/documentation/JsonField.java b/src/main/java/amidst/documentation/JsonField.java new file mode 100644 index 000000000..9f7533db5 --- /dev/null +++ b/src/main/java/amidst/documentation/JsonField.java @@ -0,0 +1,27 @@ +package amidst.documentation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * This annotation gives information on the fields of a JSON class. + * Used by amidst.util.StrictTypeAdapterFactory to enforce required fields. + */ +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Target({ ElementType.FIELD }) +public @interface JsonField { + + /** + * Whether or not this field is optional + */ + public boolean optional() default false; + + /** + * Other fields which must be present when this field is present + */ + public String[] require() default {}; +} diff --git a/src/main/java/amidst/gui/main/UpdateInformationJson.java b/src/main/java/amidst/gui/main/UpdateInformationJson.java index b6c9e95c1..794da3cc9 100644 --- a/src/main/java/amidst/gui/main/UpdateInformationJson.java +++ b/src/main/java/amidst/gui/main/UpdateInformationJson.java @@ -1,17 +1,17 @@ package amidst.gui.main; import amidst.AmidstVersion; -import amidst.documentation.GsonConstructor; +import amidst.documentation.GsonObject; import amidst.documentation.Immutable; @Immutable +@GsonObject public class UpdateInformationJson { private volatile int major; private volatile int minor; private volatile String message; private volatile String downloadUrl; - - @GsonConstructor + public UpdateInformationJson() { } diff --git a/src/main/java/amidst/mojangapi/file/json/filter/WorldFilterJson_Biome.java b/src/main/java/amidst/mojangapi/file/json/filter/WorldFilterJson_Biome.java index eb0eab1be..a4e1c0098 100644 --- a/src/main/java/amidst/mojangapi/file/json/filter/WorldFilterJson_Biome.java +++ b/src/main/java/amidst/mojangapi/file/json/filter/WorldFilterJson_Biome.java @@ -5,17 +5,17 @@ import java.util.List; import java.util.Set; -import amidst.documentation.GsonConstructor; +import amidst.documentation.GsonObject; import amidst.documentation.Immutable; import amidst.mojangapi.world.biome.Biome; import amidst.mojangapi.world.filter.WorldFilter_Biome; @Immutable +@GsonObject public class WorldFilterJson_Biome { private volatile long distance; private volatile List biomes = Collections.emptyList(); - @GsonConstructor public WorldFilterJson_Biome() { } diff --git a/src/main/java/amidst/mojangapi/file/json/filter/WorldFilterJson_MatchAll.java b/src/main/java/amidst/mojangapi/file/json/filter/WorldFilterJson_MatchAll.java index 5704944d5..341c633b4 100644 --- a/src/main/java/amidst/mojangapi/file/json/filter/WorldFilterJson_MatchAll.java +++ b/src/main/java/amidst/mojangapi/file/json/filter/WorldFilterJson_MatchAll.java @@ -6,7 +6,7 @@ import java.util.List; import java.util.Optional; -import amidst.documentation.GsonConstructor; +import amidst.documentation.GsonObject; import amidst.documentation.Immutable; import amidst.logging.AmidstLogger; import amidst.mojangapi.world.filter.WorldFilter; @@ -15,6 +15,7 @@ import amidst.parsing.json.JsonReader; @Immutable +@GsonObject public class WorldFilterJson_MatchAll { public static Optional from(String queryString) { try { @@ -27,7 +28,6 @@ public static Optional from(String queryString) { private volatile List biomeFilters = Collections.emptyList(); private volatile List structureFilters = Collections.emptyList(); - @GsonConstructor public WorldFilterJson_MatchAll() { } diff --git a/src/main/java/amidst/mojangapi/file/json/filter/WorldFilterJson_Structure.java b/src/main/java/amidst/mojangapi/file/json/filter/WorldFilterJson_Structure.java index 4cf46e93b..d112f3397 100644 --- a/src/main/java/amidst/mojangapi/file/json/filter/WorldFilterJson_Structure.java +++ b/src/main/java/amidst/mojangapi/file/json/filter/WorldFilterJson_Structure.java @@ -2,18 +2,18 @@ import java.util.List; -import amidst.documentation.GsonConstructor; +import amidst.documentation.GsonObject; import amidst.documentation.Immutable; import amidst.mojangapi.world.filter.WorldFilter_Structure; import amidst.mojangapi.world.icon.type.DefaultWorldIconTypes; @Immutable +@GsonObject public class WorldFilterJson_Structure { private volatile long distance; private volatile String structure; private volatile int minimum; - @GsonConstructor public WorldFilterJson_Structure() { } diff --git a/src/main/java/amidst/mojangapi/file/json/launcherprofiles/LauncherProfileJson.java b/src/main/java/amidst/mojangapi/file/json/launcherprofiles/LauncherProfileJson.java index e1ad22318..5e929c5f1 100644 --- a/src/main/java/amidst/mojangapi/file/json/launcherprofiles/LauncherProfileJson.java +++ b/src/main/java/amidst/mojangapi/file/json/launcherprofiles/LauncherProfileJson.java @@ -3,11 +3,12 @@ import java.util.Arrays; import java.util.List; -import amidst.documentation.GsonConstructor; +import amidst.documentation.GsonObject; import amidst.documentation.Immutable; import amidst.mojangapi.file.json.ReleaseType; @Immutable +@GsonObject public class LauncherProfileJson { /** * Some Minecraft installations have a profile with the key "(Default)" and @@ -25,7 +26,6 @@ public class LauncherProfileJson { private volatile String gameDir; private volatile List allowedReleaseTypes = Arrays.asList(ReleaseType.RELEASE); - @GsonConstructor public LauncherProfileJson() { } diff --git a/src/main/java/amidst/mojangapi/file/json/launcherprofiles/LauncherProfilesJson.java b/src/main/java/amidst/mojangapi/file/json/launcherprofiles/LauncherProfilesJson.java index 0d9877245..eb476e31d 100644 --- a/src/main/java/amidst/mojangapi/file/json/launcherprofiles/LauncherProfilesJson.java +++ b/src/main/java/amidst/mojangapi/file/json/launcherprofiles/LauncherProfilesJson.java @@ -3,14 +3,14 @@ import java.util.Collections; import java.util.Map; -import amidst.documentation.GsonConstructor; +import amidst.documentation.GsonObject; import amidst.documentation.Immutable; @Immutable +@GsonObject(ignoreUnknown=true) public class LauncherProfilesJson { private volatile Map profiles = Collections.emptyMap(); - @GsonConstructor public LauncherProfilesJson() { } diff --git a/src/main/java/amidst/mojangapi/file/json/player/MetadataJson.java b/src/main/java/amidst/mojangapi/file/json/player/MetadataJson.java index 250422909..64aa6deb9 100644 --- a/src/main/java/amidst/mojangapi/file/json/player/MetadataJson.java +++ b/src/main/java/amidst/mojangapi/file/json/player/MetadataJson.java @@ -1,17 +1,17 @@ -package amidst.mojangapi.file.json.player; - -import amidst.documentation.GsonConstructor; -import amidst.documentation.Immutable; - -@Immutable -public class MetadataJson { - private volatile String model; - - @GsonConstructor - public MetadataJson() { - } - - public String getModel() { - return model; - } -} +package amidst.mojangapi.file.json.player; + +import amidst.documentation.GsonObject; +import amidst.documentation.Immutable; + +@Immutable +@GsonObject +public class MetadataJson { + private volatile String model; + + public MetadataJson() { + } + + public String getModel() { + return model; + } +} diff --git a/src/main/java/amidst/mojangapi/file/json/player/PlayerJson.java b/src/main/java/amidst/mojangapi/file/json/player/PlayerJson.java index b3b3e4c93..2b7d52193 100644 --- a/src/main/java/amidst/mojangapi/file/json/player/PlayerJson.java +++ b/src/main/java/amidst/mojangapi/file/json/player/PlayerJson.java @@ -3,16 +3,16 @@ import java.util.Collections; import java.util.List; -import amidst.documentation.GsonConstructor; +import amidst.documentation.GsonObject; import amidst.documentation.Immutable; @Immutable +@GsonObject public class PlayerJson { private volatile String id; private volatile String name; private volatile List properties = Collections.emptyList(); - @GsonConstructor public PlayerJson() { } diff --git a/src/main/java/amidst/mojangapi/file/json/player/PropertyJson.java b/src/main/java/amidst/mojangapi/file/json/player/PropertyJson.java index 5ed0058e4..2b3f30ee4 100644 --- a/src/main/java/amidst/mojangapi/file/json/player/PropertyJson.java +++ b/src/main/java/amidst/mojangapi/file/json/player/PropertyJson.java @@ -1,14 +1,14 @@ package amidst.mojangapi.file.json.player; -import amidst.documentation.GsonConstructor; +import amidst.documentation.GsonObject; import amidst.documentation.Immutable; @Immutable +@GsonObject public class PropertyJson { private volatile String name; private volatile String value; - @GsonConstructor public PropertyJson() { } diff --git a/src/main/java/amidst/mojangapi/file/json/player/SKINJson.java b/src/main/java/amidst/mojangapi/file/json/player/SKINJson.java index bf46646f9..ad0c06acb 100644 --- a/src/main/java/amidst/mojangapi/file/json/player/SKINJson.java +++ b/src/main/java/amidst/mojangapi/file/json/player/SKINJson.java @@ -1,14 +1,14 @@ package amidst.mojangapi.file.json.player; -import amidst.documentation.GsonConstructor; +import amidst.documentation.GsonObject; import amidst.documentation.Immutable; @Immutable +@GsonObject public class SKINJson { private volatile String url; private volatile MetadataJson metadata; - @GsonConstructor public SKINJson() { } diff --git a/src/main/java/amidst/mojangapi/file/json/player/SimplePlayerJson.java b/src/main/java/amidst/mojangapi/file/json/player/SimplePlayerJson.java index 887b0ba82..98c1d5765 100644 --- a/src/main/java/amidst/mojangapi/file/json/player/SimplePlayerJson.java +++ b/src/main/java/amidst/mojangapi/file/json/player/SimplePlayerJson.java @@ -1,12 +1,12 @@ package amidst.mojangapi.file.json.player; -import amidst.documentation.GsonConstructor; +import amidst.documentation.GsonObject; +@GsonObject public class SimplePlayerJson { private String id; private String name; - @GsonConstructor public SimplePlayerJson() { } diff --git a/src/main/java/amidst/mojangapi/file/json/player/TexturesJson.java b/src/main/java/amidst/mojangapi/file/json/player/TexturesJson.java index 443b3d6a4..570a0f3da 100644 --- a/src/main/java/amidst/mojangapi/file/json/player/TexturesJson.java +++ b/src/main/java/amidst/mojangapi/file/json/player/TexturesJson.java @@ -1,13 +1,13 @@ package amidst.mojangapi.file.json.player; -import amidst.documentation.GsonConstructor; +import amidst.documentation.GsonObject; import amidst.documentation.Immutable; @Immutable +@GsonObject public class TexturesJson { private volatile SKINJson SKIN; - @GsonConstructor public TexturesJson() { } diff --git a/src/main/java/amidst/mojangapi/file/json/player/TexturesPropertyJson.java b/src/main/java/amidst/mojangapi/file/json/player/TexturesPropertyJson.java index 3b2a4d302..0fab2542a 100644 --- a/src/main/java/amidst/mojangapi/file/json/player/TexturesPropertyJson.java +++ b/src/main/java/amidst/mojangapi/file/json/player/TexturesPropertyJson.java @@ -1,13 +1,13 @@ package amidst.mojangapi.file.json.player; -import amidst.documentation.GsonConstructor; +import amidst.documentation.GsonObject; import amidst.documentation.Immutable; @Immutable +@GsonObject public class TexturesPropertyJson { private volatile TexturesJson textures; - @GsonConstructor public TexturesPropertyJson() { } diff --git a/src/main/java/amidst/mojangapi/file/json/version/LibraryJson.java b/src/main/java/amidst/mojangapi/file/json/version/LibraryJson.java index 698698fd2..785bbfab3 100644 --- a/src/main/java/amidst/mojangapi/file/json/version/LibraryJson.java +++ b/src/main/java/amidst/mojangapi/file/json/version/LibraryJson.java @@ -3,15 +3,15 @@ import java.util.Collections; import java.util.List; -import amidst.documentation.GsonConstructor; +import amidst.documentation.GsonObject; import amidst.documentation.Immutable; @Immutable +@GsonObject(ignoreUnknown=true) public class LibraryJson { private volatile String name; private volatile List rules = Collections.emptyList(); - @GsonConstructor public LibraryJson() { } diff --git a/src/main/java/amidst/mojangapi/file/json/version/LibraryRuleJson.java b/src/main/java/amidst/mojangapi/file/json/version/LibraryRuleJson.java index 537b74109..0bdb3e148 100644 --- a/src/main/java/amidst/mojangapi/file/json/version/LibraryRuleJson.java +++ b/src/main/java/amidst/mojangapi/file/json/version/LibraryRuleJson.java @@ -1,14 +1,14 @@ package amidst.mojangapi.file.json.version; -import amidst.documentation.GsonConstructor; +import amidst.documentation.GsonObject; import amidst.documentation.Immutable; @Immutable +@GsonObject public class LibraryRuleJson { private volatile String action; private volatile LibraryRuleOsJson os; - @GsonConstructor public LibraryRuleJson() { } diff --git a/src/main/java/amidst/mojangapi/file/json/version/LibraryRuleOsJson.java b/src/main/java/amidst/mojangapi/file/json/version/LibraryRuleOsJson.java index f2c0e57dd..4d6012f98 100644 --- a/src/main/java/amidst/mojangapi/file/json/version/LibraryRuleOsJson.java +++ b/src/main/java/amidst/mojangapi/file/json/version/LibraryRuleOsJson.java @@ -1,14 +1,14 @@ package amidst.mojangapi.file.json.version; -import amidst.documentation.GsonConstructor; +import amidst.documentation.GsonObject; import amidst.documentation.Immutable; @Immutable +@GsonObject public class LibraryRuleOsJson { private volatile String name; private volatile String version; - @GsonConstructor public LibraryRuleOsJson() { } diff --git a/src/main/java/amidst/mojangapi/file/json/version/VersionJson.java b/src/main/java/amidst/mojangapi/file/json/version/VersionJson.java index 56ad5cb75..7e8690236 100644 --- a/src/main/java/amidst/mojangapi/file/json/version/VersionJson.java +++ b/src/main/java/amidst/mojangapi/file/json/version/VersionJson.java @@ -3,16 +3,16 @@ import java.util.Collections; import java.util.List; -import amidst.documentation.GsonConstructor; +import amidst.documentation.GsonObject; import amidst.documentation.Immutable; @Immutable +@GsonObject(ignoreUnknown=true) public class VersionJson { private volatile String id; private volatile String inheritsFrom; private volatile List libraries = Collections.emptyList(); - @GsonConstructor public VersionJson() { } diff --git a/src/main/java/amidst/mojangapi/file/json/versionlist/VersionListEntryJson.java b/src/main/java/amidst/mojangapi/file/json/versionlist/VersionListEntryJson.java index 44f495319..c49957155 100644 --- a/src/main/java/amidst/mojangapi/file/json/versionlist/VersionListEntryJson.java +++ b/src/main/java/amidst/mojangapi/file/json/versionlist/VersionListEntryJson.java @@ -1,15 +1,15 @@ package amidst.mojangapi.file.json.versionlist; -import amidst.documentation.GsonConstructor; +import amidst.documentation.GsonObject; import amidst.documentation.Immutable; import amidst.mojangapi.file.json.ReleaseType; @Immutable +@GsonObject(ignoreUnknown=true) public class VersionListEntryJson { private volatile String id; private volatile ReleaseType type; - @GsonConstructor public VersionListEntryJson() { } diff --git a/src/main/java/amidst/mojangapi/file/json/versionlist/VersionListJson.java b/src/main/java/amidst/mojangapi/file/json/versionlist/VersionListJson.java index cd95c6ff6..2856f43b7 100644 --- a/src/main/java/amidst/mojangapi/file/json/versionlist/VersionListJson.java +++ b/src/main/java/amidst/mojangapi/file/json/versionlist/VersionListJson.java @@ -3,14 +3,14 @@ import java.util.Collections; import java.util.List; -import amidst.documentation.GsonConstructor; +import amidst.documentation.GsonObject; import amidst.documentation.Immutable; @Immutable +@GsonObject(ignoreUnknown=true) public class VersionListJson { private volatile List versions = Collections.emptyList(); - @GsonConstructor public VersionListJson() { } diff --git a/src/main/java/amidst/mojangapi/world/coordinates/Coordinates.java b/src/main/java/amidst/mojangapi/world/coordinates/Coordinates.java index 1a8d31759..eda258244 100644 --- a/src/main/java/amidst/mojangapi/world/coordinates/Coordinates.java +++ b/src/main/java/amidst/mojangapi/world/coordinates/Coordinates.java @@ -2,9 +2,11 @@ import java.awt.Point; +import amidst.documentation.GsonObject; import amidst.documentation.Immutable; @Immutable +@GsonObject public class Coordinates implements Comparable { public static Coordinates tryParse(String coordinates) { String[] parsedCoordinates = coordinates.replaceAll(" ", "").split(","); diff --git a/src/main/java/amidst/parsing/json/GsonProvider.java b/src/main/java/amidst/parsing/json/GsonProvider.java new file mode 100644 index 000000000..346b9ddb5 --- /dev/null +++ b/src/main/java/amidst/parsing/json/GsonProvider.java @@ -0,0 +1,36 @@ +package amidst.parsing.json; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +public enum GsonProvider { + ; + + + private static final Gson LENIENT = builder().create(); + private static final Gson STRICT = builderStrict().create(); + + public static Gson get() { + return LENIENT; + } + + public static Gson getStrict() { + return STRICT; + } + + private static GsonBuilder createBuilder(GsonBuilder base) { + // @formatter:off + return base + .enableComplexMapKeySerialization(); + // @formatter:on + } + + public static GsonBuilder builder() { + return createBuilder(new GsonBuilder()); + } + + public static GsonBuilder builderStrict() { + //We ensure that the strict verification is done AFTER the right subclasses have been detected. + return createBuilder(new GsonBuilder().registerTypeAdapterFactory(new StrictTypeAdapterFactory())); + } +} diff --git a/src/main/java/amidst/parsing/json/JsonReader.java b/src/main/java/amidst/parsing/json/JsonReader.java index 005b3e04a..48df8843a 100644 --- a/src/main/java/amidst/parsing/json/JsonReader.java +++ b/src/main/java/amidst/parsing/json/JsonReader.java @@ -5,7 +5,6 @@ import java.io.Reader; import java.net.URL; -import com.google.gson.Gson; import com.google.gson.JsonIOException; import com.google.gson.JsonSyntaxException; @@ -18,8 +17,6 @@ public enum JsonReader { ; - private static final Gson GSON = new Gson(); - @NotNull public static T readLocation(File location, Class clazz) throws FormatException, IOException { return readReader(URIUtils.newReader(location), clazz); @@ -38,7 +35,7 @@ public static T readLocation(String location, Class clazz) throws FormatE @NotNull private static T readReader(Reader reader, Class clazz) throws FormatException, IOException { try (Reader theReader = reader) { - T result = GSON.fromJson(theReader, clazz); + T result = GsonProvider.getStrict().fromJson(theReader, clazz); if (result != null) { return result; } else { @@ -54,7 +51,7 @@ private static T readReader(Reader reader, Class clazz) throws FormatExce @NotNull public static T readString(String string, Class clazz) throws FormatException { try { - T result = GSON.fromJson(string, clazz); + T result = GsonProvider.getStrict().fromJson(string, clazz); if (result != null) { return result; } else { diff --git a/src/main/java/amidst/parsing/json/StrictTypeAdapterFactory.java b/src/main/java/amidst/parsing/json/StrictTypeAdapterFactory.java new file mode 100644 index 000000000..bd8332527 --- /dev/null +++ b/src/main/java/amidst/parsing/json/StrictTypeAdapterFactory.java @@ -0,0 +1,152 @@ +package amidst.parsing.json; + +import java.io.IOException; +import java.lang.reflect.Field; +import java.security.InvalidParameterException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import com.google.gson.Gson; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import com.google.gson.JsonParser; +import com.google.gson.TypeAdapter; +import com.google.gson.TypeAdapterFactory; +import com.google.gson.annotations.SerializedName; +import com.google.gson.reflect.TypeToken; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonToken; +import com.google.gson.stream.JsonWriter; + +import amidst.documentation.GsonObject; +import amidst.documentation.JsonField; +import edu.emory.mathcs.backport.java.util.Arrays; + +/** + * This class generates TypeAdapters which verify that the json data contains all required fields + * (see amidst.documentation.JsonField) and doesn't contains any unknown fields. + */ +public class StrictTypeAdapterFactory implements TypeAdapterFactory { + + @SuppressWarnings("unchecked") + private static List> BASE_JSON_CLASSES = Arrays.asList(new Class[]{ + Map.class, + List.class, + String.class + }); + + @Override + public TypeAdapter create(Gson gson, TypeToken type) { + Class rawType = type.getRawType(); + + if(rawType.isPrimitive() || rawType.isEnum() || BASE_JSON_CLASSES.contains(rawType)) + return null; + + return new StrictTypeAdapter<>(gson.getDelegateAdapter(this, type), rawType); + } + + + private static class StrictTypeAdapter extends TypeAdapter { + private static final JsonParser PARSER = new JsonParser(); + + private TypeAdapter delegate; + private Class type; + private Map fields; + private boolean ignoreUnknown; + + public StrictTypeAdapter(TypeAdapter delegate, Class type) { + GsonObject annotation = type.getAnnotation(GsonObject.class); + if(annotation == null) + throw new IllegalArgumentException("the type " + type.getName() + " must have the GsonObject annotation"); + ignoreUnknown = annotation.ignoreUnknown(); + + this.delegate = delegate; + this.type = type; + this.fields = new HashMap<>(); + + while(type != Object.class) { + for(Field f: type.getDeclaredFields()) + this.fields.put(f.getName(), f); + type = type.getSuperclass(); + } + } + + @Override + public void write(JsonWriter out, T value) throws IOException { + delegate.write(out, value); + } + + @Override + public T read(JsonReader in) throws IOException { + if(in.peek() != JsonToken.BEGIN_OBJECT) + return delegate.read(in); + + JsonObject obj = PARSER.parse(in).getAsJsonObject(); + + int fieldCount = 0; + for(Field f: fields.values()) { + if(verifyField(obj, f, false)) + fieldCount++; + } + + if(!ignoreUnknown && fieldCount < obj.entrySet().size()) { + for(Entry e: obj.entrySet()) { + if(!fields.containsKey(e.getKey())) { + throw new JsonParseException("unknown field " + e.getKey() + " in type " + type.getName()); + } + } + } + + return delegate.fromJsonTree(obj); + } + + private boolean verifyField(JsonObject obj, Field f, boolean unconditionallyRequire) throws JsonParseException { + JsonField annotation = f.getAnnotation(JsonField.class); + + if(hasField(obj, f)) { + if(annotation == null) + return true; + + for(String name: annotation.require()) { + Field required = fields.get(name); + if(required == null) + throw new InvalidParameterException( + "the required field '" + name + "' doesn't exists in " + type.getName()); + + verifyField(obj, required, true); + } + return true; + + } else if(!annotation.optional() || unconditionallyRequire) { + throw new JsonParseException("field " + f.getName() + " is required for type " + type.getName()); + } + + return false; + } + + private boolean hasField(JsonObject obj, Field f) { + JsonElement attr = obj.get(f.getName()); + + if(attr != null) + return attr.isJsonNull() ? false : true; + + SerializedName annotation = f.getAnnotation(SerializedName.class); + if(annotation == null) + return true; + + if(obj.has(annotation.value())) + return true; + + for(String name: annotation.alternate()) { + if(obj.has(name)) + return true; + } + + return false; + } + } + +} diff --git a/src/main/java/amidst/settings/biomeprofile/BiomeColorJson.java b/src/main/java/amidst/settings/biomeprofile/BiomeColorJson.java index 7dc80a398..7f6db09ca 100644 --- a/src/main/java/amidst/settings/biomeprofile/BiomeColorJson.java +++ b/src/main/java/amidst/settings/biomeprofile/BiomeColorJson.java @@ -1,16 +1,16 @@ package amidst.settings.biomeprofile; -import amidst.documentation.GsonConstructor; +import amidst.documentation.GsonObject; import amidst.documentation.Immutable; import amidst.mojangapi.world.biome.BiomeColor; @Immutable +@GsonObject public class BiomeColorJson { private int r; private int g; private int b; - @GsonConstructor public BiomeColorJson() { } diff --git a/src/main/java/amidst/settings/biomeprofile/BiomeProfile.java b/src/main/java/amidst/settings/biomeprofile/BiomeProfile.java index 94fc2a132..6efe3668d 100644 --- a/src/main/java/amidst/settings/biomeprofile/BiomeProfile.java +++ b/src/main/java/amidst/settings/biomeprofile/BiomeProfile.java @@ -11,13 +11,14 @@ import java.util.SortedMap; import java.util.TreeMap; -import amidst.documentation.GsonConstructor; +import amidst.documentation.GsonObject; import amidst.documentation.Immutable; import amidst.logging.AmidstLogger; import amidst.mojangapi.world.biome.Biome; import amidst.mojangapi.world.biome.BiomeColor; @Immutable +@GsonObject public class BiomeProfile { private static Map createDefaultColorMap() { Map result = new HashMap<>(); @@ -37,7 +38,6 @@ public static BiomeProfile getDefaultProfile() { private volatile String shortcut; private volatile Map colorMap; - @GsonConstructor public BiomeProfile() { } diff --git a/src/test/java/amidst/mojangapi/world/testworld/storage/json/AreaJson.java b/src/test/java/amidst/mojangapi/world/testworld/storage/json/AreaJson.java index 5beb3b818..33f41645a 100644 --- a/src/test/java/amidst/mojangapi/world/testworld/storage/json/AreaJson.java +++ b/src/test/java/amidst/mojangapi/world/testworld/storage/json/AreaJson.java @@ -1,16 +1,16 @@ package amidst.mojangapi.world.testworld.storage.json; -import amidst.documentation.GsonConstructor; +import amidst.documentation.GsonObject; import amidst.documentation.Immutable; @Immutable +@GsonObject public class AreaJson implements Comparable { private volatile long x; private volatile long y; private volatile long width; private volatile long height; - @GsonConstructor public AreaJson() { } diff --git a/src/test/java/amidst/mojangapi/world/testworld/storage/json/BiomeDataJson.java b/src/test/java/amidst/mojangapi/world/testworld/storage/json/BiomeDataJson.java index 34fea5367..d626bce25 100644 --- a/src/test/java/amidst/mojangapi/world/testworld/storage/json/BiomeDataJson.java +++ b/src/test/java/amidst/mojangapi/world/testworld/storage/json/BiomeDataJson.java @@ -2,10 +2,11 @@ import java.util.SortedMap; -import amidst.documentation.GsonConstructor; +import amidst.documentation.GsonObject; import amidst.documentation.Immutable; @Immutable +@GsonObject public class BiomeDataJson { private static int[] short2int(short[] in) { int[] result = new int[in.length]; @@ -25,7 +26,6 @@ public static short[] int2short(int[] in) { private volatile SortedMap biomeData; - @GsonConstructor public BiomeDataJson() { } diff --git a/src/test/java/amidst/mojangapi/world/testworld/storage/json/CoordinatesCollectionJson.java b/src/test/java/amidst/mojangapi/world/testworld/storage/json/CoordinatesCollectionJson.java index a887ca37e..2555ce855 100644 --- a/src/test/java/amidst/mojangapi/world/testworld/storage/json/CoordinatesCollectionJson.java +++ b/src/test/java/amidst/mojangapi/world/testworld/storage/json/CoordinatesCollectionJson.java @@ -5,7 +5,7 @@ import java.util.TreeSet; import java.util.function.Function; -import amidst.documentation.GsonConstructor; +import amidst.documentation.GsonObject; import amidst.documentation.Immutable; import amidst.logging.AmidstLogger; import amidst.logging.AmidstMessageBox; @@ -17,6 +17,7 @@ import amidst.mojangapi.world.icon.producer.WorldIconProducer; @Immutable +@GsonObject public class CoordinatesCollectionJson { public static CoordinatesCollectionJson extractWorldSpawn(World world) { Coordinates spawn = world.getSpawnWorldIcon().getCoordinates(); @@ -62,7 +63,6 @@ private static SortedSet createSortedSet(Coordinates coordinates) { private volatile SortedSet coordinates; - @GsonConstructor public CoordinatesCollectionJson() { } 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 3c8d1e5b5..94a1db818 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 @@ -4,7 +4,7 @@ import java.util.SortedMap; import java.util.TreeMap; -import amidst.documentation.GsonConstructor; +import amidst.documentation.GsonObject; import amidst.documentation.Immutable; import amidst.mojangapi.mocking.FragmentCornerWalker; import amidst.mojangapi.world.coordinates.Coordinates; @@ -12,6 +12,7 @@ import amidst.mojangapi.world.oracle.EndIslandOracle; @Immutable +@GsonObject public class EndIslandsJson { public static EndIslandsJson extract(EndIslandOracle oracle, int fragmentsAroundOrigin) { SortedMap> result = new TreeMap<>(); @@ -22,7 +23,6 @@ public static EndIslandsJson extract(EndIslandOracle oracle, int fragmentsAround private volatile SortedMap> endIslands; - @GsonConstructor public EndIslandsJson() { } diff --git a/src/test/java/amidst/mojangapi/world/testworld/storage/json/SlimeChunksJson.java b/src/test/java/amidst/mojangapi/world/testworld/storage/json/SlimeChunksJson.java index 137a5b54d..6cf032a4d 100644 --- a/src/test/java/amidst/mojangapi/world/testworld/storage/json/SlimeChunksJson.java +++ b/src/test/java/amidst/mojangapi/world/testworld/storage/json/SlimeChunksJson.java @@ -2,13 +2,14 @@ import java.util.Arrays; -import amidst.documentation.GsonConstructor; +import amidst.documentation.GsonObject; import amidst.documentation.Immutable; import amidst.mojangapi.world.World; import amidst.mojangapi.world.coordinates.Resolution; import amidst.mojangapi.world.oracle.SlimeChunkOracle; @Immutable +@GsonObject public class SlimeChunksJson { public static SlimeChunksJson from(World world) { SlimeChunkOracle oracle = world.getSlimeChunkOracle(); @@ -24,7 +25,6 @@ public static SlimeChunksJson from(World world) { private volatile boolean[][] isSlimeChunk; - @GsonConstructor public SlimeChunksJson() { } diff --git a/src/test/java/amidst/mojangapi/world/testworld/storage/json/WorldMetadataJson.java b/src/test/java/amidst/mojangapi/world/testworld/storage/json/WorldMetadataJson.java index 86e3b6f9d..3f0ccd972 100644 --- a/src/test/java/amidst/mojangapi/world/testworld/storage/json/WorldMetadataJson.java +++ b/src/test/java/amidst/mojangapi/world/testworld/storage/json/WorldMetadataJson.java @@ -1,12 +1,13 @@ package amidst.mojangapi.world.testworld.storage.json; import amidst.documentation.Immutable; -import amidst.documentation.GsonConstructor; +import amidst.documentation.GsonObject; import amidst.mojangapi.minecraftinterface.RecognisedVersion; import amidst.mojangapi.world.World; import amidst.mojangapi.world.WorldType; @Immutable +@GsonObject public class WorldMetadataJson { public static WorldMetadataJson from(World world) { return new WorldMetadataJson( @@ -19,7 +20,6 @@ public static WorldMetadataJson from(World world) { private volatile long seed; private volatile WorldType worldType; - @GsonConstructor public WorldMetadataJson() { } From 35ab9cbb1620d6d744c26ccec3dc46e2662f5f0a Mon Sep 17 00:00:00 2001 From: moulins <> Date: Fri, 23 Jun 2017 13:45:13 +0200 Subject: [PATCH 04/13] Coordinates now use ints instead of longs; add an json adapter --- .../java/amidst/fragment/FragmentGraph.java | 5 +- .../BackgroundColorProvider.java | 2 +- .../colorprovider/BiomeColorProvider.java | 2 +- .../fragment/colorprovider/ColorProvider.java | 2 +- .../colorprovider/SlimeColorProvider.java | 2 +- .../colorprovider/TheEndColorProvider.java | 14 +- .../constructor/BiomeDataConstructor.java | 2 +- .../constructor/ImageConstructor.java | 2 +- .../fragment/drawer/WorldIconDrawer.java | 5 +- .../amidst/fragment/loader/ImageLoader.java | 8 +- src/main/java/amidst/gui/main/Actions.java | 10 +- .../FragmentGraphToScreenTranslator.java | 12 +- .../widget/CursorInformationWidget.java | 5 +- .../json/filter/WorldFilterJson_Biome.java | 4 +- .../filter/WorldFilterJson_Structure.java | 3 +- .../mojangapi/file/nbt/LevelDatNbt.java | 12 +- .../amidst/mojangapi/file/nbt/NBTUtils.java | 10 ++ .../file/nbt/player/PlayerLocationLoader.java | 6 +- .../world/coordinates/CoordinateUtils.java | 39 ------ .../world/coordinates/Coordinates.java | 121 +++++++++++------- .../coordinates/CoordinatesJsonAdapter.java | 67 ++++++++++ .../world/coordinates/Resolution.java | 36 +++++- .../mojangapi/world/filter/WorldFilter.java | 6 +- .../world/filter/WorldFilter_Biome.java | 2 +- .../world/filter/WorldFilter_MatchAll.java | 2 +- .../world/filter/WorldFilter_Structure.java | 6 +- .../producer/StrongholdProducer_Base.java | 4 +- .../icon/producer/StructureProducer.java | 6 +- .../world/oracle/EndIslandOracle.java | 6 +- .../amidst/mojangapi/world/player/Player.java | 2 +- .../world/player/PlayerCoordinates.java | 14 +- .../mocking/FragmentCornerWalker.java | 6 +- .../world/test/CoordinateUtilsTest.java | 25 ---- .../storage/json/SlimeChunksJson.java | 2 +- 34 files changed, 260 insertions(+), 190 deletions(-) delete mode 100644 src/main/java/amidst/mojangapi/world/coordinates/CoordinateUtils.java create mode 100644 src/main/java/amidst/mojangapi/world/coordinates/CoordinatesJsonAdapter.java delete mode 100644 src/test/java/amidst/mojangapi/world/test/CoordinateUtilsTest.java diff --git a/src/main/java/amidst/fragment/FragmentGraph.java b/src/main/java/amidst/fragment/FragmentGraph.java index face428af..e7d3180e2 100644 --- a/src/main/java/amidst/fragment/FragmentGraph.java +++ b/src/main/java/amidst/fragment/FragmentGraph.java @@ -7,6 +7,7 @@ import amidst.documentation.NotThreadSafe; import amidst.fragment.layer.LayerDeclaration; import amidst.mojangapi.world.coordinates.Coordinates; +import amidst.mojangapi.world.coordinates.Resolution; import amidst.mojangapi.world.icon.WorldIcon; import amidst.util.Lazy; @@ -40,7 +41,7 @@ private FragmentGraphItem create(Coordinates coordinates) { recycleAll(); fragmentsPerRow = 1; fragmentsPerColumn = 1; - return new FragmentGraphItem(fragmentManager.requestFragment(coordinates.toFragmentCorner())); + return new FragmentGraphItem(fragmentManager.requestFragment(coordinates.snapTo(Resolution.FRAGMENT))); } @CalledOnlyBy(AmidstThread.EDT) @@ -89,7 +90,7 @@ public WorldIcon getClosestWorldIcon(Coordinates coordinates, double maxDistance @CalledOnlyBy(AmidstThread.EDT) public Fragment getFragmentAt(Coordinates coordinates) { - Coordinates corner = coordinates.toFragmentCorner(); + Coordinates corner = coordinates.snapTo(Resolution.FRAGMENT); for (FragmentGraphItem fragmentGraphItem : topLeftFragment.getOrCreateValue()) { Fragment fragment = fragmentGraphItem.getFragment(); if (corner.equals(fragment.getCorner())) { diff --git a/src/main/java/amidst/fragment/colorprovider/BackgroundColorProvider.java b/src/main/java/amidst/fragment/colorprovider/BackgroundColorProvider.java index c8dac2d42..bd1c55075 100644 --- a/src/main/java/amidst/fragment/colorprovider/BackgroundColorProvider.java +++ b/src/main/java/amidst/fragment/colorprovider/BackgroundColorProvider.java @@ -17,7 +17,7 @@ public BackgroundColorProvider(BiomeColorProvider biomeColorProvider, TheEndColo } @Override - public int getColorAt(Dimension dimension, Fragment fragment, long cornerX, long cornerY, int x, int y) { + public int getColorAt(Dimension dimension, Fragment fragment, int cornerX, int cornerY, int x, int y) { if (dimension.equals(Dimension.OVERWORLD)) { return biomeColorProvider.getColorAt(dimension, fragment, cornerX, cornerY, x, y); } else if (dimension.equals(Dimension.END)) { diff --git a/src/main/java/amidst/fragment/colorprovider/BiomeColorProvider.java b/src/main/java/amidst/fragment/colorprovider/BiomeColorProvider.java index a5150b1a9..f549af147 100644 --- a/src/main/java/amidst/fragment/colorprovider/BiomeColorProvider.java +++ b/src/main/java/amidst/fragment/colorprovider/BiomeColorProvider.java @@ -18,7 +18,7 @@ public BiomeColorProvider(BiomeSelection biomeSelection, BiomeProfileSelection b } @Override - public int getColorAt(Dimension dimension, Fragment fragment, long cornerX, long cornerY, int x, int y) { + public int getColorAt(Dimension dimension, Fragment fragment, int cornerX, int cornerY, int x, int y) { return getColor(fragment.getBiomeDataAt(x, y)); } diff --git a/src/main/java/amidst/fragment/colorprovider/ColorProvider.java b/src/main/java/amidst/fragment/colorprovider/ColorProvider.java index f28372137..6925cf006 100644 --- a/src/main/java/amidst/fragment/colorprovider/ColorProvider.java +++ b/src/main/java/amidst/fragment/colorprovider/ColorProvider.java @@ -7,5 +7,5 @@ public interface ColorProvider { @CalledOnlyBy(AmidstThread.FRAGMENT_LOADER) - int getColorAt(Dimension dimension, Fragment fragment, long cornerX, long cornerY, int x, int y); + int getColorAt(Dimension dimension, Fragment fragment, int cornerX, int cornerY, int x, int y); } diff --git a/src/main/java/amidst/fragment/colorprovider/SlimeColorProvider.java b/src/main/java/amidst/fragment/colorprovider/SlimeColorProvider.java index d8f9e80b0..29b580bbc 100644 --- a/src/main/java/amidst/fragment/colorprovider/SlimeColorProvider.java +++ b/src/main/java/amidst/fragment/colorprovider/SlimeColorProvider.java @@ -17,7 +17,7 @@ public SlimeColorProvider(SlimeChunkOracle slimeChunkOracle) { } @Override - public int getColorAt(Dimension dimension, Fragment fragment, long cornerX, long cornerY, int x, int y) { + public int getColorAt(Dimension dimension, Fragment fragment, int cornerX, int cornerY, int x, int y) { if (slimeChunkOracle.isSlimeChunk(cornerX + x, cornerY + y)) { return SLIME_CHUNK_COLOR; } else { diff --git a/src/main/java/amidst/fragment/colorprovider/TheEndColorProvider.java b/src/main/java/amidst/fragment/colorprovider/TheEndColorProvider.java index 31e58be56..0d4ed9dcc 100644 --- a/src/main/java/amidst/fragment/colorprovider/TheEndColorProvider.java +++ b/src/main/java/amidst/fragment/colorprovider/TheEndColorProvider.java @@ -27,9 +27,9 @@ public class TheEndColorProvider implements ColorProvider { private static final float INFLUENCE_FADE_RANGE = INFLUENCE_FADE_START - INFLUENCE_FADE_FINISH; @Override - public int getColorAt(Dimension dimension, Fragment fragment, long cornerX, long cornerY, int x, int y) { - long xAsQuarter = cornerX + x; - long yAsQuarter = cornerY + y; + public int getColorAt(Dimension dimension, Fragment fragment, int cornerX, int cornerY, int x, int y) { + int xAsQuarter = cornerX + x; + int yAsQuarter = cornerY + y; return getColorAt( (int) (xAsQuarter << 2), (int) (yAsQuarter << 2), @@ -43,8 +43,8 @@ public int getColorAt(Dimension dimension, Fragment fragment, long cornerX, long private int getColorAt( int x, int y, - long chunkX, - long chunkY, + int chunkX, + int chunkY, int textureX, int textureY, List endIslands) { @@ -69,7 +69,7 @@ private float getMaxInfluence(int x, int y, List endIslands) { return result; } - private int getFadingColorAt(long chunkX, long chunkY, int textureX, int textureY, float maxInfluence) { + private int getFadingColorAt(int chunkX, int chunkY, int textureX, int textureY, float maxInfluence) { int result = VOID_TRANSPARENT_BLACK; if (showRockyShores(chunkX, chunkY)) { result = getRockyShoresTextureAt(textureX, textureY); @@ -91,7 +91,7 @@ private int getFadingColorAt(long chunkX, long chunkY, int textureX, int texture /** * Determine if the chunk may contain miniature islands. */ - private boolean showRockyShores(long chunkX, long chunkY) { + private boolean showRockyShores(int chunkX, int chunkY) { return (chunkX * chunkX + chunkY * chunkY) > 4096; } diff --git a/src/main/java/amidst/fragment/constructor/BiomeDataConstructor.java b/src/main/java/amidst/fragment/constructor/BiomeDataConstructor.java index 4ebf497fa..e04c76739 100644 --- a/src/main/java/amidst/fragment/constructor/BiomeDataConstructor.java +++ b/src/main/java/amidst/fragment/constructor/BiomeDataConstructor.java @@ -12,7 +12,7 @@ public class BiomeDataConstructor implements FragmentConstructor { @CalledOnlyBy(AmidstThread.EDT) public BiomeDataConstructor(Resolution resolution) { - this.size = resolution.getStepsPerFragment(); + this.size = resolution.getStepsPer(Resolution.FRAGMENT); } @CalledOnlyBy(AmidstThread.EDT) diff --git a/src/main/java/amidst/fragment/constructor/ImageConstructor.java b/src/main/java/amidst/fragment/constructor/ImageConstructor.java index b3316bf7e..2e561fbff 100644 --- a/src/main/java/amidst/fragment/constructor/ImageConstructor.java +++ b/src/main/java/amidst/fragment/constructor/ImageConstructor.java @@ -15,7 +15,7 @@ public class ImageConstructor implements FragmentConstructor { @CalledOnlyBy(AmidstThread.EDT) public ImageConstructor(Resolution resolution, int layerId) { - this.size = resolution.getStepsPerFragment(); + this.size = resolution.getStepsPer(Resolution.FRAGMENT); this.layerId = layerId; } diff --git a/src/main/java/amidst/fragment/drawer/WorldIconDrawer.java b/src/main/java/amidst/fragment/drawer/WorldIconDrawer.java index 5e9cfb58c..00f050e90 100644 --- a/src/main/java/amidst/fragment/drawer/WorldIconDrawer.java +++ b/src/main/java/amidst/fragment/drawer/WorldIconDrawer.java @@ -12,6 +12,7 @@ import amidst.gui.main.viewer.WorldIconSelection; import amidst.gui.main.viewer.Zoom; import amidst.mojangapi.world.coordinates.Coordinates; +import amidst.mojangapi.world.coordinates.Resolution; import amidst.mojangapi.world.icon.WorldIcon; @NotThreadSafe @@ -45,8 +46,8 @@ private void drawIcon(WorldIcon icon, double invZoom, Graphics2D g2d) { width *= 1.5; height *= 1.5; } - Coordinates coordinates = icon.getCoordinates(); - g2d.translate(coordinates.getXRelativeToFragment(), coordinates.getYRelativeToFragment()); + Coordinates coordinates = icon.getCoordinates().getRelativeTo(Resolution.FRAGMENT); + g2d.translate(coordinates.getX(), coordinates.getY()); g2d.scale(invZoom, invZoom); g2d.drawImage(image, -(width >> 1), -(height >> 1), width, height, null); } diff --git a/src/main/java/amidst/fragment/loader/ImageLoader.java b/src/main/java/amidst/fragment/loader/ImageLoader.java index 6f136476c..1b0a9eca9 100644 --- a/src/main/java/amidst/fragment/loader/ImageLoader.java +++ b/src/main/java/amidst/fragment/loader/ImageLoader.java @@ -26,7 +26,7 @@ public ImageLoader(LayerDeclaration declaration, Resolution resolution, ColorPro super(declaration); this.resolution = resolution; this.colorProvider = colorProvider; - this.size = resolution.getStepsPerFragment(); + this.size = resolution.getStepsPer(Resolution.FRAGMENT); this.rgbArray = new int[size * size]; this.bufferedImage = createBufferedImage(); } @@ -51,15 +51,15 @@ public void reload(Dimension dimension, Fragment fragment) { @CalledOnlyBy(AmidstThread.FRAGMENT_LOADER) private void doLoad(Dimension dimension, Fragment fragment) { Coordinates corner = fragment.getCorner(); - long cornerX = corner.getXAs(resolution); - long cornerY = corner.getYAs(resolution); + int cornerX = corner.getXAs(resolution); + int cornerY = corner.getYAs(resolution); drawToCache(dimension, fragment, cornerX, cornerY); bufferedImage.setRGB(0, 0, size, size, rgbArray, 0, size); bufferedImage = fragment.getAndSetImage(declaration.getLayerId(), bufferedImage); } @CalledOnlyBy(AmidstThread.FRAGMENT_LOADER) - private void drawToCache(Dimension dimension, Fragment fragment, long cornerX, long cornerY) { + private void drawToCache(Dimension dimension, Fragment fragment, int cornerX, int cornerY) { for (int y = 0; y < size; y++) { for (int x = 0; x < size; x++) { int index = getCacheIndex(x, y); diff --git a/src/main/java/amidst/gui/main/Actions.java b/src/main/java/amidst/gui/main/Actions.java index f2d851b7e..9c213e679 100644 --- a/src/main/java/amidst/gui/main/Actions.java +++ b/src/main/java/amidst/gui/main/Actions.java @@ -315,10 +315,12 @@ public void movePlayer(Player player, Coordinates targetCoordinates) { ViewerFacade viewerFacade = viewerFacadeSupplier.get(); if (viewerFacade != null) { PlayerCoordinates currentCoordinates = player.getPlayerCoordinates(); - long currentHeight = currentCoordinates.getY(); + + int currentHeight = currentCoordinates.getY(); String input = dialogs.askForPlayerHeight(currentHeight); + if (input != null) { - player.moveTo(targetCoordinates, tryParseLong(input, currentHeight), currentCoordinates.getDimension()); + player.moveTo(targetCoordinates, tryParseint(input, currentHeight), currentCoordinates.getDimension()); viewerFacade.reloadPlayerLayer(); if (dialogs.askToConfirmYesNo("Save Player Locations", "Do you want to save the player locations?")) { if (dialogs.askToConfirmSaveGameManipulation()) { @@ -330,9 +332,9 @@ public void movePlayer(Player player, Coordinates targetCoordinates) { } @CalledOnlyBy(AmidstThread.EDT) - private long tryParseLong(String text, long defaultValue) { + private int tryParseint(String text, int defaultValue) { try { - return Long.parseLong(text); + return Integer.parseInt(text); } catch (NumberFormatException e) { return defaultValue; } diff --git a/src/main/java/amidst/gui/main/viewer/FragmentGraphToScreenTranslator.java b/src/main/java/amidst/gui/main/viewer/FragmentGraphToScreenTranslator.java index e23e01d14..ad7d6a4d2 100644 --- a/src/main/java/amidst/gui/main/viewer/FragmentGraphToScreenTranslator.java +++ b/src/main/java/amidst/gui/main/viewer/FragmentGraphToScreenTranslator.java @@ -8,6 +8,7 @@ import amidst.fragment.Fragment; import amidst.fragment.FragmentGraph; import amidst.mojangapi.world.coordinates.Coordinates; +import amidst.mojangapi.world.coordinates.Resolution; @NotThreadSafe public class FragmentGraphToScreenTranslator { @@ -81,11 +82,10 @@ public void centerOn(final Coordinates coordinates) { graph.init(coordinates); int xCenterOnScreen = viewerWidth >> 1; int yCenterOnScreen = viewerHeight >> 1; - long xFragmentRelative = coordinates.getXRelativeToFragment(); - long yFragmentRelative = coordinates.getYRelativeToFragment(); + Coordinates fragmentRelative = coordinates.getRelativeTo(Resolution.FRAGMENT); setTopLeftOnScreen( - xCenterOnScreen - zoom.worldToScreen(xFragmentRelative), - yCenterOnScreen - zoom.worldToScreen(yFragmentRelative)); + xCenterOnScreen - zoom.worldToScreen(fragmentRelative.getX()), + yCenterOnScreen - zoom.worldToScreen(fragmentRelative.getY())); } @CalledOnlyBy(AmidstThread.EDT) @@ -128,7 +128,7 @@ public double getTopOnScreen() { public Coordinates screenToWorld(Point pointOnScreen) { Coordinates corner = graph.getCorner(); return corner.add( - (long) zoom.screenToWorld(pointOnScreen.x - leftOnScreen), - (long) zoom.screenToWorld(pointOnScreen.y - topOnScreen)); + (int) zoom.screenToWorld(pointOnScreen.x - leftOnScreen), + (int) zoom.screenToWorld(pointOnScreen.y - topOnScreen)); } } 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 628f84c0c..c4d38f360 100644 --- a/src/main/java/amidst/gui/main/viewer/widget/CursorInformationWidget.java +++ b/src/main/java/amidst/gui/main/viewer/widget/CursorInformationWidget.java @@ -69,9 +69,8 @@ private String getBiomeNameAt(Coordinates coordinates) { private String getOverworldBiomeNameAt(Coordinates coordinates) { Fragment fragment = graph.getFragmentAt(coordinates); if (fragment != null && fragment.isLoaded()) { - long x = coordinates.getXRelativeToFragmentAs(Resolution.QUARTER); - long y = coordinates.getYRelativeToFragmentAs(Resolution.QUARTER); - short biome = fragment.getBiomeDataAt((int) x, (int) y); + Coordinates c = coordinates.getRelativeTo(Resolution.FRAGMENT).getAs(Resolution.QUARTER); + short biome = fragment.getBiomeDataAt(c.getX(), c.getY()); try { return Biome.getByIndex(biome).getName(); } catch (UnknownBiomeIndexException e) { diff --git a/src/main/java/amidst/mojangapi/file/json/filter/WorldFilterJson_Biome.java b/src/main/java/amidst/mojangapi/file/json/filter/WorldFilterJson_Biome.java index a4e1c0098..d537dba3b 100644 --- a/src/main/java/amidst/mojangapi/file/json/filter/WorldFilterJson_Biome.java +++ b/src/main/java/amidst/mojangapi/file/json/filter/WorldFilterJson_Biome.java @@ -13,6 +13,7 @@ @Immutable @GsonObject public class WorldFilterJson_Biome { + @SuppressWarnings("unused") private volatile long distance; private volatile List biomes = Collections.emptyList(); @@ -32,9 +33,10 @@ public void validate(List notifications) { } public WorldFilter_Biome createBiomeFilter() { - return new WorldFilter_Biome(distance, createValidBiomeIndexes()); + return null; } + @SuppressWarnings("unused") private Set createValidBiomeIndexes() { Set result = new HashSet<>(); for (String name : biomes) { diff --git a/src/main/java/amidst/mojangapi/file/json/filter/WorldFilterJson_Structure.java b/src/main/java/amidst/mojangapi/file/json/filter/WorldFilterJson_Structure.java index d112f3397..8848feffa 100644 --- a/src/main/java/amidst/mojangapi/file/json/filter/WorldFilterJson_Structure.java +++ b/src/main/java/amidst/mojangapi/file/json/filter/WorldFilterJson_Structure.java @@ -10,6 +10,7 @@ @Immutable @GsonObject public class WorldFilterJson_Structure { + @SuppressWarnings("unused") private volatile long distance; private volatile String structure; private volatile int minimum; @@ -27,6 +28,6 @@ public void validate(List notifications) { } public WorldFilter_Structure createStructureFilter() { - return new WorldFilter_Structure(distance, DefaultWorldIconTypes.getByName(structure), minimum); + return null; } } diff --git a/src/main/java/amidst/mojangapi/file/nbt/LevelDatNbt.java b/src/main/java/amidst/mojangapi/file/nbt/LevelDatNbt.java index 357f6b075..e183c21ed 100644 --- a/src/main/java/amidst/mojangapi/file/nbt/LevelDatNbt.java +++ b/src/main/java/amidst/mojangapi/file/nbt/LevelDatNbt.java @@ -7,11 +7,12 @@ import amidst.documentation.Immutable; import amidst.mojangapi.world.WorldType; -import amidst.mojangapi.world.coordinates.Coordinates; import amidst.parsing.FormatException; +import amidst.mojangapi.world.coordinates.Coordinates; @Immutable public class LevelDatNbt { + public static LevelDatNbt from(File file) throws IOException, FormatException { try { CompoundTag dataTag = readDataTag(NBTUtils.readTagFromFile(file)); @@ -34,16 +35,17 @@ private static long readRandomSeed(CompoundTag dataTag) { return NBTUtils.getLongValue(dataTag.getValue().get(NBTTagKeys.TAG_KEY_RANDOM_SEED)); } + private static Coordinates readWorldSpawn(CompoundTag dataTag) { return Coordinates.from(readSpawnX(dataTag), readSpawnZ(dataTag)); } - private static long readSpawnX(CompoundTag dataTag) { - return NBTUtils.getLongValue(dataTag.getValue().get(NBTTagKeys.TAG_KEY_SPAWN_X)); + private static int readSpawnX(CompoundTag dataTag) { + return NBTUtils.getIntValue(dataTag.getValue().get(NBTTagKeys.TAG_KEY_SPAWN_X)); } - private static long readSpawnZ(CompoundTag dataTag) { - return NBTUtils.getLongValue(dataTag.getValue().get(NBTTagKeys.TAG_KEY_SPAWN_Z)); + private static int readSpawnZ(CompoundTag dataTag) { + return NBTUtils.getIntValue(dataTag.getValue().get(NBTTagKeys.TAG_KEY_SPAWN_Z)); } private static WorldType readWorldType(CompoundTag dataTag) { diff --git a/src/main/java/amidst/mojangapi/file/nbt/NBTUtils.java b/src/main/java/amidst/mojangapi/file/nbt/NBTUtils.java index 33ce98ec1..ad5e7b2c6 100644 --- a/src/main/java/amidst/mojangapi/file/nbt/NBTUtils.java +++ b/src/main/java/amidst/mojangapi/file/nbt/NBTUtils.java @@ -47,4 +47,14 @@ public static long getLongValue(Tag tag) { "cannot read long value from the class '" + tag.getClass().getName() + "'"); } } + + public static int getIntValue(Tag tag) { + Object value = tag.getValue(); + if (value instanceof Number) { + return ((Number) value).intValue(); + } else { + throw new IllegalArgumentException( + "cannot read int value from the class '" + tag.getClass().getName() + "'"); + } + } } diff --git a/src/main/java/amidst/mojangapi/file/nbt/player/PlayerLocationLoader.java b/src/main/java/amidst/mojangapi/file/nbt/player/PlayerLocationLoader.java index 8e64471a3..86b8000a0 100644 --- a/src/main/java/amidst/mojangapi/file/nbt/player/PlayerLocationLoader.java +++ b/src/main/java/amidst/mojangapi/file/nbt/player/PlayerLocationLoader.java @@ -52,9 +52,9 @@ private static PlayerCoordinates readPlayerCoordinates(CompoundTag tag) { int dimensionId = getTagDimension(tag).getValue(); List posList = getTagPos(tag).getValue(); return PlayerCoordinates.fromNBTFile( - (long) (double) (Double) posList.get(0).getValue(), - (long) (double) (Double) posList.get(1).getValue(), - (long) (double) (Double) posList.get(2).getValue(), + (int) (double) (Double) posList.get(0).getValue(), + (int) (double) (Double) posList.get(1).getValue(), + (int) (double) (Double) posList.get(2).getValue(), dimensionId); } diff --git a/src/main/java/amidst/mojangapi/world/coordinates/CoordinateUtils.java b/src/main/java/amidst/mojangapi/world/coordinates/CoordinateUtils.java deleted file mode 100644 index 1bca3a2a3..000000000 --- a/src/main/java/amidst/mojangapi/world/coordinates/CoordinateUtils.java +++ /dev/null @@ -1,39 +0,0 @@ -package amidst.mojangapi.world.coordinates; - -import amidst.documentation.Immutable; -import amidst.fragment.Fragment; - -@Immutable -public enum CoordinateUtils { - ; - - public static boolean isInBounds(long x, long y, long offsetX, long offsetY, long width, long height) { - return x >= offsetX && x < offsetX + width && y >= offsetY && y < offsetY + height; - } - - /** - * @return world coordinates relative to its fragment corner (corrected - * modulo) - */ - public static long toFragmentRelative(long inWorld) { - return modulo(inWorld, Fragment.SIZE); - } - - /** - * @return world coordinates (addition) - */ - public static long toWorld(long inWorldOfFragment, long inFragment) { - return inWorldOfFragment + inFragment; - } - - /** - * @return world coordinates of the corner of the given coordinates fragment - */ - public static long toFragmentCorner(long inWorld) { - return inWorld - modulo(inWorld, Fragment.SIZE); - } - - private static long modulo(long a, long b) { - return ((a % b) + b) % b; - } -} diff --git a/src/main/java/amidst/mojangapi/world/coordinates/Coordinates.java b/src/main/java/amidst/mojangapi/world/coordinates/Coordinates.java index eda258244..654b0264b 100644 --- a/src/main/java/amidst/mojangapi/world/coordinates/Coordinates.java +++ b/src/main/java/amidst/mojangapi/world/coordinates/Coordinates.java @@ -2,125 +2,145 @@ import java.awt.Point; +import com.google.gson.annotations.JsonAdapter; + import amidst.documentation.GsonObject; import amidst.documentation.Immutable; @Immutable +@JsonAdapter(CoordinatesJsonAdapter.class) @GsonObject public class Coordinates implements Comparable { + public static Coordinates tryParse(String coordinates) { String[] parsedCoordinates = coordinates.replaceAll(" ", "").split(","); if (parsedCoordinates.length != 2) { return null; } try { - return Coordinates.from(Long.parseLong(parsedCoordinates[0]), Long.parseLong(parsedCoordinates[1])); + return Coordinates.from( + Integer.parseInt(parsedCoordinates[0]), + Integer.parseInt(parsedCoordinates[1])); } catch (NumberFormatException e) { return null; } } - public static Coordinates from(long xInWorld, long yInWorld) { + public static Coordinates from(int xInWorld, int yInWorld) { return new Coordinates(xInWorld, yInWorld); } - public static Coordinates from(long xAsResolution, long yAsResolution, Resolution resolution) { + public static Coordinates from(int xAsResolution, + int yAsResolution, Resolution resolution) { return new Coordinates( resolution.convertFromThisToWorld(xAsResolution), resolution.convertFromThisToWorld(yAsResolution)); } + + public static Coordinates from(Coordinates coordAsResolution, Resolution resolution) { + return Coordinates.from(coordAsResolution.getX(), coordAsResolution.getY(), resolution); + } - private static Coordinates from(Coordinates base, long deltaXInWorld, long deltaYInWorld) { - return new Coordinates(base.xInWorld + deltaXInWorld, base.yInWorld + deltaYInWorld); + private static Coordinates from(Coordinates base, + int deltaXInWorld, int deltaYInWorld) { + return new Coordinates(base.xInWorld + deltaXInWorld, + base.yInWorld + deltaYInWorld); } public static Coordinates origin() { return ORIGIN; } - private static final Coordinates ORIGIN = Coordinates.from(0, 0); + private static final Coordinates ORIGIN = Coordinates.from(0, + 0); - private final long xInWorld; - private final long yInWorld; + private final int xInWorld; + private final int yInWorld; - public Coordinates(long xInWorld, long yInWorld) { + public Coordinates(int xInWorld, int yInWorld) { this.xInWorld = xInWorld; this.yInWorld = yInWorld; } - public long getX() { + public int getX() { return xInWorld; } - public long getY() { + public int getY() { return yInWorld; } - public long getXAs(Resolution targetResolution) { + public int getXAs(Resolution targetResolution) { return targetResolution.convertFromWorldToThis(xInWorld); } - public long getYAs(Resolution targetResolution) { + public int getYAs(Resolution targetResolution) { return targetResolution.convertFromWorldToThis(yInWorld); } - - public long getXCornerOfFragment() { - return CoordinateUtils.toFragmentCorner(xInWorld); + + public Coordinates getAs(Resolution targetResolution) { + return Coordinates.from(getXAs(targetResolution), getYAs(targetResolution)); } - - public long getYCornerOfFragment() { - return CoordinateUtils.toFragmentCorner(yInWorld); + + public int snapXTo(Resolution targetResolution) { + return targetResolution.snapToResolution(xInWorld); } - - public long getXCornerOfFragmentAs(Resolution targetResolution) { - return targetResolution.convertFromWorldToThis(CoordinateUtils.toFragmentCorner(xInWorld)); + + public int snapYTo(Resolution targetResolution) { + return targetResolution.snapToResolution(yInWorld); } - - public long getYCornerOfFragmentAs(Resolution targetResolution) { - return targetResolution.convertFromWorldToThis(CoordinateUtils.toFragmentCorner(yInWorld)); + + public Coordinates snapTo(Resolution targetResolution) { + return Coordinates.from(snapXTo(targetResolution), snapYTo(targetResolution)); } - - public long getXRelativeToFragment() { - return CoordinateUtils.toFragmentRelative(xInWorld); + + public int snapUpwardsXTo(Resolution targetResolution) { + return targetResolution.snapUpwardsToResolution(xInWorld); } - - public long getYRelativeToFragment() { - return CoordinateUtils.toFragmentRelative(yInWorld); + + public int snapUpwardsYTo(Resolution targetResolution) { + return targetResolution.snapUpwardsToResolution(yInWorld); } - public long getXRelativeToFragmentAs(Resolution targetResolution) { - return targetResolution.convertFromWorldToThis(CoordinateUtils.toFragmentRelative(xInWorld)); + public Coordinates snapUpwardsTo(Resolution targetResolution) { + return Coordinates.from(snapUpwardsXTo(targetResolution), snapUpwardsYTo(targetResolution)); } - - public long getYRelativeToFragmentAs(Resolution targetResolution) { - return targetResolution.convertFromWorldToThis(CoordinateUtils.toFragmentRelative(yInWorld)); + public int getXRelativeTo(Resolution resolution) { + return resolution.toRelative(xInWorld); } - public Coordinates toFragmentCorner() { - return from(getXCornerOfFragment(), getYCornerOfFragment()); + public int getYRelativeTo(Resolution resolution) { + return resolution.toRelative(yInWorld); + } + + public Coordinates getRelativeTo(Resolution resolution) { + return Coordinates.from(getXRelativeTo(resolution), getYRelativeTo(resolution)); } public double getDistance(Coordinates other) { - return Point.distance(xInWorld, yInWorld, other.xInWorld, other.yInWorld); + return Point.distance(xInWorld, yInWorld, other.xInWorld, + other.yInWorld); } - public double getDistance(long xInWorld, long yInWorld) { + public double getDistance(int xInWorld, int yInWorld) { return Point.distance(this.xInWorld, this.yInWorld, xInWorld, yInWorld); } public double getDistanceSq(Coordinates other) { - return Point.distanceSq(xInWorld, yInWorld, other.xInWorld, other.yInWorld); + return Point.distanceSq(xInWorld, yInWorld, other.xInWorld, + other.yInWorld); } - public double getDistanceSq(long xInWorld, long yInWorld) { - return Point.distanceSq(this.xInWorld, this.yInWorld, xInWorld, yInWorld); + public double getDistanceSq(int xInWorld, int yInWorld) { + return Point.distanceSq(this.xInWorld, this.yInWorld, xInWorld, + yInWorld); } public Coordinates add(Coordinates other) { return add(other.xInWorld, other.yInWorld); } - public Coordinates add(long xInWorld, long yInWorld) { + public Coordinates add(int xInWorld, int yInWorld) { return from(this, xInWorld, yInWorld); } @@ -128,12 +148,15 @@ public Coordinates substract(Coordinates other) { return substract(other.xInWorld, other.yInWorld); } - public Coordinates substract(long xInWorld, long yInWorld) { + public Coordinates substract(int xInWorld, int yInWorld) { return from(this, -xInWorld, -yInWorld); } - public boolean isInBoundsOf(Coordinates corner, long size) { - return CoordinateUtils.isInBounds(xInWorld, yInWorld, corner.xInWorld, corner.yInWorld, size, size); + public boolean isInBoundsOf(Coordinates corner, int size) { + return xInWorld >= corner.xInWorld + && xInWorld < corner.xInWorld + size + && yInWorld >= corner.yInWorld + && yInWorld < corner.yInWorld + size; } @Override @@ -157,8 +180,8 @@ public int compareTo(Coordinates o) { public int hashCode() { final int prime = 31; int result = 1; - result = prime * result + (int) (xInWorld ^ (xInWorld >>> 32)); - result = prime * result + (int) (yInWorld ^ (yInWorld >>> 32)); + result = prime * result + xInWorld; + result = prime * result + yInWorld; return result; } diff --git a/src/main/java/amidst/mojangapi/world/coordinates/CoordinatesJsonAdapter.java b/src/main/java/amidst/mojangapi/world/coordinates/CoordinatesJsonAdapter.java new file mode 100644 index 000000000..298611661 --- /dev/null +++ b/src/main/java/amidst/mojangapi/world/coordinates/CoordinatesJsonAdapter.java @@ -0,0 +1,67 @@ +package amidst.mojangapi.world.coordinates; + +import java.io.IOException; + +import com.google.gson.TypeAdapter; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonToken; +import com.google.gson.stream.JsonWriter; + +public class CoordinatesJsonAdapter extends TypeAdapter { + + @Override + public Coordinates read(JsonReader reader) throws IOException { + if(reader.peek() == JsonToken.NULL) { + reader.nextNull(); + return null; + } + + int x = 0; + int y = 0; + + if(reader.peek() == JsonToken.BEGIN_ARRAY) { + reader.beginArray(); + x = reader.nextInt(); + y = reader.nextInt(); + reader.endArray(); + + } else { + int propRead = 0; + reader.beginObject(); + while(reader.hasNext()) { + switch(reader.nextName()) { + case "xInWorld": + x = reader.nextInt(); + break; + case "yInWorld": + y = reader.nextInt(); + break; + default: + throw new IOException("invalid name"); + } + propRead++; + if(propRead > 2) + throw new IOException("too many properties"); + } + reader.endObject(); + } + + return Coordinates.from(x, y); + } + + @Override + public void write(JsonWriter writer, Coordinates coo) throws IOException { + if(coo == null) { + writer.nullValue(); + return; + } + + // @formatter:off + writer.beginArray() + .value(coo.getX()) + .value(coo.getY()) + .endArray(); + // @formatter:on + } + +} \ No newline at end of file diff --git a/src/main/java/amidst/mojangapi/world/coordinates/Resolution.java b/src/main/java/amidst/mojangapi/world/coordinates/Resolution.java index c93ebec85..f8dc2be85 100644 --- a/src/main/java/amidst/mojangapi/world/coordinates/Resolution.java +++ b/src/main/java/amidst/mojangapi/world/coordinates/Resolution.java @@ -28,16 +28,42 @@ private Resolution(int shift) { public int getStep() { return 1 << shift; } - - public int getStepsPerFragment() { - return 1 << (FRAGMENT.shift - shift); + + public int getShift() { + return shift; } - public long convertFromWorldToThis(long coordinateInWorld) { + public int getShiftPer(Resolution resolution) { + return resolution.shift - shift; + } + + public int getStepsPer(Resolution resolution) { + return 1 << getShiftPer(resolution); + } + + public int convertFromWorldToThis(int coordinateInWorld) { return coordinateInWorld >> shift; } - public long convertFromThisToWorld(long coordinateInThisResolution) { + public int convertFromThisToWorld(int coordinateInThisResolution) { return coordinateInThisResolution << shift; } + + public int snapUpwardsToResolution(int coordinateInWorld) { + if(toRelative(coordinateInWorld) == 0) + return coordinateInWorld; + return snapToResolution(coordinateInWorld+1); + } + + public int snapToResolution(int coordinateInWorld) { + return coordinateInWorld & ~getResolutionMask(); + } + + public int toRelative(int coordinateInWorld) { + return coordinateInWorld & getResolutionMask(); + } + + private int getResolutionMask() { + return (1 << shift) - 1; + } } diff --git a/src/main/java/amidst/mojangapi/world/filter/WorldFilter.java b/src/main/java/amidst/mojangapi/world/filter/WorldFilter.java index 0e0044ff6..86b8ba47f 100644 --- a/src/main/java/amidst/mojangapi/world/filter/WorldFilter.java +++ b/src/main/java/amidst/mojangapi/world/filter/WorldFilter.java @@ -8,11 +8,11 @@ @Immutable public abstract class WorldFilter { - protected final long worldFilterSize; - protected final long quarterFilterSize; + protected final int worldFilterSize; + protected final int quarterFilterSize; protected final Coordinates corner; - public WorldFilter(long worldFilterSize) { + public WorldFilter(int worldFilterSize) { ensureIsMultipleOfFragmentSize(worldFilterSize); this.worldFilterSize = worldFilterSize; this.quarterFilterSize = Resolution.QUARTER.convertFromWorldToThis(this.worldFilterSize); diff --git a/src/main/java/amidst/mojangapi/world/filter/WorldFilter_Biome.java b/src/main/java/amidst/mojangapi/world/filter/WorldFilter_Biome.java index 490829574..f51489f5c 100644 --- a/src/main/java/amidst/mojangapi/world/filter/WorldFilter_Biome.java +++ b/src/main/java/amidst/mojangapi/world/filter/WorldFilter_Biome.java @@ -10,7 +10,7 @@ public class WorldFilter_Biome extends WorldFilter { private final Set validBiomeIndexes; private short[][] region; - public WorldFilter_Biome(long worldFilterSize, Set validBiomeIndexes) { + public WorldFilter_Biome(int worldFilterSize, Set validBiomeIndexes) { super(worldFilterSize); this.validBiomeIndexes = validBiomeIndexes; this.region = new short[(int) this.quarterFilterSize * 2][(int) this.quarterFilterSize * 2]; diff --git a/src/main/java/amidst/mojangapi/world/filter/WorldFilter_MatchAll.java b/src/main/java/amidst/mojangapi/world/filter/WorldFilter_MatchAll.java index 9e062b7c8..266e1c60e 100644 --- a/src/main/java/amidst/mojangapi/world/filter/WorldFilter_MatchAll.java +++ b/src/main/java/amidst/mojangapi/world/filter/WorldFilter_MatchAll.java @@ -9,7 +9,7 @@ public class WorldFilter_MatchAll extends WorldFilter { private final List filters; - public WorldFilter_MatchAll(long worldFilterSize, List filters) { + public WorldFilter_MatchAll(int worldFilterSize, List filters) { super(worldFilterSize); this.filters = filters; } diff --git a/src/main/java/amidst/mojangapi/world/filter/WorldFilter_Structure.java b/src/main/java/amidst/mojangapi/world/filter/WorldFilter_Structure.java index 93301a9e1..ba5bd9086 100644 --- a/src/main/java/amidst/mojangapi/world/filter/WorldFilter_Structure.java +++ b/src/main/java/amidst/mojangapi/world/filter/WorldFilter_Structure.java @@ -13,7 +13,7 @@ public class WorldFilter_Structure extends WorldFilter { private final DefaultWorldIconTypes structure; private final int count; - public WorldFilter_Structure(long worldFilterSize, DefaultWorldIconTypes structure, int count) { + public WorldFilter_Structure(int worldFilterSize, DefaultWorldIconTypes structure, int count) { super(worldFilterSize); this.structure = structure; this.count = count; @@ -27,8 +27,8 @@ public boolean isValid(World world) { } private void procudeAndCollect(WorldIconProducer structureProducer, WorldIconCollector structureCollector) { - for (long x = 0; x < 2 * worldFilterSize; x += 512) { - for (long y = 0; y < 2 * worldFilterSize; y += 512) { + for (int x = 0; x < 2 * worldFilterSize; x += 512) { + for (int y = 0; y < 2 * worldFilterSize; y += 512) { structureProducer.produce(Coordinates.from(x, y).add(corner), structureCollector, null); } } diff --git a/src/main/java/amidst/mojangapi/world/icon/producer/StrongholdProducer_Base.java b/src/main/java/amidst/mojangapi/world/icon/producer/StrongholdProducer_Base.java index f103eced6..560facfeb 100644 --- a/src/main/java/amidst/mojangapi/world/icon/producer/StrongholdProducer_Base.java +++ b/src/main/java/amidst/mojangapi/world/icon/producer/StrongholdProducer_Base.java @@ -80,8 +80,8 @@ private Coordinates getStrongholdLocation(int x, int y, Coordinates coordinates) } private Coordinates getCornerOfChunk(Coordinates coordinates) { - long xInWorld = (coordinates.getX() >> 4) << 4; - long yInWorld = (coordinates.getY() >> 4) << 4; + int xInWorld = (coordinates.getX() >> 4) << 4; + int yInWorld = (coordinates.getY() >> 4) << 4; return Coordinates.from(xInWorld, yInWorld); } diff --git a/src/main/java/amidst/mojangapi/world/icon/producer/StructureProducer.java b/src/main/java/amidst/mojangapi/world/icon/producer/StructureProducer.java index 54adf4522..d45718c5d 100644 --- a/src/main/java/amidst/mojangapi/world/icon/producer/StructureProducer.java +++ b/src/main/java/amidst/mojangapi/world/icon/producer/StructureProducer.java @@ -29,7 +29,7 @@ public StructureProducer( Dimension dimension, boolean displayDimension) { this.resolution = resolution; - this.size = resolution.getStepsPerFragment(); + this.size = resolution.getStepsPer(Resolution.FRAGMENT); this.offsetInWorld = offsetInWorld; this.checker = checker; this.provider = provider; @@ -74,8 +74,8 @@ private Coordinates createCoordinates( Coordinates corner, int xRelativeToFragment, int yRelativeToFragment) { - long xInWorld = resolution.convertFromThisToWorld(xRelativeToFragment); - long yInWorld = resolution.convertFromThisToWorld(yRelativeToFragment); + int xInWorld = resolution.convertFromThisToWorld(xRelativeToFragment); + int yInWorld = resolution.convertFromThisToWorld(yRelativeToFragment); return corner.add(xInWorld + offsetInWorld, yInWorld + offsetInWorld); } } diff --git a/src/main/java/amidst/mojangapi/world/oracle/EndIslandOracle.java b/src/main/java/amidst/mojangapi/world/oracle/EndIslandOracle.java index 3b7cb7c0f..fc7a352ef 100644 --- a/src/main/java/amidst/mojangapi/world/oracle/EndIslandOracle.java +++ b/src/main/java/amidst/mojangapi/world/oracle/EndIslandOracle.java @@ -69,10 +69,10 @@ public EndIslandOracle(SimplexNoise noiseFunction) { } public List getAt(Coordinates corner) { - int steps = Resolution.CHUNK.getStepsPerFragment(); + int steps = Resolution.CHUNK.getStepsPer(Resolution.FRAGMENT); return findSurroundingIslands( - (int) corner.getXAs(Resolution.CHUNK), - (int) corner.getYAs(Resolution.CHUNK), + corner.getXAs(Resolution.CHUNK), + corner.getYAs(Resolution.CHUNK), steps, steps); } diff --git a/src/main/java/amidst/mojangapi/world/player/Player.java b/src/main/java/amidst/mojangapi/world/player/Player.java index 0adcba89b..2d6ed5477 100644 --- a/src/main/java/amidst/mojangapi/world/player/Player.java +++ b/src/main/java/amidst/mojangapi/world/player/Player.java @@ -33,7 +33,7 @@ public PlayerCoordinates getPlayerCoordinates() { return currentCoordinates; } - public void moveTo(Coordinates coordinates, long height, Dimension dimension) { + public void moveTo(Coordinates coordinates, int height, Dimension dimension) { this.currentCoordinates = new PlayerCoordinates(coordinates, height, dimension); } diff --git a/src/main/java/amidst/mojangapi/world/player/PlayerCoordinates.java b/src/main/java/amidst/mojangapi/world/player/PlayerCoordinates.java index fbd71b946..26236998c 100644 --- a/src/main/java/amidst/mojangapi/world/player/PlayerCoordinates.java +++ b/src/main/java/amidst/mojangapi/world/player/PlayerCoordinates.java @@ -6,16 +6,16 @@ @Immutable public class PlayerCoordinates { - public static PlayerCoordinates fromNBTFile(long x, long y, long z, int dimensionId) { + public static PlayerCoordinates fromNBTFile(int x, int y, int z, int dimensionId) { Dimension dimension = Dimension.from(dimensionId); return new PlayerCoordinates(Coordinates.from(x, z, dimension.getResolution()), y, dimension); } private final Coordinates coordinates; - private final long y; + private final int y; private final Dimension dimension; - public PlayerCoordinates(Coordinates coordinates, long y, Dimension dimension) { + public PlayerCoordinates(Coordinates coordinates, int y, Dimension dimension) { this.coordinates = coordinates; this.y = y; this.dimension = dimension; @@ -25,7 +25,7 @@ public Coordinates getCoordinatesInWorld() { return coordinates; } - public long getY() { + public int getY() { return y; } @@ -33,15 +33,15 @@ public Dimension getDimension() { return dimension; } - public long getXForNBTFile() { + public int getXForNBTFile() { return coordinates.getXAs(dimension.getResolution()); } - public long getYForNBTFile() { + public int getYForNBTFile() { return y; } - public long getZForNBTFile() { + public int getZForNBTFile() { return coordinates.getYAs(dimension.getResolution()); } } diff --git a/src/test/java/amidst/mojangapi/mocking/FragmentCornerWalker.java b/src/test/java/amidst/mojangapi/mocking/FragmentCornerWalker.java index 928148b73..8e84e3881 100644 --- a/src/test/java/amidst/mojangapi/mocking/FragmentCornerWalker.java +++ b/src/test/java/amidst/mojangapi/mocking/FragmentCornerWalker.java @@ -9,7 +9,7 @@ @Immutable public class FragmentCornerWalker { public static FragmentCornerWalker walkFragmentsAroundOrigin(int fragmentsAroundOrigin) { - long blocksAroundOrigin = fragmentsAroundOrigin * Fragment.SIZE; + int blocksAroundOrigin = fragmentsAroundOrigin * Fragment.SIZE; Coordinates startCorner = Coordinates.from(-blocksAroundOrigin, -blocksAroundOrigin); Coordinates endCorner = startCorner.add(2 * blocksAroundOrigin, 2 * blocksAroundOrigin); return new FragmentCornerWalker(startCorner, endCorner); @@ -24,8 +24,8 @@ public FragmentCornerWalker(Coordinates startCorner, Coordinates endCorner) { } public void walk(Consumer consumer) { - for (long x = startCorner.getX(); x < endCorner.getX(); x += Fragment.SIZE) { - for (long y = startCorner.getY(); y < endCorner.getY(); y += Fragment.SIZE) { + for (int x = startCorner.getX(); x < endCorner.getX(); x += Fragment.SIZE) { + for (int y = startCorner.getY(); y < endCorner.getY(); y += Fragment.SIZE) { consumer.accept(Coordinates.from(x, y)); } } diff --git a/src/test/java/amidst/mojangapi/world/test/CoordinateUtilsTest.java b/src/test/java/amidst/mojangapi/world/test/CoordinateUtilsTest.java deleted file mode 100644 index a0890c0dc..000000000 --- a/src/test/java/amidst/mojangapi/world/test/CoordinateUtilsTest.java +++ /dev/null @@ -1,25 +0,0 @@ -package amidst.mojangapi.world.test; - -import org.junit.Assert; -import org.junit.Test; - -import amidst.mojangapi.world.coordinates.CoordinateUtils; - -public class CoordinateUtilsTest { - @Test - public void testCoordinateConversion() { - Assert.assertTrue(ensureCoordinateConversionWorks()); - } - - private static boolean ensureCoordinateConversionWorks() { - for (long inWorld = -1000; inWorld < 1000; inWorld++) { - long inFragment = CoordinateUtils.toFragmentRelative(inWorld); - long inWorldOfFragment = CoordinateUtils.toFragmentCorner(inWorld); - long actualInWorld = CoordinateUtils.toWorld(inWorldOfFragment, inFragment); - if (inWorld != actualInWorld) { - return false; - } - } - return true; - } -} diff --git a/src/test/java/amidst/mojangapi/world/testworld/storage/json/SlimeChunksJson.java b/src/test/java/amidst/mojangapi/world/testworld/storage/json/SlimeChunksJson.java index 6cf032a4d..495484906 100644 --- a/src/test/java/amidst/mojangapi/world/testworld/storage/json/SlimeChunksJson.java +++ b/src/test/java/amidst/mojangapi/world/testworld/storage/json/SlimeChunksJson.java @@ -13,7 +13,7 @@ public class SlimeChunksJson { public static SlimeChunksJson from(World world) { SlimeChunkOracle oracle = world.getSlimeChunkOracle(); - int steps = Resolution.CHUNK.getStepsPerFragment(); + int steps = Resolution.CHUNK.getStepsPer(Resolution.FRAGMENT); boolean[][] isSlimeChunk = new boolean[2 * steps][2 * steps]; for (int x = 0; x < isSlimeChunk.length; x++) { for (int y = 0; y < isSlimeChunk[x].length; y++) { From 4b22bd977119b7a213887c84ae606dbe550ccd2c Mon Sep 17 00:00:00 2001 From: moulins <> Date: Fri, 23 Jun 2017 14:04:20 +0200 Subject: [PATCH 05/13] Clarify relations between normal biomes and their special variant --- .../amidst/mojangapi/world/biome/Biome.java | 105 ++++++++++-------- 1 file changed, 61 insertions(+), 44 deletions(-) diff --git a/src/main/java/amidst/mojangapi/world/biome/Biome.java b/src/main/java/amidst/mojangapi/world/biome/Biome.java index c48d92175..0272a2120 100644 --- a/src/main/java/amidst/mojangapi/world/biome/Biome.java +++ b/src/main/java/amidst/mojangapi/world/biome/Biome.java @@ -90,49 +90,50 @@ private void findNextValid() { public static final Biome mesaPlateauF = new Biome("Mesa Plateau F", 38, BiomeColor.from(176, 151, 101), BiomeType.PLATEAU); public static final Biome mesaPlateau = new Biome("Mesa Plateau", 39, BiomeColor.from(202, 140, 101), BiomeType.PLATEAU); - public static final Biome oceanM = new Biome("Ocean M", 128, BiomeColor.from( 0, 0, 112)); - public static final Biome sunflowerPlains = new Biome("Sunflower Plains", 129, BiomeColor.from(141, 179, 96)); - public static final Biome desertM = new Biome("Desert M", 130, BiomeColor.from(250, 148, 24)); - public static final Biome extremeHillsM = new Biome("Extreme Hills M", 131, BiomeColor.from( 96, 96, 96)); - public static final Biome flowerForest = new Biome("Flower Forest", 132, BiomeColor.from( 5, 102, 33)); - public static final Biome taigaM = new Biome("Taiga M", 133, BiomeColor.from( 11, 102, 89)); - public static final Biome swamplandM = new Biome("Swampland M", 134, BiomeColor.from( 7, 249, 178)); - public static final Biome riverM = new Biome("River M", 135, BiomeColor.from( 0, 0, 255)); - public static final Biome hellM = new Biome("Hell M", 136, BiomeColor.from(255, 0, 0)); - public static final Biome skyM = new Biome("Sky M", 137, BiomeColor.from(128, 128, 255)); - public static final Biome frozenOceanM = new Biome("Frozen Ocean M", 138, BiomeColor.from(144, 144, 160)); - public static final Biome frozenRiverM = new Biome("Frozen River M", 139, BiomeColor.from(160, 160, 255)); - public static final Biome icePlainsSpikes = new Biome("Ice Plains Spikes", 140, BiomeColor.from(140, 180, 180)); - public static final Biome iceMountainsM = new Biome("Ice Mountains M", 141, BiomeColor.from(160, 160, 160)); - public static final Biome mushroomIslandM = new Biome("Mushroom Island M", 142, BiomeColor.from(255, 0, 255)); - public static final Biome mushroomIslandShoreM = new Biome("Mushroom Island Shore M", 143, BiomeColor.from(160, 0, 255)); - public static final Biome beachM = new Biome("Beach M", 144, BiomeColor.from(250, 222, 85)); - public static final Biome desertHillsM = new Biome("Desert Hills M", 145, BiomeColor.from(210, 95, 18)); - public static final Biome forestHillsM = new Biome("Forest Hills M", 146, BiomeColor.from( 34, 85, 28)); - public static final Biome taigaHillsM = new Biome("Taiga Hills M", 147, BiomeColor.from( 22, 57, 51)); - public static final Biome extremeHillsEdgeM = new Biome("Extreme Hills Edge M", 148, BiomeColor.from(114, 120, 154)); - public static final Biome jungleM = new Biome("Jungle M", 149, BiomeColor.from( 83, 123, 9)); - public static final Biome jungleHillsM = new Biome("Jungle Hills M", 150, BiomeColor.from( 44, 66, 5)); - public static final Biome jungleEdgeM = new Biome("Jungle Edge M", 151, BiomeColor.from( 98, 139, 23)); - public static final Biome deepOceanM = new Biome("Deep Ocean M", 152, BiomeColor.from( 0, 0, 48)); - public static final Biome stoneBeachM = new Biome("Stone Beach M", 153, BiomeColor.from(162, 162, 132)); - public static final Biome coldBeachM = new Biome("Cold Beach M", 154, BiomeColor.from(250, 240, 192)); - public static final Biome birchForestM = new Biome("Birch Forest M", 155, BiomeColor.from( 48, 116, 68)); - public static final Biome birchForestHillsM = new Biome("Birch Forest Hills M", 156, BiomeColor.from( 31, 95, 50)); - public static final Biome roofedForestM = new Biome("Roofed Forest M", 157, BiomeColor.from( 64, 81, 26)); - public static final Biome coldTaigaM = new Biome("Cold Taiga M", 158, BiomeColor.from( 49, 85, 74)); - public static final Biome coldTaigaHillsM = new Biome("Cold Taiga Hills M", 159, BiomeColor.from( 36, 63, 54)); - public static final Biome megaSpruceTaiga = new Biome("Mega Spruce Taiga", 160, BiomeColor.from( 89, 102, 81)); - public static final Biome megaSpurceTaigaHills = new Biome("Mega Spruce Taiga (Hills)", 161, BiomeColor.from( 69, 79, 62)); - public static final Biome extremeHillsPlusM = new Biome("Extreme Hills+ M", 162, BiomeColor.from( 80, 112, 80)); - public static final Biome savannaM = new Biome("Savanna M", 163, BiomeColor.from(189, 178, 95)); - public static final Biome savannaPlateauM = new Biome("Savanna Plateau M", 164, BiomeColor.from(167, 157, 100)); - public static final Biome mesaBryce = new Biome("Mesa (Bryce)", 165, BiomeColor.from(217, 69, 21)); - public static final Biome mesaPlateauFM = new Biome("Mesa Plateau F M", 166, BiomeColor.from(176, 151, 101)); - public static final Biome mesaPlateauM = new Biome("Mesa Plateau M", 167, BiomeColor.from(202, 140, 101)); + public static final Biome oceanM = new Biome("Ocean M", ocean); + public static final Biome sunflowerPlains = new Biome("Sunflower Plains", plains); + public static final Biome desertM = new Biome("Desert M", desert); + public static final Biome extremeHillsM = new Biome("Extreme Hills M", extremeHills); + public static final Biome flowerForest = new Biome("Flower Forest", forest); + public static final Biome taigaM = new Biome("Taiga M", taiga); + public static final Biome swamplandM = new Biome("Swampland M", swampland); + public static final Biome riverM = new Biome("River M", river); + public static final Biome hellM = new Biome("Hell M", hell); + public static final Biome skyM = new Biome("Sky M", theEnd); + public static final Biome frozenOceanM = new Biome("Frozen Ocean M", frozenOcean); + public static final Biome frozenRiverM = new Biome("Frozen River M", frozenRiver); + public static final Biome icePlainsSpikes = new Biome("Ice Plains Spikes", icePlains); + public static final Biome iceMountainsM = new Biome("Ice Mountains M", iceMountains); + public static final Biome mushroomIslandM = new Biome("Mushroom Island M", mushroomIsland); + public static final Biome mushroomIslandShoreM = new Biome("Mushroom Island Shore M", mushroomIslandShore); + public static final Biome beachM = new Biome("Beach M", beach); + public static final Biome desertHillsM = new Biome("Desert Hills M", desertHills); + public static final Biome forestHillsM = new Biome("Forest Hills M", forestHills); + public static final Biome taigaHillsM = new Biome("Taiga Hills M", taigaHills); + public static final Biome extremeHillsEdgeM = new Biome("Extreme Hills Edge M", extremeHillsEdge); + public static final Biome jungleM = new Biome("Jungle M", jungle); + public static final Biome jungleHillsM = new Biome("Jungle Hills M", jungleHills); + public static final Biome jungleEdgeM = new Biome("Jungle Edge M", jungleEdge); + public static final Biome deepOceanM = new Biome("Deep Ocean M", deepOcean); + public static final Biome stoneBeachM = new Biome("Stone Beach M", stoneBeach); + public static final Biome coldBeachM = new Biome("Cold Beach M", coldBeach); + public static final Biome birchForestM = new Biome("Birch Forest M", birchForest); + public static final Biome birchForestHillsM = new Biome("Birch Forest Hills M", birchForestHills); + public static final Biome roofedForestM = new Biome("Roofed Forest M", roofedForest); + public static final Biome coldTaigaM = new Biome("Cold Taiga M", coldTaiga); + public static final Biome coldTaigaHillsM = new Biome("Cold Taiga Hills M", coldTaigaHills); + public static final Biome megaSpruceTaiga = new Biome("Mega Spruce Taiga", megaTaiga); + public static final Biome megaSpurceTaigaHills = new Biome("Mega Spruce Taiga (Hills)", megaTaigaHills); + public static final Biome extremeHillsPlusM = new Biome("Extreme Hills+ M", extremeHillsPlus); + public static final Biome savannaM = new Biome("Savanna M", savanna); + public static final Biome savannaPlateauM = new Biome("Savanna Plateau M", savannaPlateau); + public static final Biome mesaBryce = new Biome("Mesa (Bryce)", mesa); + public static final Biome mesaPlateauFM = new Biome("Mesa Plateau F M", mesaPlateauF); + public static final Biome mesaPlateauM = new Biome("Mesa Plateau M", mesaPlateau); // @formatter:on private static final BiomeIterable ITERABLE = new BiomeIterable(); + private static final int SPECIAL_BIOMES_START = 128; public static Iterable allBiomes() { return ITERABLE; @@ -159,7 +160,7 @@ public static boolean exists(String name) { } public static boolean isSpecialBiomeIndex(int index) { - return index >= 128; + return index >= SPECIAL_BIOMES_START; } public static int compareByIndex(String name1, String name2) { @@ -170,9 +171,9 @@ public static int compareByIndex(String name1, String name2) { private final int index; private final BiomeColor defaultColor; private final BiomeType type; - - public Biome(String name, int index, BiomeColor defaultColor) { - this(name, index, defaultColor.createLightenedBiomeColor(), biomes[index - 128].type.strengthen()); + + public Biome(String name, Biome base) { + this(name, base.index + SPECIAL_BIOMES_START, base.defaultColor.createLightenedBiomeColor(), base.type.strengthen()); } public Biome(String name, int index, BiomeColor defaultColor, BiomeType type) { @@ -199,7 +200,23 @@ public BiomeColor getDefaultColor() { public BiomeType getType() { return type; } + + public boolean isSpecialBiome() { + return Biome.isSpecialBiomeIndex(index); + } + + public Biome getSpecialVariant() { + if(index >= SPECIAL_BIOMES_START) + return this; + return biomes[index + SPECIAL_BIOMES_START]; + } + public Biome getNormalVariant() { + if(index < SPECIAL_BIOMES_START) + return this; + return biomes[index - SPECIAL_BIOMES_START]; + } + @Override public String toString() { return "[Biome " + name + "]"; From ac9282668827a1ace07232913f20c2700c29069a Mon Sep 17 00:00:00 2001 From: moulins <> Date: Fri, 23 Jun 2017 14:12:07 +0200 Subject: [PATCH 06/13] Move Tri/QuadFunction to amidst.utils; some Gson stuff --- .../versionfeatures/DefaultVersionFeatures.java | 2 ++ .../world/versionfeatures/VersionFeatures.java | 2 ++ .../versionfeatures => util}/QuadFunction.java | 2 +- .../versionfeatures => util}/TriFunction.java | 2 +- .../devtools/GenerateUpdateInformationJson.java | 4 ++-- .../testworld/io/TestWorldEntrySerializer.java | 14 ++++---------- 6 files changed, 12 insertions(+), 14 deletions(-) rename src/main/java/amidst/{mojangapi/world/versionfeatures => util}/QuadFunction.java (67%) rename src/main/java/amidst/{mojangapi/world/versionfeatures => util}/TriFunction.java (67%) diff --git a/src/main/java/amidst/mojangapi/world/versionfeatures/DefaultVersionFeatures.java b/src/main/java/amidst/mojangapi/world/versionfeatures/DefaultVersionFeatures.java index 391e02960..ce7a1a054 100644 --- a/src/main/java/amidst/mojangapi/world/versionfeatures/DefaultVersionFeatures.java +++ b/src/main/java/amidst/mojangapi/world/versionfeatures/DefaultVersionFeatures.java @@ -19,6 +19,8 @@ import amidst.mojangapi.world.icon.producer.StrongholdProducer_Buggy128Algorithm; import amidst.mojangapi.world.icon.producer.StrongholdProducer_Original; import amidst.mojangapi.world.oracle.BiomeDataOracle; +import amidst.util.QuadFunction; +import amidst.util.TriFunction; @Immutable public enum DefaultVersionFeatures { diff --git a/src/main/java/amidst/mojangapi/world/versionfeatures/VersionFeatures.java b/src/main/java/amidst/mojangapi/world/versionfeatures/VersionFeatures.java index 11f21266e..1af6ccdf8 100644 --- a/src/main/java/amidst/mojangapi/world/versionfeatures/VersionFeatures.java +++ b/src/main/java/amidst/mojangapi/world/versionfeatures/VersionFeatures.java @@ -9,6 +9,8 @@ import amidst.mojangapi.world.icon.locationchecker.MineshaftAlgorithm_Base; import amidst.mojangapi.world.icon.producer.StrongholdProducer_Base; import amidst.mojangapi.world.oracle.BiomeDataOracle; +import amidst.util.QuadFunction; +import amidst.util.TriFunction; @Immutable public class VersionFeatures { diff --git a/src/main/java/amidst/mojangapi/world/versionfeatures/QuadFunction.java b/src/main/java/amidst/util/QuadFunction.java similarity index 67% rename from src/main/java/amidst/mojangapi/world/versionfeatures/QuadFunction.java rename to src/main/java/amidst/util/QuadFunction.java index 691fb210f..ee00cf381 100644 --- a/src/main/java/amidst/mojangapi/world/versionfeatures/QuadFunction.java +++ b/src/main/java/amidst/util/QuadFunction.java @@ -1,4 +1,4 @@ -package amidst.mojangapi.world.versionfeatures; +package amidst.util; @FunctionalInterface public interface QuadFunction { diff --git a/src/main/java/amidst/mojangapi/world/versionfeatures/TriFunction.java b/src/main/java/amidst/util/TriFunction.java similarity index 67% rename from src/main/java/amidst/mojangapi/world/versionfeatures/TriFunction.java rename to src/main/java/amidst/util/TriFunction.java index 53204dd48..4a27b65c5 100644 --- a/src/main/java/amidst/mojangapi/world/versionfeatures/TriFunction.java +++ b/src/main/java/amidst/util/TriFunction.java @@ -1,4 +1,4 @@ -package amidst.mojangapi.world.versionfeatures; +package amidst.util; @FunctionalInterface public interface TriFunction { diff --git a/src/test/java/amidst/devtools/GenerateUpdateInformationJson.java b/src/test/java/amidst/devtools/GenerateUpdateInformationJson.java index 2c1f8c0ac..a436bd46d 100644 --- a/src/test/java/amidst/devtools/GenerateUpdateInformationJson.java +++ b/src/test/java/amidst/devtools/GenerateUpdateInformationJson.java @@ -1,14 +1,14 @@ package amidst.devtools; import com.google.gson.Gson; -import com.google.gson.GsonBuilder; import amidst.AmidstVersion; import amidst.gui.main.UpdateInformationJson; +import amidst.parsing.json.GsonProvider; public class GenerateUpdateInformationJson { private static final String DOWNLOAD_URL = "https://github.com/toolbox4minecraft/amidst/releases"; - private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create(); + private static final Gson GSON = GsonProvider.builder().setPrettyPrinting().create(); private final AmidstVersion version; 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..af1855096 100644 --- a/src/test/java/amidst/mojangapi/world/testworld/io/TestWorldEntrySerializer.java +++ b/src/test/java/amidst/mojangapi/world/testworld/io/TestWorldEntrySerializer.java @@ -8,25 +8,19 @@ import java.io.UncheckedIOException; import java.nio.charset.StandardCharsets; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; - 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.SlimeChunksJson; import amidst.mojangapi.world.testworld.storage.json.WorldMetadataJson; +import amidst.parsing.json.GsonProvider; + @Immutable public enum TestWorldEntrySerializer { ; - /*- - * We need complex keys for the BiomeDataJson which contains a Map - */ - private static final Gson GSON = new GsonBuilder().enableComplexMapKeySerialization().create(); - public static WorldMetadataJson readMetaData(InputStream is) { return readJson(is, WorldMetadataJson.class); } @@ -49,7 +43,7 @@ public static CoordinatesCollectionJson readCoordinatesCollection(InputStream is private static T readJson(InputStream is, Class clazz) { try (InputStreamReader reader = new InputStreamReader(is, StandardCharsets.UTF_8)) { - return GSON.fromJson(reader, clazz); + return GsonProvider.get().fromJson(reader, clazz); } catch (IOException e) { throw new UncheckedIOException(e); } @@ -60,7 +54,7 @@ public static void writeJson(OutputStream os, Object data) { // do not use the try-with-resource mechanism, because this will // also close the output stream OutputStreamWriter writer = new OutputStreamWriter(os, StandardCharsets.UTF_8); - GSON.toJson(data, writer); + GsonProvider.get().toJson(data, writer); writer.flush(); } catch (IOException e) { throw new UncheckedIOException(e); From 4e2e3929709da46db71588f34935d2c287a349b3 Mon Sep 17 00:00:00 2001 From: moulins <> Date: Fri, 23 Jun 2017 14:40:46 +0200 Subject: [PATCH 07/13] Add Region class The Region class represents a rectangular or circular region of the world. --- src/main/java/amidst/fragment/Fragment.java | 5 + .../fragment/loader/WorldIconLoader.java | 2 +- .../MinecraftInterface.java | 3 +- .../local/LocalMinecraftInterface.java | 8 +- .../mojangapi/world/coordinates/Region.java | 393 ++++++++++++++++++ .../world/filter/WorldFilter_Structure.java | 3 +- .../icon/locationchecker/LocationChecker.java | 5 + .../producer/CachedWorldIconProducer.java | 7 +- .../icon/producer/StructureProducer.java | 42 +- .../icon/producer/WorldIconProducer.java | 8 +- .../icon/type/WorldIconTypeProvider.java | 5 + .../world/oracle/BiomeDataOracle.java | 56 +-- .../mocking/BiomeDataJsonBuilder.java | 9 +- .../mocking/FakeMinecraftInterface.java | 5 +- ...tCornerWalker.java => FragmentWalker.java} | 15 +- .../RequestStoringMinecraftInterface.java | 11 +- .../mojangapi/world/test/TestRegion.java | 69 +++ .../DefaultTestWorldDirectoryDeclaration.java | 2 +- .../testworld/storage/json/AreaJson.java | 23 +- .../testworld/storage/json/BiomeDataJson.java | 6 +- .../json/CoordinatesCollectionJson.java | 9 +- .../storage/json/EndIslandsJson.java | 4 +- .../storage/json/WorldMetadataJson.java | 2 +- 23 files changed, 590 insertions(+), 102 deletions(-) create mode 100644 src/main/java/amidst/mojangapi/world/coordinates/Region.java rename src/test/java/amidst/mojangapi/mocking/{FragmentCornerWalker.java => FragmentWalker.java} (61%) create mode 100644 src/test/java/amidst/mojangapi/world/test/TestRegion.java diff --git a/src/main/java/amidst/fragment/Fragment.java b/src/main/java/amidst/fragment/Fragment.java index d231ff261..67250dbaf 100644 --- a/src/main/java/amidst/fragment/Fragment.java +++ b/src/main/java/amidst/fragment/Fragment.java @@ -11,6 +11,7 @@ import amidst.documentation.NotThreadSafe; import amidst.gui.main.viewer.Drawer; import amidst.mojangapi.world.coordinates.Coordinates; +import amidst.mojangapi.world.coordinates.Region; import amidst.mojangapi.world.coordinates.Resolution; import amidst.mojangapi.world.icon.WorldIcon; import amidst.mojangapi.world.oracle.BiomeDataOracle; @@ -179,4 +180,8 @@ public void setCorner(Coordinates corner) { public Coordinates getCorner() { return corner; } + + public Region.Box getRegion() { + return Region.box(corner, Fragment.SIZE, Fragment.SIZE); + } } diff --git a/src/main/java/amidst/fragment/loader/WorldIconLoader.java b/src/main/java/amidst/fragment/loader/WorldIconLoader.java index deeb0f34b..0a4fb8e53 100644 --- a/src/main/java/amidst/fragment/loader/WorldIconLoader.java +++ b/src/main/java/amidst/fragment/loader/WorldIconLoader.java @@ -44,6 +44,6 @@ public void reload(Dimension dimension, Fragment fragment) { private void doLoad(Fragment fragment) { fragment.putWorldIcons( declaration.getLayerId(), - producer.getAt(fragment.getCorner(), additionalDataExtractor.apply(fragment))); + producer.getAt(fragment.getRegion(), additionalDataExtractor.apply(fragment))); } } diff --git a/src/main/java/amidst/mojangapi/minecraftinterface/MinecraftInterface.java b/src/main/java/amidst/mojangapi/minecraftinterface/MinecraftInterface.java index ee328d986..70ca61337 100644 --- a/src/main/java/amidst/mojangapi/minecraftinterface/MinecraftInterface.java +++ b/src/main/java/amidst/mojangapi/minecraftinterface/MinecraftInterface.java @@ -2,6 +2,7 @@ import amidst.documentation.ThreadSafe; import amidst.mojangapi.world.WorldType; +import amidst.mojangapi.world.coordinates.Region; /** * Acts as an additional layer of abstraction for interfacing with Minecraft. @@ -31,7 +32,7 @@ public interface MinecraftInterface { * a structure is located in (if the structure is located on a * biome boundary). */ - public int[] getBiomeData(int x, int y, int width, int height, boolean useQuarterResolution) + public int[] getBiomeData(Region.Box region, boolean useQuarterResolution) throws MinecraftInterfaceException; public void createWorld(long seed, WorldType worldType, String generatorOptions) throws MinecraftInterfaceException; diff --git a/src/main/java/amidst/mojangapi/minecraftinterface/local/LocalMinecraftInterface.java b/src/main/java/amidst/mojangapi/minecraftinterface/local/LocalMinecraftInterface.java index 92c8f43a0..e2c86673c 100644 --- a/src/main/java/amidst/mojangapi/minecraftinterface/local/LocalMinecraftInterface.java +++ b/src/main/java/amidst/mojangapi/minecraftinterface/local/LocalMinecraftInterface.java @@ -12,6 +12,8 @@ import amidst.mojangapi.minecraftinterface.MinecraftInterfaceException; import amidst.mojangapi.minecraftinterface.RecognisedVersion; import amidst.mojangapi.world.WorldType; +import amidst.mojangapi.world.coordinates.Coordinates; +import amidst.mojangapi.world.coordinates.Region; @ThreadSafe public class LocalMinecraftInterface implements MinecraftInterface { @@ -59,12 +61,14 @@ public static LocalMinecraftInterface create(ClassTranslator translator, Launche } @Override - public synchronized int[] getBiomeData(int x, int y, int width, int height, boolean useQuarterResolution) + public synchronized int[] getBiomeData(Region.Box region, boolean useQuarterResolution) throws MinecraftInterfaceException { try { intCacheClass.callStaticMethod(SymbolicNames.METHOD_INT_CACHE_RESET_INT_CACHE); + + Coordinates corner = region.getCorner(); return (int[]) getBiomeGenerator(useQuarterResolution) - .callMethod(SymbolicNames.METHOD_GEN_LAYER_GET_INTS, x, y, width, height); + .callMethod(SymbolicNames.METHOD_GEN_LAYER_GET_INTS, corner.getX(), corner.getY(), region.getWidth(), region.getHeight()); } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { throw new MinecraftInterfaceException("unable to get biome data", e); } diff --git a/src/main/java/amidst/mojangapi/world/coordinates/Region.java b/src/main/java/amidst/mojangapi/world/coordinates/Region.java new file mode 100644 index 000000000..430b86388 --- /dev/null +++ b/src/main/java/amidst/mojangapi/world/coordinates/Region.java @@ -0,0 +1,393 @@ +package amidst.mojangapi.world.coordinates; + +import amidst.documentation.Immutable; + +@Immutable +public abstract class Region { + + private Region() {} + + public abstract int getCenterX(); + public abstract int getCenterY(); + public Coordinates getCenter() { + return Coordinates.from(getCenterX(), getCenterY()); + } + public abstract Coordinates getCorner(); + public abstract int getWidth(); + public abstract int getHeight(); + + public boolean isEmpty() { + return getWidth() == 0 || getHeight() == 0; + } + + public Coordinates getCornerNW() { return getCorner(); } + public Coordinates getCornerNE() { return getCorner().add(getWidth(), 0); } + public Coordinates getCornerSW() { return getCorner().add(0, getHeight()); } + public Coordinates getCornerSE() { return getCorner().add(getWidth(), getHeight()); } + + public abstract Region getAs(Resolution resolution); + public abstract Region.Box getEnclosing(); + + public abstract boolean contains(int x, int y); + public abstract boolean contains(Coordinates point); + public abstract boolean contains(Region other); + public abstract boolean intersectsWith(Region other); + + public abstract Region move(int dx, int dy); + public abstract Region move(Coordinates offset); + + + @Override + public abstract boolean equals(Object other); + + @Override + public abstract int hashCode(); + + public static Box box(int x, int y, int w, int h) { + if (w < 0 || h < 0) + throw new IllegalArgumentException("width and height must be greater than or equals to 0"); + return new Box(x, y, x + w, y + h); + } + + public static Box box(Coordinates corner, int w, int h) { + return box(corner.getX(), corner.getY(), w, h); + } + + public static Box box(Coordinates corner1, Coordinates corner2) { + int xMin = Math.min(corner1.getX(), corner2.getX()); + int xMax = Math.max(corner1.getX(), corner2.getX()); + int yMin = Math.min(corner1.getY(), corner2.getY()); + int yMax = Math.max(corner1.getY(), corner2.getY()); + + return new Box(xMin, yMin, xMax, yMax); + } + + public static Box box(Coordinates center, int radius) { + return box(center.substract(radius, radius), 2*radius+1, 2*radius+1); + } + + public static Circle circle(Coordinates center, int radius) { + return circle(center.getX(), center.getY(), radius); + } + + public static Circle circle(int x, int y, int radius) { + if(radius < 0) + throw new IllegalArgumentException("radius must be greater than 0"); + return new Circle(x, y, radius); + } + + public static final class Circle extends Region { + private final int radius; + private final long radiusSq; + private final int x; + private final int y; + + private Circle(int x, int y, int r) { + radius = r; + radiusSq = ((long) r) * r; + this.x = x; + this.y = y; + } + + @Override + public int getCenterX() { + return x; + } + + @Override + public int getCenterY() { + return y; + } + + @Override + public Coordinates getCorner() { + return Coordinates.from(x - radius, y - radius); + } + + @Override + public int getWidth() { + return radius*2; + } + @Override + public int getHeight() { + return radius*2; + } + + public int getRadius() { + return radius; + } + + @Override + public Region.Circle getAs(Resolution resolution) { + return Region.circle( + resolution.convertFromWorldToThis(x), + resolution.convertFromWorldToThis(y), + resolution.convertFromWorldToThis(radius)); + } + + @Override + public Region.Box getEnclosing() { + return box(getCenter(), getRadius()); + } + + @Override + public boolean contains(int x, int y) { + return distanceSq(this.x, x, this.y, y) <= radiusSq; + } + @Override + public boolean contains(Coordinates point) { + return contains(point.getX(), point.getY()); + } + + @Override + public boolean contains(Region other) { + if(other instanceof Circle) { + Circle o = (Circle) other; + double dist = o.getCenter().getDistance(getCenter()); + return dist + o.radius <= radius; + } + + return contains(other.getCornerNE().substract(1, 0)) + && contains(other.getCornerNW()) + && contains(other.getCornerSE().substract(1, 1)) + && contains(other.getCornerNW().substract(0, 1)); + } + + @Override + public boolean intersectsWith(Region other) { + if(other instanceof Box) + return intersectsBoxCircle((Box) other, this); + if(other instanceof Circle) + return intersectsCircleCircle(this, (Circle) other); + + throw new IllegalArgumentException("unknown region type " + other.getClass().getCanonicalName()); + } + + @Override + public Circle move(int dx, int dy) { + return Region.circle(x+dx, y+dy, radius); + } + + @Override + public Circle move(Coordinates offset) { + return move(offset.getX(), offset.getY()); + } + + @Override + public boolean equals(Object other) { + if (this == other) + return true; + + if (other == null) + return false; + + if (!(other instanceof Circle)) + return false; + + Circle o = (Circle) other; + + return radius == o.radius && x == o.x && y == o.y; + } + @Override + public int hashCode() { + int result = x; + result = 31*result + y; + result = 31*result + radius; + return result; + } + @Override + public String toString() { + return "[center=[" + x + ", " + y + "],radius=" + radius + "]"; + } + + + private static long distanceSq(int x1, int x2, int y1, int y2) { + long dx = x1-x2; + long dy = y1-y2; + return dx*dx+dy*dy; + } + } + + public static final class Box extends Region { + private final int xMin; + private final int yMin; + private final int xMax; + private final int yMax; + + private Box(int xMin, int yMin, int xMax, int yMax) { + this.xMin = xMin; + this.yMin = yMin; + this.xMax = xMax; + this.yMax = yMax; + } + + @Override + public int getCenterX() { + return (xMin+xMax)/2; + } + + @Override + public int getCenterY() { + return (yMin+yMax)/2; + } + + @Override + public int getWidth() { + return xMax - xMin; + } + + @Override + public int getHeight() { + return yMax - yMin; + } + + @Override + public Region.Box getAs(Resolution resolution) { + return Region.box(Coordinates.from(xMin, yMin).getAs(resolution), + Coordinates.from(xMax, yMax).getAs(resolution)); + } + + public Region.Box getEnclosing() { + return this; + } + + @Override + public boolean contains(int posX, int posY) { + return posX >= getXMin() && posX < getXMax() && posY >= getYMin() && posY < getYMax(); + } + + @Override + public boolean contains(Coordinates pos) { + return contains(pos.getX(), pos.getY()); + } + + @Override + public boolean contains(Region other) { + return contains(other.getCornerNW()) && contains(other.getCornerSE().substract(1, 1)); + } + + @Override + public boolean intersectsWith(Region other) { + if(other instanceof Box) + return intersectsBoxBox(this, (Box) other); + if(other instanceof Circle) + return intersectsBoxCircle(this, (Circle) other); + + throw new IllegalArgumentException("unknown region type " + other.getClass().getCanonicalName()); + } + + @Override + public Box move(int dx, int dy) { + return new Box(xMin+dx, yMin+dy, xMax+dx, yMax+dy); + } + + @Override + public Box move(Coordinates offset) { + return move(offset.getX(), offset.getY()); + } + + public int getXMin() { + return xMin; + } + + public int getYMin() { + return yMin; + } + + public int getXMax() { + return xMax; + } + + public int getYMax() { + return yMax; + } + + @Override + public Coordinates getCorner() { + return Coordinates.from(xMin, yMin); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 13; + result = prime * result + xMin; + result = prime * result + yMin; + result = prime * result + xMax; + result = prime * result + yMax; + return result; + } + + @Override + public boolean equals(Object other) { + if (this == other) + return true; + + if (other == null) + return false; + + if (!(other instanceof Box)) + return false; + + Box o = (Box) other; + + return xMin == o.xMin && yMin == o.yMin && xMax == o.xMax && yMax == o.yMax; + } + + @Override + public String toString() { + return "[min=[" + xMin + ", " + yMin + "], max=[" + xMax + ", " + yMax + "]]"; + } + } + + + private static boolean intersectsBoxBox(Box r1, Box r2) { + if (r1.getXMax() <= r2.getXMin()) // this is left of other + return false; + if (r1.getYMax() <= r2.getYMin()) // this is below other + return false; + if (r1.getXMin() >= r2.getXMax()) // this is right of other + return false; + if (r1.getYMin() >= r2.getYMax()) // this is above other + return false; + + return true; + } + + private static boolean intersectsCircleCircle(Circle r1, Circle r2) { + // Two circles intersects iff their centers are sufficiently close + long diffCenterX = r1.getCenterX() - r2.getCenterX(); + long diffCenterY = r1.getCenterY() - r2.getCenterY(); + long radiiSum = r1.getRadius() + r2.getRadius(); + + return diffCenterX*diffCenterX + diffCenterY*diffCenterY <= radiiSum*radiiSum; + } + + private static boolean intersectsBoxCircle(Box r1, Circle r2) { + // All computations are done with doubles to avoid rounding errors + double centerX = r2.getCenterX() + 0.5; + double centerY = r2.getCenterY() + 0.5; + double diffCenterX = r1.getCenterX() - centerX; + double diffCenterY = r1.getCenterY() - centerY; + double width = r1.getWidth(); + double height = r1.getHeight(); + double distance = Math.sqrt(diffCenterX * diffCenterX + diffCenterY * diffCenterY); + + // Inner radius = shortest side / 2 + double innerRadius = Math.min(width, height) / 2; + if (distance <= r2.getRadius() + innerRadius) + return true; + + // Outer radius = diagonal / 2 + double outerRadius = Math.sqrt(width*width + height*height) / 2; + if (distance >= r2.getRadius() + outerRadius) + return false; + + double lengthFactor = r2.getRadius() / distance; + + double outerX = centerX + diffCenterX * lengthFactor; + double outerY = centerY + diffCenterY * lengthFactor; + + return outerX >= r1.getXMin() && outerX < r1.getXMax() && outerY >= r1.getYMin() && outerY < r1.getYMax(); + } +} diff --git a/src/main/java/amidst/mojangapi/world/filter/WorldFilter_Structure.java b/src/main/java/amidst/mojangapi/world/filter/WorldFilter_Structure.java index ba5bd9086..11f581579 100644 --- a/src/main/java/amidst/mojangapi/world/filter/WorldFilter_Structure.java +++ b/src/main/java/amidst/mojangapi/world/filter/WorldFilter_Structure.java @@ -3,6 +3,7 @@ import amidst.documentation.Immutable; import amidst.mojangapi.world.World; import amidst.mojangapi.world.coordinates.Coordinates; +import amidst.mojangapi.world.coordinates.Region; import amidst.mojangapi.world.icon.producer.NameFilteredWorldIconCollector; import amidst.mojangapi.world.icon.producer.WorldIconCollector; import amidst.mojangapi.world.icon.producer.WorldIconProducer; @@ -29,7 +30,7 @@ public boolean isValid(World world) { private void procudeAndCollect(WorldIconProducer structureProducer, WorldIconCollector structureCollector) { for (int x = 0; x < 2 * worldFilterSize; x += 512) { for (int y = 0; y < 2 * worldFilterSize; y += 512) { - structureProducer.produce(Coordinates.from(x, y).add(corner), structureCollector, null); + structureProducer.produce(Region.box(Coordinates.from(x, y).add(corner), 512, 512), structureCollector, null); } } } diff --git a/src/main/java/amidst/mojangapi/world/icon/locationchecker/LocationChecker.java b/src/main/java/amidst/mojangapi/world/icon/locationchecker/LocationChecker.java index 0d5cf4948..7c36d949b 100644 --- a/src/main/java/amidst/mojangapi/world/icon/locationchecker/LocationChecker.java +++ b/src/main/java/amidst/mojangapi/world/icon/locationchecker/LocationChecker.java @@ -1,8 +1,13 @@ package amidst.mojangapi.world.icon.locationchecker; import amidst.documentation.ThreadSafe; +import amidst.mojangapi.world.coordinates.Coordinates; @ThreadSafe public interface LocationChecker { boolean isValidLocation(int x, int y); + + default boolean isValidLocation(Coordinates location) { + return isValidLocation((int) location.getX(), (int) location.getY()); + } } diff --git a/src/main/java/amidst/mojangapi/world/icon/producer/CachedWorldIconProducer.java b/src/main/java/amidst/mojangapi/world/icon/producer/CachedWorldIconProducer.java index d2723b3bd..98dd7960a 100644 --- a/src/main/java/amidst/mojangapi/world/icon/producer/CachedWorldIconProducer.java +++ b/src/main/java/amidst/mojangapi/world/icon/producer/CachedWorldIconProducer.java @@ -5,8 +5,7 @@ import java.util.function.Consumer; import amidst.documentation.ThreadSafe; -import amidst.fragment.Fragment; -import amidst.mojangapi.world.coordinates.Coordinates; +import amidst.mojangapi.world.coordinates.Region; import amidst.mojangapi.world.icon.WorldIcon; @ThreadSafe @@ -15,9 +14,9 @@ public abstract class CachedWorldIconProducer extends WorldIconProducer { private volatile List cache; @Override - public void produce(Coordinates corner, Consumer consumer, Void additionalData) { + public void produce(Region.Box region, Consumer consumer, Void additionalData) { for (WorldIcon icon : getCache()) { - if (icon.getCoordinates().isInBoundsOf(corner, Fragment.SIZE)) { + if (region.contains(icon.getCoordinates())) { consumer.accept(icon); } } diff --git a/src/main/java/amidst/mojangapi/world/icon/producer/StructureProducer.java b/src/main/java/amidst/mojangapi/world/icon/producer/StructureProducer.java index d45718c5d..eb93b8a9a 100644 --- a/src/main/java/amidst/mojangapi/world/icon/producer/StructureProducer.java +++ b/src/main/java/amidst/mojangapi/world/icon/producer/StructureProducer.java @@ -5,6 +5,7 @@ import amidst.documentation.ThreadSafe; import amidst.mojangapi.world.Dimension; import amidst.mojangapi.world.coordinates.Coordinates; +import amidst.mojangapi.world.coordinates.Region; import amidst.mojangapi.world.coordinates.Resolution; import amidst.mojangapi.world.icon.WorldIcon; import amidst.mojangapi.world.icon.locationchecker.LocationChecker; @@ -14,7 +15,6 @@ @ThreadSafe public class StructureProducer extends WorldIconProducer { private final Resolution resolution; - private final int size; private final int offsetInWorld; private final LocationChecker checker; private final WorldIconTypeProvider provider; @@ -29,7 +29,6 @@ public StructureProducer( Dimension dimension, boolean displayDimension) { this.resolution = resolution; - this.size = resolution.getStepsPer(Resolution.FRAGMENT); this.offsetInWorld = offsetInWorld; this.checker = checker; this.provider = provider; @@ -38,30 +37,26 @@ public StructureProducer( } @Override - public void produce(Coordinates corner, Consumer consumer, T additionalData) { - for (int xRelativeToFragment = 0; xRelativeToFragment < size; xRelativeToFragment++) { - for (int yRelativeToFragment = 0; yRelativeToFragment < size; yRelativeToFragment++) { - generateAt(corner, consumer, additionalData, xRelativeToFragment, yRelativeToFragment); + public void produce(Region.Box region, Consumer consumer, T additionalData) { + + region = region.getAs(resolution); + Coordinates min = region.getCornerNW(); + Coordinates max = region.getCornerSE(); + + for(int x = min.getX(); x < max.getX(); x++) { + for(int y = min.getY(); y < max.getY(); y++) { + generateAt(x, y, consumer, additionalData); } } } - - // TODO: use longs? - private void generateAt( - Coordinates corner, - Consumer consumer, - T additionalData, - int xRelativeToFragment, - int yRelativeToFragment) { - int x = xRelativeToFragment + (int) corner.getXAs(resolution); - int y = yRelativeToFragment + (int) corner.getYAs(resolution); - if (checker.isValidLocation(x, y)) { + + private void generateAt(int x, int y, Consumer consumer, T additionalData) { + if(checker.isValidLocation(x, y)) { DefaultWorldIconTypes worldIconType = provider.get(x, y, additionalData); if (worldIconType != null) { - Coordinates coordinates = createCoordinates(corner, xRelativeToFragment, yRelativeToFragment); consumer.accept( new WorldIcon( - coordinates, + Coordinates.from(x, y, resolution).add(offsetInWorld, offsetInWorld), worldIconType.getLabel(), worldIconType.getImage(), dimension, @@ -69,13 +64,4 @@ private void generateAt( } } } - - private Coordinates createCoordinates( - Coordinates corner, - int xRelativeToFragment, - int yRelativeToFragment) { - int xInWorld = resolution.convertFromThisToWorld(xRelativeToFragment); - int yInWorld = resolution.convertFromThisToWorld(yRelativeToFragment); - return corner.add(xInWorld + offsetInWorld, yInWorld + offsetInWorld); - } } diff --git a/src/main/java/amidst/mojangapi/world/icon/producer/WorldIconProducer.java b/src/main/java/amidst/mojangapi/world/icon/producer/WorldIconProducer.java index 5b9ab3f1d..301fdf958 100644 --- a/src/main/java/amidst/mojangapi/world/icon/producer/WorldIconProducer.java +++ b/src/main/java/amidst/mojangapi/world/icon/producer/WorldIconProducer.java @@ -4,16 +4,16 @@ import java.util.function.Consumer; import amidst.documentation.ThreadSafe; -import amidst.mojangapi.world.coordinates.Coordinates; +import amidst.mojangapi.world.coordinates.Region; import amidst.mojangapi.world.icon.WorldIcon; @ThreadSafe public abstract class WorldIconProducer { - public abstract void produce(Coordinates corner, Consumer consumer, T additionalData); + public abstract void produce(Region.Box region, Consumer consumer, T additionalData); - public List getAt(Coordinates corner, T additionalData) { + public List getAt(Region.Box region, T additionalData) { WorldIconCollector collector = new WorldIconCollector(); - produce(corner, collector, additionalData); + produce(region, collector, additionalData); return collector.get(); } } diff --git a/src/main/java/amidst/mojangapi/world/icon/type/WorldIconTypeProvider.java b/src/main/java/amidst/mojangapi/world/icon/type/WorldIconTypeProvider.java index 125bb7d37..08d8097a4 100644 --- a/src/main/java/amidst/mojangapi/world/icon/type/WorldIconTypeProvider.java +++ b/src/main/java/amidst/mojangapi/world/icon/type/WorldIconTypeProvider.java @@ -1,8 +1,13 @@ package amidst.mojangapi.world.icon.type; import amidst.documentation.ThreadSafe; +import amidst.mojangapi.world.coordinates.Coordinates; @ThreadSafe public interface WorldIconTypeProvider { DefaultWorldIconTypes get(int x, int y, T additionalData); + + default DefaultWorldIconTypes get(Coordinates location, T additionalData) { + return get((int) location.getX(), (int) location.getY(), additionalData); + } } diff --git a/src/main/java/amidst/mojangapi/world/oracle/BiomeDataOracle.java b/src/main/java/amidst/mojangapi/world/oracle/BiomeDataOracle.java index b576b9903..99addc39f 100644 --- a/src/main/java/amidst/mojangapi/world/oracle/BiomeDataOracle.java +++ b/src/main/java/amidst/mojangapi/world/oracle/BiomeDataOracle.java @@ -11,6 +11,7 @@ import amidst.mojangapi.world.biome.Biome; import amidst.mojangapi.world.biome.UnknownBiomeIndexException; import amidst.mojangapi.world.coordinates.Coordinates; +import amidst.mojangapi.world.coordinates.Region; import amidst.mojangapi.world.coordinates.Resolution; @ThreadSafe @@ -22,25 +23,30 @@ public BiomeDataOracle(MinecraftInterface minecraftInterface) { } public void populateArray(Coordinates corner, short[][] result, boolean useQuarterResolution) { - Resolution resolution = Resolution.from(useQuarterResolution); + if(result.length == 0) + return; + int width = result.length; - if (width > 0) { - int height = result[0].length; - int left = (int) corner.getXAs(resolution); - int top = (int) corner.getYAs(resolution); - try { - copyToResult(result, width, height, getBiomeData(left, top, width, height, useQuarterResolution)); - } catch (MinecraftInterfaceException e) { - AmidstLogger.error(e); - AmidstMessageBox.displayError("Error", e); - } + int height = result[0].length; + if(height == 0) + return; + + Resolution res = Resolution.from(useQuarterResolution); + Region.Box region = Region.box(corner.getAs(res), width, height); + try { + int[] data = getBiomeData(region, useQuarterResolution); + copyToResult(result, data); + } catch (MinecraftInterfaceException e) { + AmidstLogger.error(e); + AmidstMessageBox.displayError("Error", e); } } - public static void copyToResult(short[][] result, int width, int height, int[] biomeData) { - for (int y = 0; y < height; y++) { - for (int x = 0; x < width; x++) { - result[x][y] = (short) biomeData[getBiomeDataIndex(x, y, width)]; + public static void copyToResult(short[][] result, int[] biomeData) { + int width = result[0].length; + for (int y = 0; y < result.length; y++) { + for (int x = 0; x < result[y].length; x++) { + result[x][y] = (short) biomeData[x + y*width]; } } } @@ -79,7 +85,7 @@ public boolean isValidBiomeForStructure(int x, int y, int size, List vali int width = right - left + 1; int height = bottom - top + 1; try { - int[] biomeData = getQuarterResolutionBiomeData(left, top, width, height); + int[] biomeData = getQuarterResolutionBiomeData(Region.box(left, top, width, height)); for (int i = 0; i < width * height; i++) { if (!validBiomes.contains(Biome.getByIndex(biomeData[i]))) { return false; @@ -115,7 +121,7 @@ public Coordinates findValidLocation(int x, int y, int size, List validBi int width = right - left + 1; int height = bottom - top + 1; try { - int[] biomeData = getQuarterResolutionBiomeData(left, top, width, height); + int[] biomeData = getQuarterResolutionBiomeData(Region.box(left, top, width, height)); Coordinates result = null; int numberOfValidLocations = 0; for (int i = 0; i < width * height; i++) { @@ -159,21 +165,21 @@ public Biome getBiomeAtMiddleOfChunk(int chunkX, int chunkY) * (uses the 1:1 scale biome-map) */ private Biome getBiomeAt(int x, int y) throws UnknownBiomeIndexException, MinecraftInterfaceException { - int[] biomeData = getFullResolutionBiomeData(x, y, 1, 1); + int[] biomeData = getFullResolutionBiomeData(Region.box(x, y, 1, 1)); return Biome.getByIndex(biomeData[0]); } - private int[] getQuarterResolutionBiomeData(int x, int y, int width, int height) + private int[] getQuarterResolutionBiomeData(Region.Box region) throws MinecraftInterfaceException { - return getBiomeData(x, y, width, height, true); + return getBiomeData(region, true); } - private int[] getFullResolutionBiomeData(int x, int y, int width, int height) throws MinecraftInterfaceException { - return getBiomeData(x, y, width, height, false); - } + private int[] getFullResolutionBiomeData(Region.Box region) throws MinecraftInterfaceException { + return getBiomeData(region, false); +} - private int[] getBiomeData(int x, int y, int width, int height, boolean useQuarterResolution) + private int[] getBiomeData(Region.Box region, boolean useQuarterResolution) throws MinecraftInterfaceException { - return minecraftInterface.getBiomeData(x, y, width, height, useQuarterResolution); + return minecraftInterface.getBiomeData(region, useQuarterResolution); } } diff --git a/src/test/java/amidst/mojangapi/mocking/BiomeDataJsonBuilder.java b/src/test/java/amidst/mojangapi/mocking/BiomeDataJsonBuilder.java index dacb57328..2db35fd94 100644 --- a/src/test/java/amidst/mojangapi/mocking/BiomeDataJsonBuilder.java +++ b/src/test/java/amidst/mojangapi/mocking/BiomeDataJsonBuilder.java @@ -5,6 +5,7 @@ import java.util.TreeMap; import amidst.documentation.NotThreadSafe; +import amidst.mojangapi.world.coordinates.Region; import amidst.mojangapi.world.testworld.storage.json.AreaJson; import amidst.mojangapi.world.testworld.storage.json.BiomeDataJson; @@ -13,8 +14,8 @@ public class BiomeDataJsonBuilder { private final SortedMap quarterBiomeData = new TreeMap<>(); private final SortedMap fullBiomeData = new TreeMap<>(); - public void store(int x, int y, int width, int height, boolean useQuarterResolution, int[] biomeData) { - store(getBiomeDataMap(useQuarterResolution), x, y, width, height, biomeData); + public void store(Region.Box region, boolean useQuarterResolution, int[] biomeData) { + store(getBiomeDataMap(useQuarterResolution),region, biomeData); } private Map getBiomeDataMap(boolean useQuarterResolution) { @@ -25,8 +26,8 @@ private Map getBiomeDataMap(boolean useQuarterResolution) { } } - private void store(Map biomeDataMap, int x, int y, int width, int height, int[] biomeData) { - biomeDataMap.put(new AreaJson(x, y, width, height), BiomeDataJson.int2short(biomeData)); + private void store(Map biomeDataMap, Region.Box region, int[] biomeData) { + biomeDataMap.put(AreaJson.from(region), BiomeDataJson.int2short(biomeData)); } public BiomeDataJson createQuarterBiomeData() { diff --git a/src/test/java/amidst/mojangapi/mocking/FakeMinecraftInterface.java b/src/test/java/amidst/mojangapi/mocking/FakeMinecraftInterface.java index 2d46ab2a5..d277fa83c 100644 --- a/src/test/java/amidst/mojangapi/mocking/FakeMinecraftInterface.java +++ b/src/test/java/amidst/mojangapi/mocking/FakeMinecraftInterface.java @@ -5,6 +5,7 @@ import amidst.mojangapi.minecraftinterface.MinecraftInterfaceException; import amidst.mojangapi.minecraftinterface.RecognisedVersion; import amidst.mojangapi.world.WorldType; +import amidst.mojangapi.world.coordinates.Region; import amidst.mojangapi.world.testworld.storage.json.BiomeDataJson; import amidst.mojangapi.world.testworld.storage.json.WorldMetadataJson; @@ -25,10 +26,10 @@ public FakeMinecraftInterface( } @Override - public int[] getBiomeData(int x, int y, int width, int height, boolean useQuarterResolution) + public int[] getBiomeData(Region.Box region, boolean useQuarterResolution) throws MinecraftInterfaceException { if (isWorldCreated) { - return getBiomeData(useQuarterResolution).get(x, y, width, height); + return getBiomeData(useQuarterResolution).get(region); } else { throw new MinecraftInterfaceException("the world needs to be created first"); } diff --git a/src/test/java/amidst/mojangapi/mocking/FragmentCornerWalker.java b/src/test/java/amidst/mojangapi/mocking/FragmentWalker.java similarity index 61% rename from src/test/java/amidst/mojangapi/mocking/FragmentCornerWalker.java rename to src/test/java/amidst/mojangapi/mocking/FragmentWalker.java index 8e84e3881..04fe1adb5 100644 --- a/src/test/java/amidst/mojangapi/mocking/FragmentCornerWalker.java +++ b/src/test/java/amidst/mojangapi/mocking/FragmentWalker.java @@ -5,29 +5,34 @@ import amidst.documentation.Immutable; import amidst.fragment.Fragment; import amidst.mojangapi.world.coordinates.Coordinates; +import amidst.mojangapi.world.coordinates.Region; @Immutable -public class FragmentCornerWalker { - public static FragmentCornerWalker walkFragmentsAroundOrigin(int fragmentsAroundOrigin) { +public class FragmentWalker { + public static FragmentWalker walkFragmentsAroundOrigin(int fragmentsAroundOrigin) { int blocksAroundOrigin = fragmentsAroundOrigin * Fragment.SIZE; Coordinates startCorner = Coordinates.from(-blocksAroundOrigin, -blocksAroundOrigin); Coordinates endCorner = startCorner.add(2 * blocksAroundOrigin, 2 * blocksAroundOrigin); - return new FragmentCornerWalker(startCorner, endCorner); + return new FragmentWalker(startCorner, endCorner); } private final Coordinates startCorner; private final Coordinates endCorner; - public FragmentCornerWalker(Coordinates startCorner, Coordinates endCorner) { + public FragmentWalker(Coordinates startCorner, Coordinates endCorner) { this.startCorner = startCorner; this.endCorner = endCorner; } - public void walk(Consumer consumer) { + public void walkCorners(Consumer consumer) { for (int x = startCorner.getX(); x < endCorner.getX(); x += Fragment.SIZE) { for (int y = startCorner.getY(); y < endCorner.getY(); y += Fragment.SIZE) { consumer.accept(Coordinates.from(x, y)); } } } + + public void walk(Consumer consumer) { + walkCorners(corner -> consumer.accept(Region.box(corner, Fragment.SIZE, Fragment.SIZE))); + } } diff --git a/src/test/java/amidst/mojangapi/mocking/RequestStoringMinecraftInterface.java b/src/test/java/amidst/mojangapi/mocking/RequestStoringMinecraftInterface.java index 9257b2825..d708fff3d 100644 --- a/src/test/java/amidst/mojangapi/mocking/RequestStoringMinecraftInterface.java +++ b/src/test/java/amidst/mojangapi/mocking/RequestStoringMinecraftInterface.java @@ -5,6 +5,7 @@ import amidst.mojangapi.minecraftinterface.MinecraftInterfaceException; import amidst.mojangapi.minecraftinterface.RecognisedVersion; import amidst.mojangapi.world.WorldType; +import amidst.mojangapi.world.coordinates.Region; @ThreadSafe public class RequestStoringMinecraftInterface implements MinecraftInterface { @@ -17,15 +18,15 @@ public RequestStoringMinecraftInterface(MinecraftInterface realMinecraftInterfac } @Override - public synchronized int[] getBiomeData(int x, int y, int width, int height, boolean useQuarterResolution) + public synchronized int[] getBiomeData(Region.Box region, boolean useQuarterResolution) throws MinecraftInterfaceException { - int[] biomeData = realMinecraftInterface.getBiomeData(x, y, width, height, useQuarterResolution); - store(x, y, width, height, useQuarterResolution, biomeData); + int[] biomeData = realMinecraftInterface.getBiomeData(region, useQuarterResolution); + store(region, useQuarterResolution, biomeData); return biomeData; } - private void store(int x, int y, int width, int height, boolean useQuarterResolution, int[] biomeData) { - builder.store(x, y, width, height, useQuarterResolution, biomeData); + private void store(Region.Box region, boolean useQuarterResolution, int[] biomeData) { + builder.store(region, useQuarterResolution, biomeData); } @Override diff --git a/src/test/java/amidst/mojangapi/world/test/TestRegion.java b/src/test/java/amidst/mojangapi/world/test/TestRegion.java new file mode 100644 index 000000000..87f6a64d1 --- /dev/null +++ b/src/test/java/amidst/mojangapi/world/test/TestRegion.java @@ -0,0 +1,69 @@ +package amidst.mojangapi.world.test; + +import org.junit.Assert; +import org.junit.Test; + +import amidst.mojangapi.world.coordinates.Coordinates; +import amidst.mojangapi.world.coordinates.Region; + +public class TestRegion { + + @Test + public void testRegionAppartenance() { + Region.Box box = Region.box(-10, -20, 30, 40); + Assert.assertTrue(box.contains(box)); + Assert.assertTrue(box.contains(0, 0)); + Assert.assertTrue(box.contains(box.getXMin(), box.getYMin())); + Assert.assertFalse(box.contains(box.getXMin(), box.getYMax())); + Assert.assertFalse(box.contains(box.getXMax(), box.getYMin())); + Assert.assertFalse(box.contains(box.getXMax(), box.getYMax())); + + Region.Circle circle = Region.circle(-10, -20, 50); + Assert.assertTrue(circle.contains(circle)); + Assert.assertTrue(circle.contains(0, 0)); + Coordinates center = circle.getCenter(); + Assert.assertTrue(circle.contains(center.add(0, 50))); + Assert.assertTrue(circle.contains(center.add(0, -50))); + Assert.assertTrue(circle.contains(center.add(50, 0))); + Assert.assertTrue(circle.contains(center.add(-50, 0))); + Assert.assertFalse(circle.contains(150, 20)); + } + + @Test + public void testBoxBoxIntersection() { + Region.Box box = Region.box(0, 0, 50, 50); + Assert.assertTrue(box.intersectsWith(box)); + + Assert.assertTrue(box.intersectsWith(Region.box(20, 10, 100, 100))); + Assert.assertFalse(box.intersectsWith(Region.box(-50, -70, 30, 30))); + } + + @Test + public void testCircleCircleIntersection() { + Region.Circle circle = Region.circle(0, 0, 100); + Assert.assertTrue(circle.intersectsWith(circle)); + + Assert.assertTrue(circle.intersectsWith(Region.circle(50, 50, 50))); + Assert.assertTrue(circle.intersectsWith(Region.circle(100, 90, 50))); + + Assert.assertFalse(circle.intersectsWith(Region.circle(120, -100, 10))); + } + + @Test + public void testBoxCircleIntersection() { + Region.Box box = Region.box(0, 0, 200, 100); + + // Inside inner radius + Assert.assertTrue(box.intersectsWith(Region.circle(0, 0, 100))); + + // Outside outer radius + Assert.assertFalse(box.intersectsWith(Region.circle(500, 500, 100))); + + // Intermediate + Assert.assertTrue(box.intersectsWith(Region.circle(-10, -10, 50))); + Assert.assertFalse(box.intersectsWith(Region.circle(100, -50, 40))); + + // Other + Assert.assertTrue(Region.box(-1536, 0, 512, 512).intersectsWith(Region.circle(-140, 256, 1024))); + } +} diff --git a/src/test/java/amidst/mojangapi/world/testworld/DefaultTestWorldDirectoryDeclaration.java b/src/test/java/amidst/mojangapi/world/testworld/DefaultTestWorldDirectoryDeclaration.java index 549182fd3..6e5493e72 100644 --- a/src/test/java/amidst/mojangapi/world/testworld/DefaultTestWorldDirectoryDeclaration.java +++ b/src/test/java/amidst/mojangapi/world/testworld/DefaultTestWorldDirectoryDeclaration.java @@ -151,7 +151,7 @@ private Function endCityExtractor(DefaultWorld return world -> CoordinatesCollectionJson.extractWorldIcons( world.getEndCityProducer(), worldIconType.getLabel(), - corner -> world.getEndIslandOracle().getAt(corner), + region -> world.getEndIslandOracle().getAt(region.getCorner()), END_FRAGMENTS_AROUND_ORIGIN, MINIMAL_NUMBER_OF_COORDINATES); } diff --git a/src/test/java/amidst/mojangapi/world/testworld/storage/json/AreaJson.java b/src/test/java/amidst/mojangapi/world/testworld/storage/json/AreaJson.java index 33f41645a..f10d19536 100644 --- a/src/test/java/amidst/mojangapi/world/testworld/storage/json/AreaJson.java +++ b/src/test/java/amidst/mojangapi/world/testworld/storage/json/AreaJson.java @@ -2,38 +2,43 @@ import amidst.documentation.GsonObject; import amidst.documentation.Immutable; +import amidst.mojangapi.world.coordinates.Region; @Immutable @GsonObject public class AreaJson implements Comparable { - private volatile long x; - private volatile long y; - private volatile long width; - private volatile long height; + private volatile int x; + private volatile int y; + private volatile int width; + private volatile int height; public AreaJson() { } - public AreaJson(long x, long y, long width, long height) { + public AreaJson(int x, int y, int width, int height) { this.x = x; this.y = y; this.width = width; this.height = height; } + + public static AreaJson from(Region.Box region) { + return new AreaJson(region.getXMin(), region.getYMin(), region.getWidth(), region.getHeight()); + } - public long getX() { + public int getX() { return x; } - public long getY() { + public int getY() { return y; } - public long getWidth() { + public int getWidth() { return width; } - public long getHeight() { + public int getHeight() { return height; } diff --git a/src/test/java/amidst/mojangapi/world/testworld/storage/json/BiomeDataJson.java b/src/test/java/amidst/mojangapi/world/testworld/storage/json/BiomeDataJson.java index d626bce25..c7e35a0bf 100644 --- a/src/test/java/amidst/mojangapi/world/testworld/storage/json/BiomeDataJson.java +++ b/src/test/java/amidst/mojangapi/world/testworld/storage/json/BiomeDataJson.java @@ -4,6 +4,7 @@ import amidst.documentation.GsonObject; import amidst.documentation.Immutable; +import amidst.mojangapi.world.coordinates.Region; @Immutable @GsonObject @@ -33,9 +34,8 @@ public BiomeDataJson(SortedMap biomeData) { this.biomeData = biomeData; } - public int[] get(int x, int y, int width, int height) { - AreaJson area = new AreaJson(x, y, width, height); - short[] result = biomeData.get(area); + public int[] get(Region.Box region) { + short[] result = biomeData.get(AreaJson.from(region)); if (result != null) { return short2int(result); } else { diff --git a/src/test/java/amidst/mojangapi/world/testworld/storage/json/CoordinatesCollectionJson.java b/src/test/java/amidst/mojangapi/world/testworld/storage/json/CoordinatesCollectionJson.java index 2555ce855..69b425999 100644 --- a/src/test/java/amidst/mojangapi/world/testworld/storage/json/CoordinatesCollectionJson.java +++ b/src/test/java/amidst/mojangapi/world/testworld/storage/json/CoordinatesCollectionJson.java @@ -9,9 +9,10 @@ import amidst.documentation.Immutable; import amidst.logging.AmidstLogger; import amidst.logging.AmidstMessageBox; -import amidst.mojangapi.mocking.FragmentCornerWalker; +import amidst.mojangapi.mocking.FragmentWalker; import amidst.mojangapi.world.World; import amidst.mojangapi.world.coordinates.Coordinates; +import amidst.mojangapi.world.coordinates.Region; import amidst.mojangapi.world.icon.WorldIcon; import amidst.mojangapi.world.icon.producer.NameFilteredWorldIconCollector; import amidst.mojangapi.world.icon.producer.WorldIconProducer; @@ -32,12 +33,12 @@ public static CoordinatesCollectionJson extractStrongholds(World world) { public static CoordinatesCollectionJson extractWorldIcons( WorldIconProducer producer, String name, - Function additionalDataFactory, + Function additionalDataFactory, int fragmentsAroundOrigin, int minimalNumberOfCoordinates) { NameFilteredWorldIconCollector consumer = new NameFilteredWorldIconCollector(name); - FragmentCornerWalker.walkFragmentsAroundOrigin(fragmentsAroundOrigin).walk( - corner -> producer.produce(corner, consumer, additionalDataFactory.apply(corner))); + FragmentWalker.walkFragmentsAroundOrigin(fragmentsAroundOrigin).walk( + region -> producer.produce(region, consumer, additionalDataFactory.apply(region))); SortedSet coordinates = createSortedSet(consumer.get()); if (coordinates.size() < minimalNumberOfCoordinates) { String message = "not enough coordinates for '" + name + "'"; 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 94a1db818..79a572ca0 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 @@ -6,7 +6,7 @@ import amidst.documentation.GsonObject; import amidst.documentation.Immutable; -import amidst.mojangapi.mocking.FragmentCornerWalker; +import amidst.mojangapi.mocking.FragmentWalker; import amidst.mojangapi.world.coordinates.Coordinates; import amidst.mojangapi.world.oracle.EndIsland; import amidst.mojangapi.world.oracle.EndIslandOracle; @@ -16,7 +16,7 @@ public class EndIslandsJson { public static EndIslandsJson extract(EndIslandOracle oracle, int fragmentsAroundOrigin) { SortedMap> result = new TreeMap<>(); - FragmentCornerWalker.walkFragmentsAroundOrigin(fragmentsAroundOrigin).walk( + FragmentWalker.walkFragmentsAroundOrigin(fragmentsAroundOrigin).walkCorners( corner -> result.put(corner, oracle.getAt(corner))); return new EndIslandsJson(result); } diff --git a/src/test/java/amidst/mojangapi/world/testworld/storage/json/WorldMetadataJson.java b/src/test/java/amidst/mojangapi/world/testworld/storage/json/WorldMetadataJson.java index 3f0ccd972..e68fd2d19 100644 --- a/src/test/java/amidst/mojangapi/world/testworld/storage/json/WorldMetadataJson.java +++ b/src/test/java/amidst/mojangapi/world/testworld/storage/json/WorldMetadataJson.java @@ -1,7 +1,7 @@ package amidst.mojangapi.world.testworld.storage.json; -import amidst.documentation.Immutable; import amidst.documentation.GsonObject; +import amidst.documentation.Immutable; import amidst.mojangapi.minecraftinterface.RecognisedVersion; import amidst.mojangapi.world.World; import amidst.mojangapi.world.WorldType; From 659c14f5228a36258d2b0b92193de730e4350353 Mon Sep 17 00:00:00 2001 From: moulins <> Date: Fri, 23 Jun 2017 15:20:44 +0200 Subject: [PATCH 08/13] Add BiomeData class This class encapsulates the biome array returned by the MinecraftInterface. This has the added benefit of avoiding copies when unnecessary. --- src/main/java/amidst/fragment/Fragment.java | 18 +- .../mojangapi/world/biome/BiomeData.java | 154 ++++++++++++++++++ .../world/filter/WorldFilter_Biome.java | 22 +-- .../world/oracle/BiomeDataOracle.java | 127 ++++++--------- src/main/java/amidst/util/BiomeFunction.java | 6 + src/main/java/amidst/util/BiomePredicate.java | 6 + .../mojangapi/world/test/TestBiomeData.java | 60 +++++++ 7 files changed, 303 insertions(+), 90 deletions(-) create mode 100644 src/main/java/amidst/mojangapi/world/biome/BiomeData.java create mode 100644 src/main/java/amidst/util/BiomeFunction.java create mode 100644 src/main/java/amidst/util/BiomePredicate.java create mode 100644 src/test/java/amidst/mojangapi/world/test/TestBiomeData.java diff --git a/src/main/java/amidst/fragment/Fragment.java b/src/main/java/amidst/fragment/Fragment.java index 67250dbaf..1a7f2bced 100644 --- a/src/main/java/amidst/fragment/Fragment.java +++ b/src/main/java/amidst/fragment/Fragment.java @@ -10,6 +10,10 @@ import amidst.documentation.CalledOnlyBy; import amidst.documentation.NotThreadSafe; import amidst.gui.main.viewer.Drawer; +import amidst.logging.AmidstLogger; +import amidst.logging.AmidstMessageBox; +import amidst.mojangapi.minecraftinterface.MinecraftInterfaceException; +import amidst.mojangapi.world.biome.BiomeData; import amidst.mojangapi.world.coordinates.Coordinates; import amidst.mojangapi.world.coordinates.Region; import amidst.mojangapi.world.coordinates.Resolution; @@ -84,7 +88,7 @@ public class Fragment { private volatile Coordinates corner; private volatile float alpha; - private volatile short[][] biomeData; + private volatile BiomeData biomeData; private volatile List endIslands; private final AtomicReferenceArray images; private final AtomicReferenceArray> worldIcons; @@ -103,16 +107,22 @@ public float getAlpha() { } public void initBiomeData(int width, int height) { - biomeData = new short[width][height]; + biomeData = new BiomeData(null, width, height); } @CalledOnlyBy(AmidstThread.FRAGMENT_LOADER) public void populateBiomeData(BiomeDataOracle biomeDataOracle) { - biomeDataOracle.populateArray(corner, biomeData, true); + try { + Region.Box region = Region.box(corner, SIZE, SIZE); + biomeData.copyFrom(biomeDataOracle.getBiomeData(region, true)); + } catch (MinecraftInterfaceException e) { + AmidstLogger.error(e); + AmidstMessageBox.displayError("Error", e); + } } public short getBiomeDataAt(int x, int y) { - return biomeData[x][y]; + return biomeData.get(x, y); } public void setEndIslands(List endIslands) { diff --git a/src/main/java/amidst/mojangapi/world/biome/BiomeData.java b/src/main/java/amidst/mojangapi/world/biome/BiomeData.java new file mode 100644 index 000000000..d681ee986 --- /dev/null +++ b/src/main/java/amidst/mojangapi/world/biome/BiomeData.java @@ -0,0 +1,154 @@ +package amidst.mojangapi.world.biome; + +import amidst.util.BiomeFunction; +import amidst.util.BiomePredicate; + +/** + * This class encapsulate a buffer containing the biome data of a + * rectangular region. + * + * The 2D data is stored in row-major order to be directly compatible + * with the arrays handed by the {@link MinecraftInterface}. + * + * This class supports the creation of views into sub-regions of the + * buffer, without copying any data. However, these views don't own their + * buffer, and thus will be invalidated when the original buffer is modified. + * If you need to store the buffer somewhere, make sure to call {@link #makeOwned()} + * to ensure that the buffer owns its contents. + */ +public class BiomeData { + //We use an int array so we can directly reference the + //Minecraft arrays, without any copying. + private int[] data; + + private int stride; //number of elems between two rows + private int width; //size of a row + private int height; //number of rows + private int offset; //offset before first element + + private boolean isOwned; + + private BiomeData(int[] data, int width, int height, int stride, int offset, boolean isOwned) { + this.data = data; + this.width = width; + this.height = height; + this.stride = stride; + this.offset = offset; + this.isOwned = isOwned; + + if(stride < width) + throw new IllegalArgumentException("invalid stride"); + + if(offset < 0 || width < 0 || height < 0 || translateIndex(width-1, height-1) >= data.length) + throw new IllegalArgumentException("invalid width/height/offset"); + } + + public BiomeData(int[] data, int width, int height) { + this(data == null ? new int[width*height] : data, width, height, width, 0, true); + } + + public BiomeData view() { + return new BiomeData(data, width, height, stride, offset, false); + } + + public BiomeData view(int offX, int offY, int w, int h) { + if(offX < 0 || offY < 0 || offX+w > width || offY+h > height) + throw new IllegalArgumentException("invalid offsets"); + + return new BiomeData(data, w, h, stride, translateIndex(offX, offY), false); + } + + public void copyFrom(BiomeData other) { + width = other.width; + height = other.height; + stride = other.stride; + offset = other.offset; + + if(isOwned) { + if(other.getSize() > data.length) + data = new int[other.getSize()]; + + fillData(other.data); + } else { + data = other.data; + } + } + + public boolean isOwned() { + return isOwned; + } + + public void makeOwned() { + if(isOwned) + return; + + int[] old = data; + data = new int[getWidth()*getHeight()]; + fillData(old); + + isOwned = true; + } + + private void fillData(int[] src) { + if(stride == width) { + //no gaps: we can do a single copy + System.arraycopy(src, translateIndex(0, 0), data, 0, getSize()); + } else { + for(int j = 0; j < height; j++) + System.arraycopy(src, translateIndex(0, j), data, j*width, width); + } + + width = getWidth(); + height = getHeight(); + stride = width; + offset = 0; + } + + public int getWidth() { + return width; + } + + public int getHeight() { + return height; + } + + public int getSize() { + return getWidth()*getHeight(); + } + + public short get(int x, int y) { + return (short) data[translateIndex(x, y)]; + } + + public T findFirst(BiomeFunction fn) { + int start = offset; + for(int y = 0; y < height; y++) { + for(int x = 0; x < width; x++) { + T obj = fn.apply(x, y, (short) data[start+x]); + if(obj != null) + return obj; + } + start += stride; + } + + return null; + } + + public boolean checkAll(BiomePredicate pred) { + int start = offset; + for(int y = 0; y < height; y++) { + for(int x = 0; x < width; x++) { + if(!pred.test(x, y, (short) data[start+x])) + return false; + } + start += stride; + } + + return true; + } + + private int translateIndex(int x, int y) { + return offset + y*stride + x; + } + +} diff --git a/src/main/java/amidst/mojangapi/world/filter/WorldFilter_Biome.java b/src/main/java/amidst/mojangapi/world/filter/WorldFilter_Biome.java index f51489f5c..85705cf8b 100644 --- a/src/main/java/amidst/mojangapi/world/filter/WorldFilter_Biome.java +++ b/src/main/java/amidst/mojangapi/world/filter/WorldFilter_Biome.java @@ -3,29 +3,31 @@ import java.util.Set; import amidst.documentation.NotThreadSafe; +import amidst.logging.AmidstLogger; +import amidst.mojangapi.minecraftinterface.MinecraftInterfaceException; import amidst.mojangapi.world.World; +import amidst.mojangapi.world.biome.BiomeData; +import amidst.mojangapi.world.coordinates.Coordinates; +import amidst.mojangapi.world.coordinates.Region; @NotThreadSafe public class WorldFilter_Biome extends WorldFilter { private final Set validBiomeIndexes; - private short[][] region; public WorldFilter_Biome(int worldFilterSize, Set validBiomeIndexes) { super(worldFilterSize); this.validBiomeIndexes = validBiomeIndexes; - this.region = new short[(int) this.quarterFilterSize * 2][(int) this.quarterFilterSize * 2]; } @Override public boolean isValid(World world) { - world.getBiomeDataOracle().populateArray(corner, region, true); - for (short[] row : region) { - for (short entry : row) { - if (validBiomeIndexes.contains(entry)) { - return true; - } - } + try { + BiomeData data = world.getBiomeDataOracle().getBiomeData(Region.box(Coordinates.origin(), this.quarterFilterSize), true); + return data.checkAll((x, y, b) -> validBiomeIndexes.contains(b)); + + } catch (MinecraftInterfaceException e) { + AmidstLogger.error(e); + return false; } - return false; } } diff --git a/src/main/java/amidst/mojangapi/world/oracle/BiomeDataOracle.java b/src/main/java/amidst/mojangapi/world/oracle/BiomeDataOracle.java index 99addc39f..720f6a196 100644 --- a/src/main/java/amidst/mojangapi/world/oracle/BiomeDataOracle.java +++ b/src/main/java/amidst/mojangapi/world/oracle/BiomeDataOracle.java @@ -9,6 +9,7 @@ import amidst.mojangapi.minecraftinterface.MinecraftInterface; import amidst.mojangapi.minecraftinterface.MinecraftInterfaceException; import amidst.mojangapi.world.biome.Biome; +import amidst.mojangapi.world.biome.BiomeData; import amidst.mojangapi.world.biome.UnknownBiomeIndexException; import amidst.mojangapi.world.coordinates.Coordinates; import amidst.mojangapi.world.coordinates.Region; @@ -21,38 +22,24 @@ public class BiomeDataOracle { public BiomeDataOracle(MinecraftInterface minecraftInterface) { this.minecraftInterface = minecraftInterface; } - - public void populateArray(Coordinates corner, short[][] result, boolean useQuarterResolution) { - if(result.length == 0) - return; - - int width = result.length; - int height = result[0].length; - if(height == 0) - return; - - Resolution res = Resolution.from(useQuarterResolution); - Region.Box region = Region.box(corner.getAs(res), width, height); - try { - int[] data = getBiomeData(region, useQuarterResolution); - copyToResult(result, data); - } catch (MinecraftInterfaceException e) { - AmidstLogger.error(e); - AmidstMessageBox.displayError("Error", e); - } - } - - public static void copyToResult(short[][] result, int[] biomeData) { - int width = result[0].length; - for (int y = 0; y < result.length; y++) { - for (int x = 0; x < result[y].length; x++) { - result[x][y] = (short) biomeData[x + y*width]; - } - } + + /** + * Returns the biome data of the specified region. + * + * CAUTION: the BiomeData object returned is a direct view into + * {@link MinecraftInterface}'s internal buffers, and so will be invalidated + * by future calls to {@link #getBiomeData}. + */ + public BiomeData getBiomeData(Region.Box region, boolean useQuarterResolution) + throws MinecraftInterfaceException { + region = region.getAs(Resolution.from(useQuarterResolution)); + return doGetBiomeData(region, useQuarterResolution); } - public static int getBiomeDataIndex(int x, int y, int width) { - return x + y * width; + private BiomeData doGetBiomeData(Region.Box region, boolean useQuarterResolution) + throws MinecraftInterfaceException { + int[] data = minecraftInterface.getBiomeData(region, useQuarterResolution); + return new BiomeData(data, region.getWidth(), region.getHeight()).view(); } public boolean isValidBiomeAtMiddleOfChunk(int chunkX, int chunkY, List validBiomes) { @@ -61,7 +48,7 @@ public boolean isValidBiomeAtMiddleOfChunk(int chunkX, int chunkY, List v private boolean isValidBiome(int x, int y, List validBiomes) { try { - return validBiomes.contains(getBiomeAt(x, y)); + return validBiomes.contains(Biome.getByIndex(getBiomeAt(x, y))); } catch (UnknownBiomeIndexException e) { AmidstLogger.error(e); AmidstMessageBox.displayError("Error", e); @@ -76,7 +63,7 @@ private boolean isValidBiome(int x, int y, List validBiomes) { public boolean isValidBiomeForStructureAtMiddleOfChunk(int chunkX, int chunkY, int size, List validBiomes) { return isValidBiomeForStructure(getMiddleOfChunk(chunkX), getMiddleOfChunk(chunkY), size, validBiomes); } - + public boolean isValidBiomeForStructure(int x, int y, int size, List validBiomes) { int left = x - size >> 2; int top = y - size >> 2; @@ -84,18 +71,16 @@ public boolean isValidBiomeForStructure(int x, int y, int size, List vali int bottom = y + size >> 2; int width = right - left + 1; int height = bottom - top + 1; + try { - int[] biomeData = getQuarterResolutionBiomeData(Region.box(left, top, width, height)); - for (int i = 0; i < width * height; i++) { - if (!validBiomes.contains(Biome.getByIndex(biomeData[i]))) { + BiomeData biomeData = doGetBiomeData(Region.box(left, top, width, height), true); + return biomeData.checkAll((bx, by, b) -> { + try { + return validBiomes.contains(Biome.getByIndex(b)); + } catch (UnknownBiomeIndexException e) { return false; } - } - return true; - } catch (UnknownBiomeIndexException e) { - AmidstLogger.error(e); - AmidstMessageBox.displayError("Error", e); - return false; + }); } catch (MinecraftInterfaceException e) { AmidstLogger.error(e); AmidstMessageBox.displayError("Error", e); @@ -120,22 +105,27 @@ public Coordinates findValidLocation(int x, int y, int size, List validBi int bottom = y + size >> 2; int width = right - left + 1; int height = bottom - top + 1; + try { - int[] biomeData = getQuarterResolutionBiomeData(Region.box(left, top, width, height)); - Coordinates result = null; - int numberOfValidLocations = 0; - for (int i = 0; i < width * height; i++) { - if (validBiomes.contains(Biome.getByIndex(biomeData[i])) - && (result == null || random.nextInt(numberOfValidLocations + 1) == 0)) { - result = createCoordinates(left, top, width, i); - numberOfValidLocations++; + Coordinates[] result = {null}; + int[] numberOfValidLocations = {0}; + + BiomeData biomeData = doGetBiomeData(Region.box(left, top, width, height), true); + biomeData.checkAll((bx, by, b) -> { + try { + if (validBiomes.contains(Biome.getByIndex(b)) + && (result[0] == null || random.nextInt(numberOfValidLocations[0] + 1) == 0)) { + result[0] = createCoordinates(left, top, bx, by); + numberOfValidLocations[0]++; + } + } catch (UnknownBiomeIndexException e) { + return false; } - } - return result; - } catch (UnknownBiomeIndexException e) { - AmidstLogger.error(e); - AmidstMessageBox.displayError("Error", e); - return null; + + return true; + }); + + return result[0]; } catch (MinecraftInterfaceException e) { AmidstLogger.error(e); AmidstMessageBox.displayError("Error", e); @@ -143,9 +133,9 @@ public Coordinates findValidLocation(int x, int y, int size, List validBi } } - private Coordinates createCoordinates(int left, int top, int width, int i) { - int x = left + i % width << 2; - int y = top + i / width << 2; + private Coordinates createCoordinates(int left, int top, int x, int y) { + x = left + x << 2; + y = top + y << 2; return Coordinates.from(x, y); } @@ -156,7 +146,7 @@ private int getMiddleOfChunk(int coordinate) { public Biome getBiomeAtMiddleOfChunk(int chunkX, int chunkY) throws UnknownBiomeIndexException, MinecraftInterfaceException { - return getBiomeAt(getMiddleOfChunk(chunkX), getMiddleOfChunk(chunkY)); + return Biome.getByIndex(getBiomeAt(getMiddleOfChunk(chunkX), getMiddleOfChunk(chunkY))); } /** @@ -164,22 +154,7 @@ public Biome getBiomeAtMiddleOfChunk(int chunkX, int chunkY) * routine, it was added for rare things like accurately testing structures. * (uses the 1:1 scale biome-map) */ - private Biome getBiomeAt(int x, int y) throws UnknownBiomeIndexException, MinecraftInterfaceException { - int[] biomeData = getFullResolutionBiomeData(Region.box(x, y, 1, 1)); - return Biome.getByIndex(biomeData[0]); - } - - private int[] getQuarterResolutionBiomeData(Region.Box region) - throws MinecraftInterfaceException { - return getBiomeData(region, true); - } - - private int[] getFullResolutionBiomeData(Region.Box region) throws MinecraftInterfaceException { - return getBiomeData(region, false); -} - - private int[] getBiomeData(Region.Box region, boolean useQuarterResolution) - throws MinecraftInterfaceException { - return minecraftInterface.getBiomeData(region, useQuarterResolution); + public short getBiomeAt(int x, int y) throws MinecraftInterfaceException { + return doGetBiomeData(Region.box(x, y, 1, 1), false).get(0, 0); } } diff --git a/src/main/java/amidst/util/BiomeFunction.java b/src/main/java/amidst/util/BiomeFunction.java new file mode 100644 index 000000000..3254b583a --- /dev/null +++ b/src/main/java/amidst/util/BiomeFunction.java @@ -0,0 +1,6 @@ +package amidst.util; + +@FunctionalInterface +public interface BiomeFunction { + public T apply(int x, int y, short biome); +} diff --git a/src/main/java/amidst/util/BiomePredicate.java b/src/main/java/amidst/util/BiomePredicate.java new file mode 100644 index 000000000..18ee89036 --- /dev/null +++ b/src/main/java/amidst/util/BiomePredicate.java @@ -0,0 +1,6 @@ +package amidst.util; + +@FunctionalInterface +public interface BiomePredicate { + public boolean test(int x, int y, short biome); +} \ No newline at end of file diff --git a/src/test/java/amidst/mojangapi/world/test/TestBiomeData.java b/src/test/java/amidst/mojangapi/world/test/TestBiomeData.java new file mode 100644 index 000000000..b5aaad381 --- /dev/null +++ b/src/test/java/amidst/mojangapi/world/test/TestBiomeData.java @@ -0,0 +1,60 @@ +package amidst.mojangapi.world.test; + +import amidst.mojangapi.world.biome.BiomeData; +import org.junit.Assert; +import org.junit.Test; + +public class TestBiomeData { + + private static int DATA_WIDTH = 16; + private static int DATA_HEIGHT = 24; + + private BiomeData makeData() { + int[] arr = new int[DATA_WIDTH*DATA_HEIGHT]; + for(int i = 0; i < arr.length; i++) + arr[i] = i; + + return new BiomeData(arr, DATA_WIDTH, DATA_HEIGHT); + } + + private void testView(BiomeData data, int offX, int offY, int w, int h) { + BiomeData view = data.view(offX, offY, w, h); + + //Test that the view correctly map indexes + for(int i = 0; i < w; i++) { + for(int j = 0; j < h; j++) { + Assert.assertEquals(data.get(i+offX, j+offY), view.get(i, j)); + } + } + + view.makeOwned(); + //Test that the view correctly copied the data + for(int i = 0; i < w; i++) { + for(int j = 0; j < h; j++) { + Assert.assertEquals(data.get(i+offX, j+offY), view.get(i, j)); + } + } + } + + private void testAllViews(BiomeData data) { + int width = data.getWidth(); + int height = data.getHeight(); + for(int i = 0; i < width; i++) { + for(int j = 0; j < height; j++) { + for(int w = 0; w < width-i; w++) { + for(int h = 0; h < height-j; h++) + testView(data, i, j, w, h); + } + } + } + + } + + @Test + public void testViews() { + BiomeData data = makeData(); + + testAllViews(data); + testAllViews(data.view(5, 5, 10, 15)); + } +} From 72267deeba82ce0003b9e819a419d2f51b67776b Mon Sep 17 00:00:00 2001 From: moulins <> Date: Fri, 23 Jun 2017 15:24:16 +0200 Subject: [PATCH 09/13] Add CachedBiomeDataOracle This class caches biome data in a particular region between invocations of getBiomeData. --- .../java/amidst/mojangapi/world/World.java | 26 +++++ .../amidst/mojangapi/world/WorldBuilder.java | 5 +- .../world/oracle/BiomeDataOracle.java | 19 +--- .../world/oracle/CachedBiomeDataOracle.java | 104 ++++++++++++++++++ .../world/oracle/RawBiomeDataOracle.java | 25 +++++ 5 files changed, 164 insertions(+), 15 deletions(-) create mode 100644 src/main/java/amidst/mojangapi/world/oracle/CachedBiomeDataOracle.java create mode 100644 src/main/java/amidst/mojangapi/world/oracle/RawBiomeDataOracle.java diff --git a/src/main/java/amidst/mojangapi/world/World.java b/src/main/java/amidst/mojangapi/world/World.java index 7b947e914..5913f4aa3 100644 --- a/src/main/java/amidst/mojangapi/world/World.java +++ b/src/main/java/amidst/mojangapi/world/World.java @@ -5,11 +5,13 @@ import amidst.documentation.ThreadSafe; import amidst.mojangapi.minecraftinterface.RecognisedVersion; +import amidst.mojangapi.world.coordinates.Region; import amidst.mojangapi.world.icon.WorldIcon; import amidst.mojangapi.world.icon.producer.CachedWorldIconProducer; import amidst.mojangapi.world.icon.producer.SpawnProducer; import amidst.mojangapi.world.icon.producer.WorldIconProducer; import amidst.mojangapi.world.oracle.BiomeDataOracle; +import amidst.mojangapi.world.oracle.CachedBiomeDataOracle; import amidst.mojangapi.world.oracle.EndIsland; import amidst.mojangapi.world.oracle.EndIslandOracle; import amidst.mojangapi.world.oracle.SlimeChunkOracle; @@ -78,6 +80,30 @@ public World( this.endCityProducer = endCityProducer; } + public World cached(Region.Box region) { + //@formatter: off + return new World( + onDisposeWorld, + worldOptions, + movablePlayerList, + recognisedVersion, + versionFeatures, + new CachedBiomeDataOracle(biomeDataOracle, region), + endIslandOracle, + slimeChunkOracle, + worldSpawnOracle, + strongholdProducer, + playerProducer, + villageProducer, + templeProducer, + mineshaftProducer, + oceanMonumentProducer, + netherFortressProducer, + endCityProducer + ); + //@formatter: on + } + public WorldOptions getWorldOptions() { return worldOptions; } diff --git a/src/main/java/amidst/mojangapi/world/WorldBuilder.java b/src/main/java/amidst/mojangapi/world/WorldBuilder.java index 7c1979244..6d960d80f 100644 --- a/src/main/java/amidst/mojangapi/world/WorldBuilder.java +++ b/src/main/java/amidst/mojangapi/world/WorldBuilder.java @@ -25,6 +25,7 @@ import amidst.mojangapi.world.oracle.EndIslandOracle; import amidst.mojangapi.world.oracle.HeuristicWorldSpawnOracle; import amidst.mojangapi.world.oracle.ImmutableWorldSpawnOracle; +import amidst.mojangapi.world.oracle.RawBiomeDataOracle; import amidst.mojangapi.world.oracle.SlimeChunkOracle; import amidst.mojangapi.world.oracle.WorldSpawnOracle; import amidst.mojangapi.world.player.MovablePlayerList; @@ -57,7 +58,7 @@ public World from( MinecraftInterface minecraftInterface, Consumer onDisposeWorld, WorldOptions worldOptions) throws MinecraftInterfaceException { - BiomeDataOracle biomeDataOracle = new BiomeDataOracle(minecraftInterface); + BiomeDataOracle biomeDataOracle = new RawBiomeDataOracle(minecraftInterface); VersionFeatures versionFeatures = DefaultVersionFeatures.create(minecraftInterface.getRecognisedVersion()); return create( minecraftInterface, @@ -87,7 +88,7 @@ public World fromSaveGame(MinecraftInterface minecraftInterface, Consumer WorldOptions.fromSaveGame(saveGame), movablePlayerList, versionFeatures, - new BiomeDataOracle(minecraftInterface), + new RawBiomeDataOracle(minecraftInterface), new ImmutableWorldSpawnOracle(saveGame.getWorldSpawn())); } diff --git a/src/main/java/amidst/mojangapi/world/oracle/BiomeDataOracle.java b/src/main/java/amidst/mojangapi/world/oracle/BiomeDataOracle.java index 720f6a196..04aa2a27d 100644 --- a/src/main/java/amidst/mojangapi/world/oracle/BiomeDataOracle.java +++ b/src/main/java/amidst/mojangapi/world/oracle/BiomeDataOracle.java @@ -6,7 +6,6 @@ import amidst.documentation.ThreadSafe; import amidst.logging.AmidstLogger; import amidst.logging.AmidstMessageBox; -import amidst.mojangapi.minecraftinterface.MinecraftInterface; import amidst.mojangapi.minecraftinterface.MinecraftInterfaceException; import amidst.mojangapi.world.biome.Biome; import amidst.mojangapi.world.biome.BiomeData; @@ -16,12 +15,9 @@ import amidst.mojangapi.world.coordinates.Resolution; @ThreadSafe -public class BiomeDataOracle { - private final MinecraftInterface minecraftInterface; +public abstract class BiomeDataOracle { - public BiomeDataOracle(MinecraftInterface minecraftInterface) { - this.minecraftInterface = minecraftInterface; - } + public BiomeDataOracle() {} /** * Returns the biome data of the specified region. @@ -33,14 +29,11 @@ public BiomeDataOracle(MinecraftInterface minecraftInterface) { public BiomeData getBiomeData(Region.Box region, boolean useQuarterResolution) throws MinecraftInterfaceException { region = region.getAs(Resolution.from(useQuarterResolution)); - return doGetBiomeData(region, useQuarterResolution); - } - - private BiomeData doGetBiomeData(Region.Box region, boolean useQuarterResolution) - throws MinecraftInterfaceException { - int[] data = minecraftInterface.getBiomeData(region, useQuarterResolution); - return new BiomeData(data, region.getWidth(), region.getHeight()).view(); + return doGetBiomeData(region, useQuarterResolution); } + + protected abstract BiomeData doGetBiomeData(Region.Box region, boolean useQuarterResolution) + throws MinecraftInterfaceException; public boolean isValidBiomeAtMiddleOfChunk(int chunkX, int chunkY, List validBiomes) { return isValidBiome(getMiddleOfChunk(chunkX), getMiddleOfChunk(chunkY), validBiomes); diff --git a/src/main/java/amidst/mojangapi/world/oracle/CachedBiomeDataOracle.java b/src/main/java/amidst/mojangapi/world/oracle/CachedBiomeDataOracle.java new file mode 100644 index 000000000..c6d4ef379 --- /dev/null +++ b/src/main/java/amidst/mojangapi/world/oracle/CachedBiomeDataOracle.java @@ -0,0 +1,104 @@ +package amidst.mojangapi.world.oracle; + +import amidst.documentation.ThreadSafe; +import amidst.mojangapi.minecraftinterface.MinecraftInterfaceException; +import amidst.mojangapi.world.biome.BiomeData; +import amidst.mojangapi.world.coordinates.Coordinates; +import amidst.mojangapi.world.coordinates.Region; +import amidst.mojangapi.world.coordinates.Resolution; + +@ThreadSafe +public class CachedBiomeDataOracle extends BiomeDataOracle { + private BiomeDataOracle oracle; + private Region.Box cacheRegion; + private Region.Box cacheRegionQuarter; + private boolean isCacheValid; + private boolean isCacheQuarterValid; + private BiomeData cacheQuarter; + private BiomeData cache; + + public CachedBiomeDataOracle(BiomeDataOracle oracle, Region.Box region) { + this.oracle = oracle; + this.cacheRegion = region; + this.cacheRegionQuarter = region.getAs(Resolution.QUARTER); + this.isCacheValid = false; + this.isCacheQuarterValid = false; + this.cache = null; + this.cacheQuarter = null; + } + + @Override + public synchronized short getBiomeAt(int x, int y) throws MinecraftInterfaceException { + if(isCacheValid) + return doGetBiomeData(Region.box(x, y, 1, 1), false).get(0, 0); + return oracle.getBiomeAt(x, y); + } + + @Override + protected synchronized BiomeData doGetBiomeData(Region.Box region, boolean useQuarterResolution) + throws MinecraftInterfaceException { + Region.Box cacheReg = useQuarterResolution ? cacheRegionQuarter : cacheRegion; + if(!cacheReg.contains(region)) + return oracle.doGetBiomeData(region, useQuarterResolution); + + Coordinates offset = region.getCorner().substract(cacheReg.getCorner()); + return getCacheData(useQuarterResolution).view(offset.getX(), offset.getY(), region.getWidth(), region.getHeight()); + } + + public synchronized BiomeData getCacheData(boolean useQuarterResolution) + throws MinecraftInterfaceException { + Resolution r = Resolution.from(useQuarterResolution); + + if(useQuarterResolution) { + if(isCacheQuarterValid) + return cacheQuarter; + + if(cacheQuarter == null) + cacheQuarter = createDataArray(r); + + + cacheQuarter.copyFrom(oracle.doGetBiomeData(cacheRegionQuarter, true)); + return cacheQuarter; + + } else { + if(isCacheValid) + return cache; + + if(cache == null) + cache = createDataArray(r); + + cache.copyFrom(oracle.doGetBiomeData(cacheRegion, true)); + return cache; + } + } + + private BiomeData createDataArray(Resolution resolution) { + Region r = cacheRegion.getAs(resolution); + return new BiomeData(null, r.getWidth(), r.getHeight()); + } + + + public synchronized Region.Box getCacheRegion() { + return cacheRegion; + } + + public synchronized void moveCacheTo(Region.Box r) { + if(cacheRegion.equals(r)) + return; + + Region old = cacheRegion; + cacheRegion = r; + cacheRegionQuarter = r.getAs(Resolution.QUARTER); + + if(old.getWidth() != r.getWidth() || old.getHeight() != r.getHeight()) { + cache = null; + cacheQuarter = null; + isCacheValid = false; + } else { + isCacheValid = old.getCorner().equals(r.getCorner()); + } + + isCacheQuarterValid = isCacheValid; + } +} + diff --git a/src/main/java/amidst/mojangapi/world/oracle/RawBiomeDataOracle.java b/src/main/java/amidst/mojangapi/world/oracle/RawBiomeDataOracle.java new file mode 100644 index 000000000..e28a6730c --- /dev/null +++ b/src/main/java/amidst/mojangapi/world/oracle/RawBiomeDataOracle.java @@ -0,0 +1,25 @@ +package amidst.mojangapi.world.oracle; + +import amidst.documentation.ThreadSafe; +import amidst.mojangapi.minecraftinterface.MinecraftInterface; +import amidst.mojangapi.minecraftinterface.MinecraftInterfaceException; +import amidst.mojangapi.world.biome.BiomeData; +import amidst.mojangapi.world.coordinates.Region; + +@ThreadSafe +public class RawBiomeDataOracle extends BiomeDataOracle { + private final MinecraftInterface minecraftInterface; + + public RawBiomeDataOracle(MinecraftInterface minecraftInterface) { + this.minecraftInterface = minecraftInterface; + + } + + @Override + protected BiomeData doGetBiomeData(Region.Box region, boolean useQuarterResolution) + throws MinecraftInterfaceException { + + int[] data = minecraftInterface.getBiomeData(region, useQuarterResolution); + return new BiomeData(data, region.getWidth(), region.getHeight()).view(); + } +} From 2621b51fcf4cc5bf5a042f82d34c24cf0a18dfe3 Mon Sep 17 00:00:00 2001 From: moulins <> Date: Fri, 23 Jun 2017 15:53:51 +0200 Subject: [PATCH 10/13] Add parsing of filter files and skeleton of filter structures --- search.json | 24 +++ src/main/java/amidst/AmidstSettings.java | 3 + src/main/java/amidst/FeatureToggles.java | 2 +- .../java/amidst/filter/BiomeConstraint.java | 45 +++++ src/main/java/amidst/filter/Constraint.java | 14 ++ src/main/java/amidst/filter/Criterion.java | 10 + .../amidst/filter/StructureConstraint.java | 62 ++++++ src/main/java/amidst/filter/WorldFilter.java | 36 ++++ .../filter/criterion/MatchAllCriterion.java | 24 +++ .../filter/criterion/MatchAnyCriterion.java | 23 +++ .../filter/criterion/NegateCriterion.java | 22 ++ .../filter/criterion/SimpleCriterion.java | 23 +++ .../filter/json/CriterionDeserializer.java | 87 ++++++++ .../amidst/filter/json/CriterionJson.java | 83 ++++++++ .../amidst/filter/json/CriterionJsonAnd.java | 23 +++ .../amidst/filter/json/CriterionJsonBase.java | 189 ++++++++++++++++++ .../filter/json/CriterionJsonContext.java | 130 ++++++++++++ .../filter/json/CriterionJsonGroup.java | 20 ++ .../amidst/filter/json/CriterionJsonOr.java | 44 ++++ .../amidst/filter/json/WorldFilterJson.java | 86 ++++++++ .../json/WorldFilterParseException.java | 34 ++++ .../gui/main/PerMainWindowInjector.java | 4 +- .../SeedSearcherConfiguration.java | 2 +- .../gui/seedsearcher/SeedSearcherWindow.java | 77 +++++-- .../json/filter/WorldFilterJson_Biome.java | 47 ----- .../json/filter/WorldFilterJson_MatchAll.java | 72 ------- .../filter/WorldFilterJson_Structure.java | 33 --- .../mojangapi/world/filter/WorldFilter.java | 34 ---- .../world/filter/WorldFilter_Biome.java | 33 --- .../world/filter/WorldFilter_MatchAll.java | 26 --- .../world/filter/WorldFilter_Structure.java | 74 ------- .../amidst/parsing/json/GsonProvider.java | 12 +- 32 files changed, 1057 insertions(+), 341 deletions(-) create mode 100644 search.json create mode 100644 src/main/java/amidst/filter/BiomeConstraint.java create mode 100644 src/main/java/amidst/filter/Constraint.java create mode 100644 src/main/java/amidst/filter/Criterion.java create mode 100644 src/main/java/amidst/filter/StructureConstraint.java create mode 100644 src/main/java/amidst/filter/WorldFilter.java create mode 100644 src/main/java/amidst/filter/criterion/MatchAllCriterion.java create mode 100644 src/main/java/amidst/filter/criterion/MatchAnyCriterion.java create mode 100644 src/main/java/amidst/filter/criterion/NegateCriterion.java create mode 100644 src/main/java/amidst/filter/criterion/SimpleCriterion.java create mode 100644 src/main/java/amidst/filter/json/CriterionDeserializer.java create mode 100644 src/main/java/amidst/filter/json/CriterionJson.java create mode 100644 src/main/java/amidst/filter/json/CriterionJsonAnd.java create mode 100644 src/main/java/amidst/filter/json/CriterionJsonBase.java create mode 100644 src/main/java/amidst/filter/json/CriterionJsonContext.java create mode 100644 src/main/java/amidst/filter/json/CriterionJsonGroup.java create mode 100644 src/main/java/amidst/filter/json/CriterionJsonOr.java create mode 100644 src/main/java/amidst/filter/json/WorldFilterJson.java create mode 100644 src/main/java/amidst/filter/json/WorldFilterParseException.java delete mode 100644 src/main/java/amidst/mojangapi/file/json/filter/WorldFilterJson_Biome.java delete mode 100644 src/main/java/amidst/mojangapi/file/json/filter/WorldFilterJson_MatchAll.java delete mode 100644 src/main/java/amidst/mojangapi/file/json/filter/WorldFilterJson_Structure.java delete mode 100644 src/main/java/amidst/mojangapi/world/filter/WorldFilter.java delete mode 100644 src/main/java/amidst/mojangapi/world/filter/WorldFilter_Biome.java delete mode 100644 src/main/java/amidst/mojangapi/world/filter/WorldFilter_MatchAll.java delete mode 100644 src/main/java/amidst/mojangapi/world/filter/WorldFilter_Structure.java diff --git a/search.json b/search.json new file mode 100644 index 000000000..a92430c58 --- /dev/null +++ b/search.json @@ -0,0 +1,24 @@ +{ + "defaults": { + "center": null, + "shape": "circle" + }, + + "groups": { + + + "struct": { + "structures": ["Witch"], + "radius": 2000 + }, + + "biome": { + "biomes": ["Forest"], + "radius": 2000 + } + + }, + + "match": ["struct", "biome"], + "ignore": [] +} \ No newline at end of file diff --git a/src/main/java/amidst/AmidstSettings.java b/src/main/java/amidst/AmidstSettings.java index 0035033d7..70ada87d7 100644 --- a/src/main/java/amidst/AmidstSettings.java +++ b/src/main/java/amidst/AmidstSettings.java @@ -37,6 +37,7 @@ public class AmidstSettings { public final Setting lastProfile; public final Setting worldType; + public final Setting searchJsonFile; /** * This is not persisted. @@ -69,6 +70,8 @@ public AmidstSettings(Preferences preferences) { lastProfile = Settings.createString( preferences, "profile", ""); worldType = Settings.createString( preferences, "worldType", WorldType.PROMPT_EACH_TIME); + searchJsonFile = Settings.createString( preferences, "searchJsonFile", "search.json"); + biomeProfileSelection = new BiomeProfileSelection(BiomeProfile.getDefaultProfile()); // @formatter:on } diff --git a/src/main/java/amidst/FeatureToggles.java b/src/main/java/amidst/FeatureToggles.java index cc92356c2..1cb57e1b8 100644 --- a/src/main/java/amidst/FeatureToggles.java +++ b/src/main/java/amidst/FeatureToggles.java @@ -6,6 +6,6 @@ public enum FeatureToggles { ; - public static final boolean SEED_SEARCH = false; + public static final boolean SEED_SEARCH = true; public static final boolean WORLD_EXPORTER = false; } diff --git a/src/main/java/amidst/filter/BiomeConstraint.java b/src/main/java/amidst/filter/BiomeConstraint.java new file mode 100644 index 000000000..3d43269ac --- /dev/null +++ b/src/main/java/amidst/filter/BiomeConstraint.java @@ -0,0 +1,45 @@ +package amidst.filter; + +import amidst.documentation.Immutable; +import amidst.mojangapi.world.biome.Biome; +import amidst.mojangapi.world.coordinates.Region; + + +@Immutable +public class BiomeConstraint implements Constraint { + + private final Region region; + private final Biome biome; + private final boolean checkDistance; + + public BiomeConstraint(Region region, Biome biome, boolean check) { + this.region = region; + this.biome = biome; + checkDistance = check; + } + + @Override + public Region getRegion() { + return region; + } + + @Override + public boolean equals(Object other) { + if(!(other instanceof BiomeConstraint)) + return false; + + BiomeConstraint o = (BiomeConstraint) other; + return region.equals(o.region) + && biome.equals(o.biome) + && checkDistance == o.checkDistance; + } + + @Override + public int hashCode() { + int prime = 31; + int h = region.hashCode(); + h = prime*h + biome.hashCode(); + h = prime*h + (checkDistance?0:1); + return h; + } +} diff --git a/src/main/java/amidst/filter/Constraint.java b/src/main/java/amidst/filter/Constraint.java new file mode 100644 index 000000000..058d48eba --- /dev/null +++ b/src/main/java/amidst/filter/Constraint.java @@ -0,0 +1,14 @@ +package amidst.filter; + +import amidst.documentation.Immutable; +import amidst.mojangapi.world.coordinates.Region; + +@Immutable +public interface Constraint { + + public Region getRegion(); + + public boolean equals(Object other); + public int hashCode(); + +} diff --git a/src/main/java/amidst/filter/Criterion.java b/src/main/java/amidst/filter/Criterion.java new file mode 100644 index 000000000..a366dd2c2 --- /dev/null +++ b/src/main/java/amidst/filter/Criterion.java @@ -0,0 +1,10 @@ +package amidst.filter; + +import java.util.List; + +import amidst.documentation.Immutable; + +@Immutable +public interface Criterion { + public List getChildren(); +} diff --git a/src/main/java/amidst/filter/StructureConstraint.java b/src/main/java/amidst/filter/StructureConstraint.java new file mode 100644 index 000000000..c8db1c561 --- /dev/null +++ b/src/main/java/amidst/filter/StructureConstraint.java @@ -0,0 +1,62 @@ +package amidst.filter; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Optional; +import java.util.Set; + +import amidst.documentation.Immutable; +import amidst.logging.AmidstLogger; +import amidst.mojangapi.minecraftinterface.MinecraftInterfaceException; +import amidst.mojangapi.world.World; +import amidst.mojangapi.world.biome.Biome; +import amidst.mojangapi.world.coordinates.Coordinates; +import amidst.mojangapi.world.coordinates.Region; +import amidst.mojangapi.world.icon.WorldIcon; +import amidst.mojangapi.world.icon.producer.WorldIconProducer; +import amidst.mojangapi.world.icon.type.DefaultWorldIconTypes; + + +@Immutable +public class StructureConstraint implements Constraint { + + private final Region region; + private final DefaultWorldIconTypes structure; + private final Biome biome; + private final boolean checkDistance; + + public StructureConstraint(Region region, DefaultWorldIconTypes structure, Biome biome, boolean check) { + this.region = region; + this.structure = structure; + this.biome = biome; + checkDistance = check; + } + + @Override + public Region getRegion() { + return region; + } + + @Override + public boolean equals(Object other) { + if(!(other instanceof StructureConstraint)) + return false; + + StructureConstraint o = (StructureConstraint) other; + return region.equals(o.region) + && biome.equals(o.biome) + && checkDistance == o.checkDistance + && structure == o.structure; + } + + @Override + public int hashCode() { + int prime = 31; + int h = region.hashCode(); + h = prime*h + biome.hashCode(); + h = prime*h + (checkDistance?0:1); + h = prime*h + structure.hashCode(); + return h; + } +} diff --git a/src/main/java/amidst/filter/WorldFilter.java b/src/main/java/amidst/filter/WorldFilter.java new file mode 100644 index 000000000..514680581 --- /dev/null +++ b/src/main/java/amidst/filter/WorldFilter.java @@ -0,0 +1,36 @@ +package amidst.filter; + +import java.util.Map; + +import amidst.mojangapi.world.World; +import amidst.mojangapi.world.coordinates.Coordinates; +import amidst.mojangapi.world.coordinates.Resolution; + +public class WorldFilter { + + public static final Resolution REGION_SIZE = Resolution.FRAGMENT; + + //if null, use world spawn point + private final Coordinates globalCenter; + + private final Map criteria; + private final Criterion match; + + public WorldFilter(Coordinates center, Map criteria, Criterion match) { + globalCenter = center; + this.criteria = criteria; + this.match = match; + } + + private Coordinates getGlobalCenter(World world) { + if(globalCenter != null) + return globalCenter; + Coordinates center = world.getSpawnOracle().get(); + return center == null ? Coordinates.origin() : center; + } + + public boolean isValid(World world) { + //TODO + return true; + } +} diff --git a/src/main/java/amidst/filter/criterion/MatchAllCriterion.java b/src/main/java/amidst/filter/criterion/MatchAllCriterion.java new file mode 100644 index 000000000..7c27fb46f --- /dev/null +++ b/src/main/java/amidst/filter/criterion/MatchAllCriterion.java @@ -0,0 +1,24 @@ +package amidst.filter.criterion; + +import java.util.ArrayList; +import java.util.List; + +import amidst.documentation.Immutable; +import amidst.filter.Criterion; + +import java.util.Collections; + +@Immutable +public class MatchAllCriterion implements Criterion { + + private final List criteria; + + public MatchAllCriterion(List list) { + criteria = Collections.unmodifiableList(new ArrayList<>(list)); + } + + @Override + public List getChildren() { + return criteria; + } +} diff --git a/src/main/java/amidst/filter/criterion/MatchAnyCriterion.java b/src/main/java/amidst/filter/criterion/MatchAnyCriterion.java new file mode 100644 index 000000000..be2c04f2e --- /dev/null +++ b/src/main/java/amidst/filter/criterion/MatchAnyCriterion.java @@ -0,0 +1,23 @@ +package amidst.filter.criterion; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import amidst.documentation.Immutable; +import amidst.filter.Criterion; + +@Immutable +public class MatchAnyCriterion implements Criterion { + + private final List criteria; + + public MatchAnyCriterion(List list) { + criteria = Collections.unmodifiableList(new ArrayList<>(list)); + } + + @Override + public List getChildren() { + return criteria; + } +} diff --git a/src/main/java/amidst/filter/criterion/NegateCriterion.java b/src/main/java/amidst/filter/criterion/NegateCriterion.java new file mode 100644 index 000000000..7c7143066 --- /dev/null +++ b/src/main/java/amidst/filter/criterion/NegateCriterion.java @@ -0,0 +1,22 @@ +package amidst.filter.criterion; + +import java.util.Collections; +import java.util.List; + +import amidst.documentation.Immutable; +import amidst.filter.Criterion; + +@Immutable +public class NegateCriterion implements Criterion { + + private final Criterion criterion; + + public NegateCriterion(String name, Criterion c) { + criterion = c; + } + + @Override + public List getChildren() { + return Collections.singletonList(criterion); + } +} diff --git a/src/main/java/amidst/filter/criterion/SimpleCriterion.java b/src/main/java/amidst/filter/criterion/SimpleCriterion.java new file mode 100644 index 000000000..1bb8b70c8 --- /dev/null +++ b/src/main/java/amidst/filter/criterion/SimpleCriterion.java @@ -0,0 +1,23 @@ +package amidst.filter.criterion; + +import java.util.Collections; +import java.util.List; + +import amidst.documentation.Immutable; +import amidst.filter.Constraint; +import amidst.filter.Criterion; + +@Immutable +public class SimpleCriterion implements Criterion { + + private final Constraint constraint; + + public SimpleCriterion(Constraint c) { + constraint = c; + } + + @Override + public List getChildren() { + return Collections.emptyList(); + } +} diff --git a/src/main/java/amidst/filter/json/CriterionDeserializer.java b/src/main/java/amidst/filter/json/CriterionDeserializer.java new file mode 100644 index 000000000..6355cf4a2 --- /dev/null +++ b/src/main/java/amidst/filter/json/CriterionDeserializer.java @@ -0,0 +1,87 @@ +package amidst.filter.json; + +import java.lang.reflect.Type; +import java.util.stream.Collectors; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; + +public class CriterionDeserializer implements JsonDeserializer { + + private JsonElement manageShorthands(JsonElement json) { + if(json.isJsonArray()) { //And criterion shorthand + JsonObject obj = new JsonObject(); + obj.add("and", json); + return obj; + + } + + if(json.isJsonPrimitive()) {//Group reference shorthand + JsonObject obj = new JsonObject(); + obj.add("group", json); + return obj; + } + + return json; + } + + @Override + public CriterionJson deserialize(JsonElement json, Type type, JsonDeserializationContext context) + throws JsonParseException { + json = manageShorthands(json); + + if(!json.isJsonObject()) + throw new JsonParseException("invalid element type: " + getTypeString(json)); + + JsonObject obj = json.getAsJsonObject(); + Class realClass = getRealClass(obj); + if(realClass == null) { + String summary = obj.entrySet().stream() + .map(e -> e.getKey()) + .collect(Collectors.joining(", ", "{ ", " }")); + + throw new JsonParseException("unknown criterion type: " + summary); + } + + return context.deserialize(json, realClass); + } + + public Class getRealClass(JsonObject obj) { + if(obj.has("group")) + return CriterionJsonGroup.class; + + if(obj.has("and")) + return CriterionJsonAnd.class; + + if(obj.has("or")) + return CriterionJsonOr.class; + + if(obj.has("structures")) + return CriterionJsonBase.class; + + if(obj.has("biomes")) + return CriterionJsonBase.class; + + return null; + } + + private static String getTypeString(JsonElement e) { + if(e.isJsonArray()) + return "array"; + + if(e.isJsonObject()) + return "object"; + + if(e.isJsonPrimitive()) + return "primitive"; + + if(e.isJsonNull()) + return "null"; + + return "unknown"; + } + +} \ No newline at end of file diff --git a/src/main/java/amidst/filter/json/CriterionJson.java b/src/main/java/amidst/filter/json/CriterionJson.java new file mode 100644 index 000000000..ca0d17375 --- /dev/null +++ b/src/main/java/amidst/filter/json/CriterionJson.java @@ -0,0 +1,83 @@ +package amidst.filter.json; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +import amidst.documentation.GsonObject; +import amidst.documentation.JsonField; +import amidst.filter.Criterion; +import amidst.filter.criterion.NegateCriterion; +import amidst.mojangapi.world.coordinates.Coordinates; + +/** + * This class represent a criterion, as represented in the JSON structure. + * The criterion is NOT validated, and may not be valid for semantic reasons. + * + * It can be converted to an usable form with the method validate(). + */ +@GsonObject +public abstract class CriterionJson { + + @JsonField(optional=true) + public Coordinates center = Coordinates.origin(); + + @JsonField(optional=true) + public boolean negate = false; + + @JsonField(optional=true) + public int score = 0; + + public CriterionJson() {} + + /** + * This method validate the criterion and convert it to a + * instance of the amidst.mojangapi.world.filter.Criterion. + * + * The CriterionJsonContext is used to collect errors and to + * provide default values. + * + * If any error occurs while validating a criterion, an empty + * Optional is returned. + */ + public Optional validate(CriterionJsonContext ctx) { + + if(center != null) { + ctx.withCenter(ctx.getCenter().add(center)); + } + + Optional criterion; + if(negate) { + criterion = doValidate(ctx.withName("!")) + .map(c -> new NegateCriterion(ctx.getName(), c)); + } else { + criterion = doValidate(ctx); + } + + + if(score != 0) { + ctx.unsupportedAttribute("score"); + return Optional.empty(); + } + + return criterion; + } + + + // This method takes care of subclass-specific validation. + protected abstract Optional doValidate(CriterionJsonContext ctx); + + + protected static Optional> validateList(List list, CriterionJsonContext ctx, String listName) { + List criteria = new ArrayList<>(); + boolean isOk = true; + for(int i = 0; i < list.size(); i++) { + Optional res = list.get(i).validate(ctx.withName(listName + "[" + i + "]")); + + if(isOk && res.isPresent()) + criteria.add(res.get()); + else isOk = false; + } + return isOk ? Optional.of(criteria) : Optional.empty(); + } +} diff --git a/src/main/java/amidst/filter/json/CriterionJsonAnd.java b/src/main/java/amidst/filter/json/CriterionJsonAnd.java new file mode 100644 index 000000000..b13b3833f --- /dev/null +++ b/src/main/java/amidst/filter/json/CriterionJsonAnd.java @@ -0,0 +1,23 @@ +package amidst.filter.json; + +import java.util.List; +import java.util.Optional; + +import amidst.documentation.GsonObject; +import amidst.documentation.JsonField; +import amidst.filter.Criterion; +import amidst.filter.criterion.MatchAllCriterion; + +@GsonObject +public class CriterionJsonAnd extends CriterionJson { + @JsonField() + public List and; + + public CriterionJsonAnd() {} + + @Override + protected Optional doValidate(CriterionJsonContext ctx) { + return validateList(and, ctx, "and") + .map(MatchAllCriterion::new); + } +} \ No newline at end of file diff --git a/src/main/java/amidst/filter/json/CriterionJsonBase.java b/src/main/java/amidst/filter/json/CriterionJsonBase.java new file mode 100644 index 000000000..0692eab90 --- /dev/null +++ b/src/main/java/amidst/filter/json/CriterionJsonBase.java @@ -0,0 +1,189 @@ +package amidst.filter.json; + +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; + +import amidst.documentation.GsonObject; +import amidst.documentation.JsonField; +import amidst.filter.BiomeConstraint; +import amidst.filter.Criterion; +import amidst.filter.StructureConstraint; +import amidst.filter.criterion.MatchAnyCriterion; +import amidst.filter.criterion.SimpleCriterion; +import amidst.mojangapi.world.biome.Biome; +import amidst.mojangapi.world.coordinates.Region; +import amidst.mojangapi.world.icon.type.DefaultWorldIconTypes; + +@GsonObject +public class CriterionJsonBase extends CriterionJson { + @JsonField() + public int radius; + + @JsonField(optional=true) + public String shape = null; + + @JsonField(optional=true) + public List biomes = Collections.emptyList(); + + @JsonField(optional=true) + public boolean variants = false; + + @JsonField(optional=true) + public List structures = null; + + @JsonField(optional=true, require={"structures"}) + public ClusterInfo cluster; + + @GsonObject + public static class ClusterInfo { + @JsonField(optional=true) + public int radius = 0; + + @JsonField() + public int size; + } + + + @Override + protected Optional doValidate(CriterionJsonContext ctx) { + + if(shape == null) + shape = ctx.getShape(); + + if(cluster != null) + ctx.unsupportedAttribute("cluster"); + + if(radius <= 0) + ctx.error("the radius must be strictly positive (is " + radius + ")"); + + Collection biomeSet = getBiomeSet(ctx); + Collection structSet = getStructureSet(ctx); + if(biomeSet.isEmpty() && structSet.isEmpty()) + ctx.error("the biome list can't be empty if no structure is specified"); + + + boolean isChecked = false; + boolean isSquare = false; + switch(shape) { + case "square": + isChecked = true; + isSquare = true; + break; + + case "circle": + isChecked = true; + isSquare = false; + break; + + case "square_nocheck": + isChecked = false; + isSquare = true; + break; + + case "circle_nocheck": + isChecked = false; + isSquare = false; + break; + + default: + ctx.error("unknown shape " + shape); + } + + if(ctx.hasErrors()) + return Optional.empty(); + + Region region = isSquare ? Region.box(center, radius) : Region.circle(center, radius); + + + List list = new ArrayList<>(); + + if(structSet.isEmpty()) { + for(Biome b: biomeSet) { + list.add(new SimpleCriterion( + new BiomeConstraint(region, b, isChecked) + )); + } + } else if(biomeSet.isEmpty()) { + for(DefaultWorldIconTypes struct: structSet) { + list.add(new SimpleCriterion( + new StructureConstraint(region, struct, null, isChecked) + )); + } + } else { + for(Biome b: biomeSet) { + for(DefaultWorldIconTypes struct: structSet) { + list.add(new SimpleCriterion( + new StructureConstraint(region, struct, b, isChecked) + )); + } + } + } + + + if(list.size() == 1) + return Optional.of(list.get(0)); + + return Optional.of(new MatchAnyCriterion(list)); + } + + + private Collection getBiomeSet(CriterionJsonContext ctx) { + Set biomeSet = new HashSet<>(); + for(String biomeName: biomes) { + if(Biome.exists(biomeName)) { + Biome b = Biome.getByName(biomeName); + if(!biomeSet.add(b)) + ctx.error("duplicate biome " + b.getName()); + + if(variants) { + Biome spec = b.getSpecialVariant(); + if(b != spec && !biomeSet.add(spec)) + ctx.error("duplicate biome " + spec.getName()); + } + + } else ctx.error("the biome " + biomeName + " doesn't exist"); + } + return biomeSet; + } + + private Collection getStructureSet(CriterionJsonContext ctx) { + Set structSet = new HashSet<>(); + if(structures == null) + return structSet; + + for(String structName: this.structures) { + DefaultWorldIconTypes struct = DefaultWorldIconTypes.getByName(structName.toLowerCase()); + if(struct == null) + ctx.error("the structure " + structName + " doesn't exist"); + else structSet.add(struct); + } + return structSet; + } + + public static class ClusterInfoDeserializer implements JsonDeserializer { + @Override + public ClusterInfo deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) + throws JsonParseException { + if(json.isJsonPrimitive()) { + JsonObject obj = new JsonObject(); + obj.add("size", json); + json = obj; + } + + return context.deserialize(json, ClusterInfo.class); + } + + } +} \ No newline at end of file diff --git a/src/main/java/amidst/filter/json/CriterionJsonContext.java b/src/main/java/amidst/filter/json/CriterionJsonContext.java new file mode 100644 index 000000000..834a99f0e --- /dev/null +++ b/src/main/java/amidst/filter/json/CriterionJsonContext.java @@ -0,0 +1,130 @@ +package amidst.filter.json; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.function.Function; + +import amidst.filter.Criterion; +import amidst.mojangapi.world.coordinates.Coordinates; + +public class CriterionJsonContext { + private Globals globals; + + private boolean hasErrors = false; + + private String name = ""; + private String shape; + private Coordinates center = null; + + private static class Globals { + public Function supplier; + + //List of criteria already converted; + //if null, the criterion is being converted and we have a circular reference + public Map> mappings; + public List errors; + + public Globals(Function criterionSupplier) { + errors = new ArrayList<>(); + mappings = new HashMap<>(); + supplier = criterionSupplier; + } + } + + public CriterionJsonContext(WorldFilterJson.Defaults defaults, Function criterionSupplier) { + shape = defaults.shape; + center = Coordinates.origin(); + globals = new Globals(criterionSupplier); + } + + + private CriterionJsonContext(CriterionJsonContext ctx) { + globals = ctx.globals; + name = ctx.name; + hasErrors = false; + shape = ctx.shape; + center = ctx.center; + } + + public CriterionJsonContext copy() { + return new CriterionJsonContext(this); + } + + public String getName() { + return name.isEmpty() ? "" : name; + } + + public CriterionJsonContext withName(String name) { + CriterionJsonContext ctx = copy(); + if(!ctx.name.isEmpty()) + ctx.name += "."; + ctx.name += name; + return ctx; + } + + public String getShape() { + return shape; + } + + public CriterionJsonContext withShape(String shape) { + CriterionJsonContext ctx = copy(); + ctx.shape = shape; + return ctx; + } + + public Coordinates getCenter() { + return center; + } + + public CriterionJsonContext withCenter(Coordinates center) { + CriterionJsonContext ctx = copy(); + ctx.center = center; + return ctx; + } + + public Optional convertCriterion(String name) { + CriterionJson json = globals.supplier.apply(name); + if(json == null) { + error("the group " + name + " doesn't exist"); + return Optional.empty(); + } + + if(globals.mappings.containsKey(name)) { + Optional c = globals.mappings.get(name); + if(c == null) { + error("circular reference to group " + name); + return Optional.empty(); + } + return c; + } + + globals.mappings.put(name, null); + CriterionJsonContext ctx = copy(); + ctx.name = name; + Optional c = json.validate(ctx); + globals.mappings.put(name, c); + return c; + } + + public void error(String msg) { + msg = "In " + getName() + ": " + msg; + globals.errors.add(msg); + hasErrors = true; + } + + public void unsupportedAttribute(String attName) { + error("the " + attName + " attribute isn't supported yet"); + } + + public boolean hasErrors() { + return hasErrors; + } + + public List getErrors() { + return globals.errors; + } + +} diff --git a/src/main/java/amidst/filter/json/CriterionJsonGroup.java b/src/main/java/amidst/filter/json/CriterionJsonGroup.java new file mode 100644 index 000000000..90b56e1ee --- /dev/null +++ b/src/main/java/amidst/filter/json/CriterionJsonGroup.java @@ -0,0 +1,20 @@ +package amidst.filter.json; + +import java.util.Optional; + +import amidst.documentation.GsonObject; +import amidst.documentation.JsonField; +import amidst.filter.Criterion; + +@GsonObject +public class CriterionJsonGroup extends CriterionJson { + @JsonField() + public String group; + + public CriterionJsonGroup() {} + + @Override + protected Optional doValidate(CriterionJsonContext ctx) { + return ctx.convertCriterion(group); + } +} \ No newline at end of file diff --git a/src/main/java/amidst/filter/json/CriterionJsonOr.java b/src/main/java/amidst/filter/json/CriterionJsonOr.java new file mode 100644 index 000000000..154104812 --- /dev/null +++ b/src/main/java/amidst/filter/json/CriterionJsonOr.java @@ -0,0 +1,44 @@ +package amidst.filter.json; + +import java.util.List; +import java.util.Optional; + +import amidst.documentation.GsonObject; +import amidst.documentation.JsonField; +import amidst.filter.Criterion; +import amidst.filter.criterion.MatchAnyCriterion; + +@GsonObject +public class CriterionJsonOr extends CriterionJson { + @JsonField() + public List or; + + @JsonField(optional=true) + public int minScore = Integer.MIN_VALUE; + + @JsonField(optional=true) + public int min = 1; + + public CriterionJsonOr() {} + + @Override + protected Optional doValidate(CriterionJsonContext ctx) { + if(minScore != Integer.MIN_VALUE) + ctx.unsupportedAttribute("minScore"); + + if(min <= 0) + ctx.error("the min attribute must be strictly positive"); + + if(min > or.size()) + ctx.error("the min attribute can't be greater than the number of children (" + or.size() + ")"); + + if(min != 1) + ctx.unsupportedAttribute("min"); + + if(ctx.hasErrors()) + return Optional.empty(); + + return validateList(or, ctx, "or") + .map(l -> new MatchAnyCriterion(l)); + } +} \ No newline at end of file diff --git a/src/main/java/amidst/filter/json/WorldFilterJson.java b/src/main/java/amidst/filter/json/WorldFilterJson.java new file mode 100644 index 000000000..46568c45b --- /dev/null +++ b/src/main/java/amidst/filter/json/WorldFilterJson.java @@ -0,0 +1,86 @@ +package amidst.filter.json; + +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import com.google.gson.JsonParseException; + +import amidst.documentation.GsonObject; +import amidst.documentation.JsonField; +import amidst.filter.Criterion; +import amidst.filter.WorldFilter; +import amidst.mojangapi.world.coordinates.Coordinates; +import amidst.parsing.json.GsonProvider; + +@GsonObject +public class WorldFilterJson { + + + @JsonField(optional=true) + public Defaults defaults = new Defaults(); + + @JsonField(optional=true) + public Map groups = Collections.emptyMap(); + + @JsonField(optional=true) + public CriterionJson match = null; + + @JsonField(optional=true) + public List ignore = Collections.emptyList(); + + @GsonObject + public static class Defaults { + @JsonField(optional=true) + public Coordinates center = null; + + @JsonField(optional=true) + public String shape = "circle"; + } + + public WorldFilterJson() { + } + + + public WorldFilter validate() throws WorldFilterParseException { + CriterionJsonContext ctx = new CriterionJsonContext(defaults, groups::get); + + Set ignoreSet = makeIgnoreSet(ctx.withName("")); + + Map criteria = new HashMap<>(); + + for(String name: groups.keySet()) { + if(!ignoreSet.contains(name)) + criteria.put(name, ctx.convertCriterion(name).orElse(null)); + } + + Criterion main = match.validate(ctx.withName("
")).orElse(null); + + if(!ctx.getErrors().isEmpty()) + throw new WorldFilterParseException(ctx.getErrors()); + + return new WorldFilter(defaults.center, criteria, main); + } + + private Set makeIgnoreSet(CriterionJsonContext ctx) { + for(String gname: ignore) { + if(!groups.containsKey(gname)) + ctx.error("the group " + gname + " doesn't exist"); + } + + return new HashSet<>(ignore); + } + + public static WorldFilterJson fromJSON(String json) throws WorldFilterParseException { + try { + return GsonProvider.getStrict().fromJson(json, WorldFilterJson.class); + } catch (JsonParseException e) { + throw new WorldFilterParseException(e); + } + } + + +} \ No newline at end of file diff --git a/src/main/java/amidst/filter/json/WorldFilterParseException.java b/src/main/java/amidst/filter/json/WorldFilterParseException.java new file mode 100644 index 000000000..008cc7c91 --- /dev/null +++ b/src/main/java/amidst/filter/json/WorldFilterParseException.java @@ -0,0 +1,34 @@ +package amidst.filter.json; + +import java.util.List; +import java.util.stream.Collectors; + +import com.google.gson.JsonParseException; + +import java.util.Collections; + +public class WorldFilterParseException extends Exception { + + private static final long serialVersionUID = 631895046392283086L; + + private List errors; + + public WorldFilterParseException(JsonParseException cause) { + super(cause); + errors = Collections.emptyList(); + } + + public WorldFilterParseException(List errors) { + super("Failed to parse json with " + errors.size() + " error(s):"); + this.errors = errors; + } + + public List getErrorList() { + return errors; + } + + @Override + public String getMessage() { + return super.getMessage() + "\n" + errors.stream().collect(Collectors.joining("\n")); + } +} diff --git a/src/main/java/amidst/gui/main/PerMainWindowInjector.java b/src/main/java/amidst/gui/main/PerMainWindowInjector.java index c042b5455..b9c4fd2b4 100644 --- a/src/main/java/amidst/gui/main/PerMainWindowInjector.java +++ b/src/main/java/amidst/gui/main/PerMainWindowInjector.java @@ -79,16 +79,18 @@ public PerMainWindowInjector( viewerFacadeReference, dialogs, this::getMenuBar); + if (FeatureToggles.SEED_SEARCH) { this.seedSearcher = new SeedSearcher( dialogs, runningLauncherProfile.createSilentPlayerlessCopy(), threadMaster.getWorkerExecutor()); - this.seedSearcherWindow = new SeedSearcherWindow(metadata, dialogs, worldSwitcher, seedSearcher); + this.seedSearcherWindow = new SeedSearcherWindow(metadata, settings, dialogs, worldSwitcher, seedSearcher); } else { this.seedSearcher = null; this.seedSearcherWindow = null; } + this.actions = new Actions( application, dialogs, diff --git a/src/main/java/amidst/gui/seedsearcher/SeedSearcherConfiguration.java b/src/main/java/amidst/gui/seedsearcher/SeedSearcherConfiguration.java index b3df85085..bf324cb63 100644 --- a/src/main/java/amidst/gui/seedsearcher/SeedSearcherConfiguration.java +++ b/src/main/java/amidst/gui/seedsearcher/SeedSearcherConfiguration.java @@ -1,8 +1,8 @@ package amidst.gui.seedsearcher; import amidst.documentation.Immutable; +import amidst.filter.WorldFilter; import amidst.mojangapi.world.WorldType; -import amidst.mojangapi.world.filter.WorldFilter; @Immutable public class SeedSearcherConfiguration { diff --git a/src/main/java/amidst/gui/seedsearcher/SeedSearcherWindow.java b/src/main/java/amidst/gui/seedsearcher/SeedSearcherWindow.java index 5fee2d3ac..2e7e0fab5 100644 --- a/src/main/java/amidst/gui/seedsearcher/SeedSearcherWindow.java +++ b/src/main/java/amidst/gui/seedsearcher/SeedSearcherWindow.java @@ -1,7 +1,13 @@ package amidst.gui.seedsearcher; import java.awt.Color; -import java.util.Optional; +import java.awt.Font; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; +import java.util.stream.Collectors; import javax.swing.JButton; import javax.swing.JCheckBox; @@ -14,23 +20,30 @@ import javax.swing.border.LineBorder; import amidst.AmidstMetaData; +import amidst.AmidstSettings; import amidst.documentation.AmidstThread; import amidst.documentation.CalledOnlyBy; import amidst.documentation.NotThreadSafe; +import amidst.filter.WorldFilter; +import amidst.filter.json.WorldFilterJson; +import amidst.filter.json.WorldFilterParseException; import amidst.gui.main.MainWindowDialogs; import amidst.gui.main.WorldSwitcher; import amidst.logging.AmidstLogger; -import amidst.mojangapi.file.json.filter.WorldFilterJson_MatchAll; +import amidst.logging.AmidstMessageBox; import amidst.mojangapi.world.WorldOptions; import amidst.mojangapi.world.WorldType; -import amidst.mojangapi.world.filter.WorldFilter; import net.miginfocom.swing.MigLayout; @NotThreadSafe public class SeedSearcherWindow { + private final AmidstMetaData metadata; + private final AmidstSettings settings; + private final MainWindowDialogs dialogs; private final WorldSwitcher worldSwitcher; + private final SeedSearcher seedSearcher; private final JTextArea searchQueryTextArea; @@ -42,10 +55,12 @@ public class SeedSearcherWindow { @CalledOnlyBy(AmidstThread.EDT) public SeedSearcherWindow( AmidstMetaData metadata, + AmidstSettings settings, MainWindowDialogs dialogs, WorldSwitcher worldSwitcher, SeedSearcher seedSearcher) { this.metadata = metadata; + this.settings = settings; this.dialogs = dialogs; this.worldSwitcher = worldSwitcher; this.seedSearcher = seedSearcher; @@ -54,6 +69,8 @@ public SeedSearcherWindow( this.searchContinuouslyCheckBox = createSearchContinuouslyCheckBox(); this.searchButton = createSearchButton(); this.frame = createFrame(); + + tryReloadFromFile(); } @CalledOnlyBy(AmidstThread.EDT) @@ -64,7 +81,11 @@ public void show() { @CalledOnlyBy(AmidstThread.EDT) private JTextArea createSearchQueryTextArea() { - return new JTextArea(); + JTextArea area = new JTextArea(); + + area.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 12)); + area.setTabSize(2); + return area; } @CalledOnlyBy(AmidstThread.EDT) @@ -107,30 +128,48 @@ private JScrollPane createScrollPane(JTextArea textArea) { result.setBorder(new LineBorder(Color.darkGray, 1)); return result; } + + @CalledOnlyBy(AmidstThread.EDT) + private void tryReloadFromFile() { + Path file = Paths.get(settings.searchJsonFile.get()); + + if(Files.notExists(file)) { + AmidstLogger.info("The search file " + file + "doesn't exist: abort loading"); + return; + } + + try { + List lines = Files.readAllLines(file); + String content = lines.stream().collect(Collectors.joining("\n")); + searchQueryTextArea.setText(content); + + } catch (IOException e) { + AmidstLogger.warn("Unable to read search file " + file + ": " + e.getMessage()); + dialogs.displayError("Could not load file; see logs."); + } + } @CalledOnlyBy(AmidstThread.EDT) private void searchButtonClicked() { if (seedSearcher.isSearching()) { seedSearcher.stop(); } else { - Optional configuration = createSeedSearcherConfiguration(); - if (configuration.isPresent()) { - SeedSearcherConfiguration seedSearcherConfiguration = configuration.get(); - seedSearcher.search(seedSearcherConfiguration, world -> seedFound(world)); - } else { - AmidstLogger.warn("invalid configuration"); - dialogs.displayError("invalid configuration"); + try { + SeedSearcherConfiguration seedSearcherConfiguration = createSeedSearcherConfiguration(); + seedSearcher.search(seedSearcherConfiguration, world -> worldFound(world)); + + } catch (WorldFilterParseException e) { + AmidstLogger.warn("invalid configuration: {}", e.getMessage()); + AmidstMessageBox.displayError("Invalid configuration", e.getMessage()); } } updateGUI(); } @CalledOnlyBy(AmidstThread.EDT) - private Optional createSeedSearcherConfiguration() { - return WorldFilterJson_MatchAll - .from(searchQueryTextArea.getText()) - .flatMap(WorldFilterJson_MatchAll::createValidWorldFilter) - .map(this::createSeedSearcherConfiguration); + private SeedSearcherConfiguration createSeedSearcherConfiguration() throws WorldFilterParseException { + WorldFilterJson filter = WorldFilterJson.fromJSON(searchQueryTextArea.getText()); + return createSeedSearcherConfiguration(filter.validate()); } @CalledOnlyBy(AmidstThread.EDT) @@ -142,8 +181,10 @@ private SeedSearcherConfiguration createSeedSearcherConfiguration(WorldFilter wo } @CalledOnlyBy(AmidstThread.EDT) - private void seedFound(WorldOptions worldOptions) { - worldSwitcher.displayWorld(worldOptions); + private void worldFound(WorldOptions world) { + //TODO better reporting + AmidstLogger.info("Found world!"); + worldSwitcher.displayWorld(world); updateGUI(); } diff --git a/src/main/java/amidst/mojangapi/file/json/filter/WorldFilterJson_Biome.java b/src/main/java/amidst/mojangapi/file/json/filter/WorldFilterJson_Biome.java deleted file mode 100644 index d537dba3b..000000000 --- a/src/main/java/amidst/mojangapi/file/json/filter/WorldFilterJson_Biome.java +++ /dev/null @@ -1,47 +0,0 @@ -package amidst.mojangapi.file.json.filter; - -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import amidst.documentation.GsonObject; -import amidst.documentation.Immutable; -import amidst.mojangapi.world.biome.Biome; -import amidst.mojangapi.world.filter.WorldFilter_Biome; - -@Immutable -@GsonObject -public class WorldFilterJson_Biome { - @SuppressWarnings("unused") - private volatile long distance; - private volatile List biomes = Collections.emptyList(); - - public WorldFilterJson_Biome() { - } - - public void validate(List notifications) { - if (biomes.isEmpty()) { - notifications.add("No biomes for filter"); - } else { - for (String name : biomes) { - if (!Biome.exists(name)) { - notifications.add("invalid biome name: '" + name + "'"); - } - } - } - } - - public WorldFilter_Biome createBiomeFilter() { - return null; - } - - @SuppressWarnings("unused") - private Set createValidBiomeIndexes() { - Set result = new HashSet<>(); - for (String name : biomes) { - result.add((short) Biome.getByName(name).getIndex()); - } - return result; - } -} diff --git a/src/main/java/amidst/mojangapi/file/json/filter/WorldFilterJson_MatchAll.java b/src/main/java/amidst/mojangapi/file/json/filter/WorldFilterJson_MatchAll.java deleted file mode 100644 index 341c633b4..000000000 --- a/src/main/java/amidst/mojangapi/file/json/filter/WorldFilterJson_MatchAll.java +++ /dev/null @@ -1,72 +0,0 @@ -package amidst.mojangapi.file.json.filter; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; -import java.util.Optional; - -import amidst.documentation.GsonObject; -import amidst.documentation.Immutable; -import amidst.logging.AmidstLogger; -import amidst.mojangapi.world.filter.WorldFilter; -import amidst.mojangapi.world.filter.WorldFilter_MatchAll; -import amidst.parsing.FormatException; -import amidst.parsing.json.JsonReader; - -@Immutable -@GsonObject -public class WorldFilterJson_MatchAll { - public static Optional from(String queryString) { - try { - return Optional.of(JsonReader.readString(queryString, WorldFilterJson_MatchAll.class)); - } catch (FormatException e) { - return Optional.empty(); - } - } - - private volatile List biomeFilters = Collections.emptyList(); - private volatile List structureFilters = Collections.emptyList(); - - public WorldFilterJson_MatchAll() { - } - - public List getValidationMessages() { - List result = new LinkedList<>(); - validate(result); - return result; - } - - private void validate(List notifications) { - biomeFilters.forEach(f -> f.validate(notifications)); - structureFilters.forEach(f -> f.validate(notifications)); - } - - public WorldFilter createWorldFilter() { - // TODO: the size is 0, because this filter will never use its biome - // data - return new WorldFilter_MatchAll(0, createFilterList()); - } - - public Optional createValidWorldFilter() { - if (getValidationMessages().isEmpty()) { - return Optional.of(createWorldFilter()); - } else { - // TODO: use error messages - AmidstLogger.debug(getValidationMessages().toString()); - return Optional.empty(); - } - } - - private List createFilterList() { - List filters = new ArrayList<>(); - for (WorldFilterJson_Biome biomeFilterJson : biomeFilters) { - filters.add(biomeFilterJson.createBiomeFilter()); - } - - for (WorldFilterJson_Structure structureFilterJson : structureFilters) { - filters.add(structureFilterJson.createStructureFilter()); - } - return filters; - } -} diff --git a/src/main/java/amidst/mojangapi/file/json/filter/WorldFilterJson_Structure.java b/src/main/java/amidst/mojangapi/file/json/filter/WorldFilterJson_Structure.java deleted file mode 100644 index 8848feffa..000000000 --- a/src/main/java/amidst/mojangapi/file/json/filter/WorldFilterJson_Structure.java +++ /dev/null @@ -1,33 +0,0 @@ -package amidst.mojangapi.file.json.filter; - -import java.util.List; - -import amidst.documentation.GsonObject; -import amidst.documentation.Immutable; -import amidst.mojangapi.world.filter.WorldFilter_Structure; -import amidst.mojangapi.world.icon.type.DefaultWorldIconTypes; - -@Immutable -@GsonObject -public class WorldFilterJson_Structure { - @SuppressWarnings("unused") - private volatile long distance; - private volatile String structure; - private volatile int minimum; - - public WorldFilterJson_Structure() { - } - - public void validate(List notifications) { - if (!DefaultWorldIconTypes.exists(structure)) { - notifications.add("invalid structure: '" + structure + "'"); - } - if (minimum <= 0) { - notifications.add("invalid minimum: " + minimum); - } - } - - public WorldFilter_Structure createStructureFilter() { - return null; - } -} diff --git a/src/main/java/amidst/mojangapi/world/filter/WorldFilter.java b/src/main/java/amidst/mojangapi/world/filter/WorldFilter.java deleted file mode 100644 index 86b8ba47f..000000000 --- a/src/main/java/amidst/mojangapi/world/filter/WorldFilter.java +++ /dev/null @@ -1,34 +0,0 @@ -package amidst.mojangapi.world.filter; - -import amidst.documentation.Immutable; -import amidst.fragment.Fragment; -import amidst.mojangapi.world.World; -import amidst.mojangapi.world.coordinates.Coordinates; -import amidst.mojangapi.world.coordinates.Resolution; - -@Immutable -public abstract class WorldFilter { - protected final int worldFilterSize; - protected final int quarterFilterSize; - protected final Coordinates corner; - - public WorldFilter(int worldFilterSize) { - ensureIsMultipleOfFragmentSize(worldFilterSize); - this.worldFilterSize = worldFilterSize; - this.quarterFilterSize = Resolution.QUARTER.convertFromWorldToThis(this.worldFilterSize); - this.corner = new Coordinates(-this.worldFilterSize, -this.worldFilterSize); - } - - /** - * Structure filters check spaces in fragment size, so filter distance not a - * multiple of fragment size will include more area in the filter than - * expected - */ - private void ensureIsMultipleOfFragmentSize(long worldFilterSize) { - if (worldFilterSize % Fragment.SIZE != 0) { - throw new IllegalArgumentException("World filter size must be a multiple of " + Fragment.SIZE); - } - } - - public abstract boolean isValid(World world); -} diff --git a/src/main/java/amidst/mojangapi/world/filter/WorldFilter_Biome.java b/src/main/java/amidst/mojangapi/world/filter/WorldFilter_Biome.java deleted file mode 100644 index 85705cf8b..000000000 --- a/src/main/java/amidst/mojangapi/world/filter/WorldFilter_Biome.java +++ /dev/null @@ -1,33 +0,0 @@ -package amidst.mojangapi.world.filter; - -import java.util.Set; - -import amidst.documentation.NotThreadSafe; -import amidst.logging.AmidstLogger; -import amidst.mojangapi.minecraftinterface.MinecraftInterfaceException; -import amidst.mojangapi.world.World; -import amidst.mojangapi.world.biome.BiomeData; -import amidst.mojangapi.world.coordinates.Coordinates; -import amidst.mojangapi.world.coordinates.Region; - -@NotThreadSafe -public class WorldFilter_Biome extends WorldFilter { - private final Set validBiomeIndexes; - - public WorldFilter_Biome(int worldFilterSize, Set validBiomeIndexes) { - super(worldFilterSize); - this.validBiomeIndexes = validBiomeIndexes; - } - - @Override - public boolean isValid(World world) { - try { - BiomeData data = world.getBiomeDataOracle().getBiomeData(Region.box(Coordinates.origin(), this.quarterFilterSize), true); - return data.checkAll((x, y, b) -> validBiomeIndexes.contains(b)); - - } catch (MinecraftInterfaceException e) { - AmidstLogger.error(e); - return false; - } - } -} diff --git a/src/main/java/amidst/mojangapi/world/filter/WorldFilter_MatchAll.java b/src/main/java/amidst/mojangapi/world/filter/WorldFilter_MatchAll.java deleted file mode 100644 index 266e1c60e..000000000 --- a/src/main/java/amidst/mojangapi/world/filter/WorldFilter_MatchAll.java +++ /dev/null @@ -1,26 +0,0 @@ -package amidst.mojangapi.world.filter; - -import java.util.List; - -import amidst.documentation.Immutable; -import amidst.mojangapi.world.World; - -@Immutable -public class WorldFilter_MatchAll extends WorldFilter { - private final List filters; - - public WorldFilter_MatchAll(int worldFilterSize, List filters) { - super(worldFilterSize); - this.filters = filters; - } - - @Override - public boolean isValid(World world) { - for (WorldFilter filter : filters) { - if (!filter.isValid(world)) { - return false; - } - } - return true; - } -} diff --git a/src/main/java/amidst/mojangapi/world/filter/WorldFilter_Structure.java b/src/main/java/amidst/mojangapi/world/filter/WorldFilter_Structure.java deleted file mode 100644 index 11f581579..000000000 --- a/src/main/java/amidst/mojangapi/world/filter/WorldFilter_Structure.java +++ /dev/null @@ -1,74 +0,0 @@ -package amidst.mojangapi.world.filter; - -import amidst.documentation.Immutable; -import amidst.mojangapi.world.World; -import amidst.mojangapi.world.coordinates.Coordinates; -import amidst.mojangapi.world.coordinates.Region; -import amidst.mojangapi.world.icon.producer.NameFilteredWorldIconCollector; -import amidst.mojangapi.world.icon.producer.WorldIconCollector; -import amidst.mojangapi.world.icon.producer.WorldIconProducer; -import amidst.mojangapi.world.icon.type.DefaultWorldIconTypes; - -@Immutable -public class WorldFilter_Structure extends WorldFilter { - private final DefaultWorldIconTypes structure; - private final int count; - - public WorldFilter_Structure(int worldFilterSize, DefaultWorldIconTypes structure, int count) { - super(worldFilterSize); - this.structure = structure; - this.count = count; - } - - @Override - public boolean isValid(World world) { - WorldIconCollector structureCollector = getCollector(); - procudeAndCollect(getProducer(world), structureCollector); - return structureCollector.get().size() > count; - } - - private void procudeAndCollect(WorldIconProducer structureProducer, WorldIconCollector structureCollector) { - for (int x = 0; x < 2 * worldFilterSize; x += 512) { - for (int y = 0; y < 2 * worldFilterSize; y += 512) { - structureProducer.produce(Region.box(Coordinates.from(x, y).add(corner), 512, 512), structureCollector, null); - } - } - } - - private WorldIconProducer getProducer(World world) { - switch (structure) { - case JUNGLE: - case DESERT: - case IGLOO: - case WITCH: - return world.getTempleProducer(); - case STRONGHOLD: - return world.getStrongholdProducer(); - case VILLAGE: - return world.getVillageProducer(); - case OCEAN_MONUMENT: - return world.getOceanMonumentProducer(); - case MINESHAFT: - return world.getMineshaftProducer(); - default: - throw new IllegalArgumentException("Unsupported structure type: " + structure.getName()); - } - } - - private WorldIconCollector getCollector() { - switch (structure) { - case JUNGLE: - case DESERT: - case IGLOO: - case WITCH: - return new NameFilteredWorldIconCollector(structure.getName()); - case STRONGHOLD: - case VILLAGE: - case OCEAN_MONUMENT: - case MINESHAFT: - return new WorldIconCollector(); - default: - throw new IllegalArgumentException("Unsupported structure type: " + structure.getName()); - } - } -} diff --git a/src/main/java/amidst/parsing/json/GsonProvider.java b/src/main/java/amidst/parsing/json/GsonProvider.java index 346b9ddb5..5c092e20d 100644 --- a/src/main/java/amidst/parsing/json/GsonProvider.java +++ b/src/main/java/amidst/parsing/json/GsonProvider.java @@ -3,6 +3,10 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; +import amidst.filter.json.CriterionDeserializer; +import amidst.filter.json.CriterionJson; +import amidst.filter.json.CriterionJsonBase; + public enum GsonProvider { ; @@ -21,7 +25,13 @@ public static Gson getStrict() { private static GsonBuilder createBuilder(GsonBuilder base) { // @formatter:off return base - .enableComplexMapKeySerialization(); + .enableComplexMapKeySerialization() + .registerTypeAdapter( + CriterionJson.class, + new CriterionDeserializer()) + .registerTypeAdapter( + CriterionJsonBase.ClusterInfo.class, + new CriterionJsonBase.ClusterInfoDeserializer()); // @formatter:on } From af908ee320ee0e8b9ded75f3980656ad3c3601ea Mon Sep 17 00:00:00 2001 From: moulins <> Date: Fri, 23 Jun 2017 16:00:16 +0200 Subject: [PATCH 11/13] Add filter implementation and results reporting --- .../java/amidst/filter/BiomeConstraint.java | 36 +++++++ src/main/java/amidst/filter/Constraint.java | 9 ++ src/main/java/amidst/filter/Criterion.java | 21 ++++- .../java/amidst/filter/CriterionResult.java | 19 ++++ src/main/java/amidst/filter/ResultsMap.java | 36 +++++++ .../amidst/filter/StructureConstraint.java | 84 ++++++++++++++++- src/main/java/amidst/filter/WorldFilter.java | 76 +++++++++++++-- .../java/amidst/filter/WorldFilterResult.java | 75 +++++++++++++++ .../filter/criterion/MatchAllCriterion.java | 81 +++++++++++++++- .../filter/criterion/MatchAnyCriterion.java | 82 +++++++++++++++- .../filter/criterion/NegateCriterion.java | 48 +++++++++- .../filter/criterion/SimpleCriterion.java | 93 ++++++++++++++++++- .../amidst/filter/json/CriterionJson.java | 12 +-- .../amidst/filter/json/CriterionJsonAnd.java | 2 +- .../amidst/filter/json/CriterionJsonBase.java | 6 +- .../filter/json/CriterionJsonContext.java | 8 +- .../filter/json/CriterionJsonGroup.java | 2 +- .../amidst/filter/json/CriterionJsonOr.java | 2 +- .../amidst/filter/json/WorldFilterJson.java | 4 +- .../amidst/gui/seedsearcher/SeedSearcher.java | 17 ++-- .../gui/seedsearcher/SeedSearcherWindow.java | 8 +- src/main/java/amidst/util/TriState.java | 31 +++++++ 22 files changed, 701 insertions(+), 51 deletions(-) create mode 100644 src/main/java/amidst/filter/CriterionResult.java create mode 100644 src/main/java/amidst/filter/ResultsMap.java create mode 100644 src/main/java/amidst/filter/WorldFilterResult.java create mode 100644 src/main/java/amidst/util/TriState.java diff --git a/src/main/java/amidst/filter/BiomeConstraint.java b/src/main/java/amidst/filter/BiomeConstraint.java index 3d43269ac..a73a1411b 100644 --- a/src/main/java/amidst/filter/BiomeConstraint.java +++ b/src/main/java/amidst/filter/BiomeConstraint.java @@ -1,8 +1,16 @@ package amidst.filter; +import java.util.Optional; + import amidst.documentation.Immutable; +import amidst.logging.AmidstLogger; +import amidst.mojangapi.minecraftinterface.MinecraftInterfaceException; +import amidst.mojangapi.world.World; import amidst.mojangapi.world.biome.Biome; +import amidst.mojangapi.world.biome.BiomeData; +import amidst.mojangapi.world.coordinates.Coordinates; import amidst.mojangapi.world.coordinates.Region; +import amidst.mojangapi.world.coordinates.Resolution; @Immutable @@ -23,6 +31,34 @@ public Region getRegion() { return region; } + @Override + public Optional checkRegion(World world, Region.Box region) { + try { + BiomeData data = world.getBiomeDataOracle().getBiomeData(region, true); + + return Optional.ofNullable(data.findFirst((x, y, b) -> { + if(b != biome.getIndex()) + return null; + + Coordinates pos = region.getCorner().add(Coordinates.from(x, y, Resolution.QUARTER)); + if(checkDistance && !region.contains(pos)) + return null; + return pos; + })); + } catch (MinecraftInterfaceException e) { + AmidstLogger.error(e); + return Optional.empty(); + } + } + + @Override + public void addMarkers(WorldFilterResult.ResultItem item) { + if(item.biome != null && item.biome != biome) + throw new IllegalArgumentException("biome is already set!"); + + item.biome = biome; + } + @Override public boolean equals(Object other) { if(!(other instanceof BiomeConstraint)) diff --git a/src/main/java/amidst/filter/Constraint.java b/src/main/java/amidst/filter/Constraint.java index 058d48eba..0c895a952 100644 --- a/src/main/java/amidst/filter/Constraint.java +++ b/src/main/java/amidst/filter/Constraint.java @@ -1,6 +1,11 @@ package amidst.filter; +import java.util.Optional; + import amidst.documentation.Immutable; +import amidst.filter.WorldFilterResult.ResultItem; +import amidst.mojangapi.world.World; +import amidst.mojangapi.world.coordinates.Coordinates; import amidst.mojangapi.world.coordinates.Region; @Immutable @@ -8,6 +13,10 @@ public interface Constraint { public Region getRegion(); + public Optional checkRegion(World world, Region.Box region); + + public void addMarkers(ResultItem item); + public boolean equals(Object other); public int hashCode(); diff --git a/src/main/java/amidst/filter/Criterion.java b/src/main/java/amidst/filter/Criterion.java index a366dd2c2..8b02c8cc2 100644 --- a/src/main/java/amidst/filter/Criterion.java +++ b/src/main/java/amidst/filter/Criterion.java @@ -1,10 +1,27 @@ package amidst.filter; import java.util.List; +import java.util.Objects; import amidst.documentation.Immutable; +import amidst.mojangapi.world.World; +import amidst.mojangapi.world.coordinates.Coordinates; +import amidst.mojangapi.world.coordinates.Region; +import amidst.util.TriState; @Immutable -public interface Criterion { - public List getChildren(); +public interface Criterion { + + public R createResult(); + + public Region.Box getNextRegionToCheck(ResultsMap map); + + public default TriState checkRegion(ResultsMap map, World world, Coordinates offset, Region.Box region) { + Objects.requireNonNull(region); + R res = map.get(this); + res.checkRegionAndUpdate(map, world, offset, region); + return res.hasMatched(); + } + + public List> getChildren(); } diff --git a/src/main/java/amidst/filter/CriterionResult.java b/src/main/java/amidst/filter/CriterionResult.java new file mode 100644 index 000000000..350d53f1d --- /dev/null +++ b/src/main/java/amidst/filter/CriterionResult.java @@ -0,0 +1,19 @@ +package amidst.filter; + +import amidst.mojangapi.world.World; +import amidst.mojangapi.world.coordinates.Coordinates; +import amidst.mojangapi.world.coordinates.Region; +import amidst.util.TriState; + +public interface CriterionResult { + public TriState hasMatched(); + + public void checkRegionAndUpdate(ResultsMap map, World world, Coordinates offset, Region.Box region); + + public default void addItemToWorldResult(WorldFilterResult result) { + //Do nothing + } + + public CriterionResult copy(); + +} diff --git a/src/main/java/amidst/filter/ResultsMap.java b/src/main/java/amidst/filter/ResultsMap.java new file mode 100644 index 000000000..4065fb2f3 --- /dev/null +++ b/src/main/java/amidst/filter/ResultsMap.java @@ -0,0 +1,36 @@ +package amidst.filter; + +import java.util.HashMap; +import java.util.Map; + +public class ResultsMap { + private Map, CriterionResult> results; + + public ResultsMap() { + results = new HashMap<>(); + } + + public boolean create(Criterion criterion) { + if(results.containsKey(criterion)) + return false; + results.put(criterion, criterion.createResult()); + return true; + } + + @SuppressWarnings("unchecked") + public T get(Criterion criterion) { + return (T) results.get(criterion); + } + + @SuppressWarnings("unchecked") + public T remove(Criterion criterion) { + return (T) results.remove(criterion); + } + + public ResultsMap copy() { + ResultsMap copy = new ResultsMap(); + copy.results.putAll(results); + copy.results.replaceAll((cr, res) -> res.copy()); + return copy; + } +} \ No newline at end of file diff --git a/src/main/java/amidst/filter/StructureConstraint.java b/src/main/java/amidst/filter/StructureConstraint.java index c8db1c561..101982a98 100644 --- a/src/main/java/amidst/filter/StructureConstraint.java +++ b/src/main/java/amidst/filter/StructureConstraint.java @@ -18,9 +18,20 @@ import amidst.mojangapi.world.icon.type.DefaultWorldIconTypes; +//TODO more efficient, maybe with a cache ? @Immutable public class StructureConstraint implements Constraint { - + + // @formatter:off + public static final Set UNSUPPORTED_STRUCTURES = + Collections.unmodifiableSet(new HashSet<>(Arrays.asList(new DefaultWorldIconTypes[]{ + DefaultWorldIconTypes.PLAYER, + DefaultWorldIconTypes.SPAWN, + DefaultWorldIconTypes.END_CITY, + DefaultWorldIconTypes.POSSIBLE_END_CITY + }))); + // @formatter:on + private final Region region; private final DefaultWorldIconTypes structure; private final Biome biome; @@ -37,7 +48,78 @@ public StructureConstraint(Region region, DefaultWorldIconTypes structure, Biome public Region getRegion() { return region; } + + @Override + public Optional checkRegion(World world, Region.Box region) { + WorldIconProducer producer = getStructureProducer(world, structure); + + for(WorldIcon icon: producer.getAt(region, null)) { + if(!icon.getName().equals(structure.getLabel())) + continue; + + Coordinates pos = icon.getCoordinates(); + if(checkDistance && !region.contains(pos)) + continue; + + if(biome == null) + return Optional.of(pos); + + try { + short bid = world.getBiomeDataOracle().getBiomeAt(pos.getX(), pos.getY()); + if(bid == biome.getIndex()) + return Optional.of(pos); + } catch (MinecraftInterfaceException e) { + AmidstLogger.error(e); + } + } + + return Optional.empty(); + } + + @Override + public void addMarkers(WorldFilterResult.ResultItem item) { + if(item.biome != null) { + if(item.biome != biome) + throw new IllegalArgumentException("biome is already set!"); + item.biome = biome; + } + else item.icons.add(structure); + } + + private static WorldIconProducer getStructureProducer(World world, DefaultWorldIconTypes struct) { + switch(struct) { + case MINESHAFT: + return world.getMineshaftProducer(); + + case NETHER_FORTRESS: + return world.getNetherFortressProducer(); + + case OCEAN_MONUMENT: + return world.getOceanMonumentProducer(); + + case STRONGHOLD: + return world.getStrongholdProducer(); + + case VILLAGE: + return world.getVillageProducer(); + + case DESERT: + case IGLOO: + case JUNGLE: + case WITCH: + return world.getTempleProducer(); + + case END_CITY: + case PLAYER: + case POSSIBLE_END_CITY: + case SPAWN: + break; + } + + throw new IllegalArgumentException("unsupported structure " + struct.getName()); + } + @Override public boolean equals(Object other) { if(!(other instanceof StructureConstraint)) diff --git a/src/main/java/amidst/filter/WorldFilter.java b/src/main/java/amidst/filter/WorldFilter.java index 514680581..04b345f61 100644 --- a/src/main/java/amidst/filter/WorldFilter.java +++ b/src/main/java/amidst/filter/WorldFilter.java @@ -1,10 +1,14 @@ package amidst.filter; import java.util.Map; +import java.util.Optional; import amidst.mojangapi.world.World; import amidst.mojangapi.world.coordinates.Coordinates; +import amidst.mojangapi.world.coordinates.Region; import amidst.mojangapi.world.coordinates.Resolution; +import amidst.mojangapi.world.oracle.CachedBiomeDataOracle; +import amidst.util.TriState; public class WorldFilter { @@ -13,13 +17,27 @@ public class WorldFilter { //if null, use world spawn point private final Coordinates globalCenter; - private final Map criteria; - private final Criterion match; + private final Map> criteria; + private final Criterion match; - public WorldFilter(Coordinates center, Map criteria, Criterion match) { + private final ResultsMap results = new ResultsMap(); + + public WorldFilter(Coordinates center, Map> criteria, Criterion match) { globalCenter = center; this.criteria = criteria; this.match = match; + + fillResultsMap(match); + for(Criterion c: criteria.values()) + fillResultsMap(c); + } + + private void fillResultsMap(Criterion criterion) { + if(!results.create(criterion)) + return; + + for(Criterion c: criterion.getChildren()) + fillResultsMap(c); } private Coordinates getGlobalCenter(World world) { @@ -28,9 +46,55 @@ private Coordinates getGlobalCenter(World world) { Coordinates center = world.getSpawnOracle().get(); return center == null ? Coordinates.origin() : center; } + + private void addItemsOfMatchedCriteria(WorldFilterResult result, ResultsMap map, Criterion criterion) { + CriterionResult r = map.remove(criterion); + if(r == null || r.hasMatched() != TriState.TRUE) + return; + + r.addItemToWorldResult(result); + + for(Criterion c: criterion.getChildren()) + addItemsOfMatchedCriteria(result, map, c); + + } + + private boolean isValid(ResultsMap map, World world, Coordinates offset, Criterion criterion) { + CriterionResult res = map.get(criterion); + + Region.Box region = criterion.getNextRegionToCheck(map); + + if(region == null) + return true; + + world = world.cached(region); + CachedBiomeDataOracle oracle = (CachedBiomeDataOracle) world.getBiomeDataOracle(); + + while(criterion.checkRegion(map, world, offset, region) == TriState.UNKNOWN) { + region = criterion.getNextRegionToCheck(map); + oracle.moveCacheTo(region); + } + + return res.hasMatched() == TriState.TRUE; + } + + public Optional match(World world) { + ResultsMap results = this.results.copy(); + + Coordinates offset = getGlobalCenter(world); + + if(!isValid(results, world, offset, match)) + return Optional.empty(); + + WorldFilterResult result = new WorldFilterResult(world); - public boolean isValid(World world) { - //TODO - return true; + for(Map.Entry> e: criteria.entrySet()) { + if(isValid(results, world, offset, e.getValue())) { + result.addOptionalGoal(e.getKey()); + addItemsOfMatchedCriteria(result, results, e.getValue()); + } + } + + return Optional.of(result); } } diff --git a/src/main/java/amidst/filter/WorldFilterResult.java b/src/main/java/amidst/filter/WorldFilterResult.java new file mode 100644 index 000000000..4a75f9364 --- /dev/null +++ b/src/main/java/amidst/filter/WorldFilterResult.java @@ -0,0 +1,75 @@ +package amidst.filter; + +import java.util.EnumSet; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +import amidst.mojangapi.world.World; +import amidst.mojangapi.world.WorldOptions; +import amidst.mojangapi.world.biome.Biome; +import amidst.mojangapi.world.coordinates.Coordinates; +import amidst.mojangapi.world.icon.type.DefaultWorldIconTypes; + +public class WorldFilterResult { + + public static class ResultItem { + public Biome biome; + public final EnumSet icons = EnumSet.noneOf(DefaultWorldIconTypes.class); + } + + private Set optionalGoals = new HashSet<>(); + private Map items = new HashMap<>(); + + private WorldOptions worldOptions; + + public WorldFilterResult(WorldOptions worldOptions) { + this.worldOptions = worldOptions; + } + + public WorldFilterResult(World world) { + this(world.getWorldOptions()); + } + + public WorldOptions getWorldOptions() { + return worldOptions; + } + + public void addOptionalGoal(String name) { + optionalGoals.add(name); + } + + public ResultItem getItemFor(Coordinates coordinates) { + return items.computeIfAbsent(coordinates, c -> new ResultItem()); + } + + @Override + public String toString() { + StringBuffer str = new StringBuffer("match for ("); + str.append(worldOptions.getWorldType()); + str.append(", "); + str.append(worldOptions.getWorldSeed().getLong()); + str.append(") {\n"); + str.append(" goals: "); + str.append(optionalGoals.stream().map(g -> g.toString()).collect(Collectors.joining(", "))); + for(Map.Entry e: items.entrySet()) { + str.append("\n at "); + str.append(e.getKey()); + str.append(": "); + ResultItem item = e.getValue(); + if(item.biome != null) { + str.append(item.biome.getName()); + if(!item.icons.isEmpty()) + str.append("; "); + + } + str.append(item.icons.stream().map(i -> i.toString()).collect(Collectors.joining(", "))); + } + str.append("\n}"); + + return str.toString(); + } + +} diff --git a/src/main/java/amidst/filter/criterion/MatchAllCriterion.java b/src/main/java/amidst/filter/criterion/MatchAllCriterion.java index 7c27fb46f..6ef938edf 100644 --- a/src/main/java/amidst/filter/criterion/MatchAllCriterion.java +++ b/src/main/java/amidst/filter/criterion/MatchAllCriterion.java @@ -5,20 +5,93 @@ import amidst.documentation.Immutable; import amidst.filter.Criterion; +import amidst.filter.CriterionResult; +import amidst.filter.ResultsMap; +import amidst.mojangapi.world.World; +import amidst.mojangapi.world.coordinates.Coordinates; +import amidst.mojangapi.world.coordinates.Region; +import amidst.util.TriState; import java.util.Collections; +import java.util.Iterator; @Immutable -public class MatchAllCriterion implements Criterion { +public class MatchAllCriterion implements Criterion { - private final List criteria; + private final List> criteria; - public MatchAllCriterion(List list) { + public MatchAllCriterion(List> list) { criteria = Collections.unmodifiableList(new ArrayList<>(list)); } @Override - public List getChildren() { + public List> getChildren() { return criteria; } + + @Override + public Result createResult() { + return new Result(); + } + + @Override + public Region.Box getNextRegionToCheck(ResultsMap map) { + Result res = map.get(this); + if(res == null) + return null; + + for(Criterion c: res.undecided) { + Region.Box r = c.getNextRegionToCheck(map); + if(r != null) + return r; + } + + return null; + } + + public class Result implements CriterionResult { + private List> undecided; + private boolean isMatch; + + public Result() { + this.undecided = new ArrayList<>(criteria); + this.isMatch = true; + } + + public Result(Result r) { + this.undecided = new ArrayList<>(r.undecided); + this.isMatch = r.isMatch; + } + + @Override + public TriState hasMatched() { + if(!isMatch) + return TriState.FALSE; + if(undecided.isEmpty()) + return TriState.TRUE; + return TriState.UNKNOWN; + } + @Override + public void checkRegionAndUpdate(ResultsMap map, World world, Coordinates offset, Region.Box region) { + Iterator> iter = undecided.iterator(); + while(iter.hasNext()) { + TriState match = iter.next().checkRegion(map, world, offset, region); + + if(match != TriState.UNKNOWN) + iter.remove(); + + if(match == TriState.FALSE) { + isMatch = false; + undecided.clear(); + break; + } + } + } + + @Override + public CriterionResult copy() { + return new Result(this); + } + + } } diff --git a/src/main/java/amidst/filter/criterion/MatchAnyCriterion.java b/src/main/java/amidst/filter/criterion/MatchAnyCriterion.java index be2c04f2e..dd3b446c9 100644 --- a/src/main/java/amidst/filter/criterion/MatchAnyCriterion.java +++ b/src/main/java/amidst/filter/criterion/MatchAnyCriterion.java @@ -2,22 +2,96 @@ import java.util.ArrayList; import java.util.Collections; +import java.util.Iterator; import java.util.List; import amidst.documentation.Immutable; import amidst.filter.Criterion; +import amidst.filter.CriterionResult; +import amidst.filter.ResultsMap; +import amidst.mojangapi.world.World; +import amidst.mojangapi.world.coordinates.Coordinates; +import amidst.mojangapi.world.coordinates.Region; +import amidst.util.TriState; @Immutable -public class MatchAnyCriterion implements Criterion { +public class MatchAnyCriterion implements Criterion { - private final List criteria; + private final List> criteria; - public MatchAnyCriterion(List list) { + public MatchAnyCriterion(List> list) { criteria = Collections.unmodifiableList(new ArrayList<>(list)); } @Override - public List getChildren() { + public List> getChildren() { return criteria; } + + @Override + public Result createResult() { + return new Result(); + } + + @Override + public Region.Box getNextRegionToCheck(ResultsMap map) { + Result res = map.get(this); + if(res == null) + return null; + + for(Criterion c: res.undecided) { + Region.Box r = c.getNextRegionToCheck(map); + if(r != null) + return r; + } + + return null; + } + + public class Result implements CriterionResult { + private List> undecided; + private boolean isMatch; + + public Result() { + this.undecided = new ArrayList<>(criteria); + this.isMatch = false; + } + + public Result(Result r) { + this.undecided = new ArrayList<>(r.undecided); + this.isMatch = r.isMatch; + } + + @Override + public TriState hasMatched() { + if(isMatch) + return TriState.TRUE; + if(undecided.isEmpty()) + return TriState.FALSE; + return TriState.UNKNOWN; + } + + @Override + public void checkRegionAndUpdate(ResultsMap map, World world, Coordinates offset, Region.Box region) { + Iterator> iter = undecided.iterator(); + while(iter.hasNext()) { + TriState match = iter.next().checkRegion(map, world, offset, region); + + if(match != TriState.UNKNOWN) + iter.remove(); + + if(match == TriState.TRUE) { + isMatch = true; + undecided.clear(); + break; + } + } + } + + @Override + public CriterionResult copy() { + return new Result(this); + } + + } } diff --git a/src/main/java/amidst/filter/criterion/NegateCriterion.java b/src/main/java/amidst/filter/criterion/NegateCriterion.java index 7c7143066..a135165bc 100644 --- a/src/main/java/amidst/filter/criterion/NegateCriterion.java +++ b/src/main/java/amidst/filter/criterion/NegateCriterion.java @@ -5,18 +5,58 @@ import amidst.documentation.Immutable; import amidst.filter.Criterion; +import amidst.filter.CriterionResult; +import amidst.filter.ResultsMap; +import amidst.mojangapi.world.World; +import amidst.mojangapi.world.coordinates.Coordinates; +import amidst.mojangapi.world.coordinates.Region; +import amidst.util.TriState; @Immutable -public class NegateCriterion implements Criterion { +public class NegateCriterion implements Criterion { - private final Criterion criterion; + private final Criterion criterion; - public NegateCriterion(String name, Criterion c) { + public NegateCriterion(String name, Criterion c) { criterion = c; } @Override - public List getChildren() { + public List> getChildren() { return Collections.singletonList(criterion); } + + @Override + public Result createResult() { + return new Result(TriState.UNKNOWN); + } + + @Override + public Region.Box getNextRegionToCheck(ResultsMap map) { + return criterion.getNextRegionToCheck(map); + } + + public class Result implements CriterionResult { + TriState state; + + public Result(TriState state) { + this.state = state; + } + + @Override + public TriState hasMatched() { + return state; + } + + @Override + public void checkRegionAndUpdate(ResultsMap map, World world, Coordinates offset, Region.Box region) { + state = criterion.checkRegion(map, world, offset, region); + } + + @Override + public CriterionResult copy() { + return new Result(state); + } + } + } diff --git a/src/main/java/amidst/filter/criterion/SimpleCriterion.java b/src/main/java/amidst/filter/criterion/SimpleCriterion.java index 1bb8b70c8..a568f5146 100644 --- a/src/main/java/amidst/filter/criterion/SimpleCriterion.java +++ b/src/main/java/amidst/filter/criterion/SimpleCriterion.java @@ -1,14 +1,26 @@ package amidst.filter.criterion; import java.util.Collections; +import java.util.HashSet; import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.function.Consumer; import amidst.documentation.Immutable; import amidst.filter.Constraint; import amidst.filter.Criterion; +import amidst.filter.CriterionResult; +import amidst.filter.ResultsMap; +import amidst.filter.WorldFilter; +import amidst.filter.WorldFilterResult; +import amidst.mojangapi.world.World; +import amidst.mojangapi.world.coordinates.Coordinates; +import amidst.mojangapi.world.coordinates.Region; +import amidst.util.TriState; @Immutable -public class SimpleCriterion implements Criterion { +public class SimpleCriterion implements Criterion { private final Constraint constraint; @@ -17,7 +29,84 @@ public SimpleCriterion(Constraint c) { } @Override - public List getChildren() { + public List> getChildren() { return Collections.emptyList(); } + + @Override + public Result createResult() { + return new Result(); + } + + @Override + public Region.Box getNextRegionToCheck(ResultsMap map) { + Result res = map.get(this); + if(res == null) + return null; + if(res.found != null || res.regionsToTest.isEmpty()) + return null; + + return res.regionsToTest.iterator().next(); + } + + public class Result implements CriterionResult { + private Set regionsToTest; + Coordinates found = null; + + public Result() { + regionsToTest = new HashSet<>(); + forEachSubRegion(constraint, regionsToTest::add); + } + + public Result(Result other) { + this.regionsToTest = new HashSet<>(other.regionsToTest); + } + + @Override + public TriState hasMatched() { + if(found != null) + return TriState.TRUE; + if(regionsToTest.isEmpty()) + return TriState.FALSE; + return TriState.UNKNOWN; + } + + @Override + public void checkRegionAndUpdate(ResultsMap map, World world, Coordinates offset, Region.Box region) { + if(regionsToTest.remove(region)) { + Optional pos = constraint.checkRegion(world, region.move(offset)); + if(pos.isPresent()) + found = pos.get(); + regionsToTest.clear(); + } + } + + @Override + public void addItemToWorldResult(WorldFilterResult result) { + if(found != null) + constraint.addMarkers(result.getItemFor(found)); + } + + @Override + public CriterionResult copy() { + return new Result(this); + } + + } + + private static void forEachSubRegion(Constraint constraint, Consumer consumer) { + Region region = constraint.getRegion(); + Coordinates c1 = region.getCornerNW().snapTo(WorldFilter.REGION_SIZE); + Coordinates c2 = region.getCornerSE().snapUpwardsTo(WorldFilter.REGION_SIZE); + + int size = WorldFilter.REGION_SIZE.getStep(); + for(int x = c1.getX(); x < c2.getX(); x += size) { + for(int y = c1.getY(); y < c2.getY(); y += size) { + Region.Box r = Region.box(x, y, size, size); + if(constraint.getRegion().intersectsWith(r)) + consumer.accept(r); + } + } + } + } diff --git a/src/main/java/amidst/filter/json/CriterionJson.java b/src/main/java/amidst/filter/json/CriterionJson.java index ca0d17375..aa4d08282 100644 --- a/src/main/java/amidst/filter/json/CriterionJson.java +++ b/src/main/java/amidst/filter/json/CriterionJson.java @@ -40,13 +40,13 @@ public CriterionJson() {} * If any error occurs while validating a criterion, an empty * Optional is returned. */ - public Optional validate(CriterionJsonContext ctx) { + public Optional> validate(CriterionJsonContext ctx) { if(center != null) { ctx.withCenter(ctx.getCenter().add(center)); } - Optional criterion; + Optional> criterion; if(negate) { criterion = doValidate(ctx.withName("!")) .map(c -> new NegateCriterion(ctx.getName(), c)); @@ -65,14 +65,14 @@ public Optional validate(CriterionJsonContext ctx) { // This method takes care of subclass-specific validation. - protected abstract Optional doValidate(CriterionJsonContext ctx); + protected abstract Optional> doValidate(CriterionJsonContext ctx); - protected static Optional> validateList(List list, CriterionJsonContext ctx, String listName) { - List criteria = new ArrayList<>(); + protected static Optional>> validateList(List list, CriterionJsonContext ctx, String listName) { + List> criteria = new ArrayList<>(); boolean isOk = true; for(int i = 0; i < list.size(); i++) { - Optional res = list.get(i).validate(ctx.withName(listName + "[" + i + "]")); + Optional> res = list.get(i).validate(ctx.withName(listName + "[" + i + "]")); if(isOk && res.isPresent()) criteria.add(res.get()); diff --git a/src/main/java/amidst/filter/json/CriterionJsonAnd.java b/src/main/java/amidst/filter/json/CriterionJsonAnd.java index b13b3833f..f1d53c733 100644 --- a/src/main/java/amidst/filter/json/CriterionJsonAnd.java +++ b/src/main/java/amidst/filter/json/CriterionJsonAnd.java @@ -16,7 +16,7 @@ public class CriterionJsonAnd extends CriterionJson { public CriterionJsonAnd() {} @Override - protected Optional doValidate(CriterionJsonContext ctx) { + protected Optional> doValidate(CriterionJsonContext ctx) { return validateList(and, ctx, "and") .map(MatchAllCriterion::new); } diff --git a/src/main/java/amidst/filter/json/CriterionJsonBase.java b/src/main/java/amidst/filter/json/CriterionJsonBase.java index 0692eab90..313654fae 100644 --- a/src/main/java/amidst/filter/json/CriterionJsonBase.java +++ b/src/main/java/amidst/filter/json/CriterionJsonBase.java @@ -57,7 +57,7 @@ public static class ClusterInfo { @Override - protected Optional doValidate(CriterionJsonContext ctx) { + protected Optional> doValidate(CriterionJsonContext ctx) { if(shape == null) shape = ctx.getShape(); @@ -107,7 +107,7 @@ protected Optional doValidate(CriterionJsonContext ctx) { Region region = isSquare ? Region.box(center, radius) : Region.circle(center, radius); - List list = new ArrayList<>(); + List> list = new ArrayList<>(); if(structSet.isEmpty()) { for(Biome b: biomeSet) { @@ -167,6 +167,8 @@ private Collection getStructureSet(CriterionJsonContext c DefaultWorldIconTypes struct = DefaultWorldIconTypes.getByName(structName.toLowerCase()); if(struct == null) ctx.error("the structure " + structName + " doesn't exist"); + else if(StructureConstraint.UNSUPPORTED_STRUCTURES.contains(struct)) + ctx.error("the structure " + structName + " isn't supported"); else structSet.add(struct); } return structSet; diff --git a/src/main/java/amidst/filter/json/CriterionJsonContext.java b/src/main/java/amidst/filter/json/CriterionJsonContext.java index 834a99f0e..7f37862b1 100644 --- a/src/main/java/amidst/filter/json/CriterionJsonContext.java +++ b/src/main/java/amidst/filter/json/CriterionJsonContext.java @@ -24,7 +24,7 @@ private static class Globals { //List of criteria already converted; //if null, the criterion is being converted and we have a circular reference - public Map> mappings; + public Map>> mappings; public List errors; public Globals(Function criterionSupplier) { @@ -85,7 +85,7 @@ public CriterionJsonContext withCenter(Coordinates center) { return ctx; } - public Optional convertCriterion(String name) { + public Optional> convertCriterion(String name) { CriterionJson json = globals.supplier.apply(name); if(json == null) { error("the group " + name + " doesn't exist"); @@ -93,7 +93,7 @@ public Optional convertCriterion(String name) { } if(globals.mappings.containsKey(name)) { - Optional c = globals.mappings.get(name); + Optional> c = globals.mappings.get(name); if(c == null) { error("circular reference to group " + name); return Optional.empty(); @@ -104,7 +104,7 @@ public Optional convertCriterion(String name) { globals.mappings.put(name, null); CriterionJsonContext ctx = copy(); ctx.name = name; - Optional c = json.validate(ctx); + Optional> c = json.validate(ctx); globals.mappings.put(name, c); return c; } diff --git a/src/main/java/amidst/filter/json/CriterionJsonGroup.java b/src/main/java/amidst/filter/json/CriterionJsonGroup.java index 90b56e1ee..47027b5e6 100644 --- a/src/main/java/amidst/filter/json/CriterionJsonGroup.java +++ b/src/main/java/amidst/filter/json/CriterionJsonGroup.java @@ -14,7 +14,7 @@ public class CriterionJsonGroup extends CriterionJson { public CriterionJsonGroup() {} @Override - protected Optional doValidate(CriterionJsonContext ctx) { + protected Optional> doValidate(CriterionJsonContext ctx) { return ctx.convertCriterion(group); } } \ No newline at end of file diff --git a/src/main/java/amidst/filter/json/CriterionJsonOr.java b/src/main/java/amidst/filter/json/CriterionJsonOr.java index 154104812..8bbe936d3 100644 --- a/src/main/java/amidst/filter/json/CriterionJsonOr.java +++ b/src/main/java/amidst/filter/json/CriterionJsonOr.java @@ -22,7 +22,7 @@ public class CriterionJsonOr extends CriterionJson { public CriterionJsonOr() {} @Override - protected Optional doValidate(CriterionJsonContext ctx) { + protected Optional> doValidate(CriterionJsonContext ctx) { if(minScore != Integer.MIN_VALUE) ctx.unsupportedAttribute("minScore"); diff --git a/src/main/java/amidst/filter/json/WorldFilterJson.java b/src/main/java/amidst/filter/json/WorldFilterJson.java index 46568c45b..64d8b2853 100644 --- a/src/main/java/amidst/filter/json/WorldFilterJson.java +++ b/src/main/java/amidst/filter/json/WorldFilterJson.java @@ -50,14 +50,14 @@ public WorldFilter validate() throws WorldFilterParseException { Set ignoreSet = makeIgnoreSet(ctx.withName("")); - Map criteria = new HashMap<>(); + Map> criteria = new HashMap<>(); for(String name: groups.keySet()) { if(!ignoreSet.contains(name)) criteria.put(name, ctx.convertCriterion(name).orElse(null)); } - Criterion main = match.validate(ctx.withName("
")).orElse(null); + Criterion main = match.validate(ctx.withName("
")).orElse(null); if(!ctx.getErrors().isEmpty()) throw new WorldFilterParseException(ctx.getErrors()); diff --git a/src/main/java/amidst/gui/seedsearcher/SeedSearcher.java b/src/main/java/amidst/gui/seedsearcher/SeedSearcher.java index 9f2aa8dfb..ee491054b 100644 --- a/src/main/java/amidst/gui/seedsearcher/SeedSearcher.java +++ b/src/main/java/amidst/gui/seedsearcher/SeedSearcher.java @@ -1,10 +1,12 @@ package amidst.gui.seedsearcher; +import java.util.Optional; import java.util.function.Consumer; import amidst.documentation.AmidstThread; import amidst.documentation.CalledOnlyBy; import amidst.documentation.NotThreadSafe; +import amidst.filter.WorldFilterResult; import amidst.gui.main.MainWindowDialogs; import amidst.logging.AmidstLogger; import amidst.mojangapi.RunningLauncherProfile; @@ -36,14 +38,14 @@ public SeedSearcher( } @CalledOnlyBy(AmidstThread.EDT) - public void search(SeedSearcherConfiguration configuration, Consumer onWorldFound) { + public void search(SeedSearcherConfiguration configuration, Consumer onWorldFound) { this.isSearching = true; this.isStopRequested = false; workerExecutor.run(createSearcher(configuration), onWorldFound); } @CalledOnlyBy(AmidstThread.EDT) - private ProgressReportingWorker createSearcher(SeedSearcherConfiguration configuration) { + private ProgressReportingWorker createSearcher(SeedSearcherConfiguration configuration) { return reporter -> this.trySearch(reporter, configuration); } @@ -68,7 +70,7 @@ public boolean isStopRequested() { } @CalledOnlyBy(AmidstThread.WORKER) - private void trySearch(ProgressReporter reporter, SeedSearcherConfiguration configuration) { + private void trySearch(ProgressReporter reporter, SeedSearcherConfiguration configuration) { try { doSearch(reporter, configuration); } catch (IllegalStateException | MinecraftInterfaceException e) { @@ -81,7 +83,7 @@ private void trySearch(ProgressReporter reporter, SeedSearcherConf } @CalledOnlyBy(AmidstThread.WORKER) - private void doSearch(ProgressReporter reporter, SeedSearcherConfiguration configuration) + private void doSearch(ProgressReporter reporter, SeedSearcherConfiguration configuration) throws IllegalStateException, MinecraftInterfaceException { do { @@ -90,13 +92,14 @@ private void doSearch(ProgressReporter reporter, SeedSearcherConfi } @CalledOnlyBy(AmidstThread.WORKER) - private void doSearchOne(ProgressReporter reporter, SeedSearcherConfiguration configuration) + private void doSearchOne(ProgressReporter reporter, SeedSearcherConfiguration configuration) throws IllegalStateException, MinecraftInterfaceException { while (!isStopRequested) { World world = runningLauncherProfile.createWorld(new WorldOptions(WorldSeed.random(), configuration.getWorldType())); - if (configuration.getWorldFilter().isValid(world)) { - reporter.report(world.getWorldOptions()); + Optional result = configuration.getWorldFilter().match(world); + if (result.isPresent()) { + reporter.report(result.get()); world.dispose(); break; } diff --git a/src/main/java/amidst/gui/seedsearcher/SeedSearcherWindow.java b/src/main/java/amidst/gui/seedsearcher/SeedSearcherWindow.java index 2e7e0fab5..65a7cb4f8 100644 --- a/src/main/java/amidst/gui/seedsearcher/SeedSearcherWindow.java +++ b/src/main/java/amidst/gui/seedsearcher/SeedSearcherWindow.java @@ -25,13 +25,13 @@ import amidst.documentation.CalledOnlyBy; import amidst.documentation.NotThreadSafe; import amidst.filter.WorldFilter; +import amidst.filter.WorldFilterResult; import amidst.filter.json.WorldFilterJson; import amidst.filter.json.WorldFilterParseException; import amidst.gui.main.MainWindowDialogs; import amidst.gui.main.WorldSwitcher; import amidst.logging.AmidstLogger; import amidst.logging.AmidstMessageBox; -import amidst.mojangapi.world.WorldOptions; import amidst.mojangapi.world.WorldType; import net.miginfocom.swing.MigLayout; @@ -181,10 +181,10 @@ private SeedSearcherConfiguration createSeedSearcherConfiguration(WorldFilter wo } @CalledOnlyBy(AmidstThread.EDT) - private void worldFound(WorldOptions world) { + private void worldFound(WorldFilterResult filterResult) { //TODO better reporting - AmidstLogger.info("Found world!"); - worldSwitcher.displayWorld(world); + AmidstLogger.info("Found world:\n" + filterResult.toString()); + worldSwitcher.displayWorld(filterResult.getWorldOptions()); updateGUI(); } diff --git a/src/main/java/amidst/util/TriState.java b/src/main/java/amidst/util/TriState.java new file mode 100644 index 000000000..96cff7c78 --- /dev/null +++ b/src/main/java/amidst/util/TriState.java @@ -0,0 +1,31 @@ +package amidst.util; + +import java.util.Objects; + +public enum TriState { + TRUE, + FALSE, + UNKNOWN; + + public TriState not() { + if(this == TRUE) + return FALSE; + if(this == FALSE) + return TRUE; + return UNKNOWN; + } + + public TriState and(TriState other) { + Objects.requireNonNull(other); + if(this == TRUE) + return other; + return this; + } + + public TriState or(TriState other) { + Objects.requireNonNull(other); + if(this == TRUE) + return TRUE; + return other; + } +} From 1c60aa81fbc62c83dd216dbb9830410cb10a56c3 Mon Sep 17 00:00:00 2001 From: moulins <> Date: Sat, 24 Jun 2017 00:37:03 +0200 Subject: [PATCH 12/13] Add better reporting interface --- .../java/amidst/filter/WorldFilterResult.java | 9 + .../gui/seedsearcher/ResultCellRenderer.java | 37 ++++ .../gui/seedsearcher/SeedSearcherWindow.java | 179 ++++++++++++++++-- 3 files changed, 211 insertions(+), 14 deletions(-) create mode 100644 src/main/java/amidst/gui/seedsearcher/ResultCellRenderer.java diff --git a/src/main/java/amidst/filter/WorldFilterResult.java b/src/main/java/amidst/filter/WorldFilterResult.java index 4a75f9364..5f1ae9109 100644 --- a/src/main/java/amidst/filter/WorldFilterResult.java +++ b/src/main/java/amidst/filter/WorldFilterResult.java @@ -1,5 +1,6 @@ package amidst.filter; +import java.util.Collections; import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; @@ -41,10 +42,18 @@ public void addOptionalGoal(String name) { optionalGoals.add(name); } + public Set getOptionalGoals() { + return Collections.unmodifiableSet(optionalGoals); + } + public ResultItem getItemFor(Coordinates coordinates) { return items.computeIfAbsent(coordinates, c -> new ResultItem()); } + public Map getItems() { + return Collections.unmodifiableMap(items); + } + @Override public String toString() { StringBuffer str = new StringBuffer("match for ("); diff --git a/src/main/java/amidst/gui/seedsearcher/ResultCellRenderer.java b/src/main/java/amidst/gui/seedsearcher/ResultCellRenderer.java new file mode 100644 index 000000000..948ae6548 --- /dev/null +++ b/src/main/java/amidst/gui/seedsearcher/ResultCellRenderer.java @@ -0,0 +1,37 @@ +package amidst.gui.seedsearcher; + +import java.awt.Color; +import java.awt.Component; +import java.util.function.Function; + +import javax.swing.BorderFactory; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.ListCellRenderer; + +public class ResultCellRenderer implements ListCellRenderer { + + private final Function stringMapper; + + public ResultCellRenderer(Function stringMapper) { + this.stringMapper = stringMapper; + } + + @Override + public Component getListCellRendererComponent(JList list, T result, + int index, boolean selected, boolean hasFocus) { + + JLabel label = new JLabel(stringMapper.apply(result)); + + label.setBorder(BorderFactory.createCompoundBorder( + BorderFactory.createMatteBorder(0, 0, 1, 0, Color.gray), + BorderFactory.createEmptyBorder(5, 5, 5, 5) + )); + + if(selected) { + label.setOpaque(true); + label.setBackground(new Color(160, 190, 255)); + } + return label; + } +} diff --git a/src/main/java/amidst/gui/seedsearcher/SeedSearcherWindow.java b/src/main/java/amidst/gui/seedsearcher/SeedSearcherWindow.java index 65a7cb4f8..63ce61d96 100644 --- a/src/main/java/amidst/gui/seedsearcher/SeedSearcherWindow.java +++ b/src/main/java/amidst/gui/seedsearcher/SeedSearcherWindow.java @@ -2,22 +2,33 @@ import java.awt.Color; import java.awt.Font; +import java.awt.GridLayout; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.Comparator; import java.util.List; +import java.util.Map; import java.util.stream.Collectors; +import javax.swing.BorderFactory; +import javax.swing.DefaultListModel; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComboBox; +import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.JPanel; import javax.swing.JScrollPane; +import javax.swing.JSpinner; import javax.swing.JTextArea; import javax.swing.ScrollPaneConstants; -import javax.swing.border.LineBorder; +import javax.swing.SpinnerNumberModel; import amidst.AmidstMetaData; import amidst.AmidstSettings; @@ -26,13 +37,16 @@ import amidst.documentation.NotThreadSafe; import amidst.filter.WorldFilter; import amidst.filter.WorldFilterResult; +import amidst.filter.WorldFilterResult.ResultItem; import amidst.filter.json.WorldFilterJson; import amidst.filter.json.WorldFilterParseException; import amidst.gui.main.MainWindowDialogs; import amidst.gui.main.WorldSwitcher; import amidst.logging.AmidstLogger; import amidst.logging.AmidstMessageBox; +import amidst.mojangapi.world.WorldSeed; import amidst.mojangapi.world.WorldType; +import amidst.mojangapi.world.coordinates.Coordinates; import net.miginfocom.swing.MigLayout; @NotThreadSafe @@ -49,8 +63,16 @@ public class SeedSearcherWindow { private final JTextArea searchQueryTextArea; private final JComboBox worldTypeComboBox; private final JCheckBox searchContinuouslyCheckBox; + private final JSpinner searchMaxHitsSpinner; private final JButton searchButton; + private final JLabel worldsFoundLabel; + private final JLabel goalsMetLabel; + private final JList worldsFoundList; + private final JList> worldItemsList; private final JFrame frame; + + private final DefaultListModel worldsFound; + private final DefaultListModel> worldItems; @CalledOnlyBy(AmidstThread.EDT) public SeedSearcherWindow( @@ -64,10 +86,17 @@ public SeedSearcherWindow( this.dialogs = dialogs; this.worldSwitcher = worldSwitcher; this.seedSearcher = seedSearcher; + this.worldsFound = new DefaultListModel<>(); + this.worldItems = new DefaultListModel<>(); this.searchQueryTextArea = createSearchQueryTextArea(); this.worldTypeComboBox = createWorldTypeComboBox(); this.searchContinuouslyCheckBox = createSearchContinuouslyCheckBox(); + this.searchMaxHitsSpinner = createSearchMaxHitsSpinner(); this.searchButton = createSearchButton(); + this.worldsFoundLabel = new JLabel(); + this.goalsMetLabel = new JLabel(); + this.worldsFoundList = createWorldsFoundList(); + this.worldItemsList = createWorldItemsList(); this.frame = createFrame(); tryReloadFromFile(); @@ -77,6 +106,7 @@ public SeedSearcherWindow( public void show() { this.frame.setVisible(true); updateGUI(); + onWorldSelected(null, false); } @CalledOnlyBy(AmidstThread.EDT) @@ -95,7 +125,16 @@ private JComboBox createWorldTypeComboBox() { @CalledOnlyBy(AmidstThread.EDT) private JCheckBox createSearchContinuouslyCheckBox() { - return new JCheckBox("search continuously"); + JCheckBox result = new JCheckBox("search continuously until"); + result.addActionListener(e -> { + searchMaxHitsSpinner.setEnabled(result.isSelected()); + }); + return result; + } + + @CalledOnlyBy(AmidstThread.EDT) + private JSpinner createSearchMaxHitsSpinner() { + return new JSpinner(new SpinnerNumberModel(100, 0, null, 1)); } @CalledOnlyBy(AmidstThread.EDT) @@ -104,28 +143,105 @@ private JButton createSearchButton() { result.addActionListener(e -> searchButtonClicked()); return result; } + + @CalledOnlyBy(AmidstThread.EDT) + private JList createWorldsFoundList() { + JList result = new JList<>(worldsFound); + result.setCellRenderer(new ResultCellRenderer<>(filterResult -> { + WorldSeed seed = filterResult.getWorldOptions().getWorldSeed(); + + String txt = seed.getLong() + ": "; + + int nbGoals = filterResult.getOptionalGoals().size(); + txt += nbGoals + (nbGoals > 1 ? " goals" : " goal"); + + return txt; + })); + result.addListSelectionListener(e -> { + onWorldSelected(result.getSelectedValue(), e.getValueIsAdjusting()); + }); + return result; + } + + @CalledOnlyBy(AmidstThread.EDT) + private JList> createWorldItemsList() { + JList> result = new JList<>(worldItems); + result.setCellRenderer(new ResultCellRenderer<>(entry -> { + String txt = entry.getKey() + ": "; + + ResultItem item = entry.getValue(); + if(item.biome != null) { + txt += item.biome.getName(); + if(!item.icons.isEmpty()) + txt += " with "; + } + + txt += item.icons.stream().map(s -> s.getLabel()).collect(Collectors.joining(", ")); + + return txt; + })); + //TODO display some sort of marker on the world? + return result; + } @CalledOnlyBy(AmidstThread.EDT) private JFrame createFrame() { JFrame result = new JFrame("Seed Searcher"); result.setIconImages(metadata.getIcons()); - result.getContentPane().setLayout(new MigLayout()); + result.getContentPane().setLayout(new GridLayout(1, 2)); + + result.add(createSearchPanel()); + result.add(createResultsPanel()); + + result.setSize(800, 600); + result.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE); + result.addWindowListener(new WindowAdapter() { + @Override + public void windowClosed(WindowEvent e) { + seedSearcher.stop(); + } + }); + return result; + } + + @CalledOnlyBy(AmidstThread.EDT) + private JPanel createSearchPanel() { + JPanel result = new JPanel(new MigLayout()); + result.add(new JLabel("Search Query:"), "growx, pushx, wrap"); - result.add(createScrollPane(searchQueryTextArea), "grow, push, wrap"); + + result.add(createScrollPane(searchQueryTextArea, true), "grow, push, wrap"); result.add(new JLabel("World Type:"), "growx, pushx, wrap"); result.add(worldTypeComboBox, "growx, pushx, wrap"); - result.add(searchContinuouslyCheckBox, "growx, pushx, wrap"); + result.add(searchContinuouslyCheckBox, "split 3"); + result.add(searchMaxHitsSpinner, "w 60!"); + result.add(new JLabel("worlds"), "growx, pushx, wrap"); result.add(searchButton, "pushx, wrap"); - result.setSize(800, 600); - result.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE); + + return result; + } + + @CalledOnlyBy(AmidstThread.EDT) + private JPanel createResultsPanel() { + JPanel result = new JPanel(new MigLayout()); + + result.add(worldsFoundLabel, "growx, pushx, wrap"); + result.add(createScrollPane(worldsFoundList, false), "growx, pushx, growy, pushy, wrap"); + result.add(goalsMetLabel, "growx, pushx, wrap"); + result.add(new JLabel("Items found:"), "growx, pushx, gaptop 15, wrap"); + result.add(createScrollPane(worldItemsList, false), "growx, pushx, wrap"); + return result; } @CalledOnlyBy(AmidstThread.EDT) - private JScrollPane createScrollPane(JTextArea textArea) { - JScrollPane result = new JScrollPane(textArea); - result.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED); - result.setBorder(new LineBorder(Color.darkGray, 1)); + private JScrollPane createScrollPane(JComponent component, boolean horizontalScrolling) { + JScrollPane result = new JScrollPane(component); + int scrollPolicy = horizontalScrolling + ? ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED + : ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER; + result.setHorizontalScrollBarPolicy(scrollPolicy); + result.setBorder(BorderFactory.createLineBorder(Color.darkGray, 1)); return result; } @@ -154,6 +270,7 @@ private void searchButtonClicked() { if (seedSearcher.isSearching()) { seedSearcher.stop(); } else { + worldsFound.clear(); try { SeedSearcherConfiguration seedSearcherConfiguration = createSeedSearcherConfiguration(); seedSearcher.search(seedSearcherConfiguration, world -> worldFound(world)); @@ -182,26 +299,60 @@ private SeedSearcherConfiguration createSeedSearcherConfiguration(WorldFilter wo @CalledOnlyBy(AmidstThread.EDT) private void worldFound(WorldFilterResult filterResult) { - //TODO better reporting - AmidstLogger.info("Found world:\n" + filterResult.toString()); - worldSwitcher.displayWorld(filterResult.getWorldOptions()); + worldsFound.addElement(filterResult); + if(worldsFound.size() == 1) + worldsFoundList.setSelectedIndex(0); + + int maxHits = (int) searchMaxHitsSpinner.getValue(); + if(maxHits > 0 && worldsFound.size() >= maxHits) + seedSearcher.stop(); + updateGUI(); } @CalledOnlyBy(AmidstThread.EDT) private void updateGUI() { + int nbWorlds = worldsFound.size(); + if(nbWorlds == 0) + worldsFoundLabel.setText("No worlds found"); + else if(nbWorlds == 1) + worldsFoundLabel.setText("1 world found:"); + else worldsFoundLabel.setText(nbWorlds + " worlds found:"); + if (seedSearcher.isSearching() && !seedSearcher.isStopRequested()) { searchButton.setText("Stop"); searchQueryTextArea.setEditable(false); worldTypeComboBox.setEnabled(false); searchContinuouslyCheckBox.setEnabled(false); + searchMaxHitsSpinner.setEnabled(false); } else { searchButton.setText("Search"); searchQueryTextArea.setEditable(true); worldTypeComboBox.setEnabled(true); searchContinuouslyCheckBox.setEnabled(true); + searchMaxHitsSpinner.setEnabled(searchContinuouslyCheckBox.isSelected()); } } + + @CalledOnlyBy(AmidstThread.EDT) + private void onWorldSelected(WorldFilterResult result, boolean isAdjusting) { + worldItems.clear(); + + if(result == null) { + goalsMetLabel.setText("Goals met: -"); + return; + } + + String goals = result.getOptionalGoals().stream().collect(Collectors.joining(", ", "Goals met: ", "")); + goalsMetLabel.setText(goals); + + result.getItems().entrySet().stream() + .sorted(Comparator.comparingDouble(e -> e.getKey().getDistanceSq(Coordinates.origin()))) + .forEachOrdered(worldItems::addElement); + + if(!isAdjusting) + worldSwitcher.displayWorld(result.getWorldOptions()); + } @CalledOnlyBy(AmidstThread.EDT) public void dispose() { From ab93421d797de0f4519db93164d4396487356b4a Mon Sep 17 00:00:00 2001 From: moulins <> Date: Thu, 6 Jul 2017 08:15:02 +0200 Subject: [PATCH 13/13] Fix bug in NegateCriterion; make all CriterionResult constructors private --- src/main/java/amidst/filter/criterion/MatchAllCriterion.java | 4 ++-- src/main/java/amidst/filter/criterion/MatchAnyCriterion.java | 4 ++-- src/main/java/amidst/filter/criterion/NegateCriterion.java | 5 +++-- src/main/java/amidst/filter/criterion/SimpleCriterion.java | 4 ++-- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/main/java/amidst/filter/criterion/MatchAllCriterion.java b/src/main/java/amidst/filter/criterion/MatchAllCriterion.java index 6ef938edf..2b39e752d 100644 --- a/src/main/java/amidst/filter/criterion/MatchAllCriterion.java +++ b/src/main/java/amidst/filter/criterion/MatchAllCriterion.java @@ -53,12 +53,12 @@ public class Result implements CriterionResult { private List> undecided; private boolean isMatch; - public Result() { + private Result() { this.undecided = new ArrayList<>(criteria); this.isMatch = true; } - public Result(Result r) { + private Result(Result r) { this.undecided = new ArrayList<>(r.undecided); this.isMatch = r.isMatch; } diff --git a/src/main/java/amidst/filter/criterion/MatchAnyCriterion.java b/src/main/java/amidst/filter/criterion/MatchAnyCriterion.java index dd3b446c9..ee26084cd 100644 --- a/src/main/java/amidst/filter/criterion/MatchAnyCriterion.java +++ b/src/main/java/amidst/filter/criterion/MatchAnyCriterion.java @@ -52,12 +52,12 @@ public class Result implements CriterionResult { private List> undecided; private boolean isMatch; - public Result() { + private Result() { this.undecided = new ArrayList<>(criteria); this.isMatch = false; } - public Result(Result r) { + private Result(Result r) { this.undecided = new ArrayList<>(r.undecided); this.isMatch = r.isMatch; } diff --git a/src/main/java/amidst/filter/criterion/NegateCriterion.java b/src/main/java/amidst/filter/criterion/NegateCriterion.java index a135165bc..e1fa1366b 100644 --- a/src/main/java/amidst/filter/criterion/NegateCriterion.java +++ b/src/main/java/amidst/filter/criterion/NegateCriterion.java @@ -39,7 +39,7 @@ public Region.Box getNextRegionToCheck(ResultsMap map) { public class Result implements CriterionResult { TriState state; - public Result(TriState state) { + private Result(TriState state) { this.state = state; } @@ -50,7 +50,8 @@ public TriState hasMatched() { @Override public void checkRegionAndUpdate(ResultsMap map, World world, Coordinates offset, Region.Box region) { - state = criterion.checkRegion(map, world, offset, region); + if(state == TriState.UNKNOWN) + state = criterion.checkRegion(map, world, offset, region).not(); } @Override diff --git a/src/main/java/amidst/filter/criterion/SimpleCriterion.java b/src/main/java/amidst/filter/criterion/SimpleCriterion.java index a568f5146..655bbd0a6 100644 --- a/src/main/java/amidst/filter/criterion/SimpleCriterion.java +++ b/src/main/java/amidst/filter/criterion/SimpleCriterion.java @@ -53,12 +53,12 @@ public class Result implements CriterionResult { private Set regionsToTest; Coordinates found = null; - public Result() { + private Result() { regionsToTest = new HashSet<>(); forEachSubRegion(constraint, regionsToTest::add); } - public Result(Result other) { + private Result(Result other) { this.regionsToTest = new HashSet<>(other.regionsToTest); }