├── .classpath ├── .github ├── ISSUE_TEMPLATE │ └── bug_report.md └── workflows │ └── gradle.yml ├── .gitignore ├── BlockMap-cli ├── build.gradle └── src │ └── main │ ├── java │ └── de │ │ └── piegames │ │ └── blockmap │ │ └── standalone │ │ ├── CommandLineMain.java │ │ ├── DeserializeNullChecker.java │ │ ├── PostProcessing.java │ │ ├── ServerSettings.java │ │ └── VersionProvider.java │ └── resources │ ├── de │ └── piegames │ │ └── blockmap │ │ └── standalone │ │ └── tiles.css │ └── log4j2.xml ├── BlockMap-core ├── build.gradle └── src │ └── main │ └── java │ └── de │ └── piegames │ └── blockmap │ ├── DotMinecraft.java │ ├── MinecraftDimension.java │ ├── MinecraftVersion.java │ ├── color │ ├── BiomeColorMap.java │ ├── BlockColorMap.java │ ├── Color.java │ └── package-info.java │ ├── renderer │ ├── Block.java │ ├── BlockState.java │ ├── ChunkRenderer.java │ ├── ChunkRenderer_1_13.java │ ├── ChunkRenderer_1_14.java │ ├── ChunkRenderer_1_15.java │ ├── ChunkRenderer_1_16.java │ ├── ChunkRenderer_1_17.java │ ├── ChunkRenderer_1_18.java │ ├── RegionRenderer.java │ ├── RegionShader.java │ └── RenderSettings.java │ └── world │ ├── ChunkMetadata.java │ ├── LevelMetadata.java │ ├── Region.java │ ├── RegionFolder.java │ └── ServerMetadata.java ├── BlockMap-gui ├── build.gradle └── src │ └── main │ ├── java │ └── de │ │ └── piegames │ │ └── blockmap │ │ └── gui │ │ ├── DisplaySettings.java │ │ ├── DisplayViewport.java │ │ ├── MapPane.java │ │ ├── RenderedMap.java │ │ ├── RenderedRegion.java │ │ ├── ResizableCanvas.java │ │ ├── VersionProvider.java │ │ ├── WorldRendererCanvas.java │ │ ├── decoration │ │ ├── DragScrollDecoration.java │ │ ├── GridDecoration.java │ │ ├── Pin.java │ │ ├── PinDecoration.java │ │ ├── ScaleDecoration.java │ │ ├── SelectRectangleDecoration.java │ │ └── package-info.java │ │ ├── package-info.java │ │ └── standalone │ │ ├── CacheEntry.java │ │ ├── DateConverter.java │ │ ├── FileCache.java │ │ ├── GuiController.java │ │ ├── GuiControllerServer.java │ │ ├── GuiControllerWorld.java │ │ ├── GuiMain.java │ │ ├── GuiMainLauncher.java │ │ ├── GuiMainPreloader.java │ │ ├── HistoryItem.java │ │ ├── HistoryManager.java │ │ ├── OpenDialog.java │ │ ├── RegionFolderCache.java │ │ ├── SimpleImageCache.java │ │ ├── SimplePlayerProfileCache.java │ │ ├── StatusBarSkin2.java │ │ ├── about │ │ ├── AboutDialog.java │ │ └── DependencyPane.java │ │ └── package-info.java │ └── resources │ ├── de │ └── piegames │ │ └── blockmap │ │ └── gui │ │ ├── decoration │ │ ├── SettingsOverlay.fxml │ │ ├── pins.css │ │ └── textures │ │ │ ├── overlays │ │ │ ├── chunk_corrupted.png │ │ │ ├── chunk_forced.png │ │ │ ├── chunk_outdated.png │ │ │ ├── chunk_unfinished.png │ │ │ ├── pin_chunk_corrupted.png │ │ │ ├── pin_chunk_outdated.png │ │ │ ├── pin_chunk_unfinished.png │ │ │ └── pin_chunks.png │ │ │ ├── pins │ │ │ ├── banner.png │ │ │ ├── banner_fabric.png │ │ │ ├── banner_sticks.png │ │ │ ├── lodestone.png │ │ │ ├── map.png │ │ │ ├── pins.png │ │ │ ├── player.png │ │ │ ├── spawn_map.png │ │ │ ├── spawn_player.png │ │ │ └── structures.png │ │ │ ├── structures │ │ │ ├── bastion_remnant.png │ │ │ ├── bee_hive.png │ │ │ ├── buried_treasure.png │ │ │ ├── desert_pyramid.png │ │ │ ├── end_city.png │ │ │ ├── fortress.png │ │ │ ├── fossil.png │ │ │ ├── house.png │ │ │ ├── igloo.png │ │ │ ├── jungle_pyramid.png │ │ │ ├── mansion.png │ │ │ ├── mineshaft.png │ │ │ ├── monument.png │ │ │ ├── nether_portal.png │ │ │ ├── ocean_ruin.png │ │ │ ├── outpost.png │ │ │ ├── ruined_portal.png │ │ │ ├── shipwreck.png │ │ │ ├── stronghold.png │ │ │ ├── swamp_hut.png │ │ │ └── village.png │ │ │ └── villages │ │ │ ├── armorer.png │ │ │ ├── bell.png │ │ │ ├── butcher.png │ │ │ ├── cartographer.png │ │ │ ├── cleric.png │ │ │ ├── farmer.png │ │ │ ├── fisherman.png │ │ │ ├── fletcher.png │ │ │ ├── home.png │ │ │ ├── leatherworker.png │ │ │ ├── librarian.png │ │ │ ├── mason.png │ │ │ ├── shepherd.png │ │ │ ├── toolsmith.png │ │ │ └── weaponsmith.png │ │ └── standalone │ │ ├── about │ │ ├── aboutpane.fxml │ │ ├── dependency.fxml │ │ ├── licenseReport.json │ │ └── style.css │ │ ├── icon.png │ │ ├── opendialog.fxml │ │ ├── scene-serversettings.fxml │ │ ├── scene-worldsettings.fxml │ │ ├── scene.fxml │ │ └── style.css │ ├── log4j2.xml │ ├── tmp.png │ └── unknown_server.png ├── BlockMap-internal ├── build.gradle ├── icon │ ├── globe.blend │ └── world.png └── src │ ├── jmh │ ├── java │ │ └── de │ │ │ └── piegames │ │ │ └── blockmap │ │ │ └── renderer │ │ │ └── RenderBenchmark.java │ └── resources │ │ └── log4j2.xml │ ├── main │ ├── java │ │ └── de │ │ │ └── piegames │ │ │ └── blockmap │ │ │ ├── BlockStateHelper.java │ │ │ ├── MinecraftBlocks.java │ │ │ └── generate │ │ │ ├── ColorCompiler.java │ │ │ ├── ColorMapBuilder.java │ │ │ ├── Downloader.java │ │ │ ├── Generator.java │ │ │ ├── GifSequenceWriter.java │ │ │ └── Screenshots.java │ └── resources │ │ ├── BlockMapWorld │ │ ├── data │ │ │ ├── villages.dat │ │ │ ├── villages_end.dat │ │ │ └── villages_nether.dat │ │ ├── icon.png │ │ ├── level.dat │ │ ├── level.dat_old │ │ ├── region │ │ │ ├── r.-1.1.mca │ │ │ ├── r.-1.2.mca │ │ │ ├── r.0.1.mca │ │ │ └── r.0.2.mca │ │ └── session.lock │ │ ├── BlockMapWorld2 │ │ ├── data │ │ │ ├── villages.dat │ │ │ ├── villages_end.dat │ │ │ └── villages_nether.dat │ │ ├── icon.png │ │ ├── level.dat │ │ ├── level.dat_old │ │ ├── region │ │ │ ├── r.-1.2.mca │ │ │ └── r.0.1.mca │ │ └── session.lock │ │ ├── biome-color-instructions.json │ │ ├── block-color-instructions-1_13.json │ │ ├── block-color-instructions-1_14.json │ │ ├── block-color-instructions-1_15.json │ │ ├── block-color-instructions-1_16.json │ │ ├── block-color-instructions-1_17.json │ │ ├── block-color-instructions-1_18.json │ │ ├── heightmap.blend │ │ └── heightmap.png │ └── test │ ├── java │ └── de │ │ └── piegames │ │ └── blockmap │ │ ├── AllTests.java │ │ ├── BiomesTest.java │ │ ├── ColorMapTest.java │ │ ├── ColorTest.java │ │ ├── CommandLineTest.java │ │ ├── RegionFolderTest.java │ │ └── RegionRendererTest.java │ └── resources │ ├── Debug-1_13 │ ├── data │ │ ├── villages.dat │ │ ├── villages_end.dat │ │ └── villages_nether.dat │ ├── icon.png │ ├── level.dat │ ├── level.dat_old │ ├── region │ │ ├── r.-1.-1.mca │ │ ├── r.-1.0.mca │ │ ├── r.-1.1.mca │ │ ├── r.0.-1.mca │ │ ├── r.0.0.mca │ │ ├── r.0.1.mca │ │ ├── r.1.-1.mca │ │ ├── r.1.0.mca │ │ └── r.1.1.mca │ └── session.lock │ ├── Debug-1_14 │ ├── DIM-1 │ │ └── data │ │ │ └── raids_nether.dat │ ├── DIM1 │ │ └── data │ │ │ └── raids_end.dat │ ├── data │ │ └── raids.dat │ ├── icon.png │ ├── level.dat │ ├── level.dat_old │ ├── poi │ │ └── r.0.0.mca │ ├── region │ │ ├── r.-1.-1.mca │ │ ├── r.-1.0.mca │ │ ├── r.-1.1.mca │ │ ├── r.0.-1.mca │ │ ├── r.0.0.mca │ │ ├── r.0.1.mca │ │ ├── r.1.-1.mca │ │ ├── r.1.0.mca │ │ └── r.1.1.mca │ └── session.lock │ ├── Debug-1_15 │ ├── DIM-1 │ │ └── data │ │ │ └── raids_nether.dat │ ├── DIM1 │ │ └── data │ │ │ └── raids_end.dat │ ├── data │ │ └── raids.dat │ ├── icon.png │ ├── level.dat │ ├── level.dat_old │ ├── poi │ │ └── r.0.0.mca │ ├── region │ │ ├── r.-1.-1.mca │ │ ├── r.-1.0.mca │ │ ├── r.-1.1.mca │ │ ├── r.0.-1.mca │ │ ├── r.0.0.mca │ │ ├── r.0.1.mca │ │ ├── r.1.-1.mca │ │ ├── r.1.0.mca │ │ └── r.1.1.mca │ └── session.lock │ ├── Debug-1_16 │ ├── DIM-1 │ │ └── data │ │ │ └── raids.dat │ ├── DIM1 │ │ └── data │ │ │ └── raids_end.dat │ ├── data │ │ └── raids.dat │ ├── icon.png │ ├── level.dat │ ├── level.dat_old │ ├── poi │ │ └── r.0.0.mca │ ├── region │ │ ├── r.-1.-1.mca │ │ ├── r.-1.0.mca │ │ ├── r.-1.1.mca │ │ ├── r.0.-1.mca │ │ ├── r.0.0.mca │ │ ├── r.0.1.mca │ │ ├── r.1.-1.mca │ │ ├── r.1.0.mca │ │ └── r.1.1.mca │ └── session.lock │ ├── Debug-1_17 │ ├── DIM-1 │ │ └── data │ │ │ └── raids.dat │ ├── DIM1 │ │ └── data │ │ │ └── raids_end.dat │ ├── data │ │ └── raids.dat │ ├── entities │ │ ├── r.-1.-1.mca │ │ ├── r.-1.0.mca │ │ ├── r.0.-1.mca │ │ └── r.0.0.mca │ ├── icon.png │ ├── level.dat │ ├── level.dat_old │ └── region │ │ ├── r.-1.-1.mca │ │ ├── r.-1.-2.mca │ │ ├── r.-1.0.mca │ │ ├── r.0.-1.mca │ │ ├── r.0.0.mca │ │ ├── r.1.-2.mca │ │ └── r.1.0.mca │ ├── Debug-1_18 │ ├── DIM-1 │ │ └── data │ │ │ └── raids.dat │ ├── DIM1 │ │ └── data │ │ │ └── raids_end.dat │ ├── data │ │ └── raids.dat │ ├── entities │ │ ├── r.-1.-1.mca │ │ ├── r.-1.0.mca │ │ ├── r.0.-1.mca │ │ └── r.0.0.mca │ ├── icon.png │ ├── level.dat │ ├── level.dat_old │ ├── poi │ │ ├── r.-1.0.mca │ │ ├── r.0.-1.mca │ │ └── r.0.0.mca │ └── region │ │ ├── r.-1.-1.mca │ │ ├── r.-1.0.mca │ │ ├── r.-1.1.mca │ │ ├── r.0.-1.mca │ │ ├── r.0.0.mca │ │ ├── r.0.1.mca │ │ ├── r.1.-1.mca │ │ ├── r.1.0.mca │ │ └── r.1.1.mca │ ├── r.-1.1.mca │ ├── r.0.0.mca │ └── r.1.3.mca ├── LICENSE ├── README.md ├── build.gradle ├── changelog.md ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── icon ├── blockmap-1024.png ├── blockmap-128.png ├── blockmap-16.png ├── blockmap-2048.png ├── blockmap-22.png ├── blockmap-256.png ├── blockmap-32.png ├── blockmap-48.png ├── blockmap-512.png └── blockmap-64.png ├── screenshots ├── screenshot-0.gif ├── screenshot-1.png ├── screenshot-2.png ├── screenshot-3.png └── screenshot-4.png ├── server-settings.json ├── server.sh └── settings.gradle /.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | 5 | --- 6 | 7 | 9 | 10 | ## Environment info 11 | 12 | - **BlockMap version**: `0.0.0` 13 | - **Java version**: `java -version` 14 | - **Operating system**: `Replace me` 15 | 16 | ## Description 17 | 18 | Replace me 19 | 20 | ## Steps to reproduce 21 | 22 | 1. Replace me 23 | 2. Replace me 24 | 3. ... 25 | 26 | 27 | ### Actual result 28 | Replace me 29 | 30 | 31 | ### Expected result: 32 | Replace me 33 | 34 | ## Screenshots 35 | 36 | 37 | 38 | ## Debug log 39 | 42 | 43 | -------------------------------------------------------------------------------- /.github/workflows/gradle.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a Java project with Gradle 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle 3 | 4 | name: Build 5 | 6 | on: 7 | push: 8 | branches: [ master ] 9 | pull_request: 10 | branches: [ master ] 11 | 12 | jobs: 13 | build: 14 | runs-on: ${{ matrix.os }} 15 | strategy: 16 | fail-fast: false 17 | matrix: 18 | os: [ubuntu-latest] 19 | jdk: [17] 20 | steps: 21 | - uses: actions/checkout@v2 22 | - name: Set up JDK ${{ matrix.jdk }} 23 | uses: actions/setup-java@v1 24 | with: 25 | java-version: ${{ matrix.jdk }} 26 | - name: Cache Gradle packages 27 | uses: actions/cache@v2 28 | with: 29 | path: ~/.gradle/caches 30 | key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }} 31 | restore-keys: ${{ runner.os }}-gradle 32 | - name: Cache generated resources 33 | uses: actions/cache@v2 34 | with: 35 | path: ./BlockMap-internal/build/generated-resources/BlockMap-internal 36 | key: ${{ runner.os }}-resources 37 | restore-keys: ${{ runner.os }}-resources 38 | - name: Grant execute permission for gradlew 39 | run: chmod +x gradlew 40 | - name: Build with Gradle 41 | run: ./gradlew regenerate && ./gradlew assemble 42 | - name: Generate jar files 43 | run: ./gradlew previewRelease 44 | - name: Upload jars as artifact 45 | uses: actions/upload-artifact@v2 46 | with: 47 | name: BlockMap-${{ matrix.os }}-${{ github.sha }} 48 | path: BlockMap-*/build/libs/fat/BlockMap-* 49 | continue-on-error: true 50 | - name: Execute tests 51 | run: ./gradlew test 52 | -------------------------------------------------------------------------------- /BlockMap-cli/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'application' 3 | id 'com.github.johnrengelman.shadow' 4 | } 5 | 6 | dependencies { 7 | implementation project(':BlockMap-core') 8 | 9 | implementation 'com.github.piegamesde:gson-fire:9210521426' 10 | implementation 'net.dongliu:gson-java8-datatype:1.1.0' 11 | implementation 'info.picocli:picocli:4.6.2' 12 | implementation 'org.apache.logging.log4j:log4j-core:2.17.1' 13 | implementation 'org.apache.logging.log4j:log4j-jcl:2.17.1' 14 | } 15 | 16 | application { 17 | mainClassName = "de.piegames.blockmap.standalone.CommandLineMain" 18 | } 19 | 20 | // Hide tasks 21 | knows.group = null 22 | 23 | jar { 24 | archiveVersion = "" 25 | manifest { 26 | attributes 'Implementation-Title': 'BlockMap', 27 | 'Main-Class': 'de.piegames.blockmap.standalone.CommandLineMain' 28 | } 29 | } 30 | 31 | shadowJar { 32 | destinationDir = file("build/libs/fat") 33 | archiveClassifier = "" 34 | } 35 | 36 | task versionlessJar(type: Jar) { 37 | archiveVersion = "" 38 | } 39 | -------------------------------------------------------------------------------- /BlockMap-cli/src/main/java/de/piegames/blockmap/standalone/DeserializeNullChecker.java: -------------------------------------------------------------------------------- 1 | package de.piegames.blockmap.standalone; 2 | 3 | import java.lang.annotation.Documented; 4 | import java.lang.annotation.ElementType; 5 | import java.lang.annotation.Retention; 6 | import java.lang.annotation.RetentionPolicy; 7 | import java.lang.annotation.Target; 8 | import java.lang.reflect.Field; 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | 12 | import com.google.gson.Gson; 13 | import com.google.gson.JsonElement; 14 | import com.google.gson.JsonParseException; 15 | import com.google.gson.annotations.SerializedName; 16 | 17 | import io.gsonfire.PostProcessor; 18 | 19 | public class DeserializeNullChecker implements PostProcessor { 20 | 21 | @Documented 22 | @Retention(RetentionPolicy.RUNTIME) 23 | @Target({ ElementType.FIELD }) 24 | public @interface DeserializeNonNull { 25 | } 26 | 27 | @Override 28 | public void postDeserialize(Object result, JsonElement src, Gson gson) { 29 | check(result, src); 30 | } 31 | 32 | @Override 33 | public void postSerialize(JsonElement result, Object src, Gson gson) { 34 | } 35 | 36 | private void check(Object result, JsonElement src) { 37 | try { 38 | List nullFields = new ArrayList<>(); 39 | for (Field f : result.getClass().getDeclaredFields()) { 40 | if (f.isAnnotationPresent(DeserializeNonNull.class)) { 41 | String name = f.getName(); 42 | if (f.isAnnotationPresent(SerializedName.class)) 43 | name = f.getAnnotation(SerializedName.class).value(); 44 | if (f.get(result) == null) 45 | nullFields.add('"' + name + '"'); 46 | } 47 | } 48 | if (!nullFields.isEmpty()) { 49 | if (nullFields.size() == 1) 50 | throw new JsonParseException("Field " + nullFields.get(0) + " in object '" 51 | + ellipsisString(src.toString()) 52 | + "' must be specified"); 53 | else 54 | throw new JsonParseException("Fields " + nullFields + " in object '" 55 | + ellipsisString(src.toString()) 56 | + "' must be specified"); 57 | } 58 | } catch (IllegalArgumentException | IllegalAccessException e) { 59 | throw new JsonParseException("Could not check all null fields", e); 60 | } 61 | } 62 | 63 | private String ellipsisString(String text) { 64 | if (text.length() < 40) 65 | return text; 66 | else 67 | return text.substring(0, 30) + "[…]" + text.substring(text.length() - 10, text.length()); 68 | } 69 | } -------------------------------------------------------------------------------- /BlockMap-cli/src/main/java/de/piegames/blockmap/standalone/ServerSettings.java: -------------------------------------------------------------------------------- 1 | package de.piegames.blockmap.standalone; 2 | 3 | import java.nio.file.Path; 4 | import java.util.Optional; 5 | import java.util.Set; 6 | import java.util.stream.Collectors; 7 | 8 | import com.google.gson.annotations.SerializedName; 9 | 10 | import de.piegames.blockmap.MinecraftDimension; 11 | import de.piegames.blockmap.renderer.RenderSettings; 12 | import de.piegames.blockmap.standalone.DeserializeNullChecker.DeserializeNonNull; 13 | import de.piegames.blockmap.world.LevelMetadata; 14 | import de.piegames.blockmap.world.ServerMetadata; 15 | 16 | public class ServerSettings { 17 | 18 | public RegionFolderSettings[] worlds = new RegionFolderSettings[0]; 19 | @SerializedName("output dir") 20 | @DeserializeNonNull 21 | public Path outputDir; 22 | @SerializedName("server") 23 | public Optional serverMetadata = Optional.empty(); 24 | @SerializedName("show pins") 25 | public Optional pinSettings = Optional.empty(); 26 | 27 | public static class PinSettings { 28 | public static enum ShowPlayers { 29 | ALL, ONLINE_ONLY, NONE; 30 | } 31 | 32 | @SerializedName("players") 33 | public ShowPlayers showPlayers = ShowPlayers.ALL; 34 | @SerializedName("maps") 35 | public boolean showMaps = true; 36 | @SerializedName("slime chunks") 37 | public boolean showSlimeChunks = true; 38 | @SerializedName("force-loaded chunks") 39 | public boolean showLoadedChunks = true; 40 | @SerializedName("barrier") 41 | public boolean showBarrier = true; 42 | @SerializedName("world spawn") 43 | public boolean showWorldSpawn = true; 44 | @SerializedName("POIs") 45 | public Optional> showPOIs = Optional.empty(); 46 | 47 | @SerializedName("structures") 48 | public Optional> showStructures = Optional.empty(); 49 | 50 | /** 51 | * Return a new {@link LevelMetadata} instance of a current one, but with all undesired entries 52 | * stripped. 53 | */ 54 | public LevelMetadata apply(LevelMetadata metadata, Set onlinePlayers) { 55 | return new LevelMetadata( 56 | metadata.getWorldName(), 57 | metadata.getPlayers().flatMap(players -> { 58 | switch (showPlayers) { 59 | case ALL: 60 | return Optional.of(players); 61 | case ONLINE_ONLY: 62 | return Optional.of( 63 | players.stream() 64 | .filter(player -> player.getUUID().map(onlinePlayers::contains).orElse(true)) 65 | .collect(Collectors.toList())); 66 | case NONE: 67 | return Optional.empty(); 68 | default: 69 | throw new InternalError(); 70 | } 71 | }), 72 | metadata.getMaps().filter(__ -> showMaps), 73 | metadata.getVillageObjects().map(pins -> { 74 | if (showPOIs.isEmpty()) 75 | return pins; 76 | else { 77 | var showPOIs = this.showPOIs.get(); 78 | return pins.stream() 79 | .filter(pin -> showPOIs.contains(pin.getType())) 80 | .collect(Collectors.toList()); 81 | } 82 | }), 83 | metadata.getSlimeChunks().filter(__ -> showSlimeChunks), 84 | metadata.getLoadedChunks().filter(__ -> showLoadedChunks), 85 | metadata.getBarrier().filter(__ -> showBarrier), 86 | metadata.getWorldSpawn().filter(__ -> showBarrier)); 87 | } 88 | } 89 | 90 | public static class RegionFolderSettings { 91 | 92 | @DeserializeNonNull 93 | public String name; 94 | @SerializedName("input dir") 95 | @DeserializeNonNull 96 | public Path inputDir; 97 | public MinecraftDimension dimension = MinecraftDimension.OVERWORLD; 98 | public boolean force = false; 99 | @SerializedName("render settings") 100 | public RenderSettings renderSettings = new RenderSettings(); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /BlockMap-cli/src/main/java/de/piegames/blockmap/standalone/VersionProvider.java: -------------------------------------------------------------------------------- 1 | package de.piegames.blockmap.standalone; 2 | 3 | import picocli.CommandLine.IVersionProvider; 4 | 5 | public class VersionProvider implements IVersionProvider { 6 | 7 | /* This part of the code gets generated automatically through `gradle generateSources`. Do not modify! */ 8 | // $REPLACE_START 9 | public static final String VERSION = "2.4.1"; 10 | // $REPLACE_END 11 | 12 | @Override 13 | public String[] getVersion() throws Exception { 14 | return new String[] { VERSION }; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /BlockMap-cli/src/main/resources/de/piegames/blockmap/standalone/tiles.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: black; 3 | box-sizing: border-box; 4 | } 5 | 6 | a.tile { 7 | display: block; 8 | border: 0; 9 | text-decoration: none; 10 | -moz-background-origin: border; 11 | background-origin: border-box; 12 | overflow: hidden; 13 | outline: none; 14 | margin: 0; 15 | padding: 0; 16 | 17 | -ms-interpolation-mode: nearest-neighbor; 18 | image-rendering: -webkit-optimize-contrast; 19 | image-rendering: -webkit-crisp-edges; 20 | image-rendering: -moz-crisp-edges; 21 | image-rendering: -o-crisp-edges; 22 | image-rendering: pixelated; 23 | } 24 | a.tile:hover { 25 | border: 1px solid rgba(255,255,255,0.5); 26 | } 27 | 28 | .notes, .scales-nav { 29 | color: cornflowerblue; 30 | font-size: smaller; 31 | font-family: monospace; 32 | } 33 | .scales-nav ul { 34 | margin: 0; 35 | padding: 0; 36 | } 37 | .scales-nav a { 38 | color: lime; 39 | } 40 | p.notes, .scales-nav p, .scales-nav li { 41 | margin: 3px 6px; 42 | } 43 | .scales-nav p, .scales-nav ul, .scales-nav ul li { 44 | display: inline-block; 45 | } 46 | -------------------------------------------------------------------------------- /BlockMap-cli/src/main/resources/log4j2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /BlockMap-core/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'java-library' 2 | 3 | dependencies { 4 | // Core dependencies 5 | api 'com.github.piegamesde:nbt:3.0.1' 6 | api 'com.google.code.gson:gson:2.8.9' 7 | implementation 'net.dongliu:gson-java8-datatype:1.1.0' 8 | // implementation 'io.gsonfire:gson-fire:1.8.3' 9 | implementation 'com.github.piegamesde:gson-fire:9210521426' 10 | api 'commons-logging:commons-logging:1.2' 11 | api 'org.joml:joml:1.9.25' // TODO update (warning: 1.10 is breaking) 12 | } 13 | -------------------------------------------------------------------------------- /BlockMap-core/src/main/java/de/piegames/blockmap/DotMinecraft.java: -------------------------------------------------------------------------------- 1 | package de.piegames.blockmap; 2 | 3 | import java.nio.file.Path; 4 | import java.nio.file.Paths; 5 | 6 | /** 7 | * Helper class to find the default {@code .minecraft} folder on different operating systems. 8 | * 9 | * @author piegames 10 | */ 11 | public final class DotMinecraft { 12 | 13 | private DotMinecraft() { 14 | } 15 | 16 | /** 17 | * The default location of the {@code .minecraft} folder. May not actually point to that folder. May point to a non-existing folder or to a 18 | * non-folder. Should point to the actual .minecraft folder for most users. 19 | */ 20 | public static final Path DOTMINECRAFT; 21 | 22 | static { 23 | Path mc = null; 24 | String os = System.getProperty("os.name").toUpperCase(); 25 | if (os.contains("WIN")) { 26 | mc = Paths.get(System.getenv("APPDATA")).resolve(".minecraft"); 27 | } else if (os.contains("MAC")) { 28 | mc = Paths.get(System.getProperty("user.home") + "/Library/Application Support").resolve("minecraft"); 29 | } else if (os.contains("NUX")) { 30 | mc = Paths.get(System.getProperty("user.home"), ".minecraft"); 31 | } else { 32 | mc = Paths.get(System.getProperty("user.dir")); 33 | } 34 | DOTMINECRAFT = mc; 35 | } 36 | } -------------------------------------------------------------------------------- /BlockMap-core/src/main/java/de/piegames/blockmap/MinecraftDimension.java: -------------------------------------------------------------------------------- 1 | package de.piegames.blockmap; 2 | 3 | import java.nio.file.Path; 4 | import java.nio.file.Paths; 5 | 6 | public enum MinecraftDimension { 7 | 8 | OVERWORLD("Overworld", 0, Paths.get("region"), Paths.get("data", "villages.dat"), Paths.get("poi")), 9 | NETHER("Nether", -1, Paths.get("DIM-1", "region"), Paths.get("data", "villages_nether.dat"), Paths.get("DIM-1", "poi")), 10 | END("End", 1, Paths.get("DIM1", "region"), Paths.get("data", "villages_end.dat"), Paths.get("DIM1", "poi")); 11 | 12 | public final int index; 13 | public final String displayName; 14 | public final Path regionPath, villagePath, poiPath; 15 | 16 | MinecraftDimension(String displayName, int index, Path regionPath, Path villagePath, Path poiPath) { 17 | this.displayName = displayName; 18 | this.index = index; 19 | this.regionPath = regionPath; 20 | this.villagePath = villagePath; 21 | this.poiPath = poiPath; 22 | } 23 | 24 | public Path getRegionPath() { 25 | return regionPath; 26 | } 27 | 28 | public Path getVillagePath() { 29 | return villagePath; 30 | } 31 | 32 | public Path getPoiPath() { 33 | return poiPath; 34 | } 35 | 36 | public static MinecraftDimension byName(String uuid) { 37 | switch (uuid) { 38 | case "minecraft:overworld": 39 | return OVERWORLD; 40 | case "minecraft:the_nether": 41 | return NETHER; 42 | case "minecraft:the_end": 43 | return END; 44 | default: 45 | throw new IllegalArgumentException("Unknown dimension ID '" + uuid + "'"); 46 | } 47 | } 48 | 49 | public static MinecraftDimension byID(int id) { 50 | switch (id) { 51 | case -1: 52 | return NETHER; 53 | case 0: 54 | return OVERWORLD; 55 | case 1: 56 | return END; 57 | default: 58 | throw new IllegalArgumentException("Value must be either -1, 0 or 1"); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /BlockMap-core/src/main/java/de/piegames/blockmap/MinecraftVersion.java: -------------------------------------------------------------------------------- 1 | package de.piegames.blockmap; 2 | 3 | import java.io.InputStreamReader; 4 | 5 | import com.google.gson.Gson; 6 | 7 | import de.piegames.blockmap.renderer.BlockState; 8 | 9 | public enum MinecraftVersion { 10 | /* 11 | * See https://minecraft.gamepedia.com/Data_version#List_of_data_versions and 12 | * https://minecraft-de.gamepedia.com/Versionen#Version-IDs 13 | * 14 | * Snapshots and pre-releases usually are not supported. 15 | */ 16 | MC_1_13("1_13", 1519, 1631, "1.13.2", "https://launchermeta.mojang.com/v1/packages/57f0285f5e6800233e3269a93ad11bfb631f2412/1.13.2.json"), 17 | MC_1_14("1_14", 1901, 1976, "1.14.4", "https://launchermeta.mojang.com/v1/packages/d73c3f908365863ebe6b01cf454990182f2652f4/1.14.4.json"), 18 | MC_1_15("1_15", 2200, 2230, "1.15.2", "https://launchermeta.mojang.com/v1/packages/52396828d64eae5bdd01a5ac787234a5f4c85e59/1.15.2.json"), 19 | MC_1_16("1_16", 2566, 2586, "1.16.5", "https://launchermeta.mojang.com/v1/packages/934dfe455cf13141a1ab8a64c57114becba83c6d/1.16.5.json"), 20 | MC_1_17("1_17", 2724, 2730, "1.17.1", "https://launchermeta.mojang.com/v1/packages/1c11f595196065f7311b5f4ffe74a4fde433ff27/1.17.1.json"), 21 | MC_1_18("1_18", 2860, Integer.MAX_VALUE, "1.18.0", "https://launchermeta.mojang.com/v1/packages/cdd1c0f485c0ea5a5aae60d4e62d316b2141f227/1.18.json"); 22 | 23 | public static final MinecraftVersion LATEST = MC_1_18; 24 | 25 | public final int minVersion, maxVersion; 26 | public final String fileSuffix, versionName, manifestURL; 27 | private BlockState states; 28 | 29 | MinecraftVersion(String fileName, int minVersion, int maxVersion, String versionName, String manifestURL) { 30 | this.fileSuffix = fileName; 31 | this.minVersion = minVersion; 32 | this.maxVersion = maxVersion; 33 | this.versionName = versionName; 34 | this.manifestURL = manifestURL; 35 | } 36 | 37 | public BlockState getBlockStates() { 38 | if (states == null) 39 | states = new Gson().fromJson( 40 | new InputStreamReader(BlockState.class.getResourceAsStream("/block-states-" + fileSuffix + ".json")), 41 | BlockState.class 42 | ); 43 | return states; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /BlockMap-core/src/main/java/de/piegames/blockmap/color/BiomeColorMap.java: -------------------------------------------------------------------------------- 1 | package de.piegames.blockmap.color; 2 | 3 | import java.io.InputStreamReader; 4 | import java.io.Reader; 5 | import java.util.Map; 6 | import java.util.Objects; 7 | 8 | import com.google.gson.Gson; 9 | import com.google.gson.GsonBuilder; 10 | 11 | /** 12 | * Represents a mapping from biome IDs to their actual color. 13 | * 14 | * @author piegames 15 | */ 16 | public class BiomeColorMap { 17 | 18 | public static final Gson GSON = new GsonBuilder().registerTypeAdapter(Color.class, Color.ADAPTER).setPrettyPrinting().create(); 19 | public static final BiomeColor MISSING = new BiomeColor(Color.MISSING, Color.MISSING, Color.MISSING, Color.MISSING); 20 | 21 | /** Holds all distinct colors a biome has: its water color, grass color, foliage color and universal color. */ 22 | public static class BiomeColor { 23 | /** The water color in that biome. */ 24 | public Color waterColor; 25 | /** The grass color in that biome. */ 26 | public Color grassColor; 27 | /** The foliage color in that biome. */ 28 | public Color foliageColor; 29 | /** This color does not actually exist in Minecraft. It is used to represent a biome on a map and not to just tint a block's texture. */ 30 | public Color biomeColor; 31 | 32 | public BiomeColor() { 33 | 34 | } 35 | 36 | public BiomeColor(Color waterColor, Color grassColor, Color foliageColor, Color biomeColor) { 37 | this.waterColor = waterColor; 38 | this.grassColor = grassColor; 39 | this.foliageColor = foliageColor; 40 | this.biomeColor = biomeColor; 41 | } 42 | 43 | @Override 44 | public int hashCode() { 45 | return Objects.hash(biomeColor, foliageColor, grassColor, waterColor); 46 | } 47 | 48 | @Override 49 | public boolean equals(Object obj) { 50 | if (this == obj) 51 | return true; 52 | if (obj == null) 53 | return false; 54 | if (getClass() != obj.getClass()) 55 | return false; 56 | BiomeColor other = (BiomeColor) obj; 57 | return Objects.equals(biomeColor, other.biomeColor) && Objects.equals(foliageColor, other.foliageColor) && Objects.equals(grassColor, 58 | other.grassColor) && Objects.equals(waterColor, other.waterColor); 59 | } 60 | } 61 | 62 | protected Map biomeColors; 63 | 64 | @SuppressWarnings("unused") 65 | private BiomeColorMap() { 66 | // For deserialization purposes 67 | } 68 | 69 | public BiomeColor getBiomeColor(String biome) { 70 | return biomeColors.getOrDefault(biome, MISSING); 71 | } 72 | 73 | public BiomeColorMap(Map biomeColors) { 74 | this.biomeColors = Objects.requireNonNull(biomeColors); 75 | } 76 | 77 | @Override 78 | public int hashCode() { 79 | return Objects.hash(biomeColors); 80 | } 81 | 82 | @Override 83 | public boolean equals(Object obj) { 84 | if (this == obj) 85 | return true; 86 | if (obj == null) 87 | return false; 88 | if (getClass() != obj.getClass()) 89 | return false; 90 | BiomeColorMap other = (BiomeColorMap) obj; 91 | return Objects.equals(biomeColors, other.biomeColors); 92 | } 93 | 94 | public static BiomeColorMap load(Reader reader) { 95 | return GSON.fromJson(reader, BiomeColorMap.class); 96 | } 97 | 98 | public static BiomeColorMap loadDefault() { 99 | return load(new InputStreamReader(BiomeColorMap.class.getResourceAsStream("/biome-colors.json"))); 100 | } 101 | } -------------------------------------------------------------------------------- /BlockMap-core/src/main/java/de/piegames/blockmap/color/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Package used for color calculations. The main class is {@link de.piegames.blockmap.color.Color} which is used to represent colors in 3 | * memory and to manipulate them. {@link BlockColorMap} and {@link de.piegames.blockmap.color.BiomeColorMap} represent a mapping from blocks 4 | * and biomes to their respective color used to render the map. 5 | * 6 | * @author piegames 7 | */ 8 | package de.piegames.blockmap.color; -------------------------------------------------------------------------------- /BlockMap-core/src/main/java/de/piegames/blockmap/renderer/Block.java: -------------------------------------------------------------------------------- 1 | package de.piegames.blockmap.renderer; 2 | 3 | import java.util.BitSet; 4 | import java.util.Objects; 5 | 6 | /** 7 | * Each object of this class represents an existing Minecraft block, with its ID and block state, but without its position or object data. 8 | * 9 | * @author piegames 10 | */ 11 | public class Block { 12 | 13 | /** The plain, empty state. DO NOT MODIFY! (There are no immutable bit sets) */ 14 | public static final BitSet STATE_NONE = new BitSet(0); 15 | 16 | public static final Block AIR = new Block("minecraft:air"); 17 | 18 | /** The name/id of the block including the namespace, like in Minecraft. Example: {@code minecraft:air} */ 19 | public final String name; 20 | /** 21 | * The block's state. Each set bit represents one {@code key=value} property the block has. The actual names are not important any more, 22 | * only a unique identification of each state is required for rendering.
23 | * Note that through the way of encoding, there is the possibility to create invalid values (e.g. assigning the same property key multiple 24 | * times or assigning properties to block that can't have it). 25 | */ 26 | public final BitSet state; 27 | 28 | public Block(String name) { 29 | this(name, STATE_NONE); 30 | } 31 | 32 | public Block(String name, BitSet state) { 33 | this.name = Objects.requireNonNull(name); 34 | this.state = state; 35 | } 36 | 37 | /** 38 | * Serialized this block to its compact form 39 | * 40 | * @see #byCompactForm(String) 41 | */ 42 | @Override 43 | public String toString() { 44 | return name + ":" + state.toString(); 45 | } 46 | 47 | @Override 48 | public int hashCode() { 49 | final int prime = 31; 50 | int result = 1; 51 | result = prime * result + ((name == null) ? 0 : name.hashCode()); 52 | result = prime * result + ((state == null) ? 0 : state.hashCode()); 53 | return result; 54 | } 55 | 56 | @Override 57 | public boolean equals(Object obj) { 58 | if (this == obj) 59 | return true; 60 | if (obj == null) 61 | return false; 62 | if (getClass() != obj.getClass()) 63 | return false; 64 | Block other = (Block) obj; 65 | if (name == null) { 66 | if (other.name != null) 67 | return false; 68 | } else if (!name.equals(other.name)) 69 | return false; 70 | if (state == null) { 71 | if (other.state != null) 72 | return false; 73 | } else if (!state.equals(other.state)) 74 | return false; 75 | return true; 76 | } 77 | } -------------------------------------------------------------------------------- /BlockMap-core/src/main/java/de/piegames/blockmap/renderer/BlockState.java: -------------------------------------------------------------------------------- 1 | package de.piegames.blockmap.renderer; 2 | 3 | import java.util.BitSet; 4 | import java.util.Collections; 5 | import java.util.Map; 6 | import java.util.Map.Entry; 7 | import java.util.Objects; 8 | 9 | public class BlockState { 10 | 11 | Map> states; 12 | 13 | @SuppressWarnings("unused") 14 | private BlockState() { 15 | /* Used by GSON */ 16 | } 17 | 18 | public BlockState(Map> states) { 19 | this.states = Objects.requireNonNull(states); 20 | } 21 | 22 | public int getProperty(String key, String value) { 23 | return states.getOrDefault(key, Collections.emptyMap()).get(value); 24 | } 25 | 26 | public BitSet getState(Map properties) { 27 | BitSet state = new BitSet(states.size()); 28 | for (Entry entry : properties.entrySet()) 29 | state.set(getProperty(entry.getKey(), entry.getValue())); 30 | return state; 31 | } 32 | 33 | public int getSize() { 34 | return states.size(); 35 | } 36 | 37 | @Override 38 | public int hashCode() { 39 | return Objects.hash(states); 40 | } 41 | 42 | @Override 43 | public boolean equals(Object obj) { 44 | if (this == obj) 45 | return true; 46 | if (obj == null) 47 | return false; 48 | if (getClass() != obj.getClass()) 49 | return false; 50 | BlockState other = (BlockState) obj; 51 | return Objects.equals(states, other.states); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /BlockMap-core/src/main/java/de/piegames/blockmap/renderer/ChunkRenderer.java: -------------------------------------------------------------------------------- 1 | package de.piegames.blockmap.renderer; 2 | 3 | import java.util.BitSet; 4 | import java.util.Map.Entry; 5 | 6 | import org.joml.Vector2ic; 7 | 8 | import de.piegames.blockmap.MinecraftVersion; 9 | import de.piegames.blockmap.color.BlockColorMap; 10 | import de.piegames.blockmap.color.Color; 11 | import de.piegames.blockmap.world.ChunkMetadata; 12 | import de.piegames.nbt.CompoundTag; 13 | import de.piegames.nbt.StringTag; 14 | import de.piegames.nbt.Tag; 15 | 16 | abstract class ChunkRenderer { 17 | 18 | final MinecraftVersion version; 19 | final RenderSettings settings; 20 | BlockColorMap blockColors; 21 | 22 | ChunkRenderer(MinecraftVersion version, RenderSettings settings) { 23 | this.version = version; 24 | this.settings = settings; 25 | } 26 | 27 | abstract ChunkMetadata renderChunk(Vector2ic chunkPosRegion, Vector2ic chunkPosWorld, CompoundTag level, Color[] map, int[] height, String[] regionBiomes); 28 | 29 | protected static BitSet parseBlockState(CompoundTag properties, BlockState state) { 30 | BitSet ret = new BitSet(state.getSize()); 31 | if (properties != null) 32 | for (Entry> entry : properties.getValue().entrySet()) 33 | ret.set(state.getProperty(entry.getKey(), ((StringTag) entry.getValue()).getValue())); 34 | return ret; 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /BlockMap-core/src/main/java/de/piegames/blockmap/renderer/RenderSettings.java: -------------------------------------------------------------------------------- 1 | package de.piegames.blockmap.renderer; 2 | 3 | import java.util.Map; 4 | import java.util.Objects; 5 | import java.util.stream.Collectors; 6 | 7 | import de.piegames.blockmap.MinecraftVersion; 8 | import de.piegames.blockmap.color.BiomeColorMap; 9 | import de.piegames.blockmap.color.BlockColorMap; 10 | import de.piegames.blockmap.color.BlockColorMap.InternalColorMap; 11 | import de.piegames.blockmap.renderer.RegionShader.DefaultShader; 12 | import de.piegames.blockmap.renderer.RegionShader.ReliefShader; 13 | import io.gsonfire.annotations.ExposeMethodParam; 14 | 15 | public class RenderSettings { 16 | 17 | public int minX = Integer.MIN_VALUE; 18 | public int maxX = Integer.MAX_VALUE; 19 | public int minY = Integer.MIN_VALUE; 20 | public int maxY = Integer.MAX_VALUE; 21 | public int minZ = Integer.MIN_VALUE; 22 | public int maxZ = Integer.MAX_VALUE; 23 | 24 | public Map blockColors; 25 | public BiomeColorMap biomeColors; 26 | public RegionShader regionShader = new ReliefShader(); 27 | 28 | public RenderSettings() { 29 | loadDefaultColors(); 30 | } 31 | 32 | public RenderSettings(int minX, int maxX, int minY, int maxY, int minZ, int maxZ, Map blockColors, 33 | BiomeColorMap biomeColors, RegionShader regionShader) { 34 | this.minX = minX; 35 | this.maxX = maxX; 36 | this.minY = minY; 37 | this.maxY = maxY; 38 | this.minZ = minZ; 39 | this.maxZ = maxZ; 40 | this.blockColors = blockColors; 41 | this.biomeColors = biomeColors; 42 | this.regionShader = regionShader; 43 | } 44 | 45 | public void loadDefaultColors() { 46 | blockColors = InternalColorMap.DEFAULT.getColorMap(); 47 | biomeColors = BiomeColorMap.loadDefault(); 48 | } 49 | 50 | @ExposeMethodParam("block colors") 51 | public void loadBlockColors(String name) { 52 | blockColors = InternalColorMap.valueOf(name).getColorMap(); 53 | } 54 | 55 | @ExposeMethodParam("shader") 56 | public void loadShader(DefaultShader shader) { 57 | this.regionShader = shader.getShader(); 58 | } 59 | 60 | @Override 61 | public int hashCode() { 62 | /* 63 | * The keys of blockColors are an enum type. Sadly, for some reason, there is no possibility to override the hashCode function for enums. 64 | * This means, that the keys of the map will always get different hash codes at runtime. To avoid this, we replace them with their ordinal 65 | * for the hash calculation. 66 | */ 67 | return Objects.hash( 68 | biomeColors, 69 | blockColors.entrySet() 70 | .stream() 71 | .collect(Collectors.toMap(e -> e.getKey().ordinal(), Map.Entry::getValue)), 72 | maxX, maxY, maxZ, minX, minY, minZ, regionShader); 73 | } 74 | 75 | @Override 76 | public boolean equals(Object obj) { 77 | if (this == obj) 78 | return true; 79 | if (obj == null) 80 | return false; 81 | if (getClass() != obj.getClass()) 82 | return false; 83 | RenderSettings other = (RenderSettings) obj; 84 | return Objects.equals(biomeColors, other.biomeColors) && Objects.equals(blockColors, other.blockColors) && maxX == other.maxX && maxY == other.maxY 85 | && maxZ == other.maxZ && minX == other.minX && minY == other.minY && minZ == other.minZ && Objects.equals(regionShader, other.regionShader); 86 | } 87 | } -------------------------------------------------------------------------------- /BlockMap-core/src/main/java/de/piegames/blockmap/world/Region.java: -------------------------------------------------------------------------------- 1 | package de.piegames.blockmap.world; 2 | 3 | import java.awt.image.BufferedImage; 4 | import java.util.Map; 5 | import java.util.Objects; 6 | 7 | import org.joml.Vector2ic; 8 | 9 | /** Objects of this class represent a rendered Minecraft region. The image data is stored as {@link BufferedImage} in memory. */ 10 | public class Region { 11 | protected Vector2ic position; 12 | protected BufferedImage image; 13 | protected Map metadata; 14 | 15 | public Region(Vector2ic position, BufferedImage image, Map metadata) { 16 | this.position = Objects.requireNonNull(position); 17 | this.image = Objects.requireNonNull(image); 18 | this.metadata = Objects.requireNonNull(metadata); 19 | } 20 | 21 | public Vector2ic getPosition() { 22 | return position; 23 | } 24 | 25 | public BufferedImage getImage() { 26 | return image; 27 | } 28 | 29 | public Map getChunkMetadata() { 30 | return metadata; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /BlockMap-core/src/main/java/de/piegames/blockmap/world/ServerMetadata.java: -------------------------------------------------------------------------------- 1 | package de.piegames.blockmap.world; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import java.util.Optional; 6 | import java.util.Set; 7 | 8 | import com.google.gson.annotations.SerializedName; 9 | 10 | public class ServerMetadata { 11 | 12 | /** This will be used in the future to keep track of old serialized files. */ 13 | int version = 0; 14 | @SerializedName("server name") 15 | public Optional name; 16 | @SerializedName("server description") 17 | public Optional description; 18 | @SerializedName("server address") 19 | public Optional ipAddress; 20 | @SerializedName("server icon") 21 | public Optional iconLocation; 22 | @SerializedName("online players") 23 | public Optional> onlinePlayers; 24 | public List levels = new ArrayList<>(); 25 | @SerializedName("max players") 26 | public int maxPlayers; 27 | 28 | public ServerMetadata() { 29 | this(null, null, null, null, null, -1); 30 | } 31 | 32 | public ServerMetadata(String name, String description, String ipAddress, String iconLocation, Set onlinePlayers, int maxPlayers) { 33 | this.name = Optional.ofNullable(name); 34 | this.description = Optional.ofNullable(description); 35 | this.ipAddress = Optional.ofNullable(ipAddress); 36 | this.iconLocation = Optional.ofNullable(iconLocation); 37 | this.onlinePlayers = Optional.ofNullable(onlinePlayers); 38 | this.maxPlayers = maxPlayers; 39 | } 40 | 41 | public static class ServerLevel { 42 | public String name; 43 | public String path; 44 | 45 | public ServerLevel() { 46 | 47 | } 48 | 49 | public ServerLevel(String name, String path) { 50 | this.name = name; 51 | this.path = path; 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /BlockMap-gui/build.gradle: -------------------------------------------------------------------------------- 1 | import static org.apache.tools.ant.taskdefs.condition.Os.* 2 | 3 | plugins { 4 | id 'org.openjfx.javafxplugin' version '0.0.10' 5 | id 'application' 6 | id 'com.github.johnrengelman.shadow' 7 | } 8 | 9 | javafx { 10 | version = "17" 11 | modules = [ 'javafx.controls', 'javafx.fxml', 'javafx.swing' ] 12 | //configuration = 'compileOnly' 13 | } 14 | 15 | dependencies { 16 | implementation project(':BlockMap-core') 17 | // GUI dependencies 18 | implementation 'org.controlsfx:controlsfx:11.1.1' 19 | implementation 'com.github.piegamesde:gson-fire:9210521426' 20 | implementation group: 'com.github.haifengl', name: 'smile-core', version: '1.5.3' // TODO upgrade to v2.x 21 | implementation group: 'com.codepoetics', name: 'protonpack', version: '1.16' // TODO upgrade to v2.x 22 | implementation 'com.github.saibotk:JMAW:0.3.1' 23 | 24 | // GUI standalone dependencies 25 | implementation 'com.google.guava:guava:31.0.1-jre' 26 | implementation 'net.dongliu:gson-java8-datatype:1.1.0' 27 | implementation 'io.github.soc:directories:12' 28 | implementation 'me.xdrop:fuzzywuzzy:1.3.3' 29 | implementation 'org.apache.logging.log4j:log4j-core:2.17.1' 30 | implementation 'org.apache.logging.log4j:log4j-jcl:2.17.1' 31 | } 32 | 33 | application { 34 | mainClassName = "de.piegames.blockmap.gui.standalone.GuiMainLauncher" 35 | applicationDefaultJvmArgs = ['--add-exports=javafx.base/com.sun.javafx.event=org.controlsfx.controls'] 36 | } 37 | 38 | run { 39 | jvmArgs = ['--add-exports=javafx.base/com.sun.javafx.event=ALL-UNNAMED'] 40 | // jvmArgs = ['--add-exports=javafx.base/com.sun.javafx.event=org.controlsfx.controls'] 41 | } 42 | 43 | // Hide tasks 44 | knows.group = null 45 | 46 | jar { 47 | archiveVersion = "" 48 | archiveClassifier = "${OS_NAME}" 49 | manifest { 50 | attributes 'Implementation-Title': 'BlockMap', 51 | 'Main-Class': 'de.piegames.blockmap.gui.standalone.GuiMainLauncher' 52 | } 53 | } 54 | 55 | shadowJar { 56 | destinationDir = file("build/libs/fat") 57 | archiveClassifier = "${OS_NAME}" 58 | } 59 | 60 | task versionlessJar(type: Jar) { 61 | archiveVersion = "" 62 | } 63 | -------------------------------------------------------------------------------- /BlockMap-gui/src/main/java/de/piegames/blockmap/gui/DisplaySettings.java: -------------------------------------------------------------------------------- 1 | package de.piegames.blockmap.gui; 2 | 3 | import org.joml.AABBd; 4 | 5 | public class DisplaySettings { 6 | // public int threadCount; 7 | // public int overDraw; 8 | // public int diskCacheSize; 9 | // public int memoryCacheSize; 10 | // public int maxZoomInLevel; 11 | // public int maxZoomOutLevel; 12 | // public float loadingFrustum; 13 | public AABBd viewRestrictionInner; 14 | public AABBd viewRestrictionOuter; 15 | 16 | public DisplaySettings() { 17 | } 18 | } -------------------------------------------------------------------------------- /BlockMap-gui/src/main/java/de/piegames/blockmap/gui/MapPane.java: -------------------------------------------------------------------------------- 1 | package de.piegames.blockmap.gui; 2 | 3 | import java.util.Objects; 4 | 5 | import javafx.collections.FXCollections; 6 | import javafx.collections.ListChangeListener; 7 | import javafx.collections.ObservableList; 8 | import javafx.event.Event; 9 | import javafx.event.EventHandler; 10 | import javafx.event.EventType; 11 | import javafx.scene.Node; 12 | import javafx.scene.layout.Region; 13 | import javafx.scene.layout.StackPane; 14 | 15 | /** 16 | * This {@link StackPane} wraps a {@link WorldRendererCanvas} together with nodes overlaid on top of it. These nodes are grouped into event 17 | * classes depending on their functionality. 18 | * 19 | * @see settingsLayers 20 | * @see pinLayers 21 | * @see decorationLayers 22 | */ 23 | public class MapPane extends StackPane { 24 | 25 | /** 26 | * Nodes placed here are normal children of the stack pane. This is intended to be used for GUI elements that are shown above the map, but 27 | * are not affected by the map's position and scale. This set of layers is the topmost one. 28 | */ 29 | public final ObservableList settingsLayers = FXCollections.observableArrayList(); 30 | /** 31 | * (Mouse) events that reach these nodes are caught, duplicated and sent to all nodes in the decoration layers. This is intended to be used 32 | * for GUI elements that are shown on the map (e.g. pins) without interfering with the map's navigation. If node A obstructs node B, events 33 | * accepted by node A will be passed on to decoration layers but not to B. This set of layers is between {@link #settingsLayers} and 34 | * {@link #decorationLayers}. 35 | */ 36 | public final ObservableList pinLayers = FXCollections.observableArrayList(); 37 | /** 38 | * Nodes placed here receive all (mouse) events that target the map or any of the {@link #settingsLayers}. This is intended to be used for 39 | * decorative map overlays and map navigation through event listener (e.g. selecting an area on the map). Note that since all of the events 40 | * that land here are duplicated ones, they won't reach any children of the nodes added here. This set of layers is between 41 | * {@link #settingsLayers} and the map. 42 | */ 43 | public final ObservableList decorationLayers = FXCollections.observableArrayList(); 44 | 45 | protected Region catchEvents; 46 | 47 | public final WorldRendererCanvas renderer; 48 | 49 | public MapPane(WorldRendererCanvas renderer) { 50 | this.renderer = Objects.requireNonNull(renderer); 51 | catchEvents = new Region(); 52 | ListChangeListener l = c -> { 53 | getChildren().clear(); 54 | getChildren().addAll(decorationLayers); 55 | getChildren().add(catchEvents); 56 | getChildren().addAll(pinLayers); 57 | getChildren().addAll(settingsLayers); 58 | }; 59 | settingsLayers.addListener(l); 60 | pinLayers.addListener(l); 61 | decorationLayers.addListener(l); 62 | 63 | catchEvents.setPickOnBounds(true); 64 | EventHandler refireEvent = event -> decorationLayers.forEach(l1 -> l1.fireEvent(event)); 65 | catchEvents.addEventFilter(EventType.ROOT, refireEvent); 66 | pinLayers.addListener((ListChangeListener) e -> { 67 | while (e.next()) { 68 | e.getRemoved().forEach(n -> n.removeEventFilter(EventType.ROOT, refireEvent)); 69 | e.getAddedSubList().forEach(n -> n.addEventFilter(EventType.ROOT, refireEvent)); 70 | } 71 | }); 72 | 73 | decorationLayers.add(renderer); 74 | } 75 | } -------------------------------------------------------------------------------- /BlockMap-gui/src/main/java/de/piegames/blockmap/gui/RenderedRegion.java: -------------------------------------------------------------------------------- 1 | package de.piegames.blockmap.gui; 2 | 3 | import org.joml.AABBd; 4 | import org.joml.Vector2i; 5 | import org.joml.Vector2ic; 6 | 7 | import javafx.scene.canvas.GraphicsContext; 8 | import javafx.scene.image.PixelReader; 9 | import javafx.scene.image.PixelWriter; 10 | import javafx.scene.image.WritableImage; 11 | 12 | public class RenderedRegion { 13 | 14 | /* Set to null if the region has no pixels (all transparent) */ 15 | protected WritableImage[] images; 16 | public final Vector2ic position; 17 | protected boolean isRendered = false; 18 | 19 | public RenderedRegion(Vector2ic position) { 20 | this.position = new Vector2i(position); 21 | this.images = new WritableImage[6]; 22 | } 23 | 24 | /** Set this region's image and also calculate down-scaled versions */ 25 | public void setImage(WritableImage image) { 26 | check: { /* Check if the image is empty to save RAM */ 27 | var reader = image.getPixelReader(); 28 | for (int y = 0; y < 512; y++) 29 | for (int x = 0; x < 512; x++) { 30 | int argb = reader.getArgb(x, y); 31 | if (argb != 0) 32 | break check; 33 | } 34 | /* The image is transparent */ 35 | this.images = null; 36 | this.isRendered = true; 37 | return; 38 | } 39 | 40 | /* We do 5 MIP map levels, giving 1:32 zoom at most. This corresponds to 1 pixel per chunk. */ 41 | this.images[0] = image; 42 | for (int i = 1; i <= 5; i++) { 43 | var size = 512 >>> i; 44 | var nextImage = new WritableImage(size, size); 45 | 46 | PixelWriter writer = nextImage.getPixelWriter(); 47 | PixelReader reader = image.getPixelReader(); 48 | for (int y = 0; y < size; y++) 49 | for (int x = 0; x < size; x++) { 50 | int c1 = reader.getArgb(x << 1, y << 1), 51 | c2 = reader.getArgb((x << 1) + 1, y << 1), 52 | c3 = reader.getArgb(x << 1, (y << 1) + 1), 53 | c4 = reader.getArgb((x << 1) + 1, (y << 1) + 1); 54 | // TODO pre-multiply alpha to avoid dark edges 55 | long argb = 0;// use long against overflow 56 | long a1 = c1 >>> 24, a2 = c2 >>> 24, a3 = c3 >>> 24, a4 = c4 >>> 24; 57 | // alpha 58 | argb |= ((a1 + a2 + a3 + a4) << 22) & 0xFF000000; 59 | // red 60 | argb |= ((((c1 & 0x00FF0000) * a1 + (c2 & 0x00FF0000) * a2 + (c3 & 0x00FF0000) * a3 + (c4 & 0x00FF0000) * a4) / 255) >> 2) & 0x00FF0000; 61 | // green 62 | argb |= ((((c1 & 0x0000FF00) * a1 + (c2 & 0x0000FF00) * a2 + (c3 & 0x0000FF00) * a3 + (c4 & 0x0000FF00) * a4) / 255) >> 2) & 0x0000FF00; 63 | // blue 64 | argb |= ((((c1 & 0x000000FF) * a1 + (c2 & 0x000000FF) * a2 + (c3 & 0x000000FF) * a3 + (c4 & 0x000000FF) * a4) / 255) >> 2) & 0x000000FF; 65 | 66 | writer.setArgb(x, y, (int) argb); 67 | } 68 | 69 | image = nextImage; 70 | this.images[i] = image; 71 | } 72 | this.isRendered = true; 73 | } 74 | 75 | public boolean isVisible(AABBd frustum) { 76 | int size = 512; 77 | return frustum.intersectsAABB(new AABBd(position.x() * size, position.y() * size, 0, (position.x() + 1) * size, (position.y() + 1) * size, 0)); 78 | } 79 | 80 | public void draw(GraphicsContext gc, AABBd frustum, double scale, int level) { 81 | if (this.isRendered) { 82 | int size = 512; 83 | if (images != null) 84 | gc.drawImage(images[level], position.x() * size, position.y() * size, size, size); 85 | } else { 86 | drawBackground(gc, scale); 87 | } 88 | } 89 | 90 | /** This method assumes the appropriate fill is already set */ 91 | public void drawBackground(GraphicsContext gc, double scale) { 92 | int size = 512; 93 | gc.fillRect(position.x() * size - 1 / scale, position.y() * size - 1 / scale, size + 2 / scale, size + 2 / scale); 94 | } 95 | 96 | public void drawForeground(GraphicsContext gc, AABBd frustum, double scale) { 97 | double x = position.x() * 512, y = position.y() * 512, w = 512, h = 512, m = Math.min(6 / scale, 35); 98 | double xw = Math.min(frustum.maxX, x + w); 99 | double yh = Math.min(frustum.maxY, y + h); 100 | x = Math.max(frustum.minX, x); 101 | y = Math.max(frustum.minY, y); 102 | w = xw - x; 103 | h = yh - y; 104 | 105 | // gc.translate(x, y); 106 | gc.fillRect(x, y, w, m); 107 | gc.fillRect(x, y + h - m, w, m); 108 | gc.fillRect(x, y, m, h); 109 | gc.fillRect(x + w - m, y, m, h); 110 | } 111 | } -------------------------------------------------------------------------------- /BlockMap-gui/src/main/java/de/piegames/blockmap/gui/ResizableCanvas.java: -------------------------------------------------------------------------------- 1 | package de.piegames.blockmap.gui; 2 | 3 | import javafx.application.Platform; 4 | import javafx.scene.canvas.Canvas; 5 | import javafx.scene.canvas.GraphicsContext; 6 | 7 | public abstract class ResizableCanvas extends Canvas { 8 | 9 | protected GraphicsContext gc; 10 | 11 | public ResizableCanvas() { 12 | gc = getGraphicsContext2D(); 13 | } 14 | 15 | /** Queues in a repaint event calling {@link render} from the JavaFX Application Thread */ 16 | public final void repaint() { 17 | if (Platform.isFxApplicationThread()) 18 | render(); 19 | else 20 | Platform.runLater(this::render); 21 | } 22 | 23 | /** Requires to be called from the JavaFX Application Thread. */ 24 | protected abstract void render(); 25 | 26 | @Override 27 | public boolean isResizable() { 28 | return true; 29 | } 30 | 31 | @Override 32 | public double maxHeight(double width) { 33 | return Double.POSITIVE_INFINITY; 34 | } 35 | 36 | @Override 37 | public double maxWidth(double height) { 38 | return Double.POSITIVE_INFINITY; 39 | } 40 | 41 | @Override 42 | public double minWidth(double height) { 43 | return 0; 44 | } 45 | 46 | @Override 47 | public double minHeight(double width) { 48 | return 0; 49 | } 50 | 51 | @Override 52 | public void resize(double width, double height) { 53 | this.setWidth(width); 54 | this.setHeight(height); 55 | } 56 | } -------------------------------------------------------------------------------- /BlockMap-gui/src/main/java/de/piegames/blockmap/gui/VersionProvider.java: -------------------------------------------------------------------------------- 1 | package de.piegames.blockmap.gui; 2 | 3 | public class VersionProvider { 4 | /* This part of the code gets generated automatically through `gradle generateSources`. Do not modify! */ 5 | // $REPLACE_START 6 | public static final String VERSION = "2.4.1"; 7 | // $REPLACE_END 8 | } 9 | -------------------------------------------------------------------------------- /BlockMap-gui/src/main/java/de/piegames/blockmap/gui/decoration/DragScrollDecoration.java: -------------------------------------------------------------------------------- 1 | package de.piegames.blockmap.gui.decoration; 2 | 3 | import org.joml.Vector2d; 4 | import org.joml.Vector2dc; 5 | 6 | import de.piegames.blockmap.gui.DisplayViewport; 7 | import javafx.scene.input.MouseButton; 8 | import javafx.scene.layout.Region; 9 | import javafx.scene.robot.Robot; 10 | 11 | /** 12 | * This decoration provides basic drag and zoom support, where you can set the button used for dragging as well as zoom speed and direction. 13 | * Both functionalities are optional and can be disabled separately. If you disable both, this class will still forward mouse moved events 14 | * to the view frustum. This might be useful if you use manual zooming logic externally and still want to zoom around the mouse center as it 15 | * does normally. 16 | */ 17 | public class DragScrollDecoration extends Region { 18 | 19 | protected Robot robot; 20 | protected int cooldown; 21 | 22 | /** Creates an instance of this class that will drag with the right mouse button and a scroll factor of 1/10. */ 23 | public DragScrollDecoration(DisplayViewport frustum) { 24 | this(frustum, MouseButton.SECONDARY, 0.01d); 25 | } 26 | 27 | public DragScrollDecoration(DisplayViewport frustum, boolean allowDrag, boolean allowZoom) { 28 | this(frustum, allowDrag ? MouseButton.SECONDARY : null, allowZoom ? 0.01d : 0); 29 | } 30 | 31 | /** 32 | * @param dragButton 33 | * The button that must be pressed to activate dragging. null will disable dragging. 34 | * @param scrollFactor 35 | * Higher values will increase the zoomed amount per scroll. Zero deactivates scrolling. Negative values invert the scroll 36 | * direction. 37 | */ 38 | public DragScrollDecoration(DisplayViewport frustum, MouseButton dragButton, double scrollFactor) { 39 | setOnMouseMoved(e -> frustum.mousePosProperty.set(new Vector2d(e.getX(), e.getY()))); 40 | setOnMouseDragged(e -> { 41 | Vector2dc old = frustum.mousePosProperty.get(); 42 | Vector2d current = new Vector2d(e.getX(), e.getY()); 43 | if (cooldown <= 0) { 44 | if (dragButton != null && e.getButton() == dragButton) { 45 | frustum.pan(current.x() - old.x(), current.y() - old.y()); 46 | double width = getWidth(); 47 | double height = getHeight(); 48 | double dx = 0, dy = 0; 49 | if (current.x() < 1) 50 | dx = width - 3; 51 | if (current.x() > width - 2) 52 | dx = -width + 4; 53 | if (current.y() < 1) 54 | dy = height - 3; 55 | if (current.y() > height - 2) 56 | dy = -height + 4; 57 | if (dx != 0 || dy != 0) { 58 | current.add(dx, dy); 59 | /* Lazy initialization */ 60 | if (robot == null) 61 | robot = new Robot(); 62 | robot.mouseMove((int) (e.getScreenX() + dx), (int) (e.getScreenY() + dy)); 63 | cooldown = 3; 64 | } 65 | } 66 | } else if (cooldown > 0) 67 | cooldown--; 68 | frustum.mousePosProperty.set(current); 69 | }); 70 | 71 | if (scrollFactor != 0) 72 | setOnScroll(e -> frustum.mouseScroll(e.getDeltaY() * scrollFactor)); 73 | } 74 | } -------------------------------------------------------------------------------- /BlockMap-gui/src/main/java/de/piegames/blockmap/gui/decoration/GridDecoration.java: -------------------------------------------------------------------------------- 1 | package de.piegames.blockmap.gui.decoration; 2 | 3 | import java.util.Objects; 4 | 5 | import org.joml.AABBd; 6 | import org.joml.Vector2dc; 7 | 8 | import de.piegames.blockmap.gui.ResizableCanvas; 9 | import de.piegames.blockmap.gui.DisplayViewport; 10 | import javafx.scene.paint.Color; 11 | 12 | public class GridDecoration extends ResizableCanvas { 13 | 14 | protected final DisplayViewport viewport; 15 | 16 | public GridDecoration(DisplayViewport viewport) { 17 | this.viewport = Objects.requireNonNull(viewport); 18 | viewport.frustumProperty.addListener(e -> repaint()); 19 | visibleProperty().addListener(e -> repaint()); 20 | } 21 | 22 | @Override 23 | protected void render() { 24 | if (!isVisible()) 25 | return; 26 | gc.clearRect(0, 0, getWidth(), getHeight()); 27 | 28 | double scale = viewport.scaleProperty.get(); 29 | gc.save(); 30 | gc.scale(scale, scale); 31 | Vector2dc translation = viewport.getTranslation(); 32 | gc.translate(translation.x(), translation.y()); 33 | 34 | AABBd frustum = viewport.frustumProperty.get(); 35 | 36 | if (scale >= 1.5 * 0.9) { 37 | double w = Math.min(2 / scale, 0.5); 38 | double a = 1 - Math.min(1.5 / scale, 1); 39 | gc.setStroke(new Color(0.4f, 0.4f, 0.4f, 0.6f * a)); 40 | gc.setLineWidth(w); 41 | for (int x = (int) frustum.minX - 1; x <= frustum.maxX; x++) 42 | gc.strokeLine(x, frustum.minY, x, frustum.maxY); 43 | for (int y = (int) frustum.minY - 1; y <= frustum.maxY; y++) 44 | gc.strokeLine(frustum.minX, y, frustum.maxX, y); 45 | } 46 | 47 | if (scale > 0.2 * 0.9) { 48 | double w = Math.min(3 / scale, 3); 49 | double a = 1 - Math.min(0.2 / scale, 1); 50 | gc.setStroke(new Color(0.3f, 0.3f, 0.3f, 0.6f * a)); 51 | gc.setLineWidth(w); 52 | for (int x = (int) frustum.minX & 0xFFFFFFF0; x <= frustum.maxX; x += 16) 53 | gc.strokeLine(x, frustum.minY, x, frustum.maxY); 54 | for (int y = (int) frustum.minY & 0xFFFFFFF0; y <= frustum.maxY; y += 16) 55 | gc.strokeLine(frustum.minX, y, frustum.maxX, y); 56 | } 57 | 58 | { 59 | double w = Math.min(5 / scale, 30); 60 | gc.setStroke(new Color(0.2f, 0.2f, 0.2f, 0.6f)); 61 | gc.setLineWidth(w); 62 | for (int x = (int) frustum.minX & 0xFFFFFE00; x <= frustum.maxX; x += 512) 63 | for (int y = (int) frustum.minY & 0xFFFFFE00; y <= frustum.maxY; y += 512) { 64 | gc.strokeRect(x, y, 512, 512); 65 | } 66 | } 67 | 68 | gc.restore(); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /BlockMap-gui/src/main/java/de/piegames/blockmap/gui/decoration/ScaleDecoration.java: -------------------------------------------------------------------------------- 1 | package de.piegames.blockmap.gui.decoration; 2 | 3 | import java.util.Objects; 4 | 5 | import de.piegames.blockmap.gui.ResizableCanvas; 6 | import de.piegames.blockmap.gui.DisplayViewport; 7 | import javafx.scene.effect.BlurType; 8 | import javafx.scene.effect.DropShadow; 9 | import javafx.scene.effect.GaussianBlur; 10 | import javafx.scene.paint.Color; 11 | import javafx.scene.paint.CycleMethod; 12 | import javafx.scene.paint.LinearGradient; 13 | import javafx.scene.paint.Stop; 14 | 15 | public class ScaleDecoration extends ResizableCanvas { 16 | 17 | protected final DisplayViewport viewport; 18 | 19 | public ScaleDecoration(DisplayViewport viewport) { 20 | this.viewport = Objects.requireNonNull(viewport); 21 | viewport.frustumProperty.addListener(e -> repaint()); 22 | visibleProperty().addListener(e -> repaint()); 23 | setCache(true); 24 | setMouseTransparent(true); 25 | } 26 | 27 | @Override 28 | protected void render() { 29 | double[] scale1 = new double[] { 0, 0.1, 1, 5, 10, 50, 100, 500, 1000, 2000, 5000, 10000, 20000, 50000 }; 30 | String[] text1 = new String[] { "", "10cm", "1m", "5m", "10m", "50m", "100m", "500m", "1km", "2km", "5km", "10km", "20km", "50km" }; 31 | double[] scale2 = new double[] { 0, 1, 16, 512, 1024, 2048, 4096 }; 32 | String[] text2 = new String[] { "", "1 block", "1 chunk", "1 region", "2 regions", "4 regions", "8 regions" }; 33 | 34 | if (!isVisible()) 35 | return; 36 | gc.clearRect(0, 0, getWidth(), getHeight()); 37 | 38 | double scale = viewport.scaleProperty.get(); 39 | gc.save(); 40 | 41 | gc.setEffect(new GaussianBlur(20)); 42 | // gc.setFill(Color.WHITE.deriveColor(0, 1, 1, 0.5)); 43 | gc.setFill(new LinearGradient(0, 0.5, 1, 0.5, true, CycleMethod.NO_CYCLE, new Stop(0, Color.TRANSPARENT), new Stop(0.8, Color.grayRgb(255, 0.5)))); 44 | gc.fillRect(getWidth() - 500, getHeight() - 130, 500 - 30, 130); 45 | gc.setEffect(null); 46 | gc.setFill(Color.BLACK); 47 | gc.setLineWidth(3); 48 | final double MAX_POS = Math.min(500, Math.max(getWidth() - 100, 0)); 49 | 50 | DropShadow ds = new DropShadow(); 51 | ds.setRadius(50); 52 | ds.setSpread(0.9); 53 | ds.setBlurType(BlurType.GAUSSIAN); 54 | ds.setColor(Color.WHITE); 55 | // Text t = new Text(); 56 | // t.setEffect(ds); 57 | // t.setCache(true); 58 | // t.setX(10.0f); 59 | // t.setY(270.0f); 60 | // t.setFill(Color.RED); 61 | // t.setText("JavaFX drop shadow..."); 62 | // t.setFont(Font.font(null, FontWeight.BOLD, 32)); 63 | 64 | double max = 0; 65 | for (int i = 0; i < scale1.length; i++) { 66 | double pos = scale * scale1[i]; 67 | if (pos < MAX_POS) { 68 | gc.strokeLine(getWidth() - 50 - pos, getHeight() - 50, getWidth() - 50 - pos, getHeight() - 40); 69 | if (pos > 8) { 70 | gc.save(); 71 | gc.translate(getWidth() - 60 - pos, getHeight() - 30); 72 | gc.rotate(50); 73 | gc.fillText(text1[i], 0, 0); 74 | gc.restore(); 75 | } 76 | max = Math.max(max, pos); 77 | } 78 | } 79 | for (int i = 0; i < scale2.length; i++) { 80 | double pos = scale * scale2[i]; 81 | if (pos < MAX_POS) { 82 | gc.strokeLine(getWidth() - 50 - pos, getHeight() - 60, getWidth() - 50 - pos, getHeight() - 50); 83 | if (pos > 8) { 84 | gc.save(); 85 | gc.translate(getWidth() - 60 - pos, getHeight() - 70); 86 | gc.rotate(-50); 87 | gc.fillText(text2[i], 0, 0); 88 | gc.restore(); 89 | } 90 | max = Math.max(max, pos); 91 | } 92 | } 93 | 94 | gc.strokeLine(getWidth() - 50, getHeight() - 50, getWidth() - 50 - max, getHeight() - 50); 95 | gc.restore(); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /BlockMap-gui/src/main/java/de/piegames/blockmap/gui/decoration/SelectRectangleDecoration.java: -------------------------------------------------------------------------------- 1 | package de.piegames.blockmap.gui.decoration; 2 | 3 | import java.util.Objects; 4 | import org.joml.Rectangled; 5 | import org.joml.Vector2d; 6 | import org.joml.Vector2dc; 7 | 8 | import de.piegames.blockmap.gui.ResizableCanvas; 9 | import de.piegames.blockmap.gui.DisplayViewport; 10 | import javafx.scene.input.MouseButton; 11 | import javafx.scene.paint.Color; 12 | 13 | public class SelectRectangleDecoration extends ResizableCanvas { 14 | 15 | protected final DisplayViewport frustum; 16 | 17 | protected final Vector2d dragStart = new Vector2d(), dragEnd = new Vector2d(); 18 | 19 | public SelectRectangleDecoration(DisplayViewport frustum, Rectangled initialSelection) { 20 | this(frustum, MouseButton.PRIMARY, initialSelection); 21 | } 22 | 23 | public SelectRectangleDecoration(DisplayViewport frustum, MouseButton selectButton, Rectangled initialSelection) { 24 | this.frustum = Objects.requireNonNull(frustum); 25 | Objects.requireNonNull(selectButton); 26 | frustum.frustumProperty.addListener(e -> repaint()); 27 | 28 | if (initialSelection == null) 29 | initialSelection = new Rectangled(); 30 | dragStart.set(initialSelection.minX, initialSelection.minY); 31 | dragEnd.set(initialSelection.maxX, initialSelection.maxY); 32 | 33 | setOnMousePressed(e -> { 34 | if (e.getButton() == selectButton) { 35 | dragStart.set(frustum.getMouseInWorld()); 36 | dragEnd.set(dragStart); 37 | repaint(); 38 | } 39 | }); 40 | setOnMouseDragged(e -> { 41 | if (e.getButton() == selectButton) 42 | dragEnd.set(frustum.getMouseInWorld()); 43 | repaint(); 44 | }); 45 | gc.setLineWidth(2); 46 | gc.setFill(Color.BLUE.deriveColor(-35, .6, 1.3, 0.5)); 47 | gc.setStroke(Color.BLUE.deriveColor(0, 0.8, 1.3, 0.8)); 48 | } 49 | 50 | @Override 51 | public void render() { 52 | gc.clearRect(0, 0, getWidth(), getHeight()); 53 | 54 | double scale = frustum.scaleProperty.get(); 55 | gc.save(); 56 | gc.scale(scale, scale); 57 | Vector2dc translation = frustum.getTranslation(); 58 | gc.translate(translation.x(), translation.y()); 59 | 60 | gc.fillRect(Math.min(dragStart.x, dragEnd.x), Math.min(dragStart.y, dragEnd.y), Math.abs(dragStart.x - dragEnd.x), Math.abs(dragStart.y - 61 | dragEnd.y)); 62 | gc.strokeRect(Math.min(dragStart.x, dragEnd.x), Math.min(dragStart.y, dragEnd.y), Math.abs(dragStart.x - dragEnd.x), Math.abs(dragStart.y - 63 | dragEnd.y)); 64 | 65 | gc.restore(); 66 | } 67 | 68 | public Rectangled getSelected() { 69 | return new Rectangled(Math.min(dragStart.x, dragEnd.x), Math.min(dragStart.y, dragEnd.y), Math.max(dragStart.x, dragEnd.x), Math.max(dragStart.y, dragEnd.y)); 70 | } 71 | } -------------------------------------------------------------------------------- /BlockMap-gui/src/main/java/de/piegames/blockmap/gui/decoration/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | *

3 | * A Decoration is a {@link javafx.scene.Node} (typically a subclass of {@link javafx.scene.layout.Region}), that is part of the internal stack of the 4 | * {@link de.piegames.blockmap.gui.MapPane}. Multiple decorations are added on top of each other and may provide additional functionality or visual 5 | * information to the world map. They all have the same size as the map. There are two types of Decorations: 6 | *

    7 | *
  • A settings layer is a normal GUI layer on top that provides additional functionality. The pickOnBounds property should be set on false so 8 | * that events only get caught where there actually is some GUI beneath. Otherwise user interaction events with the map might not reach their goal.
  • 9 | *
  • A decoration layer does not catch any events. All events that would reach it get caught by an internal component and distributed to all decoration 10 | * layers. This allows to have multiple decorations that use the same events, e.g. one for dragging and one for clicking or using different mouse buttons. 11 | * Decoration layers thus cannot easily contain conventional GUI.
  • 12 | *
13 | *

14 | *

15 | * Available decorations: 16 | *

    17 | *
  • {@link de.piegames.blockmap.gui.decoration.SettingsOverlay}: Puts a hideable panel at the right of the map to change the min and max height setting. 18 | *
  • 19 | *
  • {@link de.piegames.blockmap.gui.decoration.DragScrollDecoration}: Provides basic drag and zoom functionality.
  • 20 | *
21 | * What is yet to come: More settings, different decorations for selecting areas, showing points (players, monuments, etc.) on the map 22 | *

23 | * 24 | * @author piegames 25 | */ 26 | package de.piegames.blockmap.gui.decoration; -------------------------------------------------------------------------------- /BlockMap-gui/src/main/java/de/piegames/blockmap/gui/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | *

3 | * This package contains all classes to show an interactive map of a world in a JavaFX application that is highly customizable (or will be in the future). The 4 | * most important class is {@link de.piegames.blockmap.gui.WorldRendererCanvas}, a Canvas that loads and renders the world map. But it is recommended to use 5 | * {@link de.piegames.blockmap.gui.MapPane} instead, which will wrap the canvas in a GUI with proper layout and the possibility to add decorations and 6 | * interactive functionality. 7 | *

8 | *

9 | * This package is still under development. Everything might be subject to change. Use at own risk. 10 | *

11 | * 12 | * @author piegames 13 | */ 14 | package de.piegames.blockmap.gui; -------------------------------------------------------------------------------- /BlockMap-gui/src/main/java/de/piegames/blockmap/gui/standalone/CacheEntry.java: -------------------------------------------------------------------------------- 1 | package de.piegames.blockmap.gui.standalone; 2 | 3 | import de.piegames.blockmap.world.RegionFolder; 4 | 5 | public class CacheEntry { 6 | public String hash; 7 | public RegionFolder folder; 8 | public boolean force; 9 | 10 | public CacheEntry(String hash, RegionFolder folder, boolean force) { 11 | this.hash = hash; 12 | this.folder = folder; 13 | this.force = force; 14 | } 15 | 16 | @Override 17 | public int hashCode() { 18 | final int prime = 31; 19 | int result = 1; 20 | result = prime * result + ((folder == null) ? 0 : folder.hashCode()); 21 | result = prime * result + (force ? 1231 : 1237); 22 | result = prime * result + ((hash == null) ? 0 : hash.hashCode()); 23 | return result; 24 | } 25 | 26 | @Override 27 | public boolean equals(Object obj) { 28 | if (this == obj) 29 | return true; 30 | if (obj == null) 31 | return false; 32 | if (getClass() != obj.getClass()) 33 | return false; 34 | CacheEntry other = (CacheEntry) obj; 35 | if (folder == null) { 36 | if (other.folder != null) 37 | return false; 38 | } else if (!folder.equals(other.folder)) 39 | return false; 40 | if (force != other.force) 41 | return false; 42 | if (hash == null) { 43 | if (other.hash != null) 44 | return false; 45 | } else if (!hash.equals(other.hash)) 46 | return false; 47 | return true; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /BlockMap-gui/src/main/java/de/piegames/blockmap/gui/standalone/DateConverter.java: -------------------------------------------------------------------------------- 1 | package de.piegames.blockmap.gui.standalone; 2 | 3 | import java.text.SimpleDateFormat; 4 | import java.time.Instant; 5 | import java.util.Date; 6 | 7 | /** 8 | * A simple class to convert dates, for example to relative strings. 9 | * 10 | * @author saibotk 11 | */ 12 | class DateConverter { 13 | 14 | public static final long SECOND_MILLI = 1000; 15 | public static final long MINUTE_MILLI = 60 * SECOND_MILLI; 16 | public static final long HOUR_MILLI = 60 * MINUTE_MILLI; 17 | public static final long DAY_MILLI = 24 * HOUR_MILLI; 18 | public static final long MONTH_MILLI = 30 * DAY_MILLI; 19 | 20 | /** 21 | * Converts an epoch millisecond timestamp to an human readable relative string. 22 | * 23 | * @param timestamp the epoch timestamp in milliseconds 24 | * @return the relative time eg. "2 hours" or "just now" 25 | */ 26 | public static String toRelative(long timestamp) { 27 | var date = Date.from(Instant.ofEpochMilli(timestamp)); 28 | long passedTime = Instant.now().toEpochMilli() - timestamp; 29 | 30 | if (passedTime < MINUTE_MILLI) { 31 | return "just now"; 32 | } else if (passedTime < HOUR_MILLI) { 33 | int count = (int) (passedTime / MINUTE_MILLI); 34 | return count + " " + selectLangUnitAmount(count, "minute", "minutes") + " ago"; 35 | } else if (passedTime < DAY_MILLI) { 36 | int count = (int) (passedTime / HOUR_MILLI); 37 | return count + " " + selectLangUnitAmount(count, "hour", "hours") + " ago"; 38 | } else if (passedTime < MONTH_MILLI) { 39 | int count = (int) (passedTime / DAY_MILLI); 40 | return count + " " + selectLangUnitAmount(count, "day", "days") + " ago"; 41 | } else { 42 | return "in " + new SimpleDateFormat("MMMM YYYY").format(date); 43 | } 44 | } 45 | 46 | private static String selectLangUnitAmount(int amount, String single, String multiple) { 47 | return amount > 1 ? multiple : single; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /BlockMap-gui/src/main/java/de/piegames/blockmap/gui/standalone/FileCache.java: -------------------------------------------------------------------------------- 1 | package de.piegames.blockmap.gui.standalone; 2 | 3 | import org.apache.commons.logging.Log; 4 | import org.apache.commons.logging.LogFactory; 5 | 6 | import java.io.IOException; 7 | import java.nio.file.Files; 8 | import java.nio.file.Path; 9 | import java.util.stream.Stream; 10 | 11 | /** 12 | * A simple file based cache superclass, that holds redundant actions, like flushing, evicting and creating the cache 13 | * directory. 14 | * 15 | * @author saibotk 16 | */ 17 | abstract class FileCache { 18 | private static final Log log = LogFactory.getLog(FileCache.class); 19 | protected final Path cacheDir; 20 | 21 | /** 22 | * Constructs the cache instance and creates the cache folder if needed. 23 | * This will also call {@link #evict()}, to clear all old items from the cache. 24 | */ 25 | FileCache(Path cacheDir) { 26 | this.cacheDir = cacheDir; 27 | 28 | try { 29 | Files.createDirectories(cacheDir); 30 | } catch (IOException exception) { 31 | log.warn("Could not create cache directory!", exception); 32 | } 33 | } 34 | 35 | /** 36 | * Clears the cache. 37 | * This will remove all elements from the cache folder, regardless of their date. 38 | */ 39 | public void flush() { 40 | try (Stream stream = Files.list(cacheDir)) { 41 | stream.forEach(this::removeFile); 42 | } catch (IOException e) { 43 | log.error("Could not delete old cache files.", e); 44 | } 45 | 46 | log.info("Flushed all items from cache..."); 47 | } 48 | 49 | /** 50 | * Removes old items from the cache. 51 | * This will remove all items from the cache, that are too old and would not be used anymore. 52 | */ 53 | public void evict() { 54 | try (Stream stream = Files.list(cacheDir)) { 55 | stream 56 | .filter(path -> !isFresh(path)) 57 | .forEach(this::removeFile); 58 | } catch (IOException e) { 59 | log.error("Could not delete old cache files.", e); 60 | } 61 | 62 | log.info("Evicted old items from cache..."); 63 | } 64 | 65 | /** 66 | * Determine if a file is still considered "fresh". 67 | * Fresh means it is still valid and should be used, instead of fetching the resource from the origin again. 68 | * 69 | * @param path The path to the cache file. 70 | * 71 | * @return If it is still considered fresh. 72 | */ 73 | protected abstract boolean isFresh(Path path); 74 | 75 | protected void removeFile(Path path) { 76 | try { 77 | Files.delete(path); 78 | } catch (IOException e) { 79 | log.error("Could not delete cached file: " + path.getFileName() ); 80 | } 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /BlockMap-gui/src/main/java/de/piegames/blockmap/gui/standalone/GuiMain.java: -------------------------------------------------------------------------------- 1 | package de.piegames.blockmap.gui.standalone; 2 | 3 | import java.io.IOException; 4 | 5 | import org.apache.commons.logging.Log; 6 | import org.apache.commons.logging.LogFactory; 7 | import org.apache.logging.log4j.Level; 8 | import org.apache.logging.log4j.core.config.Configurator; 9 | 10 | import javafx.application.Application; 11 | import javafx.fxml.FXMLLoader; 12 | import javafx.scene.Parent; 13 | import javafx.scene.Scene; 14 | import javafx.scene.image.Image; 15 | import javafx.scene.input.KeyCode; 16 | import javafx.scene.input.KeyCodeCombination; 17 | import javafx.scene.input.KeyCombination; 18 | import javafx.stage.Stage; 19 | 20 | public class GuiMain extends Application { 21 | 22 | private static Log log = null; 23 | 24 | /** Lazily initialize the logger to avoid loading Log4j too early (startup performance). */ 25 | private static void checkLogger() { 26 | if (log == null) 27 | log = LogFactory.getLog(GuiMain.class); 28 | } 29 | 30 | /** Internal API, public due to technical reasons */ 31 | public static GuiMain instance; 32 | /** Internal API, public due to technical reasons */ 33 | public GuiController controller; 34 | /** Internal API, public due to technical reasons */ 35 | public Stage stage; 36 | 37 | private Parent root; 38 | 39 | public GuiMain() { 40 | } 41 | 42 | @Override 43 | public void init() throws IOException { 44 | try { 45 | FXMLLoader loader = new FXMLLoader(getClass().getResource("scene.fxml")); 46 | root = (Parent) loader.load(); 47 | controller = (GuiController) loader.getController(); 48 | } catch (Throwable t) { 49 | checkLogger(); 50 | log.fatal("Cannot start BlockMap", t); 51 | System.exit(-1); 52 | } 53 | } 54 | 55 | @Override 56 | public void start(Stage stage) throws IOException { 57 | try { 58 | this.stage = stage; 59 | stage.setTitle("BlockMap map viewer"); 60 | stage.getIcons().add(new Image(getClass().getResourceAsStream("icon.png"))); 61 | 62 | Scene scene = new Scene(root, 700, 450); 63 | scene.getStylesheets().add("/de/piegames/blockmap/gui/standalone/style.css"); 64 | stage.setScene(scene); 65 | stage.show(); 66 | scene.getAccelerators().put(new KeyCodeCombination(KeyCode.Q, KeyCombination.CONTROL_DOWN), () -> controller.exit()); 67 | 68 | GuiMainPreloader.splashScreen.hide(); 69 | 70 | /* Put this last to guarantee that the application is fully initialized once instance!=null. */ 71 | instance = this; 72 | } catch (Throwable t) { 73 | checkLogger(); 74 | log.fatal("Cannot start BlockMap", t); 75 | System.exit(-1); 76 | } 77 | 78 | var parameters = getParameters().getUnnamed(); 79 | if (parameters.contains("-v") || parameters.contains("--verbose")) { 80 | Configurator.setRootLevel(Level.DEBUG); 81 | checkLogger(); 82 | log.debug("Debug logging enabled."); 83 | } 84 | /* Load a world on start if specified on the command line */ 85 | parameters 86 | .stream() 87 | .filter(p -> !"-v".equals(p) && !"--verbose".equals(p)) 88 | .findFirst() 89 | .ifPresentOrElse(world -> { 90 | checkLogger(); 91 | log.info("Loading path specified on command line"); 92 | controller.load(world); 93 | }, () -> { 94 | controller.showLoadDialog(); 95 | }); 96 | } 97 | 98 | @Override 99 | public void stop() { 100 | controller.shutDown(); 101 | } 102 | 103 | public static void main(String... args) { 104 | /* Without this, JOML will print vectors out in scientific notation which isn't the most human readable thing in the world */ 105 | System.setProperty("joml.format", "false"); 106 | System.setProperty("javafx.preloader", GuiMainPreloader.class.getCanonicalName()); 107 | Application.launch(args); 108 | } 109 | } -------------------------------------------------------------------------------- /BlockMap-gui/src/main/java/de/piegames/blockmap/gui/standalone/GuiMainLauncher.java: -------------------------------------------------------------------------------- 1 | package de.piegames.blockmap.gui.standalone; 2 | 3 | public final class GuiMainLauncher { 4 | 5 | /** 6 | * This is weird. If I launch {@code GuiMain} directly, it will fail because the modules path is not set up. But if I launch 7 | * {@code GuiMainLauncher} (which really does absolutely nothing), it will just run fine without having to do anything. 8 | */ 9 | public static void main(String[] args) { 10 | GuiMain.main(args); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /BlockMap-gui/src/main/java/de/piegames/blockmap/gui/standalone/GuiMainPreloader.java: -------------------------------------------------------------------------------- 1 | package de.piegames.blockmap.gui.standalone; 2 | 3 | import javafx.application.Preloader; 4 | import javafx.scene.Scene; 5 | import javafx.scene.image.Image; 6 | import javafx.scene.image.ImageView; 7 | import javafx.scene.layout.Background; 8 | import javafx.scene.layout.BorderPane; 9 | import javafx.scene.paint.Color; 10 | import javafx.stage.Stage; 11 | import javafx.stage.StageStyle; 12 | 13 | public class GuiMainPreloader extends Preloader { 14 | 15 | /** 16 | * Yeah this is kind of ugly, but we need GuiMain to know the preloader stage to hide it. This would be realizable in a less ugly way using 17 | * event communication, but this would infer some latency while switching windows. 18 | */ 19 | static Stage splashScreen; 20 | 21 | private static final int SPLASH_WIDTH = 300; 22 | private static final int SPLASH_HEIGHT = SPLASH_WIDTH; 23 | 24 | @Override 25 | public void start(Stage stage) throws Exception { 26 | splashScreen = stage; 27 | stage.centerOnScreen(); 28 | stage.initStyle(StageStyle.TRANSPARENT); 29 | stage.getIcons().add(new Image(getClass().getResourceAsStream("icon.png"))); 30 | ImageView icon = new ImageView(getClass().getResource("icon.png").toString()); 31 | icon.setFitWidth(SPLASH_WIDTH); 32 | icon.setPreserveRatio(true); 33 | BorderPane parent = new BorderPane(icon); 34 | parent.setBackground(Background.EMPTY); 35 | Scene scene = new Scene(parent, SPLASH_WIDTH, SPLASH_HEIGHT); 36 | scene.setFill(Color.TRANSPARENT); 37 | 38 | splashScreen.setScene(scene); 39 | splashScreen.show(); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /BlockMap-gui/src/main/java/de/piegames/blockmap/gui/standalone/HistoryItem.java: -------------------------------------------------------------------------------- 1 | package de.piegames.blockmap.gui.standalone; 2 | 3 | import java.util.Objects; 4 | 5 | public class HistoryItem { 6 | protected boolean server; 7 | protected long timestamp; 8 | protected String iconURL; 9 | protected String name; 10 | protected String path; 11 | 12 | public HistoryItem() { 13 | } 14 | 15 | public HistoryItem(boolean server, String name, String path, String iconURL, long timestamp) { 16 | this.server = server; 17 | this.name = Objects.requireNonNull(name); 18 | this.path = Objects.requireNonNull(path); 19 | this.iconURL = iconURL; 20 | this.timestamp = timestamp; 21 | } 22 | 23 | @Override 24 | public String toString() { 25 | return name + "\t–\t" + path; 26 | } 27 | 28 | public String getName() { 29 | return name; 30 | } 31 | 32 | public String getPath() { 33 | return path; 34 | } 35 | 36 | /** For history items, the last time the item was loaded in BlockMap. Otherwise, the last time it was loaded in Minecraft */ 37 | public long lastAccessed() { 38 | return timestamp; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /BlockMap-gui/src/main/java/de/piegames/blockmap/gui/standalone/SimpleImageCache.java: -------------------------------------------------------------------------------- 1 | package de.piegames.blockmap.gui.standalone; 2 | 3 | import io.github.soc.directories.ProjectDirectories; 4 | import javafx.embed.swing.SwingFXUtils; 5 | import javafx.scene.image.Image; 6 | import org.apache.commons.logging.Log; 7 | import org.apache.commons.logging.LogFactory; 8 | 9 | import javax.imageio.ImageIO; 10 | import java.awt.image.BufferedImage; 11 | import java.io.IOException; 12 | import java.nio.file.*; 13 | import java.time.Instant; 14 | 15 | /** 16 | * A simple image cache implementation, which will save fetched images to disk and load them from disk when possible. 17 | * 18 | * @author saibotk 19 | */ 20 | public class SimpleImageCache extends FileCache { 21 | private static final Log log = LogFactory.getLog(SimpleImageCache.class); 22 | 23 | private static final long MAX_CACHE_TIME = 60000L; 24 | 25 | /** 26 | * Constructs the cache instance. 27 | */ 28 | public SimpleImageCache() { 29 | super(Paths.get(ProjectDirectories.from("de", "piegames", "blockmap").cacheDir + "/images")); 30 | } 31 | 32 | /** 33 | * Gets an image from the cache if possible and will otherwise fetch the image using {@link #fetch(String)}. 34 | * This will also evict a file from the cache, if it is not fresh enough (deletes the file). It will also save 35 | * the file to the disk, if the file has been requested / fetched from the URL. 36 | * 37 | * @param url The URL of the Image to fetch. 38 | * 39 | * @return The requested image 40 | */ 41 | public Image get(String url) { 42 | String fileName = getFileName(url); 43 | 44 | try { 45 | Path cachedFile = cacheDir.resolve(fileName); 46 | boolean exists = Files.exists(cachedFile); 47 | 48 | if (exists) { 49 | if (isFresh(cachedFile)) { 50 | log.info("Loading image from cache: " + fileName); 51 | 52 | return new Image(Files.newInputStream(cachedFile)); 53 | } else { 54 | log.info("Image will be removed from cache: " + fileName); 55 | 56 | removeFile(cachedFile); 57 | } 58 | } 59 | } catch (IOException exception) { 60 | log.info("Image could not be fetched from cache..."); 61 | } 62 | 63 | log.info("Fetching image from source instead: " + url); 64 | 65 | // retrieve the image from the URL 66 | Image image = fetch(url); 67 | 68 | // Save the image, if it is not an error 69 | if(!image.isError()) 70 | writeToCache(image, fileName); 71 | 72 | return image; 73 | } 74 | 75 | /** 76 | * 77 | * Fetches an Image from an URL and returns the image. 78 | * 79 | * @param url The url that is used to fetch the object from. 80 | * 81 | * @return The requested image object. 82 | * 83 | * @throws NullPointerException if URL is null 84 | * @throws IllegalArgumentException if URL is invalid or unsupported 85 | */ 86 | public Image fetch(String url) { 87 | String fileName = getFileName(url); 88 | 89 | Image result = new Image(url, 0, 0, true, false); 90 | 91 | log.info("Fetched image: " + fileName); 92 | 93 | return result; 94 | } 95 | 96 | @Override 97 | protected boolean isFresh(Path path) { 98 | try { 99 | return Instant.now().toEpochMilli() - Files.getLastModifiedTime(path).toMillis() <= MAX_CACHE_TIME; 100 | } catch (IOException e) { 101 | log.error("Could not access last modified date on file: " + path.getFileName(), e); 102 | 103 | return false; 104 | } 105 | } 106 | 107 | protected void writeToCache(Image image, String fileName) { 108 | BufferedImage bufferedImage = SwingFXUtils.fromFXImage(image, null); 109 | 110 | try { 111 | ImageIO.write(bufferedImage, "png", Files.newOutputStream(cacheDir.resolve(fileName))); 112 | 113 | log.info("Wrote file to image cache: " + fileName); 114 | } catch (IOException e) { 115 | log.error("Could not write to file for image cache: " + fileName, e); 116 | } 117 | } 118 | 119 | protected String getFileName(String url) { 120 | Path location = Paths.get(url); 121 | 122 | return location.getFileName().toString() + ".png"; 123 | } 124 | 125 | } 126 | -------------------------------------------------------------------------------- /BlockMap-gui/src/main/java/de/piegames/blockmap/gui/standalone/SimplePlayerProfileCache.java: -------------------------------------------------------------------------------- 1 | package de.piegames.blockmap.gui.standalone; 2 | 3 | import com.google.gson.Gson; 4 | import de.saibotk.jmaw.ApiResponseException; 5 | import de.saibotk.jmaw.MojangAPI; 6 | import de.saibotk.jmaw.PlayerProfile; 7 | import de.saibotk.jmaw.Util; 8 | import io.github.soc.directories.ProjectDirectories; 9 | import org.apache.commons.logging.Log; 10 | import org.apache.commons.logging.LogFactory; 11 | 12 | import java.io.BufferedWriter; 13 | import java.io.IOException; 14 | import java.nio.file.Files; 15 | import java.nio.file.Path; 16 | import java.nio.file.Paths; 17 | import java.time.Instant; 18 | import java.util.Optional; 19 | 20 | /** 21 | * A simple {@link PlayerProfile} cache implementation, to overcome the API request limit of a minute. 22 | * It will save fetched profiles to disk and load them from the disk when possible. 23 | * 24 | * @author saibotk 25 | */ 26 | public class SimplePlayerProfileCache extends FileCache { 27 | private static final Log log = LogFactory.getLog(SimplePlayerProfileCache.class); 28 | 29 | private final MojangAPI api = new MojangAPI(); 30 | private static final Gson GSON = Util.getGson(); 31 | 32 | private static final long MAX_CACHE_TIME = 60000L; 33 | 34 | /** 35 | * Constructs the cache instance. 36 | */ 37 | public SimplePlayerProfileCache() { 38 | super(Paths.get(ProjectDirectories.from("de", "piegames", "blockmap").cacheDir + "/profiles")); 39 | } 40 | 41 | /** 42 | * Gets a {@link PlayerProfile} from cache if available and loads it from Mojang otherwise. 43 | * This will also remove an existing old cache entry and save a fresh profile to disk if necessary. 44 | * 45 | * @param uuid The uuid of the player. 46 | * 47 | * @return The requested profile, wrapped as an Optional. 48 | * 49 | * @throws ApiResponseException Exception when contacting the Mojang API. 50 | */ 51 | public Optional get(String uuid) throws ApiResponseException { 52 | try { 53 | Path cachedFile = cacheDir.resolve(uuid + ".json"); 54 | boolean exists = Files.exists(cachedFile); 55 | 56 | if (exists) { 57 | if (isFresh(cachedFile)) { 58 | log.info("Loading player profile from cache with uuid: " + uuid); 59 | 60 | return Optional.of(GSON.fromJson(Files.newBufferedReader(cachedFile), PlayerProfile.class)); 61 | } else { 62 | log.info("Player profile will be removed from cache for uuid: " + uuid); 63 | 64 | removeFile(cachedFile); 65 | } 66 | } 67 | } catch (IOException exception) { 68 | log.info("Could not fetch player profile from cache for uuid: " + uuid, exception); 69 | } 70 | 71 | log.info("Fetching player profile from API for uuid: " + uuid); 72 | 73 | // retrieve the profile from Mojang 74 | Optional profile = fetch(uuid); 75 | 76 | // Save the profile, if it is available 77 | profile.ifPresent(this::writeToCache); 78 | 79 | return profile; 80 | } 81 | 82 | /** 83 | * Fetches a player profile from Mojang's API and returns it. 84 | * 85 | * @param uuid The uuid of the player. 86 | * 87 | * @return The {@link PlayerProfile} wrapped in an Optional. 88 | * 89 | * @throws ApiResponseException The exception of the Mojang API call. 90 | */ 91 | public Optional fetch(String uuid) throws ApiResponseException { 92 | return api.getPlayerProfile(uuid); 93 | } 94 | 95 | @Override 96 | protected boolean isFresh(Path path) { 97 | try { 98 | return Instant.now().toEpochMilli() - Files.getLastModifiedTime(path).toMillis() <= MAX_CACHE_TIME; 99 | } catch (IOException e) { 100 | log.error("Could not access last modified date on file: " + path.getFileName(), e); 101 | 102 | return false; 103 | } 104 | } 105 | 106 | protected void writeToCache(PlayerProfile profile) { 107 | try { 108 | BufferedWriter writer = Files.newBufferedWriter(cacheDir.resolve(profile.getId() + ".json")); 109 | GSON.toJson(profile, writer); 110 | writer.flush(); 111 | log.info("Cached profile to disk with uuid: " + profile.getId()); 112 | } catch (IOException exception) { 113 | log.error("Could not write player profile with uuid: " + profile.getId()); 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /BlockMap-gui/src/main/java/de/piegames/blockmap/gui/standalone/StatusBarSkin2.java: -------------------------------------------------------------------------------- 1 | package de.piegames.blockmap.gui.standalone; 2 | 3 | import org.controlsfx.control.StatusBar; 4 | 5 | import javafx.beans.Observable; 6 | import javafx.geometry.Insets; 7 | import javafx.scene.control.Label; 8 | import javafx.scene.control.ProgressBar; 9 | import javafx.scene.control.SkinBase; 10 | import javafx.scene.layout.GridPane; 11 | import javafx.scene.layout.HBox; 12 | import javafx.scene.layout.Priority; 13 | 14 | /** A simple mutation of {@link impl.org.controlsfx.skin.StatusBarSkin} because I want the label to be on the left */ 15 | public class StatusBarSkin2 extends SkinBase { 16 | 17 | private HBox leftBox; 18 | private HBox rightBox; 19 | private Label label; 20 | private ProgressBar progressBar; 21 | 22 | public StatusBarSkin2(StatusBar statusBar) { 23 | super(statusBar); 24 | 25 | leftBox = new HBox(); 26 | leftBox.getStyleClass().add("left-items"); //$NON-NLS-1$ 27 | 28 | rightBox = new HBox(); 29 | rightBox.getStyleClass().add("right-items"); //$NON-NLS-1$ 30 | 31 | progressBar = new ProgressBar(); 32 | progressBar.progressProperty().bind(statusBar.progressProperty()); 33 | // progressBar.visibleProperty().bind( 34 | // Bindings.notEqual(0, statusBar.progressProperty())); 35 | 36 | label = new Label(); 37 | label.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE); 38 | label.textProperty().bind(statusBar.textProperty()); 39 | label.graphicProperty().bind(statusBar.graphicProperty()); 40 | label.getStyleClass().add("status-label"); //$NON-NLS-1$ 41 | 42 | leftBox.getChildren().setAll(getSkinnable().getLeftItems()); 43 | 44 | rightBox.getChildren().setAll(getSkinnable().getRightItems()); 45 | 46 | statusBar.getLeftItems().addListener( 47 | (Observable evt) -> leftBox.getChildren().setAll( 48 | getSkinnable().getLeftItems())); 49 | 50 | statusBar.getRightItems().addListener( 51 | (Observable evt) -> rightBox.getChildren().setAll( 52 | getSkinnable().getRightItems())); 53 | 54 | GridPane gridPane = new GridPane(); 55 | 56 | GridPane.setFillHeight(leftBox, true); 57 | GridPane.setFillHeight(rightBox, true); 58 | GridPane.setFillHeight(label, true); 59 | GridPane.setFillHeight(progressBar, true); 60 | 61 | GridPane.setVgrow(leftBox, Priority.ALWAYS); 62 | GridPane.setVgrow(rightBox, Priority.ALWAYS); 63 | GridPane.setVgrow(label, Priority.ALWAYS); 64 | GridPane.setVgrow(progressBar, Priority.ALWAYS); 65 | 66 | GridPane.setHgrow(leftBox, Priority.ALWAYS); 67 | 68 | GridPane.setMargin(leftBox, new Insets(0, 10, 0, 10)); 69 | GridPane.setMargin(rightBox, new Insets(0, 10, 0, 10)); 70 | 71 | gridPane.add(label, 0, 0); 72 | gridPane.add(leftBox, 1, 0); 73 | gridPane.add(rightBox, 2, 0); 74 | gridPane.add(progressBar, 4, 0); 75 | 76 | getChildren().add(gridPane); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /BlockMap-gui/src/main/java/de/piegames/blockmap/gui/standalone/about/AboutDialog.java: -------------------------------------------------------------------------------- 1 | package de.piegames.blockmap.gui.standalone.about; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStreamReader; 5 | import java.util.List; 6 | import java.util.Optional; 7 | 8 | import com.google.common.reflect.TypeToken; 9 | import com.google.gson.GsonBuilder; 10 | import com.google.gson.annotations.SerializedName; 11 | 12 | import de.piegames.blockmap.gui.VersionProvider; 13 | import javafx.fxml.FXML; 14 | import javafx.fxml.FXMLLoader; 15 | import javafx.scene.control.Alert; 16 | import javafx.scene.control.ButtonType; 17 | import javafx.scene.control.Label; 18 | import javafx.scene.control.ScrollPane; 19 | import javafx.scene.control.TextArea; 20 | import javafx.scene.layout.VBox; 21 | import javafx.stage.Modality; 22 | import net.dongliu.gson.GsonJava8TypeAdapterFactory; 23 | 24 | public class AboutDialog extends Alert { 25 | private static final String LICENSE_TEXT = "MIT License\n" + 26 | "\n" + 27 | "Copyright (c) 2020 piegames\n" + 28 | "\n" + 29 | "Permission is hereby granted, free of charge, to any person obtaining a copy\n" + 30 | "of this software and associated documentation files (the \"Software\"), to deal\n" + 31 | "in the Software without restriction, including without limitation the rights\n" + 32 | "to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n" + 33 | "copies of the Software, and to permit persons to whom the Software is\n" + 34 | "furnished to do so, subject to the following conditions:\n" + 35 | "\n" + 36 | "The above copyright notice and this permission notice shall be included in all\n" + 37 | "copies or substantial portions of the Software.\n" + 38 | "\n" + 39 | "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n" + 40 | "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n" + 41 | "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n" + 42 | "AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n" + 43 | "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n" + 44 | "OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n" + 45 | "SOFTWARE.\n" + 46 | ""; 47 | 48 | @FXML 49 | Label aboutTitle; 50 | @FXML 51 | VBox dependencies; 52 | @FXML 53 | ScrollPane dependencyContainer; 54 | @FXML 55 | TextArea license; 56 | 57 | public AboutDialog() throws IOException { 58 | super(AlertType.NONE, null, ButtonType.CLOSE); 59 | setTitle("About BlockMap"); 60 | setResizable(true); 61 | initModality(Modality.APPLICATION_MODAL); 62 | 63 | FXMLLoader loader = new FXMLLoader(getClass().getResource("aboutpane.fxml")); 64 | loader.setController(this); 65 | getDialogPane().setContent(loader.load()); 66 | getDialogPane().getStylesheets().add("/de/piegames/blockmap/gui/standalone/about/style.css"); 67 | 68 | aboutTitle.setText("BlockMap " + VersionProvider.VERSION); 69 | 70 | @SuppressWarnings("serial") 71 | List dependencies = new GsonBuilder().registerTypeAdapterFactory(new GsonJava8TypeAdapterFactory()).create().fromJson( 72 | // TODO automate copying that file on dependency change 73 | new InputStreamReader(getClass().getResourceAsStream("licenseReport.json")), 74 | new TypeToken>() { 75 | }.getType()); 76 | 77 | for (Dependency dependency : dependencies) { 78 | this.dependencies.getChildren().add(new DependencyPane(dependency)); 79 | } 80 | 81 | license.setText(LICENSE_TEXT); 82 | } 83 | 84 | static class Dependency { 85 | @SerializedName("project") 86 | String name; 87 | Optional version; 88 | Optional description; 89 | License[] licenses; 90 | Optional year; 91 | String[] developers; 92 | Optional url; 93 | String dependency; 94 | } 95 | 96 | static class License { 97 | @SerializedName("license") 98 | String name; 99 | @SerializedName("license_url") 100 | String url; 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /BlockMap-gui/src/main/java/de/piegames/blockmap/gui/standalone/about/DependencyPane.java: -------------------------------------------------------------------------------- 1 | package de.piegames.blockmap.gui.standalone.about; 2 | 3 | import java.io.IOException; 4 | import java.util.stream.Collectors; 5 | import java.util.stream.Stream; 6 | 7 | import com.google.common.collect.Streams; 8 | 9 | import de.piegames.blockmap.gui.standalone.about.AboutDialog.Dependency; 10 | import de.piegames.blockmap.gui.standalone.about.AboutDialog.License; 11 | import javafx.fxml.FXML; 12 | import javafx.fxml.FXMLLoader; 13 | import javafx.scene.control.Hyperlink; 14 | import javafx.scene.control.Label; 15 | import javafx.scene.control.Separator; 16 | import javafx.scene.layout.VBox; 17 | 18 | public class DependencyPane extends VBox { 19 | 20 | @FXML 21 | Label name, version; 22 | 23 | public DependencyPane(Dependency dependency) throws IOException { 24 | FXMLLoader loader = new FXMLLoader(getClass().getResource("dependency.fxml")); 25 | loader.setRoot(this); 26 | loader.setController(this); 27 | loader.load(); 28 | 29 | name.setText(dependency.name); 30 | dependency.version.ifPresentOrElse(v -> version.setText("v." + v), () -> version.setVisible(false)); 31 | { 32 | Label l = new Label(dependency.dependency); 33 | l.getStyleClass().add("dependency-gradle"); 34 | getChildren().add(l); 35 | } 36 | dependency.description.ifPresent(d -> getChildren().add(new Label(String.valueOf(d).replace("\\n", "\n").replace("\\t", "\t")))); 37 | /* If there is a year or a set of developers, combine them to a string */ 38 | if (dependency.year.isPresent() || dependency.developers.length > 0) 39 | getChildren().add(new Label(Streams.concat(dependency.year.stream(), Stream.of(String.join(", ", dependency.developers))).collect(Collectors 40 | .joining(" ", "© ", "")))); 41 | dependency.url.ifPresent(url -> getChildren().add(new Hyperlink(url))); 42 | 43 | Separator separator = new Separator(); 44 | separator.setMaxWidth(150); 45 | getChildren().add(separator); 46 | 47 | for (License license : dependency.licenses) { 48 | getChildren().add(new Label(license.name)); 49 | getChildren().add(new Hyperlink(license.url)); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /BlockMap-gui/src/main/java/de/piegames/blockmap/gui/standalone/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | /** 5 | * @author piegames 6 | * 7 | */ 8 | package de.piegames.blockmap.gui.standalone; -------------------------------------------------------------------------------- /BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/SettingsOverlay.fxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 17 | 18 | 22 | 23 | 28 | 32 | 33 | 42 | 59 | 60 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 76 | 77 | -------------------------------------------------------------------------------- /BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/pins.css: -------------------------------------------------------------------------------- 1 | /*-popover > .content > */ 2 | .grid { 3 | -fx-alignment: center; 4 | -fx-hgap: 3; 5 | -fx-vgap: 5; 6 | -fx-padding: 5; 7 | } 8 | 9 | .popover .title .text { 10 | -fx-label-padding: 0 20 0 20; 11 | } 12 | 13 | .pin { 14 | -fx-background-radius: 6em; 15 | } 16 | 17 | .mergedpin-box { 18 | -fx-background-color: transparent; 19 | } 20 | 21 | .mergedpin-box .label { 22 | -fx-font-size: 130%; 23 | -fx-label-padding: 5px; 24 | } -------------------------------------------------------------------------------- /BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/overlays/chunk_corrupted.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piegamesde/BlockMap/19af32ac7683ff7bf3c22757b27b00f450c9158c/BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/overlays/chunk_corrupted.png -------------------------------------------------------------------------------- /BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/overlays/chunk_forced.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piegamesde/BlockMap/19af32ac7683ff7bf3c22757b27b00f450c9158c/BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/overlays/chunk_forced.png -------------------------------------------------------------------------------- /BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/overlays/chunk_outdated.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piegamesde/BlockMap/19af32ac7683ff7bf3c22757b27b00f450c9158c/BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/overlays/chunk_outdated.png -------------------------------------------------------------------------------- /BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/overlays/chunk_unfinished.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piegamesde/BlockMap/19af32ac7683ff7bf3c22757b27b00f450c9158c/BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/overlays/chunk_unfinished.png -------------------------------------------------------------------------------- /BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/overlays/pin_chunk_corrupted.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piegamesde/BlockMap/19af32ac7683ff7bf3c22757b27b00f450c9158c/BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/overlays/pin_chunk_corrupted.png -------------------------------------------------------------------------------- /BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/overlays/pin_chunk_outdated.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piegamesde/BlockMap/19af32ac7683ff7bf3c22757b27b00f450c9158c/BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/overlays/pin_chunk_outdated.png -------------------------------------------------------------------------------- /BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/overlays/pin_chunk_unfinished.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piegamesde/BlockMap/19af32ac7683ff7bf3c22757b27b00f450c9158c/BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/overlays/pin_chunk_unfinished.png -------------------------------------------------------------------------------- /BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/overlays/pin_chunks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piegamesde/BlockMap/19af32ac7683ff7bf3c22757b27b00f450c9158c/BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/overlays/pin_chunks.png -------------------------------------------------------------------------------- /BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/pins/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piegamesde/BlockMap/19af32ac7683ff7bf3c22757b27b00f450c9158c/BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/pins/banner.png -------------------------------------------------------------------------------- /BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/pins/banner_fabric.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piegamesde/BlockMap/19af32ac7683ff7bf3c22757b27b00f450c9158c/BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/pins/banner_fabric.png -------------------------------------------------------------------------------- /BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/pins/banner_sticks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piegamesde/BlockMap/19af32ac7683ff7bf3c22757b27b00f450c9158c/BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/pins/banner_sticks.png -------------------------------------------------------------------------------- /BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/pins/lodestone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piegamesde/BlockMap/19af32ac7683ff7bf3c22757b27b00f450c9158c/BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/pins/lodestone.png -------------------------------------------------------------------------------- /BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/pins/map.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piegamesde/BlockMap/19af32ac7683ff7bf3c22757b27b00f450c9158c/BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/pins/map.png -------------------------------------------------------------------------------- /BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/pins/pins.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piegamesde/BlockMap/19af32ac7683ff7bf3c22757b27b00f450c9158c/BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/pins/pins.png -------------------------------------------------------------------------------- /BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/pins/player.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piegamesde/BlockMap/19af32ac7683ff7bf3c22757b27b00f450c9158c/BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/pins/player.png -------------------------------------------------------------------------------- /BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/pins/spawn_map.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piegamesde/BlockMap/19af32ac7683ff7bf3c22757b27b00f450c9158c/BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/pins/spawn_map.png -------------------------------------------------------------------------------- /BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/pins/spawn_player.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piegamesde/BlockMap/19af32ac7683ff7bf3c22757b27b00f450c9158c/BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/pins/spawn_player.png -------------------------------------------------------------------------------- /BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/pins/structures.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piegamesde/BlockMap/19af32ac7683ff7bf3c22757b27b00f450c9158c/BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/pins/structures.png -------------------------------------------------------------------------------- /BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/structures/bastion_remnant.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piegamesde/BlockMap/19af32ac7683ff7bf3c22757b27b00f450c9158c/BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/structures/bastion_remnant.png -------------------------------------------------------------------------------- /BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/structures/bee_hive.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piegamesde/BlockMap/19af32ac7683ff7bf3c22757b27b00f450c9158c/BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/structures/bee_hive.png -------------------------------------------------------------------------------- /BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/structures/buried_treasure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piegamesde/BlockMap/19af32ac7683ff7bf3c22757b27b00f450c9158c/BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/structures/buried_treasure.png -------------------------------------------------------------------------------- /BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/structures/desert_pyramid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piegamesde/BlockMap/19af32ac7683ff7bf3c22757b27b00f450c9158c/BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/structures/desert_pyramid.png -------------------------------------------------------------------------------- /BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/structures/end_city.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piegamesde/BlockMap/19af32ac7683ff7bf3c22757b27b00f450c9158c/BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/structures/end_city.png -------------------------------------------------------------------------------- /BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/structures/fortress.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piegamesde/BlockMap/19af32ac7683ff7bf3c22757b27b00f450c9158c/BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/structures/fortress.png -------------------------------------------------------------------------------- /BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/structures/fossil.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piegamesde/BlockMap/19af32ac7683ff7bf3c22757b27b00f450c9158c/BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/structures/fossil.png -------------------------------------------------------------------------------- /BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/structures/house.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piegamesde/BlockMap/19af32ac7683ff7bf3c22757b27b00f450c9158c/BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/structures/house.png -------------------------------------------------------------------------------- /BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/structures/igloo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piegamesde/BlockMap/19af32ac7683ff7bf3c22757b27b00f450c9158c/BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/structures/igloo.png -------------------------------------------------------------------------------- /BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/structures/jungle_pyramid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piegamesde/BlockMap/19af32ac7683ff7bf3c22757b27b00f450c9158c/BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/structures/jungle_pyramid.png -------------------------------------------------------------------------------- /BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/structures/mansion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piegamesde/BlockMap/19af32ac7683ff7bf3c22757b27b00f450c9158c/BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/structures/mansion.png -------------------------------------------------------------------------------- /BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/structures/mineshaft.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piegamesde/BlockMap/19af32ac7683ff7bf3c22757b27b00f450c9158c/BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/structures/mineshaft.png -------------------------------------------------------------------------------- /BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/structures/monument.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piegamesde/BlockMap/19af32ac7683ff7bf3c22757b27b00f450c9158c/BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/structures/monument.png -------------------------------------------------------------------------------- /BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/structures/nether_portal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piegamesde/BlockMap/19af32ac7683ff7bf3c22757b27b00f450c9158c/BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/structures/nether_portal.png -------------------------------------------------------------------------------- /BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/structures/ocean_ruin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piegamesde/BlockMap/19af32ac7683ff7bf3c22757b27b00f450c9158c/BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/structures/ocean_ruin.png -------------------------------------------------------------------------------- /BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/structures/outpost.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piegamesde/BlockMap/19af32ac7683ff7bf3c22757b27b00f450c9158c/BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/structures/outpost.png -------------------------------------------------------------------------------- /BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/structures/ruined_portal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piegamesde/BlockMap/19af32ac7683ff7bf3c22757b27b00f450c9158c/BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/structures/ruined_portal.png -------------------------------------------------------------------------------- /BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/structures/shipwreck.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piegamesde/BlockMap/19af32ac7683ff7bf3c22757b27b00f450c9158c/BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/structures/shipwreck.png -------------------------------------------------------------------------------- /BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/structures/stronghold.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piegamesde/BlockMap/19af32ac7683ff7bf3c22757b27b00f450c9158c/BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/structures/stronghold.png -------------------------------------------------------------------------------- /BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/structures/swamp_hut.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piegamesde/BlockMap/19af32ac7683ff7bf3c22757b27b00f450c9158c/BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/structures/swamp_hut.png -------------------------------------------------------------------------------- /BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/structures/village.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piegamesde/BlockMap/19af32ac7683ff7bf3c22757b27b00f450c9158c/BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/structures/village.png -------------------------------------------------------------------------------- /BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/villages/armorer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piegamesde/BlockMap/19af32ac7683ff7bf3c22757b27b00f450c9158c/BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/villages/armorer.png -------------------------------------------------------------------------------- /BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/villages/bell.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piegamesde/BlockMap/19af32ac7683ff7bf3c22757b27b00f450c9158c/BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/villages/bell.png -------------------------------------------------------------------------------- /BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/villages/butcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piegamesde/BlockMap/19af32ac7683ff7bf3c22757b27b00f450c9158c/BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/villages/butcher.png -------------------------------------------------------------------------------- /BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/villages/cartographer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piegamesde/BlockMap/19af32ac7683ff7bf3c22757b27b00f450c9158c/BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/villages/cartographer.png -------------------------------------------------------------------------------- /BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/villages/cleric.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piegamesde/BlockMap/19af32ac7683ff7bf3c22757b27b00f450c9158c/BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/villages/cleric.png -------------------------------------------------------------------------------- /BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/villages/farmer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piegamesde/BlockMap/19af32ac7683ff7bf3c22757b27b00f450c9158c/BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/villages/farmer.png -------------------------------------------------------------------------------- /BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/villages/fisherman.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piegamesde/BlockMap/19af32ac7683ff7bf3c22757b27b00f450c9158c/BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/villages/fisherman.png -------------------------------------------------------------------------------- /BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/villages/fletcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piegamesde/BlockMap/19af32ac7683ff7bf3c22757b27b00f450c9158c/BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/villages/fletcher.png -------------------------------------------------------------------------------- /BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/villages/home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piegamesde/BlockMap/19af32ac7683ff7bf3c22757b27b00f450c9158c/BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/villages/home.png -------------------------------------------------------------------------------- /BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/villages/leatherworker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piegamesde/BlockMap/19af32ac7683ff7bf3c22757b27b00f450c9158c/BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/villages/leatherworker.png -------------------------------------------------------------------------------- /BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/villages/librarian.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piegamesde/BlockMap/19af32ac7683ff7bf3c22757b27b00f450c9158c/BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/villages/librarian.png -------------------------------------------------------------------------------- /BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/villages/mason.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piegamesde/BlockMap/19af32ac7683ff7bf3c22757b27b00f450c9158c/BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/villages/mason.png -------------------------------------------------------------------------------- /BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/villages/shepherd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piegamesde/BlockMap/19af32ac7683ff7bf3c22757b27b00f450c9158c/BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/villages/shepherd.png -------------------------------------------------------------------------------- /BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/villages/toolsmith.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piegamesde/BlockMap/19af32ac7683ff7bf3c22757b27b00f450c9158c/BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/villages/toolsmith.png -------------------------------------------------------------------------------- /BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/villages/weaponsmith.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/piegamesde/BlockMap/19af32ac7683ff7bf3c22757b27b00f450c9158c/BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/decoration/textures/villages/weaponsmith.png -------------------------------------------------------------------------------- /BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/standalone/about/aboutpane.fxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 24 | 25 | 26 | 27 |